/** * ESRI → GeoJSON conversion for eTerra features. */ import type { EsriFeature } from "./eterra-client"; export type GeoJsonPolygon = { type: "Polygon"; coordinates: number[][][] }; export type GeoJsonMultiPolygon = { type: "MultiPolygon"; coordinates: number[][][][]; }; export type GeoJsonFeature = { type: "Feature"; properties: Record; geometry: GeoJsonPolygon | GeoJsonMultiPolygon | null; }; export type GeoJsonFeatureCollection = { type: "FeatureCollection"; features: GeoJsonFeature[]; }; const ringArea = (ring: number[][]) => { let area = 0; for (let i = 0; i < ring.length - 1; i++) { const curr = ring[i]!; const next = ring[i + 1]!; area += curr[0]! * next[1]! - next[0]! * curr[1]!; } return area / 2; }; const isClockwise = (ring: number[][]) => ringArea(ring) < 0; const closeRing = (ring: number[][]) => { if (ring.length === 0) return ring; const first = ring[0]!; const last = ring[ring.length - 1]!; return first[0] !== last[0] || first[1] !== last[1] ? [...ring, [first[0]!, first[1]!]] : ring; }; const ringsToPolygons = (rings: number[][][]) => { const polygons: number[][][][] = []; let current: number[][][] | null = null; for (const ring of rings) { const closed = closeRing(ring); if (closed.length < 4) continue; if (isClockwise(closed) || !current) { if (current) polygons.push(current); current = [closed]; } else { current.push(closed); } } if (current) polygons.push(current); return polygons; }; export const esriToGeojson = ( features: EsriFeature[], ): GeoJsonFeatureCollection => { const geoFeatures: GeoJsonFeature[] = []; for (const feature of features) { const rings = feature.geometry?.rings; if (!rings?.length) continue; const polygons = ringsToPolygons(rings); if (!polygons.length) continue; const geometry: GeoJsonPolygon | GeoJsonMultiPolygon = polygons.length === 1 ? { type: "Polygon", coordinates: polygons[0]! } : { type: "MultiPolygon", coordinates: polygons }; geoFeatures.push({ type: "Feature", properties: feature.attributes ?? {}, geometry, }); } return { type: "FeatureCollection", features: geoFeatures }; };