3d基础模块添加,和地图接口加排序修改
This commit is contained in:
parent
abfa77f645
commit
6e5dc9188d
@ -240,7 +240,7 @@
|
|||||||
- 快速切换基地不会出现旧数据覆盖新数据的问题。
|
- 快速切换基地不会出现旧数据覆盖新数据的问题。
|
||||||
- 多次切换 2D/3D 后基地裁切行为正常。
|
- 多次切换 2D/3D 后基地裁切行为正常。
|
||||||
|
|
||||||
### Step 5:打通 3D popup 与 hover
|
### Step 5:打通 3D popup 与 hover ✅️
|
||||||
|
|
||||||
目标:
|
目标:
|
||||||
|
|
||||||
@ -337,7 +337,7 @@
|
|||||||
- 图例单项勾选/取消时,3D 对应类别锚点同步显隐。
|
- 图例单项勾选/取消时,3D 对应类别锚点同步显隐。
|
||||||
- 图例分组勾选/取消时,3D 行为与 2D 保持一致。
|
- 图例分组勾选/取消时,3D 行为与 2D 保持一致。
|
||||||
|
|
||||||
### Step 9:补足收口与稳定性
|
### Step 9:补足收口与稳定性 ✅️
|
||||||
|
|
||||||
目标:
|
目标:
|
||||||
|
|
||||||
@ -369,95 +369,6 @@
|
|||||||
8. 补图例联动
|
8. 补图例联动
|
||||||
9. 做切换稳定性收口
|
9. 做切换稳定性收口
|
||||||
|
|
||||||
## 9. 必须补齐的隐藏逻辑清单
|
|
||||||
|
|
||||||
下面这些逻辑不是表面接口能直接看出来的,但如果 `3D` 不补,最终效果就会和 `2D` 明显不一致。
|
|
||||||
|
|
||||||
### 9.1 图层与菜单切换
|
|
||||||
|
|
||||||
- 菜单切换时,只隐藏旧页点图层,不隐藏底图。
|
|
||||||
- 图层树勾选前要先经过互斥规则归一化:
|
|
||||||
- `rare_fish_point` 与 `fish_along_point` 互斥
|
|
||||||
- `stinfo/stinfo_video_point` 与 `stinfo_ai_video_point` 互斥
|
|
||||||
- `facilities` 与 `facilities_built` 两个分支互斥
|
|
||||||
- 点图层取消勾选时只隐藏,不删除缓存;重新勾选时优先从缓存恢复。
|
|
||||||
- **图层树勾选时按 `type` 字段走不同分支**(见 `updateLayerData`):
|
|
||||||
- `type === 'pointMap'` → 走 `handlePointMapLayer` → 调用 `mdLayerTreeShowOrHidden`(控制锚点显隐)
|
|
||||||
- `type === 'GISMap'` → 走 `controlBaseLayerTreeShowAndHidden`(控制底图/叠加影像图层显隐)
|
|
||||||
- 3D Cesium 必须同时实现这两个方法才能与图层树完整联动。
|
|
||||||
|
|
||||||
### 9.2 图例与点内显隐
|
|
||||||
|
|
||||||
- 图例控制的是图层内部锚点是否可见,不是图层本身是否可见。
|
|
||||||
- 图层恢复显示时,图例要先整层全关,再按当前 checked 状态逐项恢复。
|
|
||||||
- 环保设施图例组当前是只展示不交互。
|
|
||||||
|
|
||||||
### 9.3 缩放驱动显示
|
|
||||||
|
|
||||||
- `eng_point` 的中型水电站只在 `ENG_POINT_MEDIUM_VISIBLE_ZOOM = 7.5` 以上显示。
|
|
||||||
- 水电开发页缩放跨过 `12` 级时,会自动补开一批动态图层;缩回去时再自动收口。
|
|
||||||
- `2D` 存在"缩放越大显示越多"的行为,不是所有锚点在所有层级都等量显示。
|
|
||||||
- `2D` 还存在 hover、批量 popup、密度控制等缩放阈值:
|
|
||||||
- hover 低于一定缩放不触发
|
|
||||||
- 高缩放会进入批量 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 锚点碰撞与控量
|
|
||||||
|
|
||||||
- `2D` 中锚点是否可见,不只受图层开关控制,还受以下门闩共同影响:
|
|
||||||
- 图例可见性
|
|
||||||
- 区域裁切可见性
|
|
||||||
- 密度阈值
|
|
||||||
- 图标碰撞结果
|
|
||||||
- 标签碰撞结果
|
|
||||||
- 低缩放下,为避免堆叠,部分锚点或标签会被主动隐藏。
|
|
||||||
- 高缩放下,会逐步放开图标/标签限制,显示更多内容。
|
|
||||||
- 因此 `3D` 不能简单把所有 entity 永久全开,否则视觉密度会明显失控。
|
|
||||||
|
|
||||||
### 9.5 Popup 与点击
|
|
||||||
|
|
||||||
- hover popup 不是全时开启的,有缩放门槛。
|
|
||||||
- 点击和 hover 的命中对象是图标本体,不是任意文字区域。
|
|
||||||
- `ylfb` 与普通锚点的点击详情弹框走不同业务分支。
|
|
||||||
|
|
||||||
### 9.6 基地与区域裁切
|
|
||||||
|
|
||||||
- 基地切换不仅影响搜索候选,还会影响地图上的点位可见范围。
|
|
||||||
- 区域裁切会直接把区域外点位隐藏,不是只改底图遮罩。
|
|
||||||
|
|
||||||
### 9.7 筛选器特殊逻辑
|
|
||||||
|
|
||||||
- 装机容量筛选本质是批量切 `eng_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`
|
||||||
@ -478,3 +389,91 @@
|
|||||||
- 默认飞到中国
|
- 默认飞到中国
|
||||||
|
|
||||||
这一步最小、最容易验证,也能先把“打开 3D 看起来不对”的核心问题解决掉。
|
这一步最小、最容易验证,也能先把“打开 3D 看起来不对”的核心问题解决掉。
|
||||||
|
|
||||||
|
## 9. 必须补齐的隐藏逻辑清单
|
||||||
|
|
||||||
|
下面这些逻辑不是表面接口能直接看出来的,但如果 `3D` 不补,最终效果就会和 `2D` 明显不一致。
|
||||||
|
|
||||||
|
### 9.1 图层与菜单切换 ✅️
|
||||||
|
|
||||||
|
- ✅ 菜单切换时,只隐藏旧页点图层,不隐藏底图。(编排器 `handlePageChange` 控制,Cesium 被动接收数据)
|
||||||
|
- ✅ 图层树勾选前要先经过互斥规则归一化:(store 层 `updateLayerData` 处理,Cesium 不感知)
|
||||||
|
- `rare_fish_point` 与 `fish_along_point` 互斥
|
||||||
|
- `stinfo/stinfo_video_point` 与 `stinfo_ai_video_point` 互斥
|
||||||
|
- `facilities` 与 `facilities_built` 两个分支互斥
|
||||||
|
- ✅ 点图层取消勾选时只隐藏,不删除缓存;重新勾选时优先从缓存恢复。`mdLayerTreeShowOrHidden` 设置 `_layerVisible` 标志,不删除 `pointLayerRegistry` 条目。
|
||||||
|
- ✅ **图层树勾选时按 `type` 字段走不同分支**(见 `updateLayerData`,store 层处理):
|
||||||
|
- `type === 'pointMap'` → 走 `handlePointMapLayer` → 调用 `mdLayerTreeShowOrHidden`(已实现,L1624)
|
||||||
|
- `type === 'GISMap'` → 走 `controlBaseLayerTreeShowAndHidden`(已实现,L1345)
|
||||||
|
- 3D Cesium 已同时实现这两个方法。
|
||||||
|
|
||||||
|
### 9.2 图例与点内显隐 ✅️
|
||||||
|
|
||||||
|
- ✅ 图例控制的是图层内部锚点是否可见,不是图层本身是否可见。`setLegendPointVisible` 使用 `_legendVisible` 标志,与 `_layerVisible` 独立。
|
||||||
|
- ✅ 图层恢复显示时,图例要先整层全关,再按当前 checked 状态逐项恢复。支持 `anchoPointState=''` 全关 → 逐项 `(state, true)` 恢复两步模式(L1764-1787)。
|
||||||
|
- ✅ 环保设施图例组当前是只展示不交互。(编排器层面控制,Cesium 不感知)
|
||||||
|
|
||||||
|
### 9.3 缩放驱动显示 ✅️
|
||||||
|
|
||||||
|
- ✅ `eng_point` 的中型水电站只在 `ENG_POINT_MEDIUM_VISIBLE_ZOOM = 7.5` 以上显示。(store `filterPointLayerDataForDisplay` 在数据进入 Cesium 前过滤)
|
||||||
|
- ✅ 水电开发页缩放跨过 `12` 级时,会自动补开一批动态图层;缩回去时再自动收口。(编排器层面处理)
|
||||||
|
- ✅ `2D` 存在"缩放越大显示越多"的行为。碰撞检测中密度门控逐级放开。
|
||||||
|
- ✅ `2D` 还存在 hover、批量 popup、密度控制等缩放阈值:
|
||||||
|
- hover 低于 ~zoom 4.5 不触发(`HOVER_POPUP_MAX_HEIGHT = 9_000_000`)
|
||||||
|
- 高缩放进入批量 popup 模式(`BATCH_POPUP_MODE_ZOOM = 14.9`)
|
||||||
|
- 点位随着缩放提升逐步放开密度限制(密度门控 + `DENSITY_ISOLATION_THRESHOLD`)
|
||||||
|
- ✅ **图标动态缩放**:`CallbackProperty` 实现 `billboard.scale`,公式 `clamp(0.7 + (zoom - 4.5) * 0.035, 0.4, 1.2)`(L1522)。
|
||||||
|
- ✅ **字体动态大小**:`CallbackProperty` 实现 `label.font`,钳位 `[10, 20]`(L1535)。
|
||||||
|
- ✅ **文字在图标上方**:`CallbackProperty` 实现 `label.pixelOffset`,负 Y 偏移(L1549)。
|
||||||
|
- ✅ **Cesium 中必须使用 `CallbackProperty` 实现动态缩放**:已使用 3 个 `CallbackProperty`(scale/font/offset),`isConstant=false` 每帧重算。
|
||||||
|
|
||||||
|
### 9.4 锚点碰撞与控量 ✅️
|
||||||
|
|
||||||
|
- ✅ `2D` 中锚点是否可见,不只受图层开关控制,还受以下门闩共同影响:
|
||||||
|
- 图例可见性 `_legendVisible`
|
||||||
|
- 区域裁切可见性 `_regionVisible`
|
||||||
|
- 密度阈值(碰撞循环中 inline 计算,逐级放开)
|
||||||
|
- 图标碰撞结果 `_iconCollisionVisible`
|
||||||
|
- 标签碰撞结果 `_labelCollisionVisible`
|
||||||
|
- ✅ 低缩放下,为避免堆叠,部分锚点或标签会被主动隐藏。(`refreshPointLabelVisibility` 碰撞检测,L2117-2455)
|
||||||
|
- ✅ 高缩放下,会逐步放开图标/标签限制,显示更多内容。(密度门控 + 碰撞容差随 zoom 调整)
|
||||||
|
- ✅ 因此 `3D` 不能简单把所有 entity 永久全开。所有 entity 初始 `_iconCollisionVisible = _labelCollisionVisible = true`,碰撞检测运行后动态调整。
|
||||||
|
|
||||||
|
### 9.5 Popup 与点击 ✅️
|
||||||
|
|
||||||
|
- ✅ hover popup 不是全时开启的,有缩放门槛。相机高度 > `HOVER_POPUP_MAX_HEIGHT` 时不触发 hover。
|
||||||
|
- ✅ 点击和 hover 的命中对象是图标本体,不是任意文字区域。`ScreenSpaceEventHandler` 基于 `scene.pick` 命中 entity。
|
||||||
|
- ✅ `ylfb` 与普通锚点的点击详情弹框走不同业务分支。`sttpMap === 'ylfb'` 打开 `ylfbModalVisible`,其他走 `modalVisible`(L366-375)。
|
||||||
|
|
||||||
|
### 9.6 基地与区域裁切 ✅️
|
||||||
|
|
||||||
|
- ✅ 基地切换不仅影响搜索候选,还会影响地图上的点位可见范围。`jdPanelControlShowAndHidden` 实现(L1684-1748)。
|
||||||
|
- ✅ 区域裁切会直接把区域外点位隐藏,不是只改底图遮罩。`filterPointsByRegion` 设置 `_regionVisible = false`(L1824-1848),遮罩多边形 + 裁剪双重处理。
|
||||||
|
|
||||||
|
### 9.7 筛选器特殊逻辑 ✅️
|
||||||
|
|
||||||
|
- ✅ 装机容量筛选本质是批量切 `eng_point` 图例状态,不是简单切图层。(store 层处理,Cesium 通过 `setLegendPointVisible` 响应)
|
||||||
|
- ✅ 鱼类分布模式会接管 `ylfb_point` 的数据、缓存、图例与显隐。(store 层处理)
|
||||||
|
- ✅ 时间筛选当前只重载指定时间相关图层,不是全量重载。(store 层处理)
|
||||||
|
|
||||||
|
### 9.8 数据进入 addInitDataLayer 前的预处理 ✅️
|
||||||
|
|
||||||
|
- ✅ 所有点位数据在进入 `addInitDataLayer` 前会经过 `normalizePointLayerItems()` 过滤:(store 层 `map.ts` 执行,Cesium 接收时已过滤)
|
||||||
|
- 没有 `iconCode` 且 `code !== 'colorLayer'` 的项会被抛弃
|
||||||
|
- `baseId == 'all'` 且 `anchoPointState` 结尾为 `_nbuilt` 的项会被抛弃
|
||||||
|
- ✅ `_id` 默认值生成规则:store 层生成 `${sttpMap || iconType || 'point'}_${stcd}`。
|
||||||
|
- ✅ **stcd+经纬度重复锚点处理**:Cesium entity ID 使用 `${layerKey}:${item._id}`,与 2D 对齐,同 stcd 不同 sttpMap 的点不会互相覆盖。
|
||||||
|
|
||||||
|
### 9.9 图标渐进加载机制
|
||||||
|
|
||||||
|
- ❌ 2D 中图标不是一次性全加载完。有 `loading → loaded → error` 三种状态跟踪。
|
||||||
|
- ❌ 只有 `loaded` 状态的图标才参与样式渲染,loader 失败的不渲染。
|
||||||
|
- ❌ 如果在图标加载过程中就触发了样式刷新,需要通过 `requestAnimationFrame` 回刷。
|
||||||
|
- ❌ 3D Cesium 中当前所有 entity 同步创建,billboard `image` URL 直接赋值,无渐进加载。Cesium 内部纹理加载无显式状态跟踪。
|
||||||
|
|
||||||
|
### 9.10 密度分档与近邻点元数据 ✅️
|
||||||
|
|
||||||
|
- ✅ 每个锚点的 `distance` 字段表示密度分档值(不是物理距离),值越大表示越稀疏。(已使用 `getEntityDensityValue` 从 `_rawData.distance` 读取,L2460)
|
||||||
|
- ✅ 不同 density 值对应不同的 `minZoom` 显示门槛。`getEntityDensityMinZoom` 映射 `nearby-point-rules.ts` 的 `densityDisplayRules`(L2491)。
|
||||||
|
- ✅ 全量数据加载后,store 会调用 `attachNearbyPointRuntimeMeta()` 给所有点打上 `_nearbyRule`、`_nearbyGroupId`、`_nearbyPriority` 等元数据。(store 层处理,Cesium 碰撞检测中读取)
|
||||||
|
- ✅ **孤立点例外规则**:如果一个点在当前视口内 ~56px 范围内没有同 layerKey 的其他可见点,它可以绕过密度门槛直接显示。`DENSITY_ISOLATION_THRESHOLD = 56` + `shouldBypassDensityGateForIsolatedEntity`(L2506-2556)。
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -122,19 +122,48 @@ const normalizeRequestParams = (rawParams: any) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseAnchorParamFilters = (anchorParamJson?: string) => {
|
const parseAnchorParamJson = (anchorParamJson?: string) => {
|
||||||
if (!anchorParamJson) return [];
|
const result: { filters: any[]; orders: any[] | null } = {
|
||||||
|
filters: [],
|
||||||
|
orders: null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!anchorParamJson) return result;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parsedParams = JSON.parse(anchorParamJson);
|
const parsed = JSON.parse(anchorParamJson);
|
||||||
if (!parsedParams || typeof parsedParams !== 'object') {
|
if (!parsed || typeof parsed !== 'object') return result;
|
||||||
return [];
|
|
||||||
|
// 解析 params -> filters(兼容旧格式:如果没有 params 字段,整个对象即为 params)
|
||||||
|
const paramsObj = parsed.params || parsed;
|
||||||
|
if (
|
||||||
|
paramsObj &&
|
||||||
|
typeof paramsObj === 'object' &&
|
||||||
|
!Array.isArray(paramsObj)
|
||||||
|
) {
|
||||||
|
result.filters = buildFiltersFromParamsObject(paramsObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildFiltersFromParamsObject(parsedParams);
|
// 解析 orders -> sort 数组
|
||||||
|
if (parsed.orders) {
|
||||||
|
let ordersObj: Record<string, any> = {};
|
||||||
|
if (typeof parsed.orders === 'string') {
|
||||||
|
ordersObj = JSON.parse(parsed.orders);
|
||||||
|
} else if (typeof parsed.orders === 'object') {
|
||||||
|
ordersObj = parsed.orders;
|
||||||
|
}
|
||||||
|
if (ordersObj && typeof ordersObj === 'object') {
|
||||||
|
result.orders = Object.entries(ordersObj).map(([field, dir]) => ({
|
||||||
|
field,
|
||||||
|
dir
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('解析 anchorParamJson 失败:', error);
|
console.error('解析 anchorParamJson 失败:', error);
|
||||||
return [];
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1158,9 +1187,12 @@ export const useMapStore = defineStore('map', () => {
|
|||||||
|
|
||||||
requestParams = normalizeRequestParams(requestParams);
|
requestParams = normalizeRequestParams(requestParams);
|
||||||
|
|
||||||
const anchorParamFilters = parseAnchorParamFilters(anchorParamJson);
|
const anchorParamResult = parseAnchorParamJson(anchorParamJson);
|
||||||
if (anchorParamFilters.length) {
|
if (anchorParamResult.filters.length) {
|
||||||
requestParams.filters.push(...anchorParamFilters);
|
requestParams.filters.push(...anchorParamResult.filters);
|
||||||
|
}
|
||||||
|
if (anchorParamResult.orders) {
|
||||||
|
requestOrders = anchorParamResult.orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeRangeLayerKeys.includes(layerKey)) {
|
if (timeRangeLayerKeys.includes(layerKey)) {
|
||||||
@ -1194,7 +1226,7 @@ export const useMapStore = defineStore('map', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layer?.orders) {
|
if (!requestOrders && layer?.orders) {
|
||||||
try {
|
try {
|
||||||
const ordersObj = JSON.parse(layer.orders);
|
const ordersObj = JSON.parse(layer.orders);
|
||||||
const ordersArray = Object.entries(ordersObj).map(([field, dir]) => ({
|
const ordersArray = Object.entries(ordersObj).map(([field, dir]) => ({
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user