97 lines
2.7 KiB
JavaScript
97 lines
2.7 KiB
JavaScript
|
|
import fs from "node:fs/promises";
|
|||
|
|
import path from "node:path";
|
|||
|
|
|
|||
|
|
const ROOT = process.cwd();
|
|||
|
|
|
|||
|
|
const ZONES = [
|
|||
|
|
{ zoneId: "1", file: "특정어업수역Ⅰ.json", label: "Ⅰ", name: "수역I(동해)" },
|
|||
|
|
{ zoneId: "2", file: "특정어업수역Ⅱ.json", label: "Ⅱ", name: "수역II(제주남방)" },
|
|||
|
|
{ zoneId: "3", file: "특정어업수역Ⅲ.json", label: "Ⅲ", name: "수역III(서해남부)" },
|
|||
|
|
{ zoneId: "4", file: "특정어업수역Ⅳ.json", label: "Ⅳ", name: "수역IV(서해중간)" },
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
// Inverse Web Mercator (EPSG:3857) -> WGS84 lon/lat
|
|||
|
|
function mercatorToLonLat(x, y) {
|
|||
|
|
const R = 6378137;
|
|||
|
|
const lon = (x / R) * (180 / Math.PI);
|
|||
|
|
const lat = (2 * Math.atan(Math.exp(y / R)) - Math.PI / 2) * (180 / Math.PI);
|
|||
|
|
return [lon, lat];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function looksLikeLonLat(x, y) {
|
|||
|
|
return Math.abs(x) <= 180 && Math.abs(y) <= 90;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function convertPoint(pt) {
|
|||
|
|
if (!Array.isArray(pt) || pt.length < 2) return pt;
|
|||
|
|
const x = pt[0];
|
|||
|
|
const y = pt[1];
|
|||
|
|
if (typeof x !== "number" || typeof y !== "number") return pt;
|
|||
|
|
if (looksLikeLonLat(x, y)) return [x, y];
|
|||
|
|
return mercatorToLonLat(x, y);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function convertCoords(coords) {
|
|||
|
|
if (!Array.isArray(coords)) return coords;
|
|||
|
|
if (coords.length === 0) return coords;
|
|||
|
|
|
|||
|
|
// Point: [x, y]
|
|||
|
|
if (typeof coords[0] === "number") return convertPoint(coords);
|
|||
|
|
|
|||
|
|
// Nested arrays
|
|||
|
|
return coords.map(convertCoords);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function main() {
|
|||
|
|
const rawDir = path.join(ROOT, "data", "raw", "zones");
|
|||
|
|
const outDir = path.join(ROOT, "apps", "web", "public", "data", "zones");
|
|||
|
|
await fs.mkdir(outDir, { recursive: true });
|
|||
|
|
|
|||
|
|
const features = [];
|
|||
|
|
for (const z of ZONES) {
|
|||
|
|
const rawPath = path.join(rawDir, z.file);
|
|||
|
|
const txt = await fs.readFile(rawPath, "utf-8");
|
|||
|
|
const fc = JSON.parse(txt);
|
|||
|
|
if (!fc || fc.type !== "FeatureCollection" || !Array.isArray(fc.features)) {
|
|||
|
|
throw new Error(`Unexpected GeoJSON in ${rawPath}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (const f of fc.features) {
|
|||
|
|
if (!f?.geometry?.coordinates) continue;
|
|||
|
|
const geometry = {
|
|||
|
|
...f.geometry,
|
|||
|
|
coordinates: convertCoords(f.geometry.coordinates),
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
features.push({
|
|||
|
|
...f,
|
|||
|
|
properties: {
|
|||
|
|
...(f.properties || {}),
|
|||
|
|
zoneId: z.zoneId,
|
|||
|
|
zoneLabel: z.label,
|
|||
|
|
zoneName: z.name,
|
|||
|
|
},
|
|||
|
|
geometry,
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const out = {
|
|||
|
|
type: "FeatureCollection",
|
|||
|
|
name: "zones.wgs84",
|
|||
|
|
features,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const outPath = path.join(outDir, "zones.wgs84.geojson");
|
|||
|
|
await fs.writeFile(outPath, JSON.stringify(out), "utf-8");
|
|||
|
|
// eslint-disable-next-line no-console
|
|||
|
|
console.log(`Wrote ${features.length} features -> ${path.relative(ROOT, outPath)}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
main().catch((err) => {
|
|||
|
|
// eslint-disable-next-line no-console
|
|||
|
|
console.error(err);
|
|||
|
|
process.exit(1);
|
|||
|
|
});
|
|||
|
|
|