面向部署人员(运维/实施)。只改配置与静态目录,不需要重新构建前端。 阅读顺序:先看「一页速查」,按第 2 节改配置,按第 3 节挂数据,最后跑第 5 节自检。
| 我要做的事 | 改哪里 | 怎么改 |
|---|---|---|
| 打开/关闭运行时配置 | 产物 index.html |
加/删一行 window.__COCKPIT_CONFIG_URL__ = 'auto' |
| 改后端接口地址 | dist/cockpit/config.json → api.baseUrl |
填网关地址,如 /api/cockpit/v1 |
| 改数据源(mock / 真接口) | 同上 → dataSource |
"mock"(默认)或 "real" |
| 配置真接口凭据(已废弃) | — | 2026-09-28 起免鉴权:驾驶舱 REST 与 /ws/deviceStatus 均不要求凭据,api.token/ws.token 留空即可(旧配置写了也会被前端忽略);安全性由网络层内网隔离保障 |
| 换全局底图 | 同上 → map.basemap.source |
默认 natural-earth(离线);可选 xyz / tianditu / arcgis-imagery |
| 挂地形 DEM | 同上 → map.terrain.url |
指向含 layer.json 的目录 URL |
| 挂本地精细影像 | 同上 → map.overlays[] |
url(TMS 用 {reverseY})+ bounds |
| 挂 3D Tiles / 倾斜摄影 | 同上 → map.tilesets[] |
每项一个 tileset.json 的 URL |
| 挂 GIS 静态目录(nginx) | 服务器 nginx | 见第 3 节的两段 location |
| 改「目标城市」与归位视角 | dist/cockpit/config.json → map.home |
见 2.6 |
| 开关进入引导(自转星球 + 点击进入) | 同上 → map.intro.enabled |
见 2.6 |
| 地面/管线视图的倾角与半透明 | 同上 → map.viewMode |
见 2.7(不配则用默认:管线 pitch -18°、距离 ×0.35、地形 α0.45、模型 α0.4) |
| 框定主场景区(区域限位 + 自建雾气) | 同上 → map.primaryArea.bounds |
见 2.9;不填就等于关掉这两个能力 |
| 主场景区限位的运行表现 | — | 见 2.10(进区后最远=map.home.range、拖到边界软回弹、返回全球) |
| 区内视距雾(色/距离/进出时长) | 同上 → map.fog |
见 2.11(默认 18/42 km、#bdcdd6;不配也是这一套) |
| 粗→精影像交接(渐显时机/时长) | 同上 → map.handover |
见 2.12(默认等雾保护 + 瓦片就绪才渐显,700 ms;不配也是这一套) |
| 现场调试生产包(显示诊断信息) | 同上 → map.ui.debug |
true 临时打开;false 强制干净形态(默认跟随构建) |
| 完全关掉三维引擎 | 同上 → map.cesium.enabled |
false → 中央区域退回临时影像 |
三个文件的分工(先记住这句,后面就不会乱):
dist/cockpit/config.json —— 运行时配置,部署后改这个(环境地址、数据源、图层)。index.html —— 加一行开关,告诉前端「去读上面那个文件」。前端按四层取值,后者覆盖前者(合并是深合并,只写要改的字段即可):
| 层 | 来源 | 谁改 | 说明 |
|---|---|---|---|
| 1 | 内置默认 | 前端 | dataSource: mock、basemap: natural-earth、不挂地形/影像/3D Tiles |
| 2 | 构建期 VITE_COCKPIT_* |
前端构建者 | 打进产物,换环境要重建,不推荐生产用 |
| 3 | <base>cockpit/config.json |
部署人员 | 本次部署的主入口,改完刷新页面即生效 |
| 4 | 宿主注入 window.__COCKPIT_CONFIG__ |
集成方(qiankun 主应用等) | 整份对象注入,优先级最高;给了就不再读第 3 层 |
打开第 3 层:默认前端不探测配置文件(免得每页刷 404)。二选一:
部署后改产物(推荐,无需重建):编辑 dist/index.html,在 <head> 里加一行 ——
<script>window.__COCKPIT_CONFIG_URL__ = 'auto';</script>
'auto' = 取应用基址下的 cockpit/config.json(publicPath: './' 与 qiankun 子路径下都对)。
也可以给绝对 URL(例如配置由后端下发):window.__COCKPIT_CONFIG_URL__ = 'https://cfg.example.com/cockpit.json';
构建期:.env 里写 VITE_COCKPIT_CONFIG_URL=auto 再 yarn build。
顶栏能看到状态:.cockpit-shell__config 标签显示 配置就绪 / 配置缺失 / 配置载入失败 与数据源;
引擎浮标显示 底图:… · 地形:… · 叠加影像 N 层。排查问题时先看这两个地方。
dist/cockpit/config.json模板见仓库 public/cockpit/config.example.json(带逐字段注释)。只需写要覆盖的字段:
{
"dataSource": "real", // 真接口;默认 mock
"api": { "baseUrl": "/api/cockpit/v1" }, // 网关地址;同源可写相对路径
"ws": { "url": "wss://内网主机/ws/deviceStatus" }
}
api.token / ws.token 已废弃(2026-09-28 免鉴权):按 ADR 0005 修订,驾驶舱 REST 与
/ws/deviceStatus 均不再要求凭据;接口安全由网络层内网隔离保障(网关与可直连的应用端口
都须只在内网可达)。旧配置里写了 token 也会被前端忽略。若个别分区出现 401/403(例如网关层
另行加了策略),仍只把受影响分区标「数据不可用」,不会跳登录页。"dataSource": "mock",无需改其它字段。"map": { "basemap": { "source": "natural-earth" } } // 推荐:Cesium 自带离线影像,零外网
| 取值 | 是否离线 | 需要什么 |
|---|---|---|
natural-earth |
✅ 完全离线 | 无(影像随 dist/cesium/Assets 一起部署) |
xyz |
✅ 可离线 | url 模板,如 /gis/world/{z}/{x}/{y}.png |
tianditu |
❌ 在线 | map.tianditu.token(天地图 tk),开发走 /tianditu.map 代理 |
arcgis-imagery |
❌ 在线 | 无(免 key,Esri 署名),但需要外网 |
none |
— | 不配底图,中央区域显示临时影像 |
内网环境请用
natural-earth或xyz。本项目优先本地/内网离线数据。
"map": { "terrain": { "url": "/gis/dem", "requestVertexNormals": true } }
url 指向含 layer.json 的目录(量化网格 quantized-mesh)。layer.json 里 scheme: "tms" —— Cesium 自己会认,服务端不要翻 y。地形加载失败(…)· 回退椭球,页面照常(不白屏)。"map": {
"overlays": [{
"url": "/gis/pic3/{z}/{x}/{reverseY}.png",
"minimumLevel": 0, "maximumLevel": 19,
"bounds": [109.96215820312366, 27.962869019358852, 111.04568213224277, 29.060971407383555],
"credit": "沅陵本地影像"
}]
}
{reverseY}(cesiumlab 导出的 meta.json 里 tiletrans: "tms");
若你的切片是标准 XYZ(slippy map),则写 {y}。bounds = [西, 南, 东, 北],抄 meta.json 的 bounds。填了它,范围外不会发请求(否则整球视角刷一屏 404)。"map": { "tilesets": [{ "url": "/gis/model/tileset.json", "lighting": true }] }
lighting:true 是显式模型图层开关:Cesium 为该 tileset 根据其地理包围球配置固定局部日光与环境光,不跟随相机改变;省略时不修改现有场景光照。运行时 LOD 必须由根 tileset 的空间子节点、geometricError 与 refine: "REPLACE" 提供,单个 GLB 旁边放一个 LOD1 文件不会自动切换。范围较大的程序化街区可以使用“粗父节点 LOD1 → 多个细叶子 LOD0”的两级空间树,让远景只请求粗父节点、近景再替换为局部叶子;根节点应保留有效的空 GLB 锚点,避免 Cesium 跳过无内容根节点。
tileset.json 的 URL(根 tileset,不是分块 json),可选 maximumScreenSpaceError。3D Tiles 加载失败(…);内容阶段错误由 tileFailed 事件捕获,不能只用根 fromUrl() 成功判断。geometricError 应按数据层级和实际误差设置。旧版把一次未加载现象直接归因于数值0,未隔离相机/视锥等变量,不能作为格式硬约束。globe.depthTestAgainstTerrain。本轮已修复默认值;地面与管线半透明来回切换由像素回归验证,不能用“模型已加载”替代遮挡验收。D:\GIS\GIS\cityMassing\ 保留作历史资料,map.tilesets 留空。新 Blender 资产库与示范街区在 tools/city-massing/house-library/ 和 tools/city-massing/district/generated/,仅通过本机 serve-review.mjs 提供效果审查,不进入生产包,也未获得完整县城验收。
所有立面/楼层为估计,影像描绘存在视差;非测绘建筑,不得用于量测/统计/应急决策。复现、资产与覆盖清单见 tools/city-massing/README.md。D:\GIS\GIS\yuanlingSHP(cesiumlab model2tiles 导出)缺根
tileset.json、分块是局部坐标;曾补过一版合成根文件,经目测错位且是白模,不采用
(合成文件已删除)。若日后用 cesiumlab 重导并勾选生成根 tileset,直接填 map.tilesets 即可替换示意模型。进系统时的落点、以及顶栏「归位」按钮回到的视角,都由 map.home 决定;引导层由 map.intro 控制。
"map": {
"home": {
"lon": 110.39028164, // 目标点经度(CGCS2000/天地图口径,与 WGS-84 本项目视为一致)
"lat": 28.4436787, // 目标点纬度
"heading": 0, // 朝向(0 = 正北)
"pitch": -55, // 俯角(负 = 从上往下看)
"range": 12000, // 弹簧臂距离(米):12000 ≈ 县域级;4600 ≈ 街区级
"label": "沅陵县城" // 归位按钮的提示文案
},
"intro": {
"enabled": true, // false = 跳过引导,打开即进系统(不需要引导的部署设 false)
"title": "点击进入系统",
"spinSeconds": 48 // 星球自转一圈的秒数;0 = 不自转
}
}
要点:
home.lon/lat 换成别的城市即可;range 决定"进入时看多细",别小于 2000(再近看不到县城轮廓)。intro.enabled=false 时不显示引导层,但「归位」按钮仍在、home 仍然生效。驾驶舱顶栏筛选条里有「地面视图 / 管线视图」两个按钮(全局条件,切一级模式不重置)。 管线视图会压低相机 + 放宽俯仰,并把地形与 3D Tiles 压成半透明,以便露出管线;地面视图还原。 旋转中心不重置、三键操作完全不变(左键平移、右键旋转、滚轮伸缩)。
默认值一般够用;要调就写 map.viewMode:
"map": {
"viewMode": {
"pipeline": {
"pitch": -18, // 管线视图倾角(度,越接近 0 越平视;允许到 +5)
"rangeFactor": 0.35, // 弹簧臂压到当前距离的几成
"terrainAlpha": 0.45, // 地形透明度(0–1;1 = 不透明,0 = 全透)
"tilesetAlpha": 0.4 // 3D Tiles / 倾斜摄影透明度
}
}
}
写错类型/范围外的值会被忽略并退回默认(不会把相机顶到奇怪角度)。
驾驶舱有两套界面形态,生产构建默认就是干净形态,不需要配置:
| 调试形态(开发构建默认) | 生产干净形态 | |
|---|---|---|
| 中央图上的「中央一张图」标题、图层占位、实例提示 | 显示 | 收起 |
| 引擎状态(底图/地形/影像/3D Tiles) | 顶部药丸,带边框底色 | 贴底半透明小字(无边框无底色) |
| 顶栏「配置就绪 · 数据源」标签、「当前模式」读数 | 显示 | 收起 |
| 点位状态图例(颜色语义)、状态灯 | 显示 | 保留 |
| 范围/行业/视图筛选、归位按钮、只读视图、时钟 | 显示 | 保留 |
判定规则:map.ui.debug 显式给布尔值就听它,否则跟随构建模式(yarn dev → 调试;yarn build 产物 → 干净)。
"map": {
"ui": {
"debug": false // 想在生产包里临时看诊断信息就写 true;不写则跟随构建模式
}
}
故障时颜色语义仍在:引擎失败时那行小字会转成淡红(生产形态没有边框,靠字色传达)。
map.primaryArea.bounds 框出主场景区:进入后相机被限位在区内、远景罩上自建距离雾。
两个能力(限位、雾)都以这一个字段为开关 —— 不填 = 两个能力一起关闭,不是"用了某个默认范围"。
"map": {
"primaryArea": {
"bounds": [110.328749941417, 28.3917230659234, 110.465779226663, 28.4913038992907]
}
}
顺序是硬要求:[西, 南, 东, 北](度),与 map.overlays[].bounds 完全同口径,
必须满足 west < east 且 south < north。
[W, N, E, S](纬度降序)给范围 —— 请在写配置时就把两个纬度对调,
运行时代码不会替你翻转。data-map-primary-area-reason 上写明原因(见下)。矩形怎么定:优先按甲方框定的范围。若要以数据覆盖为准,由部署侧离线算
影像 bounds ∩ DEM layer.json 的 valid_bounds,围绕 map.home 经纬度、东西/南北各折半后写入本字段
(不要指望前端在运行时推导,也不要在源码里写死某个县的地理常量)。
怎么确认生效:引擎浮标(调试形态在中央顶部,生产形态在右下角小字)会出现
| 情况 | 浮标文字 | 宿主读数 |
|---|---|---|
| 已配置且顺序正确 | 主场景区:13.4×11.1 km |
data-map-primary-area="ready" |
| 没写这个字段 | 主场景区:未配置 · 限位与雾气关闭 |
data-map-primary-area="missing" |
| 顺序写反 | 主场景区:配置无效(顺序应为 [西,南,东,北],不自动翻转) · 限位与雾气关闭 |
data-map-primary-area="invalid",原因在 data-map-primary-area-reason |
| 数值个数/类型不对 | 主场景区:配置无效(bounds 应为四个有限数字) · 限位与雾气关闭 |
同上(invalid) |
未配置 与 配置无效 都会让「进区锁定 / 相机区域限位」(票 37)与「区内视距雾气」(票 38)关闭;
配置问题同时会记进配置服务的 issues 清单,便于排查。
配好主场景区之后,进区即被限位:最远视野=归位默认视角(map.home.range,本部署 12,000 m),
中心点约束在框内,相机不钻地形。运行期表现为:
| 位置 | 顶栏 | 相机 | 宿主读数 |
|---|---|---|---|
| 区外(自由) | 只有一个「归位/进入」 | 可拉到全球尺度(上限 3×10⁷ m) | data-map-area-state="global" |
| 区内(锁定) | 「归位」+「返回全球」 | 滚轮最远只到 map.home.range;中心被夹在框内 |
data-map-area-state="locked"、data-map-range-max="12000" |
map.intro.enabled=false 时按自由全球视角启动、不发生这件事。data-map-area-state 是否真是 locked(不是 locked 就说明引导
或「归位」没走通,或主场景区配置不可用);data-map-range-max 与 map.home.range 应一致。进入主场景区后自动加一层距离雾:近处地物清晰、远处平滑融入雾色,旋转镜头时不露出远景瑕疵。 只在区内生效,全球/引导态关闭。默认参数就是下面这组,不配也是它:
"map": {
"fog": {
"enabled": true,
"startDistance": 18000, // 世界射线距离:从这里开始起雾(米)
"endDistance": 42000, // 到这里完全遮蔽(米);必须大于 startDistance
"color": "#bdcdd6", // 基准雾色(必须是 #rrggbb)
"fadeInMs": 900, // 进区渐显时长(本部署现场调校值)
"fadeOutMs": 300, // 返回全球淡出时长(本部署现场调校值;要更利落就调小)
"altitudeFullM": 14000, // 相机高度 ≤ 此值时雾满强度
"altitudeZeroM": 3000000, // 相机高度 ≥ 此值时雾收到 0
"altitudeSlewMs": 600 // 上面这层收口的**时间限速**:系数走完全程至少这么久
}
}
上面这四个值(
fadeInMs/fadeOutMs/altitudeFullM/altitudeZeroM/altitudeSlewMs) 是本部署(沅陵)在真机上调定的,不是规格默认。代码默认另有一套 (domain/fog.js的FOG_DEFAULTS:800/400 ms、20/42 km、400 ms)。 换部署若没有自己的现场值,删掉map.fog里这几项就会退回代码默认。
要点:
#bdcdd6 是起点不是终值 —— 正式色值请在真实画面上复核后调。
写非 #rrggbb 的值会被判非法并退回默认(同时记一条配置问题,不会静默生效失败)。altitudeFullM / altitudeZeroM):雾的距离端点只有 18–42 km,
而相机在高空时画面上每个片元都远在 42 km 之外 —— 不加收口就会整个地球被涂成雾色。
这两个值一压,退出时不再出现"地球变白一段时间"、进入时也不再有"白→突然一团绿"。
代码默认取 20/42 km 有依据:42 km 是雾自己的全遮蔽端点(高过它雾已无保护意义),
20 km 以下恢复满强度,而区内归位视角约 9.8 km 远在下方 → 区内观感完全不受影响。
必须 altitudeZeroM > altitudeFullM:写反会退化为"高度系数恒为 1"(等于把这层保护关掉)。
配置层会报一条 issue(不静默),但不会替你改值 —— 两个数各自都合法,
"要不要在高空保留雾"是部署的取舍(本部署有意把上端放到 3000 km,使飞入全程都有雾)。altitudeSlewMs 是必需的,不是可选的润色:返回全球时相机 range 从 12 km 变到
20,000 km(跨 1.6e6 倍),而雾带只有 20–42 km —— 相机头一帧就越过整条带。
所以"瞬时按高度决定雾浓淡"必然跳变,观感是「雾几乎一瞬间就产生或消失」。
这一项给高度系数加了时间限速:无论相机多快,画面上的雾都不可能比它更快地变化。
写成 0 = 不限速(回到会跳变的旧行为),一般不要这么配。endDistance 必须大于 startDistance,否则两端语义不成立,配置层会退回默认并记问题。map.handover)。怎么确认生效:顶栏出现「视距雾 1.00 · 18–42 km」这样的读数(进区后才显示),
宿主读数在 .map-canvas 上:
| 情况 | 读数 | 顶栏文案 |
|---|---|---|
| 区内正常 | data-map-fog="ready"、data-fog-phase="locked"、data-fog-strength="1" |
视距雾 1.00 · 18–42 km |
| 区外待命 | data-fog-phase="global"、强度 0 |
视距雾待命 · 区内生效 |
| 管线视图观察到透明错位(减弱) | data-fog-degrade="reduced"、强度被压低 |
视距雾减弱 · 强度 0.35 |
| 不支持深度 / 运行失败 | data-map-fog="unsupported" |
视距雾不可用 · 无深度支持 |
ready不等于「矩形边缘已被遮住」。 它只说明后处理级可执行; 画面是否真的被遮住属观感验收,读数里data-map-fog-covers-edges恒为no, 前端不提供"已遮缝"这类未经证实的承诺。
降级与可读性:如果管线视图下透明对象被错误浓雾盖住,可读性优先 —— 可在控制台
(DEV)__cockpitMap.setFogTranslucentArtifacts(true) 减弱,或 setFogUserDisabled(true) 暂关;
false 恢复。降级不影响相机区域限位(票 37 的限位照旧生效)。
正常情况不会预先关闭管线视图的雾。
性能:规格要求同机同视角下开启雾的 P95 帧耗时增幅 ≤15%。
DEV 下可采样:__cockpitMap.startFogPerfSample('with-fog') → 等一会儿 →
__cockpitMap.stopFogPerfSample() 读 P95。注意它采的是渲染事件间隔,不是 GPU 时间,
只能作参考,不能单独判定达标。
从全球飞进县城的全过程不出现肉眼可辨的矩形色块跳变。做法是把精细叠加影像的显隐 交给一套交接时序:全球/引导态隐藏精细层、只留粗底图;进区后等雾先形成保护、 且目标视野瓦片可显示,才渐显精细层;返回时反向交接。
"map": {
"handover": {
"enabled": true, // false = 精细层常显(回到票 39 之前的行为)
"fadeMs": 500, // 渐显/收起时长(本部署现场调校值;见下方"为什么调这么短")
"minEdgeOcclusion": 0.7, // 相机到影像边处,雾的遮蔽率须 ≥ 此值才允许揭示(0–1)
"requireTilesLoaded": false// 揭示前是否要求瓦片加载队列为空
}
}
这三个值也是本部署真机调定的(不是规格默认)。代码默认是
domain/imageryHandover.js的HANDOVER_DEFAULTS:380 ms / 0.9 /true。 换部署若没有自己的现场值,删掉map.handover就退回默认。
要点:
fadeMs 要调短("进入时会变绿"的修复):雾是距离雾,18 km 起步;
而区内相机只有约 9.8 km 高 —— 屏幕中心(脚下街区)的视线距离恒 < 18 km,
那里恒无雾,雾在结构上盖不住它。所以渐显期间,中心是
「粗糙 Natural Earth II(陆地偏绿)↔ 精细影像」的混合,窗口越长那段绿越明显。
调短它 = 让混合一闪而过(维护者 2026-09-29 选定此方向)。
改成硬切(fadeMs: 0)也能去掉绿,但会丢掉"粗→精"的过渡感,不推荐。minEdgeOcclusion 调小 = 更早揭示、但遮边更弱。它同时是"相机离矩形边太近就不揭示"
的门槛:值越小,允许在雾更薄的地方揭示。本部署取 0.7(默认 0.9);
现网参数下区内最坏点(西南角,距影像边 35.9 km)遮蔽率约 0.97,两种阈值都能过。requireTilesLoaded: false 的含义与代价:不再等"瓦片加载队列为空"就允许揭示。
决策 08 当初要求这一条是为了避免"先空白再跳方块";关掉后牺牲的正是这个保障
(globe.tilesLoaded 本来也只是代理信号,见下)。本部署接受该代价 ——
若现场看到揭示后才有方块补上,把它改回 true 即可。alpha = 0 且保持 show = true
—— 若改成 show = false 会真的停掉预取,等揭示时才开始下载,反而造成"先空白再跳方块"。
可在 DEV 用 __cockpitMap.getHandoverState().layerProbe 核对:应为 [{show: true, alpha: 0}]。minEdgeOcclusion 用的是同一条雾衰减曲线,不是另写一套阈值。
所以调 map.fog.startDistance/endDistance 时,边缘判据自动跟着变,不会各调各的。
现网参数下,相机停在家(县城中心)时到影像最近边约 42 km、遮蔽率 1.0;区内四角最坏约 0.97,都能过闸。enabled: false 关掉时,精细层常显(把 alpha 还到 1),不会停在隐形状态。怎么确认生效:宿主读数在 .map-canvas 上(data-map-handover*):
| 情况 | 读数 |
|---|---|
| 全球/引导态(精细层隐藏) | data-map-handover="global"、data-map-handover-alpha="0" |
| 进区等待保护 | data-map-handover-block="fog" 或 "tiles",alpha 仍为 0 |
| 已揭示 | data-map-handover="revealed"、data-map-handover-alpha="1" |
| 瓦片加载失败(保持粗底图) | data-map-handover-block="tilesFailed" |
| 相机贴近影像边(保护不成立) | data-map-handover-block="edge" |
| 无雾降级(已揭示但不承诺遮边) | data-map-handover-degrade="yes" |
同样不承诺"边缘已被遮住":
data-map-handover-tiles-proxy="yes"明确标注 瓦片信号(globe.tilesLoaded)只是加载队列为空的代理,不证明目标视野逐像素就绪。 「全程无肉眼可辨的方形跳变」属观感验收,请在真实画面上确认。已知边界:矩形内部随地形 LOD 的逐级换挡不由整层 alpha 修复 —— 若在真实画面上仍可辨,属票 40 评估局部过渡手段的范围,本层不声称已解决。
性能:与雾同一预算(合计 P95 增幅 ≤15%)。交接读数在渲染循环里带 diff 缓存, 稳态帧不重写 DOM;进区/返回是离散动作,会立刻定相位(不被稳态节流吞掉)。
DEM / 影像 / 3D Tiles 都是静态文件,直接从磁盘服务,不要放进前端产物(上百 MB 起)。
# 地形 DEM(里面必须有 layer.json)
location /gis/dem/ {
alias /data/gis/yuanlingDEM/;
add_header Cache-Control "public, max-age=86400";
types { application/octet-stream terrain; }
default_type application/octet-stream;
}
# 本地影像切片
location /gis/pic3/ {
alias /data/gis/yuanlingPic3/;
add_header Cache-Control "public, max-age=86400";
}
# 3D Tiles(若启用)
location /gis/model/ {
alias /data/gis/model/;
add_header Cache-Control "public, max-age=86400";
types { model/gltf-binary glb; application/json json; }
}
两条硬要求:
.terrain / .png / .glb / layer.json 走 SPA 回退。回退成 index.html 时,
Cesium 会报 Unexpected token '<'(地形)或整片瓦片加载失败。上面的 location 前缀即是隔离手段。允许跨域(前端与数据不同源时):
add_header Access-Control-Allow-Origin "*";
目录搬走/换机器:只改 config.json 里对应的 url,不用改前端、不用重建。
{
"dataSource": "mock",
"map": {
"basemap": { "source": "natural-earth" },
"home": { "lon": 110.39028164, "lat": 28.4436787, "heading": 0, "pitch": -55, "range": 12000, "label": "沅陵县城" },
"intro": { "enabled": true, "title": "点击进入系统", "spinSeconds": 48 },
"terrain": { "url": "/gis/dem", "requestVertexNormals": true },
"overlays": [{
"url": "/gis/pic3/{z}/{x}/{reverseY}.png",
"minimumLevel": 0,
"maximumLevel": 19,
"bounds": [109.96215820312366, 27.962869019358852, 111.04568213224277, 29.060971407383555]
}],
"primaryArea": {
"bounds": [110.328749941417, 28.3917230659234, 110.465779226663, 28.4913038992907]
},
"tilesets": []
}
}
| 检查 | 期望现象 | 不通过时看哪里 |
|---|---|---|
| 页面能打开 | /#/cockpit 出现驾驶舱外壳,无白屏 |
控制台报错、index.html 的配置开关 |
| 进入引导 | 先看到自转星球 + 闪烁「点击进入系统」 | map.intro.enabled |
| 进入落点 | 点一下后镜头落在目标城市(不是随机位置) | map.home.lon/lat/range |
| 归位按钮 | 顶栏「归位」可点,平移后点它回到目标视角 | map.home、引擎是否就绪 |
| 配置生效 | 顶栏标签「配置就绪 · 数据源 mock/real」 | config.json 路径、__COCKPIT_CONFIG_URL__ |
| 全局底图 | 地球有影像(不是黑球) | map.basemap.source |
| 地形 | 浮标含「地形:dem」;拉近后有山体起伏 | nginx /gis/dem/、layer.json 能直接下载 |
| 本地影像 | 压到目标区域能看到精细影像 | overlay 的 url/{reverseY}/bounds |
| 主场景区 | 浮标含「主场景区:N×M km」;没配或顺序错则写明「限位与雾气关闭」 | map.primaryArea.bounds(顺序 [西,南,东,北]) |
| 进区限位 | 点「进入系统/归位」后顶栏出现「返回全球」,滚轮最远只到 map.home.range |
data-map-area-state 应为 locked、data-map-range-max 应等于 map.home.range |
| 区内视距雾 | 进区后顶栏出现「视距雾 1.00 · 18–42 km」;远景平滑融入雾色、近景清晰 | data-map-fog="ready"、data-map-fog-phase="locked";区外应为 global 强度 0 |
| 雾的高度收口 | 返回全球时画面不再整片变白;飞入时也不再"白→突然一团绿" | 相机升过 42 km 后 data-map-fog-strength 应为 0;区内约 9.8 km 时仍能到 1 |
| 雾不"一瞬间"散掉 | 返回全球时雾看得出是一段淡出,不是一闪就没 | 返回后连续采样 data-map-fog-strength,应能取到 0 与 1 之间的中间值(若只采到 0/1 说明限速没生效) |
| 粗→精影像交接 | 全球态只有粗底图;飞入时雾先起、精细影像后亮,全程无肉眼可辨的矩形跳变 | data-map-handover:全球 global→进区 entering→revealed;alpha 由 0 升到 1;layerProbe 在隐藏时应为 show:true, alpha:0 |
| 交接的"先收后撤" | 点「返回全球」:影像先变淡收起,然后雾才淡出(不应反过来) | 采样 data-map-handover-alpha 与 data-fog-strength 的时间序:alpha 归 0 之前雾应保持 1 |
| 闲置回引导 | 全球态闲置 10 秒后回到初始态:镜头回 0/−60/2×10⁷、星球重新自转、中央闪烁「点击进入系统」 |
__cockpitMap.getLifecycleStats():spin 应为 true、spinWanted > 0;getCameraState() 的 hpr 应回到 0/−60/2×10⁷。只在 global 态计时,区内闲置不回 |
| 无 404 噪声 | 浏览器网络面板里 /gis/** 全是 200 |
多半是 y 轴约定:TMS 未写 {reverseY} |
| 3D Tiles(若启用) | 浮标含「3D Tiles N 个」 | 根 tileset.json 是否存在(见 2.5) |
| 真接口联调 | 分区出数据(REST 与 WS 均免鉴权,无需任何凭据);若个别分区 401/403 只该分区「数据不可用」 | 网络层内网隔离是否覆盖所有入口(网关 + 可直连的应用端口) |
先记录前端提交、运行页面 URL、配置数据源(real/mock)、后端运行版本及时间。mock 受控样例通过不等于 real 数据通过;HTTP 200 不等于业务成功,业务错误分区应显示加载失败。不得为走查绕过坐标校验或修改面板 pointer-events。
map.primaryArea.bounds 内即可上图,不再要求人工校核来源):真实数据通常大部分可上图,若本项观察到“全部不可上图”,应先核对后端是否已加载含该判定的构建、以及坐标是否确实落在主场景区内,再决定是否记为缺口——不要沿用旧口径直接判成“无可上图样本”。map.effects.abnormalLimit(默认30)时停动画并显示异常数;这与 map.overlap.maxFan(默认10)的展开上限是两种不同降级,不能互代。字段位于 .cockpit-shell__map .map-canvas,DEV只读句柄为 window.__cockpitMap;生产没有DEV句柄时查看元素属性和真实画面。
| 检查 | 字段/句柄 | 判据 |
|---|---|---|
| 详情身份 | data-map-panel-selection、data-map-device-picked |
point/equipment 与面板当前对象一致;不把聚合符号当虚构设备 |
| 聚合 | data-map-overlap-state、data-map-overlap-count;getOverlapState() |
collapsed/expanded/selected,超量为degraded;count为K,selectedCode为真实编码 |
| 展开子点 | getOverlapTargets()、getAssociatedDevices() |
可见子点有真实编码,overlapEntry只标记中心已选设备;位置偏移仅用于屏幕呈现 |
| 设备状态动画 | data-map-device-effects、data-map-device-effects-animated、data-map-device-effects-abnormal;getDeviceEffects() |
on/off/degraded,台数真实;tick增长只是驱动证据,还要确认可见外围环跨帧变化 |
| 面板锚定 | .cockpit-shell__detail 的 data-anchor-code、data-anchor-state |
点位摘要指向点位,设备详情指向设备;离屏暂隐不改身份 |
| 位置退化 | 详情/清单的原因、无地图位置列表 | 坐标缺失(MISSING_)、越界(_OUT_OF_RANGE)、零坐标、区外(OUTSIDE_PRIMARY_AREA)、地形失败/待确认、资产降级分别记录,不冒充成功 |
Q:改了 config.json 没生效?
确认产物 index.html 里有 window.__COCKPIT_CONFIG_URL__ 那一行;再硬刷新(配置请求带 cache: 'no-store',
但 index.html 可能被缓存)。
Q:地形/影像请求 404?
先看 URL 能不能直接下载(浏览器地址栏打开 …/gis/dem/layer.json)。全 404 = nginx alias 路径不对;
个别 404 = 瓦片 y 轴约定不对(TMS 要 {reverseY})或 bounds 与数据范围不一致。
Q:能不能用天地图/ArcGIS?
可以但有前提:天地图要免费 tk;ArcGIS(arcgis-imagery,免 key)要外网。
本项目约定优先用本地/内网离线数据,在线源仅作可选补充。
Q:拉很近画面变黑? 已修(票 30):中心点高程现在按 DEM 实测取(不是椭球 0 米),拉到距离下限 120 m 仍能看到地面。 若仍黑,检查地形是否加载成功(浮标应有「地形:dem」)。
Q:不想每次进来都点「点击进入系统」?
map.intro.enabled = false。归位按钮不受影响。
Q:进入引导挡住了自动化/走查?
同上,或由宿主页注入 window.__COCKPIT_CONFIG__ = { map: { intro: { enabled: false } } } 只关这一个用例。
Q:要换一批数据(另一个区县)?
把新数据按同样形态准备好 → nginx 挂新目录 → 改 config.json 的三个 url。前端不用动。