新增支持双地图对比模式,显示灾害前后场景, 新的视频模态框用于全屏监控并带有方向控制, 位置面板显示地理信息, 地图工具提示显示实体详情,以及用于3D瓦片管理的可组合组件, 地图标记和模型对比功能。包括新的共享组件 如DecorativePanel和MapTooltip,以及Cesium数据 和模型对比设置的配置文件。
259 lines
7.3 KiB
JavaScript
259 lines
7.3 KiB
JavaScript
import { ref, onUnmounted } from 'vue'
|
||
import * as Cesium from 'cesium'
|
||
import { use3DTiles } from './use3DTiles'
|
||
|
||
/**
|
||
* 双地图对比模式
|
||
* 使用两个独立的Cesium Viewer实现并排对比
|
||
* 左侧显示灾前场景,右侧显示灾后场景
|
||
*/
|
||
export function useDualMapCompare() {
|
||
/** 左侧Viewer引用 */
|
||
const leftViewer = ref(null)
|
||
|
||
/** 右侧Viewer引用 */
|
||
const rightViewer = ref(null)
|
||
|
||
/** 对比模式是否激活 */
|
||
const isCompareMode = ref(false)
|
||
|
||
/** 相机同步监听器移除函数 */
|
||
let cameraSyncRemover = null
|
||
|
||
/** 左侧3D Tileset(灾前) */
|
||
let leftTileset = null
|
||
|
||
/** 右侧3D Tileset(灾后,主地图的tileset) */
|
||
let rightTileset = null
|
||
|
||
const { load3DTileset } = use3DTiles()
|
||
|
||
/**
|
||
* 初始化左侧Viewer(灾前场景)
|
||
* @param {HTMLElement} container - 容器元素
|
||
* @returns {Cesium.Viewer}
|
||
*/
|
||
const initLeftViewer = (container) => {
|
||
if (!container) {
|
||
console.error('[useDualMapCompare] 左侧容器元素不存在')
|
||
return null
|
||
}
|
||
|
||
// 创建左侧viewer
|
||
const viewer = new Cesium.Viewer(container, {
|
||
animation: false,
|
||
baseLayerPicker: false,
|
||
fullscreenButton: false,
|
||
geocoder: false,
|
||
homeButton: false,
|
||
infoBox: false,
|
||
sceneModePicker: false,
|
||
selectionIndicator: false,
|
||
timeline: false,
|
||
navigationHelpButton: false,
|
||
scene3DOnly: true,
|
||
shouldAnimate: false,
|
||
})
|
||
|
||
// 移除默认的Cesium logo和版权信息
|
||
viewer.cesiumWidget.creditContainer.style.display = 'none'
|
||
|
||
leftViewer.value = viewer
|
||
console.log('[useDualMapCompare] 左侧Viewer初始化成功')
|
||
|
||
return viewer
|
||
}
|
||
|
||
/**
|
||
* 设置相机同步
|
||
* 右侧相机移动时,左侧相机跟随
|
||
* @param {Cesium.Viewer} rightViewerInstance - 右侧Viewer(主地图)
|
||
* @param {Cesium.Viewer} leftViewerInstance - 左侧Viewer(对比地图)
|
||
*/
|
||
const setupCameraSync = (rightViewerInstance, leftViewerInstance) => {
|
||
if (!rightViewerInstance || !leftViewerInstance) {
|
||
console.warn('[useDualMapCompare] Viewer未初始化,无法设置相机同步')
|
||
return
|
||
}
|
||
|
||
console.log('[useDualMapCompare] 设置相机同步(右侧主地图 → 左侧对比地图)...')
|
||
|
||
// 监听右侧相机变化
|
||
const syncCamera = () => {
|
||
if (!leftViewerInstance || leftViewerInstance.isDestroyed()) return
|
||
|
||
const rightCamera = rightViewerInstance.camera
|
||
const leftCamera = leftViewerInstance.camera
|
||
|
||
// 同步位置和方向
|
||
leftCamera.setView({
|
||
destination: rightCamera.position.clone(),
|
||
orientation: {
|
||
heading: rightCamera.heading,
|
||
pitch: rightCamera.pitch,
|
||
roll: rightCamera.roll
|
||
}
|
||
})
|
||
}
|
||
|
||
// 添加监听器
|
||
rightViewerInstance.camera.changed.addEventListener(syncCamera)
|
||
|
||
// 保存移除函数
|
||
cameraSyncRemover = () => {
|
||
rightViewerInstance.camera.changed.removeEventListener(syncCamera)
|
||
console.log('[useDualMapCompare] 相机同步已移除')
|
||
}
|
||
|
||
console.log('[useDualMapCompare] 相机同步设置完成')
|
||
}
|
||
|
||
/**
|
||
* 启用对比模式
|
||
* @param {Cesium.Viewer} rightViewerInstance - 右侧Viewer实例(主地图,灾后场景)
|
||
*/
|
||
const enableCompareMode = async (rightViewerInstance) => {
|
||
if (!rightViewerInstance) {
|
||
console.error('[useDualMapCompare] 右侧主地图Viewer未初始化')
|
||
return
|
||
}
|
||
|
||
console.log('[useDualMapCompare] 启用对比模式...')
|
||
|
||
rightViewer.value = rightViewerInstance
|
||
|
||
// 查找左侧容器(容器已存在于DOM中)
|
||
const leftContainer = document.getElementById('leftCesiumContainer')
|
||
if (!leftContainer) {
|
||
console.error('[useDualMapCompare] 找不到左侧容器元素')
|
||
return
|
||
}
|
||
|
||
// 先设置状态,触发CSS动画
|
||
isCompareMode.value = true
|
||
|
||
// 等待一小段时间让CSS过渡开始
|
||
await new Promise(resolve => setTimeout(resolve, 50))
|
||
|
||
// 初始化左侧Viewer
|
||
const leftViewerInstance = initLeftViewer(leftContainer)
|
||
if (!leftViewerInstance) {
|
||
console.error('[useDualMapCompare] 左侧Viewer初始化失败')
|
||
isCompareMode.value = false
|
||
return
|
||
}
|
||
|
||
// 立即同步右侧相机的当前位置到左侧
|
||
console.log('[useDualMapCompare] 同步初始相机位置...')
|
||
const rightCamera = rightViewerInstance.camera
|
||
leftViewerInstance.camera.setView({
|
||
destination: rightCamera.position.clone(),
|
||
orientation: {
|
||
heading: rightCamera.heading,
|
||
pitch: rightCamera.pitch,
|
||
roll: rightCamera.roll
|
||
}
|
||
})
|
||
|
||
// 设置相机同步(右侧主地图 → 左侧对比地图)
|
||
setupCameraSync(rightViewerInstance, leftViewerInstance)
|
||
|
||
// 异步加载灾前模型到左侧(不阻塞对比模式启用)
|
||
// 让模型在后台加载,用户可以立即看到对比效果
|
||
console.log('[useDualMapCompare] 开始异步加载左侧灾前模型...')
|
||
load3DTileset(leftViewerInstance, 'before', false)
|
||
.then(tileset => {
|
||
if (tileset) {
|
||
leftTileset = tileset
|
||
console.log('[useDualMapCompare] 左侧灾前模型加载完成')
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('[useDualMapCompare] 左侧模型加载失败:', error)
|
||
})
|
||
|
||
// 右侧保持灾后模型(已加载)
|
||
|
||
// 触发左侧viewer resize
|
||
setTimeout(() => {
|
||
if (leftViewerInstance && leftViewerInstance.canvas) {
|
||
leftViewerInstance.resize()
|
||
leftViewerInstance.camera.changed.raiseEvent()
|
||
}
|
||
// 同时触发右侧viewer resize
|
||
if (rightViewerInstance && rightViewerInstance.canvas) {
|
||
rightViewerInstance.resize()
|
||
}
|
||
}, 350)
|
||
|
||
console.log('[useDualMapCompare] 对比模式已启用')
|
||
}
|
||
|
||
/**
|
||
* 禁用对比模式
|
||
*/
|
||
const disableCompareMode = () => {
|
||
console.log('[useDualMapCompare] 禁用对比模式...')
|
||
|
||
// 移除相机同步
|
||
if (cameraSyncRemover) {
|
||
cameraSyncRemover()
|
||
cameraSyncRemover = null
|
||
}
|
||
|
||
// 销毁左侧Viewer
|
||
if (leftViewer.value && !leftViewer.value.isDestroyed()) {
|
||
// 清理左侧tileset
|
||
if (leftTileset) {
|
||
leftViewer.value.scene.primitives.remove(leftTileset)
|
||
leftTileset = null
|
||
}
|
||
|
||
leftViewer.value.destroy()
|
||
leftViewer.value = null
|
||
console.log('[useDualMapCompare] 左侧Viewer已销毁')
|
||
}
|
||
|
||
// 触发右侧viewer resize恢复全屏
|
||
if (rightViewer.value && !rightViewer.value.isDestroyed()) {
|
||
setTimeout(() => {
|
||
if (rightViewer.value && rightViewer.value.canvas) {
|
||
rightViewer.value.resize()
|
||
}
|
||
}, 350)
|
||
}
|
||
|
||
isCompareMode.value = false
|
||
console.log('[useDualMapCompare] 对比模式已禁用')
|
||
}
|
||
|
||
/**
|
||
* 切换对比模式
|
||
* @param {boolean} active - true启用,false禁用
|
||
* @param {Cesium.Viewer} rightViewerInstance - 右侧Viewer实例(主地图)
|
||
*/
|
||
const toggleCompareMode = async (active, rightViewerInstance) => {
|
||
if (active) {
|
||
await enableCompareMode(rightViewerInstance)
|
||
} else {
|
||
disableCompareMode()
|
||
}
|
||
}
|
||
|
||
// 清理
|
||
onUnmounted(() => {
|
||
disableCompareMode()
|
||
})
|
||
|
||
return {
|
||
leftViewer,
|
||
rightViewer,
|
||
isCompareMode,
|
||
enableCompareMode,
|
||
disableCompareMode,
|
||
toggleCompareMode
|
||
}
|
||
}
|
||
|
||
export default useDualMapCompare
|