@trackunit/geo-json-utils 1.14.66 → 1.14.67-alpha-636ec415cd1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs.js CHANGED
@@ -29,6 +29,66 @@ const isPositionInsideRing = (point, ring) => {
29
29
  }
30
30
  return inside;
31
31
  };
32
+ /**
33
+ * Hole-aware point-in-polygon: true when the point is inside the exterior ring
34
+ * and outside every hole ring (RFC 7946 Section 3.1.6).
35
+ *
36
+ * This is an unvalidated, performance-optimized version of
37
+ * `isGeoJsonPointInPolygon` for use in hot rendering paths.
38
+ */
39
+ const isPointInPolygon = (point, polygon) => {
40
+ const [exteriorRing, ...holeRings] = polygon.coordinates;
41
+ if (!exteriorRing) {
42
+ return false;
43
+ }
44
+ if (!isPositionInsideRing(point, exteriorRing)) {
45
+ return false;
46
+ }
47
+ return !holeRings.some(hole => isPositionInsideRing(point, hole));
48
+ };
49
+ /**
50
+ * Whether every outer-ring vertex of `geom1` lies inside `geom2` (holes respected;
51
+ * for a MultiPolygon `geom2`, "inside" means inside any one member). A degenerate
52
+ * `geom1` with no outer rings is contained in nothing.
53
+ *
54
+ * This is an unvalidated, performance-optimized version of
55
+ * `isFullyContainedInGeoJsonGeometry` for use in hot rendering paths.
56
+ */
57
+ const isFullyContainedInGeometry = (geom1, geom2) => {
58
+ const outerRings1 = [];
59
+ if (geom1.type === "Polygon") {
60
+ const outerRing = geom1.coordinates[0];
61
+ if (outerRing)
62
+ outerRings1.push(outerRing);
63
+ }
64
+ else {
65
+ for (const poly of geom1.coordinates) {
66
+ const outerRing = poly[0];
67
+ if (outerRing)
68
+ outerRings1.push(outerRing);
69
+ }
70
+ }
71
+ if (outerRings1.length === 0) {
72
+ return false;
73
+ }
74
+ const polygonParts2 = [];
75
+ if (geom2.type === "Polygon") {
76
+ polygonParts2.push(geom2);
77
+ }
78
+ else {
79
+ for (const poly of geom2.coordinates) {
80
+ polygonParts2.push({ type: "Polygon", coordinates: poly });
81
+ }
82
+ }
83
+ for (const outerRing of outerRings1) {
84
+ for (const position of outerRing) {
85
+ if (!polygonParts2.some(part => isPointInPolygon(position, part))) {
86
+ return false;
87
+ }
88
+ }
89
+ }
90
+ return true;
91
+ };
32
92
  /**
33
93
  * Check if a bounding box is fully inside any polygon in a feature collection.
34
94
  * All four corners of the bbox must be inside the same polygon and outside
@@ -403,12 +463,14 @@ const geoJsonPolygonSchema = zod.z.object({
403
463
  });
404
464
  /**
405
465
  * MultiPolygon geometry object.
466
+ * Each member is a Polygon coordinate array and must have at least one ring
467
+ * (its exterior ring), per RFC 7946 Sections 3.1.6/3.1.7.
406
468
  * Foreign members (RFC 7946 Section 6.1) are stripped during parse.
407
469
  * https://tools.ietf.org/html/rfc7946#section-3.1.7
408
470
  */
409
471
  const geoJsonMultiPolygonSchema = zod.z.object({
410
472
  type: zod.z.literal("MultiPolygon"),
411
- coordinates: zod.z.array(zod.z.array(geoJsonLinearRingSchema)),
473
+ coordinates: zod.z.array(zod.z.array(geoJsonLinearRingSchema).min(1)),
412
474
  bbox: geoJsonBboxSchema.optional(),
413
475
  });
414
476
  /**
@@ -1170,37 +1232,15 @@ const isGeoJsonPositionInLinearRing = ({ position, linearRing, }) => {
1170
1232
  * @description Checks if a point is inside a polygon.
1171
1233
  * Correctly handles holes per RFC 7946 Section 3.1.6: the first ring is the
1172
1234
  * exterior boundary, subsequent rings are holes. A point inside a hole is
1173
- * considered outside the polygon.
1235
+ * considered outside the polygon. Validates, then delegates to the pure
1236
+ * `isPointInPolygon`.
1174
1237
  */
1175
1238
  const isGeoJsonPointInPolygon = ({ point, polygon, }) => {
1176
1239
  const polygonParsed = geoJsonPolygonSchema.safeParse(polygon);
1177
1240
  if (!polygonParsed.success) {
1178
1241
  return null;
1179
1242
  }
1180
- const [exteriorRing, ...holeRings] = polygonParsed.data.coordinates;
1181
- if (!exteriorRing) {
1182
- return null;
1183
- }
1184
- // Explicit null checks are intentional: isGeoJsonPositionInLinearRing returns
1185
- // boolean | null, and using it directly in boolean expressions like
1186
- // `!result` or `.some()` would silently treat null as false/truthy, masking errors.
1187
- const insideExterior = isGeoJsonPositionInLinearRing({ position: point.coordinates, linearRing: exteriorRing });
1188
- if (insideExterior === null) {
1189
- return null;
1190
- }
1191
- if (!insideExterior) {
1192
- return false;
1193
- }
1194
- for (const hole of holeRings) {
1195
- const insideHole = isGeoJsonPositionInLinearRing({ position: point.coordinates, linearRing: hole });
1196
- if (insideHole === null) {
1197
- return null;
1198
- }
1199
- if (insideHole) {
1200
- return false;
1201
- }
1202
- }
1203
- return true;
1243
+ return isPointInPolygon(point.coordinates, polygonParsed.data);
1204
1244
  };
1205
1245
  /**
1206
1246
  * Checks if polygon1 is fully contained within polygon2.
@@ -1423,63 +1463,37 @@ const unprojectPolygonalFromWebMercator = (geometry) => mapPolygonalCoordinates(
1423
1463
  * Generalisation of isFullyContainedInGeoJsonPolygon to Polygon|MultiPolygon arguments.
1424
1464
  * Returns true when every outer-ring vertex of geom1 lies inside geom2 (holes respected).
1425
1465
  * For a MultiPolygon geom2, "inside" means inside any one of its polygon members.
1426
- * Returns null if either geometry fails validation.
1466
+ * Returns null if either geometry fails validation. Once validated, delegates to
1467
+ * the pure `isFullyContainedInGeometry`.
1427
1468
  */
1428
1469
  const isFullyContainedInGeoJsonGeometry = (geom1, geom2) => {
1429
- const outerRings1 = [];
1470
+ let validGeom1;
1430
1471
  if (geom1.type === "Polygon") {
1431
1472
  const parsed = geoJsonPolygonSchema.safeParse(geom1);
1432
1473
  if (!parsed.success)
1433
1474
  return null;
1434
- const outerRing = parsed.data.coordinates[0];
1435
- if (!outerRing)
1436
- return null;
1437
- outerRings1.push(outerRing);
1475
+ validGeom1 = parsed.data;
1438
1476
  }
1439
1477
  else {
1440
1478
  const parsed = geoJsonMultiPolygonSchema.safeParse(geom1);
1441
1479
  if (!parsed.success)
1442
1480
  return null;
1443
- for (const poly of parsed.data.coordinates) {
1444
- const outerRing = poly[0];
1445
- if (!outerRing)
1446
- return null;
1447
- outerRings1.push(outerRing);
1448
- }
1481
+ validGeom1 = parsed.data;
1449
1482
  }
1450
- const polygonParts2 = [];
1483
+ let validGeom2;
1451
1484
  if (geom2.type === "Polygon") {
1452
1485
  const parsed = geoJsonPolygonSchema.safeParse(geom2);
1453
1486
  if (!parsed.success)
1454
1487
  return null;
1455
- polygonParts2.push(parsed.data);
1488
+ validGeom2 = parsed.data;
1456
1489
  }
1457
1490
  else {
1458
1491
  const parsed = geoJsonMultiPolygonSchema.safeParse(geom2);
1459
1492
  if (!parsed.success)
1460
1493
  return null;
1461
- for (const poly of parsed.data.coordinates) {
1462
- polygonParts2.push({ type: "Polygon", coordinates: poly });
1463
- }
1494
+ validGeom2 = parsed.data;
1464
1495
  }
1465
- for (const outerRing of outerRings1) {
1466
- for (const position of outerRing) {
1467
- const point = { coordinates: position };
1468
- let insideAny = false;
1469
- for (const part of polygonParts2) {
1470
- const result = isGeoJsonPointInPolygon({ point, polygon: part });
1471
- if (result === null)
1472
- return null;
1473
- if (result) {
1474
- insideAny = true;
1475
- break;
1476
- }
1477
- }
1478
- if (!insideAny)
1479
- return false;
1480
- }
1481
- }
1482
- return true;
1496
+ return isFullyContainedInGeometry(validGeom1, validGeom2);
1483
1497
  };
1484
1498
  /**
1485
1499
  * Returns the minimum distance (in coordinate units) from the given position to the
@@ -1919,8 +1933,10 @@ exports.getPolygonFromPointAndRadius = getPolygonFromPointAndRadius;
1919
1933
  exports.isBboxInsideFeatureCollection = isBboxInsideFeatureCollection;
1920
1934
  exports.isFullyContainedInGeoJsonGeometry = isFullyContainedInGeoJsonGeometry;
1921
1935
  exports.isFullyContainedInGeoJsonPolygon = isFullyContainedInGeoJsonPolygon;
1936
+ exports.isFullyContainedInGeometry = isFullyContainedInGeometry;
1922
1937
  exports.isGeoJsonPointInPolygon = isGeoJsonPointInPolygon;
1923
1938
  exports.isGeoJsonPositionInLinearRing = isGeoJsonPositionInLinearRing;
1939
+ exports.isPointInPolygon = isPointInPolygon;
1924
1940
  exports.isPositionInsideRing = isPositionInsideRing;
1925
1941
  exports.lngLatToMercatorPxWS = lngLatToMercatorPxWS;
1926
1942
  exports.lngLatToWebMercatorPx = lngLatToWebMercatorPx;
package/index.esm.js CHANGED
@@ -27,6 +27,66 @@ const isPositionInsideRing = (point, ring) => {
27
27
  }
28
28
  return inside;
29
29
  };
30
+ /**
31
+ * Hole-aware point-in-polygon: true when the point is inside the exterior ring
32
+ * and outside every hole ring (RFC 7946 Section 3.1.6).
33
+ *
34
+ * This is an unvalidated, performance-optimized version of
35
+ * `isGeoJsonPointInPolygon` for use in hot rendering paths.
36
+ */
37
+ const isPointInPolygon = (point, polygon) => {
38
+ const [exteriorRing, ...holeRings] = polygon.coordinates;
39
+ if (!exteriorRing) {
40
+ return false;
41
+ }
42
+ if (!isPositionInsideRing(point, exteriorRing)) {
43
+ return false;
44
+ }
45
+ return !holeRings.some(hole => isPositionInsideRing(point, hole));
46
+ };
47
+ /**
48
+ * Whether every outer-ring vertex of `geom1` lies inside `geom2` (holes respected;
49
+ * for a MultiPolygon `geom2`, "inside" means inside any one member). A degenerate
50
+ * `geom1` with no outer rings is contained in nothing.
51
+ *
52
+ * This is an unvalidated, performance-optimized version of
53
+ * `isFullyContainedInGeoJsonGeometry` for use in hot rendering paths.
54
+ */
55
+ const isFullyContainedInGeometry = (geom1, geom2) => {
56
+ const outerRings1 = [];
57
+ if (geom1.type === "Polygon") {
58
+ const outerRing = geom1.coordinates[0];
59
+ if (outerRing)
60
+ outerRings1.push(outerRing);
61
+ }
62
+ else {
63
+ for (const poly of geom1.coordinates) {
64
+ const outerRing = poly[0];
65
+ if (outerRing)
66
+ outerRings1.push(outerRing);
67
+ }
68
+ }
69
+ if (outerRings1.length === 0) {
70
+ return false;
71
+ }
72
+ const polygonParts2 = [];
73
+ if (geom2.type === "Polygon") {
74
+ polygonParts2.push(geom2);
75
+ }
76
+ else {
77
+ for (const poly of geom2.coordinates) {
78
+ polygonParts2.push({ type: "Polygon", coordinates: poly });
79
+ }
80
+ }
81
+ for (const outerRing of outerRings1) {
82
+ for (const position of outerRing) {
83
+ if (!polygonParts2.some(part => isPointInPolygon(position, part))) {
84
+ return false;
85
+ }
86
+ }
87
+ }
88
+ return true;
89
+ };
30
90
  /**
31
91
  * Check if a bounding box is fully inside any polygon in a feature collection.
32
92
  * All four corners of the bbox must be inside the same polygon and outside
@@ -401,12 +461,14 @@ const geoJsonPolygonSchema = z.object({
401
461
  });
402
462
  /**
403
463
  * MultiPolygon geometry object.
464
+ * Each member is a Polygon coordinate array and must have at least one ring
465
+ * (its exterior ring), per RFC 7946 Sections 3.1.6/3.1.7.
404
466
  * Foreign members (RFC 7946 Section 6.1) are stripped during parse.
405
467
  * https://tools.ietf.org/html/rfc7946#section-3.1.7
406
468
  */
407
469
  const geoJsonMultiPolygonSchema = z.object({
408
470
  type: z.literal("MultiPolygon"),
409
- coordinates: z.array(z.array(geoJsonLinearRingSchema)),
471
+ coordinates: z.array(z.array(geoJsonLinearRingSchema).min(1)),
410
472
  bbox: geoJsonBboxSchema.optional(),
411
473
  });
412
474
  /**
@@ -1168,37 +1230,15 @@ const isGeoJsonPositionInLinearRing = ({ position, linearRing, }) => {
1168
1230
  * @description Checks if a point is inside a polygon.
1169
1231
  * Correctly handles holes per RFC 7946 Section 3.1.6: the first ring is the
1170
1232
  * exterior boundary, subsequent rings are holes. A point inside a hole is
1171
- * considered outside the polygon.
1233
+ * considered outside the polygon. Validates, then delegates to the pure
1234
+ * `isPointInPolygon`.
1172
1235
  */
1173
1236
  const isGeoJsonPointInPolygon = ({ point, polygon, }) => {
1174
1237
  const polygonParsed = geoJsonPolygonSchema.safeParse(polygon);
1175
1238
  if (!polygonParsed.success) {
1176
1239
  return null;
1177
1240
  }
1178
- const [exteriorRing, ...holeRings] = polygonParsed.data.coordinates;
1179
- if (!exteriorRing) {
1180
- return null;
1181
- }
1182
- // Explicit null checks are intentional: isGeoJsonPositionInLinearRing returns
1183
- // boolean | null, and using it directly in boolean expressions like
1184
- // `!result` or `.some()` would silently treat null as false/truthy, masking errors.
1185
- const insideExterior = isGeoJsonPositionInLinearRing({ position: point.coordinates, linearRing: exteriorRing });
1186
- if (insideExterior === null) {
1187
- return null;
1188
- }
1189
- if (!insideExterior) {
1190
- return false;
1191
- }
1192
- for (const hole of holeRings) {
1193
- const insideHole = isGeoJsonPositionInLinearRing({ position: point.coordinates, linearRing: hole });
1194
- if (insideHole === null) {
1195
- return null;
1196
- }
1197
- if (insideHole) {
1198
- return false;
1199
- }
1200
- }
1201
- return true;
1241
+ return isPointInPolygon(point.coordinates, polygonParsed.data);
1202
1242
  };
1203
1243
  /**
1204
1244
  * Checks if polygon1 is fully contained within polygon2.
@@ -1421,63 +1461,37 @@ const unprojectPolygonalFromWebMercator = (geometry) => mapPolygonalCoordinates(
1421
1461
  * Generalisation of isFullyContainedInGeoJsonPolygon to Polygon|MultiPolygon arguments.
1422
1462
  * Returns true when every outer-ring vertex of geom1 lies inside geom2 (holes respected).
1423
1463
  * For a MultiPolygon geom2, "inside" means inside any one of its polygon members.
1424
- * Returns null if either geometry fails validation.
1464
+ * Returns null if either geometry fails validation. Once validated, delegates to
1465
+ * the pure `isFullyContainedInGeometry`.
1425
1466
  */
1426
1467
  const isFullyContainedInGeoJsonGeometry = (geom1, geom2) => {
1427
- const outerRings1 = [];
1468
+ let validGeom1;
1428
1469
  if (geom1.type === "Polygon") {
1429
1470
  const parsed = geoJsonPolygonSchema.safeParse(geom1);
1430
1471
  if (!parsed.success)
1431
1472
  return null;
1432
- const outerRing = parsed.data.coordinates[0];
1433
- if (!outerRing)
1434
- return null;
1435
- outerRings1.push(outerRing);
1473
+ validGeom1 = parsed.data;
1436
1474
  }
1437
1475
  else {
1438
1476
  const parsed = geoJsonMultiPolygonSchema.safeParse(geom1);
1439
1477
  if (!parsed.success)
1440
1478
  return null;
1441
- for (const poly of parsed.data.coordinates) {
1442
- const outerRing = poly[0];
1443
- if (!outerRing)
1444
- return null;
1445
- outerRings1.push(outerRing);
1446
- }
1479
+ validGeom1 = parsed.data;
1447
1480
  }
1448
- const polygonParts2 = [];
1481
+ let validGeom2;
1449
1482
  if (geom2.type === "Polygon") {
1450
1483
  const parsed = geoJsonPolygonSchema.safeParse(geom2);
1451
1484
  if (!parsed.success)
1452
1485
  return null;
1453
- polygonParts2.push(parsed.data);
1486
+ validGeom2 = parsed.data;
1454
1487
  }
1455
1488
  else {
1456
1489
  const parsed = geoJsonMultiPolygonSchema.safeParse(geom2);
1457
1490
  if (!parsed.success)
1458
1491
  return null;
1459
- for (const poly of parsed.data.coordinates) {
1460
- polygonParts2.push({ type: "Polygon", coordinates: poly });
1461
- }
1492
+ validGeom2 = parsed.data;
1462
1493
  }
1463
- for (const outerRing of outerRings1) {
1464
- for (const position of outerRing) {
1465
- const point = { coordinates: position };
1466
- let insideAny = false;
1467
- for (const part of polygonParts2) {
1468
- const result = isGeoJsonPointInPolygon({ point, polygon: part });
1469
- if (result === null)
1470
- return null;
1471
- if (result) {
1472
- insideAny = true;
1473
- break;
1474
- }
1475
- }
1476
- if (!insideAny)
1477
- return false;
1478
- }
1479
- }
1480
- return true;
1494
+ return isFullyContainedInGeometry(validGeom1, validGeom2);
1481
1495
  };
1482
1496
  /**
1483
1497
  * Returns the minimum distance (in coordinate units) from the given position to the
@@ -1875,4 +1889,4 @@ const edgePixelLength = (x0, y0, x1, y1, zoom, midLatDeg, tileSize = 256) => {
1875
1889
  return Math.sqrt(dxPx * dxPx + dyPx * dyPx);
1876
1890
  };
1877
1891
 
1878
- export { EARTH_RADIUS, EMPTY_FEATURE_COLLECTION, boundingBoxCrossesMeridian, checkCrossesMeridian, computeGeometryCentroid, coordinatesToStandardFormat, denormalizeLongitude, distanceToGeoJsonPolygonBoundary, edgePixelLength, extractEdges, extractFirstPointCoordinate, extractPositionsFromGeometry, geoJsonBboxSchema, geoJsonFeatureCollectionSchema, geoJsonFeatureSchema, geoJsonGeometryCollectionSchema, geoJsonGeometrySchema, geoJsonLineStringSchema, geoJsonLinearRingSchema, geoJsonMultiLineStringSchema, geoJsonMultiPointSchema, geoJsonMultiPolygonSchema, geoJsonPointSchema, geoJsonPolygonDifference, geoJsonPolygonSchema, geoJsonPosition2dSchema, geoJsonPositionSchema, getBboxFromGeoJsonPolygon, getBoundingBoxFromGeoJsonBbox, getBoundingBoxFromGeoJsonPolygon, getExtremeGeoJsonPointFromPolygon, getGeoJsonPolygonFromBoundingBox, getGeoJsonPolygonIntersection, getMinMaxLongitudes, getMultipleCoordinatesFromGeoJsonObject, getPointCoordinateFromGeoJsonObject, getPointCoordinateFromGeoJsonPoint, getPolygonFromBbox, getPolygonFromPointAndRadius, isBboxInsideFeatureCollection, isFullyContainedInGeoJsonGeometry, isFullyContainedInGeoJsonPolygon, isGeoJsonPointInPolygon, isGeoJsonPositionInLinearRing, isPositionInsideRing, lngLatToMercatorPxWS, lngLatToWebMercatorPx, mercatorPxToLngLatWS, normalizeLongitudes, padGeoJsonBbox, pixelsToLatDegrees, projectLngLatToWebMercator, projectPolygonalToWebMercator, scaleGeoJsonBbox, splitPolygonAtAntimeridian, splitPolygonWithHolesAtAntimeridian, toFeatureCollection, toPosition2d, tuGeoJsonPointRadiusSchema, tuGeoJsonPolygonNoHolesSchema, tuGeoJsonRectangularBoxPolygonSchema, unprojectPolygonalFromWebMercator, unprojectWebMercatorToLngLat, validateBbox, validateBboxWithFallback, validateFeatureCollection, validatePosition, webMercatorPxToLngLat };
1892
+ export { EARTH_RADIUS, EMPTY_FEATURE_COLLECTION, boundingBoxCrossesMeridian, checkCrossesMeridian, computeGeometryCentroid, coordinatesToStandardFormat, denormalizeLongitude, distanceToGeoJsonPolygonBoundary, edgePixelLength, extractEdges, extractFirstPointCoordinate, extractPositionsFromGeometry, geoJsonBboxSchema, geoJsonFeatureCollectionSchema, geoJsonFeatureSchema, geoJsonGeometryCollectionSchema, geoJsonGeometrySchema, geoJsonLineStringSchema, geoJsonLinearRingSchema, geoJsonMultiLineStringSchema, geoJsonMultiPointSchema, geoJsonMultiPolygonSchema, geoJsonPointSchema, geoJsonPolygonDifference, geoJsonPolygonSchema, geoJsonPosition2dSchema, geoJsonPositionSchema, getBboxFromGeoJsonPolygon, getBoundingBoxFromGeoJsonBbox, getBoundingBoxFromGeoJsonPolygon, getExtremeGeoJsonPointFromPolygon, getGeoJsonPolygonFromBoundingBox, getGeoJsonPolygonIntersection, getMinMaxLongitudes, getMultipleCoordinatesFromGeoJsonObject, getPointCoordinateFromGeoJsonObject, getPointCoordinateFromGeoJsonPoint, getPolygonFromBbox, getPolygonFromPointAndRadius, isBboxInsideFeatureCollection, isFullyContainedInGeoJsonGeometry, isFullyContainedInGeoJsonPolygon, isFullyContainedInGeometry, isGeoJsonPointInPolygon, isGeoJsonPositionInLinearRing, isPointInPolygon, isPositionInsideRing, lngLatToMercatorPxWS, lngLatToWebMercatorPx, mercatorPxToLngLatWS, normalizeLongitudes, padGeoJsonBbox, pixelsToLatDegrees, projectLngLatToWebMercator, projectPolygonalToWebMercator, scaleGeoJsonBbox, splitPolygonAtAntimeridian, splitPolygonWithHolesAtAntimeridian, toFeatureCollection, toPosition2d, tuGeoJsonPointRadiusSchema, tuGeoJsonPolygonNoHolesSchema, tuGeoJsonRectangularBoxPolygonSchema, unprojectPolygonalFromWebMercator, unprojectWebMercatorToLngLat, validateBbox, validateBboxWithFallback, validateFeatureCollection, validatePosition, webMercatorPxToLngLat };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/geo-json-utils",
3
- "version": "1.14.66",
3
+ "version": "1.14.67-alpha-636ec415cd1.0",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -1,4 +1,4 @@
1
- import type { GeoJsonBbox, GeoJsonFeatureCollection, GeoJsonGeometry, GeoJsonPosition } from "./GeoJsonSchemas";
1
+ import type { GeoJsonBbox, GeoJsonFeatureCollection, GeoJsonGeometry, GeoJsonMultiPolygon, GeoJsonPolygon, GeoJsonPosition } from "./GeoJsonSchemas";
2
2
  type Edge = readonly [GeoJsonPosition, GeoJsonPosition];
3
3
  /**
4
4
  * Ray-casting point-in-polygon test for a single coordinate ring.
@@ -8,6 +8,23 @@ type Edge = readonly [GeoJsonPosition, GeoJsonPosition];
8
8
  * `isGeoJsonPositionInLinearRing` for use in hot rendering paths.
9
9
  */
10
10
  export declare const isPositionInsideRing: (point: GeoJsonPosition, ring: ReadonlyArray<GeoJsonPosition>) => boolean;
11
+ /**
12
+ * Hole-aware point-in-polygon: true when the point is inside the exterior ring
13
+ * and outside every hole ring (RFC 7946 Section 3.1.6).
14
+ *
15
+ * This is an unvalidated, performance-optimized version of
16
+ * `isGeoJsonPointInPolygon` for use in hot rendering paths.
17
+ */
18
+ export declare const isPointInPolygon: (point: GeoJsonPosition, polygon: GeoJsonPolygon) => boolean;
19
+ /**
20
+ * Whether every outer-ring vertex of `geom1` lies inside `geom2` (holes respected;
21
+ * for a MultiPolygon `geom2`, "inside" means inside any one member). A degenerate
22
+ * `geom1` with no outer rings is contained in nothing.
23
+ *
24
+ * This is an unvalidated, performance-optimized version of
25
+ * `isFullyContainedInGeoJsonGeometry` for use in hot rendering paths.
26
+ */
27
+ export declare const isFullyContainedInGeometry: (geom1: GeoJsonPolygon | GeoJsonMultiPolygon, geom2: GeoJsonPolygon | GeoJsonMultiPolygon) => boolean;
11
28
  /**
12
29
  * Check if a bounding box is fully inside any polygon in a feature collection.
13
30
  * All four corners of the bbox must be inside the same polygon and outside
@@ -140,6 +140,8 @@ export declare const geoJsonPolygonSchema: z.ZodObject<{
140
140
  export type GeoJsonPolygon = z.infer<typeof geoJsonPolygonSchema>;
141
141
  /**
142
142
  * MultiPolygon geometry object.
143
+ * Each member is a Polygon coordinate array and must have at least one ring
144
+ * (its exterior ring), per RFC 7946 Sections 3.1.6/3.1.7.
143
145
  * Foreign members (RFC 7946 Section 6.1) are stripped during parse.
144
146
  * https://tools.ietf.org/html/rfc7946#section-3.1.7
145
147
  */
@@ -84,7 +84,8 @@ export declare const isGeoJsonPositionInLinearRing: ({ position, linearRing, }:
84
84
  * @description Checks if a point is inside a polygon.
85
85
  * Correctly handles holes per RFC 7946 Section 3.1.6: the first ring is the
86
86
  * exterior boundary, subsequent rings are holes. A point inside a hole is
87
- * considered outside the polygon.
87
+ * considered outside the polygon. Validates, then delegates to the pure
88
+ * `isPointInPolygon`.
88
89
  */
89
90
  export declare const isGeoJsonPointInPolygon: ({ point, polygon, }: {
90
91
  point: GeoJsonPoint;
@@ -167,7 +168,8 @@ export declare const unprojectPolygonalFromWebMercator: (geometry: GeoJsonPolygo
167
168
  * Generalisation of isFullyContainedInGeoJsonPolygon to Polygon|MultiPolygon arguments.
168
169
  * Returns true when every outer-ring vertex of geom1 lies inside geom2 (holes respected).
169
170
  * For a MultiPolygon geom2, "inside" means inside any one of its polygon members.
170
- * Returns null if either geometry fails validation.
171
+ * Returns null if either geometry fails validation. Once validated, delegates to
172
+ * the pure `isFullyContainedInGeometry`.
171
173
  */
172
174
  export declare const isFullyContainedInGeoJsonGeometry: (geom1: GeoJsonPolygon | GeoJsonMultiPolygon, geom2: GeoJsonPolygon | GeoJsonMultiPolygon) => boolean | null;
173
175
  /**