@trackunit/geo-json-utils 1.15.42 → 1.15.45

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
@@ -1547,6 +1547,48 @@ const padGeoJsonBbox = (bbox, padding) => {
1547
1547
  const [west, east] = getBboxLongitudesFromUnwrappedRange({ west: paddedWest, east: paddedEast });
1548
1548
  return [west, clampedSouth, east, clampedNorth];
1549
1549
  };
1550
+ /**
1551
+ * Expands any bbox axis narrower than `minSpanDegrees` to that span around the axis midpoint.
1552
+ * A single point (or a set of co-located points) produces a zero-area bbox, which
1553
+ * `geoJsonBboxSchema` rejects — north must be *strictly* greater than south (RFC 7946 §5.2) —
1554
+ * so map `fitBounds` calls silently no-op on it. Latitudes are clamped to ±90 after padding,
1555
+ * shifting the window back inside the valid range so the span is preserved even at the poles.
1556
+ * Longitudes use the unwrapped span (so a narrow antimeridian-crossing strip is padded too)
1557
+ * and are wrapped into `[-180, 180]` the same way as {@link padGeoJsonBbox}.
1558
+ *
1559
+ * Wide bboxes are returned as-is (same reference).
1560
+ */
1561
+ const ensureMinimumGeoJsonBboxSpan = (bbox, minSpanDegrees) => {
1562
+ const [, minLat, , maxLat] = bbox;
1563
+ const { west: unwrappedWest, east: unwrappedEast, span: lngSpan } = getUnwrappedBboxLongitudeRange(bbox);
1564
+ const lngSpanTooSmall = lngSpan < minSpanDegrees;
1565
+ const latSpanTooSmall = maxLat - minLat < minSpanDegrees;
1566
+ if (!lngSpanTooSmall && !latSpanTooSmall) {
1567
+ return bbox;
1568
+ }
1569
+ const half = minSpanDegrees / 2;
1570
+ let [south, north] = [minLat, maxLat];
1571
+ if (latSpanTooSmall) {
1572
+ const midLat = (minLat + maxLat) / 2;
1573
+ south = midLat - half;
1574
+ north = midLat + half;
1575
+ if (north > MAX_LAT) {
1576
+ south -= north - MAX_LAT;
1577
+ north = MAX_LAT;
1578
+ }
1579
+ if (south < MIN_LAT) {
1580
+ north += MIN_LAT - south;
1581
+ south = MIN_LAT;
1582
+ north = Math.min(north, MAX_LAT);
1583
+ }
1584
+ }
1585
+ if (!lngSpanTooSmall) {
1586
+ return [bbox[0], south, bbox[2], north];
1587
+ }
1588
+ const midLng = (unwrappedWest + unwrappedEast) / 2;
1589
+ const [west, east] = getBboxLongitudesFromUnwrappedRange({ west: midLng - half, east: midLng + half });
1590
+ return [west, south, east, north];
1591
+ };
1550
1592
  /**
1551
1593
  * Computes the difference of subject minus the union of all clips.
1552
1594
  * Returns null when the subject is fully covered (zero remaining area).
@@ -2067,6 +2109,7 @@ exports.coordinatesToStandardFormat = coordinatesToStandardFormat;
2067
2109
  exports.denormalizeLongitude = denormalizeLongitude;
2068
2110
  exports.distanceToGeoJsonPolygonBoundary = distanceToGeoJsonPolygonBoundary;
2069
2111
  exports.edgePixelLength = edgePixelLength;
2112
+ exports.ensureMinimumGeoJsonBboxSpan = ensureMinimumGeoJsonBboxSpan;
2070
2113
  exports.extractEdges = extractEdges;
2071
2114
  exports.extractFirstPointCoordinate = extractFirstPointCoordinate;
2072
2115
  exports.extractPositionsFromGeometry = extractPositionsFromGeometry;
package/index.esm.js CHANGED
@@ -1545,6 +1545,48 @@ const padGeoJsonBbox = (bbox, padding) => {
1545
1545
  const [west, east] = getBboxLongitudesFromUnwrappedRange({ west: paddedWest, east: paddedEast });
1546
1546
  return [west, clampedSouth, east, clampedNorth];
1547
1547
  };
1548
+ /**
1549
+ * Expands any bbox axis narrower than `minSpanDegrees` to that span around the axis midpoint.
1550
+ * A single point (or a set of co-located points) produces a zero-area bbox, which
1551
+ * `geoJsonBboxSchema` rejects — north must be *strictly* greater than south (RFC 7946 §5.2) —
1552
+ * so map `fitBounds` calls silently no-op on it. Latitudes are clamped to ±90 after padding,
1553
+ * shifting the window back inside the valid range so the span is preserved even at the poles.
1554
+ * Longitudes use the unwrapped span (so a narrow antimeridian-crossing strip is padded too)
1555
+ * and are wrapped into `[-180, 180]` the same way as {@link padGeoJsonBbox}.
1556
+ *
1557
+ * Wide bboxes are returned as-is (same reference).
1558
+ */
1559
+ const ensureMinimumGeoJsonBboxSpan = (bbox, minSpanDegrees) => {
1560
+ const [, minLat, , maxLat] = bbox;
1561
+ const { west: unwrappedWest, east: unwrappedEast, span: lngSpan } = getUnwrappedBboxLongitudeRange(bbox);
1562
+ const lngSpanTooSmall = lngSpan < minSpanDegrees;
1563
+ const latSpanTooSmall = maxLat - minLat < minSpanDegrees;
1564
+ if (!lngSpanTooSmall && !latSpanTooSmall) {
1565
+ return bbox;
1566
+ }
1567
+ const half = minSpanDegrees / 2;
1568
+ let [south, north] = [minLat, maxLat];
1569
+ if (latSpanTooSmall) {
1570
+ const midLat = (minLat + maxLat) / 2;
1571
+ south = midLat - half;
1572
+ north = midLat + half;
1573
+ if (north > MAX_LAT) {
1574
+ south -= north - MAX_LAT;
1575
+ north = MAX_LAT;
1576
+ }
1577
+ if (south < MIN_LAT) {
1578
+ north += MIN_LAT - south;
1579
+ south = MIN_LAT;
1580
+ north = Math.min(north, MAX_LAT);
1581
+ }
1582
+ }
1583
+ if (!lngSpanTooSmall) {
1584
+ return [bbox[0], south, bbox[2], north];
1585
+ }
1586
+ const midLng = (unwrappedWest + unwrappedEast) / 2;
1587
+ const [west, east] = getBboxLongitudesFromUnwrappedRange({ west: midLng - half, east: midLng + half });
1588
+ return [west, south, east, north];
1589
+ };
1548
1590
  /**
1549
1591
  * Computes the difference of subject minus the union of all clips.
1550
1592
  * Returns null when the subject is fully covered (zero remaining area).
@@ -2056,4 +2098,4 @@ const edgePixelLength = (x0, y0, x1, y1, zoom, midLatDeg, tileSize = 256) => {
2056
2098
  return Math.sqrt(dxPx * dxPx + dyPx * dyPx);
2057
2099
  };
2058
2100
 
2059
- 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, splitAntimeridianCrossingFeatures, splitAntimeridianCrossingGeometry, splitPolygonAtAntimeridian, splitPolygonWithHolesAtAntimeridian, toFeatureCollection, toPosition2d, tuGeoJsonPointRadiusSchema, tuGeoJsonPolygonNoHolesSchema, tuGeoJsonRectangularBoxPolygonSchema, unprojectPolygonalFromWebMercator, unprojectWebMercatorToLngLat, validateBbox, validateBboxWithFallback, validateFeatureCollection, validatePosition, webMercatorPxToLngLat };
2101
+ export { EARTH_RADIUS, EMPTY_FEATURE_COLLECTION, boundingBoxCrossesMeridian, checkCrossesMeridian, computeGeometryCentroid, coordinatesToStandardFormat, denormalizeLongitude, distanceToGeoJsonPolygonBoundary, edgePixelLength, ensureMinimumGeoJsonBboxSpan, 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, splitAntimeridianCrossingFeatures, splitAntimeridianCrossingGeometry, 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.15.42",
3
+ "version": "1.15.45",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -161,6 +161,18 @@ export type BboxPadding = number | {
161
161
  * Latitudes are clamped to `[-90, 90]`, longitudes to `[-180, 180]`.
162
162
  */
163
163
  export declare const padGeoJsonBbox: (bbox: GeoJsonBbox, padding: BboxPadding) => GeoJsonBbox;
164
+ /**
165
+ * Expands any bbox axis narrower than `minSpanDegrees` to that span around the axis midpoint.
166
+ * A single point (or a set of co-located points) produces a zero-area bbox, which
167
+ * `geoJsonBboxSchema` rejects — north must be *strictly* greater than south (RFC 7946 §5.2) —
168
+ * so map `fitBounds` calls silently no-op on it. Latitudes are clamped to ±90 after padding,
169
+ * shifting the window back inside the valid range so the span is preserved even at the poles.
170
+ * Longitudes use the unwrapped span (so a narrow antimeridian-crossing strip is padded too)
171
+ * and are wrapped into `[-180, 180]` the same way as {@link padGeoJsonBbox}.
172
+ *
173
+ * Wide bboxes are returned as-is (same reference).
174
+ */
175
+ export declare const ensureMinimumGeoJsonBboxSpan: (bbox: GeoJsonBbox, minSpanDegrees: number) => GeoJsonBbox;
164
176
  /**
165
177
  * Computes the difference of subject minus the union of all clips.
166
178
  * Returns null when the subject is fully covered (zero remaining area).