# 地图模块-抽吸规则与参数说明 ## 1. 文档目的 本文档用于记录当前地图点位“抽吸”功能的现行实现、默认参数、分层级显示规则和后续调参方式。 本文件描述的是**当前已经落地并验证通过**的规则,适合作为后续维护和继续调参的依据。 相关文档: - `docs/地图模块-详细说明.md`:地图整体架构说明 - `docs/地图模块-OpenLayers抽吸与碰撞分析.md`:问题成因与改造分析 ## 2. 当前实现范围 当前抽吸逻辑运行在 OpenLayers 主链路中,核心文件如下: 1. `src/modules/map/domain/nearby-point-rules.ts` 2. `src/store/modules/map.ts` 3. `src/components/gis/map.ol.ts` 4. `src/components/gis/ol/point-layer-manager.ts` 各文件职责: - `nearby-point-rules.ts` - 近邻点规则入口 - 自动识别配置入口 - 近邻点运行时元数据挂载 - `map.ts` - 点位数据加载完成后,统一触发近邻点分组和元数据写回 - `map.ol.ts` - 决定某个点在当前缩放级别是否显示 - 决定文字是否允许显示 - 承担图标与文字的手工碰撞控制 - `point-layer-manager.ts` - 负责展开阶段的几何偏移 - 负责把同组点位按扇形/环形布局散开 ## 3. 当前抽吸规则 ### 3.1 近邻点自动识别 当前采用自动识别近邻点,不再靠固定站名手工枚举。 识别原则: - 只在同一 `layerKey` 内做近邻识别 - 按点位空间距离自动聚组 - 满足最小组大小后,才认为是近邻组 - 组内自动生成显示优先级 `priority` 这样做的目的: - 避免跨图层混组 - 避免后续新增点位还要手工补名字 - 让抽吸能力可以覆盖整批业务点 ### 3.2 分层级显示规则 当前显示规则以 `replaceAtZoom` 和 `expandAtZoom` 为核心: 1. `zoom < replaceAtZoom` - 只显示组内主点 - 即 `priority = 1` 2. `replaceAtZoom <= zoom < expandAtZoom` - 显示组内前两个点 - 即 `priority <= 2` - 当前这样设计是为了避免主点在放大过程中突然消失 3. `zoom >= expandAtZoom` - 同组点全部显示 - 同时进入展开布局阶段 补充规则: - 如果某个点配置了 `showZoomMin` 或 `showZoomMax`,则先受这两个值约束 - 没有进入近邻组的点,不受抽吸规则影响 ### 3.3 文字显示规则 当前文字显示必须依赖点位显示。 也就是说: - 点不显示,文字一定不显示 - 点显示后,文字还要继续通过碰撞判断才会显示 当前文字碰撞规则: - 文字要避让已保留的图标占位盒 - 文字要避让已保留的其他文字 - 图标与文字不再分别交给 OpenLayers 的 `declutter` 自动处理 - 当前走的是项目内的手工碰撞控制 ## 4. 当前默认参数 当前默认参数集中维护在: - `src/modules/map/domain/nearby-point-rules.ts` 当前采用统一配置对象: ```ts type NearbyPointConfig = { autoRule: NearbyPointAutoRule; layoutRule: NearbyPointLayoutRule; densityDisplayRules: NearbyPointDensityDisplayRule[]; }; ``` 其中: - `autoRule`:控制近邻点自动识别、缩放切换和基础展开半径 - `layoutRule`:控制展开阶段的单圈容量、扇形角度和外圈间距 - `densityDisplayRules`:控制 `distance` 密度分档在不同缩放级别的基础显示资格 ### 4.1 自动识别与缩放阶段参数 当前值如下: | 参数 | 当前值 | 作用 | |---|---:|---| | `distanceThresholdMeters` | `9000` | 近邻点自动识别距离阈值 | | `replaceAtZoom` | `10.5` | 进入“主点 + 次点”阶段的缩放级别 | | `expandAtZoom` | `12.2` | 进入“全部显示并展开”阶段的缩放级别 | | `expandOffsetPx` | `48` | 第一圈展开的基础偏移半径 | | `angleStepDeg` | `55` | 默认角度步长参考值 | | `minGroupSize` | `2` | 最小成组数量 | ### 4.2 展开布局参数 当前值如下: | 参数 | 当前值 | 作用 | |---|---:|---| | `ringSize` | `5` | 单圈最多容纳的次点数量 | | `fanAngleForTwoDeg` | `110` | 单圈剩余 `2` 个点时的扇形总角度 | | `fanAngleForFourDeg` | `150` | 单圈剩余 `3` 到 `4` 个点时的扇形总角度 | | `fanAngleForManyDeg` | `180` | 单圈剩余 `5` 个及以上点时的扇形总角度 | | `ringGapPx` | `22` | 外圈最小增量半径 | | `ringGapFactor` | `0.85` | 外圈相对基础半径的增量系数 | ### 4.3 密度分档显示参数 这里的 `distance` 不是物理距离,而是点位密度分档值。 数值越大表示越稀疏,越早参与显示候选;数值越小表示越密集,需要更高缩放级别才参与显示。 当前值如下: | `minDensityValue` | `minZoom` | 作用 | |---:|---:|---| | `1500000` | `0` | 很稀疏的点,从小缩放级别就可参与显示 | | `800000` | `6.1` | 稍密集的点,从中低缩放开始参与显示 | | `400000` | `8.1` | 中密度点,需要进一步放大 | | `50000` | `10.6` | 高密度点,需要较高缩放级别 | | `0` | `12.2` | 极密集点,只有更高缩放才进入候选 | ## 5. 当前展开布局 展开阶段由 `point-layer-manager.ts` 控制。 ### 5.1 布局特点 当前布局不是简单把点随机散开,而是按以下方式处理: - 主点保留原位置 - 同组其他点围绕主点展开 - 展开方向优先偏下方 - 单圈数量由 `ringSize` 控制 - 点数较多时进入第二圈 - 第二圈半径按 `ringGapPx` 和 `ringGapFactor` 继续增大,避免继续叠在一起 ### 5.2 为什么优先向下展开 当前优先向下展开,主要是为了: - 尽量给上方文字留空间 - 降低文字压住上方点的概率 - 让地图上的抽吸形态更稳定 ## 6. 当前实现的几个关键约束 ### 6.1 不使用淡入淡出动画 目前已移除点和文字的淡入淡出动画。 原因: - 抽吸状态切换和动画叠加时,容易让点和文字出现闪烁 - 会干扰对“当前到底该不该显示”的判断 当前阶段优先保证: - 显示规则稳定 - 点和文字状态一致 - 放大缩小时不出现明显错位 ### 6.2 不再依赖 OL 的自动 declutter 决定图标命运 当前图标和文字都设置为 `declutterMode: 'none'`,由项目内逻辑统一控制。 这样做的原因: - 避免图标和文字落在两套碰撞体系里 - 避免出现“文字先出来、点没出来” - 避免文字压住其他点却仍然显示 ## 7. 常见现象与调参建议 ### 7.1 成组太少 表现: - 相近点仍然容易互相盖住 - 进入展开前看起来还是压在一起 优先调整: - 增大 `distanceThresholdMeters` ### 7.2 成组太多 表现: - 距离并不算近的点也被当成一组 - 放大后过早进入抽吸关系 优先调整: - 减小 `distanceThresholdMeters` ### 7.3 展开太晚 表现: - 放大到较深层级前仍然挤在一起 优先调整: - 减小 `expandAtZoom` ### 7.4 展开太早 表现: - 中等缩放时地图上就出现较明显散开效果 优先调整: - 增大 `expandAtZoom` ### 7.5 展开后间距不够 表现: - 虽然已经展开,但图标还是比较挤 优先调整: - 增大 `expandOffsetPx` - 或增大 `ringGapPx` - 或增大 `ringGapFactor` ### 7.6 单圈散开角度不合适 表现: - 次点虽然已经展开,但仍然集中在一侧 - 或者扇形过大,展开形态显得太散 优先调整: - 调整 `fanAngleForTwoDeg` - 调整 `fanAngleForFourDeg` - 调整 `fanAngleForManyDeg` ### 7.7 主点在放大过程中突然消失 当前已经通过 `replaceAtZoom <= zoom < expandAtZoom` 阶段显示 `priority <= 2` 进行修正。 如果后续又出现类似现象,优先检查: - `map.ol.ts` 中 `shouldRenderNearbyFeature()` 的阶段逻辑 - 是否误改成只显示 `priority = 2` ### 7.8 明明周围看起来空,文字还是不显示 优先检查: - 图标碰撞盒是否过大 - 文字碰撞盒是否过于保守 - 候选集是否发生重复统计 相关代码主要在: - `src/components/gis/map.ol.ts` ## 8. 后续维护建议 建议后续继续保持以下约定: 1. 抽吸参数继续统一放在 `nearby-point-rules.ts` 2. 显示决策继续集中在 `map.ol.ts` 3. 几何偏移继续集中在 `point-layer-manager.ts` 4. 后续调参优先改 `DEFAULT_NEARBY_POINT_CONFIG`,不要直接把数值散落回业务代码 5. 如果继续加动画,必须放在抽吸状态机完全稳定之后 6. 如果继续优化 popup,也要同步遵循“点先于文字”的显示原则 ## 9. 当前结论 当前这版抽吸实现已经形成了比较稳定的主线: - 自动识别近邻点 - 统一缩放阶段显示规则 - 展开阶段几何偏移 - 文字依赖点显示 - 文字避让图标与文字 后续如果继续调优,建议先从参数层开始,不要先动主逻辑结构。