| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 |
- // Expand old-town candidates into actual ENU footprints and bind them to original DEM samples.
- import { readFileSync, writeFileSync } from 'node:fs'
- import { Cartesian3, Cartographic, Matrix4, Transforms } from 'cesium'
- const dir = 'tools/city-massing/city/oldtown/',
- plan = JSON.parse(readFileSync(dir + 'plan.json'))
- const origin = { lon: 110.395, lat: 28.454 },
- transform = Transforms.eastNorthUpToFixedFrame(Cartesian3.fromDegrees(origin.lon, origin.lat)),
- inverse = Matrix4.inverseTransformation(transform, new Matrix4())
- const toLocal = (lon, lat) => Matrix4.multiplyByPoint(inverse, Cartesian3.fromDegrees(lon, lat), new Cartesian3())
- const toGeo = (q) => {
- const c = Cartographic.fromCartesian(Matrix4.multiplyByPoint(transform, new Cartesian3(...q), new Cartesian3()))
- return { lon: (c.longitude * 180) / Math.PI, lat: (c.latitude * 180) / Math.PI }
- }
- const modules = []
- const samples = []
- for (const b of plan.buildings) {
- const center = toLocal(b.lon, b.lat)
- const u = [Math.cos(b.heading), Math.sin(b.heading)],
- v = [-u[1], u[0]]
- const corners = [
- [-b.width / 2, -b.depth / 2],
- [b.width / 2, -b.depth / 2],
- [b.width / 2, b.depth / 2],
- [-b.width / 2, b.depth / 2],
- ].map(([x, y]) => [center.x + u[0] * x + v[0] * y, center.y + u[1] * x + v[1] * y, 0])
- const pts = [...corners, [center.x, center.y, 0]]
- // Interior samples at 5 m spacing so terrain relief is measured across the real footprint,
- // not inferred from four corners.
- const cols = Math.max(1, Math.ceil(b.width / 5))
- const rows = Math.max(1, Math.ceil(b.depth / 5))
- for (let row = 0; row <= rows; row++)
- for (let col = 0; col <= cols; col++) {
- const lx = -b.width / 2 + (b.width * col) / cols
- const ly = -b.depth / 2 + (b.depth * row) / rows
- pts.push([center.x + u[0] * lx + v[0] * ly, center.y + u[1] * lx + v[1] * ly, 0])
- }
- const sampleIndices = pts.map((p) => {
- samples.push({ ...toGeo(p), building: b.id })
- return samples.length - 1
- })
- modules.push({
- ...b,
- sourceId: b.sourceId || b.id,
- center: toGeo([center.x, center.y, 0]),
- centerENU: [center.x, center.y, 0],
- footprintENU: corners,
- archetype: b.archetype || 'house',
- sampleIndices,
- })
- }
- const out = {
- ...plan,
- origin,
- transform: Matrix4.toArray(transform),
- modules,
- samples,
- source: 'residential masks + bounded road/water exclusion + direct original DEM corners/center',
- disclaimer: plan.disclaimer + ' Terrain seating is validated separately against the original quantized mesh.',
- }
- writeFileSync(dir + 'plan.json', JSON.stringify(out, null, 2))
- console.log({ modules: modules.length, samples: samples.length })
|