containsLocationPoly static method

bool containsLocationPoly(
  1. Point<double> point,
  2. List<Point<double>> polygon, {
  3. bool geodesic = false,
})

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;
}