# 沅陵县城「轻量体块」模型 ⚠️ **这是示意体块,不是实测建筑。** 除 OSM 的少量真实轮廓外,其余体块是按**真实路网 / 建成区 / 水系**程序化排布的**可复用楼房单元**,用途是让一张图的**远景**看起来像一座县城。 **不得用于任何量测、统计或应急决策** —— 产物里的 `meta.json` 也写了同一条免责声明。 ## 为什么存在这套工具 甲方给的 `D:\GIS\GIS\yuanlingSHP`(cesiumlab `model2tiles` 导出)经维护者目测判定 **「明显错位 + 只是白色立方体,效果非常差」**,不采用。维护者 2026-09-18 定的替代方向: **参照 GIS 数据做轻量建模,远景基本达标、不要求近景细节、楼房模型可复用**。 ## 地理配准由构造保证 原料是经纬度,生成时投影到以**目标点**(沅陵县城 `110.39028164, 28.4436787`)为原点的 ENU 米制坐标,再写成 3D Tiles 根的 `transform` —— 不存在"猜模型原点"这件事 (上一版的错位正是因为原始导出缺根 `tileset.json`、分块是局部坐标)。 ## 原料(本机) | 输入 | 用途 | | --- | --- | | `planet_...-shp/shpOutput/buildings.json` | OSM 真实轮廓(城区内 169 个,直接按真实形状起楼) | | `planet_...-shp/shpOutput/roads.json` | OSM 路网 —— 体块**沿街**排布(贴街是我国县城的常态) | | `planet_...-shp/shpOutput/waterways.json` | 水系(沅江)—— 体块避让 ≥22–26 m | | `output/resa.json` | 1:5 万居民地面(建成区范围)—— 区内抖动网格补白 | | `cityMassing/terrain-grid.json` | 本地 DEM 采样网格 —— 体块**坐到地形上**(否则基准面在椭球 0 m,整片楼被埋进山里) | ## 可复用楼房单元 三种单元反复使用,只有位置/朝向/层数/配色不同("楼房模型可复用"的落点): | 单元 | 体量 | 层数 | 屋顶 | 出现位置 | | --- | --- | --- | --- | --- | | `house` | 9 × 7.2 m | 1–2 层 | 坡屋顶 | 城区外围 | | `block` | 14 × 10 m | 3–5 层 | 平顶 | 一般街区 | | `slab` | 26 × 11 m | 6–8 层 | 平顶 | 城区核心(≤400 m) | 配色共 13 种(墙 8 + 屋顶 5),按位置哈希稳定分配 —— 同一输入每次生成结果一致,不会刷一次变一样。 ## 用法 ```bash # 1) 先在本地 DEM 上采一张高程网格(需要 dev server 在跑:yarn dev) node <一次性脚本> sample-terrain-grid.mjs # 写到 /terrain-grid.json # 2) 生成 node tools/city-massing/build-town.mjs # 产物写到 D:\GIS\GIS\cityMassing # 3) 回验(地理配准 / tileset 自洽 / GLB 结构 / 免责声明) node tools/city-massing/verify-town.mjs ``` 生成后把它配到一张图上(`map.tilesets`): ```jsonc "map": { "tilesets": [{ "url": "/gis/city/tileset.json" }] } ``` dev 环境由 `.env.local` 的 `COCKPIT_GIS_MOUNTS` 把 `D:\GIS\GIS\cityMassing` 挂到 `/gis/city`; 生产环境按 `docs/cockpit-deployment.md` 第 3 节的 nginx 片段挂同一目录。 ## 踩过的坑(别再踩) - **tileset 级 `geometricError` 必须 > 0**:写成 0 时 Cesium 不请求根瓦片内容 —— `tileset.json` 拉到了、`town.glb` 始终不下载,页面上一栋楼都没有(已用诊断读数定位并修掉)。 - **glTF 是 Y-up,3D Tiles 的局部系是 Z-up**:顶点要写 `(east, up, -north)`, Cesium 装载时按 `(x, -z, y)` 转回来。写反了模型会躺倒/镜像 —— `verify-town.mjs` 里那条 "真实轮廓外接盒角点 2 m 内命中"就是专门证伪这条链的。 ## 已知取舍 - **不是实测**:只有 74 个真实轮廓(城区内可用部分),其余是程序化体块。 - **高度是估的**:OSM 无 `height`/`building:levels`,层数按"离中心距离 + 位置哈希"给。 - **地形做了简化**:高程来自 17×17 采样网格的双线性插值,局部起伏会有几米误差。 - **覆盖窗口**:目标点 ±0.020 经 / ±0.015 纬(约 1.9 km × 1.7 km),超出窗口不生成。