Skip to content

Commit e65f62b

Browse files
aoi.js erstellen
1 parent f4b3c11 commit e65f62b

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

services/aoi/aoi.js

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import fs from "fs/promises";
2+
import path from "path";
3+
import { DOMParser } from "xmldom";
4+
import * as turf from "@turf/turf";
5+
6+
// Minimal KML -> GeoJSON parser (supports Polygon/MultiPolygon/LineString/Point)
7+
// For production можно заменить на полноценный парсер, но этот уже рабочий для AOI-полигонов.
8+
function kmlToGeoJSON(kmlText) {
9+
const doc = new DOMParser().parseFromString(kmlText, "text/xml");
10+
const placemarks = Array.from(doc.getElementsByTagName("Placemark"));
11+
12+
const features = placemarks.map((pm) => {
13+
const nameEl = pm.getElementsByTagName("name")[0];
14+
const name = nameEl ? nameEl.textContent : "AOI";
15+
16+
const poly = pm.getElementsByTagName("Polygon")[0];
17+
const line = pm.getElementsByTagName("LineString")[0];
18+
const point = pm.getElementsByTagName("Point")[0];
19+
20+
const parseCoords = (coordsText) =>
21+
coordsText
22+
.trim()
23+
.split(/\s+/)
24+
.map((tuple) => tuple.split(",").map(Number))
25+
.map(([lon, lat]) => [lon, lat]);
26+
27+
if (poly) {
28+
const coordsEl = poly.getElementsByTagName("coordinates")[0];
29+
if (!coordsEl) throw new Error("KML Polygon missing coordinates");
30+
const ring = parseCoords(coordsEl.textContent);
31+
// ensure closed ring
32+
const first = ring[0];
33+
const last = ring[ring.length - 1];
34+
if (first && last && (first[0] !== last[0] || first[1] !== last[1])) ring.push(first);
35+
36+
return {
37+
type: "Feature",
38+
properties: { name },
39+
geometry: { type: "Polygon", coordinates: [ring] },
40+
};
41+
}
42+
43+
if (line) {
44+
const coordsEl = line.getElementsByTagName("coordinates")[0];
45+
if (!coordsEl) throw new Error("KML LineString missing coordinates");
46+
const coords = parseCoords(coordsEl.textContent);
47+
return {
48+
type: "Feature",
49+
properties: { name },
50+
geometry: { type: "LineString", coordinates: coords },
51+
};
52+
}
53+
54+
if (point) {
55+
const coordsEl = point.getElementsByTagName("coordinates")[0];
56+
if (!coordsEl) throw new Error("KML Point missing coordinates");
57+
const [lon, lat] = coordsEl.textContent.trim().split(",").map(Number);
58+
return {
59+
type: "Feature",
60+
properties: { name },
61+
geometry: { type: "Point", coordinates: [lon, lat] },
62+
};
63+
}
64+
65+
// If nothing matched:
66+
return null;
67+
}).filter(Boolean);
68+
69+
return { type: "FeatureCollection", features };
70+
}
71+
72+
function normalizeToFeatureCollection(geojson) {
73+
if (!geojson) throw new Error("Empty AOI");
74+
if (geojson.type === "FeatureCollection") return geojson;
75+
if (geojson.type === "Feature") return { type: "FeatureCollection", features: [geojson] };
76+
// Geometry
77+
if (geojson.type && geojson.coordinates) return { type: "FeatureCollection", features: [{ type: "Feature", properties: {}, geometry: geojson }] };
78+
throw new Error("Unsupported AOI GeoJSON structure");
79+
}
80+
81+
export async function parseAoiFileToFeatureCollection(filePath, originalName = "") {
82+
const ext = (path.extname(originalName || filePath) || "").toLowerCase();
83+
84+
const raw = await fs.readFile(filePath, "utf-8");
85+
86+
if (ext === ".geojson" || ext === ".json") {
87+
const parsed = JSON.parse(raw);
88+
return normalizeToFeatureCollection(parsed);
89+
}
90+
91+
if (ext === ".kml") {
92+
return normalizeToFeatureCollection(kmlToGeoJSON(raw));
93+
}
94+
95+
throw new Error(`Unsupported AOI format: ${ext}. Use .geojson/.json or .kml`);
96+
}
97+
98+
export function computeAreaKm2(featureCollection) {
99+
// Sum polygon areas; for non-polygons, area ~0
100+
let sum = 0;
101+
for (const f of featureCollection.features || []) {
102+
try {
103+
const geom = f?.geometry;
104+
if (!geom) continue;
105+
106+
if (geom.type === "Polygon" || geom.type === "MultiPolygon") {
107+
const areaM2 = turf.area(f);
108+
sum += areaM2;
109+
}
110+
} catch (_) {
111+
// ignore malformed features
112+
}
113+
}
114+
const km2 = sum / 1_000_000;
115+
return Math.round(km2 * 1000) / 1000; // 0.001 km² precision
116+
}

0 commit comments

Comments
 (0)