# 34: 沅陵县城轻量体块模型(替代被否的 cesiumlab 导出) **What to build:** 甲方那份 `yuanlingSHP`(cesiumlab `model2tiles`)经维护者目测判定 **「明显错位 + 只是白色立方体,效果非常差」**,不采用。按维护者定的替代方向做一版 **轻量建模**:参照 GIS 数据(路网 / 建成区 / 水系 / 真实轮廓)程序化生成县城体块, **远景基本达标、不要求近景细节、楼房模型可复用**;地理配准由构造保证(不再靠猜原点)。 Status: resolved Labels: ready-for-agent ## Answer **已交付,但维护者目测否决** —— 本条就此关闭,重做见 **票 35**。 否决理由(维护者 2026-09-18 看渲染图后给出,逐条记下以免重做时再犯): 1. **远景看不清、近景完全不像房屋、屋顶是漏的**:楼房看起来是一个个漏顶的盒子。 根因(自查):`emitBuilding()` 里平屋顶那一片四边形的**绕序与法线不匹配**,而材质是 `doubleSided: false` → 顶面被背面剔除,于是"漏顶";坡屋顶那段还先 push 了一个占位四边形再回退, 几何不干净。**这是可以确定的技术缺陷,不是口味问题。** 2. **建筑尺寸与卫星图完全不搭边**:单元尺寸(9×7.2 / 14×10 / 26×11 m)与实际县城房屋尺度不符, 且密度/排布方式一眼是"程序生成"。 3. **模型图层压在 Cesium 之上、不被地形遮挡**(明显 bug):楼房没有真正贴合地形, 高处飘、洼处陷 —— 根因是**高程取自 17×17 的粗网格双线性插值**(94–188 m 区间, 局部误差可达十几米),不是逐栋按 DEM 采样。 4. 维护者明确要求:**重做时房屋要稍微加点细节**,使中近景也能辨认出是房屋。 **注意**:本条的机器验证(坐标链、GLB 结构、加载三态)都是真的,错在**几何质量与地形贴合**上 —— 教训是"能加载 + 坐标对"不等于"看起来对",视觉验收必须由维护者做,且生成器要有可调的形态参数。 ## 依赖与事实 - 被否的那版:`D:\GIS\GIS\yuanlingSHP` 缺根 `tileset.json`、分块是局部坐标。为看效果曾补过一版 合成根文件(锚定"县城稠密簇 == 目标坐标"),维护者目测**错位且只有白方块** → **该合成根文件已删除**, `/gis/models` 挂载也已移除。教训:靠推测锚点 + 单色方块 = 不可接受。 - 本机可用的真原料:OSM `buildings/roads/waterways`(城区内 169 个真实轮廓 + 85 条路 + 沅江)、 1:5 万居民地面 `output/resa.json`(148 个建成区多边形)、本地 DEM(`yuanlingDEM`)。 - 关键洞察:**原料是经纬度**,所以这次的地理配准是"由构造保证"的 —— 投影到以目标点为原点的 ENU 米制坐标,再写成 3D Tiles 根的 `transform`,不存在"猜模型原点"。 ## 验收 - [x] 有一套**可重跑**的生成工具(不是一次性手搓产物):`tools/city-massing/build-town.mjs`。 - [x] 地理配准正确:真实轮廓在模型里的位置与源经纬度一致(回验脚本可证伪整条坐标链)。 - [x] 不是单一白色:按单元类型分配 13 种材质(墙 8 + 屋顶 5),且**同一输入结果稳定**(可复现)。 - [x] 体块**坐到地形上**(本地 DEM 采样网格插值),而不是浮在椭球 0 米里。 - [x] 明确标注"示意体块、非实测建筑、不得用于量测/统计/决策"(`meta.json` + README + 本票)。 - [x] 真机加载无报错、内容真的被请求(`tileset.json` + `town.glb` 都 200,瓦片 visited/selected=1)。 - [x] **远景是否达标 —— 维护者目测判定:不达标**(三项缺陷见 Answer),故本条不采用其产物。 ## 进度:100% **实现与机器验证完成 → 维护者目测否决(三项缺陷,见 Answer)→ 本条关闭,重做转票 35。** ### 做法 1. **投影**:经纬度 → 以目标点(`110.39028164, 28.4436787`)为原点的 ENU 米制坐标; glTF 是 Y-up,顶点写 `(east, up, -north)`,由 Cesium 按 `(x, -z, y)` 转回局部 Z-up。 2. **体块来源**(优先级):① OSM 真实轮廓(74 个,按外接盒起楼)→ ② 沿街排布(每条可沿街线段 两侧各按 23 m 间距放楼,退让 11–17 m)→ ③ 建成区补白(居民地面内 30 m 抖动网格,留 28% 空隙)。 3. **可复用单元**:`house`(9×7.2 m,1–2 层,坡屋顶)/ `block`(14×10 m,3–5 层,平顶)/ `slab`(26×11 m,6–8 层,平顶);层数按"离中心距离 + 位置哈希"给,配色从 13 种里按哈希取。 4. **地形落地**:先用本地 DEM 采 17×17 高程网格(`terrain-grid.json`,94–188 m), 生成时按双线性插值给每栋楼定基准高程(略埋 0.6 m 避免露缝)。 5. **避让**:中心点距水系 < 22–26 m 的体块直接不生成(沅江两岸不"长"楼)。 6. **产物**:`D:\GIS\GIS\cityMassing\{town.glb, tileset.json, meta.json}`(4.8 MB,单瓦片 13 图元)。 ### 验证证据 - **回验脚本** `tools/city-massing/verify-town.mjs` → 全部通过: - **真实轮廓落点**:窗口内最大的 **8/8** 个真实轮廓,其外接盒角点在 GLB 顶点里 **2 m 内命中** —— 这条专门证伪"经纬度 → ENU → glTF Y-up"整条链(写反任何一步都对不上); - **transform 自洽**:局部原点经 transform 回算经纬度误差 **0.000 m**;包围盒中心落在目标城区窗口内; - **GLB 结构**:header/JSON/BIN 长度自洽、13 个 POSITION accessor 的 min/max 合法、 16.9 万顶点 / 7.07 万三角、**NaN 0**; - **免责声明**在 `meta.json` 里可检出。 - **真机加载**:`stats.tilesets=1`、`tilesetErrors=[]`、零页面报错;网络里 `tileset.json` 与 `town.glb` 都是 **200**;三个视点(2400 m / 1200 m / 1800 m 侧视)下诊断均为 **visited=1, selected=1, contentReady=1**(没被视锥剔除、被选中、内容就绪)。 - 截图(供维护者判定):`C:\Users\ASUS\AppData\Local\Temp\cockpit-city-probe\` (`1-overview-2400m-pitch-55.png`、`2-mid-1200m-pitch-45.png`、`3-oblique-1800m-pitch-20.png`)。 - 生成统计:5000 栋(真实轮廓 74 / 民居 3450 / 条楼 1449 / 板楼 27,避让水系 8), 局部范围 x ±1966 m、y ±1662 m、z 99–197 m。 ### 踩到并修掉的一个真 bug **tileset 级 `geometricError` 写成 0 时 Cesium 不请求根瓦片内容** —— 现象是 `tileset.json` 拉到了、 `town.glb` 始终不下载,页面上一栋楼都没有。是靠新加的诊断读数(`getTilesetDiagnostics()`: visited/selected/contentReady/rootContentState)定位的。改成 `800` 后内容正常加载。 ### 下一步 1. **等维护者目测**:远景是否达标。达标 → 把 `map.tilesets` 配上(`.env.local` 已挂 `/gis/city`); 不达标 → 调参数(层数分布、沿街间距、配色)或换更强的数据源重做。 2. 若正式采用,需要在部署手册与项目说明里**显著标注这是示意体块**(避免被当作实测建筑使用)。