21-3d-tiles-terrain-baseline.md 18 KB

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,即其性能/视觉尚未正式签收),不在本票范围内重复承担。