# Mapbox 全量替换 maptalks 日期:2026-08-27 范围:`MapLayer` / `mapHelper` / `config/map` + Monitor / Tasks / Replay 状态:待用户确认 ## 1. 背景与目标 现状运行时仍是 `maptalks@1.12.1` + 天地图 WMTS。共享层 `MapLayer` 单例经 `commonRefs('map')` + `mapHelper` 暴露;`MonitorView` / `TasksView` / `ReplayView` 直接使用 maptalks Marker / VectorLayer / LineString / setBaseLayer。 目标: 1. 引擎从 **maptalks 全量切到 mapbox-gl** 2. 底图用 **Mapbox 官方样式**:卫星 / 街道;环境变量 `APP_MAPBOX_TOKEN` 3. 保留常驻 `MapLayer` + `commonRefs('map')` + `mapHelper` **薄封装** 4. Monitor / Tasks / Replay **全部**改 Mapbox API;删除 `maptalks` 依赖 5. Tasks 航线编辑继续 `replaceMapContainer` / `restoreMapContainer` 搬迁共享地图容器 6. 设备点、航点、航迹、绑定线统一用 **GeoJSON Source + Layer** 非目标: - 不做统一 `MapService` 大门面 - 不保留天地图 / 双 provider - 不改球形总览 Three.js(若已有或后续独立做) - 本轮不重做机巢/无人机 pin 图标视觉(可先 `circle` / 基础 `symbol`,后续再接 pin) ## 2. 方案选型 | 方案 | 结论 | |------|------| | A. 薄封装替换 MapLayer/mapHelper,页面直用 mapbox API | **采用** | | B. 统一 MapService 门面 | 本轮不做,抽象成本高 | | C. 每页独立地图实例 | 与常驻地图 + Tasks 搬迁语义冲突 | 底图:Mapbox 官方 `satellite-streets` / `streets`,不再使用天地图 WMTS。 渲染:GeoJSON Source + Layer(非 DOM Marker 为主路径)。 ## 3. 共享层 ### 3.1 `src/config/map.js` - `token`:`import.meta.env.APP_MAPBOX_TOKEN || import.meta.env.VITE_MAPBOX_TOKEN || ''` - 保留 `center` / `zoom`(现有 `APP_MAP_CENTER_*` / `APP_MAP_ZOOM`) - 导出: ```js export const MAP_STYLES = { satellite: 'mapbox://styles/mapbox/satellite-streets-v12', street: 'mapbox://styles/mapbox/streets-v12', } ``` - 删除 `createTiandituLayerOptions` / `createTiandituBaseLayer` ### 3.2 `src/layout/MapLayer.vue` - 引入 `mapbox-gl` 与 CSS - `mapboxgl.accessToken = mapConfig.token` - 缺 token:打错误日志,`commonRefs.setRef('map', null)`,不抛崩 - 有 token: ```js map = new mapboxgl.Map({ container: 'map', style: MAP_STYLES.satellite, center: mapConfig.center, // [lng, lat] zoom: mapConfig.zoom, minZoom: 3, attributionControl: false, }) ``` - `commonRefs.setRef('map', map)` + `mapHelper.setMap(map)` - `onUnmounted`:`map.remove()`,清 ref / helper ### 3.3 `src/core/mapHelper.js` 保留对外方法,内部改 Mapbox: | 方法 | 行为 | |------|------| | `setMap` / `getMap` | 持有 `mapboxgl.Map` | | `toggleMapMode({ is2D, is3D })` | 2D:`setPitch(0)` `setBearing(0)`,关闭 dragRotate/pitch;3D:pitch≈45,开启旋转/倾角 | | `replaceMapContainer(newParent)` | 保存 view → 把 `#map` DOM 挪到 `newParent` → `map.resize()` | | `restoreMapContainer()` | 移回原父节点 → 恢复 center/zoom/pitch/bearing → `map.resize()` | 去掉 maptalks `checkSize`;一律 `resize()`。 ### 3.4 依赖与 env - `package.json`:增加 `mapbox-gl`;删除 `maptalks` - `.env.example` / `.env.development` / `.env.production`: - 增加 `APP_MAPBOX_TOKEN=` - 删除或降级 `APP_TIANDITU_TOKEN` 说明(本轮不再使用) - 保留中心/缩放变量 - CSS:去掉 `.maptalks-wrapper` 特例;Tasks 宿主适配 `#map` / `.mapboxgl-map` ## 4. 页面层 坐标约定:统一 `[lng, lat]`。 `map.setStyle(...)` 会清掉自定义 source/layer —— **切换样式后必须在 `style.load` 重建业务层并回填数据**。 ### 4.1 MonitorView | 能力 | Mapbox 落地 | |------|-------------| | 设备点 | source `monitor-devices` + `circle`(或基础 `symbol`)+ 可选 `symbol` 文本层 | | 选中态 | `feature-state` 或按 selectedId 重写 properties / paint | | 绑定虚线 | source `monitor-binding` + `line`(`line-dasharray`);仅「选中机巢 + 其飞行中无人机」或「选中飞行无人机 + 所属机巢」 | | 卫星/地图 | `map.setStyle(MAP_STYLES[mode])`;`style.load` 后重建 devices/binding 层 | | fitAll | `fitBounds`;清选中回 overview | | 选中飞向 | `flyTo` / `easeTo` | | 比例尺 | 保留现有 zoom→米估算,或挂 `ScaleControl`(二选一,UI 位置对齐现工具条) | | 点击选中 | `queryRenderedFeatures` 命中设备层 | 离开 Monitor:移除本页 source/layer 与事件,避免污染 Tasks/Replay。 ### 4.2 TasksView - 进入编辑:`mapHelper.replaceMapContainer(host)` → `resize()` → `toggleMapMode({ is2D: true })` - 航线:source `task-route`(LineString)+ `task-waypoints`(Point features,可带序号属性) - 地图点击加点:`map.on('click', ...)` → `e.lngLat` - 离开:清业务层 → `restoreMapContainer()` ### 4.3 ReplayView - source `replay-traj` line + 起/终/当前点(可同一 points source 不同 layer filter) - `fitBounds` 包住轨迹 - 离开清层 ## 5. 数据流与生命周期 ```mermaid flowchart LR MainContainer --> MapLayer MapLayer -->|"commonRefs + mapHelper"| Pages Pages --> Monitor Pages --> Tasks Pages --> Replay Tasks -->|"replace/restore DOM"| MapLayer ``` 1. 登录后壳层挂载 → `MapLayer` 初始化 Mapbox → 写入 ref 2. 业务页 `await commonRefs.getRef('map')` 拿到实例后加自己的 source/layer 3. 路由离开必须卸本页图层与监听;Tasks 额外 restore 容器 4. 无 token:地图不初始化,业务页需容忍 `map == null`(空态/提示,不白屏) ## 6. 风险与处理 | 风险 | 处理 | |------|------| | `setStyle` 清空业务层 | 统一 `style.load` 重建 + 数据回填函数 | | Tasks 搬迁后尺寸为 0 | replace/restore 后立刻 `resize()`;必要时 `requestAnimationFrame` 再 resize | | token 无效/缺省 | 跳过 init + 日志;页面降级 | | 旧文档按 maptalks/天地图写 | 本 spec 为准;相关旧计划标注过时,不在本轮改文 | | 包体变大 | 可接受;不引入 `maptalks.mapboxgl` 双引擎 | ## 7. 验收 - [ ] 配置有效 `APP_MAPBOX_TOKEN` 后登录可见 Mapbox 卫星底图 - [ ] 缺 token 不白屏崩,有明确控制台错误 - [ ] Monitor:卫星↔街道切换后点/线仍在;选中飞向;fitAll;绑定虚线条件正确;比例尺可见 - [ ] Tasks:编辑器内地图可点加点、航线/航点绘制;离开恢复主壳地图位置 - [ ] Replay:航迹线 + fitBounds - [ ] `package.json` 无 `maptalks`;源码无 `import ... maptalks` - [ ] `1920×1080` 冒烟:Monitor 主路径可用 ## 8. 实现顺序(计划阶段细化) 1. 依赖与 env / `config/map` / `MapLayer` / `mapHelper` 2. Monitor 点线与底图切换 / fit / 绑定线 3. Replay 航迹 4. Tasks 搬迁 + 航线编辑交互 5. 删 maptalks 残留 CSS/引用;冒烟验收