Zzc 536b00fab4 feat(situational-awareness): 添加双地图对比和增强视频监控功能
新增支持双地图对比模式,显示灾害前后场景,
新的视频模态框用于全屏监控并带有方向控制,
位置面板显示地理信息,
地图工具提示显示实体详情,以及用于3D瓦片管理的可组合组件,
地图标记和模型对比功能。包括新的共享组件
如DecorativePanel和MapTooltip,以及Cesium数据
和模型对比设置的配置文件。
2025-11-18 21:24:31 +08:00

177 lines
6.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ref } from 'vue'
import * as Cesium from 'cesium'
import { getModelCompareConfig } from '../config/modelCompare.config'
/**
* 3D Tiles加载管理 Composable
* 负责加载灾前/灾后3D模型数据并支持分屏对比功能
*/
export function use3DTiles() {
const beforeTileset = ref(null)
const afterTileset = ref(null)
/**
* 加载3D Tileset
* @param {Cesium.Viewer} viewer - Cesium Viewer 实例
* @param {string} sceneType - 场景类型:'before' 或 'after'
* @param {boolean} autoZoom - 是否自动缩放到模型默认false保持用户设置的相机位置
* @param {Cesium.SplitDirection} splitDirection - 分割方向(默认为 NONE
*/
const load3DTileset = async (viewer, sceneType = 'after', autoZoom = false, splitDirection = Cesium.SplitDirection.NONE) => {
if (!viewer) return null
try {
console.log(`[use3DTiles] 正在加载${sceneType === 'after' ? '灾后' : '灾前'}3D模型...`)
// 从配置中获取 URL
const config = getModelCompareConfig()
const tilesetConfig = sceneType === 'after' ? config.after3DTiles : config.before3DTiles
const url = tilesetConfig.url
const tileset = await Cesium.Cesium3DTileset.fromUrl(url, {
skipLevelOfDetail: true,
baseScreenSpaceError: 100,
skipScreenSpaceErrorFactor: 16,
skipLevels: 1,
immediatelyLoadDesiredLevelOfDetail: false,
loadSiblings: false,
maximumScreenSpaceError: 16.0, // 进一步增大最大限度减少瓦片细化之前是8.0
dynamicScreenSpaceError: false, // 禁用动态屏幕空间误差调整
dynamicScreenSpaceErrorDensity: 0, // 禁用密度调整
dynamicScreenSpaceErrorFactor: 1, // 禁用动态因子
foveatedScreenSpaceError: false, // 禁用视锥细化
foveatedConeSize: 0.1, // 减小视锥大小
foveatedMinimumScreenSpaceErrorRelaxation: 0 // 禁用放松
})
// 将tileset添加到viewer的primitives
viewer.scene.primitives.add(tileset)
// 设置splitDirection用于对比模式
tileset.splitDirection = splitDirection
if (sceneType === 'after') {
afterTileset.value = tileset
} else {
beforeTileset.value = tileset
}
console.log(`[use3DTiles] ${sceneType === 'after' ? '灾后' : '灾前'}3D模型加载成功splitDirection: ${splitDirection}`)
// 只有明确要求时才自动缩放到tileset
if (autoZoom) {
await viewer.zoomTo(tileset)
}
return tileset
} catch (error) {
console.error(`[use3DTiles] 加载${sceneType === 'after' ? '灾后' : '灾前'}3D模型失败:`, error)
return null
}
}
/**
* 等待 Tileset 完全就绪(包括首批瓦片加载)
*
* 这个函数确保:
* 1. Tileset 本身已就绪readyPromise
* 2. 初始视图的所有瓦片已加载完成initialTilesLoaded
*
* @param {Cesium.Cesium3DTileset} tileset - 要等待的 Tileset
* @param {number} timeout - 超时时间毫秒默认10秒
* @returns {Promise<void>}
*/
const waitForTilesetReady = async (tileset, timeout = 10000) => {
if (!tileset) {
console.warn('[use3DTiles] waitForTilesetReady: tileset 为空')
return
}
try {
// 步骤1等待 Tileset 基础就绪
await tileset.readyPromise
console.log('[use3DTiles] Tileset readyPromise 已完成')
// 步骤2等待初始瓦片加载完成带超时
console.log('[use3DTiles] 等待初始瓦片加载...')
await Promise.race([
// 等待initialTilesLoaded事件
new Promise((resolve) => {
const handleInitialTilesLoaded = () => {
console.log('[use3DTiles] 初始瓦片加载完成(事件触发)')
tileset.initialTilesLoaded.removeEventListener(handleInitialTilesLoaded)
resolve()
}
tileset.initialTilesLoaded.addEventListener(handleInitialTilesLoaded)
// 如果已经加载完成可能事件已经触发过了直接resolve
// 通过检查tileset.tilesLoaded来判断
if (tileset.tilesLoaded) {
console.log('[use3DTiles] 瓦片已加载,直接继续')
tileset.initialTilesLoaded.removeEventListener(handleInitialTilesLoaded)
resolve()
}
}),
// 超时机制
new Promise((resolve) => {
setTimeout(() => {
console.warn(`[use3DTiles] 等待瓦片加载超时(${timeout}ms继续执行`)
resolve()
}, timeout)
})
])
console.log('[use3DTiles] Tileset 已完全就绪')
} catch (error) {
console.error('[use3DTiles] 等待 Tileset 就绪失败:', error)
// 即使失败也不抛出异常,允许程序继续执行
}
}
/**
* 移除3D Tileset
*/
const remove3DTileset = (viewer, sceneType) => {
if (!viewer) return
const tileset = sceneType === 'after' ? afterTileset.value : beforeTileset.value
if (tileset) {
viewer.scene.primitives.remove(tileset)
if (sceneType === 'after') {
afterTileset.value = null
} else {
beforeTileset.value = null
}
console.log(`[use3DTiles] ${sceneType === 'after' ? '灾后' : '灾前'}3D模型已移除`)
}
}
/**
* 更新 Tileset 的 splitDirection
* @param {string} sceneType - 场景类型:'before' 或 'after'
* @param {Cesium.SplitDirection} splitDirection - 新的分割方向
*/
const updateTilesetSplitDirection = (sceneType, splitDirection) => {
const tileset = sceneType === 'after' ? afterTileset.value : beforeTileset.value
if (tileset) {
tileset.splitDirection = splitDirection
console.log(`[use3DTiles] ${sceneType === 'after' ? '灾后' : '灾前'}3D模型 splitDirection 已更新为: ${splitDirection}`)
} else {
console.warn(`[use3DTiles] ${sceneType === 'after' ? '灾后' : '灾前'}3D模型不存在无法更新 splitDirection`)
}
}
return {
beforeTileset,
afterTileset,
load3DTileset,
waitForTilesetReady,
remove3DTileset,
updateTilesetSplitDirection
}
}
export default use3DTiles