WholeProcessPlatform/frontend/docs/地图可视化配置平台(MapStudio)技术实现说明文档.md
2026-07-28 18:28:07 +08:00

344 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 地图可视化配置平台MapStudio技术实现说明文档
## 1. 项目概述
本项目是一个基于 **Vue3** + **Ant Design Vue 4.0** + **OpenLayers (ol ^10.8.0)** 的独立地图配图功能模块。 **核心定位**:用于流域水电场景的地图配图工具,支持多类型底图切换、基础图层叠加、业务锚点管理(含样式编辑)。 **关键特性**:作为一个高内聚、低耦合的独立模块,支持整体复制迁移至其他 Vue3 项目。
## 2. 技术栈与环境
- **框架**Vue 3 (Composition API)
- **UI 组件库**Ant Design Vue 4.0 (`a-drawer`, `a-tabs`, `a-popover`, `a-modal` 等)
- **地图引擎**OpenLayers (`ol@^10.8.0`)
- **状态管理**Vue `reactive` + `provide`/`inject` 模式(`useMapStudioState.ts`
- **辅助库**`html2canvas`(配图截图预览,待接入)
- **图标资源**`/src/assets/legend/*.svg`Vite glob 批量导入)
## 3. 页面布局结构
整体采用 **“左侧固定工具栏 + 中央地图容器 + 右侧可收抽屉 + 左下悬浮图例”** 的布局方案。
| 区域 | 组件/实现 | 交互说明 |
| :--------------------- | :--------------------- | :------------------------------------------------------------------------------------------- |
| **工具栏** | `LeftToolbar.vue` | 垂直排列【放大】、【缩小】、【全屏】、【导入(占位)】、【保存配图】。独立于抽屉,抽屉收起时依然显示。目前按钮均为静态占位,无实际交互逻辑。 |
| **中央地图容器** | `index.vue` 内联 div | 承载 OL Map 实例,直接在 `index.vue` 中通过 `ref` 挂载地图,无独立 `MapContainer.vue` 组件。 |
| **右侧抽屉** | `RightDrawer.vue` | `a-drawer` 组件,支持收起/展开。内部嵌套 `a-tabs`,包含【基础底图】、【图例/标题管理】、【锚点样式】三个 Tab。 |
| **左下悬浮图例** | `Legend.vue` | 动态显示当前可见锚点分类的图标和标签,支持图例标题/子级样式配置,位置可通过配置调整(左上/左下/右上/右下)。 |
## 4. 功能模块详细说明
### 4.1 底图切换
- **支持类型**:【影像图】、【矢量图】、【地形图】、【等高线图】。通过替换 `ol/layer/Tile``source` 实现。
- 影像图ArcGIS World Imagery
- 矢量图:内网 GeoServer WMTS`qgc_qsj_arcgistiles_l13`
- 地形图:天地图地形 `ter_w`
- 等高线图Thunderforest Cycle
- **边界图层**:可通过 `a-checkbox` 控制显示/隐藏内网 GeoServer 提供的流域边界线。切换到矢量图时强制隐藏边界。
- **空间范围限定**:未实现(后续可扩展)。
### 4.2 叠加基础图层(显隐控制)
通过 `useOverlayLayers.ts` 管理,右侧抽屉"基础底图"Tab 中的 Checkbox 勾选控制图层显隐(取消勾选时仅 `setVisible(false)`,不删除图层)。
支持以下图层(部分为 WMTS/ArcGIS/GeoServer 服务):
| 图层 | 类型 | 数据来源 |
| :----------- | :---------- | :------------------------------- |
| 行政标注 | TileLayer | 天地图矢量标注 `cia_w` |
| 区域服务 | ImageLayer | ArcGIS REST `ZH_QUYU` |
| 九段线 | ImageLayer | ArcGIS REST `九段线服务1` |
| 岛屿 | ImageLayer | GeoServer WMS `cite:dy` |
| 流域注记 | ImageLayer | ArcGIS REST `zh_LiuYu_annotation` |
| 流域线 | ImageLayer | ArcGIS REST `zh_LiuYu_line` |
| 轮渡 | ImageLayer | ArcGIS REST `jiaotong_ferry` |
| 铁路 | ImageLayer | ArcGIS REST `jiaotong_railway` |
| 地铁 | ImageLayer | ArcGIS REST `jiaotong_metro` |
| 行政区面状 | ImageLayer | ArcGIS REST `zh_XingZhengQu_Polygon` |
| 行政区 | ImageLayer | ArcGIS REST `ZH_XINGZHEGNQU` |
### 4.3 业务锚点系统
锚点类型4 类):电站、过鱼设施、鱼类增殖站、低温水减缓设施。页面初始化时即并行加载所有分类的接口数据,勾选子类型后渲染到地图上,同时图例自动显示。
#### 4.3.1 数据模型
锚点核心接口定义在 `useMapStudioState.ts` 中:
```typescript
interface AnchorCategory {
key: string; // 分类 keypowerStation / fishPassage / fishFarm / coldWater
label: string; // 分类名称
icon: string; // 分类图标 key
children: AnchorItem[];
}
interface AnchorItem {
key: string; // 子类型 key如 large_eng_built
label: string; // 子类型名称
icon: string; // 子类型图标 key
checked: boolean; // 是否勾选
}
```
每个锚点 Feature 的 properties 包含:
- `_iconUrl` - 当前图标 URL
- `_label` - 显示文字
- `_stnm` - 站点名称
- `_lng`, `_lat` - 经纬度
- `_categoryKey`, `_subTypeKey` - 分类/子类型标识
- `_iconSource` - 图标来源inherit / custom
- `_customIconUrl` - 自定义图标 URL
- `_customTypeLabel` - 自定义类型名称(更换图标时可自定义)
- `_ov_*` - 样式覆盖字段(字体、字号、颜色、加粗、图标大小、描边、文字位置/边距、显示文字)
#### 4.3.2 样式继承与优先级
- **全局样式**:存储在 `anchorGlobalStyle` ref 中,修改时全局锚点刷新。
- **单个覆盖**:存储在 feature properties 的 `_ov_*` 字段中。`null` 表示继承全局。
- **渲染合并**:在 `createStyleFunction()` 中执行:`getOv(feature, key, globalValue)` - 有覆盖用覆盖值,否则回退到全局。
- **恢复默认**:将所有 `_ov_*` 置为 `null`
#### 4.3.3 锚点交互
- **勾选显示**:右侧抽屉"基础底图"Tab 提供四类锚点的 Checkbox 组,勾选子类型后地图渲染对应锚点,图例自动更新。
- **选中与编辑**
- **点击地图锚点**:触发选中事件,右侧抽屉自动切换到"锚点样式"Tab 并展开该锚点的详情编辑区域。
- **右侧面板编辑**:可编辑当前选中锚点的:
- **经纬度**(支持直接修改并更新地图位置)
- **显示文字**(修改锚点显示的文本)
- **更换图标**(上传自定义图标,支持两个选项)
- **样式覆盖**(每个样式字段独立覆盖全局,支持一键恢复继承)
- **无气泡 popover**:目前不弹出地图气泡。
- **更换图标**
- **选项 A - 应用于此锚点**:仅替换当前选中锚点的图标。可选择"添加到图例",输入父级名称和当前名称,若图例中不存在则自动添加。
- **选项 B - 应用于所有同类型**:替换当前分类下所有锚点的图标,同时图例中该分类的图标也会更新。
- **碰撞检测**:不做碰撞检测与抽稀(`declutterMode: 'none'`)。
#### 4.3.4 锚点导入(暂为占位)
左侧工具栏【导入】按钮为静态占位,无实际交互逻辑。
#### 4.3.5 图标资源映射
图标 SVG 文件存放于 `/src/assets/legend/`,通过 `ICON_SVG_MAP``useMapStudioState.ts`)建立 icon key 到文件名不含扩展名的映射Vite glob 批量导入获取 URL。
### 4.4 图例/标题管理
右侧抽屉"图例/标题管理"Tab
#### 4.4.1 图例配置
- **显示控制**`a-checkbox` 控制图例和标题的显隐
- **图例属性**:位置(左上/左下/右上/右下)、宽度、高度、左边距/右边距、上边距/下边距
- **图例标题**:对齐方式(左/中/右)、字体、字号、颜色、加粗
- **子级样式**:字体、字号、颜色、加粗、图标大小
#### 4.4.2 标题配置
- 标题内容、X/Y 位置、宽度、高度(通过 `BasicControlOptions[1].checked` 控制显隐)
### 4.5 锚点全局样式管理
右侧抽屉"锚点样式"Tab在无选中锚点时仅显示"全局样式"配置区:
- 字体、字号、颜色、加粗
- 图标大小
- 文字描边宽度、描边颜色
- 文字位置(上/下/左/右)、水平边距、垂直边距
- 是否显示文字标注
- **恢复默认**:一键重置为 `DEFAULT_ANCHOR_STYLE`
有选中锚点时,顶部显示"当前选中"区域,包含:
- 分类标签(含自定义类型名称)
- 站点名称
- 经纬度编辑
- 显示文字编辑
- 当前图标预览 + 更换图标按钮
- **样式覆盖**区:每个样式字段独立可调,带"恢复为继承"按钮(×)
- **恢复默认**:一键清空所有覆盖
### 4.6 自定义图形绘制(未实现)
预留功能,尚未开发。
### 4.7 自定义图片叠加(未实现)
预留功能,尚未开发。
### 4.8 配图方案管理(未实现)
预留功能,尚未开发。
## 5. 目录结构与实际代码
```
src/modules/MapStudio/
├── index.vue # 主入口组件
├── api/
│ └── index.ts # 4 类锚点 API 接口
├── components/
│ ├── LeftToolbar.vue # 左侧工具栏
│ ├── RightDrawer.vue # 右侧抽屉
│ └── Legend.vue # 左下悬浮图例
├── composables/
│ ├── useMapStudioState.ts # 共享状态创建/注入
│ ├── useMapInit.ts # 地图初始化与底图切换
│ ├── useOverlayLayers.ts # 基础图层叠加管理
│ └── useAnchorLayers.ts # 锚点图层管理
└── styles/
└── index.scss # 模块样式
```
## 6. 核心数据流
- **状态管理**`useMapStudioState.ts` 通过 `createMapStudioState()` 创建所有响应式状态,`provideMapStudioState()` 注入,子组件通过 `injectMapStudioState()` 获取。
- **锚点图层**`useAnchorLayers.ts` 管理 4 类锚点的 API 数据加载、OL 图层创建/显隐、样式函数、地图点击选中、样式覆盖同步。
- **基础图层**`useOverlayLayers.ts` 管理基础底图的叠加,通过 `LAYER_MAP` 定义各图层的创建工厂,响应式同步勾选状态。
- **API 层**`api/index.ts` 定义 4 类锚点的请求参数和接口函数,使用项目通用 `request` 工具(基于 axios
## 7. 开发注意事项与难点规避
- **性能优化**:锚点数量较多(>500务必开启 VectorSource 的 `renderMode: 'vector'`,并批量添加 Features`source.addFeatures()`)。
- **样式刷新**:覆盖字段写入 feature 后,必须重新 `setStyle(styleFn)` 触发重绘。
- **交互冲突**:左侧工具栏按钮需阻止点击事件冒泡,避免触发地图的 `singleclick` 事件。
- **底图切换**:切换时将新底图 `insertAt(0)` 插入最底层,避免覆盖上层叠加图层。
- **边界图层**:切换到底图"矢量图"时强制移除边界图层(因矢量图 WMTS 已包含边界信息)。
- **图标覆盖**:选项 B 替换图标时,通过 `categoryIconOverrides` 存储覆盖 URL图例组件会读取该字段更新显示。
## 8. 未实现功能列表与推荐优先级
### 功能完成度概览
| 功能模块 | 状态 | 说明 |
| :--------------------- | :----------- | :------------------------------------------- |
| 底图切换 | ✅ 已完成 | 4 种底图,边界图层显隐控制 |
| 基础图层叠加 | ✅ 已完成 | 11 个子图层,分组 Checkbox 勾选控制 |
| 锚点数据加载与展示 | ✅ 已完成 | 4 类接口初始化加载,勾选渲染,图例自动更新 |
| 锚点全局样式控制 | ✅ 已完成 | 字体/字号/颜色/加粗/图标大小/描边/位置/边距 |
| 锚点选中与编辑 | ✅ 已完成 | 点击选中,右侧面板编辑经纬度/文字/样式 |
| 更换图标(单锚点) | ✅ 已完成 | 上传+添加到图例,自定义类型名称 |
| 更换图标(同分类) | ✅ 已完成 | 替换全部分类图标,更新图例 |
| 图例/标题配置 | ✅ 已完成 | 位置/尺寸/对齐/字体/颜色/加粗 |
| 配图方案管理 | ❌ 未实现 | 保存/加载/历史配图 |
| 工具栏按钮功能 | ❌ 未实现 | 放大/缩小/全屏/导入/保存按钮均为静态占位 |
| 自定义图形绘制 | ❌ 未实现 | 线条/箭头/矩形/圆圈 |
| 自定义图片叠加 | ❌ 未实现 | PNG 上传叠加,调整位置/大小 |
| 空间范围限定 | ❌ 未实现 | 选择区域并裁切地图 |
| 锚点拖拽 | ❌ 未实现 | Translate 交互拖动锚点位置 |
| 选中锚点 popover 气泡 | ❌ 未实现 | 点击锚点在地图弹出小气泡 |
| 锚点导入功能 | ❌ 未实现 | Excel/CSV 批量导入 |
### 推荐开发优先级
**P0 - 核心功能(建议优先完成)**
1. **配图方案管理** - 这是配图工具的核心闭环功能。左侧【保存】按钮应弹出命名输入框保存当前所有状态底图类型、图层显隐、锚点数据与样式覆盖、图例配置、标题配置为JSON配图方案并支持历史记录查看、加载、删除。
- 涉及文件:`LeftToolbar.vue`(接入保存/导入按钮逻辑)、`RightDrawer.vue`(新增加载方案界面)、新建 `useSerializer.ts`(序列化/反序列化)
- 依赖:需要后端存储接口或本地 localStorage
2. **工具栏按钮功能** - 放大/缩小/全屏/导入/保存按钮目前均为静态。放大缩小应绑定 `map.getView().zoom`,全屏应调用 `FullScreen` API导入按钮应弹出功能弹框。
- 涉及文件:`LeftToolbar.vue`(接入 mapInstance
**P1 - 重要功能(配图方案之后)**
3. **自定义图形绘制** - 利用 `ol/interaction/Draw` 实现线条、矩形、圆圈,以及箭头(起点终点+终点三角头)。图形存入独立 `VectorSource`,支持选中删除。保存配图时需包含图形坐标数据。
- 涉及文件:新建 `useDrawTools.ts`、`RightDrawer.vue`新增绘制Tab或面板
4. **自定义图片叠加** - 上传 PNG 图片以绝对定位 div 覆盖在地图上,支持鼠标拖拽移动位置。保存配图时记录 CSS 位置数据。
- 涉及文件:新建 `CustomImageOverlay.vue`、`RightDrawer.vue`(新增图片管理面板)
**P2 - 增强功能(基础功能稳定后)**
5. **空间范围限定** - 下拉选择水电基地/流域/省份,地图 fit 至对应边界,并可调用 `clippingPolygons` 实现区域外变灰的裁切效果。
- 涉及文件:`useMapInit.ts`(扩展)、`RightDrawer.vue`(新增下拉选择)
6. **选中锚点气泡 popover** - 点击锚点时在地图弹出轻量 `a-popover` 气泡,显示站点名称和"删除"按钮。
- 涉及文件:`useAnchorLayers.ts`(点击事件扩展)
7. **锚点拖拽** - 启用 `ol/interaction/Translate` 允许拖动锚点改变地理位置,拖拽结束自动更新选中面板的经纬度。
- 涉及文件:`useAnchorLayers.ts`(添加 Translate 交互)
8. **锚点导入** - 左侧【导入】按钮弹出 a-modal 弹框,支持 Excel/CSV 批量导入经纬度数据。
- 涉及文件:`LeftToolbar.vue`(接入导入弹框)、新建导入逻辑 composable
**P3 - 工程优化(可逐步完善)**
9. **迁移为 Pinia 状态管理** - 当前使用 `provide`/`inject`,迁移到 Pinia 可更好地支持 Vue Devtools 调试和模块隔离。
- 涉及文件:新建 `store/mapStudioStore.ts`,调整 `useMapStudioState.ts`
10. **目录结构调整** - 将类型定义迁移到 `types/index.ts`,新建 `MapContainer.vue` 独立组件,对外暴露 `index.ts`app.use 安装)。
## 9. 与现有底层地图的集成方案(接入指南)
### 9.1 核心原则(禁止叠放)
在集成至已有地图项目时,**严禁**使用 `z-index` 将本模块地图叠放于底层地图之上。
**原因**
1. **事件冲突**:鼠标滚轮缩放、点击拖拽等事件会同时触发两层地图,导致画面闪烁、缩放倍数混乱。
2. **性能浪费**:被遮盖的底层地图仍在持续进行瓦片请求和渲染,消耗 CPU/GPU 与网络资源。
**正确策略**:必须将底层地图**隐藏/销毁**后,再加载本模块地图。
### 9.2 推荐集成方案(二选一)
#### 方案 A隐藏底层容器推荐保留底层状态
适用于底层地图涉及复杂的业务交互状态(如选中的点位、高亮区域),退出模块后需无缝恢复。
- **进入模块时**:设置底层地图容器的 CSS 样式为 `display: none``visibility: hidden`
- **模块内部**:在底层地图容器同级位置,新建一个绝对定位的 `div` 作为本模块地图的挂载点,初始化 `ol.Map`
- **退出模块时**
1. 调用本模块地图实例的 `map.dispose()` 销毁地图。
2. 移除新建的挂载 `div` 元素。
3. 恢复底层地图容器的显示样式。
#### 方案 B销毁底层地图更彻底
适用于底层地图仅作展示、无复杂临时状态的场景。
- **进入模块时**:直接获取底层地图容器,销毁原有 `ol.Map` 实例(`targetElement.innerHTML = ''`),并复用该容器作为本模块地图的挂载点。
- **退出模块时**:销毁本模块地图实例,根据业务需求决定是否重新初始化底层地图。
### 9.3 解耦约定(确保模块可迁移)
由于本模块设计为**独立可迁移**,模块内部代码**严禁**直接操作父级项目的 DOM 元素或地图实例。
**正确的通信方式**:本模块 `MapStudio/index.vue` 通过暴露生命周期事件,由父级项目自行控制底层地图的显隐与销毁。
```vue
<!-- 父级项目使用示例 -->
<template>
<div id="app">
<!-- 原有底层地图容器 -->
<div id="old-map-container" v-show="!showMapStudio"></div>
<!-- MapStudio 模块 -->
<MapStudio
v-if="showMapStudio"
@before-mount="handleBeforeMount" <!-- 进入模块时触发 -->
@after-destroy="handleAfterDestroy" <!-- 退出模块时触发 -->
/>
</div>
</template>
<script setup>
const showMapStudio = ref(false);
const handleBeforeMount = () => {
// 父级在此处执行:隐藏底层地图或销毁其实例
// 例如document.getElementById('old-map-container').style.display = 'none';
};
const handleAfterDestroy = () => {
// 父级在此处执行:恢复底层地图显示或重新初始化
// 例如document.getElementById('old-map-container').style.display = 'block';
};
</script>
```