diff options
| author | Petri Hienonen <petri.hienonen@gmail.com> | 2025-11-03 11:19:15 +0200 |
|---|---|---|
| committer | Petri Hienonen <petri.hienonen@gmail.com> | 2025-11-04 13:16:38 +0200 |
| commit | a4ed99a370930b1a0c0f065906ed99c15a015fd4 (patch) | |
| tree | 36dbc74e78d24fea7cf58c13e0ecbc929f9f18f7 /app/geometry.js | |
| parent | b03ee7032b2ea2d4d22ab7ec1346b7c9331cfc17 (diff) | |
| download | housing-a4ed99a370930b1a0c0f065906ed99c15a015fd4.tar.zst | |
Update documentation
Diffstat (limited to 'app/geometry.js')
| -rw-r--r-- | app/geometry.js | 52 |
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); |
