// Holt die Kartendaten eines Weltausschnitts für die Platzierung, in Welt-x/-y (Meter, Norden = +y): // // node .claude/skills/placing-editor-models/scripts/site-data.mjs // // Enthält Land, Flächen (park/sand/urban), den Straßenkorridor, Gebäude, Palmen und die Laternen, // wie PropLayer sie setzt (chunkLampPoints() je Chunk — Laternen stehen bis 16 m neben ihrer // Mittellinie, deshalb werden auch die Nachbarchunks des Ausschnitts gelesen). render-site.py // zeichnet daraus das Vergleichsbild; die Grenzen für Straßenrand, Felder usw. lassen sich direkt // aus dem JSON ablesen. // // Der Straßenkorridor — bis Teil 2 ein einziger Schlüssel `roads` — steht seit dem Formatwechsel in // drei Schichten: `carriageway` (Fahrbahn, 15 cm tiefer), `sidewalk` (Gehweg) und `lots` // (Restflächen wie Parkplätze und Vorplätze). Zusammen sind sie genau die Fläche, die früher // `roads` war; sie bleiben hier getrennt, weil ein Ort neben einem Gehweg anders liegt als neben // einer Fahrbahn. import { readFileSync, writeFileSync } from "node:fs"; import { CHUNKS_PER_SIDE, CHUNK_SIZE, SLICE, chunkName } from "../../../../src/world/coords.js"; import { chunkLampPoints } from "../../../../src/world/props.js"; const [x0, x1, y0, y1] = process.argv.slice(2, 6).map(Number); const out = process.argv[6]; if ([x0, x1, y0, y1].some(Number.isNaN) || !out) { console.error("Aufruf: node site-data.mjs "); process.exit(1); } const iOf = (X) => Math.floor((X - SLICE.x0) / CHUNK_SIZE); const jOf = (Y) => Math.floor((SLICE.y1 - Y) / CHUNK_SIZE); const clamp = (k) => Math.min(CHUNKS_PER_SIDE - 1, Math.max(0, k)); const chunkUrl = (i, j) => new URL(`../../../../public/data/vice-a/chunk_${i}_${j}.json`, import.meta.url); const chunks = []; const palms = []; const lamps = []; for (let i = clamp(iOf(x0) - 1); i <= clamp(iOf(x1) + 1); i++) { for (let j = clamp(jOf(y1) - 1); j <= clamp(jOf(y0) + 1); j++) { const c = JSON.parse(readFileSync(chunkUrl(i, j), "utf8")); const [ox, oz] = c.origin; const world = (u, v) => [Number((ox + u).toFixed(2)), Number((-(oz + v)).toFixed(2))]; const rings = (poly) => poly.map((ring) => ring.map(([u, v]) => world(u, v))); chunks.push({ key: `${i}_${j}`, name: chunkName(i, j), land: c.land.map(rings), surfaces: (c.surfaces ?? []).map((s) => ({ kind: s.kind, rings: rings(s.poly) })), carriageway: (c.carriageway ?? []).map((r) => ({ rings: rings(r.poly) })), sidewalk: (c.sidewalk ?? []).map((s) => ({ rings: rings(s.poly) })), lots: (c.lots ?? []).map((l) => ({ rings: rings(l.poly) })), bridges: (c.bridges ?? []).map((b) => ({ rings: rings(b.poly) })), buildings: c.buildings.map((b) => ({ height: b.height, rings: rings(b.poly) })), }); for (const [u, v] of c.palms ?? []) palms.push(world(u, v)); for (const p of chunkLampPoints(c)) lamps.push(world(p.x, p.z)); } } const inside = ([X, Y]) => X >= x0 && X <= x1 && Y >= y0 && Y <= y1; writeFileSync(out, JSON.stringify({ box: { x0, x1, y0, y1 }, chunks, palms, lamps })); console.log(`${chunks.length} Chunks (${chunks.map((c) => c.name).join(", ")}), ` + `${palms.filter(inside).length} Palmen und ${lamps.filter(inside).length} Laternen im Ausschnitt → ${out}`); for (const lamp of lamps.filter(inside)) console.log(` Laterne bei (${lamp[0]} | ${lamp[1]})`);