aboutsummaryrefslogtreecommitdiffstats
path: root/app/geometry.js
diff options
context:
space:
mode:
Diffstat (limited to 'app/geometry.js')
-rw-r--r--app/geometry.js52
1 files changed, 46 insertions, 6 deletions
diff --git a/app/geometry.js b/app/geometry.js
index 421d90b..62236e0 100644
--- a/app/geometry.js
+++ b/app/geometry.js
@@ -10,7 +10,6 @@ const TOLERANCE = 1e-10;
/**
* Geographic bounds representation
- * @class
*/
export class Bounds {
/**
@@ -26,6 +25,19 @@ export class Bounds {
this.maxY = maxY;
}
+ /**
+ * Create a union of multiple Bounds objects to cover the combined area
+ * @param {Bounds[]} boundsArray - Array of Bounds instances
+ * @returns {Bounds} A new Bounds instance covering all input bounds
+ */
+ static union(boundsArray) {
+ const minX = Math.min(...boundsArray.map((b) => b.minX));
+ const minY = Math.min(...boundsArray.map((b) => b.minY));
+ const maxX = Math.max(...boundsArray.map((b) => b.maxX));
+ const maxY = Math.max(...boundsArray.map((b) => b.maxY));
+ return new Bounds(minX, minY, maxX, maxY);
+ }
+
/** @returns {number} Width in degrees */
get width() {
return this.maxX - this.minX;
@@ -121,8 +133,8 @@ export class Geometry {
/**
* Deserialize geometry from GeoJSON
- * @param {Object} geojson - GeoJSON geometry object
- * @returns {Geometry|null} Geometry instance or null
+ * @param {{type: string, coordinates?: any, [key: string]: any}} geojson
+ * @returns {Geometry|null}
*/
static fromGeoJSON(geojson) {
if (!geojson?.type) return null;
@@ -136,7 +148,7 @@ export class Geometry {
case "MultiLineString":
return MultiLineString.fromGeoJSON(geojson);
default:
- throw new Error(`Unsupported geometry type: ${geojson.type}`);
+ throw new Error(`Invalid GeoJSON object: missing required 'type' property`);
}
}
@@ -253,8 +265,8 @@ export class Geometry {
const pointsB = Geometry.#geometryToPoints(b);
let minDistance = Infinity;
+ /** @type {[Point|null, Point|null]} */
let closestPair = [null, null];
-
for (const pointA of pointsA) {
for (const pointB of pointsB) {
const dist = Point.distance(pointA, pointB);
@@ -639,7 +651,17 @@ export class LineString extends Geometry {
}
/**
+ /**
* Check if two segments intersect
+ * @param {number} p0x
+ * @param {number} p0y
+ * @param {number} p1x
+ * @param {number} p1y
+ * @param {number} p2x
+ * @param {number} p2y
+ * @param {number} p3x
+ * @param {number} p3y
+ * @returns {boolean}
*/
static #segmentsIntersect(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) {
const s1x = p1x - p0x;
@@ -840,7 +862,17 @@ export class Polygon extends Geometry {
* @returns {Polygon}
*/
static fromGeoJSON(geojson) {
- return new Polygon(geojson.coordinates);
+ const rings = geojson.coordinates.map((ring /*: Coordinate[] */) => {
+ if (ring.length < 1) return ring;
+ const first = ring[0];
+ const last = ring[ring.length - 1];
+ if (first[0] !== last[0] || first[1] !== last[1]) {
+ // Append first coordinate to the end if not closed
+ return [...ring, first];
+ }
+ return ring;
+ });
+ return new Polygon(rings);
}
/**
@@ -892,6 +924,10 @@ export class Feature {
* @param {Object} geojson - GeoJSON feature
* @returns {Feature|null}
*/
+ /**
+ * @param {{geometry: object, properties?: object, id?: string|number}} geojson
+ * @returns {Feature|null}
+ */
static fromGeoJSON(geojson) {
if (!geojson?.geometry) return null;
@@ -928,6 +964,10 @@ export class Collection {
* @param {Object} geojson - GeoJSON collection
* @returns {Collection}
*/
+ /**
+ * @param {{features?: any[]}} geojson
+ * @returns {Collection}
+ */
static fromGeoJSON(geojson) {
const features = (geojson.features ?? []).map(Feature.fromGeoJSON).filter(Boolean);