Skip to content

Touches

julia
export touches

What is touches?

The touches function checks if one geometry touches another geometry. In other words, the interiors of the two geometries don't interact, but one of the geometries must have a boundary point that interacts with either the other geometry's interior or boundary.

To provide an example, consider these two lines:

julia
import GeometryOps as GO
import GeoInterface as GI
using Makie
using CairoMakie

l1 = GI.Line([(0.0, 0.0), (1.0, 0.0)])
l2 = GI.Line([(1.0, 0.0), (1.0, -1.0)])

f, a, p = lines(GI.getpoint(l1))
lines!(GI.getpoint(l2))
f

We can see that these two lines touch only at their endpoints.

julia
GO.touches(l1, l2)  # true
true

Implementation

This is the GeoInterface-compatible implementation.

First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.

Each of these calls a method in the geom_geom_processors file. The methods in this file determine if the given geometries meet a set of criteria. For the touches function and arguments g1 and g2, this criteria is as follows: - points of g1 are not allowed to be in the interior of g2 - points of g1 are allowed to be on the boundary of g2 - points of g1 are allowed to be in the exterior of g2 - no points of g1 are required to be in the interior of g2 - at least one point of g1 is required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2

The code for the specific implementations is in the geom_geom_processors file.

julia
const TOUCHES_POINT_ALLOWED = (in_allow = false, on_allow = true, out_allow = false)
const TOUCHES_CURVE_ALLOWED = (over_allow = false, cross_allow = false, on_allow = true, out_allow = true)
const TOUCHES_POLYGON_ALLOWS = (in_allow = false, on_allow = true, out_allow = true)
const TOUCHES_REQUIRES = (in_require = false, on_require = true, out_require = false)
const TOUCHES_EXACT = (exact = _False(),)

"""
    touches(geom1, geom2)::Bool

Return `true` if the first geometry touches the second geometry. In other words,
the two interiors cannot interact, but one of the geometries must have a
boundary point that interacts with either the other geometry's interior or
boundary.

# Examples
```jldoctest setup=:(using GeometryOps, GeometryBasics)
import GeometryOps as GO, GeoInterface as GI

l1 = GI.Line([(0.0, 0.0), (1.0, 0.0)])
l2 = GI.Line([(1.0, 1.0), (1.0, -1.0)])

GO.touches(l1, l2)

output

julia
true
```
"""
touches(g1, g2)::Bool = _touches(trait(g1), g1, trait(g2), g2)

Convert features to geometries

julia
_touches(::GI.FeatureTrait, g1, ::Any, g2) = touches(GI.geometry(g1), g2)
_touches(::Any, g1, t2::GI.FeatureTrait, g2) = touches(g1, GI.geometry(g2))
_touches(::FeatureTrait, g1, ::FeatureTrait, g2) = touches(GI.geometry(g1), GI.geometry(g2))

Point touches geometries

Point cannot touch another point as if they are equal, interiors interact

julia
_touches(
    ::GI.PointTrait, g1,
    ::GI.PointTrait, g2,
) = false

Point touches a linestring if it equal to the first of last point of the line

julia
function _touches(
    ::GI.PointTrait, g1,
    ::Union{GI.LineTrait, GI.LineStringTrait}, g2,
)
    n = GI.npoint(g2)
    p1 = GI.getpoint(g2, 1)
    pn = GI.getpoint(g2, n)
    equals(p1, pn) && return false
    return equals(g1, p1) || equals(g1, pn)
end

Point cannot 'touch' a linearring given that the ring has no boundary points

julia
_touches(
    ::GI.PointTrait, g1,
    ::GI.LinearRingTrait, g2,
) = false

Point touches a polygon if it is on the boundary of that polygon

julia
_touches(
    ::GI.PointTrait, g1,
    ::GI.PolygonTrait, g2,
) = _point_polygon_process(
    g1, g2;
    TOUCHES_POINT_ALLOWED...,
    TOUCHES_EXACT...,
)

#= Geometry touches a point if the point is on the geometry boundary. =#
_touches(
    trait1::Union{GI.AbstractCurveTrait, GI.PolygonTrait}, g1,
    trait2::GI.PointTrait, g2,
) = _touches(trait2, g2, trait1, g1)

Lines touching geometries

julia
#= Linestring touches another line if at least one boundary point interacts with
the boundary of interior of the other line, but the interiors don't interact. =#
_touches(
    ::Union{GI.LineTrait, GI.LineStringTrait}, g1,
    ::Union{GI.LineTrait, GI.LineStringTrait}, g2,
) = _line_curve_process(
    g1, g2;
    TOUCHES_CURVE_ALLOWED...,
    TOUCHES_REQUIRES...,
    TOUCHES_EXACT...,
    closed_line = false,
    closed_curve = false,
)


#= Linestring touches a linearring if at least one of the boundary points of the
line interacts with the linear ring, but their interiors can't interact. =#
_touches(
    ::Union{GI.LineTrait, GI.LineStringTrait}, g1,
    ::GI.LinearRingTrait, g2,
) = _line_curve_process(
    g1, g2;
    TOUCHES_CURVE_ALLOWED...,
    TOUCHES_REQUIRES...,
    TOUCHES_EXACT...,
    closed_line = false,
    closed_curve = true,
)

#= Linestring touches a polygon if at least one of the boundary points of the
line interacts with the boundary of the polygon. =#
_touches(
    ::Union{GI.LineTrait, GI.LineStringTrait}, g1,
    ::GI.PolygonTrait, g2,
) = _line_polygon_process(
    g1, g2;
    TOUCHES_POLYGON_ALLOWS...,
    TOUCHES_REQUIRES...,
    TOUCHES_EXACT...,
    closed_line = false,
)

Rings touch geometries

julia
#= Linearring touches a linestring if at least one of the boundary points of the
line interacts with the linear ring, but their interiors can't interact. =#
_touches(
    trait1::GI.LinearRingTrait, g1,
    trait2::Union{GI.LineTrait, GI.LineStringTrait}, g2,
) = _touches(trait2, g2, trait1, g1)

#= Linearring cannot touch another linear ring since they are both exclusively
made up of interior points and no boundary points =#
_touches(
    ::GI.LinearRingTrait, g1,
    ::GI.LinearRingTrait, g2,
) = false

#= Linearring touches a polygon if at least one of the points of the ring
interact with the polygon boundary and non are in the polygon interior. =#
_touches(
    ::GI.LinearRingTrait, g1,
    ::GI.PolygonTrait, g2,
) = _line_polygon_process(
    g1, g2;
    TOUCHES_POLYGON_ALLOWS...,
    TOUCHES_REQUIRES...,
    TOUCHES_EXACT...,
    closed_line = true,
)

Polygons touch geometries

julia
#= Polygon touches a curve if at least one of the curve boundary points interacts
with the polygon's boundary and no curve points interact with the interior.=#
_touches(
    trait1::GI.PolygonTrait, g1,
    trait2::GI.AbstractCurveTrait, g2
) = _touches(trait2, g2, trait1, g1)


#= Polygon touches another polygon if they share at least one boundary point and
no interior points. =#
_touches(
    ::GI.PolygonTrait, g1,
    ::GI.PolygonTrait, g2,
) = _polygon_polygon_process(
    g1, g2;
    TOUCHES_POLYGON_ALLOWS...,
    TOUCHES_REQUIRES...,
    TOUCHES_EXACT...,
)

Geometries touch multi-geometry/geometry collections

julia
#= Geometry touch a multi-geometry or a collection if the geometry touches at
least one of the elements of the collection. =#
function _touches(
    ::Union{GI.PointTrait, GI.AbstractCurveTrait, GI.PolygonTrait}, g1,
    ::Union{
        GI.MultiPointTrait, GI.AbstractMultiCurveTrait,
        GI.MultiPolygonTrait, GI.GeometryCollectionTrait,
    }, g2,
)
    for sub_g2 in GI.getgeom(g2)
        !touches(g1, sub_g2) && return false
    end
    return true
end

Multi-geometry/geometry collections cross geometries

julia
#= Multi-geometry or a geometry collection touches a geometry if at least one
elements of the collection touches the geometry. =#
function _touches(
    ::Union{
        GI.MultiPointTrait, GI.AbstractMultiCurveTrait,
        GI.MultiPolygonTrait, GI.GeometryCollectionTrait,
    }, g1,
    ::GI.AbstractGeometryTrait, g2,
)
    for sub_g1 in GI.getgeom(g1)
        !touches(sub_g1, g2) && return false
    end
    return true
end

This page was generated using Literate.jl.