# 29: 底图影像(配置驱动) **What to build:** 让中央一张图能按部署配置挂上底图影像:不配就维持现状(不伪造配准),配了就要真出图。四种来源:`none`(默认)/ `natural-earth`(Cesium 自带离线影像,无需账号与外网)/ `tianditu`(天地图,tk 由配置给)/ `xyz`(任意 `{z}/{x}/{y}` 模板,甲方本地瓦片走这条)。 (本票来自维护者 2026-09-17 的提问:「我应该如何进行配置,获取一个基础的地图场景呢」。票 27 故意 `baseLayer: false`,票 21 覆盖地形 / 3D Tiles / 倾斜摄影与地面-管线视图切换,底图影像这一层原先无人负责,故独立成票。归属沿用票 02 → 票 27 的切法。) Blocked by: 04 Status: resolved Labels: ready-for-agent > 请勿据标签重复接单:本票已完成并有关闭记录,`Labels` 保留只是为了让面板不把它退化成 `needs-triage`。 - [x] `map.basemap.source` 支持 `none` / `natural-earth` / `tianditu` / `xyz`,默认 `none`(票 27 的「底图未配置不伪造配准」不变)。 - [x] 配置缺字段时给明确失败态(`invalid` + 浮标写明缺什么),不白屏、不静默无底图、不挂坏底图。 - [x] 底图真的挂上引擎并能取到瓦片(不是只把状态标签改个名字)。 - [x] 天地图:开发走仓库自带 `/tianditu.map` 代理、生产直连 `t{s}.tianditu.gov.cn`;tk 缺失时不发请求。 - [x] `xyz` 的 `url` / `subdomains` / `minimumLevel` / `maximumLevel` / `credit` 都能配。 - [x] 有纯逻辑断言(计划层,含模板拼装)与真机走查(含真实瓦片 200)。 - [x] 既有验收不回退:T01 走查 20/20、T04 走查 9/9、`yarn build` 通过。 ## 进度:100% **已完成并关闭(2026-09-17)。** 现在「怎么配出一个基础地图场景」有了确定答案:默认不挂底图;把 `map.basemap.source` 改成 `natural-earth` 刷新就能看到带大陆轮廓的地球(离线、无账号);有天地图 tk 就用 `tianditu`;甲方本地瓦片用 `xyz` 填模板。 ### 改动 - 新增 `domain/basemap.js`(纯函数,不 import cesium):`BASEMAP_SOURCE` / `BASEMAP_STATE` 词汇、`planBasemap(mapConfig, { dev })` 计划、`tiandituTileUrl()` 模板拼装、`basemapStatusText()` 浮标措辞。计划层独立的好处:逻辑脚本能脱浏览器把四种来源(含天地图开发代理 vs 生产子域、缺 tk 的失败态)全测到。 - `services/cockpitConfig.js`:`map.basemap` 归一化(source 白名单、url/credit 字符串、subdomains 字符串数组、级别非负整数);`map.tianditu.layer` 收 `img/vec/ter`;新增构建期键 `VITE_COCKPIT_BASEMAP_SOURCE` / `VITE_COCKPIT_BASEMAP_URL`。 - `services/cesiumEngine.js`:`createViewer(host, basemapPlan)` 按计划建 `baseLayer` —— `tms` 走 `TileMapServiceImageryProvider.fromUrl(buildModuleUrl(...))`(Cesium 自带离线影像,随 `Assets` 目录进产物),`xyz` 走 `UrlTemplateImageryProvider`;未配置或计划无效仍是 `false`(不挂底图)。 - `services/mapService.js`:引擎启动前算计划;`data-map-basemap` 与 `getLifecycleStats().basemap` 由计划决定(`not-configured` / `natural-earth` / `xyz` / `tianditu` / `invalid`);浮标就绪文案变成「Cesium 引擎已就绪 · 底图:…」,未配置时**保持票 27 原话**(T01 的 A15 断言不变)。 - `public/cockpit/config.example.json` + `README.md`:三种写法的可直接复制配方,以及生效判据。 ### 验证证据(本机 2026-09-17,`yarn dev` @ 8001) - **纯逻辑**:`node .scratch/cockpit-shell-panel-layout/verify/t29-basemap.mjs` → 通过(归一化白名单/子域/级别/图层、四来源计划、天地图开发代埋与生产子域模板、缺 tk 与未知来源的失败态、浮标措辞)。 - **真机走查**:`node .scratch/cockpit-shell-panel-layout/verify/t29-basemap-walkthrough.mjs` → **6/6**: - 默认 `none`:`data-map-basemap=not-configured`、浮标照旧「底图未配置」、**零影像请求**; - `natural-earth`:宿主标成 natural-earth、浮标显示底图名、**真取到 17 块离线瓦片(HTTP 200)**; - `xyz`:按 `{z}/{x}/{y}` 模板逐块取图(16 块 200,占位符被替换成数字;用仓库自带 TMS 目录当靶子,只证明通道通,不证明该地址语义——TMS 的 y 轴与 XYZ 相反); - `tianditu` 缺 tk:`invalid` + 浮标写明「缺少 tk…· 回退临时影像」,引擎仍就绪; - 未知来源:归一化退回 `none`,不炸也不挂坏底图。 - **截图**:`natural-earth` 下地区有真实大陆轮廓(界面同屏:顶栏「配置就绪 · 数据源 mock」标签 + 浮标「底图:内置离线影像 Natural Earth II」)。 - **回归**:`t01-panel-catalog` / `t03-camera-contract` / `t06-assets` / `t28-fill-container` / `t29-basemap` 全通过;`t01` 走查 **20/20**、`t04` 走查 **9/9**、`yarn build` 通过。 ### 边界与遗留 - **天地图真实取图未在本票跑**:需要真 tk,本票不伪造。模板拼装、开发代理/生产子域、缺 tk 失败态都有断言;真 key 联调留给维护者或票 21 的底图校验。 - 地形、3D Tiles、倾斜摄影与地面-管线视图切换仍归票 21(本票只做影像底图,不做地形)。 - 默认视角仍是「全球视角」(票 03 的相机契约不设初始预设);要落到沅陵县域需要票 21 的视角/地形一起定。 - 本票改动**未提交**(工作区里另有票 03 会话的在途改动)。