containsLocationPoly static method
Checks whether point is inside polygon on the sphere.
A faithful port of android-maps-utils' PolyUtil.containsLocation. It casts
a meridian ray from the point to a pole and counts edge crossings, so it is
correct for polygons of any size — including ones that cross the
antimeridian (±180° longitude) or enclose a pole. "Inside" is the region
that does not contain the South Pole.
Edges are treated as great-circle (geodesic) segments when geodesic is
true, and as Rhumb (constant-bearing) segments otherwise. The default
(geodesic: false) matches how polygons are usually drawn on a Mercator map
and the planar behaviour of earlier versions for small areas.
The polygon is implicitly closed, so the closing point may be omitted or
repeated. A point coinciding with a vertex is considered inside; the
result for a point lying exactly on an edge is not defined.
Coordinates follow this package's convention: Point.x is latitude and
Point.y is longitude, in degrees.
Implementation
static bool containsLocationPoly(
Point<double> point,
List<Point<double>> polygon, {
bool geodesic = false,
}) {
final size = polygon.length;
if (size == 0) return false;
final lat3 = SphericalUtils.toRadians(point.x);
final lng3 = SphericalUtils.toRadians(point.y);
final prev = polygon[size - 1];
double lat1 = SphericalUtils.toRadians(prev.x);
double lng1 = SphericalUtils.toRadians(prev.y);
int nIntersect = 0;
for (final point2 in polygon) {
final dLng3 = MathUtils.wrap(lng3 - lng1, -pi, pi);
// Special case: point equal to a vertex is inside.
if (lat3 == lat1 && dLng3 == 0) return true;
final lat2 = SphericalUtils.toRadians(point2.x);
final lng2 = SphericalUtils.toRadians(point2.y);
// Offset longitudes by -lng1.
if (_intersects(lat1, lat2, MathUtils.wrap(lng2 - lng1, -pi, pi), lat3,
dLng3, geodesic)) {
nIntersect++;
}
lat1 = lat2;
lng1 = lng2;
}
return (nIntersect & 1) != 0;
}