@trackunit/geo-json-utils 1.14.32 → 1.14.35
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 +86 -0
- package/index.esm.js +85 -1
- package/package.json +1 -1
- package/src/GeoJsonUtils.d.ts +35 -0
package/index.cjs.js
CHANGED
|
@@ -1256,6 +1256,90 @@ const getGeoJsonPolygonIntersection = (polygon1, polygon2) => {
|
|
|
1256
1256
|
coordinates: intersectionResult,
|
|
1257
1257
|
};
|
|
1258
1258
|
};
|
|
1259
|
+
const MIN_LNG = -180;
|
|
1260
|
+
const MAX_LNG = 180;
|
|
1261
|
+
const MIN_LAT = -90;
|
|
1262
|
+
const MAX_LAT = 90;
|
|
1263
|
+
const WORLD_LNG_SPAN = MAX_LNG - MIN_LNG;
|
|
1264
|
+
const clampLatitude = (lat) => Math.max(MIN_LAT, Math.min(MAX_LAT, lat));
|
|
1265
|
+
const normalizeBboxLongitude = (lng) => {
|
|
1266
|
+
const normalized = normalizeLongitude(lng);
|
|
1267
|
+
return normalized === MIN_LNG && lng > 0 ? MAX_LNG : normalized;
|
|
1268
|
+
};
|
|
1269
|
+
const getUnwrappedBboxLongitudeRange = (bbox) => {
|
|
1270
|
+
const [west, , east] = bbox;
|
|
1271
|
+
const unwrappedEast = west <= east ? east : east + WORLD_LNG_SPAN;
|
|
1272
|
+
return { west, east: unwrappedEast, span: unwrappedEast - west };
|
|
1273
|
+
};
|
|
1274
|
+
const getBboxLongitudesFromUnwrappedRange = ({ east, west, }) => {
|
|
1275
|
+
if (east - west >= WORLD_LNG_SPAN) {
|
|
1276
|
+
return [MIN_LNG, MAX_LNG];
|
|
1277
|
+
}
|
|
1278
|
+
return [normalizeBboxLongitude(west), normalizeBboxLongitude(east)];
|
|
1279
|
+
};
|
|
1280
|
+
/**
|
|
1281
|
+
* Scales a GeoJSON bounding box around its centre by a multiplier.
|
|
1282
|
+
*
|
|
1283
|
+
* `scale=1.0` → unchanged; `scale=2.0` → each dimension doubles; `scale=0.5` → half size.
|
|
1284
|
+
* Latitudes are clamped to `[-90, 90]`, longitudes to `[-180, 180]`.
|
|
1285
|
+
* Handles antimeridian-crossing bboxes (west > east).
|
|
1286
|
+
*/
|
|
1287
|
+
const scaleGeoJsonBbox = (bbox, scale) => {
|
|
1288
|
+
if (scale <= 0) {
|
|
1289
|
+
throw new RangeError("GeoJSON bbox scale must be greater than 0.");
|
|
1290
|
+
}
|
|
1291
|
+
const [, south, , north] = bbox;
|
|
1292
|
+
const latSpan = north - south;
|
|
1293
|
+
const latCenter = (south + north) / 2;
|
|
1294
|
+
const scaledLatSpan = latSpan * scale;
|
|
1295
|
+
const { west: unwrappedWest, east: unwrappedEast, span: lngSpan } = getUnwrappedBboxLongitudeRange(bbox);
|
|
1296
|
+
const lngCenter = (unwrappedWest + unwrappedEast) / 2;
|
|
1297
|
+
const scaledLngSpan = lngSpan * scale;
|
|
1298
|
+
const [scaledWest, scaledEast] = getBboxLongitudesFromUnwrappedRange({
|
|
1299
|
+
west: lngCenter - scaledLngSpan / 2,
|
|
1300
|
+
east: lngCenter + scaledLngSpan / 2,
|
|
1301
|
+
});
|
|
1302
|
+
return [
|
|
1303
|
+
scaledWest,
|
|
1304
|
+
clampLatitude(latCenter - scaledLatSpan / 2),
|
|
1305
|
+
scaledEast,
|
|
1306
|
+
clampLatitude(latCenter + scaledLatSpan / 2),
|
|
1307
|
+
];
|
|
1308
|
+
};
|
|
1309
|
+
const resolvePadding = (padding) => {
|
|
1310
|
+
if (typeof padding === "number") {
|
|
1311
|
+
return { n: padding, s: padding, e: padding, w: padding };
|
|
1312
|
+
}
|
|
1313
|
+
if (padding.kind === "axes") {
|
|
1314
|
+
return { n: padding.vertical, s: padding.vertical, e: padding.horizontal, w: padding.horizontal };
|
|
1315
|
+
}
|
|
1316
|
+
return { n: padding.north ?? 0, s: padding.south ?? 0, e: padding.east ?? 0, w: padding.west ?? 0 };
|
|
1317
|
+
};
|
|
1318
|
+
/**
|
|
1319
|
+
* Pads a GeoJSON bounding box by absolute degree amounts.
|
|
1320
|
+
*
|
|
1321
|
+
* See {@link BboxPadding} for the three supported forms: uniform, axis-grouped, or per-side.
|
|
1322
|
+
* Latitudes are clamped to `[-90, 90]`, longitudes to `[-180, 180]`.
|
|
1323
|
+
*/
|
|
1324
|
+
const padGeoJsonBbox = (bbox, padding) => {
|
|
1325
|
+
const [, south, , north] = bbox;
|
|
1326
|
+
const { n, s, e, w } = resolvePadding(padding);
|
|
1327
|
+
const paddedSouth = south - s;
|
|
1328
|
+
const paddedNorth = north + n;
|
|
1329
|
+
const clampedSouth = clampLatitude(paddedSouth);
|
|
1330
|
+
const clampedNorth = clampLatitude(paddedNorth);
|
|
1331
|
+
if (clampedNorth <= clampedSouth) {
|
|
1332
|
+
throw new RangeError("GeoJSON bbox padding must leave north greater than south.");
|
|
1333
|
+
}
|
|
1334
|
+
const { west: unwrappedWest, east: unwrappedEast } = getUnwrappedBboxLongitudeRange(bbox);
|
|
1335
|
+
const paddedWest = unwrappedWest - w;
|
|
1336
|
+
const paddedEast = unwrappedEast + e;
|
|
1337
|
+
if (paddedEast < paddedWest) {
|
|
1338
|
+
throw new RangeError("GeoJSON bbox padding must not invert west and east.");
|
|
1339
|
+
}
|
|
1340
|
+
const [west, east] = getBboxLongitudesFromUnwrappedRange({ west: paddedWest, east: paddedEast });
|
|
1341
|
+
return [west, clampedSouth, east, clampedNorth];
|
|
1342
|
+
};
|
|
1259
1343
|
/**
|
|
1260
1344
|
* Computes the difference of subject minus the union of all clips.
|
|
1261
1345
|
* Returns null when the subject is fully covered (zero remaining area).
|
|
@@ -1788,8 +1872,10 @@ exports.isGeoJsonPointInPolygon = isGeoJsonPointInPolygon;
|
|
|
1788
1872
|
exports.isGeoJsonPositionInLinearRing = isGeoJsonPositionInLinearRing;
|
|
1789
1873
|
exports.isPositionInsideRing = isPositionInsideRing;
|
|
1790
1874
|
exports.normalizeLongitudes = normalizeLongitudes;
|
|
1875
|
+
exports.padGeoJsonBbox = padGeoJsonBbox;
|
|
1791
1876
|
exports.projectLngLatToWebMercator = projectLngLatToWebMercator;
|
|
1792
1877
|
exports.projectPolygonalToWebMercator = projectPolygonalToWebMercator;
|
|
1878
|
+
exports.scaleGeoJsonBbox = scaleGeoJsonBbox;
|
|
1793
1879
|
exports.splitPolygonAtAntimeridian = splitPolygonAtAntimeridian;
|
|
1794
1880
|
exports.splitPolygonWithHolesAtAntimeridian = splitPolygonWithHolesAtAntimeridian;
|
|
1795
1881
|
exports.toFeatureCollection = toFeatureCollection;
|
package/index.esm.js
CHANGED
|
@@ -1254,6 +1254,90 @@ const getGeoJsonPolygonIntersection = (polygon1, polygon2) => {
|
|
|
1254
1254
|
coordinates: intersectionResult,
|
|
1255
1255
|
};
|
|
1256
1256
|
};
|
|
1257
|
+
const MIN_LNG = -180;
|
|
1258
|
+
const MAX_LNG = 180;
|
|
1259
|
+
const MIN_LAT = -90;
|
|
1260
|
+
const MAX_LAT = 90;
|
|
1261
|
+
const WORLD_LNG_SPAN = MAX_LNG - MIN_LNG;
|
|
1262
|
+
const clampLatitude = (lat) => Math.max(MIN_LAT, Math.min(MAX_LAT, lat));
|
|
1263
|
+
const normalizeBboxLongitude = (lng) => {
|
|
1264
|
+
const normalized = normalizeLongitude(lng);
|
|
1265
|
+
return normalized === MIN_LNG && lng > 0 ? MAX_LNG : normalized;
|
|
1266
|
+
};
|
|
1267
|
+
const getUnwrappedBboxLongitudeRange = (bbox) => {
|
|
1268
|
+
const [west, , east] = bbox;
|
|
1269
|
+
const unwrappedEast = west <= east ? east : east + WORLD_LNG_SPAN;
|
|
1270
|
+
return { west, east: unwrappedEast, span: unwrappedEast - west };
|
|
1271
|
+
};
|
|
1272
|
+
const getBboxLongitudesFromUnwrappedRange = ({ east, west, }) => {
|
|
1273
|
+
if (east - west >= WORLD_LNG_SPAN) {
|
|
1274
|
+
return [MIN_LNG, MAX_LNG];
|
|
1275
|
+
}
|
|
1276
|
+
return [normalizeBboxLongitude(west), normalizeBboxLongitude(east)];
|
|
1277
|
+
};
|
|
1278
|
+
/**
|
|
1279
|
+
* Scales a GeoJSON bounding box around its centre by a multiplier.
|
|
1280
|
+
*
|
|
1281
|
+
* `scale=1.0` → unchanged; `scale=2.0` → each dimension doubles; `scale=0.5` → half size.
|
|
1282
|
+
* Latitudes are clamped to `[-90, 90]`, longitudes to `[-180, 180]`.
|
|
1283
|
+
* Handles antimeridian-crossing bboxes (west > east).
|
|
1284
|
+
*/
|
|
1285
|
+
const scaleGeoJsonBbox = (bbox, scale) => {
|
|
1286
|
+
if (scale <= 0) {
|
|
1287
|
+
throw new RangeError("GeoJSON bbox scale must be greater than 0.");
|
|
1288
|
+
}
|
|
1289
|
+
const [, south, , north] = bbox;
|
|
1290
|
+
const latSpan = north - south;
|
|
1291
|
+
const latCenter = (south + north) / 2;
|
|
1292
|
+
const scaledLatSpan = latSpan * scale;
|
|
1293
|
+
const { west: unwrappedWest, east: unwrappedEast, span: lngSpan } = getUnwrappedBboxLongitudeRange(bbox);
|
|
1294
|
+
const lngCenter = (unwrappedWest + unwrappedEast) / 2;
|
|
1295
|
+
const scaledLngSpan = lngSpan * scale;
|
|
1296
|
+
const [scaledWest, scaledEast] = getBboxLongitudesFromUnwrappedRange({
|
|
1297
|
+
west: lngCenter - scaledLngSpan / 2,
|
|
1298
|
+
east: lngCenter + scaledLngSpan / 2,
|
|
1299
|
+
});
|
|
1300
|
+
return [
|
|
1301
|
+
scaledWest,
|
|
1302
|
+
clampLatitude(latCenter - scaledLatSpan / 2),
|
|
1303
|
+
scaledEast,
|
|
1304
|
+
clampLatitude(latCenter + scaledLatSpan / 2),
|
|
1305
|
+
];
|
|
1306
|
+
};
|
|
1307
|
+
const resolvePadding = (padding) => {
|
|
1308
|
+
if (typeof padding === "number") {
|
|
1309
|
+
return { n: padding, s: padding, e: padding, w: padding };
|
|
1310
|
+
}
|
|
1311
|
+
if (padding.kind === "axes") {
|
|
1312
|
+
return { n: padding.vertical, s: padding.vertical, e: padding.horizontal, w: padding.horizontal };
|
|
1313
|
+
}
|
|
1314
|
+
return { n: padding.north ?? 0, s: padding.south ?? 0, e: padding.east ?? 0, w: padding.west ?? 0 };
|
|
1315
|
+
};
|
|
1316
|
+
/**
|
|
1317
|
+
* Pads a GeoJSON bounding box by absolute degree amounts.
|
|
1318
|
+
*
|
|
1319
|
+
* See {@link BboxPadding} for the three supported forms: uniform, axis-grouped, or per-side.
|
|
1320
|
+
* Latitudes are clamped to `[-90, 90]`, longitudes to `[-180, 180]`.
|
|
1321
|
+
*/
|
|
1322
|
+
const padGeoJsonBbox = (bbox, padding) => {
|
|
1323
|
+
const [, south, , north] = bbox;
|
|
1324
|
+
const { n, s, e, w } = resolvePadding(padding);
|
|
1325
|
+
const paddedSouth = south - s;
|
|
1326
|
+
const paddedNorth = north + n;
|
|
1327
|
+
const clampedSouth = clampLatitude(paddedSouth);
|
|
1328
|
+
const clampedNorth = clampLatitude(paddedNorth);
|
|
1329
|
+
if (clampedNorth <= clampedSouth) {
|
|
1330
|
+
throw new RangeError("GeoJSON bbox padding must leave north greater than south.");
|
|
1331
|
+
}
|
|
1332
|
+
const { west: unwrappedWest, east: unwrappedEast } = getUnwrappedBboxLongitudeRange(bbox);
|
|
1333
|
+
const paddedWest = unwrappedWest - w;
|
|
1334
|
+
const paddedEast = unwrappedEast + e;
|
|
1335
|
+
if (paddedEast < paddedWest) {
|
|
1336
|
+
throw new RangeError("GeoJSON bbox padding must not invert west and east.");
|
|
1337
|
+
}
|
|
1338
|
+
const [west, east] = getBboxLongitudesFromUnwrappedRange({ west: paddedWest, east: paddedEast });
|
|
1339
|
+
return [west, clampedSouth, east, clampedNorth];
|
|
1340
|
+
};
|
|
1257
1341
|
/**
|
|
1258
1342
|
* Computes the difference of subject minus the union of all clips.
|
|
1259
1343
|
* Returns null when the subject is fully covered (zero remaining area).
|
|
@@ -1740,4 +1824,4 @@ const edgePixelLength = (x0, y0, x1, y1, zoom, midLatDeg, tileSize = 256) => {
|
|
|
1740
1824
|
return Math.sqrt(dxPx * dxPx + dyPx * dyPx);
|
|
1741
1825
|
};
|
|
1742
1826
|
|
|
1743
|
-
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, normalizeLongitudes, projectLngLatToWebMercator, projectPolygonalToWebMercator, splitPolygonAtAntimeridian, splitPolygonWithHolesAtAntimeridian, toFeatureCollection, toPosition2d, tuGeoJsonPointRadiusSchema, tuGeoJsonPolygonNoHolesSchema, tuGeoJsonRectangularBoxPolygonSchema, unprojectPolygonalFromWebMercator, unprojectWebMercatorToLngLat, validateBbox, validateBboxWithFallback, validateFeatureCollection, validatePosition };
|
|
1827
|
+
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, normalizeLongitudes, padGeoJsonBbox, projectLngLatToWebMercator, projectPolygonalToWebMercator, scaleGeoJsonBbox, splitPolygonAtAntimeridian, splitPolygonWithHolesAtAntimeridian, toFeatureCollection, toPosition2d, tuGeoJsonPointRadiusSchema, tuGeoJsonPolygonNoHolesSchema, tuGeoJsonRectangularBoxPolygonSchema, unprojectPolygonalFromWebMercator, unprojectWebMercatorToLngLat, validateBbox, validateBboxWithFallback, validateFeatureCollection, validatePosition };
|
package/package.json
CHANGED
package/src/GeoJsonUtils.d.ts
CHANGED
|
@@ -101,6 +101,41 @@ export declare const isFullyContainedInGeoJsonPolygon: (polygon1: GeoJsonPolygon
|
|
|
101
101
|
* Returns a MultiPolygon representing the intersection, or null if there is no intersection.
|
|
102
102
|
*/
|
|
103
103
|
export declare const getGeoJsonPolygonIntersection: (polygon1: GeoJsonPolygon | GeoJsonMultiPolygon, polygon2: GeoJsonPolygon | GeoJsonMultiPolygon) => GeoJsonMultiPolygon | GeoJsonPolygon | null;
|
|
104
|
+
/**
|
|
105
|
+
* Scales a GeoJSON bounding box around its centre by a multiplier.
|
|
106
|
+
*
|
|
107
|
+
* `scale=1.0` → unchanged; `scale=2.0` → each dimension doubles; `scale=0.5` → half size.
|
|
108
|
+
* Latitudes are clamped to `[-90, 90]`, longitudes to `[-180, 180]`.
|
|
109
|
+
* Handles antimeridian-crossing bboxes (west > east).
|
|
110
|
+
*/
|
|
111
|
+
export declare const scaleGeoJsonBbox: (bbox: GeoJsonBbox, scale: number) => GeoJsonBbox;
|
|
112
|
+
/**
|
|
113
|
+
* Padding argument for {@link padGeoJsonBbox}.
|
|
114
|
+
*
|
|
115
|
+
* - `number` — the same padding (in degrees) applied to all four edges.
|
|
116
|
+
* - `{ kind: 'axes' }` — separate values for horizontal (east/west) and vertical (north/south).
|
|
117
|
+
* - `{ kind: 'sides' }` — per-edge values; any omitted edge defaults to `0`.
|
|
118
|
+
*
|
|
119
|
+
* Positive values expand the bbox; negative values shrink it.
|
|
120
|
+
*/
|
|
121
|
+
export type BboxPadding = number | {
|
|
122
|
+
readonly kind: "axes";
|
|
123
|
+
readonly horizontal: number;
|
|
124
|
+
readonly vertical: number;
|
|
125
|
+
} | {
|
|
126
|
+
readonly kind: "sides";
|
|
127
|
+
readonly north?: number;
|
|
128
|
+
readonly south?: number;
|
|
129
|
+
readonly east?: number;
|
|
130
|
+
readonly west?: number;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Pads a GeoJSON bounding box by absolute degree amounts.
|
|
134
|
+
*
|
|
135
|
+
* See {@link BboxPadding} for the three supported forms: uniform, axis-grouped, or per-side.
|
|
136
|
+
* Latitudes are clamped to `[-90, 90]`, longitudes to `[-180, 180]`.
|
|
137
|
+
*/
|
|
138
|
+
export declare const padGeoJsonBbox: (bbox: GeoJsonBbox, padding: BboxPadding) => GeoJsonBbox;
|
|
104
139
|
/**
|
|
105
140
|
* Computes the difference of subject minus the union of all clips.
|
|
106
141
|
* Returns null when the subject is fully covered (zero remaining area).
|