@trackunit/geo-json-utils 0.0.1

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.esm.js ADDED
@@ -0,0 +1,491 @@
1
+ import { z } from 'zod';
2
+ import { intersection } from 'polygon-clipping';
3
+
4
+ // * NOTE: For simplicity these tools are built for 2D coordinate space only!
5
+ /**
6
+ * A Position is an array of coordinates. [x, y]
7
+ * https://tools.ietf.org/html/rfc7946#section-3.1.1
8
+ */
9
+ const geoJsonPositionSchema = z.tuple([z.number(), z.number()]);
10
+ /**
11
+ * Point geometry object.
12
+ * https://tools.ietf.org/html/rfc7946#section-3.1.2
13
+ */
14
+ const geoJsonPointSchema = z.strictObject({
15
+ type: z.literal("Point"),
16
+ coordinates: geoJsonPositionSchema,
17
+ });
18
+ /**
19
+ * MultiPoint geometry object.
20
+ * https://tools.ietf.org/html/rfc7946#section-3.1.3
21
+ */
22
+ const geoJsonMultiPointSchema = z.strictObject({
23
+ type: z.literal("MultiPoint"),
24
+ coordinates: z.array(geoJsonPositionSchema),
25
+ });
26
+ /**
27
+ * LineString geometry object.
28
+ * Minimum length of 2 positions.
29
+ * https://tools.ietf.org/html/rfc7946#section-3.1.4
30
+ */
31
+ const geoJsonLineStringSchema = z.strictObject({
32
+ type: z.literal("LineString"),
33
+ coordinates: z.array(geoJsonPositionSchema).min(2),
34
+ });
35
+ /**
36
+ * MultiLineString geometry object.
37
+ * https://tools.ietf.org/html/rfc7946#section-3.1.5
38
+ */
39
+ const geoJsonMultiLineStringSchema = z.strictObject({
40
+ type: z.literal("MultiLineString"),
41
+ coordinates: z.array(z.array(geoJsonPositionSchema)),
42
+ });
43
+ /**
44
+ * Helper type for reuse across polygon schemas.
45
+ *
46
+ * - A linear ring is a closed LineString with four or more positions.
47
+ * - The first and last positions are equivalent, and they MUST contain
48
+ identical values; their representation SHOULD also be identical
49
+ * - A linear ring is the boundary of a surface or the boundary of a
50
+ hole in a surface
51
+ * - A linear ring MUST follow the right-hand rule with respect to the
52
+ area it bounds, i.e., exterior rings are counterclockwise, and
53
+ holes are clockwise
54
+ */
55
+ const geoJsonLinearRingSchema = z
56
+ .array(geoJsonPositionSchema)
57
+ .min(4, {
58
+ 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)",
59
+ })
60
+ .superRefine((coords, ctx) => {
61
+ const first = coords[0];
62
+ const last = coords[coords.length - 1];
63
+ // Check if first and last coordinates match
64
+ if (JSON.stringify(first) !== JSON.stringify(last)) {
65
+ ctx.addIssue({
66
+ code: z.ZodIssueCode.custom,
67
+ message: "First and last coordinate positions must be identical (to close the linear ring aka polygon).",
68
+ });
69
+ }
70
+ // Check if consecutive points are identical (excluding first and last)
71
+ for (let i = 1; i < coords.length - 1; i++) {
72
+ if (JSON.stringify(coords[i]) === JSON.stringify(coords[i - 1])) {
73
+ ctx.addIssue({
74
+ code: z.ZodIssueCode.custom,
75
+ message: `Consecutive coordinates at index ${i - 1} and ${i} should not be identical.`,
76
+ });
77
+ }
78
+ }
79
+ });
80
+ /**
81
+ * Polygon geometry object.
82
+ * https://tools.ietf.org/html/rfc7946#section-3.1.6
83
+ */
84
+ const geoJsonPolygonSchema = z.strictObject({
85
+ type: z.literal("Polygon"),
86
+ coordinates: z.array(geoJsonLinearRingSchema),
87
+ });
88
+ /**
89
+ * MultiPolygon geometry object.
90
+ * https://tools.ietf.org/html/rfc7946#section-3.1.7
91
+ */
92
+ const geoJsonMultiPolygonSchema = z.strictObject({
93
+ type: z.literal("MultiPolygon"),
94
+ coordinates: z.array(z.array(geoJsonLinearRingSchema)),
95
+ });
96
+ // The same for Geometry, GeometryCollection, GeoJsonProperties, Feature, FeatureCollection, etc.
97
+ const geoJsonGeometrySchema = z.union([
98
+ geoJsonPointSchema,
99
+ geoJsonMultiPointSchema,
100
+ geoJsonLineStringSchema,
101
+ geoJsonMultiLineStringSchema,
102
+ geoJsonPolygonSchema,
103
+ geoJsonMultiPolygonSchema,
104
+ ]);
105
+ //* -------- Bbox -------- *//
106
+ /**
107
+ * 2D bounding box of the GeoJSON object.
108
+ * The value of the Bbox member is an array of length 4.
109
+ *
110
+ * [min_lon, min_lat, max_lon, max_lat]
111
+ */
112
+ const geoJsonBboxSchema = z
113
+ .tuple([z.number(), z.number(), z.number(), z.number()])
114
+ .refine(([minLng, minLat, maxLng, maxLat]) => maxLng > minLng && maxLat > minLat, {
115
+ message: "Invalid bounding box: maxLng should be greater than minLng, and maxLat should be greater than minLat.",
116
+ });
117
+
118
+ const EARTH_RADIUS = 6378137; // Earth’s mean radius in meters
119
+ /**
120
+ * @description Creates a polygon (with no holes) from a bounding box.
121
+ */
122
+ const getPolygonFromBbox = (bbox) => {
123
+ const [minLon, minLat, maxLon, maxLat] = bbox;
124
+ return {
125
+ type: "Polygon",
126
+ coordinates: [
127
+ [
128
+ [minLon, minLat],
129
+ [maxLon, minLat],
130
+ [maxLon, maxLat],
131
+ [minLon, maxLat],
132
+ [minLon, minLat],
133
+ ],
134
+ ],
135
+ };
136
+ };
137
+ /**
138
+ * @description Creates a bounding box from a GeoJSON Polygon.
139
+ */
140
+ const getBboxFromGeoJsonPolygon = (polygon) => {
141
+ const polygonParsed = geoJsonPolygonSchema.safeParse(polygon);
142
+ if (!polygonParsed.success) {
143
+ return null;
144
+ }
145
+ const points = polygonParsed.data.coordinates[0];
146
+ if (!points) {
147
+ // Should never happen since the schema checks for it
148
+ return null;
149
+ }
150
+ const latitudes = points.map(point => point[1]);
151
+ const longitudes = points.map(point => point[0]);
152
+ return [Math.min(...longitudes), Math.min(...latitudes), Math.max(...longitudes), Math.max(...latitudes)];
153
+ };
154
+ /**
155
+ * @description Creates a round polygon from a point and a radius.
156
+ */
157
+ const getPolygonFromPointAndRadius = (point, radius) => {
158
+ const [lon, lat] = point.coordinates;
159
+ // Adjust the number of points based on radius (resolution)
160
+ const pointsCount = Math.max(32, Math.floor(radius / 100)); // More points for larger radius
161
+ const angleStep = (2 * Math.PI) / pointsCount;
162
+ const coordinates = [];
163
+ for (let i = 0; i <= pointsCount; i++) {
164
+ const angle = i * angleStep;
165
+ // Calculate offset in latitude and longitude
166
+ const deltaLat = (radius / EARTH_RADIUS) * (180 / Math.PI);
167
+ const deltaLon = deltaLat / Math.cos((lat * Math.PI) / 180);
168
+ // Calculate new coordinates based on angle
169
+ const newLat = lat + deltaLat * Math.sin(angle);
170
+ const newLon = lon + deltaLon * Math.cos(angle);
171
+ coordinates.push([newLon, newLat]);
172
+ }
173
+ return {
174
+ type: "Polygon",
175
+ coordinates: [coordinates],
176
+ };
177
+ };
178
+ /**
179
+ * @description Creates a TU bounding box from a GeoJson Polygon.
180
+ */
181
+ const getBoundingBoxFromGeoJsonPolygon = (polygon) => {
182
+ const polygonParsed = geoJsonPolygonSchema.safeParse(polygon);
183
+ if (!polygonParsed.success) {
184
+ return null;
185
+ }
186
+ const points = polygonParsed.data.coordinates[0];
187
+ if (!points) {
188
+ // Should never happen since the schema checks for it
189
+ return null;
190
+ }
191
+ const latitudes = points.map(point => point[1]);
192
+ const longitudes = points.map(point => point[0]);
193
+ return {
194
+ nw: {
195
+ latitude: Math.max(...latitudes),
196
+ longitude: Math.min(...longitudes),
197
+ },
198
+ se: {
199
+ latitude: Math.min(...latitudes),
200
+ longitude: Math.max(...longitudes),
201
+ },
202
+ };
203
+ };
204
+ /**
205
+ * @description Creates a GeoJSON Polygon from a TU bounding box.
206
+ */
207
+ const getGeoJsonPolygonFromBoundingBox = (boundingBox) => {
208
+ const { nw, se } = boundingBox;
209
+ return {
210
+ type: "Polygon",
211
+ coordinates: [
212
+ [
213
+ [nw.longitude, nw.latitude], // Northwest corner
214
+ [se.longitude, nw.latitude], // Northeast corner
215
+ [se.longitude, se.latitude], // Southeast corner
216
+ [nw.longitude, se.latitude], // Southwest corner
217
+ [nw.longitude, nw.latitude], // Close the loop back to Northwest corner
218
+ ],
219
+ ],
220
+ };
221
+ };
222
+ /**
223
+ * @description Creates TU point coordinate from a GeoJSON Point.
224
+ */
225
+ const getPointCoordinateFromGeoJsonPoint = (point) => {
226
+ return { latitude: point.coordinates[1], longitude: point.coordinates[0] };
227
+ };
228
+ /**
229
+ * @description Gets the extreme point of a polygon in a given direction.
230
+ * @param {object} params - The parameters object
231
+ * @param {GeoJsonPolygon} params.polygon - The polygon to get the extreme point from
232
+ * @param {("top" | "right" | "bottom" | "left")} params.direction - The direction to get the extreme point in
233
+ * @returns {GeoJsonPoint} The extreme point in the given direction
234
+ */
235
+ const getExtremeGeoJsonPointFromPolygon = ({ polygon, direction, }) => {
236
+ var _a, _b, _c;
237
+ const polygonParsed = geoJsonPolygonSchema.safeParse(polygon);
238
+ if (!polygonParsed.success) {
239
+ return null;
240
+ }
241
+ const firstPoint = (_a = polygonParsed.data.coordinates[0]) === null || _a === void 0 ? void 0 : _a[0];
242
+ if (!firstPoint) {
243
+ // Should never happen since the schema checks for it
244
+ return null;
245
+ }
246
+ const extremePosition = (_b = polygonParsed.data.coordinates[0]) === null || _b === void 0 ? void 0 : _b.reduce((extremePoint, currentPoint) => {
247
+ var _a, _b, _c, _d;
248
+ switch (direction) {
249
+ case "top":
250
+ 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;
251
+ case "right":
252
+ 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;
253
+ case "bottom":
254
+ 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;
255
+ case "left":
256
+ 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;
257
+ default: {
258
+ throw new Error(`${direction} is not known`);
259
+ }
260
+ }
261
+ }, (_c = polygonParsed.data.coordinates[0]) === null || _c === void 0 ? void 0 : _c[0]);
262
+ return extremePosition
263
+ ? {
264
+ type: "Point",
265
+ coordinates: extremePosition,
266
+ }
267
+ : null; // Should never happen since the schema checks for it
268
+ };
269
+ /**
270
+ * Checks if a position is inside a linear ring. On edge is considered inside.
271
+ */
272
+ const isGeoJsonPositionInLinearRing = ({ position, linearRing, }) => {
273
+ const linearRingParsed = geoJsonLinearRingSchema.safeParse(linearRing);
274
+ if (!linearRingParsed.success) {
275
+ return null;
276
+ }
277
+ let inside = false;
278
+ const [x, y] = position;
279
+ for (let i = 0, j = linearRingParsed.data.length - 1; i < linearRingParsed.data.length; j = i++) {
280
+ const point1 = linearRingParsed.data[i];
281
+ const point2 = linearRingParsed.data[j];
282
+ if (!point1 || !point2) {
283
+ continue;
284
+ }
285
+ const [xi, yi] = point1;
286
+ const [xj, yj] = point2;
287
+ const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
288
+ if (intersect) {
289
+ inside = !inside;
290
+ }
291
+ }
292
+ return inside;
293
+ };
294
+ /**
295
+ * @description Checks if a point is inside a polygon.
296
+ */
297
+ const isGeoJsonPointInPolygon = ({ point, polygon, }) => {
298
+ const polygonParsed = geoJsonPolygonSchema.safeParse(polygon);
299
+ if (!polygonParsed.success) {
300
+ return null;
301
+ }
302
+ return polygonParsed.data.coordinates.some(linearRing => isGeoJsonPositionInLinearRing({ position: point.coordinates, linearRing }));
303
+ };
304
+ /**
305
+ * Checks if polygon1 is fully contained within polygon2
306
+ */
307
+ const isFullyContainedInGeoJsonPolygon = (polygon1, polygon2) => {
308
+ const polygon1Parsed = geoJsonPolygonSchema.safeParse(polygon1);
309
+ const polygon2Parsed = geoJsonPolygonSchema.safeParse(polygon2);
310
+ // The schema checks more than a TypeScript type can represent
311
+ if (!polygon1Parsed.success || !polygon2Parsed.success) {
312
+ return null;
313
+ }
314
+ return polygon1Parsed.data.coordinates.every(linearRing => polygon2Parsed.data.coordinates.some(lr => linearRing.every(position => isGeoJsonPositionInLinearRing({ position, linearRing: lr }))));
315
+ };
316
+ /**
317
+ * @description Gets the intersection between two GeoJSON polygons. If one polygon is fully contained within the other,
318
+ * returns the contained polygon. Otherwise returns a MultiPolygon representing the intersection.
319
+ * @param polygon1 The first polygon to check intersection
320
+ * @param polygon2 The second polygon to check intersection
321
+ * @returns {(GeoJsonMultiPolygon | GeoJsonPolygon)} The intersection as either a Polygon (if one contains the other) or MultiPolygon
322
+ */
323
+ const getGeoJsonPolygonIntersection = (polygon1, polygon2) => {
324
+ const polygon1Parsed = geoJsonPolygonSchema.safeParse(polygon1);
325
+ const polygon2Parsed = geoJsonPolygonSchema.safeParse(polygon2);
326
+ if (!polygon1Parsed.success || !polygon2Parsed.success) {
327
+ return null;
328
+ }
329
+ if (isFullyContainedInGeoJsonPolygon(polygon1, polygon2)) {
330
+ return polygon1;
331
+ }
332
+ if (isFullyContainedInGeoJsonPolygon(polygon2, polygon1)) {
333
+ return polygon2;
334
+ }
335
+ const intersectionResult = intersection(polygon1.coordinates, polygon2.coordinates);
336
+ if (intersectionResult.length === 1 && intersectionResult[0]) {
337
+ return {
338
+ type: "Polygon",
339
+ coordinates: intersectionResult[0],
340
+ };
341
+ }
342
+ return {
343
+ type: "MultiPolygon",
344
+ coordinates: intersection(polygon1.coordinates, polygon2.coordinates),
345
+ };
346
+ };
347
+
348
+ //! These tools are used to bridge the gap with out poorly typed graphql types
349
+ // Should be ideally be avoided but are needed until we fix the graphql types
350
+ const isDoubleNestedCoords = (coords) => Array.isArray(coords) &&
351
+ Array.isArray(coords[0]) &&
352
+ Array.isArray(coords[0][0]) &&
353
+ typeof coords[0][0][0] === "number";
354
+ const isSingleCoords = (coords) => typeof coords[0] === "number";
355
+ /**
356
+ * @description Returns coordinates in consistent format
357
+ * @param inconsistentCoordinates Single point, array of points or nested array of points
358
+ * @returns {GeoJsonPosition[]} Array of standardized coordinates
359
+ */
360
+ const coordinatesToStandardFormat = (inconsistentCoordinates) => {
361
+ if (!inconsistentCoordinates) {
362
+ return [];
363
+ }
364
+ if (isSingleCoords(inconsistentCoordinates)) {
365
+ return [inconsistentCoordinates];
366
+ }
367
+ if (isDoubleNestedCoords(inconsistentCoordinates)) {
368
+ return inconsistentCoordinates[0] || [];
369
+ }
370
+ if (inconsistentCoordinates[0] && typeof inconsistentCoordinates[0][0] === "number") {
371
+ return inconsistentCoordinates;
372
+ }
373
+ return [];
374
+ };
375
+ /**
376
+ * @description Extracts a point coordinate from a GeoJSON object.
377
+ * @param geoObject A GeoJSON object.
378
+ * @returns {PointCoordinate} A point coordinate.
379
+ */
380
+ const getPointCoordinateFromGeoJsonObject = (geoObject) => {
381
+ if (!geoObject) {
382
+ return undefined;
383
+ }
384
+ else if ("geometry" in geoObject) {
385
+ return getPointCoordinateFromGeoJsonObject(geoObject.geometry);
386
+ }
387
+ else if ("coordinates" in geoObject &&
388
+ Array.isArray(geoObject.coordinates) &&
389
+ typeof geoObject.coordinates[0] === "number" &&
390
+ typeof geoObject.coordinates[1] === "number") {
391
+ const [point] = coordinatesToStandardFormat(geoObject.coordinates);
392
+ if (point) {
393
+ return { latitude: point[1], longitude: point[0] };
394
+ }
395
+ else {
396
+ throw new Error(`Unable to extract point coordinate from ${JSON.stringify(geoObject)}`);
397
+ }
398
+ }
399
+ else {
400
+ throw new Error(`Unable to extract point coordinate from ${JSON.stringify(geoObject)}`);
401
+ }
402
+ };
403
+ /**
404
+ * @description Extracts multiple point coordinates from a GeoJSON object.
405
+ * @param geoObject A GeoJSON object.
406
+ * @returns {PointCoordinate[]} An array of point coordinates.
407
+ * @example getMultipleCoordinatesFromGeoJsonObject({ type: "Point", coordinates: [1, 2] }) // [{ longitude: 1, latitude: 2 }]
408
+ */
409
+ const getMultipleCoordinatesFromGeoJsonObject = (geoObject) => {
410
+ if (!geoObject) {
411
+ return undefined;
412
+ }
413
+ else if ("geometry" in geoObject) {
414
+ return getMultipleCoordinatesFromGeoJsonObject(geoObject.geometry);
415
+ }
416
+ else if ("coordinates" in geoObject) {
417
+ return coordinatesToStandardFormat(geoObject.coordinates).map(([longitude, latitude]) => ({ longitude, latitude }));
418
+ }
419
+ else {
420
+ throw new Error(`Unable to extract point coordinate from ${JSON.stringify(geoObject)}`);
421
+ }
422
+ };
423
+
424
+ //* -------- Trackunit-invented schemas and types to extend the GeoJson spec -------- *//
425
+ /**
426
+ * Polygon geometry object that explicitly disallows holes.
427
+ *
428
+ * Same as geoJsonPolygonSchema but type disallows holes by
429
+ * using tuple of one single linear ring instead of an array.
430
+ */
431
+ const tuGeoJsonPolygonNoHolesSchema = z.strictObject({
432
+ //The type is still "Polygon" (not PolygonNoHoles or similar) since it's always
433
+ //compliant with Polygon, just not the other way around
434
+ type: z.literal("Polygon"),
435
+ //uses tuple instead of array to enforce only 1 linear ring aka the polygon itself
436
+ coordinates: z.tuple([geoJsonLinearRingSchema]),
437
+ });
438
+ /**
439
+ * Point radius object.
440
+ * For when you wish to define an area by a point and a radius.
441
+ *
442
+ * radius is in meters
443
+ */
444
+ const tuGeoJsonPointRadiusSchema = z.strictObject({
445
+ type: z.literal("PointRadius"),
446
+ coordinates: geoJsonPositionSchema,
447
+ radius: z.number().positive(), // in meters
448
+ });
449
+ /**
450
+ * A Polygon with exactly 5 points and 4 horizontal/vertical sides that form a normal rectangular box.
451
+ */
452
+ const tuGeoJsonRectangularBoxPolygonSchema = z
453
+ .strictObject({
454
+ type: z.literal("Polygon"),
455
+ coordinates: z.array(geoJsonLinearRingSchema),
456
+ })
457
+ .superRefine((data, ctx) => {
458
+ const coordinates = data.coordinates[0];
459
+ // Validate polygon has exactly 5 points
460
+ if ((coordinates === null || coordinates === void 0 ? void 0 : coordinates.length) !== 5) {
461
+ ctx.addIssue({
462
+ code: z.ZodIssueCode.custom,
463
+ message: "Polygon must have exactly 5 coordinates to form a closed box.",
464
+ });
465
+ return;
466
+ }
467
+ // Check each side is either horizontal or vertical
468
+ for (let i = 0; i < 4; i++) {
469
+ const point1 = coordinates[i];
470
+ const point2 = coordinates[i + 1];
471
+ if (point1 === undefined || point2 === undefined) {
472
+ ctx.addIssue({
473
+ code: z.ZodIssueCode.custom,
474
+ message: "Each coordinate must be a defined point.",
475
+ });
476
+ return;
477
+ }
478
+ const [x1, y1] = point1;
479
+ const [x2, y2] = point2;
480
+ // Ensure each line segment is either horizontal or vertical
481
+ if (x1 !== x2 && y1 !== y2) {
482
+ ctx.addIssue({
483
+ code: z.ZodIssueCode.custom,
484
+ message: "Polygon sides must be horizontal or vertical to form a box shape.",
485
+ });
486
+ return;
487
+ }
488
+ }
489
+ });
490
+
491
+ 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 };
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@trackunit/geo-json-utils",
3
+ "version": "0.0.1",
4
+ "repository": "https://github.com/Trackunit/manager",
5
+ "license": "SEE LICENSE IN LICENSE.txt",
6
+ "engines": {
7
+ "node": ">=20.x"
8
+ },
9
+ "dependencies": {
10
+ "zod": "3.22.4",
11
+ "polygon-clipping": "^0.15.7"
12
+ },
13
+ "module": "./index.esm.js",
14
+ "main": "./index.cjs.js",
15
+ "types": "./index.esm.d.ts"
16
+ }
@@ -0,0 +1,176 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * A Position is an array of coordinates. [x, y]
4
+ * https://tools.ietf.org/html/rfc7946#section-3.1.1
5
+ */
6
+ export declare const geoJsonPositionSchema: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
7
+ export type GeoJsonPosition = z.infer<typeof geoJsonPositionSchema>;
8
+ /**
9
+ * Point geometry object.
10
+ * https://tools.ietf.org/html/rfc7946#section-3.1.2
11
+ */
12
+ export declare const geoJsonPointSchema: z.ZodObject<{
13
+ type: z.ZodLiteral<"Point">;
14
+ coordinates: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
15
+ }, "strict", z.ZodTypeAny, {
16
+ type: "Point";
17
+ coordinates: [number, number];
18
+ }, {
19
+ type: "Point";
20
+ coordinates: [number, number];
21
+ }>;
22
+ export type GeoJsonPoint = z.infer<typeof geoJsonPointSchema>;
23
+ /**
24
+ * MultiPoint geometry object.
25
+ * https://tools.ietf.org/html/rfc7946#section-3.1.3
26
+ */
27
+ export declare const geoJsonMultiPointSchema: z.ZodObject<{
28
+ type: z.ZodLiteral<"MultiPoint">;
29
+ coordinates: z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, "many">;
30
+ }, "strict", z.ZodTypeAny, {
31
+ type: "MultiPoint";
32
+ coordinates: [number, number][];
33
+ }, {
34
+ type: "MultiPoint";
35
+ coordinates: [number, number][];
36
+ }>;
37
+ export type GeoJsonMultiPoint = z.infer<typeof geoJsonMultiPointSchema>;
38
+ /**
39
+ * LineString geometry object.
40
+ * Minimum length of 2 positions.
41
+ * https://tools.ietf.org/html/rfc7946#section-3.1.4
42
+ */
43
+ export declare const geoJsonLineStringSchema: z.ZodObject<{
44
+ type: z.ZodLiteral<"LineString">;
45
+ coordinates: z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, "many">;
46
+ }, "strict", z.ZodTypeAny, {
47
+ type: "LineString";
48
+ coordinates: [number, number][];
49
+ }, {
50
+ type: "LineString";
51
+ coordinates: [number, number][];
52
+ }>;
53
+ export type GeoJsonLineString = z.infer<typeof geoJsonLineStringSchema>;
54
+ /**
55
+ * MultiLineString geometry object.
56
+ * https://tools.ietf.org/html/rfc7946#section-3.1.5
57
+ */
58
+ export declare const geoJsonMultiLineStringSchema: z.ZodObject<{
59
+ type: z.ZodLiteral<"MultiLineString">;
60
+ coordinates: z.ZodArray<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, "many">, "many">;
61
+ }, "strict", z.ZodTypeAny, {
62
+ type: "MultiLineString";
63
+ coordinates: [number, number][][];
64
+ }, {
65
+ type: "MultiLineString";
66
+ coordinates: [number, number][][];
67
+ }>;
68
+ export type GeoJsonMultiLineString = z.infer<typeof geoJsonMultiLineStringSchema>;
69
+ /**
70
+ * Helper type for reuse across polygon schemas.
71
+ *
72
+ * - A linear ring is a closed LineString with four or more positions.
73
+ * - The first and last positions are equivalent, and they MUST contain
74
+ identical values; their representation SHOULD also be identical
75
+ * - A linear ring is the boundary of a surface or the boundary of a
76
+ hole in a surface
77
+ * - A linear ring MUST follow the right-hand rule with respect to the
78
+ area it bounds, i.e., exterior rings are counterclockwise, and
79
+ holes are clockwise
80
+ */
81
+ export declare const geoJsonLinearRingSchema: z.ZodEffects<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, "many">, [number, number][], [number, number][]>;
82
+ export type GeoJsonLinearRing = z.infer<typeof geoJsonLinearRingSchema>;
83
+ /**
84
+ * Polygon geometry object.
85
+ * https://tools.ietf.org/html/rfc7946#section-3.1.6
86
+ */
87
+ export declare const geoJsonPolygonSchema: z.ZodObject<{
88
+ type: z.ZodLiteral<"Polygon">;
89
+ coordinates: z.ZodArray<z.ZodEffects<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, "many">, [number, number][], [number, number][]>, "many">;
90
+ }, "strict", z.ZodTypeAny, {
91
+ type: "Polygon";
92
+ coordinates: [number, number][][];
93
+ }, {
94
+ type: "Polygon";
95
+ coordinates: [number, number][][];
96
+ }>;
97
+ export type GeoJsonPolygon = z.infer<typeof geoJsonPolygonSchema>;
98
+ /**
99
+ * MultiPolygon geometry object.
100
+ * https://tools.ietf.org/html/rfc7946#section-3.1.7
101
+ */
102
+ export declare const geoJsonMultiPolygonSchema: z.ZodObject<{
103
+ type: z.ZodLiteral<"MultiPolygon">;
104
+ coordinates: z.ZodArray<z.ZodArray<z.ZodEffects<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, "many">, [number, number][], [number, number][]>, "many">, "many">;
105
+ }, "strict", z.ZodTypeAny, {
106
+ type: "MultiPolygon";
107
+ coordinates: [number, number][][][];
108
+ }, {
109
+ type: "MultiPolygon";
110
+ coordinates: [number, number][][][];
111
+ }>;
112
+ export type GeoJsonMultiPolygon = z.infer<typeof geoJsonMultiPolygonSchema>;
113
+ export declare const geoJsonGeometrySchema: z.ZodUnion<[z.ZodObject<{
114
+ type: z.ZodLiteral<"Point">;
115
+ coordinates: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
116
+ }, "strict", z.ZodTypeAny, {
117
+ type: "Point";
118
+ coordinates: [number, number];
119
+ }, {
120
+ type: "Point";
121
+ coordinates: [number, number];
122
+ }>, z.ZodObject<{
123
+ type: z.ZodLiteral<"MultiPoint">;
124
+ coordinates: z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, "many">;
125
+ }, "strict", z.ZodTypeAny, {
126
+ type: "MultiPoint";
127
+ coordinates: [number, number][];
128
+ }, {
129
+ type: "MultiPoint";
130
+ coordinates: [number, number][];
131
+ }>, z.ZodObject<{
132
+ type: z.ZodLiteral<"LineString">;
133
+ coordinates: z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, "many">;
134
+ }, "strict", z.ZodTypeAny, {
135
+ type: "LineString";
136
+ coordinates: [number, number][];
137
+ }, {
138
+ type: "LineString";
139
+ coordinates: [number, number][];
140
+ }>, z.ZodObject<{
141
+ type: z.ZodLiteral<"MultiLineString">;
142
+ coordinates: z.ZodArray<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, "many">, "many">;
143
+ }, "strict", z.ZodTypeAny, {
144
+ type: "MultiLineString";
145
+ coordinates: [number, number][][];
146
+ }, {
147
+ type: "MultiLineString";
148
+ coordinates: [number, number][][];
149
+ }>, z.ZodObject<{
150
+ type: z.ZodLiteral<"Polygon">;
151
+ coordinates: z.ZodArray<z.ZodEffects<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, "many">, [number, number][], [number, number][]>, "many">;
152
+ }, "strict", z.ZodTypeAny, {
153
+ type: "Polygon";
154
+ coordinates: [number, number][][];
155
+ }, {
156
+ type: "Polygon";
157
+ coordinates: [number, number][][];
158
+ }>, z.ZodObject<{
159
+ type: z.ZodLiteral<"MultiPolygon">;
160
+ coordinates: z.ZodArray<z.ZodArray<z.ZodEffects<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, "many">, [number, number][], [number, number][]>, "many">, "many">;
161
+ }, "strict", z.ZodTypeAny, {
162
+ type: "MultiPolygon";
163
+ coordinates: [number, number][][][];
164
+ }, {
165
+ type: "MultiPolygon";
166
+ coordinates: [number, number][][][];
167
+ }>]>;
168
+ export type GeoJsonGeometry = z.infer<typeof geoJsonGeometrySchema>;
169
+ /**
170
+ * 2D bounding box of the GeoJSON object.
171
+ * The value of the Bbox member is an array of length 4.
172
+ *
173
+ * [min_lon, min_lat, max_lon, max_lat]
174
+ */
175
+ export declare const geoJsonBboxSchema: z.ZodEffects<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>, [number, number, number, number], [number, number, number, number]>;
176
+ export type GeoJsonBbox = z.infer<typeof geoJsonBboxSchema>;