@trackunit/shared-utils 0.0.82-alpha-c6639d22933.0 → 0.0.83
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs.js +281 -0
- package/index.esm.js +264 -1
- package/package.json +3 -2
- package/src/GeoJson/GeoJsonSchemas.d.ts +210 -0
- package/src/GeoJson/GeoJsonUtils.d.ts +52 -0
- package/src/index.d.ts +2 -1
- package/src/GeoJsonUtils.d.ts +0 -26
package/index.cjs.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var zod = require('zod');
|
|
3
4
|
var uuid = require('uuid');
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -413,6 +414,181 @@ searchTerm) => {
|
|
|
413
414
|
.every(term => filterableProps(asset).some(value => value.toLowerCase().includes(term.toLowerCase()))));
|
|
414
415
|
};
|
|
415
416
|
|
|
417
|
+
// * NOTE: For simplicity these tools are built for 2D coordinate space only!
|
|
418
|
+
/**
|
|
419
|
+
* A Position is an array of coordinates. [x, y]
|
|
420
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.1
|
|
421
|
+
*/
|
|
422
|
+
const geoJsonPositionSchema = zod.z.tuple([zod.z.number(), zod.z.number()]);
|
|
423
|
+
/**
|
|
424
|
+
* Point geometry object.
|
|
425
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.2
|
|
426
|
+
*/
|
|
427
|
+
const geoJsonPointSchema = zod.z.strictObject({
|
|
428
|
+
type: zod.z.literal("Point"),
|
|
429
|
+
coordinates: geoJsonPositionSchema,
|
|
430
|
+
});
|
|
431
|
+
/**
|
|
432
|
+
* MultiPoint geometry object.
|
|
433
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.3
|
|
434
|
+
*/
|
|
435
|
+
const geoJsonMultiPointSchema = zod.z.strictObject({
|
|
436
|
+
type: zod.z.literal("MultiPoint"),
|
|
437
|
+
coordinates: zod.z.array(geoJsonPositionSchema),
|
|
438
|
+
});
|
|
439
|
+
/**
|
|
440
|
+
* LineString geometry object.
|
|
441
|
+
* Minimum length of 2 positions.
|
|
442
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.4
|
|
443
|
+
*/
|
|
444
|
+
const geoJsonLineStringSchema = zod.z.strictObject({
|
|
445
|
+
type: zod.z.literal("LineString"),
|
|
446
|
+
coordinates: zod.z.array(geoJsonPositionSchema).min(2),
|
|
447
|
+
});
|
|
448
|
+
/**
|
|
449
|
+
* MultiLineString geometry object.
|
|
450
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.5
|
|
451
|
+
*/
|
|
452
|
+
const geoJsonMultiLineStringSchema = zod.z.strictObject({
|
|
453
|
+
type: zod.z.literal("MultiLineString"),
|
|
454
|
+
coordinates: zod.z.array(zod.z.array(geoJsonPositionSchema)),
|
|
455
|
+
});
|
|
456
|
+
/**
|
|
457
|
+
* Helper type for reuse across polygon schemas.
|
|
458
|
+
*
|
|
459
|
+
* - A linear ring is a closed LineString with four or more positions.
|
|
460
|
+
* - The first and last positions are equivalent, and they MUST contain
|
|
461
|
+
identical values; their representation SHOULD also be identical
|
|
462
|
+
* - A linear ring is the boundary of a surface or the boundary of a
|
|
463
|
+
hole in a surface
|
|
464
|
+
* - A linear ring MUST follow the right-hand rule with respect to the
|
|
465
|
+
area it bounds, i.e., exterior rings are counterclockwise, and
|
|
466
|
+
holes are clockwise
|
|
467
|
+
*/
|
|
468
|
+
const geoJsonLinearRingSchema = zod.z
|
|
469
|
+
.array(geoJsonPositionSchema)
|
|
470
|
+
.min(4, {
|
|
471
|
+
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)",
|
|
472
|
+
})
|
|
473
|
+
.superRefine((coords, ctx) => {
|
|
474
|
+
const first = coords[0];
|
|
475
|
+
const last = coords[coords.length - 1];
|
|
476
|
+
// Check if first and last coordinates match
|
|
477
|
+
if (JSON.stringify(first) !== JSON.stringify(last)) {
|
|
478
|
+
ctx.addIssue({
|
|
479
|
+
code: zod.z.ZodIssueCode.custom,
|
|
480
|
+
message: "First and last coordinate positions must be identical (to close the linear ring aka polygon).",
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
// Check if any coordinate is (0, 0)
|
|
484
|
+
coords.forEach((coord, index) => {
|
|
485
|
+
if (coord[0] === 0 && coord[1] === 0) {
|
|
486
|
+
ctx.addIssue({
|
|
487
|
+
code: zod.z.ZodIssueCode.custom,
|
|
488
|
+
message: `Coordinate at index ${index} should not be (0, 0). This is likely not a valid coordinate.`,
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
// Check if consecutive points are identical (excluding first and last)
|
|
493
|
+
for (let i = 1; i < coords.length - 1; i++) {
|
|
494
|
+
if (JSON.stringify(coords[i]) === JSON.stringify(coords[i - 1])) {
|
|
495
|
+
ctx.addIssue({
|
|
496
|
+
code: zod.z.ZodIssueCode.custom,
|
|
497
|
+
message: `Consecutive coordinates at index ${i - 1} and ${i} should not be identical.`,
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
/**
|
|
503
|
+
* Polygon geometry object.
|
|
504
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.6
|
|
505
|
+
*/
|
|
506
|
+
const geoJsonPolygonSchema = zod.z.strictObject({
|
|
507
|
+
type: zod.z.literal("Polygon"),
|
|
508
|
+
coordinates: zod.z.array(geoJsonLinearRingSchema),
|
|
509
|
+
});
|
|
510
|
+
/**
|
|
511
|
+
* MultiPolygon geometry object.
|
|
512
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.7
|
|
513
|
+
*/
|
|
514
|
+
const geoJsonMultiPolygonSchema = zod.z.strictObject({
|
|
515
|
+
type: zod.z.literal("MultiPolygon"),
|
|
516
|
+
coordinates: zod.z.array(zod.z.array(geoJsonLinearRingSchema)),
|
|
517
|
+
});
|
|
518
|
+
// The same for Geometry, GeometryCollection, GeoJsonProperties, Feature, FeatureCollection, etc.
|
|
519
|
+
const geoJsonGeometrySchema = zod.z.union([
|
|
520
|
+
geoJsonPointSchema,
|
|
521
|
+
geoJsonMultiPointSchema,
|
|
522
|
+
geoJsonLineStringSchema,
|
|
523
|
+
geoJsonMultiLineStringSchema,
|
|
524
|
+
geoJsonPolygonSchema,
|
|
525
|
+
geoJsonMultiPolygonSchema,
|
|
526
|
+
]);
|
|
527
|
+
//* -------- Bbox -------- *//
|
|
528
|
+
/**
|
|
529
|
+
* 2D bounding box of the GeoJSON object.
|
|
530
|
+
* The value of the Bbox member is an array of length 4.
|
|
531
|
+
*
|
|
532
|
+
* [min_lon, min_lat, max_lon, max_lat]
|
|
533
|
+
*/
|
|
534
|
+
const geoJsonBboxSchema = zod.z
|
|
535
|
+
.tuple([zod.z.number(), zod.z.number(), zod.z.number(), zod.z.number()])
|
|
536
|
+
.refine(([minLng, minLat, maxLng, maxLat]) => maxLng > minLng && maxLat > minLat, {
|
|
537
|
+
message: "Invalid bounding box: maxLng should be greater than minLng, and maxLat should be greater than minLat.",
|
|
538
|
+
});
|
|
539
|
+
//* -------- Extras -------- *//
|
|
540
|
+
/**
|
|
541
|
+
* Polygon geometry object that explicitly disallows holes.
|
|
542
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.6
|
|
543
|
+
*/
|
|
544
|
+
const geoJsonPolygonNoHolesSchema = zod.z.strictObject({
|
|
545
|
+
type: zod.z.literal("Polygon"),
|
|
546
|
+
//uses tuple instead of array to enforce only 1 linear ring aka the polygon itself
|
|
547
|
+
coordinates: zod.z.tuple([geoJsonLinearRingSchema]),
|
|
548
|
+
});
|
|
549
|
+
/**
|
|
550
|
+
* A Polygon with exactly 5 points and 4 horizontal/vertical sides that form a normal rectangular box.
|
|
551
|
+
*/
|
|
552
|
+
const geoJsonRectangularBoxPolygonSchema = zod.z
|
|
553
|
+
.strictObject({
|
|
554
|
+
type: zod.z.literal("Polygon"),
|
|
555
|
+
coordinates: zod.z.array(geoJsonLinearRingSchema),
|
|
556
|
+
})
|
|
557
|
+
.superRefine((data, ctx) => {
|
|
558
|
+
const coordinates = data.coordinates[0];
|
|
559
|
+
// Validate polygon has exactly 5 points
|
|
560
|
+
if ((coordinates === null || coordinates === void 0 ? void 0 : coordinates.length) !== 5) {
|
|
561
|
+
ctx.addIssue({
|
|
562
|
+
code: zod.z.ZodIssueCode.custom,
|
|
563
|
+
message: "Polygon must have exactly 5 coordinates to form a closed box.",
|
|
564
|
+
});
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
// Check each side is either horizontal or vertical
|
|
568
|
+
for (let i = 0; i < 4; i++) {
|
|
569
|
+
const point1 = coordinates[i];
|
|
570
|
+
const point2 = coordinates[i + 1];
|
|
571
|
+
if (!point1 || !point2) {
|
|
572
|
+
ctx.addIssue({
|
|
573
|
+
code: zod.z.ZodIssueCode.custom,
|
|
574
|
+
message: "Each coordinate must be a defined point.",
|
|
575
|
+
});
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
const [x1, y1] = point1;
|
|
579
|
+
const [x2, y2] = point2;
|
|
580
|
+
// Ensure each line segment is either horizontal or vertical
|
|
581
|
+
if (x1 !== x2 && y1 !== y2) {
|
|
582
|
+
ctx.addIssue({
|
|
583
|
+
code: zod.z.ZodIssueCode.custom,
|
|
584
|
+
message: "Polygon sides must be horizontal or vertical to form a box shape.",
|
|
585
|
+
});
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
const EARTH_RADIUS = 6378137; // Earth’s mean radius in meters
|
|
416
592
|
/**
|
|
417
593
|
* @description Extracts a point coordinate from a GeoJSON object.
|
|
418
594
|
* @param geoObject A GeoJSON object.
|
|
@@ -459,6 +635,93 @@ const getMultipleCoordinatesFromGeoJsonObject = (geoObject) => {
|
|
|
459
635
|
throw new Error(`Unable to extract point coordinate from ${JSON.stringify(geoObject)}`);
|
|
460
636
|
}
|
|
461
637
|
};
|
|
638
|
+
/**
|
|
639
|
+
* @description Creates a polygon from a bounding box.
|
|
640
|
+
*/
|
|
641
|
+
const getPolygonFromBbox = (bbox) => {
|
|
642
|
+
const [minLon, minLat, maxLon, maxLat] = bbox;
|
|
643
|
+
return {
|
|
644
|
+
type: "Polygon",
|
|
645
|
+
coordinates: [
|
|
646
|
+
[
|
|
647
|
+
[minLon, minLat],
|
|
648
|
+
[maxLon, minLat],
|
|
649
|
+
[maxLon, maxLat],
|
|
650
|
+
[minLon, maxLat],
|
|
651
|
+
[minLon, minLat],
|
|
652
|
+
],
|
|
653
|
+
],
|
|
654
|
+
};
|
|
655
|
+
};
|
|
656
|
+
/**
|
|
657
|
+
* @description Creates a round polygon from a point and a radius.
|
|
658
|
+
*/
|
|
659
|
+
const getPolygonFromPointAndRadius = (point, radius) => {
|
|
660
|
+
const [lon, lat] = point.coordinates;
|
|
661
|
+
// Adjust the number of points based on radius (resolution)
|
|
662
|
+
const pointsCount = Math.max(32, Math.floor(radius / 100)); // More points for larger radius
|
|
663
|
+
const angleStep = (2 * Math.PI) / pointsCount;
|
|
664
|
+
const coordinates = [];
|
|
665
|
+
for (let i = 0; i <= pointsCount; i++) {
|
|
666
|
+
const angle = i * angleStep;
|
|
667
|
+
// Calculate offset in latitude and longitude
|
|
668
|
+
const deltaLat = (radius / EARTH_RADIUS) * (180 / Math.PI);
|
|
669
|
+
const deltaLon = deltaLat / Math.cos((lat * Math.PI) / 180);
|
|
670
|
+
// Calculate new coordinates based on angle
|
|
671
|
+
const newLat = lat + deltaLat * Math.sin(angle);
|
|
672
|
+
const newLon = lon + deltaLon * Math.cos(angle);
|
|
673
|
+
coordinates.push([newLon, newLat]);
|
|
674
|
+
}
|
|
675
|
+
return {
|
|
676
|
+
type: "Polygon",
|
|
677
|
+
coordinates: [coordinates],
|
|
678
|
+
};
|
|
679
|
+
};
|
|
680
|
+
/**
|
|
681
|
+
* @description Creates a TU bounding box from a GeoJson Polygon.
|
|
682
|
+
*/
|
|
683
|
+
const getBoundingBoxFromGeoJsonPolygon = (polygon) => {
|
|
684
|
+
const points = polygon.coordinates[0];
|
|
685
|
+
const latitudes = points === null || points === void 0 ? void 0 : points.map(point => point[1]);
|
|
686
|
+
const longitudes = points === null || points === void 0 ? void 0 : points.map(point => point[0]);
|
|
687
|
+
if (!latitudes || !longitudes) {
|
|
688
|
+
return undefined;
|
|
689
|
+
}
|
|
690
|
+
return {
|
|
691
|
+
nw: {
|
|
692
|
+
latitude: Math.max(...latitudes),
|
|
693
|
+
longitude: Math.min(...longitudes),
|
|
694
|
+
},
|
|
695
|
+
se: {
|
|
696
|
+
latitude: Math.min(...latitudes),
|
|
697
|
+
longitude: Math.max(...longitudes),
|
|
698
|
+
},
|
|
699
|
+
};
|
|
700
|
+
};
|
|
701
|
+
/**
|
|
702
|
+
* @description Creates a GeoJSON Polygon from a TU bounding box.
|
|
703
|
+
*/
|
|
704
|
+
const getGeoJsonPolygonFromBoundingBox = (boundingBox) => {
|
|
705
|
+
const { nw, se } = boundingBox;
|
|
706
|
+
return {
|
|
707
|
+
type: "Polygon",
|
|
708
|
+
coordinates: [
|
|
709
|
+
[
|
|
710
|
+
[nw.longitude, nw.latitude], // Northwest corner
|
|
711
|
+
[se.longitude, nw.latitude], // Northeast corner
|
|
712
|
+
[se.longitude, se.latitude], // Southeast corner
|
|
713
|
+
[nw.longitude, se.latitude], // Southwest corner
|
|
714
|
+
[nw.longitude, nw.latitude], // Close the loop back to Northwest corner
|
|
715
|
+
],
|
|
716
|
+
],
|
|
717
|
+
};
|
|
718
|
+
};
|
|
719
|
+
/**
|
|
720
|
+
* @description Creates TU point coordinate from a GeoJSON Point.
|
|
721
|
+
*/
|
|
722
|
+
const getPointCoordinateFromGeoJsonPoint = (point) => {
|
|
723
|
+
return { latitude: point.coordinates[1], longitude: point.coordinates[0] };
|
|
724
|
+
};
|
|
462
725
|
|
|
463
726
|
/**
|
|
464
727
|
* Group an array of items by a key.
|
|
@@ -1107,6 +1370,7 @@ const uuidv4 = () => {
|
|
|
1107
1370
|
const uuidv5 = (name, namespace) => uuid.v5(name, namespace);
|
|
1108
1371
|
|
|
1109
1372
|
exports.DateTimeFormat = DateTimeFormat;
|
|
1373
|
+
exports.EARTH_RADIUS = EARTH_RADIUS;
|
|
1110
1374
|
exports.align = align;
|
|
1111
1375
|
exports.alphabeticallySort = alphabeticallySort;
|
|
1112
1376
|
exports.arrayLengthCompare = arrayLengthCompare;
|
|
@@ -1128,12 +1392,29 @@ exports.filterByMultiple = filterByMultiple;
|
|
|
1128
1392
|
exports.formatAddress = formatAddress;
|
|
1129
1393
|
exports.formatCoordinates = formatCoordinates;
|
|
1130
1394
|
exports.fuzzySearch = fuzzySearch;
|
|
1395
|
+
exports.geoJsonBboxSchema = geoJsonBboxSchema;
|
|
1396
|
+
exports.geoJsonGeometrySchema = geoJsonGeometrySchema;
|
|
1397
|
+
exports.geoJsonLineStringSchema = geoJsonLineStringSchema;
|
|
1398
|
+
exports.geoJsonLinearRingSchema = geoJsonLinearRingSchema;
|
|
1399
|
+
exports.geoJsonMultiLineStringSchema = geoJsonMultiLineStringSchema;
|
|
1400
|
+
exports.geoJsonMultiPointSchema = geoJsonMultiPointSchema;
|
|
1401
|
+
exports.geoJsonMultiPolygonSchema = geoJsonMultiPolygonSchema;
|
|
1402
|
+
exports.geoJsonPointSchema = geoJsonPointSchema;
|
|
1403
|
+
exports.geoJsonPolygonNoHolesSchema = geoJsonPolygonNoHolesSchema;
|
|
1404
|
+
exports.geoJsonPolygonSchema = geoJsonPolygonSchema;
|
|
1405
|
+
exports.geoJsonPositionSchema = geoJsonPositionSchema;
|
|
1406
|
+
exports.geoJsonRectangularBoxPolygonSchema = geoJsonRectangularBoxPolygonSchema;
|
|
1407
|
+
exports.getBoundingBoxFromGeoJsonPolygon = getBoundingBoxFromGeoJsonPolygon;
|
|
1131
1408
|
exports.getDifferenceBetweenDates = getDifferenceBetweenDates;
|
|
1132
1409
|
exports.getEndOfDay = getEndOfDay;
|
|
1133
1410
|
exports.getFirstLevelObjectPropertyDifferences = getFirstLevelObjectPropertyDifferences;
|
|
1411
|
+
exports.getGeoJsonPolygonFromBoundingBox = getGeoJsonPolygonFromBoundingBox;
|
|
1134
1412
|
exports.getISOStringFromDate = getISOStringFromDate;
|
|
1135
1413
|
exports.getMultipleCoordinatesFromGeoJsonObject = getMultipleCoordinatesFromGeoJsonObject;
|
|
1136
1414
|
exports.getPointCoordinateFromGeoJsonObject = getPointCoordinateFromGeoJsonObject;
|
|
1415
|
+
exports.getPointCoordinateFromGeoJsonPoint = getPointCoordinateFromGeoJsonPoint;
|
|
1416
|
+
exports.getPolygonFromBbox = getPolygonFromBbox;
|
|
1417
|
+
exports.getPolygonFromPointAndRadius = getPolygonFromPointAndRadius;
|
|
1137
1418
|
exports.getResizedDimensions = getResizedDimensions;
|
|
1138
1419
|
exports.getStartOfDay = getStartOfDay;
|
|
1139
1420
|
exports.groupBy = groupBy;
|
package/index.esm.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { v3, v4, v5 } from 'uuid';
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -411,6 +412,181 @@ searchTerm) => {
|
|
|
411
412
|
.every(term => filterableProps(asset).some(value => value.toLowerCase().includes(term.toLowerCase()))));
|
|
412
413
|
};
|
|
413
414
|
|
|
415
|
+
// * NOTE: For simplicity these tools are built for 2D coordinate space only!
|
|
416
|
+
/**
|
|
417
|
+
* A Position is an array of coordinates. [x, y]
|
|
418
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.1
|
|
419
|
+
*/
|
|
420
|
+
const geoJsonPositionSchema = z.tuple([z.number(), z.number()]);
|
|
421
|
+
/**
|
|
422
|
+
* Point geometry object.
|
|
423
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.2
|
|
424
|
+
*/
|
|
425
|
+
const geoJsonPointSchema = z.strictObject({
|
|
426
|
+
type: z.literal("Point"),
|
|
427
|
+
coordinates: geoJsonPositionSchema,
|
|
428
|
+
});
|
|
429
|
+
/**
|
|
430
|
+
* MultiPoint geometry object.
|
|
431
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.3
|
|
432
|
+
*/
|
|
433
|
+
const geoJsonMultiPointSchema = z.strictObject({
|
|
434
|
+
type: z.literal("MultiPoint"),
|
|
435
|
+
coordinates: z.array(geoJsonPositionSchema),
|
|
436
|
+
});
|
|
437
|
+
/**
|
|
438
|
+
* LineString geometry object.
|
|
439
|
+
* Minimum length of 2 positions.
|
|
440
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.4
|
|
441
|
+
*/
|
|
442
|
+
const geoJsonLineStringSchema = z.strictObject({
|
|
443
|
+
type: z.literal("LineString"),
|
|
444
|
+
coordinates: z.array(geoJsonPositionSchema).min(2),
|
|
445
|
+
});
|
|
446
|
+
/**
|
|
447
|
+
* MultiLineString geometry object.
|
|
448
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.5
|
|
449
|
+
*/
|
|
450
|
+
const geoJsonMultiLineStringSchema = z.strictObject({
|
|
451
|
+
type: z.literal("MultiLineString"),
|
|
452
|
+
coordinates: z.array(z.array(geoJsonPositionSchema)),
|
|
453
|
+
});
|
|
454
|
+
/**
|
|
455
|
+
* Helper type for reuse across polygon schemas.
|
|
456
|
+
*
|
|
457
|
+
* - A linear ring is a closed LineString with four or more positions.
|
|
458
|
+
* - The first and last positions are equivalent, and they MUST contain
|
|
459
|
+
identical values; their representation SHOULD also be identical
|
|
460
|
+
* - A linear ring is the boundary of a surface or the boundary of a
|
|
461
|
+
hole in a surface
|
|
462
|
+
* - A linear ring MUST follow the right-hand rule with respect to the
|
|
463
|
+
area it bounds, i.e., exterior rings are counterclockwise, and
|
|
464
|
+
holes are clockwise
|
|
465
|
+
*/
|
|
466
|
+
const geoJsonLinearRingSchema = z
|
|
467
|
+
.array(geoJsonPositionSchema)
|
|
468
|
+
.min(4, {
|
|
469
|
+
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)",
|
|
470
|
+
})
|
|
471
|
+
.superRefine((coords, ctx) => {
|
|
472
|
+
const first = coords[0];
|
|
473
|
+
const last = coords[coords.length - 1];
|
|
474
|
+
// Check if first and last coordinates match
|
|
475
|
+
if (JSON.stringify(first) !== JSON.stringify(last)) {
|
|
476
|
+
ctx.addIssue({
|
|
477
|
+
code: z.ZodIssueCode.custom,
|
|
478
|
+
message: "First and last coordinate positions must be identical (to close the linear ring aka polygon).",
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
// Check if any coordinate is (0, 0)
|
|
482
|
+
coords.forEach((coord, index) => {
|
|
483
|
+
if (coord[0] === 0 && coord[1] === 0) {
|
|
484
|
+
ctx.addIssue({
|
|
485
|
+
code: z.ZodIssueCode.custom,
|
|
486
|
+
message: `Coordinate at index ${index} should not be (0, 0). This is likely not a valid coordinate.`,
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
// Check if consecutive points are identical (excluding first and last)
|
|
491
|
+
for (let i = 1; i < coords.length - 1; i++) {
|
|
492
|
+
if (JSON.stringify(coords[i]) === JSON.stringify(coords[i - 1])) {
|
|
493
|
+
ctx.addIssue({
|
|
494
|
+
code: z.ZodIssueCode.custom,
|
|
495
|
+
message: `Consecutive coordinates at index ${i - 1} and ${i} should not be identical.`,
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
/**
|
|
501
|
+
* Polygon geometry object.
|
|
502
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.6
|
|
503
|
+
*/
|
|
504
|
+
const geoJsonPolygonSchema = z.strictObject({
|
|
505
|
+
type: z.literal("Polygon"),
|
|
506
|
+
coordinates: z.array(geoJsonLinearRingSchema),
|
|
507
|
+
});
|
|
508
|
+
/**
|
|
509
|
+
* MultiPolygon geometry object.
|
|
510
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.7
|
|
511
|
+
*/
|
|
512
|
+
const geoJsonMultiPolygonSchema = z.strictObject({
|
|
513
|
+
type: z.literal("MultiPolygon"),
|
|
514
|
+
coordinates: z.array(z.array(geoJsonLinearRingSchema)),
|
|
515
|
+
});
|
|
516
|
+
// The same for Geometry, GeometryCollection, GeoJsonProperties, Feature, FeatureCollection, etc.
|
|
517
|
+
const geoJsonGeometrySchema = z.union([
|
|
518
|
+
geoJsonPointSchema,
|
|
519
|
+
geoJsonMultiPointSchema,
|
|
520
|
+
geoJsonLineStringSchema,
|
|
521
|
+
geoJsonMultiLineStringSchema,
|
|
522
|
+
geoJsonPolygonSchema,
|
|
523
|
+
geoJsonMultiPolygonSchema,
|
|
524
|
+
]);
|
|
525
|
+
//* -------- Bbox -------- *//
|
|
526
|
+
/**
|
|
527
|
+
* 2D bounding box of the GeoJSON object.
|
|
528
|
+
* The value of the Bbox member is an array of length 4.
|
|
529
|
+
*
|
|
530
|
+
* [min_lon, min_lat, max_lon, max_lat]
|
|
531
|
+
*/
|
|
532
|
+
const geoJsonBboxSchema = z
|
|
533
|
+
.tuple([z.number(), z.number(), z.number(), z.number()])
|
|
534
|
+
.refine(([minLng, minLat, maxLng, maxLat]) => maxLng > minLng && maxLat > minLat, {
|
|
535
|
+
message: "Invalid bounding box: maxLng should be greater than minLng, and maxLat should be greater than minLat.",
|
|
536
|
+
});
|
|
537
|
+
//* -------- Extras -------- *//
|
|
538
|
+
/**
|
|
539
|
+
* Polygon geometry object that explicitly disallows holes.
|
|
540
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.6
|
|
541
|
+
*/
|
|
542
|
+
const geoJsonPolygonNoHolesSchema = z.strictObject({
|
|
543
|
+
type: z.literal("Polygon"),
|
|
544
|
+
//uses tuple instead of array to enforce only 1 linear ring aka the polygon itself
|
|
545
|
+
coordinates: z.tuple([geoJsonLinearRingSchema]),
|
|
546
|
+
});
|
|
547
|
+
/**
|
|
548
|
+
* A Polygon with exactly 5 points and 4 horizontal/vertical sides that form a normal rectangular box.
|
|
549
|
+
*/
|
|
550
|
+
const geoJsonRectangularBoxPolygonSchema = z
|
|
551
|
+
.strictObject({
|
|
552
|
+
type: z.literal("Polygon"),
|
|
553
|
+
coordinates: z.array(geoJsonLinearRingSchema),
|
|
554
|
+
})
|
|
555
|
+
.superRefine((data, ctx) => {
|
|
556
|
+
const coordinates = data.coordinates[0];
|
|
557
|
+
// Validate polygon has exactly 5 points
|
|
558
|
+
if ((coordinates === null || coordinates === void 0 ? void 0 : coordinates.length) !== 5) {
|
|
559
|
+
ctx.addIssue({
|
|
560
|
+
code: z.ZodIssueCode.custom,
|
|
561
|
+
message: "Polygon must have exactly 5 coordinates to form a closed box.",
|
|
562
|
+
});
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
// Check each side is either horizontal or vertical
|
|
566
|
+
for (let i = 0; i < 4; i++) {
|
|
567
|
+
const point1 = coordinates[i];
|
|
568
|
+
const point2 = coordinates[i + 1];
|
|
569
|
+
if (!point1 || !point2) {
|
|
570
|
+
ctx.addIssue({
|
|
571
|
+
code: z.ZodIssueCode.custom,
|
|
572
|
+
message: "Each coordinate must be a defined point.",
|
|
573
|
+
});
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const [x1, y1] = point1;
|
|
577
|
+
const [x2, y2] = point2;
|
|
578
|
+
// Ensure each line segment is either horizontal or vertical
|
|
579
|
+
if (x1 !== x2 && y1 !== y2) {
|
|
580
|
+
ctx.addIssue({
|
|
581
|
+
code: z.ZodIssueCode.custom,
|
|
582
|
+
message: "Polygon sides must be horizontal or vertical to form a box shape.",
|
|
583
|
+
});
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
const EARTH_RADIUS = 6378137; // Earth’s mean radius in meters
|
|
414
590
|
/**
|
|
415
591
|
* @description Extracts a point coordinate from a GeoJSON object.
|
|
416
592
|
* @param geoObject A GeoJSON object.
|
|
@@ -457,6 +633,93 @@ const getMultipleCoordinatesFromGeoJsonObject = (geoObject) => {
|
|
|
457
633
|
throw new Error(`Unable to extract point coordinate from ${JSON.stringify(geoObject)}`);
|
|
458
634
|
}
|
|
459
635
|
};
|
|
636
|
+
/**
|
|
637
|
+
* @description Creates a polygon from a bounding box.
|
|
638
|
+
*/
|
|
639
|
+
const getPolygonFromBbox = (bbox) => {
|
|
640
|
+
const [minLon, minLat, maxLon, maxLat] = bbox;
|
|
641
|
+
return {
|
|
642
|
+
type: "Polygon",
|
|
643
|
+
coordinates: [
|
|
644
|
+
[
|
|
645
|
+
[minLon, minLat],
|
|
646
|
+
[maxLon, minLat],
|
|
647
|
+
[maxLon, maxLat],
|
|
648
|
+
[minLon, maxLat],
|
|
649
|
+
[minLon, minLat],
|
|
650
|
+
],
|
|
651
|
+
],
|
|
652
|
+
};
|
|
653
|
+
};
|
|
654
|
+
/**
|
|
655
|
+
* @description Creates a round polygon from a point and a radius.
|
|
656
|
+
*/
|
|
657
|
+
const getPolygonFromPointAndRadius = (point, radius) => {
|
|
658
|
+
const [lon, lat] = point.coordinates;
|
|
659
|
+
// Adjust the number of points based on radius (resolution)
|
|
660
|
+
const pointsCount = Math.max(32, Math.floor(radius / 100)); // More points for larger radius
|
|
661
|
+
const angleStep = (2 * Math.PI) / pointsCount;
|
|
662
|
+
const coordinates = [];
|
|
663
|
+
for (let i = 0; i <= pointsCount; i++) {
|
|
664
|
+
const angle = i * angleStep;
|
|
665
|
+
// Calculate offset in latitude and longitude
|
|
666
|
+
const deltaLat = (radius / EARTH_RADIUS) * (180 / Math.PI);
|
|
667
|
+
const deltaLon = deltaLat / Math.cos((lat * Math.PI) / 180);
|
|
668
|
+
// Calculate new coordinates based on angle
|
|
669
|
+
const newLat = lat + deltaLat * Math.sin(angle);
|
|
670
|
+
const newLon = lon + deltaLon * Math.cos(angle);
|
|
671
|
+
coordinates.push([newLon, newLat]);
|
|
672
|
+
}
|
|
673
|
+
return {
|
|
674
|
+
type: "Polygon",
|
|
675
|
+
coordinates: [coordinates],
|
|
676
|
+
};
|
|
677
|
+
};
|
|
678
|
+
/**
|
|
679
|
+
* @description Creates a TU bounding box from a GeoJson Polygon.
|
|
680
|
+
*/
|
|
681
|
+
const getBoundingBoxFromGeoJsonPolygon = (polygon) => {
|
|
682
|
+
const points = polygon.coordinates[0];
|
|
683
|
+
const latitudes = points === null || points === void 0 ? void 0 : points.map(point => point[1]);
|
|
684
|
+
const longitudes = points === null || points === void 0 ? void 0 : points.map(point => point[0]);
|
|
685
|
+
if (!latitudes || !longitudes) {
|
|
686
|
+
return undefined;
|
|
687
|
+
}
|
|
688
|
+
return {
|
|
689
|
+
nw: {
|
|
690
|
+
latitude: Math.max(...latitudes),
|
|
691
|
+
longitude: Math.min(...longitudes),
|
|
692
|
+
},
|
|
693
|
+
se: {
|
|
694
|
+
latitude: Math.min(...latitudes),
|
|
695
|
+
longitude: Math.max(...longitudes),
|
|
696
|
+
},
|
|
697
|
+
};
|
|
698
|
+
};
|
|
699
|
+
/**
|
|
700
|
+
* @description Creates a GeoJSON Polygon from a TU bounding box.
|
|
701
|
+
*/
|
|
702
|
+
const getGeoJsonPolygonFromBoundingBox = (boundingBox) => {
|
|
703
|
+
const { nw, se } = boundingBox;
|
|
704
|
+
return {
|
|
705
|
+
type: "Polygon",
|
|
706
|
+
coordinates: [
|
|
707
|
+
[
|
|
708
|
+
[nw.longitude, nw.latitude], // Northwest corner
|
|
709
|
+
[se.longitude, nw.latitude], // Northeast corner
|
|
710
|
+
[se.longitude, se.latitude], // Southeast corner
|
|
711
|
+
[nw.longitude, se.latitude], // Southwest corner
|
|
712
|
+
[nw.longitude, nw.latitude], // Close the loop back to Northwest corner
|
|
713
|
+
],
|
|
714
|
+
],
|
|
715
|
+
};
|
|
716
|
+
};
|
|
717
|
+
/**
|
|
718
|
+
* @description Creates TU point coordinate from a GeoJSON Point.
|
|
719
|
+
*/
|
|
720
|
+
const getPointCoordinateFromGeoJsonPoint = (point) => {
|
|
721
|
+
return { latitude: point.coordinates[1], longitude: point.coordinates[0] };
|
|
722
|
+
};
|
|
460
723
|
|
|
461
724
|
/**
|
|
462
725
|
* Group an array of items by a key.
|
|
@@ -1104,4 +1367,4 @@ const uuidv4 = () => {
|
|
|
1104
1367
|
*/
|
|
1105
1368
|
const uuidv5 = (name, namespace) => v5(name, namespace);
|
|
1106
1369
|
|
|
1107
|
-
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, getMultipleCoordinatesFromGeoJsonObject, getPointCoordinateFromGeoJsonObject, 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 };
|
|
1370
|
+
export { DateTimeFormat, EARTH_RADIUS, HoursAndMinutesFormat, align, alphabeticallySort, arrayLengthCompare, arrayNotEmpty, booleanCompare, capitalize, convertBlobToBase64, convertMetersToYards, convertYardsToMeters, dateCompare, deleteUndefinedKeys, difference, doNothing, enumFromValue, enumFromValueTypesafe, enumOrUndefinedFromValue, exhaustiveCheck, filterByMultiple, formatAddress, formatCoordinates, fuzzySearch, geoJsonBboxSchema, geoJsonGeometrySchema, geoJsonLineStringSchema, geoJsonLinearRingSchema, geoJsonMultiLineStringSchema, geoJsonMultiPointSchema, geoJsonMultiPolygonSchema, geoJsonPointSchema, geoJsonPolygonNoHolesSchema, geoJsonPolygonSchema, geoJsonPositionSchema, geoJsonRectangularBoxPolygonSchema, getBoundingBoxFromGeoJsonPolygon, getDifferenceBetweenDates, getEndOfDay, getFirstLevelObjectPropertyDifferences, getGeoJsonPolygonFromBoundingBox, getISOStringFromDate, getMultipleCoordinatesFromGeoJsonObject, getPointCoordinateFromGeoJsonObject, getPointCoordinateFromGeoJsonPoint, getPolygonFromBbox, getPolygonFromPointAndRadius, 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 };
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/shared-utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.83",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.x"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"uuid": "^10.0.0"
|
|
10
|
+
"uuid": "^10.0.0",
|
|
11
|
+
"zod": "3.22.4"
|
|
11
12
|
},
|
|
12
13
|
"module": "./index.esm.js",
|
|
13
14
|
"main": "./index.cjs.js",
|
|
@@ -0,0 +1,210 @@
|
|
|
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>;
|
|
177
|
+
/**
|
|
178
|
+
* Polygon geometry object that explicitly disallows holes.
|
|
179
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.6
|
|
180
|
+
*/
|
|
181
|
+
export declare const geoJsonPolygonNoHolesSchema: z.ZodObject<{
|
|
182
|
+
type: z.ZodLiteral<"Polygon">;
|
|
183
|
+
coordinates: z.ZodTuple<[z.ZodEffects<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, "many">, [number, number][], [number, number][]>], null>;
|
|
184
|
+
}, "strict", z.ZodTypeAny, {
|
|
185
|
+
type: "Polygon";
|
|
186
|
+
coordinates: [[number, number][]];
|
|
187
|
+
}, {
|
|
188
|
+
type: "Polygon";
|
|
189
|
+
coordinates: [[number, number][]];
|
|
190
|
+
}>;
|
|
191
|
+
export type GeoJsonPolygonNoHoles = z.infer<typeof geoJsonPolygonNoHolesSchema>;
|
|
192
|
+
/**
|
|
193
|
+
* A Polygon with exactly 5 points and 4 horizontal/vertical sides that form a normal rectangular box.
|
|
194
|
+
*/
|
|
195
|
+
export declare const geoJsonRectangularBoxPolygonSchema: z.ZodEffects<z.ZodObject<{
|
|
196
|
+
type: z.ZodLiteral<"Polygon">;
|
|
197
|
+
coordinates: z.ZodArray<z.ZodEffects<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, "many">, [number, number][], [number, number][]>, "many">;
|
|
198
|
+
}, "strict", z.ZodTypeAny, {
|
|
199
|
+
type: "Polygon";
|
|
200
|
+
coordinates: [number, number][][];
|
|
201
|
+
}, {
|
|
202
|
+
type: "Polygon";
|
|
203
|
+
coordinates: [number, number][][];
|
|
204
|
+
}>, {
|
|
205
|
+
type: "Polygon";
|
|
206
|
+
coordinates: [number, number][][];
|
|
207
|
+
}, {
|
|
208
|
+
type: "Polygon";
|
|
209
|
+
coordinates: [number, number][][];
|
|
210
|
+
}>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { GeoJsonBbox, GeoJsonPoint, GeoJsonPolygon } from "./GeoJsonSchemas";
|
|
2
|
+
export declare const EARTH_RADIUS = 6378137;
|
|
3
|
+
interface PointCoordinate {
|
|
4
|
+
longitude: number;
|
|
5
|
+
latitude: number;
|
|
6
|
+
}
|
|
7
|
+
interface BoundingBox {
|
|
8
|
+
nw: PointCoordinate;
|
|
9
|
+
se: PointCoordinate;
|
|
10
|
+
}
|
|
11
|
+
interface GeoJSONGeometry {
|
|
12
|
+
type?: unknown;
|
|
13
|
+
coordinates?: number[] | number[][] | number[][][] | null;
|
|
14
|
+
}
|
|
15
|
+
interface GeoJsonFeature {
|
|
16
|
+
type?: unknown;
|
|
17
|
+
geometry?: GeoJSONGeometry | null;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* @description Extracts a point coordinate from a GeoJSON object.
|
|
21
|
+
* @param geoObject A GeoJSON object.
|
|
22
|
+
* @returns {PointCoordinate} A point coordinate.
|
|
23
|
+
*/
|
|
24
|
+
export declare const getPointCoordinateFromGeoJsonObject: (geoObject: GeoJsonFeature | GeoJSONGeometry | undefined | null) => PointCoordinate | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* @description Extracts multiple point coordinates from a GeoJSON object.
|
|
27
|
+
* @param geoObject A GeoJSON object.
|
|
28
|
+
* @returns {PointCoordinate[]} An array of point coordinates.
|
|
29
|
+
* @example getMultipleCoordinatesFromGeoJsonObject({ type: "Point", coordinates: [1, 2] }) // [{ longitude: 1, latitude: 2 }]
|
|
30
|
+
*/
|
|
31
|
+
export declare const getMultipleCoordinatesFromGeoJsonObject: (geoObject: GeoJsonFeature | GeoJSONGeometry | undefined | null) => PointCoordinate[] | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* @description Creates a polygon from a bounding box.
|
|
34
|
+
*/
|
|
35
|
+
export declare const getPolygonFromBbox: (bbox: GeoJsonBbox) => GeoJsonPolygon;
|
|
36
|
+
/**
|
|
37
|
+
* @description Creates a round polygon from a point and a radius.
|
|
38
|
+
*/
|
|
39
|
+
export declare const getPolygonFromPointAndRadius: (point: GeoJsonPoint, radius: number) => GeoJsonPolygon;
|
|
40
|
+
/**
|
|
41
|
+
* @description Creates a TU bounding box from a GeoJson Polygon.
|
|
42
|
+
*/
|
|
43
|
+
export declare const getBoundingBoxFromGeoJsonPolygon: (polygon: GeoJsonPolygon) => BoundingBox | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* @description Creates a GeoJSON Polygon from a TU bounding box.
|
|
46
|
+
*/
|
|
47
|
+
export declare const getGeoJsonPolygonFromBoundingBox: (boundingBox: BoundingBox) => GeoJsonPolygon;
|
|
48
|
+
/**
|
|
49
|
+
* @description Creates TU point coordinate from a GeoJSON Point.
|
|
50
|
+
*/
|
|
51
|
+
export declare const getPointCoordinateFromGeoJsonPoint: (point: GeoJsonPoint) => PointCoordinate;
|
|
52
|
+
export {};
|
package/src/index.d.ts
CHANGED
|
@@ -9,7 +9,8 @@ export * from "./enumUtils";
|
|
|
9
9
|
export * from "./exhaustiveCheck";
|
|
10
10
|
export * from "./fastArrayOperations";
|
|
11
11
|
export * from "./filter";
|
|
12
|
-
export * from "./
|
|
12
|
+
export * from "./GeoJson/GeoJsonSchemas";
|
|
13
|
+
export * from "./GeoJson/GeoJsonUtils";
|
|
13
14
|
export * from "./groupBy/groupBy";
|
|
14
15
|
export * from "./GroupingUtility";
|
|
15
16
|
export * from "./idUtils";
|
package/src/GeoJsonUtils.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
interface PointCoordinate {
|
|
2
|
-
longitude: number;
|
|
3
|
-
latitude: number;
|
|
4
|
-
}
|
|
5
|
-
interface GeoJSONGeometry {
|
|
6
|
-
type?: unknown;
|
|
7
|
-
coordinates?: number[] | number[][] | null;
|
|
8
|
-
}
|
|
9
|
-
interface GeoJsonFeature {
|
|
10
|
-
type?: unknown;
|
|
11
|
-
geometry?: GeoJSONGeometry | null;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* @description Extracts a point coordinate from a GeoJSON object.
|
|
15
|
-
* @param geoObject A GeoJSON object.
|
|
16
|
-
* @returns {PointCoordinate} A point coordinate.
|
|
17
|
-
*/
|
|
18
|
-
export declare const getPointCoordinateFromGeoJsonObject: (geoObject: GeoJsonFeature | GeoJSONGeometry | undefined | null) => PointCoordinate | undefined;
|
|
19
|
-
/**
|
|
20
|
-
* @description Extracts multiple point coordinates from a GeoJSON object.
|
|
21
|
-
* @param geoObject A GeoJSON object.
|
|
22
|
-
* @returns {PointCoordinate[]} An array of point coordinates.
|
|
23
|
-
* @example getMultipleCoordinatesFromGeoJsonObject({ type: "Point", coordinates: [1, 2] }) // [{ longitude: 1, latitude: 2 }]
|
|
24
|
-
*/
|
|
25
|
-
export declare const getMultipleCoordinatesFromGeoJsonObject: (geoObject: GeoJsonFeature | GeoJSONGeometry | undefined | null) => PointCoordinate[] | undefined;
|
|
26
|
-
export {};
|