@trackunit/geo-json-utils
Advanced tools
Comparing version 0.0.4 to 0.0.5
203
index.cjs.js
@@ -120,2 +120,55 @@ 'use strict'; | ||
/** | ||
* Utilities for handling coordinates and bounding boxes that cross the 180/-180 meridian. | ||
*/ | ||
/** | ||
* Checks if a sequence of longitudes crosses the meridian by looking for large longitude differences. | ||
*/ | ||
const checkCrossesMeridian = (longitudes) => { | ||
return longitudes.some((lon, i) => { | ||
const nextLon = longitudes[i + 1]; | ||
if (nextLon === undefined) { | ||
return false; | ||
} | ||
return Math.abs(lon - nextLon) > 180; | ||
}); | ||
}; | ||
/** | ||
* Normalizes longitudes to the 0-360 range. | ||
*/ | ||
const normalizeLongitudes = (longitudes) => { | ||
return longitudes.map(lon => (lon < 0 ? lon + 360 : lon)); | ||
}; | ||
/** | ||
* Converts a longitude from the 0-360 range back to the -180/180 range. | ||
*/ | ||
const denormalizeLongitude = (lon) => { | ||
return lon > 180 ? lon - 360 : lon; | ||
}; | ||
/** | ||
* Checks if a bounding box crosses the meridian. | ||
*/ | ||
const boundingBoxCrossesMeridian = (nwLon, seLon) => { | ||
return Math.abs(nwLon - seLon) > 180; | ||
}; | ||
/** | ||
* Gets min/max longitudes handling meridian crossing. | ||
* Returns longitudes in -180/180 range. | ||
*/ | ||
const getMinMaxLongitudes = (longitudes) => { | ||
if (checkCrossesMeridian(longitudes)) { | ||
const normalizedLongitudes = normalizeLongitudes(longitudes); | ||
const minLon = Math.min(...normalizedLongitudes); | ||
const maxLon = Math.max(...normalizedLongitudes); | ||
return { | ||
minLon: denormalizeLongitude(minLon), | ||
maxLon: denormalizeLongitude(maxLon), | ||
}; | ||
} | ||
return { | ||
minLon: Math.min(...longitudes), | ||
maxLon: Math.max(...longitudes), | ||
}; | ||
}; | ||
const EARTH_RADIUS = 6378137; // Earth’s mean radius in meters | ||
@@ -141,17 +194,36 @@ /** | ||
/** | ||
* @description Creates a bounding box from a GeoJSON Polygon. | ||
* @description Creates a bounding box from a GeoJSON Polygon or MultiPolygon. | ||
* Handles cases where the polygon crosses the 180/-180 meridian. | ||
*/ | ||
const getBboxFromGeoJsonPolygon = (polygon) => { | ||
const polygonParsed = geoJsonPolygonSchema.safeParse(polygon); | ||
if (!polygonParsed.success) { | ||
return null; | ||
// For MultiPolygon, we need to check all polygons to find the overall bounding box | ||
const allPoints = []; | ||
if (polygon.type === "MultiPolygon") { | ||
polygon.coordinates.forEach(polygonCoords => { | ||
const outerRing = polygonCoords[0]; | ||
if (outerRing) { | ||
allPoints.push(...outerRing); | ||
} | ||
}); | ||
} | ||
const points = polygonParsed.data.coordinates[0]; | ||
if (!points) { | ||
// Should never happen since the schema checks for it | ||
else { | ||
const polygonParsed = geoJsonPolygonSchema.safeParse(polygon); | ||
if (!polygonParsed.success) { | ||
return null; | ||
} | ||
const points = polygonParsed.data.coordinates[0]; | ||
if (!points) { | ||
// Should never happen since the schema checks for it | ||
return null; | ||
} | ||
allPoints.push(...points); | ||
} | ||
if (allPoints.length === 0) { | ||
return null; | ||
} | ||
const latitudes = points.map(point => point[1]); | ||
const longitudes = points.map(point => point[0]); | ||
return [Math.min(...longitudes), Math.min(...latitudes), Math.max(...longitudes), Math.max(...latitudes)]; | ||
// Get all longitudes and check if we cross the meridian | ||
const longitudes = allPoints.map(point => point[0]); | ||
const latitudes = allPoints.map(point => point[1]); | ||
const { minLon, maxLon } = getMinMaxLongitudes(longitudes); | ||
return [minLon, Math.min(...latitudes), maxLon, Math.max(...latitudes)]; | ||
}; | ||
@@ -184,23 +256,46 @@ /** | ||
* @description Creates a TU bounding box from a GeoJson Polygon. | ||
* Handles cases where the polygon crosses the 180/-180 meridian. | ||
*/ | ||
const getBoundingBoxFromGeoJsonPolygon = (polygon) => { | ||
const polygonParsed = geoJsonPolygonSchema.safeParse(polygon); | ||
if (!polygonParsed.success) { | ||
return null; | ||
// For MultiPolygon, we need to check all polygons to find the overall bounding box | ||
const allPoints = []; | ||
if (polygon.type === "MultiPolygon") { | ||
const multiPolygonParsed = geoJsonMultiPolygonSchema.safeParse(polygon); | ||
if (!multiPolygonParsed.success) { | ||
return null; | ||
} | ||
multiPolygonParsed.data.coordinates.forEach(polygonCoords => { | ||
const outerRing = polygonCoords[0]; | ||
if (outerRing) { | ||
allPoints.push(...outerRing); | ||
} | ||
}); | ||
} | ||
const points = polygonParsed.data.coordinates[0]; | ||
if (!points) { | ||
// Should never happen since the schema checks for it | ||
else { | ||
const polygonParsed = geoJsonPolygonSchema.safeParse(polygon); | ||
if (!polygonParsed.success) { | ||
return null; | ||
} | ||
const points = polygonParsed.data.coordinates[0]; | ||
if (!points) { | ||
// Should never happen since the schema checks for it | ||
return null; | ||
} | ||
allPoints.push(...points); | ||
} | ||
if (allPoints.length === 0) { | ||
return null; | ||
} | ||
const latitudes = points.map(point => point[1]); | ||
const longitudes = points.map(point => point[0]); | ||
// Get all longitudes and check if we cross the meridian | ||
const longitudes = allPoints.map(point => point[0]); | ||
const latitudes = allPoints.map(point => point[1]); | ||
const { minLon, maxLon } = getMinMaxLongitudes(longitudes); | ||
return { | ||
nw: { | ||
latitude: Math.max(...latitudes), | ||
longitude: Math.min(...longitudes), | ||
longitude: minLon, | ||
}, | ||
se: { | ||
latitude: Math.min(...latitudes), | ||
longitude: Math.max(...longitudes), | ||
longitude: maxLon, | ||
}, | ||
@@ -210,6 +305,39 @@ }; | ||
/** | ||
* @description Creates a GeoJSON Polygon from a TU bounding box. | ||
* @description Creates a GeoJSON MultiPolygon from a TU bounding box. | ||
* It has to return a MultiPolygon because the polygon may cross the 180/-180 meridian | ||
* and we need to avoid the polygon being ambiguous about which side of the meridian it wraps around. | ||
* so if it crosses the meridian, we return a MultiPolygon with two polygons, one for the western hemisphere and one for the eastern hemisphere. | ||
* @param boundingBox The bounding box to create a polygon from | ||
* @returns The polygon created from the bounding box | ||
*/ | ||
const getGeoJsonPolygonFromBoundingBox = (boundingBox) => { | ||
const { nw, se } = boundingBox; | ||
if (nw.longitude > se.longitude) { | ||
// crossing meridian | ||
return { | ||
type: "MultiPolygon", | ||
coordinates: [ | ||
[ | ||
[ | ||
// western hemisphere | ||
[-180, nw.latitude], // Northwest corner | ||
[se.longitude, nw.latitude], // Northeast corner | ||
[se.longitude, se.latitude], // Southeast corner | ||
[-180, se.latitude], // Southwest corner | ||
[-180, nw.latitude], // Close the loop back to Northwest corner | ||
], | ||
], | ||
[ | ||
[ | ||
// eastern hemisphere | ||
[nw.longitude, nw.latitude], // Northwest corner | ||
[180, nw.latitude], // Northeast corner | ||
[180, se.latitude], // Southeast corner | ||
[nw.longitude, se.latitude], // Southwest corner | ||
[nw.longitude, nw.latitude], // Close the loop back to Northwest corner | ||
], | ||
], | ||
], | ||
}; | ||
} | ||
return { | ||
@@ -311,3 +439,3 @@ type: "Polygon", | ||
/** | ||
* Checks if polygon1 is fully contained within polygon2 | ||
* Checks if polygon1/multi-polygon1 is fully contained within polygon2/multi-polygon2 | ||
*/ | ||
@@ -324,21 +452,17 @@ const isFullyContainedInGeoJsonPolygon = (polygon1, polygon2) => { | ||
/** | ||
* @description Gets the intersection between two GeoJSON polygons. If one polygon is fully contained within the other, | ||
* returns the contained polygon. Otherwise returns a MultiPolygon representing the intersection. | ||
* @param polygon1 The first polygon to check intersection | ||
* @param polygon2 The second polygon to check intersection | ||
* @description Gets the intersection between two GeoJSON polygons/multi-polygons. If one polygon/multi-polygon is fully contained within the other, | ||
* returns either the inner or outer polygon based on containmentPreference. Otherwise returns a MultiPolygon representing the intersection. | ||
* @param polygon1 The first polygon/multi-polygon to check intersection | ||
* @param polygon2 The second polygon/multi-polygon to check intersection | ||
* @param containmentPreference Controls what to return when one polygon contains the other: | ||
* - "outer": Return the containing polygon | ||
* - "inner": Return the contained polygon | ||
* - "none": Skip containment checks and return raw intersection | ||
* @returns {(GeoJsonMultiPolygon | GeoJsonPolygon)} The intersection as either a Polygon (if one contains the other) or MultiPolygon | ||
*/ | ||
const getGeoJsonPolygonIntersection = (polygon1, polygon2) => { | ||
const polygon1Parsed = geoJsonPolygonSchema.safeParse(polygon1); | ||
const polygon2Parsed = geoJsonPolygonSchema.safeParse(polygon2); | ||
if (!polygon1Parsed.success || !polygon2Parsed.success) { | ||
const intersectionResult = polygonClipping.intersection(polygon1.type === "MultiPolygon" ? polygon1.coordinates : [polygon1.coordinates], polygon2.type === "MultiPolygon" ? polygon2.coordinates : [polygon2.coordinates]); | ||
if (intersectionResult.length === 0) { | ||
return null; | ||
} | ||
if (isFullyContainedInGeoJsonPolygon(polygon1, polygon2)) { | ||
return polygon1; | ||
} | ||
if (isFullyContainedInGeoJsonPolygon(polygon2, polygon1)) { | ||
return polygon2; | ||
} | ||
const intersectionResult = polygonClipping.intersection(polygon1.coordinates, polygon2.coordinates); | ||
if (intersectionResult.length === 1 && intersectionResult[0]) { | ||
@@ -352,3 +476,3 @@ return { | ||
type: "MultiPolygon", | ||
coordinates: polygonClipping.intersection(polygon1.coordinates, polygon2.coordinates), | ||
coordinates: intersectionResult, | ||
}; | ||
@@ -501,3 +625,6 @@ }; | ||
exports.EARTH_RADIUS = EARTH_RADIUS; | ||
exports.boundingBoxCrossesMeridian = boundingBoxCrossesMeridian; | ||
exports.checkCrossesMeridian = checkCrossesMeridian; | ||
exports.coordinatesToStandardFormat = coordinatesToStandardFormat; | ||
exports.denormalizeLongitude = denormalizeLongitude; | ||
exports.geoJsonBboxSchema = geoJsonBboxSchema; | ||
@@ -518,2 +645,3 @@ exports.geoJsonGeometrySchema = geoJsonGeometrySchema; | ||
exports.getGeoJsonPolygonIntersection = getGeoJsonPolygonIntersection; | ||
exports.getMinMaxLongitudes = getMinMaxLongitudes; | ||
exports.getMultipleCoordinatesFromGeoJsonObject = getMultipleCoordinatesFromGeoJsonObject; | ||
@@ -527,4 +655,5 @@ exports.getPointCoordinateFromGeoJsonObject = getPointCoordinateFromGeoJsonObject; | ||
exports.isGeoJsonPositionInLinearRing = isGeoJsonPositionInLinearRing; | ||
exports.normalizeLongitudes = normalizeLongitudes; | ||
exports.tuGeoJsonPointRadiusSchema = tuGeoJsonPointRadiusSchema; | ||
exports.tuGeoJsonPolygonNoHolesSchema = tuGeoJsonPolygonNoHolesSchema; | ||
exports.tuGeoJsonRectangularBoxPolygonSchema = tuGeoJsonRectangularBoxPolygonSchema; |
200
index.esm.js
@@ -118,2 +118,55 @@ import { z } from 'zod'; | ||
/** | ||
* Utilities for handling coordinates and bounding boxes that cross the 180/-180 meridian. | ||
*/ | ||
/** | ||
* Checks if a sequence of longitudes crosses the meridian by looking for large longitude differences. | ||
*/ | ||
const checkCrossesMeridian = (longitudes) => { | ||
return longitudes.some((lon, i) => { | ||
const nextLon = longitudes[i + 1]; | ||
if (nextLon === undefined) { | ||
return false; | ||
} | ||
return Math.abs(lon - nextLon) > 180; | ||
}); | ||
}; | ||
/** | ||
* Normalizes longitudes to the 0-360 range. | ||
*/ | ||
const normalizeLongitudes = (longitudes) => { | ||
return longitudes.map(lon => (lon < 0 ? lon + 360 : lon)); | ||
}; | ||
/** | ||
* Converts a longitude from the 0-360 range back to the -180/180 range. | ||
*/ | ||
const denormalizeLongitude = (lon) => { | ||
return lon > 180 ? lon - 360 : lon; | ||
}; | ||
/** | ||
* Checks if a bounding box crosses the meridian. | ||
*/ | ||
const boundingBoxCrossesMeridian = (nwLon, seLon) => { | ||
return Math.abs(nwLon - seLon) > 180; | ||
}; | ||
/** | ||
* Gets min/max longitudes handling meridian crossing. | ||
* Returns longitudes in -180/180 range. | ||
*/ | ||
const getMinMaxLongitudes = (longitudes) => { | ||
if (checkCrossesMeridian(longitudes)) { | ||
const normalizedLongitudes = normalizeLongitudes(longitudes); | ||
const minLon = Math.min(...normalizedLongitudes); | ||
const maxLon = Math.max(...normalizedLongitudes); | ||
return { | ||
minLon: denormalizeLongitude(minLon), | ||
maxLon: denormalizeLongitude(maxLon), | ||
}; | ||
} | ||
return { | ||
minLon: Math.min(...longitudes), | ||
maxLon: Math.max(...longitudes), | ||
}; | ||
}; | ||
const EARTH_RADIUS = 6378137; // Earth’s mean radius in meters | ||
@@ -139,17 +192,36 @@ /** | ||
/** | ||
* @description Creates a bounding box from a GeoJSON Polygon. | ||
* @description Creates a bounding box from a GeoJSON Polygon or MultiPolygon. | ||
* Handles cases where the polygon crosses the 180/-180 meridian. | ||
*/ | ||
const getBboxFromGeoJsonPolygon = (polygon) => { | ||
const polygonParsed = geoJsonPolygonSchema.safeParse(polygon); | ||
if (!polygonParsed.success) { | ||
return null; | ||
// For MultiPolygon, we need to check all polygons to find the overall bounding box | ||
const allPoints = []; | ||
if (polygon.type === "MultiPolygon") { | ||
polygon.coordinates.forEach(polygonCoords => { | ||
const outerRing = polygonCoords[0]; | ||
if (outerRing) { | ||
allPoints.push(...outerRing); | ||
} | ||
}); | ||
} | ||
const points = polygonParsed.data.coordinates[0]; | ||
if (!points) { | ||
// Should never happen since the schema checks for it | ||
else { | ||
const polygonParsed = geoJsonPolygonSchema.safeParse(polygon); | ||
if (!polygonParsed.success) { | ||
return null; | ||
} | ||
const points = polygonParsed.data.coordinates[0]; | ||
if (!points) { | ||
// Should never happen since the schema checks for it | ||
return null; | ||
} | ||
allPoints.push(...points); | ||
} | ||
if (allPoints.length === 0) { | ||
return null; | ||
} | ||
const latitudes = points.map(point => point[1]); | ||
const longitudes = points.map(point => point[0]); | ||
return [Math.min(...longitudes), Math.min(...latitudes), Math.max(...longitudes), Math.max(...latitudes)]; | ||
// Get all longitudes and check if we cross the meridian | ||
const longitudes = allPoints.map(point => point[0]); | ||
const latitudes = allPoints.map(point => point[1]); | ||
const { minLon, maxLon } = getMinMaxLongitudes(longitudes); | ||
return [minLon, Math.min(...latitudes), maxLon, Math.max(...latitudes)]; | ||
}; | ||
@@ -182,23 +254,46 @@ /** | ||
* @description Creates a TU bounding box from a GeoJson Polygon. | ||
* Handles cases where the polygon crosses the 180/-180 meridian. | ||
*/ | ||
const getBoundingBoxFromGeoJsonPolygon = (polygon) => { | ||
const polygonParsed = geoJsonPolygonSchema.safeParse(polygon); | ||
if (!polygonParsed.success) { | ||
return null; | ||
// For MultiPolygon, we need to check all polygons to find the overall bounding box | ||
const allPoints = []; | ||
if (polygon.type === "MultiPolygon") { | ||
const multiPolygonParsed = geoJsonMultiPolygonSchema.safeParse(polygon); | ||
if (!multiPolygonParsed.success) { | ||
return null; | ||
} | ||
multiPolygonParsed.data.coordinates.forEach(polygonCoords => { | ||
const outerRing = polygonCoords[0]; | ||
if (outerRing) { | ||
allPoints.push(...outerRing); | ||
} | ||
}); | ||
} | ||
const points = polygonParsed.data.coordinates[0]; | ||
if (!points) { | ||
// Should never happen since the schema checks for it | ||
else { | ||
const polygonParsed = geoJsonPolygonSchema.safeParse(polygon); | ||
if (!polygonParsed.success) { | ||
return null; | ||
} | ||
const points = polygonParsed.data.coordinates[0]; | ||
if (!points) { | ||
// Should never happen since the schema checks for it | ||
return null; | ||
} | ||
allPoints.push(...points); | ||
} | ||
if (allPoints.length === 0) { | ||
return null; | ||
} | ||
const latitudes = points.map(point => point[1]); | ||
const longitudes = points.map(point => point[0]); | ||
// Get all longitudes and check if we cross the meridian | ||
const longitudes = allPoints.map(point => point[0]); | ||
const latitudes = allPoints.map(point => point[1]); | ||
const { minLon, maxLon } = getMinMaxLongitudes(longitudes); | ||
return { | ||
nw: { | ||
latitude: Math.max(...latitudes), | ||
longitude: Math.min(...longitudes), | ||
longitude: minLon, | ||
}, | ||
se: { | ||
latitude: Math.min(...latitudes), | ||
longitude: Math.max(...longitudes), | ||
longitude: maxLon, | ||
}, | ||
@@ -208,6 +303,39 @@ }; | ||
/** | ||
* @description Creates a GeoJSON Polygon from a TU bounding box. | ||
* @description Creates a GeoJSON MultiPolygon from a TU bounding box. | ||
* It has to return a MultiPolygon because the polygon may cross the 180/-180 meridian | ||
* and we need to avoid the polygon being ambiguous about which side of the meridian it wraps around. | ||
* so if it crosses the meridian, we return a MultiPolygon with two polygons, one for the western hemisphere and one for the eastern hemisphere. | ||
* @param boundingBox The bounding box to create a polygon from | ||
* @returns The polygon created from the bounding box | ||
*/ | ||
const getGeoJsonPolygonFromBoundingBox = (boundingBox) => { | ||
const { nw, se } = boundingBox; | ||
if (nw.longitude > se.longitude) { | ||
// crossing meridian | ||
return { | ||
type: "MultiPolygon", | ||
coordinates: [ | ||
[ | ||
[ | ||
// western hemisphere | ||
[-180, nw.latitude], // Northwest corner | ||
[se.longitude, nw.latitude], // Northeast corner | ||
[se.longitude, se.latitude], // Southeast corner | ||
[-180, se.latitude], // Southwest corner | ||
[-180, nw.latitude], // Close the loop back to Northwest corner | ||
], | ||
], | ||
[ | ||
[ | ||
// eastern hemisphere | ||
[nw.longitude, nw.latitude], // Northwest corner | ||
[180, nw.latitude], // Northeast corner | ||
[180, se.latitude], // Southeast corner | ||
[nw.longitude, se.latitude], // Southwest corner | ||
[nw.longitude, nw.latitude], // Close the loop back to Northwest corner | ||
], | ||
], | ||
], | ||
}; | ||
} | ||
return { | ||
@@ -309,3 +437,3 @@ type: "Polygon", | ||
/** | ||
* Checks if polygon1 is fully contained within polygon2 | ||
* Checks if polygon1/multi-polygon1 is fully contained within polygon2/multi-polygon2 | ||
*/ | ||
@@ -322,21 +450,17 @@ const isFullyContainedInGeoJsonPolygon = (polygon1, polygon2) => { | ||
/** | ||
* @description Gets the intersection between two GeoJSON polygons. If one polygon is fully contained within the other, | ||
* returns the contained polygon. Otherwise returns a MultiPolygon representing the intersection. | ||
* @param polygon1 The first polygon to check intersection | ||
* @param polygon2 The second polygon to check intersection | ||
* @description Gets the intersection between two GeoJSON polygons/multi-polygons. If one polygon/multi-polygon is fully contained within the other, | ||
* returns either the inner or outer polygon based on containmentPreference. Otherwise returns a MultiPolygon representing the intersection. | ||
* @param polygon1 The first polygon/multi-polygon to check intersection | ||
* @param polygon2 The second polygon/multi-polygon to check intersection | ||
* @param containmentPreference Controls what to return when one polygon contains the other: | ||
* - "outer": Return the containing polygon | ||
* - "inner": Return the contained polygon | ||
* - "none": Skip containment checks and return raw intersection | ||
* @returns {(GeoJsonMultiPolygon | GeoJsonPolygon)} The intersection as either a Polygon (if one contains the other) or MultiPolygon | ||
*/ | ||
const getGeoJsonPolygonIntersection = (polygon1, polygon2) => { | ||
const polygon1Parsed = geoJsonPolygonSchema.safeParse(polygon1); | ||
const polygon2Parsed = geoJsonPolygonSchema.safeParse(polygon2); | ||
if (!polygon1Parsed.success || !polygon2Parsed.success) { | ||
const intersectionResult = intersection(polygon1.type === "MultiPolygon" ? polygon1.coordinates : [polygon1.coordinates], polygon2.type === "MultiPolygon" ? polygon2.coordinates : [polygon2.coordinates]); | ||
if (intersectionResult.length === 0) { | ||
return null; | ||
} | ||
if (isFullyContainedInGeoJsonPolygon(polygon1, polygon2)) { | ||
return polygon1; | ||
} | ||
if (isFullyContainedInGeoJsonPolygon(polygon2, polygon1)) { | ||
return polygon2; | ||
} | ||
const intersectionResult = intersection(polygon1.coordinates, polygon2.coordinates); | ||
if (intersectionResult.length === 1 && intersectionResult[0]) { | ||
@@ -350,3 +474,3 @@ return { | ||
type: "MultiPolygon", | ||
coordinates: intersection(polygon1.coordinates, polygon2.coordinates), | ||
coordinates: intersectionResult, | ||
}; | ||
@@ -498,2 +622,2 @@ }; | ||
export { EARTH_RADIUS, coordinatesToStandardFormat, geoJsonBboxSchema, geoJsonGeometrySchema, geoJsonLineStringSchema, geoJsonLinearRingSchema, geoJsonMultiLineStringSchema, geoJsonMultiPointSchema, geoJsonMultiPolygonSchema, geoJsonPointSchema, geoJsonPolygonSchema, geoJsonPositionSchema, getBboxFromGeoJsonPolygon, getBoundingBoxFromGeoJsonPolygon, getExtremeGeoJsonPointFromPolygon, getGeoJsonPolygonFromBoundingBox, getGeoJsonPolygonIntersection, getMultipleCoordinatesFromGeoJsonObject, getPointCoordinateFromGeoJsonObject, getPointCoordinateFromGeoJsonPoint, getPolygonFromBbox, getPolygonFromPointAndRadius, isFullyContainedInGeoJsonPolygon, isGeoJsonPointInPolygon, isGeoJsonPositionInLinearRing, tuGeoJsonPointRadiusSchema, tuGeoJsonPolygonNoHolesSchema, tuGeoJsonRectangularBoxPolygonSchema }; | ||
export { EARTH_RADIUS, boundingBoxCrossesMeridian, checkCrossesMeridian, coordinatesToStandardFormat, denormalizeLongitude, geoJsonBboxSchema, geoJsonGeometrySchema, geoJsonLineStringSchema, geoJsonLinearRingSchema, geoJsonMultiLineStringSchema, geoJsonMultiPointSchema, geoJsonMultiPolygonSchema, geoJsonPointSchema, geoJsonPolygonSchema, geoJsonPositionSchema, getBboxFromGeoJsonPolygon, getBoundingBoxFromGeoJsonPolygon, getExtremeGeoJsonPointFromPolygon, getGeoJsonPolygonFromBoundingBox, getGeoJsonPolygonIntersection, getMinMaxLongitudes, getMultipleCoordinatesFromGeoJsonObject, getPointCoordinateFromGeoJsonObject, getPointCoordinateFromGeoJsonPoint, getPolygonFromBbox, getPolygonFromPointAndRadius, isFullyContainedInGeoJsonPolygon, isGeoJsonPointInPolygon, isGeoJsonPositionInLinearRing, normalizeLongitudes, tuGeoJsonPointRadiusSchema, tuGeoJsonPolygonNoHolesSchema, tuGeoJsonRectangularBoxPolygonSchema }; |
{ | ||
"name": "@trackunit/geo-json-utils", | ||
"version": "0.0.4", | ||
"version": "0.0.5", | ||
"repository": "https://github.com/Trackunit/manager", | ||
@@ -5,0 +5,0 @@ "license": "SEE LICENSE IN LICENSE.txt", |
@@ -17,5 +17,6 @@ import { GeoJsonBbox, GeoJsonLinearRing, GeoJsonMultiPolygon, GeoJsonPoint, GeoJsonPolygon, GeoJsonPosition } from "./GeoJsonSchemas"; | ||
/** | ||
* @description Creates a bounding box from a GeoJSON Polygon. | ||
* @description Creates a bounding box from a GeoJSON Polygon or MultiPolygon. | ||
* Handles cases where the polygon crosses the 180/-180 meridian. | ||
*/ | ||
export declare const getBboxFromGeoJsonPolygon: (polygon: GeoJsonPolygon) => GeoJsonBbox | null; | ||
export declare const getBboxFromGeoJsonPolygon: (polygon: GeoJsonPolygon | GeoJsonMultiPolygon) => GeoJsonBbox | null; | ||
/** | ||
@@ -27,8 +28,14 @@ * @description Creates a round polygon from a point and a radius. | ||
* @description Creates a TU bounding box from a GeoJson Polygon. | ||
* Handles cases where the polygon crosses the 180/-180 meridian. | ||
*/ | ||
export declare const getBoundingBoxFromGeoJsonPolygon: (polygon: GeoJsonPolygon) => TuBoundingBox | null; | ||
export declare const getBoundingBoxFromGeoJsonPolygon: (polygon: GeoJsonPolygon | GeoJsonMultiPolygon) => TuBoundingBox | null; | ||
/** | ||
* @description Creates a GeoJSON Polygon from a TU bounding box. | ||
* @description Creates a GeoJSON MultiPolygon from a TU bounding box. | ||
* It has to return a MultiPolygon because the polygon may cross the 180/-180 meridian | ||
* and we need to avoid the polygon being ambiguous about which side of the meridian it wraps around. | ||
* so if it crosses the meridian, we return a MultiPolygon with two polygons, one for the western hemisphere and one for the eastern hemisphere. | ||
* @param boundingBox The bounding box to create a polygon from | ||
* @returns The polygon created from the bounding box | ||
*/ | ||
export declare const getGeoJsonPolygonFromBoundingBox: (boundingBox: TuBoundingBox) => GeoJsonPolygon; | ||
export declare const getGeoJsonPolygonFromBoundingBox: (boundingBox: TuBoundingBox) => GeoJsonMultiPolygon | GeoJsonPolygon; | ||
/** | ||
@@ -64,13 +71,17 @@ * @description Creates TU point coordinate from a GeoJSON Point. | ||
/** | ||
* Checks if polygon1 is fully contained within polygon2 | ||
* Checks if polygon1/multi-polygon1 is fully contained within polygon2/multi-polygon2 | ||
*/ | ||
export declare const isFullyContainedInGeoJsonPolygon: (polygon1: GeoJsonPolygon, polygon2: GeoJsonPolygon) => boolean | null; | ||
/** | ||
* @description Gets the intersection between two GeoJSON polygons. If one polygon is fully contained within the other, | ||
* returns the contained polygon. Otherwise returns a MultiPolygon representing the intersection. | ||
* @param polygon1 The first polygon to check intersection | ||
* @param polygon2 The second polygon to check intersection | ||
* @description Gets the intersection between two GeoJSON polygons/multi-polygons. If one polygon/multi-polygon is fully contained within the other, | ||
* returns either the inner or outer polygon based on containmentPreference. Otherwise returns a MultiPolygon representing the intersection. | ||
* @param polygon1 The first polygon/multi-polygon to check intersection | ||
* @param polygon2 The second polygon/multi-polygon to check intersection | ||
* @param containmentPreference Controls what to return when one polygon contains the other: | ||
* - "outer": Return the containing polygon | ||
* - "inner": Return the contained polygon | ||
* - "none": Skip containment checks and return raw intersection | ||
* @returns {(GeoJsonMultiPolygon | GeoJsonPolygon)} The intersection as either a Polygon (if one contains the other) or MultiPolygon | ||
*/ | ||
export declare const getGeoJsonPolygonIntersection: (polygon1: GeoJsonPolygon, polygon2: GeoJsonPolygon) => GeoJsonMultiPolygon | GeoJsonPolygon | null; | ||
export declare const getGeoJsonPolygonIntersection: (polygon1: GeoJsonPolygon | GeoJsonMultiPolygon, polygon2: GeoJsonPolygon | GeoJsonMultiPolygon) => GeoJsonMultiPolygon | GeoJsonPolygon | null; | ||
export {}; |
export * from "./GeoJsonSchemas"; | ||
export * from "./GeoJsonUtils"; | ||
export * from "./meridianUtils"; | ||
export * from "./TUGeoJsonObjectBridgeUtils"; | ||
export * from "./TuGeoJsonSchemas"; |
68750
12
1639