@trackunit/shared-utils
Advanced tools
Comparing version 0.0.85 to 0.0.86
517
index.cjs.js
'use strict'; | ||
var zod = require('zod'); | ||
var polygonClipping = require('polygon-clipping'); | ||
var uuid = require('uuid'); | ||
@@ -418,490 +416,3 @@ | ||
// * NOTE: For simplicity these tools are built for 2D coordinate space only! | ||
/** | ||
* A Position is an array of coordinates. [x, y] | ||
* https://tools.ietf.org/html/rfc7946#section-3.1.1 | ||
*/ | ||
const geoJsonPositionSchema = zod.z.tuple([zod.z.number(), zod.z.number()]); | ||
/** | ||
* Point geometry object. | ||
* https://tools.ietf.org/html/rfc7946#section-3.1.2 | ||
*/ | ||
const geoJsonPointSchema = zod.z.strictObject({ | ||
type: zod.z.literal("Point"), | ||
coordinates: geoJsonPositionSchema, | ||
}); | ||
/** | ||
* MultiPoint geometry object. | ||
* https://tools.ietf.org/html/rfc7946#section-3.1.3 | ||
*/ | ||
const geoJsonMultiPointSchema = zod.z.strictObject({ | ||
type: zod.z.literal("MultiPoint"), | ||
coordinates: zod.z.array(geoJsonPositionSchema), | ||
}); | ||
/** | ||
* LineString geometry object. | ||
* Minimum length of 2 positions. | ||
* https://tools.ietf.org/html/rfc7946#section-3.1.4 | ||
*/ | ||
const geoJsonLineStringSchema = zod.z.strictObject({ | ||
type: zod.z.literal("LineString"), | ||
coordinates: zod.z.array(geoJsonPositionSchema).min(2), | ||
}); | ||
/** | ||
* MultiLineString geometry object. | ||
* https://tools.ietf.org/html/rfc7946#section-3.1.5 | ||
*/ | ||
const geoJsonMultiLineStringSchema = zod.z.strictObject({ | ||
type: zod.z.literal("MultiLineString"), | ||
coordinates: zod.z.array(zod.z.array(geoJsonPositionSchema)), | ||
}); | ||
/** | ||
* Helper type for reuse across polygon schemas. | ||
* | ||
* - A linear ring is a closed LineString with four or more positions. | ||
* - The first and last positions are equivalent, and they MUST contain | ||
identical values; their representation SHOULD also be identical | ||
* - A linear ring is the boundary of a surface or the boundary of a | ||
hole in a surface | ||
* - A linear ring MUST follow the right-hand rule with respect to the | ||
area it bounds, i.e., exterior rings are counterclockwise, and | ||
holes are clockwise | ||
*/ | ||
const geoJsonLinearRingSchema = zod.z | ||
.array(geoJsonPositionSchema) | ||
.min(4, { | ||
message: "Coordinates array must contain at least 4 positions. 3 to make a non-line shape and 1 to close the shape (duplicate of first)", | ||
}) | ||
.superRefine((coords, ctx) => { | ||
const first = coords[0]; | ||
const last = coords[coords.length - 1]; | ||
// Check if first and last coordinates match | ||
if (JSON.stringify(first) !== JSON.stringify(last)) { | ||
ctx.addIssue({ | ||
code: zod.z.ZodIssueCode.custom, | ||
message: "First and last coordinate positions must be identical (to close the linear ring aka polygon).", | ||
}); | ||
} | ||
// Check if consecutive points are identical (excluding first and last) | ||
for (let i = 1; i < coords.length - 1; i++) { | ||
if (JSON.stringify(coords[i]) === JSON.stringify(coords[i - 1])) { | ||
ctx.addIssue({ | ||
code: zod.z.ZodIssueCode.custom, | ||
message: `Consecutive coordinates at index ${i - 1} and ${i} should not be identical.`, | ||
}); | ||
} | ||
} | ||
}); | ||
/** | ||
* Polygon geometry object. | ||
* https://tools.ietf.org/html/rfc7946#section-3.1.6 | ||
*/ | ||
const geoJsonPolygonSchema = zod.z.strictObject({ | ||
type: zod.z.literal("Polygon"), | ||
coordinates: zod.z.array(geoJsonLinearRingSchema), | ||
}); | ||
/** | ||
* MultiPolygon geometry object. | ||
* https://tools.ietf.org/html/rfc7946#section-3.1.7 | ||
*/ | ||
const geoJsonMultiPolygonSchema = zod.z.strictObject({ | ||
type: zod.z.literal("MultiPolygon"), | ||
coordinates: zod.z.array(zod.z.array(geoJsonLinearRingSchema)), | ||
}); | ||
// The same for Geometry, GeometryCollection, GeoJsonProperties, Feature, FeatureCollection, etc. | ||
const geoJsonGeometrySchema = zod.z.union([ | ||
geoJsonPointSchema, | ||
geoJsonMultiPointSchema, | ||
geoJsonLineStringSchema, | ||
geoJsonMultiLineStringSchema, | ||
geoJsonPolygonSchema, | ||
geoJsonMultiPolygonSchema, | ||
]); | ||
//* -------- Bbox -------- *// | ||
/** | ||
* 2D bounding box of the GeoJSON object. | ||
* The value of the Bbox member is an array of length 4. | ||
* | ||
* [min_lon, min_lat, max_lon, max_lat] | ||
*/ | ||
const geoJsonBboxSchema = zod.z | ||
.tuple([zod.z.number(), zod.z.number(), zod.z.number(), zod.z.number()]) | ||
.refine(([minLng, minLat, maxLng, maxLat]) => maxLng > minLng && maxLat > minLat, { | ||
message: "Invalid bounding box: maxLng should be greater than minLng, and maxLat should be greater than minLat.", | ||
}); | ||
const EARTH_RADIUS = 6378137; // Earth’s mean radius in meters | ||
/** | ||
* @description Creates a polygon (with no holes) from a bounding box. | ||
*/ | ||
const getPolygonFromBbox = (bbox) => { | ||
const [minLon, minLat, maxLon, maxLat] = bbox; | ||
return { | ||
type: "Polygon", | ||
coordinates: [ | ||
[ | ||
[minLon, minLat], | ||
[maxLon, minLat], | ||
[maxLon, maxLat], | ||
[minLon, maxLat], | ||
[minLon, minLat], | ||
], | ||
], | ||
}; | ||
}; | ||
/** | ||
* @description Creates a bounding box from a GeoJSON Polygon. | ||
*/ | ||
const getBboxFromGeoJsonPolygon = (polygon) => { | ||
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; | ||
} | ||
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)]; | ||
}; | ||
/** | ||
* @description Creates a round polygon from a point and a radius. | ||
*/ | ||
const getPolygonFromPointAndRadius = (point, radius) => { | ||
const [lon, lat] = point.coordinates; | ||
// Adjust the number of points based on radius (resolution) | ||
const pointsCount = Math.max(32, Math.floor(radius / 100)); // More points for larger radius | ||
const angleStep = (2 * Math.PI) / pointsCount; | ||
const coordinates = []; | ||
for (let i = 0; i <= pointsCount; i++) { | ||
const angle = i * angleStep; | ||
// Calculate offset in latitude and longitude | ||
const deltaLat = (radius / EARTH_RADIUS) * (180 / Math.PI); | ||
const deltaLon = deltaLat / Math.cos((lat * Math.PI) / 180); | ||
// Calculate new coordinates based on angle | ||
const newLat = lat + deltaLat * Math.sin(angle); | ||
const newLon = lon + deltaLon * Math.cos(angle); | ||
coordinates.push([newLon, newLat]); | ||
} | ||
return { | ||
type: "Polygon", | ||
coordinates: [coordinates], | ||
}; | ||
}; | ||
/** | ||
* @description Creates a TU bounding box from a GeoJson Polygon. | ||
*/ | ||
const getBoundingBoxFromGeoJsonPolygon = (polygon) => { | ||
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; | ||
} | ||
const latitudes = points.map(point => point[1]); | ||
const longitudes = points.map(point => point[0]); | ||
return { | ||
nw: { | ||
latitude: Math.max(...latitudes), | ||
longitude: Math.min(...longitudes), | ||
}, | ||
se: { | ||
latitude: Math.min(...latitudes), | ||
longitude: Math.max(...longitudes), | ||
}, | ||
}; | ||
}; | ||
/** | ||
* @description Creates a GeoJSON Polygon from a TU bounding box. | ||
*/ | ||
const getGeoJsonPolygonFromBoundingBox = (boundingBox) => { | ||
const { nw, se } = boundingBox; | ||
return { | ||
type: "Polygon", | ||
coordinates: [ | ||
[ | ||
[nw.longitude, nw.latitude], // Northwest corner | ||
[se.longitude, nw.latitude], // Northeast corner | ||
[se.longitude, se.latitude], // Southeast corner | ||
[nw.longitude, se.latitude], // Southwest corner | ||
[nw.longitude, nw.latitude], // Close the loop back to Northwest corner | ||
], | ||
], | ||
}; | ||
}; | ||
/** | ||
* @description Creates TU point coordinate from a GeoJSON Point. | ||
*/ | ||
const getPointCoordinateFromGeoJsonPoint = (point) => { | ||
return { latitude: point.coordinates[1], longitude: point.coordinates[0] }; | ||
}; | ||
/** | ||
* @description Gets the extreme point of a polygon in a given direction. | ||
* @param {object} params - The parameters object | ||
* @param {GeoJsonPolygon} params.polygon - The polygon to get the extreme point from | ||
* @param {("top" | "right" | "bottom" | "left")} params.direction - The direction to get the extreme point in | ||
* @returns {GeoJsonPoint} The extreme point in the given direction | ||
*/ | ||
const getExtremeGeoJsonPointFromPolygon = ({ polygon, direction, }) => { | ||
var _a, _b, _c; | ||
const polygonParsed = geoJsonPolygonSchema.safeParse(polygon); | ||
if (!polygonParsed.success) { | ||
return null; | ||
} | ||
const firstPoint = (_a = polygonParsed.data.coordinates[0]) === null || _a === void 0 ? void 0 : _a[0]; | ||
if (!firstPoint) { | ||
// Should never happen since the schema checks for it | ||
return null; | ||
} | ||
const extremePosition = (_b = polygonParsed.data.coordinates[0]) === null || _b === void 0 ? void 0 : _b.reduce((extremePoint, currentPoint) => { | ||
var _a, _b, _c, _d; | ||
switch (direction) { | ||
case "top": | ||
return currentPoint[1] > ((_a = extremePoint === null || extremePoint === void 0 ? void 0 : extremePoint[1]) !== null && _a !== void 0 ? _a : -Infinity) ? currentPoint : extremePoint !== null && extremePoint !== void 0 ? extremePoint : currentPoint; | ||
case "right": | ||
return currentPoint[0] > ((_b = extremePoint === null || extremePoint === void 0 ? void 0 : extremePoint[0]) !== null && _b !== void 0 ? _b : -Infinity) ? currentPoint : extremePoint !== null && extremePoint !== void 0 ? extremePoint : currentPoint; | ||
case "bottom": | ||
return currentPoint[1] < ((_c = extremePoint === null || extremePoint === void 0 ? void 0 : extremePoint[1]) !== null && _c !== void 0 ? _c : Infinity) ? currentPoint : extremePoint !== null && extremePoint !== void 0 ? extremePoint : currentPoint; | ||
case "left": | ||
return currentPoint[0] < ((_d = extremePoint === null || extremePoint === void 0 ? void 0 : extremePoint[0]) !== null && _d !== void 0 ? _d : Infinity) ? currentPoint : extremePoint !== null && extremePoint !== void 0 ? extremePoint : currentPoint; | ||
default: { | ||
throw new Error(`${direction} is not known`); | ||
} | ||
} | ||
}, (_c = polygonParsed.data.coordinates[0]) === null || _c === void 0 ? void 0 : _c[0]); | ||
return extremePosition | ||
? { | ||
type: "Point", | ||
coordinates: extremePosition, | ||
} | ||
: null; // Should never happen since the schema checks for it | ||
}; | ||
/** | ||
* Checks if a position is inside a linear ring. On edge is considered inside. | ||
*/ | ||
const isGeoJsonPositionInLinearRing = ({ position, linearRing, }) => { | ||
const linearRingParsed = geoJsonLinearRingSchema.safeParse(linearRing); | ||
if (!linearRingParsed.success) { | ||
return null; | ||
} | ||
let inside = false; | ||
const [x, y] = position; | ||
for (let i = 0, j = linearRingParsed.data.length - 1; i < linearRingParsed.data.length; j = i++) { | ||
const point1 = linearRingParsed.data[i]; | ||
const point2 = linearRingParsed.data[j]; | ||
if (!point1 || !point2) { | ||
continue; | ||
} | ||
const [xi, yi] = point1; | ||
const [xj, yj] = point2; | ||
const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi; | ||
if (intersect) { | ||
inside = !inside; | ||
} | ||
} | ||
return inside; | ||
}; | ||
/** | ||
* @description Checks if a point is inside a polygon. | ||
*/ | ||
const isGeoJsonPointInPolygon = ({ point, polygon, }) => { | ||
const polygonParsed = geoJsonPolygonSchema.safeParse(polygon); | ||
if (!polygonParsed.success) { | ||
return null; | ||
} | ||
return polygonParsed.data.coordinates.some(linearRing => isGeoJsonPositionInLinearRing({ position: point.coordinates, linearRing })); | ||
}; | ||
/** | ||
* Checks if polygon1 is fully contained within polygon2 | ||
*/ | ||
const isFullyContainedInGeoJsonPolygon = (polygon1, polygon2) => { | ||
const polygon1Parsed = geoJsonPolygonSchema.safeParse(polygon1); | ||
const polygon2Parsed = geoJsonPolygonSchema.safeParse(polygon2); | ||
// The schema checks more than a TypeScript type can represent | ||
if (!polygon1Parsed.success || !polygon2Parsed.success) { | ||
return null; | ||
} | ||
return polygon1Parsed.data.coordinates.every(linearRing => polygon2Parsed.data.coordinates.some(lr => linearRing.every(position => isGeoJsonPositionInLinearRing({ position, linearRing: lr })))); | ||
}; | ||
/** | ||
* @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 | ||
* @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) { | ||
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]) { | ||
return { | ||
type: "Polygon", | ||
coordinates: intersectionResult[0], | ||
}; | ||
} | ||
return { | ||
type: "MultiPolygon", | ||
coordinates: polygonClipping.intersection(polygon1.coordinates, polygon2.coordinates), | ||
}; | ||
}; | ||
//! These tools are used to bridge the gap with out poorly typed graphql types | ||
// Should be ideally be avoided but are needed until we fix the graphql types | ||
const isDoubleNestedCoords = (coords) => Array.isArray(coords) && | ||
Array.isArray(coords[0]) && | ||
Array.isArray(coords[0][0]) && | ||
typeof coords[0][0][0] === "number"; | ||
const isSingleCoords = (coords) => typeof coords[0] === "number"; | ||
/** | ||
* @description Returns coordinates in consistent format | ||
* @param inconsistentCoordinates Single point, array of points or nested array of points | ||
* @returns {GeoJsonPosition[]} Array of standardized coordinates | ||
*/ | ||
const coordinatesToStandardFormat = (inconsistentCoordinates) => { | ||
if (!inconsistentCoordinates) { | ||
return []; | ||
} | ||
if (isSingleCoords(inconsistentCoordinates)) { | ||
return [inconsistentCoordinates]; | ||
} | ||
if (isDoubleNestedCoords(inconsistentCoordinates)) { | ||
return inconsistentCoordinates[0] || []; | ||
} | ||
if (inconsistentCoordinates[0] && typeof inconsistentCoordinates[0][0] === "number") { | ||
return inconsistentCoordinates; | ||
} | ||
return []; | ||
}; | ||
/** | ||
* @description Extracts a point coordinate from a GeoJSON object. | ||
* @param geoObject A GeoJSON object. | ||
* @returns {PointCoordinate} A point coordinate. | ||
*/ | ||
const getPointCoordinateFromGeoJsonObject = (geoObject) => { | ||
if (!geoObject) { | ||
return undefined; | ||
} | ||
else if ("geometry" in geoObject) { | ||
return getPointCoordinateFromGeoJsonObject(geoObject.geometry); | ||
} | ||
else if ("coordinates" in geoObject && | ||
Array.isArray(geoObject.coordinates) && | ||
typeof geoObject.coordinates[0] === "number" && | ||
typeof geoObject.coordinates[1] === "number") { | ||
const [point] = coordinatesToStandardFormat(geoObject.coordinates); | ||
if (point) { | ||
return { latitude: point[1], longitude: point[0] }; | ||
} | ||
else { | ||
throw new Error(`Unable to extract point coordinate from ${JSON.stringify(geoObject)}`); | ||
} | ||
} | ||
else { | ||
throw new Error(`Unable to extract point coordinate from ${JSON.stringify(geoObject)}`); | ||
} | ||
}; | ||
/** | ||
* @description Extracts multiple point coordinates from a GeoJSON object. | ||
* @param geoObject A GeoJSON object. | ||
* @returns {PointCoordinate[]} An array of point coordinates. | ||
* @example getMultipleCoordinatesFromGeoJsonObject({ type: "Point", coordinates: [1, 2] }) // [{ longitude: 1, latitude: 2 }] | ||
*/ | ||
const getMultipleCoordinatesFromGeoJsonObject = (geoObject) => { | ||
if (!geoObject) { | ||
return undefined; | ||
} | ||
else if ("geometry" in geoObject) { | ||
return getMultipleCoordinatesFromGeoJsonObject(geoObject.geometry); | ||
} | ||
else if ("coordinates" in geoObject) { | ||
return coordinatesToStandardFormat(geoObject.coordinates).map(([longitude, latitude]) => ({ longitude, latitude })); | ||
} | ||
else { | ||
throw new Error(`Unable to extract point coordinate from ${JSON.stringify(geoObject)}`); | ||
} | ||
}; | ||
//* -------- Trackunit-invented schemas and types to extend the GeoJson spec -------- *// | ||
/** | ||
* Polygon geometry object that explicitly disallows holes. | ||
* | ||
* Same as geoJsonPolygonSchema but type disallows holes by | ||
* using tuple of one single linear ring instead of an array. | ||
*/ | ||
const tuGeoJsonPolygonNoHolesSchema = zod.z.strictObject({ | ||
//The type is still "Polygon" (not PolygonNoHoles or similar) since it's always | ||
//compliant with Polygon, just not the other way around | ||
type: zod.z.literal("Polygon"), | ||
//uses tuple instead of array to enforce only 1 linear ring aka the polygon itself | ||
coordinates: zod.z.tuple([geoJsonLinearRingSchema]), | ||
}); | ||
/** | ||
* Point radius object. | ||
* For when you wish to define an area by a point and a radius. | ||
* | ||
* radius is in meters | ||
*/ | ||
const tuGeoJsonPointRadiusSchema = zod.z.strictObject({ | ||
type: zod.z.literal("PointRadius"), | ||
coordinates: geoJsonPositionSchema, | ||
radius: zod.z.number().positive(), // in meters | ||
}); | ||
/** | ||
* A Polygon with exactly 5 points and 4 horizontal/vertical sides that form a normal rectangular box. | ||
*/ | ||
const tuGeoJsonRectangularBoxPolygonSchema = zod.z | ||
.strictObject({ | ||
type: zod.z.literal("Polygon"), | ||
coordinates: zod.z.array(geoJsonLinearRingSchema), | ||
}) | ||
.superRefine((data, ctx) => { | ||
const coordinates = data.coordinates[0]; | ||
// Validate polygon has exactly 5 points | ||
if ((coordinates === null || coordinates === void 0 ? void 0 : coordinates.length) !== 5) { | ||
ctx.addIssue({ | ||
code: zod.z.ZodIssueCode.custom, | ||
message: "Polygon must have exactly 5 coordinates to form a closed box.", | ||
}); | ||
return; | ||
} | ||
// Check each side is either horizontal or vertical | ||
for (let i = 0; i < 4; i++) { | ||
const point1 = coordinates[i]; | ||
const point2 = coordinates[i + 1]; | ||
if (point1 === undefined || point2 === undefined) { | ||
ctx.addIssue({ | ||
code: zod.z.ZodIssueCode.custom, | ||
message: "Each coordinate must be a defined point.", | ||
}); | ||
return; | ||
} | ||
const [x1, y1] = point1; | ||
const [x2, y2] = point2; | ||
// Ensure each line segment is either horizontal or vertical | ||
if (x1 !== x2 && y1 !== y2) { | ||
ctx.addIssue({ | ||
code: zod.z.ZodIssueCode.custom, | ||
message: "Polygon sides must be horizontal or vertical to form a box shape.", | ||
}); | ||
return; | ||
} | ||
} | ||
}); | ||
/** | ||
* Group an array of items by a key. | ||
@@ -1553,3 +1064,2 @@ * | ||
exports.DateTimeFormat = DateTimeFormat; | ||
exports.EARTH_RADIUS = EARTH_RADIUS; | ||
exports.align = align; | ||
@@ -1564,3 +1074,2 @@ exports.alphabeticallySort = alphabeticallySort; | ||
exports.convertYardsToMeters = convertYardsToMeters; | ||
exports.coordinatesToStandardFormat = coordinatesToStandardFormat; | ||
exports.dateCompare = dateCompare; | ||
@@ -1578,26 +1087,6 @@ exports.deleteUndefinedKeys = deleteUndefinedKeys; | ||
exports.fuzzySearch = fuzzySearch; | ||
exports.geoJsonBboxSchema = geoJsonBboxSchema; | ||
exports.geoJsonGeometrySchema = geoJsonGeometrySchema; | ||
exports.geoJsonLineStringSchema = geoJsonLineStringSchema; | ||
exports.geoJsonLinearRingSchema = geoJsonLinearRingSchema; | ||
exports.geoJsonMultiLineStringSchema = geoJsonMultiLineStringSchema; | ||
exports.geoJsonMultiPointSchema = geoJsonMultiPointSchema; | ||
exports.geoJsonMultiPolygonSchema = geoJsonMultiPolygonSchema; | ||
exports.geoJsonPointSchema = geoJsonPointSchema; | ||
exports.geoJsonPolygonSchema = geoJsonPolygonSchema; | ||
exports.geoJsonPositionSchema = geoJsonPositionSchema; | ||
exports.getBboxFromGeoJsonPolygon = getBboxFromGeoJsonPolygon; | ||
exports.getBoundingBoxFromGeoJsonPolygon = getBoundingBoxFromGeoJsonPolygon; | ||
exports.getDifferenceBetweenDates = getDifferenceBetweenDates; | ||
exports.getEndOfDay = getEndOfDay; | ||
exports.getExtremeGeoJsonPointFromPolygon = getExtremeGeoJsonPointFromPolygon; | ||
exports.getFirstLevelObjectPropertyDifferences = getFirstLevelObjectPropertyDifferences; | ||
exports.getGeoJsonPolygonFromBoundingBox = getGeoJsonPolygonFromBoundingBox; | ||
exports.getGeoJsonPolygonIntersection = getGeoJsonPolygonIntersection; | ||
exports.getISOStringFromDate = getISOStringFromDate; | ||
exports.getMultipleCoordinatesFromGeoJsonObject = getMultipleCoordinatesFromGeoJsonObject; | ||
exports.getPointCoordinateFromGeoJsonObject = getPointCoordinateFromGeoJsonObject; | ||
exports.getPointCoordinateFromGeoJsonPoint = getPointCoordinateFromGeoJsonPoint; | ||
exports.getPolygonFromBbox = getPolygonFromBbox; | ||
exports.getPolygonFromPointAndRadius = getPolygonFromPointAndRadius; | ||
exports.getResizedDimensions = getResizedDimensions; | ||
@@ -1610,5 +1099,2 @@ exports.getStartOfDay = getStartOfDay; | ||
exports.isArrayEqual = isArrayEqual; | ||
exports.isFullyContainedInGeoJsonPolygon = isFullyContainedInGeoJsonPolygon; | ||
exports.isGeoJsonPointInPolygon = isGeoJsonPointInPolygon; | ||
exports.isGeoJsonPositionInLinearRing = isGeoJsonPositionInLinearRing; | ||
exports.isSorted = isSorted; | ||
@@ -1641,5 +1127,2 @@ exports.isUUID = isUUID; | ||
exports.truthy = truthy; | ||
exports.tuGeoJsonPointRadiusSchema = tuGeoJsonPointRadiusSchema; | ||
exports.tuGeoJsonPolygonNoHolesSchema = tuGeoJsonPolygonNoHolesSchema; | ||
exports.tuGeoJsonRectangularBoxPolygonSchema = tuGeoJsonRectangularBoxPolygonSchema; | ||
exports.unionArraysByKey = unionArraysByKey; | ||
@@ -1646,0 +1129,0 @@ exports.uuidv3 = uuidv3; |
491
index.esm.js
@@ -1,3 +0,1 @@ | ||
import { z } from 'zod'; | ||
import { intersection as intersection$1 } from 'polygon-clipping'; | ||
import { v3, v4, v5 } from 'uuid'; | ||
@@ -416,490 +414,3 @@ | ||
// * NOTE: For simplicity these tools are built for 2D coordinate space only! | ||
/** | ||
* A Position is an array of coordinates. [x, y] | ||
* https://tools.ietf.org/html/rfc7946#section-3.1.1 | ||
*/ | ||
const geoJsonPositionSchema = z.tuple([z.number(), z.number()]); | ||
/** | ||
* Point geometry object. | ||
* https://tools.ietf.org/html/rfc7946#section-3.1.2 | ||
*/ | ||
const geoJsonPointSchema = z.strictObject({ | ||
type: z.literal("Point"), | ||
coordinates: geoJsonPositionSchema, | ||
}); | ||
/** | ||
* MultiPoint geometry object. | ||
* https://tools.ietf.org/html/rfc7946#section-3.1.3 | ||
*/ | ||
const geoJsonMultiPointSchema = z.strictObject({ | ||
type: z.literal("MultiPoint"), | ||
coordinates: z.array(geoJsonPositionSchema), | ||
}); | ||
/** | ||
* LineString geometry object. | ||
* Minimum length of 2 positions. | ||
* https://tools.ietf.org/html/rfc7946#section-3.1.4 | ||
*/ | ||
const geoJsonLineStringSchema = z.strictObject({ | ||
type: z.literal("LineString"), | ||
coordinates: z.array(geoJsonPositionSchema).min(2), | ||
}); | ||
/** | ||
* MultiLineString geometry object. | ||
* https://tools.ietf.org/html/rfc7946#section-3.1.5 | ||
*/ | ||
const geoJsonMultiLineStringSchema = z.strictObject({ | ||
type: z.literal("MultiLineString"), | ||
coordinates: z.array(z.array(geoJsonPositionSchema)), | ||
}); | ||
/** | ||
* Helper type for reuse across polygon schemas. | ||
* | ||
* - A linear ring is a closed LineString with four or more positions. | ||
* - The first and last positions are equivalent, and they MUST contain | ||
identical values; their representation SHOULD also be identical | ||
* - A linear ring is the boundary of a surface or the boundary of a | ||
hole in a surface | ||
* - A linear ring MUST follow the right-hand rule with respect to the | ||
area it bounds, i.e., exterior rings are counterclockwise, and | ||
holes are clockwise | ||
*/ | ||
const geoJsonLinearRingSchema = z | ||
.array(geoJsonPositionSchema) | ||
.min(4, { | ||
message: "Coordinates array must contain at least 4 positions. 3 to make a non-line shape and 1 to close the shape (duplicate of first)", | ||
}) | ||
.superRefine((coords, ctx) => { | ||
const first = coords[0]; | ||
const last = coords[coords.length - 1]; | ||
// Check if first and last coordinates match | ||
if (JSON.stringify(first) !== JSON.stringify(last)) { | ||
ctx.addIssue({ | ||
code: z.ZodIssueCode.custom, | ||
message: "First and last coordinate positions must be identical (to close the linear ring aka polygon).", | ||
}); | ||
} | ||
// Check if consecutive points are identical (excluding first and last) | ||
for (let i = 1; i < coords.length - 1; i++) { | ||
if (JSON.stringify(coords[i]) === JSON.stringify(coords[i - 1])) { | ||
ctx.addIssue({ | ||
code: z.ZodIssueCode.custom, | ||
message: `Consecutive coordinates at index ${i - 1} and ${i} should not be identical.`, | ||
}); | ||
} | ||
} | ||
}); | ||
/** | ||
* Polygon geometry object. | ||
* https://tools.ietf.org/html/rfc7946#section-3.1.6 | ||
*/ | ||
const geoJsonPolygonSchema = z.strictObject({ | ||
type: z.literal("Polygon"), | ||
coordinates: z.array(geoJsonLinearRingSchema), | ||
}); | ||
/** | ||
* MultiPolygon geometry object. | ||
* https://tools.ietf.org/html/rfc7946#section-3.1.7 | ||
*/ | ||
const geoJsonMultiPolygonSchema = z.strictObject({ | ||
type: z.literal("MultiPolygon"), | ||
coordinates: z.array(z.array(geoJsonLinearRingSchema)), | ||
}); | ||
// The same for Geometry, GeometryCollection, GeoJsonProperties, Feature, FeatureCollection, etc. | ||
const geoJsonGeometrySchema = z.union([ | ||
geoJsonPointSchema, | ||
geoJsonMultiPointSchema, | ||
geoJsonLineStringSchema, | ||
geoJsonMultiLineStringSchema, | ||
geoJsonPolygonSchema, | ||
geoJsonMultiPolygonSchema, | ||
]); | ||
//* -------- Bbox -------- *// | ||
/** | ||
* 2D bounding box of the GeoJSON object. | ||
* The value of the Bbox member is an array of length 4. | ||
* | ||
* [min_lon, min_lat, max_lon, max_lat] | ||
*/ | ||
const geoJsonBboxSchema = z | ||
.tuple([z.number(), z.number(), z.number(), z.number()]) | ||
.refine(([minLng, minLat, maxLng, maxLat]) => maxLng > minLng && maxLat > minLat, { | ||
message: "Invalid bounding box: maxLng should be greater than minLng, and maxLat should be greater than minLat.", | ||
}); | ||
const EARTH_RADIUS = 6378137; // Earth’s mean radius in meters | ||
/** | ||
* @description Creates a polygon (with no holes) from a bounding box. | ||
*/ | ||
const getPolygonFromBbox = (bbox) => { | ||
const [minLon, minLat, maxLon, maxLat] = bbox; | ||
return { | ||
type: "Polygon", | ||
coordinates: [ | ||
[ | ||
[minLon, minLat], | ||
[maxLon, minLat], | ||
[maxLon, maxLat], | ||
[minLon, maxLat], | ||
[minLon, minLat], | ||
], | ||
], | ||
}; | ||
}; | ||
/** | ||
* @description Creates a bounding box from a GeoJSON Polygon. | ||
*/ | ||
const getBboxFromGeoJsonPolygon = (polygon) => { | ||
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; | ||
} | ||
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)]; | ||
}; | ||
/** | ||
* @description Creates a round polygon from a point and a radius. | ||
*/ | ||
const getPolygonFromPointAndRadius = (point, radius) => { | ||
const [lon, lat] = point.coordinates; | ||
// Adjust the number of points based on radius (resolution) | ||
const pointsCount = Math.max(32, Math.floor(radius / 100)); // More points for larger radius | ||
const angleStep = (2 * Math.PI) / pointsCount; | ||
const coordinates = []; | ||
for (let i = 0; i <= pointsCount; i++) { | ||
const angle = i * angleStep; | ||
// Calculate offset in latitude and longitude | ||
const deltaLat = (radius / EARTH_RADIUS) * (180 / Math.PI); | ||
const deltaLon = deltaLat / Math.cos((lat * Math.PI) / 180); | ||
// Calculate new coordinates based on angle | ||
const newLat = lat + deltaLat * Math.sin(angle); | ||
const newLon = lon + deltaLon * Math.cos(angle); | ||
coordinates.push([newLon, newLat]); | ||
} | ||
return { | ||
type: "Polygon", | ||
coordinates: [coordinates], | ||
}; | ||
}; | ||
/** | ||
* @description Creates a TU bounding box from a GeoJson Polygon. | ||
*/ | ||
const getBoundingBoxFromGeoJsonPolygon = (polygon) => { | ||
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; | ||
} | ||
const latitudes = points.map(point => point[1]); | ||
const longitudes = points.map(point => point[0]); | ||
return { | ||
nw: { | ||
latitude: Math.max(...latitudes), | ||
longitude: Math.min(...longitudes), | ||
}, | ||
se: { | ||
latitude: Math.min(...latitudes), | ||
longitude: Math.max(...longitudes), | ||
}, | ||
}; | ||
}; | ||
/** | ||
* @description Creates a GeoJSON Polygon from a TU bounding box. | ||
*/ | ||
const getGeoJsonPolygonFromBoundingBox = (boundingBox) => { | ||
const { nw, se } = boundingBox; | ||
return { | ||
type: "Polygon", | ||
coordinates: [ | ||
[ | ||
[nw.longitude, nw.latitude], // Northwest corner | ||
[se.longitude, nw.latitude], // Northeast corner | ||
[se.longitude, se.latitude], // Southeast corner | ||
[nw.longitude, se.latitude], // Southwest corner | ||
[nw.longitude, nw.latitude], // Close the loop back to Northwest corner | ||
], | ||
], | ||
}; | ||
}; | ||
/** | ||
* @description Creates TU point coordinate from a GeoJSON Point. | ||
*/ | ||
const getPointCoordinateFromGeoJsonPoint = (point) => { | ||
return { latitude: point.coordinates[1], longitude: point.coordinates[0] }; | ||
}; | ||
/** | ||
* @description Gets the extreme point of a polygon in a given direction. | ||
* @param {object} params - The parameters object | ||
* @param {GeoJsonPolygon} params.polygon - The polygon to get the extreme point from | ||
* @param {("top" | "right" | "bottom" | "left")} params.direction - The direction to get the extreme point in | ||
* @returns {GeoJsonPoint} The extreme point in the given direction | ||
*/ | ||
const getExtremeGeoJsonPointFromPolygon = ({ polygon, direction, }) => { | ||
var _a, _b, _c; | ||
const polygonParsed = geoJsonPolygonSchema.safeParse(polygon); | ||
if (!polygonParsed.success) { | ||
return null; | ||
} | ||
const firstPoint = (_a = polygonParsed.data.coordinates[0]) === null || _a === void 0 ? void 0 : _a[0]; | ||
if (!firstPoint) { | ||
// Should never happen since the schema checks for it | ||
return null; | ||
} | ||
const extremePosition = (_b = polygonParsed.data.coordinates[0]) === null || _b === void 0 ? void 0 : _b.reduce((extremePoint, currentPoint) => { | ||
var _a, _b, _c, _d; | ||
switch (direction) { | ||
case "top": | ||
return currentPoint[1] > ((_a = extremePoint === null || extremePoint === void 0 ? void 0 : extremePoint[1]) !== null && _a !== void 0 ? _a : -Infinity) ? currentPoint : extremePoint !== null && extremePoint !== void 0 ? extremePoint : currentPoint; | ||
case "right": | ||
return currentPoint[0] > ((_b = extremePoint === null || extremePoint === void 0 ? void 0 : extremePoint[0]) !== null && _b !== void 0 ? _b : -Infinity) ? currentPoint : extremePoint !== null && extremePoint !== void 0 ? extremePoint : currentPoint; | ||
case "bottom": | ||
return currentPoint[1] < ((_c = extremePoint === null || extremePoint === void 0 ? void 0 : extremePoint[1]) !== null && _c !== void 0 ? _c : Infinity) ? currentPoint : extremePoint !== null && extremePoint !== void 0 ? extremePoint : currentPoint; | ||
case "left": | ||
return currentPoint[0] < ((_d = extremePoint === null || extremePoint === void 0 ? void 0 : extremePoint[0]) !== null && _d !== void 0 ? _d : Infinity) ? currentPoint : extremePoint !== null && extremePoint !== void 0 ? extremePoint : currentPoint; | ||
default: { | ||
throw new Error(`${direction} is not known`); | ||
} | ||
} | ||
}, (_c = polygonParsed.data.coordinates[0]) === null || _c === void 0 ? void 0 : _c[0]); | ||
return extremePosition | ||
? { | ||
type: "Point", | ||
coordinates: extremePosition, | ||
} | ||
: null; // Should never happen since the schema checks for it | ||
}; | ||
/** | ||
* Checks if a position is inside a linear ring. On edge is considered inside. | ||
*/ | ||
const isGeoJsonPositionInLinearRing = ({ position, linearRing, }) => { | ||
const linearRingParsed = geoJsonLinearRingSchema.safeParse(linearRing); | ||
if (!linearRingParsed.success) { | ||
return null; | ||
} | ||
let inside = false; | ||
const [x, y] = position; | ||
for (let i = 0, j = linearRingParsed.data.length - 1; i < linearRingParsed.data.length; j = i++) { | ||
const point1 = linearRingParsed.data[i]; | ||
const point2 = linearRingParsed.data[j]; | ||
if (!point1 || !point2) { | ||
continue; | ||
} | ||
const [xi, yi] = point1; | ||
const [xj, yj] = point2; | ||
const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi; | ||
if (intersect) { | ||
inside = !inside; | ||
} | ||
} | ||
return inside; | ||
}; | ||
/** | ||
* @description Checks if a point is inside a polygon. | ||
*/ | ||
const isGeoJsonPointInPolygon = ({ point, polygon, }) => { | ||
const polygonParsed = geoJsonPolygonSchema.safeParse(polygon); | ||
if (!polygonParsed.success) { | ||
return null; | ||
} | ||
return polygonParsed.data.coordinates.some(linearRing => isGeoJsonPositionInLinearRing({ position: point.coordinates, linearRing })); | ||
}; | ||
/** | ||
* Checks if polygon1 is fully contained within polygon2 | ||
*/ | ||
const isFullyContainedInGeoJsonPolygon = (polygon1, polygon2) => { | ||
const polygon1Parsed = geoJsonPolygonSchema.safeParse(polygon1); | ||
const polygon2Parsed = geoJsonPolygonSchema.safeParse(polygon2); | ||
// The schema checks more than a TypeScript type can represent | ||
if (!polygon1Parsed.success || !polygon2Parsed.success) { | ||
return null; | ||
} | ||
return polygon1Parsed.data.coordinates.every(linearRing => polygon2Parsed.data.coordinates.some(lr => linearRing.every(position => isGeoJsonPositionInLinearRing({ position, linearRing: lr })))); | ||
}; | ||
/** | ||
* @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 | ||
* @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) { | ||
return null; | ||
} | ||
if (isFullyContainedInGeoJsonPolygon(polygon1, polygon2)) { | ||
return polygon1; | ||
} | ||
if (isFullyContainedInGeoJsonPolygon(polygon2, polygon1)) { | ||
return polygon2; | ||
} | ||
const intersectionResult = intersection$1(polygon1.coordinates, polygon2.coordinates); | ||
if (intersectionResult.length === 1 && intersectionResult[0]) { | ||
return { | ||
type: "Polygon", | ||
coordinates: intersectionResult[0], | ||
}; | ||
} | ||
return { | ||
type: "MultiPolygon", | ||
coordinates: intersection$1(polygon1.coordinates, polygon2.coordinates), | ||
}; | ||
}; | ||
//! These tools are used to bridge the gap with out poorly typed graphql types | ||
// Should be ideally be avoided but are needed until we fix the graphql types | ||
const isDoubleNestedCoords = (coords) => Array.isArray(coords) && | ||
Array.isArray(coords[0]) && | ||
Array.isArray(coords[0][0]) && | ||
typeof coords[0][0][0] === "number"; | ||
const isSingleCoords = (coords) => typeof coords[0] === "number"; | ||
/** | ||
* @description Returns coordinates in consistent format | ||
* @param inconsistentCoordinates Single point, array of points or nested array of points | ||
* @returns {GeoJsonPosition[]} Array of standardized coordinates | ||
*/ | ||
const coordinatesToStandardFormat = (inconsistentCoordinates) => { | ||
if (!inconsistentCoordinates) { | ||
return []; | ||
} | ||
if (isSingleCoords(inconsistentCoordinates)) { | ||
return [inconsistentCoordinates]; | ||
} | ||
if (isDoubleNestedCoords(inconsistentCoordinates)) { | ||
return inconsistentCoordinates[0] || []; | ||
} | ||
if (inconsistentCoordinates[0] && typeof inconsistentCoordinates[0][0] === "number") { | ||
return inconsistentCoordinates; | ||
} | ||
return []; | ||
}; | ||
/** | ||
* @description Extracts a point coordinate from a GeoJSON object. | ||
* @param geoObject A GeoJSON object. | ||
* @returns {PointCoordinate} A point coordinate. | ||
*/ | ||
const getPointCoordinateFromGeoJsonObject = (geoObject) => { | ||
if (!geoObject) { | ||
return undefined; | ||
} | ||
else if ("geometry" in geoObject) { | ||
return getPointCoordinateFromGeoJsonObject(geoObject.geometry); | ||
} | ||
else if ("coordinates" in geoObject && | ||
Array.isArray(geoObject.coordinates) && | ||
typeof geoObject.coordinates[0] === "number" && | ||
typeof geoObject.coordinates[1] === "number") { | ||
const [point] = coordinatesToStandardFormat(geoObject.coordinates); | ||
if (point) { | ||
return { latitude: point[1], longitude: point[0] }; | ||
} | ||
else { | ||
throw new Error(`Unable to extract point coordinate from ${JSON.stringify(geoObject)}`); | ||
} | ||
} | ||
else { | ||
throw new Error(`Unable to extract point coordinate from ${JSON.stringify(geoObject)}`); | ||
} | ||
}; | ||
/** | ||
* @description Extracts multiple point coordinates from a GeoJSON object. | ||
* @param geoObject A GeoJSON object. | ||
* @returns {PointCoordinate[]} An array of point coordinates. | ||
* @example getMultipleCoordinatesFromGeoJsonObject({ type: "Point", coordinates: [1, 2] }) // [{ longitude: 1, latitude: 2 }] | ||
*/ | ||
const getMultipleCoordinatesFromGeoJsonObject = (geoObject) => { | ||
if (!geoObject) { | ||
return undefined; | ||
} | ||
else if ("geometry" in geoObject) { | ||
return getMultipleCoordinatesFromGeoJsonObject(geoObject.geometry); | ||
} | ||
else if ("coordinates" in geoObject) { | ||
return coordinatesToStandardFormat(geoObject.coordinates).map(([longitude, latitude]) => ({ longitude, latitude })); | ||
} | ||
else { | ||
throw new Error(`Unable to extract point coordinate from ${JSON.stringify(geoObject)}`); | ||
} | ||
}; | ||
//* -------- Trackunit-invented schemas and types to extend the GeoJson spec -------- *// | ||
/** | ||
* Polygon geometry object that explicitly disallows holes. | ||
* | ||
* Same as geoJsonPolygonSchema but type disallows holes by | ||
* using tuple of one single linear ring instead of an array. | ||
*/ | ||
const tuGeoJsonPolygonNoHolesSchema = z.strictObject({ | ||
//The type is still "Polygon" (not PolygonNoHoles or similar) since it's always | ||
//compliant with Polygon, just not the other way around | ||
type: z.literal("Polygon"), | ||
//uses tuple instead of array to enforce only 1 linear ring aka the polygon itself | ||
coordinates: z.tuple([geoJsonLinearRingSchema]), | ||
}); | ||
/** | ||
* Point radius object. | ||
* For when you wish to define an area by a point and a radius. | ||
* | ||
* radius is in meters | ||
*/ | ||
const tuGeoJsonPointRadiusSchema = z.strictObject({ | ||
type: z.literal("PointRadius"), | ||
coordinates: geoJsonPositionSchema, | ||
radius: z.number().positive(), // in meters | ||
}); | ||
/** | ||
* A Polygon with exactly 5 points and 4 horizontal/vertical sides that form a normal rectangular box. | ||
*/ | ||
const tuGeoJsonRectangularBoxPolygonSchema = z | ||
.strictObject({ | ||
type: z.literal("Polygon"), | ||
coordinates: z.array(geoJsonLinearRingSchema), | ||
}) | ||
.superRefine((data, ctx) => { | ||
const coordinates = data.coordinates[0]; | ||
// Validate polygon has exactly 5 points | ||
if ((coordinates === null || coordinates === void 0 ? void 0 : coordinates.length) !== 5) { | ||
ctx.addIssue({ | ||
code: z.ZodIssueCode.custom, | ||
message: "Polygon must have exactly 5 coordinates to form a closed box.", | ||
}); | ||
return; | ||
} | ||
// Check each side is either horizontal or vertical | ||
for (let i = 0; i < 4; i++) { | ||
const point1 = coordinates[i]; | ||
const point2 = coordinates[i + 1]; | ||
if (point1 === undefined || point2 === undefined) { | ||
ctx.addIssue({ | ||
code: z.ZodIssueCode.custom, | ||
message: "Each coordinate must be a defined point.", | ||
}); | ||
return; | ||
} | ||
const [x1, y1] = point1; | ||
const [x2, y2] = point2; | ||
// Ensure each line segment is either horizontal or vertical | ||
if (x1 !== x2 && y1 !== y2) { | ||
ctx.addIssue({ | ||
code: z.ZodIssueCode.custom, | ||
message: "Polygon sides must be horizontal or vertical to form a box shape.", | ||
}); | ||
return; | ||
} | ||
} | ||
}); | ||
/** | ||
* Group an array of items by a key. | ||
@@ -1550,2 +1061,2 @@ * | ||
export { DateTimeFormat, EARTH_RADIUS, HoursAndMinutesFormat, align, alphabeticallySort, arrayLengthCompare, arrayNotEmpty, booleanCompare, capitalize, convertBlobToBase64, convertMetersToYards, convertYardsToMeters, coordinatesToStandardFormat, dateCompare, deleteUndefinedKeys, difference, doNothing, enumFromValue, enumFromValueTypesafe, enumOrUndefinedFromValue, exhaustiveCheck, filterByMultiple, formatAddress, formatCoordinates, fuzzySearch, geoJsonBboxSchema, geoJsonGeometrySchema, geoJsonLineStringSchema, geoJsonLinearRingSchema, geoJsonMultiLineStringSchema, geoJsonMultiPointSchema, geoJsonMultiPolygonSchema, geoJsonPointSchema, geoJsonPolygonSchema, geoJsonPositionSchema, getBboxFromGeoJsonPolygon, getBoundingBoxFromGeoJsonPolygon, getDifferenceBetweenDates, getEndOfDay, getExtremeGeoJsonPointFromPolygon, getFirstLevelObjectPropertyDifferences, getGeoJsonPolygonFromBoundingBox, getGeoJsonPolygonIntersection, getISOStringFromDate, getMultipleCoordinatesFromGeoJsonObject, getPointCoordinateFromGeoJsonObject, getPointCoordinateFromGeoJsonPoint, getPolygonFromBbox, getPolygonFromPointAndRadius, getResizedDimensions, getStartOfDay, groupBy, groupTinyDataToOthers, hourIntervals, intersection, isArrayEqual, isFullyContainedInGeoJsonPolygon, isGeoJsonPointInPolygon, isGeoJsonPositionInLinearRing, isSorted, isUUID, isValidImage, nonNullable, numberCompare, numberCompareUnknownAfterHighest, objNotEmpty, objectEntries, objectFromEntries, objectKeys, objectValues, pick, removeLeftPadding, resizeBlob, resizeImage, size, stringCompare, stringCompareFromKey, stringNaturalCompare, stripHiddenCharacters, titleCase, toID, toIDs, toUUID, trimIds, trimPath, truthy, tuGeoJsonPointRadiusSchema, tuGeoJsonPolygonNoHolesSchema, tuGeoJsonRectangularBoxPolygonSchema, unionArraysByKey, uuidv3, uuidv4, uuidv5 }; | ||
export { DateTimeFormat, HoursAndMinutesFormat, align, alphabeticallySort, arrayLengthCompare, arrayNotEmpty, booleanCompare, capitalize, convertBlobToBase64, convertMetersToYards, convertYardsToMeters, dateCompare, deleteUndefinedKeys, difference, doNothing, enumFromValue, enumFromValueTypesafe, enumOrUndefinedFromValue, exhaustiveCheck, filterByMultiple, formatAddress, formatCoordinates, fuzzySearch, getDifferenceBetweenDates, getEndOfDay, getFirstLevelObjectPropertyDifferences, getISOStringFromDate, getResizedDimensions, getStartOfDay, groupBy, groupTinyDataToOthers, hourIntervals, intersection, isArrayEqual, isSorted, isUUID, isValidImage, nonNullable, numberCompare, numberCompareUnknownAfterHighest, objNotEmpty, objectEntries, objectFromEntries, objectKeys, objectValues, pick, removeLeftPadding, resizeBlob, resizeImage, size, stringCompare, stringCompareFromKey, stringNaturalCompare, stripHiddenCharacters, titleCase, toID, toIDs, toUUID, trimIds, trimPath, truthy, unionArraysByKey, uuidv3, uuidv4, uuidv5 }; |
{ | ||
"name": "@trackunit/shared-utils", | ||
"version": "0.0.85", | ||
"version": "0.0.86", | ||
"repository": "https://github.com/Trackunit/manager", | ||
@@ -10,5 +10,3 @@ "license": "SEE LICENSE IN LICENSE.txt", | ||
"dependencies": { | ||
"uuid": "^10.0.0", | ||
"zod": "3.22.4", | ||
"polygon-clipping": "^0.15.7" | ||
"uuid": "^10.0.0" | ||
}, | ||
@@ -15,0 +13,0 @@ "module": "./index.esm.js", |
@@ -12,6 +12,2 @@ export * from "./addressUtils"; | ||
export * from "./filter"; | ||
export * from "./GeoJson/GeoJsonSchemas"; | ||
export * from "./GeoJson/GeoJsonUtils"; | ||
export * from "./GeoJson/TUGeoJsonObjectBridgeUtils"; | ||
export * from "./GeoJson/TuGeoJsonSchemas"; | ||
export * from "./groupBy/groupBy"; | ||
@@ -18,0 +14,0 @@ export * from "./GroupingUtility"; |
1
104260
34
2824
- Removedpolygon-clipping@^0.15.7
- Removedzod@3.22.4
- Removedpolygon-clipping@0.15.7(transitive)
- Removedrobust-predicates@3.0.2(transitive)
- Removedsplaytree@3.1.2(transitive)
- Removedzod@3.22.4(transitive)