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

19 KiB
Raw Blame History

地图可视化配置平台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/*.svgVite 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/Tilesource 实现。
    • 影像图ArcGIS World Imagery
    • 矢量图:内网 GeoServer WMTSqgc_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 中:

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_MAPuseMapStudioState.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',并批量添加 Featuressource.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 - 重要功能(配图方案之后)

  1. 自定义图形绘制 - 利用 ol/interaction/Draw 实现线条、矩形、圆圈,以及箭头(起点终点+终点三角头)。图形存入独立 VectorSource,支持选中删除。保存配图时需包含图形坐标数据。

    • 涉及文件:新建 useDrawTools.tsRightDrawer.vue新增绘制Tab或面板
  2. 自定义图片叠加 - 上传 PNG 图片以绝对定位 div 覆盖在地图上,支持鼠标拖拽移动位置。保存配图时记录 CSS 位置数据。

    • 涉及文件:新建 CustomImageOverlay.vueRightDrawer.vue(新增图片管理面板)

P2 - 增强功能(基础功能稳定后)

  1. 空间范围限定 - 下拉选择水电基地/流域/省份,地图 fit 至对应边界,并可调用 clippingPolygons 实现区域外变灰的裁切效果。

    • 涉及文件:useMapInit.ts(扩展)、RightDrawer.vue(新增下拉选择)
  2. 选中锚点气泡 popover - 点击锚点时在地图弹出轻量 a-popover 气泡,显示站点名称和"删除"按钮。

    • 涉及文件:useAnchorLayers.ts(点击事件扩展)
  3. 锚点拖拽 - 启用 ol/interaction/Translate 允许拖动锚点改变地理位置,拖拽结束自动更新选中面板的经纬度。

    • 涉及文件:useAnchorLayers.ts(添加 Translate 交互)
  4. 锚点导入 - 左侧【导入】按钮弹出 a-modal 弹框,支持 Excel/CSV 批量导入经纬度数据。

    • 涉及文件:LeftToolbar.vue(接入导入弹框)、新建导入逻辑 composable

P3 - 工程优化(可逐步完善)

  1. 迁移为 Pinia 状态管理 - 当前使用 provide/inject,迁移到 Pinia 可更好地支持 Vue Devtools 调试和模块隔离。

    • 涉及文件:新建 store/mapStudioStore.ts,调整 useMapStudioState.ts
  2. 目录结构调整 - 将类型定义迁移到 types/index.ts,新建 MapContainer.vue 独立组件,对外暴露 index.tsapp.use 安装)。

9. 与现有底层地图的集成方案(接入指南)

9.1 核心原则(禁止叠放)

在集成至已有地图项目时,严禁使用 z-index 将本模块地图叠放于底层地图之上。

原因

  1. 事件冲突:鼠标滚轮缩放、点击拖拽等事件会同时触发两层地图,导致画面闪烁、缩放倍数混乱。
  2. 性能浪费:被遮盖的底层地图仍在持续进行瓦片请求和渲染,消耗 CPU/GPU 与网络资源。

正确策略:必须将底层地图隐藏/销毁后,再加载本模块地图。

9.2 推荐集成方案(二选一)

方案 A隐藏底层容器推荐保留底层状态

适用于底层地图涉及复杂的业务交互状态(如选中的点位、高亮区域),退出模块后需无缝恢复。

  • 进入模块时:设置底层地图容器的 CSS 样式为 display: nonevisibility: hidden
  • 模块内部:在底层地图容器同级位置,新建一个绝对定位的 div 作为本模块地图的挂载点,初始化 ol.Map
  • 退出模块时
    1. 调用本模块地图实例的 map.dispose() 销毁地图。
    2. 移除新建的挂载 div 元素。
    3. 恢复底层地图容器的显示样式。

方案 B销毁底层地图更彻底

适用于底层地图仅作展示、无复杂临时状态的场景。

  • 进入模块时:直接获取底层地图容器,销毁原有 ol.Map 实例(targetElement.innerHTML = ''),并复用该容器作为本模块地图的挂载点。
  • 退出模块时:销毁本模块地图实例,根据业务需求决定是否重新初始化底层地图。

9.3 解耦约定(确保模块可迁移)

由于本模块设计为独立可迁移,模块内部代码严禁直接操作父级项目的 DOM 元素或地图实例。

正确的通信方式:本模块 MapStudio/index.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>