新版3d修改提交,差碰撞,popup
This commit is contained in:
parent
ba64f8a223
commit
1fc41ef1f6
@ -7,14 +7,14 @@ VITE_APP_TITLE = '水电水利建设项目全过程环境管理信息平台'
|
|||||||
VITE_APP_PORT = 3000
|
VITE_APP_PORT = 3000
|
||||||
VITE_APP_BASE_API = '/dev-api'
|
VITE_APP_BASE_API = '/dev-api'
|
||||||
# 本地环境
|
# 本地环境
|
||||||
# VITE_APP_BASE_URL = 'http://10.84.121.127:8093'
|
VITE_APP_BASE_URL = 'http://localhost:8093'
|
||||||
# 测试环境
|
# 测试环境
|
||||||
# VITE_APP_BASE_URL = 'http://172.16.21.142:8093'
|
# VITE_APP_BASE_URL = 'http://172.16.21.142:8093'
|
||||||
# VITE_APP_BASE_URL = 'http://172.16.21.142:8096'
|
# VITE_APP_BASE_URL = 'http://172.16.21.142:8096'
|
||||||
# 汤伟
|
# 汤伟
|
||||||
# VITE_APP_BASE_URL = 'http://10.84.111.235:8093'
|
VITE_APP_BASE_URL = 'http://10.84.111.235:8093'
|
||||||
# 李林
|
# 李林
|
||||||
VITE_APP_BASE_URL = 'http://10.84.111.25:8093'
|
# VITE_APP_BASE_URL = 'http://10.84.111.25:8093'
|
||||||
|
|
||||||
# 测试环境线上10.84.121.122:
|
# 测试环境线上10.84.121.122:
|
||||||
VITE_APP_TEST_ONLINE_URL = 'https://211.99.26.225:12122'
|
VITE_APP_TEST_ONLINE_URL = 'https://211.99.26.225:12122'
|
||||||
|
|||||||
@ -132,7 +132,7 @@
|
|||||||
|
|
||||||
## 5. 分步骤实施计划
|
## 5. 分步骤实施计划
|
||||||
|
|
||||||
### Step 1:修正 3D 初始化并默认定位中国
|
### Step 1:修正 3D 初始化并默认定位中国 ✅️
|
||||||
|
|
||||||
目标:
|
目标:
|
||||||
|
|
||||||
@ -157,7 +157,7 @@
|
|||||||
- 首屏有加载过渡。
|
- 首屏有加载过渡。
|
||||||
- 进入后稳定落在中国。
|
- 进入后稳定落在中国。
|
||||||
|
|
||||||
### Step 2:展示 3D 基础底图
|
### Step 2:展示 3D 基础底图 ✅️
|
||||||
|
|
||||||
目标:
|
目标:
|
||||||
|
|
||||||
@ -171,6 +171,10 @@
|
|||||||
- 实现 `addBaseDataLayer(layer, isShow)`:
|
- 实现 `addBaseDataLayer(layer, isShow)`:
|
||||||
- 对 `customBaseLayer` 使用 `url_3d`
|
- 对 `customBaseLayer` 使用 `url_3d`
|
||||||
- 对 `BASEMAP-img`、`BASEMAP-white` 预留兼容能力
|
- 对 `BASEMAP-img`、`BASEMAP-white` 预留兼容能力
|
||||||
|
- **必须处理 `wmts` 类型图层**:2D 通过 `WMTS` source 加载,3D 使用 `Cesium.WebMapTileServiceImageryProvider`,从 URL 参数中提取 LAYER/TILEMATRIXSET
|
||||||
|
- **必须提供 `tileMatrixLabels`**:Cesium 默认用纯数字作为 tileMatrix 参数值,但本系统 WMTS 服务器要求 `TileMatrixSet:zoom` 格式(如 `EPSG:3857_hbb_zrbhq_l13:4`)。需构建 `["TileMatrixSet:0", "TileMatrixSet:1", ...]` 传入 `tileMatrixLabels`。
|
||||||
|
- 其他类型:`raster-dem`→XYZ、MapServer→ArcGIS、其余 →WMS
|
||||||
|
- **注意**:还存在一个 `key = "powerBaseStation"` 的基地底图图层,该图层**初始化时不加载**(已在 `IGNORED_BASE_LAYER_KEYS` 中忽略),但后续可通过图层树手动勾选显示。
|
||||||
- 实现 `controlBaseLayerTreeShowAndHidden()`:
|
- 实现 `controlBaseLayerTreeShowAndHidden()`:
|
||||||
- 让图层树勾选能控制 3D 底图显隐
|
- 让图层树勾选能控制 3D 底图显隐
|
||||||
- 根据你的要求,UI 层先不开放 `BaseLayerSwitcher` 的 3D 切换行为,只保留默认基础底图加载。
|
- 根据你的要求,UI 层先不开放 `BaseLayerSwitcher` 的 3D 切换行为,只保留默认基础底图加载。
|
||||||
@ -180,7 +184,7 @@
|
|||||||
- 切到 3D 后可稳定看到基础底图。
|
- 切到 3D 后可稳定看到基础底图。
|
||||||
- 图层初始化时 `addBaseDataLayer(this.baseLayerConfig, checked, true)` 的思路在 3D 可落地。
|
- 图层初始化时 `addBaseDataLayer(this.baseLayerConfig, checked, true)` 的思路在 3D 可落地。
|
||||||
|
|
||||||
### Step 3:打通 3D 锚点展示
|
### Step 3:打通 3D 锚点展示 ✅️
|
||||||
|
|
||||||
目标:
|
目标:
|
||||||
|
|
||||||
@ -189,13 +193,14 @@
|
|||||||
|
|
||||||
计划:
|
计划:
|
||||||
|
|
||||||
- 在 `MapCesium` 内建立点位图层注册结构,建议按 `layerKey -> entity[]` 或 `CustomDataSource` 管理。
|
- 在 `MapCesium` 内建立点位图层注册结构,按 `layerKey -> entity[]` 管理。
|
||||||
- 实现 `addInitDataLayer(pointData, layerType)`:
|
- 实现 `addInitDataLayer(pointData, layerType)`:
|
||||||
- 复用当前点位数据结构
|
- 复用当前点位数据结构
|
||||||
- 解析 `lgtd/lttd/iconCode/titleName/stnm/anchoPointState/popupHtml`
|
- 解析 `lgtd/lttd/iconCode/titleName/stnm/anchoPointState/popupHtml`
|
||||||
- 复用现有图标资源路径
|
- 复用现有图标资源路径
|
||||||
|
- Entity ID 使用 `${layerKey}:${item._id || `${iconType || 'point'}_${stcd}`}`,与 2D 对齐,确保同 stcd 不同 sttpMap 的点不被覆盖
|
||||||
- 确保 `eng_point` 仍按现有 `mapStore` 过滤后的数据展示,中型站点只在 `7.5` 以上层级出现
|
- 确保 `eng_point` 仍按现有 `mapStore` 过滤后的数据展示,中型站点只在 `7.5` 以上层级出现
|
||||||
- 为后续缩放驱动的增量显示预留运行态字段,不把所有点简单一次性硬渲染
|
- 为后续缩放驱动的增量显示预留运行态字段(`_legendVisible`、`_regionVisible`、`_densityVisible`),不把所有点简单一次性硬渲染
|
||||||
- 实现 `mdLayerTreeShowOrHidden(layerType, checked)`:
|
- 实现 `mdLayerTreeShowOrHidden(layerType, checked)`:
|
||||||
- 图层树控制整个点图层显隐
|
- 图层树控制整个点图层显隐
|
||||||
- 实现 `removePointLayer()` / `hasLayer()`
|
- 实现 `removePointLayer()` / `hasLayer()`
|
||||||
@ -208,7 +213,34 @@
|
|||||||
- 取消勾选图层后,对应锚点消失。
|
- 取消勾选图层后,对应锚点消失。
|
||||||
- `eng_point` 在低于 `7.5` 时不显示中型水电站,高于 `7.5` 时正常显示。
|
- `eng_point` 在低于 `7.5` 时不显示中型水电站,高于 `7.5` 时正常显示。
|
||||||
|
|
||||||
### Step 4:打通 3D popup 与 hover
|
### Step 4:打通 3D 基地裁切(jdPanelControlShowAndHidden) ✅️
|
||||||
|
|
||||||
|
目标:
|
||||||
|
|
||||||
|
- 3D 点击基地面板后,与 2D 行为一致。
|
||||||
|
- 加载基地边界 GeoJSON,只显示区域内锚点,隐藏区域外锚点。
|
||||||
|
- 取消基地时恢复显示所有锚点。
|
||||||
|
|
||||||
|
计划:
|
||||||
|
|
||||||
|
- 在 `MapCesium` 中捕获 `hydropBase` 配置,与 2D 一样在 `addBaseDataLayer` 中记录。
|
||||||
|
- 实现 `jdPanelControlShowAndHidden(baseid, isAll)`:
|
||||||
|
- `isAll=false`:清除 3D 球面遮罩实体、恢复所有锚点区域可见性、清除裁切状态。
|
||||||
|
- `isAll=true`:先隐藏所有锚点,异步加载基地边界 GeoJSON,执行点在面判断后仅显示区域内锚点,同时在球面绘制裁切边界多边形(`applyRegionMask`),最后飞行到区域视野。
|
||||||
|
- 在 `createEntityPropertiesBag` 中新增 `_regionVisible` 属性,初始为 `true`。
|
||||||
|
- 在 `applyPointVisibilityRefresh` 中跳过 `_regionVisible !== false` 的实体,确保裁切后碰撞检测不会把区域外锚点重新显示出来。
|
||||||
|
- 复用 `extractPolygonCoords` / `isPointInRing` 射线法判断逻辑(与 `region-mask-manager.ts` 一致)。
|
||||||
|
- 支持 `AbortController` 取消前一次未完成的请求,避免响应顺序错乱。
|
||||||
|
- `applyRegionMask` 将 GeoJSON 多边形转换为 Cesium `PolygonHierarchy`,在球面绘制半透明填充 + 可见边界的区域多边形,模拟 2D 底图 Canvas 裁切遮罩的视觉效果。
|
||||||
|
|
||||||
|
验收:
|
||||||
|
|
||||||
|
- 点击基地后,3D 只显示该基地区域内的锚点,区域外锚点隐藏。
|
||||||
|
- 取消基地后,所有锚点恢复正常显示。
|
||||||
|
- 快速切换基地不会出现旧数据覆盖新数据的问题。
|
||||||
|
- 多次切换 2D/3D 后基地裁切行为正常。
|
||||||
|
|
||||||
|
### Step 5:打通 3D popup 与 hover
|
||||||
|
|
||||||
目标:
|
目标:
|
||||||
|
|
||||||
@ -233,7 +265,7 @@
|
|||||||
- 样式和 2D 保持一致。
|
- 样式和 2D 保持一致。
|
||||||
- 移出后 popup 正常消失。
|
- 移出后 popup 正常消失。
|
||||||
|
|
||||||
### Step 5:补齐缩放驱动显示与碰撞检测
|
### Step 6:补齐缩放驱动显示与碰撞检测
|
||||||
|
|
||||||
目标:
|
目标:
|
||||||
|
|
||||||
@ -263,7 +295,7 @@
|
|||||||
- 放大到具体层级后会看到更多锚点逐步出现。
|
- 放大到具体层级后会看到更多锚点逐步出现。
|
||||||
- 标签和锚点不会大面积重叠到不可读。
|
- 标签和锚点不会大面积重叠到不可读。
|
||||||
|
|
||||||
### Step 6:打通 3D 锚点点击详情
|
### Step 7:打通 3D 锚点点击详情 ✅️
|
||||||
|
|
||||||
目标:
|
目标:
|
||||||
|
|
||||||
@ -287,7 +319,7 @@
|
|||||||
- `ylfb` 和普通锚点分支行为一致。
|
- `ylfb` 和普通锚点分支行为一致。
|
||||||
- 点击空白区域不会误触发弹框。
|
- 点击空白区域不会误触发弹框。
|
||||||
|
|
||||||
### Step 7:打通图例联动
|
### Step 8:打通图例联动 ✅️
|
||||||
|
|
||||||
目标:
|
目标:
|
||||||
|
|
||||||
@ -305,7 +337,7 @@
|
|||||||
- 图例单项勾选/取消时,3D 对应类别锚点同步显隐。
|
- 图例单项勾选/取消时,3D 对应类别锚点同步显隐。
|
||||||
- 图例分组勾选/取消时,3D 行为与 2D 保持一致。
|
- 图例分组勾选/取消时,3D 行为与 2D 保持一致。
|
||||||
|
|
||||||
### Step 8:补足收口与稳定性
|
### Step 9:补足收口与稳定性
|
||||||
|
|
||||||
目标:
|
目标:
|
||||||
|
|
||||||
@ -329,7 +361,7 @@
|
|||||||
|
|
||||||
1. 默认定位到中国 + 启动动画
|
1. 默认定位到中国 + 启动动画
|
||||||
2. 展示基础底图
|
2. 展示基础底图
|
||||||
3. 展示锚点
|
3. 展示锚点 3.5. 补基地裁切(jdPanelControlShowAndHidden)
|
||||||
4. 补 hover popup
|
4. 补 hover popup
|
||||||
5. 补缩放驱动显示与碰撞检测
|
5. 补缩放驱动显示与碰撞检测
|
||||||
6. 补锚点点击详情
|
6. 补锚点点击详情
|
||||||
@ -349,6 +381,10 @@
|
|||||||
- `stinfo/stinfo_video_point` 与 `stinfo_ai_video_point` 互斥
|
- `stinfo/stinfo_video_point` 与 `stinfo_ai_video_point` 互斥
|
||||||
- `facilities` 与 `facilities_built` 两个分支互斥
|
- `facilities` 与 `facilities_built` 两个分支互斥
|
||||||
- 点图层取消勾选时只隐藏,不删除缓存;重新勾选时优先从缓存恢复。
|
- 点图层取消勾选时只隐藏,不删除缓存;重新勾选时优先从缓存恢复。
|
||||||
|
- **图层树勾选时按 `type` 字段走不同分支**(见 `updateLayerData`):
|
||||||
|
- `type === 'pointMap'` → 走 `handlePointMapLayer` → 调用 `mdLayerTreeShowOrHidden`(控制锚点显隐)
|
||||||
|
- `type === 'GISMap'` → 走 `controlBaseLayerTreeShowAndHidden`(控制底图/叠加影像图层显隐)
|
||||||
|
- 3D Cesium 必须同时实现这两个方法才能与图层树完整联动。
|
||||||
|
|
||||||
### 9.2 图例与点内显隐
|
### 9.2 图例与点内显隐
|
||||||
|
|
||||||
@ -360,11 +396,16 @@
|
|||||||
|
|
||||||
- `eng_point` 的中型水电站只在 `ENG_POINT_MEDIUM_VISIBLE_ZOOM = 7.5` 以上显示。
|
- `eng_point` 的中型水电站只在 `ENG_POINT_MEDIUM_VISIBLE_ZOOM = 7.5` 以上显示。
|
||||||
- 水电开发页缩放跨过 `12` 级时,会自动补开一批动态图层;缩回去时再自动收口。
|
- 水电开发页缩放跨过 `12` 级时,会自动补开一批动态图层;缩回去时再自动收口。
|
||||||
- `2D` 存在“缩放越大显示越多”的行为,不是所有锚点在所有层级都等量显示。
|
- `2D` 存在"缩放越大显示越多"的行为,不是所有锚点在所有层级都等量显示。
|
||||||
- `2D` 还存在 hover、批量 popup、密度控制等缩放阈值:
|
- `2D` 还存在 hover、批量 popup、密度控制等缩放阈值:
|
||||||
- hover 低于一定缩放不触发
|
- hover 低于一定缩放不触发
|
||||||
- 高缩放会进入批量 popup 模式
|
- 高缩放会进入批量 popup 模式
|
||||||
- 点位会随着缩放提升逐步放开显示限制
|
- 点位会随着缩放提升逐步放开显示限制
|
||||||
|
- **图标动态缩放**:`scale = 0.7 + (zoom - 4.5) * 0.08`,钳位到 `[0.5, 3.0]`。
|
||||||
|
- **字体动态大小**:`fontSize = clamp(12 * scale, 10, 24)`,即 10px~24px 之间随缩放变化。
|
||||||
|
- **文字在图标上方**:2D 中 `text.offsetY = -22 * scale`,负值表示文字偏移到图标上方。
|
||||||
|
- **注意**:上述 2D scale 公式是针对 OpenLayers 2D 投影校准的,Cesium 3D 球面视角需要更高的基准值(建议 1.0 起步),且 `scaleByDistance` 在 9,000km 中国高度处不应过度压缩(建议 near/far 范围设为 1km→2x, 20,000km→0.6x)。
|
||||||
|
- **Cesium 中必须使用 `CallbackProperty` 实现动态缩放**:3D 中 `billboard.scale` 不能是静态值,否则放大后图标不会跟随变大。使用 `CallbackProperty` 每帧重新计算当前 zoom 对应的 scale,与 2D `getDynamicPointScale` 行为对齐。
|
||||||
|
|
||||||
### 9.4 锚点碰撞与控量
|
### 9.4 锚点碰撞与控量
|
||||||
|
|
||||||
@ -395,6 +436,28 @@
|
|||||||
- 鱼类分布模式会接管 `ylfb_point` 的数据、缓存、图例与显隐。
|
- 鱼类分布模式会接管 `ylfb_point` 的数据、缓存、图例与显隐。
|
||||||
- 时间筛选当前只重载指定时间相关图层,不是全量重载。
|
- 时间筛选当前只重载指定时间相关图层,不是全量重载。
|
||||||
|
|
||||||
|
### 9.8 数据进入 addInitDataLayer 前的预处理
|
||||||
|
|
||||||
|
- 所有点位数据在进入 `addInitDataLayer` 前会经过 `normalizePointLayerItems()` 过滤:
|
||||||
|
- 没有 `iconCode` 且 `code !== 'colorLayer'` 的项会被抛弃
|
||||||
|
- `baseId == 'all'` 且 `anchoPointState` 结尾为 `_nbuilt` 的项会被抛弃
|
||||||
|
- `_id` 默认值生成规则:如果项没有 `_id`,会生成 `${sttpMap || iconType || 'point'}_${stcd}`
|
||||||
|
- **stcd+经纬度重复锚点处理**:同一个 layerKey 下,相同 stcd 和经纬度但不同 sttpMap/anchoPointState 的锚点需要全部保留,不能因为 Feature/Entity ID 冲突而被覆盖。2D 使用 `${layerKey}:${_id}` 作为 ID,其中 `_id` 默认为 `${sttpMap}_${stcd}`,天然区分了同 stcd 不同业务类型的点。
|
||||||
|
|
||||||
|
### 9.9 图标渐进加载机制
|
||||||
|
|
||||||
|
- 2D 中图标不是一次性全加载完。有 `loading → loaded → error` 三种状态跟踪。
|
||||||
|
- 只有 `loaded` 状态的图标才参与样式渲染,loader 失败的不渲染。
|
||||||
|
- 如果在图标加载过程中就触发了样式刷新,需要通过 `requestAnimationFrame` 回刷。
|
||||||
|
- 3D Cesium 中如果要实际渲染图标纹理,同样需要这种渐进加载机制,或在实现时先以简单 billboard 色块兜底。
|
||||||
|
|
||||||
|
### 9.10 密度分档与近邻点元数据
|
||||||
|
|
||||||
|
- 每个锚点的 `distance` 字段表示密度分档值(不是物理距离),值越大表示越稀疏。
|
||||||
|
- 不同 density 值对应不同的 `minZoom` 显示门槛(见 `nearby-point-rules.ts` 的 `densityDisplayRules`)。
|
||||||
|
- 全量数据加载后,store 会调用 `attachNearbyPointRuntimeMeta()` 给所有点打上 `_nearbyRule`、`_nearbyGroupId`、`_nearbyPriority` 等元数据。
|
||||||
|
- **孤立点例外规则**:如果一个点在当前视口内 ~56px 范围内没有同 layerKey 的其他可见点,它可以绕过密度门槛直接显示。
|
||||||
|
|
||||||
## 7. 预计修改文件
|
## 7. 预计修改文件
|
||||||
|
|
||||||
- `src/components/gis/map.cesium.ts`
|
- `src/components/gis/map.cesium.ts`
|
||||||
|
|||||||
@ -42,9 +42,6 @@ export const legendData2Obj = (data: any[]) => {
|
|||||||
if (item?.childrenList && item.childrenList?.length > 0) {
|
if (item?.childrenList && item.childrenList?.length > 0) {
|
||||||
f(item.childrenList);
|
f(item.childrenList);
|
||||||
} else {
|
} else {
|
||||||
if (item.nameEn.includes('_测试')) {
|
|
||||||
item.nameEn = item.nameEn.split('_测试')[0];
|
|
||||||
}
|
|
||||||
_tempData[item.nameEn] = item;
|
_tempData[item.nameEn] = item;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -381,7 +381,7 @@ export class MapOl implements MapInterface {
|
|||||||
legendVisible !== false &&
|
legendVisible !== false &&
|
||||||
regionVisible !== false &&
|
regionVisible !== false &&
|
||||||
densityVisible &&
|
densityVisible &&
|
||||||
this.shouldRenderNearbyFeature(feature, currentZoom);
|
this.shouldRenderNearbyFeature();
|
||||||
|
|
||||||
const dynamicScale = this.getDynamicPointScale(currentZoom);
|
const dynamicScale = this.getDynamicPointScale(currentZoom);
|
||||||
const fontSize = this.getPointFontSize(dynamicScale);
|
const fontSize = this.getPointFontSize(dynamicScale);
|
||||||
@ -706,7 +706,7 @@ export class MapOl implements MapInterface {
|
|||||||
legendVisible: feature.get('_legendVisible') !== false,
|
legendVisible: feature.get('_legendVisible') !== false,
|
||||||
regionVisible: feature.get('_regionVisible') !== false,
|
regionVisible: feature.get('_regionVisible') !== false,
|
||||||
densityPriority: this.getFeatureDensityPriority(feature),
|
densityPriority: this.getFeatureDensityPriority(feature),
|
||||||
nearbyVisible: this.shouldRenderNearbyFeature(feature, currentZoom),
|
nearbyVisible: this.shouldRenderNearbyFeature(),
|
||||||
labelText: this.formatPointLabelText(
|
labelText: this.formatPointLabelText(
|
||||||
feature.get('_labelText') as string
|
feature.get('_labelText') as string
|
||||||
),
|
),
|
||||||
@ -1396,7 +1396,27 @@ export class MapOl implements MapInterface {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isFeatureEngAlarm(feature: Feature): boolean {
|
||||||
|
const layerKey = String(
|
||||||
|
feature.get('_layerKey') || feature.get('type') || ''
|
||||||
|
).toLowerCase();
|
||||||
|
const legendState = String(feature.get('anchoPointState') || '')
|
||||||
|
.trim()
|
||||||
|
.toLowerCase();
|
||||||
|
|
||||||
|
return (
|
||||||
|
layerKey.includes('eng_alarm_point') ||
|
||||||
|
layerKey.includes('alarm_range') ||
|
||||||
|
legendState.startsWith('alarm_range_') ||
|
||||||
|
legendState.startsWith('large_eng_built_alarm_range_') ||
|
||||||
|
legendState.startsWith('mid_eng_built_alarm_range_')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private getFeatureEngRenderPriority(feature: Feature): number {
|
private getFeatureEngRenderPriority(feature: Feature): number {
|
||||||
|
if (this.isFeatureEngAlarm(feature)) {
|
||||||
|
return 300;
|
||||||
|
}
|
||||||
return this.isFeatureEng(feature) ? 200 : 100;
|
return this.isFeatureEng(feature) ? 200 : 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1472,52 +1492,10 @@ export class MapOl implements MapInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 统一根据近邻点分组和缩放阶段判断当前要素是否应该参与渲染。
|
* 近邻点统一参与原位堆叠,具体谁显示在顶层交给碰撞排序和图层优先级控制。
|
||||||
*/
|
*/
|
||||||
private shouldRenderNearbyFeature(
|
private shouldRenderNearbyFeature(): boolean {
|
||||||
feature: Feature,
|
return true;
|
||||||
currentZoom: number
|
|
||||||
): boolean {
|
|
||||||
const nearbyGroupId = feature.get('_nearbyGroupId');
|
|
||||||
const groupSize = Number(feature.get('_nearbyGroupSize'));
|
|
||||||
const priority = Number(feature.get('_nearbyPriority'));
|
|
||||||
const showZoomMin = feature.get('_nearbyShowZoomMin');
|
|
||||||
const showZoomMax = feature.get('_nearbyShowZoomMax');
|
|
||||||
const expandAtZoom = feature.get('_nearbyExpandAtZoom');
|
|
||||||
|
|
||||||
if (!nearbyGroupId || !Number.isFinite(groupSize) || groupSize < 2) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Number.isFinite(priority) || priority <= 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
showZoomMin !== null &&
|
|
||||||
showZoomMin !== undefined &&
|
|
||||||
currentZoom < Number(showZoomMin)
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
showZoomMax !== null &&
|
|
||||||
showZoomMax !== undefined &&
|
|
||||||
currentZoom > Number(showZoomMax)
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
expandAtZoom !== null &&
|
|
||||||
expandAtZoom !== undefined &&
|
|
||||||
currentZoom >= Number(expandAtZoom)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return priority === 1;
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 初始化加载基础图层
|
* 初始化加载基础图层
|
||||||
|
|||||||
@ -5,8 +5,6 @@ import VectorLayer from 'ol/layer/Vector';
|
|||||||
import VectorSource from 'ol/source/Vector';
|
import VectorSource from 'ol/source/Vector';
|
||||||
import { fromLonLat } from 'ol/proj';
|
import { fromLonLat } from 'ol/proj';
|
||||||
import { getIconPath } from '@/utils/index';
|
import { getIconPath } from '@/utils/index';
|
||||||
import { getNearbyPointConfig } from '@/modules/map/domain/nearby-point-rules';
|
|
||||||
|
|
||||||
type PointLayerStyleFactory = (feature: Feature) => any;
|
type PointLayerStyleFactory = (feature: Feature) => any;
|
||||||
|
|
||||||
type PointLayerManagerOptions = {
|
type PointLayerManagerOptions = {
|
||||||
@ -80,103 +78,12 @@ export class PointLayerManager {
|
|||||||
updateNearbyFeatureLayout(currentZoom?: number): void {
|
updateNearbyFeatureLayout(currentZoom?: number): void {
|
||||||
if (!this.map) return;
|
if (!this.map) return;
|
||||||
|
|
||||||
const resolution = this.map.getView().getResolution();
|
if (currentZoom === undefined || !Number.isFinite(currentZoom)) {
|
||||||
if (
|
|
||||||
currentZoom === undefined ||
|
|
||||||
!Number.isFinite(currentZoom) ||
|
|
||||||
!resolution
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const nearbyPointConfig = getNearbyPointConfig();
|
|
||||||
const { autoRule, layoutRule } = nearbyPointConfig;
|
|
||||||
|
|
||||||
const groupMap = new Map<string, Feature[]>();
|
|
||||||
|
|
||||||
this.forEachFeature(feature => {
|
this.forEachFeature(feature => {
|
||||||
this.resetFeatureToBaseCoordinate(feature);
|
this.resetFeatureToBaseCoordinate(feature);
|
||||||
|
|
||||||
const groupId = feature.get('_nearbyGroupId');
|
|
||||||
const groupSize = Number(feature.get('_nearbyGroupSize'));
|
|
||||||
if (!groupId || !Number.isFinite(groupSize) || groupSize < 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const groupFeatures = groupMap.get(groupId) || [];
|
|
||||||
groupFeatures.push(feature);
|
|
||||||
groupMap.set(groupId, groupFeatures);
|
|
||||||
});
|
|
||||||
|
|
||||||
groupMap.forEach(features => {
|
|
||||||
const expandAtZoom = Number(features[0]?.get('_nearbyExpandAtZoom'));
|
|
||||||
if (!Number.isFinite(expandAtZoom) || currentZoom < expandAtZoom) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const sortedFeatures = [...features].sort((left, right) => {
|
|
||||||
return (
|
|
||||||
Number(left.get('_nearbyPriority') || 0) -
|
|
||||||
Number(right.get('_nearbyPriority') || 0)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
const secondaryFeatures = sortedFeatures.filter(feature => {
|
|
||||||
const priority = Number(feature.get('_nearbyPriority'));
|
|
||||||
return Number.isFinite(priority) && priority > 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
secondaryFeatures.forEach((feature, index) => {
|
|
||||||
const baseCoordinates = feature.get('_baseCoordinates') as
|
|
||||||
| number[]
|
|
||||||
| undefined;
|
|
||||||
const geometry = feature.getGeometry();
|
|
||||||
if (
|
|
||||||
!baseCoordinates ||
|
|
||||||
baseCoordinates.length < 2 ||
|
|
||||||
!geometry ||
|
|
||||||
geometry.getType() !== 'Point'
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const groupSecondaryCount = secondaryFeatures.length;
|
|
||||||
const baseOffsetPx =
|
|
||||||
Number(feature.get('_nearbyExpandOffsetPx')) ||
|
|
||||||
autoRule.expandOffsetPx;
|
|
||||||
const ringSize = Math.max(1, layoutRule.ringSize);
|
|
||||||
const ringIndex = Math.floor(index / ringSize);
|
|
||||||
const ringOffsetIndex = index % ringSize;
|
|
||||||
const currentRingCount = Math.min(
|
|
||||||
ringSize,
|
|
||||||
groupSecondaryCount - ringIndex * ringSize
|
|
||||||
);
|
|
||||||
const fanAngleDeg =
|
|
||||||
currentRingCount <= 1
|
|
||||||
? 0
|
|
||||||
: currentRingCount <= 2
|
|
||||||
? layoutRule.fanAngleForTwoDeg
|
|
||||||
: currentRingCount <= 4
|
|
||||||
? layoutRule.fanAngleForFourDeg
|
|
||||||
: layoutRule.fanAngleForManyDeg;
|
|
||||||
const angleStepDeg =
|
|
||||||
currentRingCount <= 1 ? 0 : fanAngleDeg / (currentRingCount - 1);
|
|
||||||
const startAngleDeg = -90 - fanAngleDeg / 2;
|
|
||||||
const expandAngleDeg = startAngleDeg + ringOffsetIndex * angleStepDeg;
|
|
||||||
const expandAngleRad = (expandAngleDeg * Math.PI) / 180;
|
|
||||||
const expandOffsetPx =
|
|
||||||
baseOffsetPx +
|
|
||||||
ringIndex *
|
|
||||||
Math.max(
|
|
||||||
layoutRule.ringGapPx,
|
|
||||||
baseOffsetPx * layoutRule.ringGapFactor
|
|
||||||
);
|
|
||||||
const offsetX = Math.cos(expandAngleRad) * expandOffsetPx * resolution;
|
|
||||||
const offsetY = Math.sin(expandAngleRad) * expandOffsetPx * resolution;
|
|
||||||
|
|
||||||
feature.set('_nearbyExpandAngleDeg', expandAngleDeg, true);
|
|
||||||
(geometry as Point).setCoordinates([
|
|
||||||
baseCoordinates[0] + offsetX,
|
|
||||||
baseCoordinates[1] - offsetY
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,7 +299,6 @@ export class PointLayerManager {
|
|||||||
if (!isFinite(coord[0]) || !isFinite(coord[1])) {
|
if (!isFinite(coord[0]) || !isFinite(coord[1])) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let iconUrl = iconCode ? getIconPath(iconCode) : '';
|
let iconUrl = iconCode ? getIconPath(iconCode) : '';
|
||||||
if (!iconUrl) {
|
if (!iconUrl) {
|
||||||
iconUrl = getIconPath('default') || '';
|
iconUrl = getIconPath('default') || '';
|
||||||
@ -403,7 +309,9 @@ export class PointLayerManager {
|
|||||||
...item,
|
...item,
|
||||||
_layerKey: targetLayerKey
|
_layerKey: targetLayerKey
|
||||||
});
|
});
|
||||||
feature.setId(item.stcd || item._id);
|
const rawFeatureId =
|
||||||
|
item.stcd || item._id || `${lon.toFixed(6)},${lat.toFixed(6)}`;
|
||||||
|
feature.setId(`${targetLayerKey}:${rawFeatureId}`);
|
||||||
feature.set('_iconUrl', iconUrl);
|
feature.set('_iconUrl', iconUrl);
|
||||||
feature.set(
|
feature.set(
|
||||||
'_labelText',
|
'_labelText',
|
||||||
@ -457,7 +365,6 @@ export class PointLayerManager {
|
|||||||
|
|
||||||
const valueMap = fieldIndexMap.get(fieldKey);
|
const valueMap = fieldIndexMap.get(fieldKey);
|
||||||
if (!valueMap) return [];
|
if (!valueMap) return [];
|
||||||
|
|
||||||
return valueMap.get(String(matchValue)) || [];
|
return valueMap.get(String(matchValue)) || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div class="map-filter-container">
|
||||||
class="map-filter-container"
|
|
||||||
:style="{ left: uiStore.mapType == '2D' ? '220px' : '20px' }"
|
|
||||||
>
|
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<a-form :model="formModel">
|
<a-form :model="formModel">
|
||||||
<a-row :gutter="10">
|
<a-row :gutter="10">
|
||||||
|
|||||||
@ -88,9 +88,7 @@ const hasEnvFac = computed(() =>
|
|||||||
|
|
||||||
const legendItemClick = (item: any) => {
|
const legendItemClick = (item: any) => {
|
||||||
if (item.parentName == '环保设施') return;
|
if (item.parentName == '环保设施') return;
|
||||||
if (item.canBeChecked == 0) {
|
if (item.canBeChecked == 0) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (item.nameEn) {
|
if (item.nameEn) {
|
||||||
mapOrchestrator.toggleLegend(item.nameEn, item.checked == 0 ? 1 : 0);
|
mapOrchestrator.toggleLegend(item.nameEn, item.checked == 0 ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { useUiStore } from '@/store/modules/ui';
|
|
||||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
const loading = ref(false);
|
|
||||||
const isOpen = ref(true);
|
const isOpen = ref(true);
|
||||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
const uiStore = useUiStore();
|
|
||||||
|
|
||||||
const jidiDataNum = ref(9);
|
const jidiDataNum = ref(9);
|
||||||
const itemClick = (index: any) => {
|
const itemClick = (index: any) => {
|
||||||
@ -15,7 +12,7 @@ onMounted(() => {});
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="jidiSelectorMod" v-if="uiStore.mapType == '2D'">
|
<div class="jidiSelectorMod">
|
||||||
<a-spin :spinning="JidiSelectEventStore.loading">
|
<a-spin :spinning="JidiSelectEventStore.loading">
|
||||||
<div
|
<div
|
||||||
class="qgc-dropdown-select"
|
class="qgc-dropdown-select"
|
||||||
|
|||||||
@ -302,13 +302,11 @@ export const useMapOrchestrator = () => {
|
|||||||
refreshEngPoint: true
|
refreshEngPoint: true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (currentMapType === '2D') {
|
const selectedBaseId = mapViewStore.selectedBaseId;
|
||||||
const selectedBaseId = mapViewStore.selectedBaseId;
|
mapClass.jdPanelControlShowAndHidden(
|
||||||
mapClass.jdPanelControlShowAndHidden(
|
selectedBaseId || 'all',
|
||||||
selectedBaseId || 'all',
|
!!selectedBaseId
|
||||||
!!selectedBaseId
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bindZoomListener(options.getIsHydroMenu);
|
bindZoomListener(options.getIsHydroMenu);
|
||||||
};
|
};
|
||||||
@ -660,12 +658,15 @@ export const useMapOrchestrator = () => {
|
|||||||
// 备注:统一处理单个图例切换命令,供图例组件复用。
|
// 备注:统一处理单个图例切换命令,供图例组件复用。
|
||||||
const toggleLegend = (nameEn: string, checked?: number) => {
|
const toggleLegend = (nameEn: string, checked?: number) => {
|
||||||
if (!nameEn) return;
|
if (!nameEn) return;
|
||||||
|
console.log('toggleLegend', nameEn, checked);
|
||||||
|
console.log(mapViewStore.getLegendChecked(nameEn));
|
||||||
const nextChecked =
|
const nextChecked =
|
||||||
checked === undefined
|
checked === undefined
|
||||||
? mapViewStore.getLegendChecked(nameEn) === 1
|
? mapViewStore.getLegendChecked(nameEn) === 1
|
||||||
? 0
|
? 0
|
||||||
: 1
|
: 1
|
||||||
: checked;
|
: checked;
|
||||||
|
console.log(nextChecked);
|
||||||
mapStore.updateLegendChecked(nameEn, nextChecked);
|
mapStore.updateLegendChecked(nameEn, nextChecked);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -680,12 +681,10 @@ export const useMapOrchestrator = () => {
|
|||||||
const changeBaseId = (baseId: string) => {
|
const changeBaseId = (baseId: string) => {
|
||||||
const nextBaseId = !baseId || baseId === 'all' ? '' : baseId;
|
const nextBaseId = !baseId || baseId === 'all' ? '' : baseId;
|
||||||
mapViewStore.setSelectedBaseId(nextBaseId);
|
mapViewStore.setSelectedBaseId(nextBaseId);
|
||||||
if (currentMapType === '2D') {
|
mapClass.jdPanelControlShowAndHidden(
|
||||||
mapClass.jdPanelControlShowAndHidden(
|
baseId || 'all',
|
||||||
baseId || 'all',
|
baseId !== 'all' && !!baseId
|
||||||
baseId !== 'all' && !!baseId
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 备注:统一处理时间范围变更,并触发相关图层数据重载。
|
// 备注:统一处理时间范围变更,并触发相关图层数据重载。
|
||||||
|
|||||||
@ -74,19 +74,19 @@ export const DEFAULT_NEARBY_POINT_DENSITY_DISPLAY_RULES: NearbyPointDensityDispl
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
minDensityValue: 800000,
|
minDensityValue: 800000,
|
||||||
minZoom: 6.1
|
minZoom: 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
minDensityValue: 400000,
|
minDensityValue: 400000,
|
||||||
minZoom: 8.1
|
minZoom: 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
minDensityValue: 50000,
|
minDensityValue: 50000,
|
||||||
minZoom: 10.6
|
minZoom: 2.0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
minDensityValue: 0,
|
minDensityValue: 0,
|
||||||
minZoom: 12.2
|
minZoom: 4.0
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -121,6 +121,37 @@ type GroupCandidate = {
|
|||||||
layerKey: string;
|
layerKey: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isAlarmRangeLegendState = (legendState: string): boolean => {
|
||||||
|
return (
|
||||||
|
legendState.startsWith('alarm_range_') ||
|
||||||
|
legendState.startsWith('large_eng_built_alarm_range_') ||
|
||||||
|
legendState.startsWith('mid_eng_built_alarm_range_')
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPointRenderPriority = (point: Record<string, any>): number => {
|
||||||
|
const layerKey = String(point.layerKey || point.type || '').toLowerCase();
|
||||||
|
const legendState = String(point.anchoPointState || '').trim().toLowerCase();
|
||||||
|
|
||||||
|
if (
|
||||||
|
layerKey.includes('eng_alarm_point') ||
|
||||||
|
layerKey.includes('alarm_range') ||
|
||||||
|
isAlarmRangeLegendState(legendState)
|
||||||
|
) {
|
||||||
|
return 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
layerKey.includes('eng_point') ||
|
||||||
|
legendState.startsWith('large_eng_') ||
|
||||||
|
legendState.startsWith('mid_eng_')
|
||||||
|
) {
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 100;
|
||||||
|
};
|
||||||
|
|
||||||
const normalizeText = (value: unknown): string => {
|
const normalizeText = (value: unknown): string => {
|
||||||
return String(value ?? '')
|
return String(value ?? '')
|
||||||
.trim()
|
.trim()
|
||||||
@ -207,6 +238,12 @@ const compareGroupCandidates = (
|
|||||||
left: GroupCandidate,
|
left: GroupCandidate,
|
||||||
right: GroupCandidate
|
right: GroupCandidate
|
||||||
): number => {
|
): number => {
|
||||||
|
const leftRenderPriority = getPointRenderPriority(left.point);
|
||||||
|
const rightRenderPriority = getPointRenderPriority(right.point);
|
||||||
|
if (leftRenderPriority !== rightRenderPriority) {
|
||||||
|
return rightRenderPriority - leftRenderPriority;
|
||||||
|
}
|
||||||
|
|
||||||
const leftDensityValue = toFiniteNumber(left.point.distance);
|
const leftDensityValue = toFiniteNumber(left.point.distance);
|
||||||
const rightDensityValue = toFiniteNumber(right.point.distance);
|
const rightDensityValue = toFiniteNumber(right.point.distance);
|
||||||
|
|
||||||
@ -232,21 +269,18 @@ const buildRuntimeMeta = (
|
|||||||
displayName: string,
|
displayName: string,
|
||||||
rule: NearbyPointAutoRule
|
rule: NearbyPointAutoRule
|
||||||
): NearbyPointRuntimeMeta => {
|
): NearbyPointRuntimeMeta => {
|
||||||
const displayMode: NearbyPointDisplayMode =
|
|
||||||
priority === 1 ? 'default' : priority === 2 ? 'replace' : 'expand';
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
groupId,
|
groupId,
|
||||||
displayName,
|
displayName,
|
||||||
groupSize,
|
groupSize,
|
||||||
priority,
|
priority,
|
||||||
showZoomMin: priority === 1 ? null : rule.replaceAtZoom,
|
showZoomMin: null,
|
||||||
showZoomMax: null,
|
showZoomMax: null,
|
||||||
replaceAtZoom: rule.replaceAtZoom,
|
replaceAtZoom: rule.replaceAtZoom,
|
||||||
expandAtZoom: rule.expandAtZoom,
|
expandAtZoom: rule.expandAtZoom,
|
||||||
expandOffsetPx: rule.expandOffsetPx,
|
expandOffsetPx: rule.expandOffsetPx,
|
||||||
expandAngleDeg: priority === 1 ? 0 : (priority - 2) * rule.angleStepDeg,
|
expandAngleDeg: 0,
|
||||||
displayMode
|
displayMode: 'default'
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -279,61 +313,49 @@ const buildGroupCandidates = (
|
|||||||
return candidates;
|
return candidates;
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildLayerScopedNearbyGroups = (
|
const buildNearbyGroups = (
|
||||||
candidates: GroupCandidate[],
|
candidates: GroupCandidate[],
|
||||||
rule: NearbyPointAutoRule
|
rule: NearbyPointAutoRule
|
||||||
): GroupCandidate[][] => {
|
): GroupCandidate[][] => {
|
||||||
const layerScopedCandidates = new Map<string, GroupCandidate[]>();
|
|
||||||
|
|
||||||
candidates.forEach(candidate => {
|
|
||||||
const layerKey = candidate.layerKey || '__empty_layer__';
|
|
||||||
const layerItems = layerScopedCandidates.get(layerKey) || [];
|
|
||||||
layerItems.push(candidate);
|
|
||||||
layerScopedCandidates.set(layerKey, layerItems);
|
|
||||||
});
|
|
||||||
|
|
||||||
const result: GroupCandidate[][] = [];
|
const result: GroupCandidate[][] = [];
|
||||||
|
const sortedCandidates = [...candidates].sort(compareGroupCandidates);
|
||||||
|
const consumedIndexes = new Set<number>();
|
||||||
|
|
||||||
layerScopedCandidates.forEach(layerItems => {
|
sortedCandidates.forEach((seedCandidate, seedIndex) => {
|
||||||
const sortedCandidates = [...layerItems].sort(compareGroupCandidates);
|
if (consumedIndexes.has(seedIndex)) {
|
||||||
const consumedIndexes = new Set<number>();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
sortedCandidates.forEach((seedCandidate, seedIndex) => {
|
const groupItems: GroupCandidate[] = [seedCandidate];
|
||||||
if (consumedIndexes.has(seedIndex)) {
|
const groupIndexes = [seedIndex];
|
||||||
return;
|
|
||||||
|
for (
|
||||||
|
let candidateIndex = seedIndex + 1;
|
||||||
|
candidateIndex < sortedCandidates.length;
|
||||||
|
candidateIndex += 1
|
||||||
|
) {
|
||||||
|
if (consumedIndexes.has(candidateIndex)) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupItems: GroupCandidate[] = [seedCandidate];
|
const currentCandidate = sortedCandidates[candidateIndex];
|
||||||
const groupIndexes = [seedIndex];
|
const distanceMeters = haversineDistanceMeters(
|
||||||
|
seedCandidate.lon,
|
||||||
|
seedCandidate.lat,
|
||||||
|
currentCandidate.lon,
|
||||||
|
currentCandidate.lat
|
||||||
|
);
|
||||||
|
|
||||||
for (
|
if (distanceMeters <= rule.distanceThresholdMeters) {
|
||||||
let candidateIndex = seedIndex + 1;
|
groupItems.push(currentCandidate);
|
||||||
candidateIndex < sortedCandidates.length;
|
groupIndexes.push(candidateIndex);
|
||||||
candidateIndex += 1
|
|
||||||
) {
|
|
||||||
if (consumedIndexes.has(candidateIndex)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentCandidate = sortedCandidates[candidateIndex];
|
|
||||||
const distanceMeters = haversineDistanceMeters(
|
|
||||||
seedCandidate.lon,
|
|
||||||
seedCandidate.lat,
|
|
||||||
currentCandidate.lon,
|
|
||||||
currentCandidate.lat
|
|
||||||
);
|
|
||||||
|
|
||||||
if (distanceMeters <= rule.distanceThresholdMeters) {
|
|
||||||
groupItems.push(currentCandidate);
|
|
||||||
groupIndexes.push(candidateIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (groupItems.length >= rule.minGroupSize) {
|
if (groupItems.length >= rule.minGroupSize) {
|
||||||
groupIndexes.forEach(index => consumedIndexes.add(index));
|
groupIndexes.forEach(index => consumedIndexes.add(index));
|
||||||
result.push(groupItems);
|
result.push(groupItems);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -354,7 +376,7 @@ export const attachNearbyPointRuntimeMeta = (
|
|||||||
if (candidates.length < rule.minGroupSize) {
|
if (candidates.length < rule.minGroupSize) {
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
const groupedCandidates = buildLayerScopedNearbyGroups(candidates, rule);
|
const groupedCandidates = buildNearbyGroups(candidates, rule);
|
||||||
|
|
||||||
let groupSeed = 0;
|
let groupSeed = 0;
|
||||||
groupedCandidates.forEach(groupItems => {
|
groupedCandidates.forEach(groupItems => {
|
||||||
|
|||||||
@ -28,10 +28,9 @@ export const useMapConfigStore = defineStore('map-config', () => {
|
|||||||
const legendLoading = ref(false);
|
const legendLoading = ref(false);
|
||||||
const lastLoadOptions = ref<MapConfigLoadOptions | null>(null);
|
const lastLoadOptions = ref<MapConfigLoadOptions | null>(null);
|
||||||
|
|
||||||
// 备注:统一规范图例 nameEn,避免 `_测试` 后缀影响索引命中。
|
|
||||||
const normalizeLegendNameEn = (nameEn?: string): string => {
|
const normalizeLegendNameEn = (nameEn?: string): string => {
|
||||||
if (!nameEn) return '';
|
if (!nameEn) return '';
|
||||||
return nameEn.includes('_测试') ? nameEn.split('_测试')[0] : nameEn;
|
return nameEn;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 备注:重建图层配置索引,后续按 key 读取图层配置时不再全量递归。
|
// 备注:重建图层配置索引,后续按 key 读取图层配置时不再全量递归。
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import { useMapViewStore } from '@/modules/map/stores/map-view.store';
|
|||||||
import request from '@/utils/request';
|
import request from '@/utils/request';
|
||||||
const mapClass = MapClass.getInstance();
|
const mapClass = MapClass.getInstance();
|
||||||
const ENG_POINT_LAYER_KEY = 'eng_point';
|
const ENG_POINT_LAYER_KEY = 'eng_point';
|
||||||
|
const ENG_ALARM_POINT_LAYER_KEY = 'eng_alarm_point';
|
||||||
const YLFB_POINT_LAYER_KEY = 'ylfb_point';
|
const YLFB_POINT_LAYER_KEY = 'ylfb_point';
|
||||||
const ENG_POINT_MEDIUM_VISIBLE_ZOOM = 7.5;
|
const ENG_POINT_MEDIUM_VISIBLE_ZOOM = 7.5;
|
||||||
const ENG_POINT_LARGE_STATES = [
|
const ENG_POINT_LARGE_STATES = [
|
||||||
@ -29,6 +30,22 @@ const ENG_POINT_MEDIUM_STATES = [
|
|||||||
'mid_eng_nbuilt'
|
'mid_eng_nbuilt'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const isEngPointRuntimeItem = (item: any): boolean => {
|
||||||
|
const runtimeLayerKey = String(
|
||||||
|
item?.layerKey || item?.type || item?._layerKey || ''
|
||||||
|
).toLowerCase();
|
||||||
|
const sttpMap = String(
|
||||||
|
item?._sttpMap || item?.sttpMap || item?.popupSttpMap || ''
|
||||||
|
).toUpperCase();
|
||||||
|
const sttpCode = String(item?.sttpCode || item?.sttp || '').toUpperCase();
|
||||||
|
|
||||||
|
if (runtimeLayerKey) {
|
||||||
|
return runtimeLayerKey.includes(ENG_POINT_LAYER_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sttpMap === 'ENG' || sttpMap === 'ENG2' || sttpCode === 'ENG';
|
||||||
|
};
|
||||||
|
|
||||||
const normalizeCachePayload = (value: any): any => {
|
const normalizeCachePayload = (value: any): any => {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
return value.map(item => normalizeCachePayload(item));
|
return value.map(item => normalizeCachePayload(item));
|
||||||
@ -183,6 +200,10 @@ export const useMapStore = defineStore('map', () => {
|
|||||||
|
|
||||||
const allowMedium = currentZoom > ENG_POINT_MEDIUM_VISIBLE_ZOOM;
|
const allowMedium = currentZoom > ENG_POINT_MEDIUM_VISIBLE_ZOOM;
|
||||||
return sourceData.filter((item: any) => {
|
return sourceData.filter((item: any) => {
|
||||||
|
if (!isEngPointRuntimeItem(item)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const state = String(item?.anchoPointState || '');
|
const state = String(item?.anchoPointState || '');
|
||||||
if (ENG_POINT_LARGE_STATES.includes(state)) {
|
if (ENG_POINT_LARGE_STATES.includes(state)) {
|
||||||
return true;
|
return true;
|
||||||
@ -985,14 +1006,21 @@ export const useMapStore = defineStore('map', () => {
|
|||||||
layerKey: string,
|
layerKey: string,
|
||||||
nameEn: string
|
nameEn: string
|
||||||
): string[] => {
|
): string[] => {
|
||||||
void layerKey;
|
|
||||||
const normalizedNameEn = normalizeLegendNameEn(nameEn);
|
const normalizedNameEn = normalizeLegendNameEn(nameEn);
|
||||||
if (!normalizedNameEn) {
|
if (!normalizedNameEn) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (normalizedNameEn.includes('alarm_range_')) {
|
if (normalizedNameEn.includes('alarm_range_')) {
|
||||||
return [`large_eng_built_${normalizedNameEn}`];
|
const candidates: string[] = [];
|
||||||
|
if (layerKey === ENG_ALARM_POINT_LAYER_KEY) {
|
||||||
|
candidates.push(`mid_eng_built_${normalizedNameEn}`);
|
||||||
|
candidates.push(`large_eng_built_${normalizedNameEn}`);
|
||||||
|
} else {
|
||||||
|
candidates.push(`large_eng_built_${normalizedNameEn}`);
|
||||||
|
candidates.push(`mid_eng_built_${normalizedNameEn}`);
|
||||||
|
}
|
||||||
|
return candidates;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [normalizedNameEn];
|
return [normalizedNameEn];
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<!-- d:\wordpack\WholeProcessPlatform\frontend\src\views\system\map\components\LegendData\index.vue -->
|
<!-- d:\wordpack\WholeProcessPlatform\frontend\src\views\system\map\componentsData\index.vue -->
|
||||||
<template>
|
<template>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<LegendDataSearch
|
<LegendDataSearch
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user