Skip to content

Crossing checks

julia
"""
     crosses(geom1, geom2)::Bool

Return `true` if the intersection results in a geometry whose dimension is one less than
the maximum dimension of the two source geometries and the intersection set is interior to
both source geometries.

TODO: broken

# Examples
```julia
import GeoInterface as GI, GeometryOps as GO

TODO: Add working example

julia
```
"""
crosses(g1, g2)::Bool = crosses(trait(g1), g1, trait(g2), g2)::Bool
crosses(t1::FeatureTrait, g1, t2, g2)::Bool = crosses(GI.geometry(g1), g2)
crosses(t1, g1, t2::FeatureTrait, g2)::Bool = crosses(g1, geometry(g2))
crosses(::MultiPointTrait, g1, ::LineStringTrait, g2)::Bool = multipoint_crosses_line(g1, g2)
crosses(::MultiPointTrait, g1, ::PolygonTrait, g2)::Bool = multipoint_crosses_poly(g1, g2)
crosses(::LineStringTrait, g1, ::MultiPointTrait, g2)::Bool = multipoint_crosses_lines(g2, g1)
crosses(::LineStringTrait, g1, ::PolygonTrait, g2)::Bool = line_crosses_poly(g1, g2)
crosses(::LineStringTrait, g1, ::LineStringTrait, g2)::Bool = line_crosses_line(g1, g2)
crosses(::PolygonTrait, g1, ::MultiPointTrait, g2)::Bool = multipoint_crosses_poly(g2, g1)
crosses(::PolygonTrait, g1, ::LineStringTrait, g2)::Bool = line_crosses_poly(g2, g1)

function multipoint_crosses_line(geom1, geom2)
    int_point = false
    ext_point = false
    i = 1
    np2 = GI.npoint(geom2)

    while i < GI.npoint(geom1) && !int_point && !ext_point
        for j in 1:GI.npoint(geom2) - 1
            exclude_boundary = (j === 1 || j === np2 - 2) ? :none : :both
            if _point_on_segment(GI.getpoint(geom1, i), (GI.getpoint(geom2, j), GI.getpoint(geom2, j + 1)); exclude_boundary)
                int_point = true
            else
                ext_point = true
            end
        end
        i += 1
    end
    return int_point && ext_point
end

function line_crosses_line(line1, line2)
    np2 = GI.npoint(line2)
    if GeometryOps.intersects(line1, line2)
        for i in 1:GI.npoint(line1) - 1
            for j in 1:GI.npoint(line2) - 1
                exclude_boundary = (j === 1 || j === np2 - 2) ? :none : :both
                pa = GI.getpoint(line1, i)
                pb = GI.getpoint(line1, i + 1)
                p = GI.getpoint(line2, j)
                _point_on_segment(p, (pa, pb); exclude_boundary) && return true
            end
        end
    end
    return false
end

function line_crosses_poly(line, poly)
    for l in flatten(AbstractCurveTrait, poly)
        intersects(line, l) && return true
    end
    return false
end

function multipoint_crosses_poly(mp, poly)
    int_point = false
    ext_point = false

    for p in GI.getpoint(mp)
        if _point_polygon_process(
            p, poly;
            in_allow = true, on_allow = true, out_allow = false, exact = _False()
        )
            int_point = true
        else
            ext_point = true
        end
        int_point && ext_point && return true
    end
    return false
end

#= TODO: Once crosses is swapped over to use the geom relations workflow, can
delete these helpers. =#

function _point_on_segment(point, (start, stop); exclude_boundary::Symbol=:none)::Bool
    x, y = GI.x(point), GI.y(point)
    x1, y1 = GI.x(start), GI.y(start)
    x2, y2 = GI.x(stop), GI.y(stop)

    dxc = x - x1
    dyc = y - y1
    dx1 = x2 - x1
    dy1 = y2 - y1

TODO use better predicate for crossing here

julia
    cross = dxc * dy1 - dyc * dx1
    cross != 0 && return false

Will constprop optimise these away?

julia
    if exclude_boundary === :none
        if abs(dx1) >= abs(dy1)
            return dx1 > 0 ? x1 <= x && x <= x2 : x2 <= x && x <= x1
        end
        return dy1 > 0 ? y1 <= y && y <= y2 : y2 <= y && y <= y1
    elseif exclude_boundary === :start
        if abs(dx1) >= abs(dy1)
             return dx1 > 0 ? x1 < x && x <= x2 : x2 <= x && x < x1
        end
        return dy1 > 0 ? y1 < y && y <= y2 : y2 <= y && y < y1
    elseif exclude_boundary === :end
        if abs(dx1) >= abs(dy1)
            return dx1 > 0 ? x1 <= x && x < x2 : x2 < x && x <= x1
        end
        return dy1 > 0 ? y1 <= y && y < y2 : y2 < y && y <= y1
    elseif exclude_boundary === :both
        if abs(dx1) >= abs(dy1)
            return dx1 > 0 ? x1 < x && x < x2 : x2 < x && x < x1
        end
        return dy1 > 0 ? y1 < y && y < y2 : y2 < y && y < y1
    end
    return false
end

This page was generated using Literate.jl.