# 地图模块现状与改造方案 ## 1. 文档目的 本文档用于完整梳理当前地图模块的技术实现、业务能力、数据流转、关键规则、已知问题与性能瓶颈,并给出可分阶段落地的重构方案。目标是: - 让地图模块职责边界清晰,便于后续新增功能; - 降低组件、Store、地图引擎之间的耦合; - 优化基础图层、锚点、图例、筛选、基地裁切等功能的响应速度; - 为后续 2D/3D 扩展、复杂专题图层、更多筛选维度、更多交互能力预留稳定结构。 --- ## 2. 当前模块范围 当前地图模块覆盖以下业务能力: - 地图初始化与 2D/3D 切换; - GeoServer 基础底图、叠加图层、区域矢量图层加载; - Java 后端锚点数据请求、缓存、渲染; - 右侧图层树勾选控制图层显示与隐藏; - 左侧图例基于图层选中结果动态生成; - 图例项点击控制锚点显隐; - 基地选择后底图裁切与区域外锚点隐藏; - 地图悬停 Popup、点击详情弹窗; - 搜索、时间筛选、容量筛选、基地筛选; - 某些业务图层的互斥显示与特殊联动; - 局部专题菜单下的缩放级别联动动态图层。 --- ## 3. 当前技术实现概览 ## 3.1 技术栈 - 地图引擎:OpenLayers 作为 2D 主引擎,Cesium 作为 3D 引擎入口; - 状态管理:Pinia; - UI 框架:Vue 3 + `script setup` + Ant Design Vue; - 数据来源: - GeoServer:基础底图、WMTS、XYZ、GeoJSON 边界; - Java 后端接口:图层配置、图例配置、锚点数据; - 业务配置: - 图层配置与图例配置来自后端; - 锚点接口映射规则由 `src/store/modules/GisUrlList.ts` 维护。 ## 3.2 核心文件职责 - `src/components/gis/GisView.vue` - 地图容器入口; - 地图初始化; - 拉取图层配置与图例配置; - 监听菜单切换、基地切换、缩放联动; - 挂载 `MapLegend`、`MapFilter`、`MapController`、`BaseLayerSwitcher`。 - `src/components/gis/map.class.ts` - 地图能力门面类; - 对外屏蔽 OpenLayers / Cesium 差异; - 当前主要起到转发作用。 - `src/components/gis/map.ol.ts` - OpenLayers 主实现; - 负责底图、锚点图层、Popup、图层显隐、区域裁切、量算、定位等; - 当前承担了过多业务规则与渲染控制逻辑。 - `src/store/modules/map.ts` - 地图图层树、图例、锚点、缓存的核心 Store; - 负责图层选中状态、图例选中状态、锚点请求、缓存、图例联动、图层联动; - 当前是业务与渲染耦合最重的文件之一。 - `src/components/mapController/LayerController.vue` - 图层树 UI; - 负责初始基础图层加载; - 勾选事件直接调用 Store 更新图层与图例状态; - 包含多个互斥业务规则。 - `src/components/mapLegend/index.vue` - 图例展示; - 基于当前选中图层动态显示; - 图例点击后直接驱动地图锚点显隐。 - `src/components/mapFilter/index.vue` - 搜索、基地筛选、容量筛选、时间筛选; - 直接依赖 `mapStore` 和地图实例; - 部分筛选逻辑通过直接修改图例和锚点实现。 - `src/components/BaseLayerSwitcher/index.vue` - 底图模式切换器; - 切换矢量 / 地形 / 影像底图。 - `src/components/gis/gisUtils.ts` - 图层结构拍平; - 基础图层 URL 处理; - 图例数据转对象; - 若干地图布局相关工具。 - `src/api/map.ts` - 拉取地图配置和图例配置。 --- ## 4. 当前业务能力与逻辑链路 ## 4.1 初始化流程 当前初始化主要发生在 `GisView.vue`: 1. 调用 `MapClass.init()` 初始化 OpenLayers; 2. 调用 `initPopupOverlay()` 挂载悬停 Popup; 3. 调用 `fetchMapConfigs()` 并行请求: - 地图图层配置; - 全量图例配置; - 当前页面图例配置; 4. `mapStore.setLegendData()` 初始化图例原始数据与映射; 5. `mapStore.setLayerData()` 初始化图层树和选中图层 key; 6. `mapStore.loadAllLayerData()` 递归加载所有有 URL 的图层数据; 7. `loadLayerData()` 将锚点数据写入缓存并直接调用 `mapClass.addInitDataLayer()` 预创建锚点图层; 8. 全部锚点加载完成后,再调用 `updateLayerData()` 统一控制初始显示状态; 9. 图例根据当前选中的图层动态生成并展示。 ### 现状特点 - 当前是“初始化阶段一次性预加载大多数锚点图层,再通过显隐控制”的策略; - 图例显示被设计为“锚点完成后再显示”; - 地图实例、Store、UI 组件三方互相直接调用。 ## 4.2 基础图层加载与切换 基础底图主要由 `LayerController.vue` 和 `map.ol.ts` 协同完成: - `LayerController.vue` 中监听图层树配置; - 对类型为 `GISMap` 的节点解析 `paramJson`,通过 `getMapConfig()` 补齐 GeoServer 地址; - 调用 `mapClass.addBaseDataLayer()` 初始化基础图层; - 同时调用 `controlBaseLayerTreeShowAndHidden()` 控制显隐; - `BaseLayerSwitcher` 再通过 `baseLayerSwitcher()` 在矢量 / 地形 / 影像底图之间切换。 当前基础图层类型主要包括: - WMTS:GeoServer WMTS 瓦片; - XYZ:例如 DEM 或其他切片图层; - vector:部分矢量底图或区域配置。 ### 自然保护区图层特点 - 属于叠加在底图之上的 GIS 图层; - 显隐仍通过基础图层控制逻辑处理; - 与区域裁切场景存在较强耦合。 ## 4.3 图层树勾选控制 右侧图层树由 `LayerController.vue` 驱动: - 树节点勾选结果通过 `broadcast()` 调用 `mapStore.updateLayerData()`; - 图层树中包含多种特殊规则: - 视频监控站与 AI 视频监控站互斥; - 环保设施和环保设施在建互斥; - 珍稀鱼类与沿程鱼类互斥; - 初始选中状态通过递归遍历图层树生成。 ### 现状问题 - 互斥逻辑分散在 UI 组件和 Store 两处; - “图层树状态”与“地图真实状态”并非单向流; - 图层节点既承担展示,又携带业务配置与运行态字段。 ## 4.4 锚点数据加载 锚点数据由 `mapStore.loadAllLayerData()` 和 `loadLayerData()` 负责: - 遍历图层树中全部带 URL 的图层; - 使用 `GisUrlList.ts` 将图层 key / title / url 映射成真实请求地址、筛选条件、排序规则; - 请求 Java 接口; - 将返回记录补充: - `iconCode` - `code` - `_id` - `tm` - 然后缓存到: - `pointDataCache[layerKey]` - `layer.data` - 合并后的 `pointData` - 若地图上还不存在该图层,则直接调用 `mapClass.addInitDataLayer()` 创建 `VectorLayer`。 ### 当前锚点渲染方式 - 每个 `pointMap` 图层对应一个 `VectorLayer`; - 每个点被转为 `Feature`; - Feature 上维护多个运行态字段: - `_iconUrl` - `_labelText` - `_legendVisible` - `_regionVisible` - `_layerKey` - 样式函数 `createPointStyle()` 在渲染时综合判断: - 图例是否显示; - 是否在基地区域内; - 当前缩放级别; - 点之间距离阈值; - 是否悬停。 ## 4.5 图例动态生成与点击联动 图例数据来源于两部分: - `legendDataOriginal`:后端返回的全量图例; - `legendDataSelected`:根据当前选中的图层动态筛选后的图例结构。 当前机制: 1. 初始化时拉取全部图例; 2. 通过 `layerCode` 与图层树选中结果建立关联; 3. `mapStore.updateLayerData()` 重新计算当前选中的图例树; 4. `MapLegend` 监听 `legendDataSelected` 进行展示; 5. 点击图例项后: - 更新 Store 中图例 checked 状态; - 若是锚点图层,则调用 `mapClass.setLegendPointVisible()`; - 若是 GIS 图层,则调用 `controlBaseLayerTreeShowAndHidden()`。 ### 环保设施特殊逻辑 - 勾选 `fp_point`、`eq_point`、`fb_point`、`vp_point`、`va_point`、`sg_point`、`dw_point` 中任意图层时,图例自动补充“环保设施”分组; - 全部取消时自动移除该图例分组。 ## 4.6 筛选与搜索 `MapFilter` 负责以下功能: - 装机容量筛选; - 锚点关键字搜索; - 时间筛选; - 基地筛选; - 个别专题下的鱼类分布筛选。 当前实现方式: - 搜索下拉列表从 `pointData` 里计算; - 只展示当前已选中图层中的锚点; - 容量筛选直接修改图例状态,并调用 `map.mdLayerShowOrHidden()` 控制 feature 显隐; - 时间筛选通过 `mapStore.updateSearchTimeRange()` 清缓存、删图层、重新请求; - 基地切换通过监听 `JidiSelectEventStore.selectedItem` 联动地图区域裁切。 ## 4.7 基地选择与区域裁切 基地逻辑入口在 `GisView.vue` 监听器中: - 当基地变化时,调用 `mapClass.jdPanelControlShowAndHidden()`; - `map.ol.ts` 内会: - 更新当前 `BASEID`; - 请求基地 GeoJSON 边界; - 隐藏所有锚点; - 通过射线法判断锚点是否在区域内; - 标记 `_regionVisible`; - 调整视角; - 对底图应用遮罩。 ### 该能力的业务效果 - 只显示当前基地区域内的锚点; - 底图裁切到当前基地范围; - 全选时取消遮罩并显示全部锚点。 ## 4.8 Popup 与点击详情 当前交互如下: - `pointermove` 时调用 `detectFeatureAtPixel()` 检查命中点要素; - 若命中图标区域,则: - 更新鼠标指针; - 更新 `hoveredFeatureId`; - 调用 `showPopup()` 展示悬停浮窗; - 重绘所有点图层; - `click` 命中点要素时,打开详情弹窗并将 feature 属性写入 `modelStore`。 ### 当前特点 - Popup 内容由 `popupHtml` 或 `generatePopupHtml(props)` 生成; - 命中检测采用逐像素回调加距离二次判断; - 所有点图层在 hover 状态切换时都会 `layer.changed()`。 ## 4.9 菜单与缩放级别联动 在水电开发状况菜单下: - 地图缩放级别大于等于 12 时自动添加一组动态图层; - 缩回 12 以下时自动移除; - 菜单切换时会根据当前层级重新判断是否添加。 当前动态图层 key 包括: - `dw_point` - `stinfo_video_point` - `stinfo_gjllz_point` - `wt_point` - `wq_ownWq_point` - `wq_countryWq_point` - `fp_point` - `eq_point` - `fb_point` - `vp_point` - `va_point` - `sg_point` --- ## 5. 当前数据流与调用关系 当前主数据流可概括为: 1. `GisView.vue` 拉取配置; 2. `mapStore` 保存图层树、图例、缓存; 3. `LayerController.vue` / `MapLegend.vue` / `MapFilter.vue` 直接操作 `mapStore`; 4. `mapStore` 又直接调用 `mapClass`; 5. `mapClass` 继续转发到 `map.ol.ts`; 6. `map.ol.ts` 内部维护图层注册表和 Feature 运行态; 7. 地图状态变化后,组件又通过监听 Store 再次更新 UI。 ### 现有结构本质 当前是“多入口、多方向写状态”的结构: - 组件可以改 Store; - 组件可以直接调地图; - Store 可以直接调地图; - 地图内部又维护一套运行态; - 图例、图层树、地图显隐存在多份状态镜像。 这会导致: - 状态同步成本高; - 某些场景下容易出现 UI 勾选状态与实际地图状态不一致; - 新增规则时只能继续在现有逻辑上叠加 `if/else`。 --- ## 6. 当前存在的主要问题 ## 6.1 架构层面问题 ### 1. UI、业务编排、地图渲染耦合过深 - `LayerController.vue`、`MapLegend.vue`、`MapFilter.vue` 都直接调 Store 和地图实例; - `map.ts` 同时负责: - 请求; - 缓存; - 图例结构整理; - 图层勾选; - 地图显隐; - 业务互斥; - `map.ol.ts` 同时负责: - OpenLayers 适配; - 业务过滤; - 区域裁切; - 弹窗; - 图例控制; - 锚点显示策略。 ### 2. 缺乏清晰的单向数据流 - 图层树 checked、图例 checked、Feature `_legendVisible`、Layer visible 都是状态源; - 任何一个点修改后都可能触发联动,难以推断最终状态; - 某些流程依赖顺序正确才能表现正常。 ### 3. 业务规则分散 - 互斥规则分散在 `LayerController.vue` 和 `map.ts`; - 环保设施图例补充逻辑在 `map.ts`; - 装机容量筛选逻辑在 `MapFilter.vue`; - 基地区域过滤逻辑在 `map.ol.ts`; - 这些规则没有统一归口,新增业务会继续扩大分散度。 ## 6.2 性能问题 ### 1. 初始化阶段预加载过重 - `loadAllLayerData()` 会递归处理大量图层; - 即使图层未勾选,也会提前请求并建图层; - 页面首次进入时网络压力和首屏渲染压力较大。 ### 2. 递归遍历频繁 - 图层树查找 `findLayerByKey()` 多次递归; - 图例生成 `getlegendData()` 递归; - `updateLayerData()` 每次变化都要递归更新 checked 状态; - 搜索项计算 `anchorPointOptions` 也会递归收集图层 key。 ### 3. 锚点显隐是全量 feature 遍历 - 图例点击时逐个遍历 feature; - 基地裁切时逐个 feature 做点在面内判断; - hover 时所有点图层重绘; - 缩放变化会重复执行样式计算。 ### 4. 底图切换会 remove / add 图层 - `baseLayerSwitcher()` 每次切换都移除旧底图再新增; - 对 WMTS / XYZ 来说会造成重复创建图层对象和重复请求开销。 ## 6.3 一致性与可靠性问题 ### 1. 存在明显临时代码与未收敛逻辑 - `GisView.vue` 中 `fetchMapConfigs()` 里对图层做了 `splice(0, 2)` 临时截断; - 日志仍显示“临时限制”,说明线上逻辑可能仍处于测试状态。 ### 2. 存在未定义引用 - `GisView.vue` 切换地图后调用 `fetchPointData()`,但项目中未找到定义; - `map.ts` 中使用了 `wq_lineTime`、`wt_lineTime`,但项目中未找到定义。 ### 3. 请求去重逻辑未真正落地 - `map.ts` 中生成了 `requestIdentifier`,但未实际用于请求去重; - `requestParamsCopy` 也未参与有效逻辑。 ### 4. 图层状态与渲染状态容易失同步 - `layer.checked`、`checkedLayerKeys`、图例 checked、Feature 可见状态是多份数据; - 其中任意一份被跳过更新,就可能出现视觉不一致。 ### 5. 部分代码存在明显遗留痕迹 - `addInitDataLayer()` 中存在无意义语句 `3;`; - 有重复日志; - 有注释掉的大量旧逻辑; - 说明代码迁移过程中缺乏结构性收口。 ## 6.4 可维护性问题 ### 1. Store 过大 - `map.ts` 既是数据仓库,又是调度器,又是业务规则中心; - 后续新增图层类型、筛选类型、专题逻辑时,文件会持续膨胀。 ### 2. MapOl 职责过多 - 当前 `map.ol.ts` 既是 OpenLayers 适配器,又是图层经理、锚点经理、Popup 经理、区域裁切经理; - 单文件理解和修改成本已经偏高。 ### 3. 缺少规范化类型系统 - 大量 `any`; - 图层配置、图例配置、锚点数据、请求映射结构都没有稳定类型; - 特殊图层规则依赖字符串常量散落在各处。 --- ## 7. 改造目标 本次改造建议达成以下目标: ### 7.1 结构目标 - 建立清晰的四层结构:视图层、应用编排层、领域状态层、地图渲染层; - 收拢业务规则,不再散落到多个 UI 组件和地图引擎实现中; - 建立稳定的数据模型与类型定义。 ### 7.2 性能目标 - 首屏只加载必要图层; - 选中图层优先加载,未选中图层延后或懒加载;(待考虑) - 图例切换、基地切换、搜索筛选尽量使用索引和差量更新; - 减少重复递归、重复建图层、重复样式重算。 ### 7.3 维护目标 - 新增图层类型、专题规则、图例规则时有固定扩展点; - 2D/3D 共享统一业务层,底层只替换渲染适配器; - 可以较容易增加埋点、日志、调试工具和单元测试。 --- ## 8. 目标架构设计 建议将地图模块拆分为四层: ## 8.1 视图层 只负责展示和用户交互,不直接编写复杂业务逻辑。 建议保留的组件: - `GisView.vue` - `LayerController.vue` - `MapLegend.vue` - `MapFilter.vue` - `BaseLayerSwitcher.vue` 视图层职责: - 展示树、图例、筛选项、底图切换器; - 分发用户动作; - 订阅 Store 派生结果; - 不直接调用 `map.ol.ts` 里的业务方法。 ## 8.2 应用编排层 新增地图编排层,建议命名为: - `src/modules/map/application/map-orchestrator.ts` - 或 `src/components/gis/core/orchestrator.ts` 职责: - 承接 UI 动作; - 统一处理“图层勾选、图例勾选、基地切换、时间筛选、搜索定位、底图切换”等命令; - 控制加载顺序; - 根据状态差异驱动地图渲染层; - 管理互斥规则和特殊规则。 ### 推荐模式 所有 UI 动作统一转换成 Command: - `toggleLayer(layerKey)` - `toggleLegend(layerKey, legendKey)` - `switchBaseMap(baseMapKey)` - `changeBaseRegion(baseId)` - `changeTimeRange(range)` - `focusPoint(pointId)` ## 8.3 领域状态层 保留 Pinia,但将当前大 Store 拆分为多个职责明确的 Store / Service: - `mapConfigStore` - 保存图层树、图例原始配置、页面配置; - `mapViewStateStore` - 保存当前选中图层、选中图例、当前底图、当前基地、当前时间范围、当前缩放级别; - `mapDataStore` - 保存锚点缓存、请求状态、加载状态、错误状态; - `mapRuleEngine` - 保存互斥规则、派生规则、特殊专题规则。 ## 8.4 地图渲染层 保留 `MapClass` 作为统一门面,但将 `MapOl` 拆分成多个 Manager: - `ol-map-adapter.ts` - 只负责地图实例生命周期; - `ol-base-layer-manager.ts` - 负责 WMTS / XYZ / GeoJSON / 底图切换; - `ol-point-layer-manager.ts` - 负责锚点图层创建、更新、索引、显隐; - `ol-popup-manager.ts` - 负责 hover / click / popup; - `ol-region-mask-manager.ts` - 负责基地裁切、遮罩、点在面内过滤; - `ol-interaction-manager.ts` - 负责缩放监听、量算、定位等。 这样做后,`MapOl` 只保留聚合职责,不再直接承载全部细节。 --- ## 9. 详细改造方案 ## 9.1 统一领域模型 首先建立类型定义,避免继续依赖 `any`。 建议新增: - `types/map-layer.ts` - `types/map-legend.ts` - `types/map-point.ts` - `types/map-rule.ts` ### 图层类型建议 - `BaseMapLayer` - `OverlayLayer` - `PointLayer` - `RegionMaskLayer` - `DynamicLayer` ### 图层运行态建议(待考虑) 将运行态从后端配置中剥离,不再直接把运行态写回原始配置: - `config`: 后端返回配置,只读; - `runtime`: - `visible` - `loaded` - `loading` - `error` - `featureCount` - `lastRequestKey` - `lastUpdatedAt` ## 9.2 统一图层树、图例、地图显隐状态源 建议明确一个唯一状态源: - 图层是否勾选:只认 `selectedLayerKeys`; - 图例是否勾选:只认 `selectedLegendKeys`; - 地图渲染层不再存业务真相,只接收派生后的可见性差量。 ### 派生关系建议 - `selectedLayerKeys` -> 派生当前显示的图例组; - `selectedLayerKeys + selectedLegendKeys + currentBaseId` -> 派生 Feature 可见性; - `selectedBaseMapKey` -> 派生当前底图组合; - `timeRange + filterConditions` -> 派生图层请求签名。 ## 9.3 建立图层索引与图例索引 为提升速度,初始化配置后立即建立索引: - `layerByKey` - `legendByNameEn` - `legendByLayerCode` - `childrenByParentKey` - `layerDescendantsByKey` - `featureIndexByLayerAndLegendKey` ### 优化收益 - 避免每次 `findLayerByKey()` 递归; - 图例切换时直接定位对应图层和对应 feature 集合; - 图层树勾选联动时只处理差异节点。 ## 9.4 抽离请求解析器 当前 `GisUrlList.ts` 的匹配逻辑散落在 `loadLayerData()` 中,建议拆出: - `map-layer-request-resolver.ts` 职责: - 根据 `layer.key` / `layer.title` / `layer.url` 解析真实请求; - 生成标准请求结构: - `url` - `method` - `filter` - `sort` - `cacheKey` ### 进一步建议 - 在应用启动时将 `GisUrlList.ts` 预编译成多索引字典; - 不要在每次 `loadLayerData()` 内重新构建 `urlListDict`; - 请求签名应真正参与去重和缓存。 ## 9.5 重新设计锚点加载策略(待考虑) 建议从“全量预加载”改为“分层加载”: ### 第一阶段 - 首屏只加载默认勾选图层; - 图例也只基于已加载的默认勾选图层生成; ### 第二阶段(待考虑) - 对常用图层做后台低优先级预取; - 非常规图层在首次勾选时再加载; ### 第三阶段(待考虑) - 对时间敏感或高频筛选图层使用缓存失效策略: - `cacheKey = layerKey + requestSignature` - 基于时间范围、基地、专题条件生成。 ### 配套措施(待考虑) - 增加并发控制,例如同时最多 4 到 6 个接口请求; - 增加可取消请求,避免菜单切换时旧请求回写新页面; - 记录每个图层单独 loading 和 error 状态。 ## 9.6 重构点图层渲染管理 建议将点图层改造成“图层级管理 + 索引级更新”模式。 ### 当前问题 - 图例点击靠遍历全量 feature; - 基地裁切靠遍历全量 feature; - hover 时所有点图层都重绘。 ### 改造建议 在 `PointLayerManager` 中维护以下索引: - `featuresByLayerKey` - `featuresByLegendKey` - `featuresByBaseId` - `featureById` ### 改造后的显隐策略 - 图例点击时,只处理该图例对应 feature 集合; - 基地切换时,优先按 `baseId` 做粗筛,再对边界交叉区域做点在面内判断; - 图层勾选时只切换对应 `VectorLayer.visible`; - 非必要不重新 `addFeatures()`。 ### 样式策略建议 - 保留 style function,但减少在 style function 内做复杂业务判断; - 将 `_legendVisible`、`_regionVisible`、`_layerVisible` 预先计算为单个 `renderVisible` 标记; - 样式函数只做: - 是否可见; - 当前缩放级别; - 当前 hover 状态; - 图标与文本样式计算。 ## 9.7 底图与叠加层管理重构(待考虑) 建议明确区分三类图层: - 主底图:矢量 / 地形 / 影像三选一; - 基础叠加层:自然保护区、行政边界等可附加; - 专题锚点层:水电工程、生态流量、水质站、视频站等。 ### 当前问题 - 底图切换会 remove / add `customBaseLayer`; - 主底图和叠加底图概念混杂; - `controlBaseLayerTreeShowAndHidden()` 实际控制的不全是底图。 ### 改造建议 - 维护 `currentBaseMapKey`; - 主底图预创建,切换时只改 visible; - 叠加层作为独立 overlay 管理; - 自然保护区等图层明确归类为 overlay,不与主底图复用同一逻辑。 ## 9.8 图例模块重构 建议把“图例结构”和“图例选中态”彻底分离: - `legendSchema`: 后端原始图例树; - `legendVisibleTree`: 根据当前图层选择派生; - `legendCheckedSet`: 当前选中的图例 key 集。 ### 这样做的好处 - 后端原始图例树不再被运行态污染; - 图例点击只更新 `legendCheckedSet`; - 图层变化只重算当前显示哪些图例,不直接修改原始结构; - 更容易实现“重置图例”、“记忆用户图例偏好”。 ## 9.9 基地裁切与区域过滤重构 建议将基地逻辑完全收口到 `RegionMaskManager`。 ### 改造重点 - 缓存基地 GeoJSON; - 一次解析边界,多次复用; - 对点位先按 `baseId` 粗筛,再按 polygon 精筛; - 避免每次基地切换都重新请求已缓存边界; - 底图遮罩和锚点区域过滤使用同一份区域上下文。 ### 进一步优化 - 若后端能返回基地与锚点的归属关系,可优先直接按 `baseId` 过滤; - 点在面内判断只作为兜底校验; - 对非常大的多边形可预处理 bbox,先做 bbox 命中,再做射线法。 - - 有一个 baseID 来传值 直接请求接口数据加载 - const url = this.hydropBaseConfig.geojson_url + `&cql_filter=BASEID='${this.BASEID}'`; console.log('正在请求裁切数据:', url); // 等待数据加载 const geoJsonData = await this.loadGeoJsonData(url); ## 9.10 Popup 与交互重构 建议将 Popup 逻辑从 `MapOl` 中抽离: - `PopupManager` 负责: - hover 命中检测; - popup 位置; - popup 内容渲染; - click 详情回调; ### 优化方向 - 命中检测尽量只针对当前可见的点图层; - 对 hover 更新增加最小移动阈值; - 只重绘当前命中 feature 所在图层,而不是所有点图层; - Popup 内容生成应支持模板注册,不直接在地图层拼接业务 HTML。 ## 9.11 业务规则引擎化 建议把分散的特殊规则抽到统一规则层。 ### 当前应纳入规则引擎的规则 - 视频监控站与 AI 视频监控站互斥; - 环保设施与环保设施在建互斥; - 珍稀鱼类与沿程鱼类互斥; - 环保设施图例自动补充; - 水电开发状况菜单的缩放级别动态图层; - 水质断面多接口拼接; - 生态流量三级数据结构处理。 ### 建议实现方式 定义规则接口: ```ts interface MapBusinessRule { id: string; when(context: RuleContext): boolean; apply(context: RuleContext): RulePatch; } ``` 这样新增业务时只加规则文件,不再继续往组件和 Store 塞判断。 ## 9.12 组件层改造建议 ### `GisView.vue` - 只保留地图容器、初始化、模块挂载; - 配置请求交给 `mapOrchestrator.initialize(pageKey)`; - 菜单切换、基地切换也只发送命令。 ### `LayerController.vue` - 只处理树控件展示与事件抛出; - 互斥逻辑不要写在组件中; - 初始底图加载不要放在组件 watch 里,改到编排层统一处理。 ### `MapLegend.vue` - 只负责展示图例树和发出切换事件; - 不直接调用 `mapClass`。 ### `MapFilter.vue` - 搜索、容量、时间、基地变化全部改为发送标准命令; - 过滤后的锚点候选列表由 Store 派生,不在组件里自行拼装业务逻辑。 ### `BaseLayerSwitcher.vue` - 只负责切换主底图; - 不关心图层树中 `customBaseLayer` 的内部结构。 ## 9.13 日志、监控与调试能力(待考虑) 建议补充以下调试能力: - 图层加载耗时; - 单图层接口耗时; - 首屏加载耗时; - 当前可见图层列表; - 当前可见锚点数量; - 当前选中图例数量; - 当前缓存命中率; - 当前基地裁切耗时。 可以在开发模式下提供一个简易的地图调试面板,便于排查性能与状态同步问题。 --- ## 10. 分阶段落地方案 建议采用“四阶段渐进式改造”,避免一次性大改导致风险过高。 ## 第 1 阶段:收口现状与止血 目标:不改变功能表现,先把明显风险收口。 建议内容: - 去掉测试遗留逻辑,如 `splice(0, 2)`; - 修复未定义方法和未定义变量; - 清理重复日志、无效代码、注释垃圾; - 补充类型定义; - 建立 `layerByKey`、`legendByNameEn` 等基础索引; - 将互斥规则从组件内搬到 Store 或规则层统一处理; - 实现真实请求去重和请求取消。 ## 第 2 阶段:拆 Store 与编排层 目标:建立单向数据流。 建议内容: - 新增 `mapOrchestrator`; - 拆 `map.ts` 为配置、状态、数据三个模块; - UI 组件只发送命令; - 图例派生、图层派生、筛选派生全部收口到编排层。 ## 第 3 阶段:拆地图渲染层 目标:把 `MapOl` 大文件拆开。 建议内容: - 抽 `BaseLayerManager`; - 抽 `PointLayerManager`; - 抽 `PopupManager`; - 抽 `RegionMaskManager`; - 明确地图引擎层只负责“渲染执行”,不处理业务真相。 ## 第 4 阶段:性能强化与能力扩展 目标:让模块适合长期迭代。 建议内容: - 实现懒加载与后台预取; - feature 索引化; - 地图交互性能优化; - 支持规则插件化; - 补充测试与性能指标看板; - 为 3D 共用业务层做适配。 --- ## 11. 推荐目录结构 建议重构后目录大致如下: ```text src/ modules/ map/ application/ map-orchestrator.ts map-commands.ts map-rule-engine.ts domain/ types/ map-layer.ts map-legend.ts map-point.ts map-filter.ts services/ layer-request-resolver.ts legend-deriver.ts layer-deriver.ts infrastructure/ stores/ map-config.store.ts map-view-state.store.ts map-data.store.ts repositories/ map-config.repository.ts map-point.repository.ts render/ map.class.ts ol/ ol-map-adapter.ts ol-base-layer-manager.ts ol-point-layer-manager.ts ol-popup-manager.ts ol-region-mask-manager.ts ol-interaction-manager.ts components/ gis/ GisView.vue mapController/ LayerController.vue mapLegend/ index.vue mapFilter/ index.vue BaseLayerSwitcher/ index.vue ``` --- ## 12. 重点业务的改造策略 ## 12.1 基础图层 策略: - 单独定义主底图与叠加图层; - 预创建主底图; - 切换时仅改 visible; - `customBaseLayer` 不再作为特殊魔法 key 到处透传。 ## 12.2 水电工程锚点 策略: - 作为标准 `PointLayer` 处理; - 保留当前图标、标签、距离阈值、Popup 规则; - 在 `PointLayerManager` 中建立 feature 索引。 ## 12.3 生态流量三级数据 策略: - 在领域层定义树结构转换器; - Store 不直接关心层级深浅; - 图层树组件只拿已经标准化后的树。 ## 12.4 水质监测断面三接口拼接 策略: - 不在 UI 或 Store 中散写拼接逻辑; - 建立专题数据聚合器,例如 `water-quality-point-aggregator.ts`; - 对外输出统一的 `PointLayerData`。 ## 12.5 视频监控站与 AI 视频监控站互斥 策略: - 写成标准业务规则; - 由规则引擎统一裁剪勾选结果; - UI 不再内嵌业务互斥判断。 ## 12.6 自然保护区与区域底图 策略: - 统一归类为 `OverlayLayer`; - 与主底图切换分离; - 与基地裁切共享区域上下文。 --- ## 13. 测试与验收建议 ## 13.1 功能验收清单 - 页面首次进入,默认勾选图层正确显示; - 图层树勾选与取消勾选,地图表现与图例表现一致; - 图例点击后,仅影响对应锚点; - 基地切换后,区域外锚点正确隐藏; - 底图切换不影响锚点和图例状态; - 时间筛选后相关图层正确重载; - 视频监控站与 AI 视频监控站互斥稳定; - 菜单切换后图层、图例、筛选项正确重置; - 悬停 Popup 与点击详情正常。 ## 13.2 性能验收指标 建议至少跟踪: - 首屏地图可交互时间; - 默认勾选图层加载总耗时; - 单图层首次加载耗时; - 图层勾选后锚点出现时间; - 图例点击后的显隐响应时间; - 基地切换后的裁切完成时间; - 缓存命中率; - 页面切换时旧请求是否被取消。 ## 13.3 自动化测试建议 建议补充三类测试: - 单元测试: - 图例派生; - 图层互斥规则; - 请求解析器; - 缓存 key 生成; - 集成测试: - 图层勾选 -> 图例变化 -> 地图显隐; - 基地切换 -> 区域裁切 -> 搜索列表更新; - 端到端测试: - 地图初始化; - 图层切换; - 图例切换; - 筛选联动; - Popup 和详情弹窗。 --- ## 14. 优先级最高的首批改造项 如果本轮改造只做最有价值的一批,建议优先按以下顺序处理: 1. 修复明显问题: - 去掉 `splice(0, 2)`; - 修复 `fetchPointData()` 未定义; - 修复 `wq_lineTime` / `wt_lineTime` 未定义; - 清理无效代码与重复日志。 2. 建立统一状态源: - `selectedLayerKeys` - `selectedLegendKeys` - `currentBaseId` - `currentBaseMapKey` 3. 抽离请求解析器和规则引擎: - URL 映射规则统一; - 互斥规则统一; - 特殊图例补充规则统一。 4. 拆 `map.ts`: - 配置状态; - 运行状态; - 数据缓存; - 行为编排。 5. 拆 `map.ol.ts`: - 底图管理; - 锚点管理; - Popup 管理; - 区域裁切管理。 --- ## 15. 改造后的预期收益 完成改造后,地图模块应达到以下效果: - 新增图层和专题能力时,不需要修改多个组件和多个文件; - 图层树、图例、地图显隐的状态不再互相打架; - 首屏加载更快,图层切换更轻; - 地图代码从“堆逻辑”变成“可扩展的稳定结构”; - 后续增加 3D 共用逻辑、专题聚合图层、更多筛选条件时,改造成本明显下降。 --- ## 16. 结论 当前地图模块功能已经较完整,但代码结构处于“功能可用、结构偏重、规则分散、性能隐患较多”的阶段。继续在现有结构上叠加新需求,后续维护成本会快速上升。 建议本次改造不要只做局部优化,而应围绕以下核心原则整体推进: - 单向数据流; - 业务规则收口; - 渲染层职责下沉; - 类型与索引先行; - 先止血,再拆层,再提速。 按照本文档的分阶段方案推进,可以在不一次性推翻现有功能的前提下,逐步完成地图模块的结构升级。