// Direct local DEM sampling at each actual rotated footprint edge/interior. Missing data fails. import { readFileSync, writeFileSync } from 'node:fs' const BASE = process.env.COCKPIT_BASE || 'http://localhost:8000' const path = 'tools/city-massing/district/plan.json' const plan = JSON.parse(readFileSync(path)) const puppeteer = ( await import( 'file:///C:/Users/ASUS/.dsh/profiles/desktop/node_modules/puppeteer-core/lib/puppeteer/puppeteer-core.js' ) ).default const browser = await puppeteer.launch({ executablePath: 'C:/Program Files/Google/Chrome/Application/chrome.exe', headless: true, args: ['--no-sandbox'], }) try { const page = await browser.newPage() await page.goto(BASE, { waitUntil: 'domcontentloaded' }) await page.setContent('

本地 DEM 逐栋采样

') const heights = await page.evaluate(async (points) => { window.CESIUM_BASE_URL = '/cesium/' const text = await (await fetch('/src/modules/cockpit/services/cesiumEngine.js')).text() const url = text.match(/from\s*["']([^"']*\/cesium\.js[^"']*)["']/)?.[1] if (!url) throw new Error('Cesium URL missing') const C = await import(url) const provider = await C.CesiumTerrainProvider.fromUrl('/gis/dem', { requestVertexNormals: false }) const result = [] for (let i = 0; i < points.length; i += 256) { const chunk = points.slice(i, i + 256).map((p) => C.Cartographic.fromDegrees(p.lon, p.lat)) const sampled = await C.sampleTerrainMostDetailed(provider, chunk, true) result.push(...sampled.map((p) => p.height)) } if (result.some((h) => !Number.isFinite(h))) throw new Error('Missing terrain: reject rather than fallback') return result }, plan.samples) writeFileSync( 'tools/city-massing/district/terrain-samples.json', JSON.stringify( { provider: '/gis/dem', sampling: 'sampleTerrainMostDetailed original provider, true on tile failure; actual rotated footprints edges and interiors', planPoints: plan.samples, heights, }, null, 2 ) ) console.log({ directSamples: heights.length, min: Math.min(...heights), max: Math.max(...heights) }) } finally { await browser.close() }