| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960 |
- // Evidence-reviewed additions only; preserves the original seated GIS/DEM contract.
- import assert from 'node:assert/strict'
- import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'
- import { resolve, join } from 'node:path'
- import { createHash } from 'node:crypto'
- import { PNG } from 'pngjs'
- import jpeg from 'jpeg-js'
- import { Matrix4, Cartesian3, Cartographic } from 'cesium'
- const root = resolve('tools/city-massing/city/cad-bim')
- const ids = ['cad-20562', 'cad-20556', 'cad-20308', 'cad-20534', 'cad-20301', 'cad-20293',
- 'cad-11179', 'cad-11204', 'cad-11154', 'cad-11167', 'cad-11192', 'cad-11395', 'cad-11386']
- const seated = JSON.parse(readFileSync(join(root, 'seated-plan.json'), 'utf8'))
- const selected = ids.map((id) => { const b = seated.buildings.find((b) => b.id === id); assert.ok(b, id); return { ...b, kind: 'massing' } })
- const out = join(root, 'near-estate-pilot-input')
- mkdirSync(out, { recursive: true })
- const textures = {}, sourceTiles = new Map(), zoom = 19, tileCount = 2 ** zoom
- const transform = Matrix4.fromArray(seated.transform)
- const slippy = (lon, lat) => [(lon + 180) / 360 * tileCount,
- (1 - Math.asinh(Math.tan(lat * Math.PI / 180)) / Math.PI) / 2 * tileCount]
- function tile(x, y) {
- const key = `${x}/${y}`
- if (!sourceTiles.has(key)) {
- const path = `D:/GIS/GIS/yuanlingPic3/${zoom}/${x}/${tileCount - 1 - y}.png`
- const bytes = readFileSync(path)
- sourceTiles.set(key, { image: bytes[0] === 0xff ? jpeg.decode(bytes) : PNG.sync.read(bytes), path,
- sha256: createHash('sha256').update(bytes).digest('hex') })
- }
- return sourceTiles.get(key).image
- }
- for (const b of selected) {
- const low = [0, 1].map((axis) => Math.min(...b.footprintENU.map((p) => p[axis])))
- const high = [0, 1].map((axis) => Math.max(...b.footprintENU.map((p) => p[axis])))
- // ENU-aligned imagery reprojection, no rotated rectangle or shifted survey outline.
- const longest = Math.max(high[0] - low[0], high[1] - low[1])
- const width = Math.max(32, Math.ceil((high[0] - low[0]) / longest * 256))
- const height = Math.max(32, Math.ceil((high[1] - low[1]) / longest * 256))
- const image = new PNG({ width, height })
- for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
- const world = Matrix4.multiplyByPoint(transform, new Cartesian3(low[0] + (x + 0.5) / width * (high[0] - low[0]),
- high[1] - (y + 0.5) / height * (high[1] - low[1]), b.positionENU[2]), new Cartesian3())
- const point = Cartographic.fromCartesian(world)
- const pixel = slippy(point.longitude * 180 / Math.PI, point.latitude * 180 / Math.PI).map((v) => v * 256)
- const tx = Math.floor(pixel[0] / 256), ty = Math.floor(pixel[1] / 256)
- const source = tile(tx, ty)
- const sourceIndex = (Math.floor(pixel[1] - ty * 256) * 256 + Math.floor(pixel[0] - tx * 256)) * 4
- source.data.copy(image.data, (y * width + x) * 4, sourceIndex, sourceIndex + 4)
- }
- const path = join(out, `${b.id}-roof.png`)
- writeFileSync(path, PNG.sync.write(image))
- textures[b.id] = { path, boundsENU: [low, high], source: 'local yuanlingPic3 EPSG3857 TMS z19 reprojected to seated ENU',
- size: [width, height], caveat: 'Orthophoto roof appearance only; capture date and redistribution rights unknown. Shadows/perspective may be baked in. No facade-photo claim.' }
- }
- writeFileSync(join(out, 'roof-textures.json'), JSON.stringify(textures, null, 2))
- writeFileSync(join(out, 'seated-plan.json'), JSON.stringify({ ...seated, buildings: selected, excluded: [],
- batch: { selected: selected.length, source: 'large-building-audit satellite review: central-estate and western-estate',
- heightsAndFacades: 'estimated from original CAD preparation, not surveyed BIM' } }, null, 2))
- writeFileSync(join(out, 'imagery-provenance.json'), JSON.stringify({ zoom, sourceTiles: [...sourceTiles.values()].map(({ image, ...record }) => record), buildings: ids,
- deploymentPolicy: 'local-only existing GIS mount; no inferred permission for public redistribution' }, null, 2))
- console.log(JSON.stringify({ out, buildings: ids.length, textures: Object.keys(textures).length, sourceTiles: sourceTiles.size }))
|