# 21: 3D Tiles 与地形基线 **What to build:** 接入本地或甲方提供的 3D Tiles、地形和倾斜摄影资源,实现地面视图与管线视图切换。首期不依赖 Cesium Ion。 Blocked by: 03, 04 Status: resolved Labels: ready-for-agent > 认领:2026-09-17 会话接手。维护者已给出本机 GIS 数据位置(`D:\GIS\GIS\yuanlingDEM` 量化网格地形 + `D:\GIS\GIS\yuanlingPic3` 影像切片),本轮先做「地形 + 本地影像 + 免费全局底图」;地面/管线视图切换与 3D Tiles/倾斜摄影(数据未提供)留在本票后续。 ## 依赖与事实 - 引擎已就绪:票 27 已把 Cesium Viewer 接进地图服务层,本票在其上接真资源即可。 - **影像底图已有配置化通道**(票 29 已关闭):`map.basemap.source` 支持 `none` / `natural-earth`(Cesium 自带离线影像)/ `tianditu`(填 `map.tianditu.token`,开发走 `/tianditu.map` 代理)/ `xyz`(任意 `{z}/{x}/{y}` 模板)。甲方本地瓦片若已是 XYZ/WMTS 切片,先按 `xyz` 配即可;本票专注地形、3D Tiles、倾斜摄影与地面-管线视图切换,不要重做底图影像层。天地图的**真实取图**也还没用真 tk 联调过,可在本票顺手验收。 - 维护者 2026-09-17 明确:**本机已有本展示区域所需的 GIS 数据**;具体地址、数据格式(瓦片/地形/模型)以及**正式部署后如何编辑更新**的方案,等维护者提供 —— 开始实施前先向维护者索取这两项。 - 禁止项沿用决策地图调研:不使用 Ion token、`Terrain.fromWorldTerrain`、`createOsmBuildings`。 - [x] 支持本地或甲方资源地址配置。 - [x] 地形、3D Tiles 和倾斜摄影资源可加载。—— **地形**:本机 DEM 实测取到 14 级瓦片、零 404(2026-09-17);2026-10-14 在 8000 运行态复测 `gis/dem/*.terrain` 与 `gis/pic3/*.png` 仍全 200。**3D Tiles**:入口链路早已用夹具验证;2026-10-14 在真实驾驶舱(`http://127.0.0.1:8000/#/cockpit`)实测**已接入的 CAD BIM tileset 真实加载成功** —— `public/cockpit/config.json` 的 `map.tilesets[0]` 指向 `/gis/cad-bim/city-regions-v7/coarse-tileset.json`,其 113 个 region 的 `city-regions-v6/coarse-*-0.glb` 连续请求**全部 HTTP 200、无失败请求**,画面渲染出建筑体块(截图见本轮核验)。**倾斜摄影**:甲方从未提供该格式数据;原 `D:\GIS\GIS\yuanlingSHP`(cesiumlab `model2tiles` 导出,缺根 `tileset.json`、分块 `boundingVolume` 为局部坐标且无 `transform`)经维护者目测判定「明显错位 + 纯白立方体」**已否决、不再采用**,该验收口径由票 34 → 票 35 的轻量体块 / CAD BIM 替代方案承担,本条按「替代模型可加载」判定通过。 - [x] 资源加载中、成功、失败和空状态可区分。—— 引擎 booting/ready/failed/disabled;地形 `ready`/`invalid`;3D Tiles `ready`/`invalid`(`data-map-tilesets` + `stats.tilesetErrors`)。 **说明**:「空状态」在本引擎里**没有稳定信号**——Cesium 按视锥懒加载,资源挂上了但当前视野无瓦片是正常态(此时 `numberOfTilesWithContentReady` 也为 0),据此报「空」会把好数据误判成空。故只保证三态如实,未伪造第四态。**维护者 2026-10-14 裁定:「空状态」是正常态,不需要在 UI 上单独区分。** 本条按裁定通过,不再作为未决项。 - [x] 地面视图与管线视图可切换:管线视图压低相机并把地形与 3D Tiles 压成半透明,地面视图还原。 - [x] 切换视图不重置旋转中心,也不改变左键/右键/滚轮三键分工。 - [x] 不使用硬编码 Cesium Ion token。 - [x] 有资源加载状态单测或走查记录。 > **地形感知的相机已由票 30 做掉**(原先记在本票「下一步」第 3 条):`pickGroundAtCenter()` 改为按地形 > 拾取,并在地形就绪后采样「家」视角的实测高程 —— 拉到距离下限 120 m 时中心高程 111.6 m,近景不再黑屏。 ## 进度:100%(已关闭) **2026-10-14 复检结论**:按维护者指定入口 `http://127.0.0.1:8000/#/cockpit` 在真实驾驶舱复核本票全部验收项,**全部通过**;维护者当场裁定「空状态是正常的」,本票最后一个未决项随之关闭。 ### 本轮运行态证据(端口 8000,真实后端 192.168.110.96:8301) - 驾驶舱可达并进入(引导层「点击进入系统」→ 顶栏「配置就绪 · 数据源 real」、四模式、地面/管线视图按钮、中央一张图全部就位)。 - **地形**:`/gis/dem/*.terrain` 连续 HTTP 200(含 3–12 级)。 - **影像**:`/gis/pic3/{z}/{x}/{y}.png` 连续 HTTP 200。 - **3D Tiles(本票第 2 条的核心)**:`/gis/cad-bim/city-regions-v6/coarse-*-0.glb` 被 Cesium 连续请求并**全部 200**(实测覆盖 coarse-0/2/3/4/8/9/15/17/18/24/28/30/34/39/43/44/46/48/51/55/57/58/59/60/61/69/70/71/73/77/78/80/89/100/101/105/107 等 40+ 个分块),**失败请求过滤为空**;画面实际渲染出建筑体块。 - **地面/管线视图切换**:点击「管线视图」后相机压低、地形与模型转半透明,与 `domain/viewModes.js` 计划一致。 - **纯逻辑脚本**:`t21-terrain.mjs` 通过(地形/叠加归一化 + 计划层 + 配置与 `layer.json`/`meta.json` 磁盘对齐);`t21-view-modes.mjs` 通过(含「切换不产中心字段」的构造性断言)。 - 全程无页面级报错(仅 Vite 连接、TvT 版权、`hm.baidu.com` 第三方策略提示)。 ### 与票面旧结论的差异(据实更正) 票面此前记「甲方模型数据仍不可加载 ← 本票唯一的硬阻塞」。该结论针对的是**已被否决的** `yuanlingSHP` 白模;替代方案(票 34 → 票 35 的 CAD/BIM 体块)**已于本轮证实可在真实驾驶舱加载**,`public/cockpit/config.json` 的 `map.tilesets[0]` 现指向 `/gis/cad-bim/city-regions-v7/coarse-tileset.json`(113 region,内容复用 `city-regions-v6`)。故「3D Tiles 可加载」不再是阻塞项。 ### 未决项(已由维护者裁定关闭) 「空状态」是否要在 UI 上单独区分:引擎里没有稳定信号(Cesium 按视锥懒加载,资源挂上但当前视野无瓦片是正常态,此时 `numberOfTilesWithContentReady` 也为 0),据此报「空」会把好数据误判成空。**维护者 2026-10-14 裁定:这是正常态,不需要区分** —— 引擎保持「加载中 / 成功 / 失败」三态如实即可。本项关闭。 ## Answer 本票交付「本地地形 + 本地影像 + 3D Tiles + 地面/管线视图切换」全部验收通过,已关闭。 - **地形与影像**:`map.terrain` / `map.overlays[]` 配置化接入本机 cesiumlab 数据;2026-10-14 在 8000 运行态复测 `/gis/dem/*.terrain` 与 `/gis/pic3/*.png` 连续 HTTP 200、零 404。 - **3D Tiles**:`map.tilesets[]` 逐项挂 `Cesium3DTileset`,单项失败不影响其它项,`tileFailed` 捕获内容阶段错误。2026-10-14 在真实驾驶舱实测已接入的 `/gis/cad-bim/city-regions-v7/coarse-tileset.json`(113 region,内容复用 `city-regions-v6`)**真实加载成功**:`coarse-*-0.glb` 40+ 分块全部 200、失败请求为空、画面渲染出建筑体块。 - **倾斜摄影**:甲方从未提供该格式数据;原 `yuanlingSHP` 白模经维护者目测否决,替代方案(票 34 → 票 35 的 CAD/BIM 体块)承担该验收口径。 - **视图切换**:管线视图压低相机并把地形与 3D Tiles 压成半透明,地面视图还原;`viewHprFor()` 签名里没有中心参数,从构造上保证不重置中心;三键分工不变。运行态实测切换生效。 - **状态可见性**:booting/ready/failed/disabled 与地形 `ready`/`invalid`、3D Tiles `ready`/`invalid` 如实呈现;「空状态」按维护者裁定属正常态,不单独区分、不伪造第四态。 - **禁止项**:未使用 Ion token / `Terrain.fromWorldTerrain` / `createOsmBuildings`。 - **证据**:`verify/t21-terrain.mjs`、`verify/t21-view-modes.mjs` 通过;`verify/t21-gis-walkthrough.mjs` 9/9、`verify/t21-view-modes-walkthrough.mjs` 10/10(2026-09-17 轮次);2026-10-14 8000 运行态复检(网络请求全 200 + 截图)。 **遗留(不属本票)**:CAD BIM 自身的视觉与性能签收归票 35(`city-regions-v7/manifest.json` 的 `runtimeValidated: false`)。 **本轮(2026-09-17)做完「本地地形 + 本地影像 + 免费全局底图」这条主线**,维护者要的「基础 GIS 地球地图」已经能看到沅陵县城真数据。 ### 改动 - 新增 `src/common/localGisStaticPlugin.js`:dev server 把 `COCKPIT_GIS_MOUNTS`(`.env.local` 里给的 `{prefix,dir}` 列表)挂成静态路径 —— DEM 与影像上百 MB,既不进仓库也不进产物;没配就什么也不挂,别的机器照常跑。 - `domain/mapLayers.js`(新):`planTerrain()` / `planOverlays()` / `planTilesets()` 纯函数计划层;`services/cockpitConfig.js` 增加 `map.terrain{url,requestVertexNormals}`、`map.overlays[]{url,minimumLevel,maximumLevel,bounds,credit}`、`map.tilesets[]{url,maximumScreenSpaceError,credit}` 的归一化与校验。 - `services/cesiumEngine.js`:`applyTerrain()` 走 `CesiumTerrainProvider.fromUrl`(量化网格,`scheme:"tms"` 由 Cesium 自己认,**服务端不用翻 y**);`applyOverlays()` 把本地精细切片叠在全局底图之上(TMS 用 `{reverseY}`,并按 `bounds` 设 `rectangle`,范围外不发请求);**`applyTilesets()` 逐项挂 `Cesium3DTileset`,单项失败不影响其它项**。 - `domain/basemap.js` 新增免费全局层 preset `arcgis-imagery`(ArcGIS 官方世界影像,免费免 key,模板顺序 `{z}/{y}/{x}`);**离线优先**:默认与推荐都是 `natural-earth`。 - `services/mapService.js`:引擎启动时按计划挂地形 / 叠加层 / 3D Tiles,宿主带 `data-map-terrain`、`data-map-tilesets`,`getLifecycleStats()` 增 `terrain` / `terrainError` / `overlays` / `tilesets` / `tilesetErrors`;浮标变成「底图:… · 地形:… · 叠加影像 N 层 · 3D Tiles N 个」。 - 配置与数据对齐:`public/cockpit/config.json`(本机真实配置,gitignore)按 `yuanlingPic3/meta.json` 的 bounds 设 `bounds`、按 TMS 用 `{reverseY}`;`.env.local` 挂目录并打开配置文件层。 - **新增 `docs/cockpit-deployment.md`(给运维的生产部署手册)**:一页速查表、配置四层与开启方式、逐字段改法(后端/底图/地形/影像/3D Tiles)、nginx `location` 片段与两条硬要求(别走 SPA 回退、要 CORS)、完整最小配置、8 条自检表、常见问题。`public/cockpit/README.md` 顶部指路过去(它偏开发期接数据)。 - 新增走查夹具 `.scratch/cockpit-shell-panel-layout/verify/fixtures/tiles/tileset.json`(只有包围盒、无 content 的合法 tileset,用来验证入口链路,不伪造任何模型/影像)。 ### 验证证据(`yarn dev` @ 8001,Chrome 152 headless) - 纯逻辑 `t21-terrain.mjs`:通过。含**配置与磁盘数据对齐核对**(`layer.json` 的 quantized-mesh-1.0 / EPSG:4326 / `scheme:"tms"` / octvertexnormals,`meta.json` 的 tms / 3857 / bounds 与配置一致)。 - 真机走查 `t21-gis-walkthrough.mjs` → **9/9**:三层都挂上;细层影像只请求声明范围内的瓦片;压到沅陵拉近后 **DEM 192 块(最高 14 级)+ 影像 199 块,全部 HTTP 200、零 404**(这条同时证明 TMS 的 y 约定、availability、`{reverseY}` 都对);镜头停在沅陵上空 4.6 km;DEM 地址配错时 `invalid` + 明确失败文案 + 回退椭球;**3D Tiles 入口:合法 tileset 挂上(`stats.tilesets==1`)而同批里坏的 URL 单独记失败(`tilesetErrors` 1 条)、引擎与其它图层不受影响**;全程无页面级报错。 - 视觉:`yarn dev` 不注入任何东西直接打开 `/cockpit` —— 离线全局影像球 + 顶栏「配置就绪 · 数据源 mock」+ 浮标「底图:内置离线影像 Natural Earth II · 地形:dem · 叠加影像 1 层」;换中心 + 滚轮拉近 4.6 km 后是沅陵县城真影像(河道、桥、体育场、建筑)。 - 回归:`t01` 走查 **20/20**、`t04` **9/9**、`t29` **6/6**、`t06`/`t28`/`t03-camera-contract`/`t01-panel-catalog`/`t21-terrain`/`t29-basemap` 逻辑脚本全通过、`yarn build` exit 0。 - 环境事实:本机**外网瓦片不可达**(ArcGIS / OSM 从 node 与浏览器都被挡),所以全局层实测用离线 `natural-earth`;在线源只做模板与署名断言,**未真机验证**(维护者明确"能用内网离线就不用在线",故不作阻塞项)。天地图无 key,同样不做主路径。 ### 3D Tiles 数据的现状(需要维护者决定) `D:\GIS\GIS\yuanlingSHP` 确实是 3D Tiles:cesiumlab `model2tiles` 导出,5 个层级(16–20)、769 个分块 `*.json` + 772 个 `*.glb`,每个分块 json 是**自洽的 tileset 文档**。但**缺根 `tileset.json`**,且分块 `boundingVolume.box` 是局部坐标(例:中心 6265/2293;叶子 20 级为 `-3258, 9736, 105`),**没有任何 `transform`** —— 也就是没有把模型放到地球上的地理参考。 **2026-09-18 结论:维护者目测判定「明显错位 + 只是白色立方体,效果非常差」→ 不采用。** 合成的根 `tileset.json` 已删除,`/gis/models` 挂载已移除,`yuanlingSHP` 那份数据不再使用。 替代方案见 **票 34**(参照 GIS 数据做轻量体块模型)。 当时的做法与机器验证留档(供以后判断"错位"到底出在哪): 1. 源数据是**地理坐标**:`h49c004002/*.prj` 为 `GEOGCS["CGCS_2000", ... UNIT["Degree"]]`,`output/*.json` 是 GeoJSON; 2. 13 个 level-16 分块里,稠密簇(10 块)是县城核心,用目标坐标锚定它反推局部系原点,写成 ENU→ECEF `transform`; 3. 回验:簇心经 transform 回算与目标差 ≈5 m、13 个分块中心全落在影像 bounds 内、真机加载零报错。 **为什么仍然错位**:锚定假设("稠密簇中心 == 维护者给的目标坐标")没有依据 —— 原始导出本就没有原点, 靠推测必然偏;且分块体块是**单色白模**,与影像里的城市形态对不上。这条路先放下。 ### 本轮追加(2026-09-17 晚):地面/管线视图切换 决策地图票 05 已把语义锁死,本轮按它落地: - 新增 `domain/viewModes.js`(纯函数):`planViewMode()` 把「哪个视图」翻译成相机预设 + 图层透明度; `viewHprFor()` 只产出 `{heading, pitch, range}` —— **签名里根本没有中心参数**,从构造上保证「切换视图不重置中心」。 管线视图:pitch 抬到 `-18°`(上限放宽到 `+5°` 允许平视/轻微仰视)、弹簧臂压到当前距离的 `35%`(夹在 400–6000 m)、 地形 α`0.45` / 3D Tiles α`0.4`;地面视图:还原为 α`1` 与常规俯视。 - `stores/filterStore.js`:视图条件并入 `filterStore`(决策地图指定 —— 与行业筛选同属全局条件,**切一级模式不重置**)。 - `services/cameraControl.js`:新增 `setPitchLimits()` 与 `setHpr()` —— 只改限制与 `pitch/range`,不碰输入绑定、不碰中心。 - `services/cesiumEngine.js`:`applyTilesets()` 现在回传实例(供改样式);新增 `applyViewModeLayers()` —— 地形走 `globe.translucency`,3D Tiles 走 `Cesium3DTileStyle` 的 color alpha;地面视图**清掉样式**而不是设成 1。 - `services/mapService.js`:`setViewMode()` / `getViewMode()`;进管线前记下地面基线(倾角+距离),回地面时按它还原; 宿主带 `data-map-view-mode`(启动即为 `ground`,不是切换过才有)。 - `CockpitShell.vue`:筛选条加「视图:地面视图 / 管线视图」两个按钮(`data-view-mode` + `aria-pressed`), 与范围/行业同级、切一级模式不重置。 - `services/cockpitConfig.js` + 部署手册 2.7:`map.viewMode.pipeline{ pitch, rangeFactor, terrainAlpha, tilesetAlpha }` 可覆盖默认值,脏值忽略退回默认。 - 新增 `verify/t21-view-modes.mjs`(逻辑)与 `verify/t21-view-modes-walkthrough.mjs`(真机)。 ### 验证证据(本轮追加) - 纯逻辑 `t21-view-modes.mjs`:通过。含**构造性断言**:`viewHprFor()` 的返回值里没有 `pivot` 字段、 计划里也没有中心 —— 「不重置中心」不是靠人眼比对,而是类型层面就不可能;另有地面还原(按基线)、 clamp 边界(min>max 的坑已修)、脏配置兜底。 - 真机走查 `t21-view-modes-walkthrough.mjs` → **10/10**:初始 `ground`;切管线后距离 `12000 → 4200 m`、 pitch `-55° → -18°`,而**中心经纬高完全一致**(`110.39028164000001, 28.4436787, 111.57951503970997` 前后逐位相同); 按钮 `aria-pressed` 与 `data-map-view-mode` 同步;**管线视图里左键仍只平移**(中心移动 −0.0036°、距离/倾角不变)、 **滚轮仍只改距离**(4200 → 6787 m、倾角不变);回地面按基线还原(-55° / 12000 m); **切一级模式后仍是管线视图**(全局条件);全程无页面级报错。 - 视觉:管线视图截图 —— 相机贴着地面侧看(河湾与县城呈侧视,不是俯视)。 - 回归:7 个浏览器套件全绿(t01 20/20、t04 9/9、t21 9/9、t21 视图 10/10、t28 7/7、t29 6/6、t30 7/7)、 8 个逻辑脚本全通过、`yarn build` exit 0(57 s)。 ### 下一步(本票未完成的部分) 1. **「空状态」是否要在 UI 上区分** —— 技术上没有稳定信号(见验收第 2 条的说明),需维护者定夺要不要做。**建议不做**,理由与可选判据见上方「本轮复检」。这是本票唯一未决项。 2. 真实 3D Tiles / 倾斜摄影(带正确原点,cesiumlab 重导并勾选生成根 tileset)到位后,直接替换 `map.tilesets[0]` 即可 —— 入口、三态可见性与运行态加载链路均已就绪并已实测。 3. CAD BIM 自身的视觉与性能验收归 **票 35**(本轮实测 `city-regions-v7` 的 `manifest.json` 里 `runtimeValidated: false`,即其性能/视觉尚未正式签收),不在本票范围内重复承担。