geokit/point_in_polygon
Point-in-polygon test for a Geometry
— the operation Turf.js calls booleanPointInPolygon.
The first ring of a Polygon is its exterior and every further
ring is a hole, so a point inside a hole is outside the polygon.
A MultiPolygon contains a point when any of its polygons does.
Rings may be closed (first point repeated at the end) or open —
an open ring is closed implicitly — and may run in either
direction.
A point exactly on a ring — on an edge or a vertex, of the
exterior or of a hole — is on the polygon’s boundary.
contains counts the boundary as inside, like
Turf’s default. locate reports it separately as
OnBoundary, so Turf’s ignoreBoundary: true is
locate(...) == Ok(Inside).
The test is planar ray casting, as in Turf: each edge is the
straight segment between its two vertices on the flat lng/lat
plane, not a great-circle arc. For polygons spanning many degrees
an edge therefore does not follow the shortest path between its
vertices. The antimeridian is not special-cased: a ring with
vertices at lng 170 and lng -170 encloses the wide band through
lng 0, and lng 180 and lng -180 are different points. Split a
shape that crosses the antimeridian into a MultiPolygon at ±180°,
as RFC 7946 §3.1.9 recommends.
Coordinates are only subtracted and multiplied, never divided. On whole-degree coordinates the answer is therefore exact, points on a ring included; elsewhere a point within rounding error of an edge may be reported on either side of it.
Types
Where a point lies relative to a polygon, as reported by
locate.
pub type Location {
Inside
OnBoundary
Outside
}
Constructors
-
InsideInside the exterior ring and outside every hole, not touching any ring.
-
OnBoundaryOn an edge or a vertex of the exterior ring or of a hole.
-
OutsideOutside the exterior ring, or inside a hole.
Values
pub fn contains(
geometry geometry: geometry.Geometry,
point point: latlng.LatLng,
) -> Result(Bool, PointInPolygonError)
Whether point lies inside geometry, a Polygon or
MultiPolygon. A point on the boundary — an edge or a vertex of
any ring — counts as inside, like Turf’s booleanPointInPolygon
default. A point inside a hole is outside. Use
locate to tell boundary points apart.
import geokit/geometry
import geokit/latlng
import geokit/point_in_polygon
let assert Ok(a) = latlng.new(lat: 0.0, lng: 0.0)
let assert Ok(b) = latlng.new(lat: 0.0, lng: 10.0)
let assert Ok(c) = latlng.new(lat: 10.0, lng: 10.0)
let assert Ok(d) = latlng.new(lat: 10.0, lng: 0.0)
let assert Ok(p) = latlng.new(lat: 5.0, lng: 5.0)
point_in_polygon.contains(
geometry: geometry.Polygon([[a, b, c, d, a]]),
point: p,
)
// == Ok(True)
Any other geometry returns NotAPolygon.
A polygon with no rings, or whose rings are empty, contains no
point.
pub fn locate(
geometry geometry: geometry.Geometry,
point point: latlng.LatLng,
) -> Result(Location, PointInPolygonError)
Where point lies relative to geometry, a Polygon or
MultiPolygon: Inside, OnBoundary, or Outside.
import geokit/geometry
import geokit/latlng
import geokit/point_in_polygon
let assert Ok(a) = latlng.new(lat: 0.0, lng: 0.0)
let assert Ok(b) = latlng.new(lat: 0.0, lng: 10.0)
let assert Ok(c) = latlng.new(lat: 10.0, lng: 10.0)
let assert Ok(d) = latlng.new(lat: 10.0, lng: 0.0)
let assert Ok(on_edge) = latlng.new(lat: 0.0, lng: 5.0)
point_in_polygon.locate(
geometry: geometry.Polygon([[a, b, c, d, a]]),
point: on_edge,
)
// == Ok(point_in_polygon.OnBoundary)
For a MultiPolygon the answer is Inside when any member
polygon has the point inside, otherwise OnBoundary when any
member has it on its boundary, otherwise Outside. Any other
geometry returns NotAPolygon.