1 changed files with 174 additions and 0 deletions
@ -0,0 +1,174 @@ |
|||
# 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/引用;冒烟验收 |
|||
Loading…
Reference in new issue