feat(screen): 添加多个实体动画和应急预案模态框
- 添加对多个实体(包括设备和其他人员组)动画的支持 - 引入具有一键调度功能的应急预案模态框 - 通过多条红色路径线和起点标记增强路径可视化效果 - 更新相机控制以实现对所有动画实体的最佳观看效果
This commit is contained in:
parent
3445cb31e7
commit
d73c5f7ca7
@ -1,6 +1,7 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import * as Cesium from 'cesium'
|
import * as Cesium from 'cesium'
|
||||||
import soldierIcon from '../assets/images/SketchPngfbec927027ff9e49207749ebaafd229429315341fda199251b6dfb1723ff17fb.png'
|
import soldierIcon from '../assets/images/SketchPngfbec927027ff9e49207749ebaafd229429315341fda199251b6dfb1723ff17fb.png'
|
||||||
|
import deviceIcon from '../assets/images/SketchPng860d54f2a31f5f441fc6a88081224f1e98534bf6d5ca1246e420983bdf690380.png'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体动画管理 Composable
|
* 实体动画管理 Composable
|
||||||
@ -10,6 +11,9 @@ export function useEntityAnimation() {
|
|||||||
// 动画实体引用
|
// 动画实体引用
|
||||||
const animatedEntity = ref(null)
|
const animatedEntity = ref(null)
|
||||||
|
|
||||||
|
// 多组动画实体引用
|
||||||
|
const animatedEntities = ref([])
|
||||||
|
|
||||||
// 动画是否正在运行
|
// 动画是否正在运行
|
||||||
const isAnimating = ref(false)
|
const isAnimating = ref(false)
|
||||||
|
|
||||||
@ -31,6 +35,49 @@ export function useEntityAnimation() {
|
|||||||
{ x: -1706343.1007708232, y: 5248165.925888667, z: 3187382.186124808 }
|
{ x: -1706343.1007708232, y: 5248165.925888667, z: 3187382.186124808 }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// 设备组路径坐标
|
||||||
|
const DEVICE_PATH_COORDINATES = [
|
||||||
|
{ x: -1706935.1617290622, y: 5248501.604252841, z: 3186498.2137252213 },
|
||||||
|
{ x: -1706870.4952433936, y: 5248490.611178698, z: 3186558.182971472 },
|
||||||
|
{ x: -1706837.227189178, y: 5248420.334709732, z: 3186690.581380162 },
|
||||||
|
{ x: -1706838.280444158, y: 5248431.37975438, z: 3186708.4743944216 },
|
||||||
|
{ x: -1706773.0696515848, y: 5248373.01216906, z: 3186823.943446862 },
|
||||||
|
{ x: -1706727.805384834, y: 5248323.242719114, z: 3186915.9148211516 },
|
||||||
|
{ x: -1706710.854065587, y: 5248281.031240471, z: 3186987.549384787 },
|
||||||
|
{ x: -1706660.1034110512, y: 5248266.553680115, z: 3187047.6342452955 },
|
||||||
|
{ x: -1706604.7906532797, y: 5248253.292764083, z: 3187082.311741225 },
|
||||||
|
{ x: -1706556.3289747096, y: 5248217.41920621, z: 3187155.538613598 },
|
||||||
|
{ x: -1706516.2888762353, y: 5248217.691641104, z: 3187197.012414378 },
|
||||||
|
{ x: -1706436.9808225571, y: 5248198.704904958, z: 3187267.311000274 },
|
||||||
|
{ x: -1706345.7538517928, y: 5248165.481249246, z: 3187379.4674185305 }
|
||||||
|
]
|
||||||
|
|
||||||
|
// 人员组2路径坐标
|
||||||
|
const PERSONNEL_PATH_COORDINATES_2 = [
|
||||||
|
{ x: -1707020.4106680197, y: 5248239.972166492, z: 3187000.301322692 },
|
||||||
|
{ x: -1706846.822522451, y: 5248275.0303560095, z: 3186986.011771928 },
|
||||||
|
{ x: -1706851.3836389545, y: 5248300.681797178, z: 3186942.7035291386 },
|
||||||
|
{ x: -1706844.580179418, y: 5248304.977808034, z: 3186932.891914393 },
|
||||||
|
{ x: -1706818.9276717228, y: 5248278.222342581, z: 3186976.253684671 },
|
||||||
|
{ x: -1706831.3209432173, y: 5248316.447298632, z: 3186957.2249293313 },
|
||||||
|
{ x: -1706806.3169318594, y: 5248307.832821272, z: 3186919.6375066047 },
|
||||||
|
{ x: -1706805.9829374112, y: 5248319.428155191, z: 3186900.13906115 },
|
||||||
|
{ x: -1706795.069820001, y: 5248321.041829423, z: 3186896.925767256 },
|
||||||
|
{ x: -1706797.8593846853, y: 5248325.909509062, z: 3186906.0008906457 },
|
||||||
|
{ x: -1706806.3364665583, y: 5248330.644777029, z: 3186917.773814682 },
|
||||||
|
{ x: -1706778.3135473165, y: 5248282.550132113, z: 3186963.408730601 },
|
||||||
|
{ x: -1706750.0602398354, y: 5248252.482706784, z: 3187012.7495610164 },
|
||||||
|
{ x: -1706738.0772460685, y: 5248263.416236532, z: 3187023.8067475185 },
|
||||||
|
{ x: -1706691.7246759017, y: 5248270.110156667, z: 3187018.588901565 },
|
||||||
|
{ x: -1706599.6318286993, y: 5248240.149769867, z: 3187087.254778178 },
|
||||||
|
{ x: -1706564.1779400432, y: 5248218.9292086465, z: 3187146.335302665 },
|
||||||
|
{ x: -1706486.5598449395, y: 5248207.86588749, z: 3187216.762631293 },
|
||||||
|
{ x: -1706445.7375556522, y: 5248203.1875622, z: 3187252.8650745875 },
|
||||||
|
{ x: -1706409.1757614242, y: 5248182.077526832, z: 3187307.1294440767 },
|
||||||
|
{ x: -1706408.0269636167, y: 5248192.685664652, z: 3187323.2121423096 },
|
||||||
|
{ x: -1706390.7352810341, y: 5248211.637499602, z: 3187344.1570185754 }
|
||||||
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启动应急人员沿路径移动动画
|
* 启动应急人员沿路径移动动画
|
||||||
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
|
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
|
||||||
@ -243,16 +290,175 @@ export function useEntityAnimation() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建单个移动实体
|
||||||
|
* @param {Cesium.Viewer} viewer
|
||||||
|
* @param {Array} pathCoordinates - 路径坐标数组
|
||||||
|
* @param {Object} config - 配置
|
||||||
|
* @param {Cesium.JulianDate} startTime
|
||||||
|
* @param {Cesium.JulianDate} stopTime
|
||||||
|
* @returns {Cesium.Entity}
|
||||||
|
*/
|
||||||
|
const createMovingEntity = (viewer, pathCoordinates, config, startTime, stopTime) => {
|
||||||
|
const positionProperty = new Cesium.SampledPositionProperty()
|
||||||
|
const numberOfPoints = pathCoordinates.length
|
||||||
|
const timeInterval = config.duration / (numberOfPoints - 1)
|
||||||
|
|
||||||
|
pathCoordinates.forEach((coord, index) => {
|
||||||
|
const time = Cesium.JulianDate.addSeconds(
|
||||||
|
startTime,
|
||||||
|
index * timeInterval,
|
||||||
|
new Cesium.JulianDate()
|
||||||
|
)
|
||||||
|
const position = new Cesium.Cartesian3(coord.x, coord.y, coord.z)
|
||||||
|
positionProperty.addSample(time, position)
|
||||||
|
})
|
||||||
|
|
||||||
|
const pulseScale = new Cesium.CallbackProperty((time) => {
|
||||||
|
const elapsed = Cesium.JulianDate.secondsDifference(time, startTime)
|
||||||
|
return 1.0 + Math.sin(elapsed * 3) * 0.3
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
const icon = config.type === 'device' ? deviceIcon : soldierIcon
|
||||||
|
const trailColor = config.type === 'device' ? Cesium.Color.ORANGE : Cesium.Color.CYAN
|
||||||
|
|
||||||
|
return viewer.entities.add({
|
||||||
|
availability: new Cesium.TimeIntervalCollection([
|
||||||
|
new Cesium.TimeInterval({ start: startTime, stop: stopTime })
|
||||||
|
]),
|
||||||
|
position: positionProperty,
|
||||||
|
orientation: new Cesium.VelocityOrientationProperty(positionProperty),
|
||||||
|
billboard: {
|
||||||
|
image: icon,
|
||||||
|
width: 48,
|
||||||
|
height: 56,
|
||||||
|
scale: pulseScale,
|
||||||
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||||
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||||
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||||
|
scaleByDistance: new Cesium.NearFarScalar(1000, 1.5, 50000, 0.8)
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
resolution: 1,
|
||||||
|
material: new Cesium.PolylineGlowMaterialProperty({
|
||||||
|
glowPower: 0.4,
|
||||||
|
taperPower: 0.5,
|
||||||
|
color: trailColor
|
||||||
|
}),
|
||||||
|
width: 8,
|
||||||
|
leadTime: 0,
|
||||||
|
trailTime: config.duration
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
type: config.type === 'device' ? 'animatedDevice' : 'animatedSoldier',
|
||||||
|
name: config.name,
|
||||||
|
department: config.department,
|
||||||
|
isAnimating: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动多组移动动画(设备组 + 人员组2)
|
||||||
|
* @param {Cesium.Viewer} viewer
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
const startMultipleMovements = (viewer, options = {}) => {
|
||||||
|
if (!viewer) {
|
||||||
|
console.warn('[useEntityAnimation] startMultipleMovements: viewer 为空')
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
duration: options.duration ?? 60
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[useEntityAnimation] 启动多组移动动画...')
|
||||||
|
|
||||||
|
const startTime = Cesium.JulianDate.now()
|
||||||
|
const stopTime = Cesium.JulianDate.addSeconds(startTime, config.duration, new Cesium.JulianDate())
|
||||||
|
|
||||||
|
viewer.clock.startTime = startTime.clone()
|
||||||
|
viewer.clock.stopTime = stopTime.clone()
|
||||||
|
viewer.clock.currentTime = startTime.clone()
|
||||||
|
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP
|
||||||
|
viewer.clock.multiplier = 1
|
||||||
|
viewer.clock.shouldAnimate = true
|
||||||
|
|
||||||
|
// 清除之前的动画实体
|
||||||
|
animatedEntities.value.forEach(entity => {
|
||||||
|
if (entity) viewer.entities.remove(entity)
|
||||||
|
})
|
||||||
|
animatedEntities.value = []
|
||||||
|
|
||||||
|
// 创建设备组移动实体
|
||||||
|
const deviceEntity = createMovingEntity(viewer, DEVICE_PATH_COORDINATES, {
|
||||||
|
duration: config.duration,
|
||||||
|
type: 'device',
|
||||||
|
name: '应急设备车',
|
||||||
|
department: '应急装备队'
|
||||||
|
}, startTime, stopTime)
|
||||||
|
animatedEntities.value.push(deviceEntity)
|
||||||
|
|
||||||
|
// 创建人员组2移动实体
|
||||||
|
const personnel2Entity = createMovingEntity(viewer, PERSONNEL_PATH_COORDINATES_2, {
|
||||||
|
duration: config.duration,
|
||||||
|
type: 'soldier',
|
||||||
|
name: '应急救援队员',
|
||||||
|
department: '应急救援队'
|
||||||
|
}, startTime, stopTime)
|
||||||
|
animatedEntities.value.push(personnel2Entity)
|
||||||
|
|
||||||
|
isAnimating.value = true
|
||||||
|
|
||||||
|
const removeListener = viewer.clock.onStop.addEventListener(() => {
|
||||||
|
console.log('[useEntityAnimation] 多组动画已结束')
|
||||||
|
isAnimating.value = false
|
||||||
|
removeListener()
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('[useEntityAnimation] 已启动 2 组额外移动动画')
|
||||||
|
return animatedEntities.value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止所有移动动画
|
||||||
|
* @param {Cesium.Viewer} viewer
|
||||||
|
*/
|
||||||
|
const stopAllMovements = (viewer) => {
|
||||||
|
if (!viewer) return
|
||||||
|
|
||||||
|
viewer.clock.shouldAnimate = false
|
||||||
|
|
||||||
|
if (animatedEntity.value) {
|
||||||
|
viewer.entities.remove(animatedEntity.value)
|
||||||
|
animatedEntity.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
animatedEntities.value.forEach(entity => {
|
||||||
|
if (entity) viewer.entities.remove(entity)
|
||||||
|
})
|
||||||
|
animatedEntities.value = []
|
||||||
|
|
||||||
|
isAnimating.value = false
|
||||||
|
console.log('[useEntityAnimation] 已停止所有移动动画')
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
animatedEntity,
|
animatedEntity,
|
||||||
|
animatedEntities,
|
||||||
isAnimating,
|
isAnimating,
|
||||||
startPersonnelMovement,
|
startPersonnelMovement,
|
||||||
|
startMultipleMovements,
|
||||||
stopPersonnelMovement,
|
stopPersonnelMovement,
|
||||||
|
stopAllMovements,
|
||||||
pauseAnimation,
|
pauseAnimation,
|
||||||
resumeAnimation,
|
resumeAnimation,
|
||||||
getStartPosition,
|
getStartPosition,
|
||||||
cartesianToLonLat,
|
cartesianToLonLat,
|
||||||
PERSONNEL_PATH_COORDINATES
|
PERSONNEL_PATH_COORDINATES,
|
||||||
|
DEVICE_PATH_COORDINATES,
|
||||||
|
PERSONNEL_PATH_COORDINATES_2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -54,7 +54,10 @@
|
|||||||
v-show="!isLeftPanelCollapsed"
|
v-show="!isLeftPanelCollapsed"
|
||||||
class="situational-awareness__panel-column situational-awareness__panel-column--left"
|
class="situational-awareness__panel-column situational-awareness__panel-column--left"
|
||||||
>
|
>
|
||||||
<LeftPanel @start-dispatch="handleStartDispatch" />
|
<LeftPanel
|
||||||
|
@start-dispatch="handleStartDispatch"
|
||||||
|
@view-plan="handleViewPlan"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
@ -164,6 +167,29 @@
|
|||||||
:center-data="selectedCenter"
|
:center-data="selectedCenter"
|
||||||
@close="showCenterDetail = false"
|
@close="showCenterDetail = false"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 智能应急方案弹窗 -->
|
||||||
|
<StretchableModal
|
||||||
|
v-model:visible="showStretchableModal"
|
||||||
|
title="公路灾害现场处置推荐方案"
|
||||||
|
:close-on-click-modal="true"
|
||||||
|
:close-on-press-escape="true"
|
||||||
|
:show-close="true"
|
||||||
|
width="clamp(100px, 50vw, 1400px)"
|
||||||
|
>
|
||||||
|
<!-- 使用应急方案内容组件 -->
|
||||||
|
<EmergencyPlanContent />
|
||||||
|
|
||||||
|
<!-- 底部一键启动按钮 -->
|
||||||
|
<template #footer>
|
||||||
|
<ActionButton
|
||||||
|
text="一键启动"
|
||||||
|
type="primary"
|
||||||
|
size="medium"
|
||||||
|
@click="handleModalStartDispatch"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</StretchableModal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -179,6 +205,9 @@ import PersonnelDetail from "./components/Popups/PersonnelDetail.vue";
|
|||||||
import EmergencyCenterDetail from "./components/Popups/EmergencyCenterDetail.vue";
|
import EmergencyCenterDetail from "./components/Popups/EmergencyCenterDetail.vue";
|
||||||
import MapTooltip from "./components/shared/MapTooltip.vue";
|
import MapTooltip from "./components/shared/MapTooltip.vue";
|
||||||
import SceneLabel from "./components/SceneLabel.vue";
|
import SceneLabel from "./components/SceneLabel.vue";
|
||||||
|
import StretchableModal from "./components/shared/StretchableModal.vue";
|
||||||
|
import ActionButton from "./components/shared/ActionButton.vue";
|
||||||
|
import EmergencyPlanContent from "./components/shared/EmergencyPlanContent.vue";
|
||||||
import { useDisasterData } from "./composables/useDisasterData";
|
import { useDisasterData } from "./composables/useDisasterData";
|
||||||
import { useDualMapCompare } from "./composables/useDualMapCompare";
|
import { useDualMapCompare } from "./composables/useDualMapCompare";
|
||||||
import { useMapMarkers } from "./composables/useMapMarkers";
|
import { useMapMarkers } from "./composables/useMapMarkers";
|
||||||
@ -247,7 +276,7 @@ const {
|
|||||||
const { tooltipState: mapTooltip, showTooltip, hideTooltip, updateTooltipPosition } = useMapTooltip();
|
const { tooltipState: mapTooltip, showTooltip, hideTooltip, updateTooltipPosition } = useMapTooltip();
|
||||||
|
|
||||||
// 实体动画功能
|
// 实体动画功能
|
||||||
const { startPersonnelMovement, stopPersonnelMovement, isAnimating } = useEntityAnimation();
|
const { startPersonnelMovement, startMultipleMovements, stopPersonnelMovement, isAnimating } = useEntityAnimation();
|
||||||
|
|
||||||
// 当前显示 tooltip 的实体(用于相机移动时更新位置)
|
// 当前显示 tooltip 的实体(用于相机移动时更新位置)
|
||||||
const currentTooltipEntity = ref(null);
|
const currentTooltipEntity = ref(null);
|
||||||
@ -255,6 +284,16 @@ const currentTooltipEntity = ref(null);
|
|||||||
// 加载动画状态
|
// 加载动画状态
|
||||||
const showLoading = ref(false);
|
const showLoading = ref(false);
|
||||||
|
|
||||||
|
// 默认相机配置
|
||||||
|
const DEFAULT_CAMERA_VIEW = {
|
||||||
|
lon: 108.011506,
|
||||||
|
lat: 30.175827,
|
||||||
|
height: 5000,
|
||||||
|
heading: 0,
|
||||||
|
pitch: -45,
|
||||||
|
roll: 0,
|
||||||
|
};
|
||||||
|
|
||||||
// 范围圈实体
|
// 范围圈实体
|
||||||
const rangeCircleEntity = ref(null);
|
const rangeCircleEntity = ref(null);
|
||||||
|
|
||||||
@ -553,16 +592,6 @@ onMounted(() => {
|
|||||||
|
|
||||||
console.log("3D态势感知地图已就绪");
|
console.log("3D态势感知地图已就绪");
|
||||||
|
|
||||||
// 默认相机配置
|
|
||||||
const DEFAULT_CAMERA_VIEW = {
|
|
||||||
lon: 108.011506,
|
|
||||||
lat: 30.175827,
|
|
||||||
height: 5000,
|
|
||||||
heading: 0,
|
|
||||||
pitch: -45,
|
|
||||||
roll: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 默认点加上图标标记,使用图片图标,图标路径为 packages\screen\src\views\3DSituationalAwarenessRefactor\assets\images\应急基地.png
|
// 默认点加上图标标记,使用图片图标,图标路径为 packages\screen\src\views\3DSituationalAwarenessRefactor\assets\images\应急基地.png
|
||||||
const defaultPoint = new Cesium.Entity({
|
const defaultPoint = new Cesium.Entity({
|
||||||
position: Cesium.Cartesian3.fromDegrees(
|
position: Cesium.Cartesian3.fromDegrees(
|
||||||
@ -628,6 +657,79 @@ onMounted(() => {
|
|||||||
|
|
||||||
console.log(`[index.vue] 已添加 ${simulatedPoints.length} 个模拟点位`);
|
console.log(`[index.vue] 已添加 ${simulatedPoints.length} 个模拟点位`);
|
||||||
|
|
||||||
|
// 添加三组路径动画的起点标记(用于"一键启动")
|
||||||
|
// 路径1起点(原有人员)
|
||||||
|
const path1Start = new Cesium.Cartesian3(-1706079.1327424292, 5247893.165552528, 3187993.9339800295);
|
||||||
|
const path1StartEntity = viewer.entities.add({
|
||||||
|
position: path1Start,
|
||||||
|
billboard: {
|
||||||
|
image: soldierIcon,
|
||||||
|
width: 36,
|
||||||
|
height: 40,
|
||||||
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||||
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||||
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
type: 'soldier',
|
||||||
|
name: '应急救援人员',
|
||||||
|
department: '应急救援队',
|
||||||
|
location: '待命中',
|
||||||
|
estimatedArrival: '待启动',
|
||||||
|
isPathStartMarker: true,
|
||||||
|
pathId: 'path1'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 路径2起点(设备组)
|
||||||
|
const path2Start = new Cesium.Cartesian3(-1706935.1617290622, 5248501.604252841, 3186498.2137252213);
|
||||||
|
const path2StartEntity = viewer.entities.add({
|
||||||
|
position: path2Start,
|
||||||
|
billboard: {
|
||||||
|
image: deviceIcon,
|
||||||
|
width: 36,
|
||||||
|
height: 40,
|
||||||
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||||
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||||
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
type: 'device',
|
||||||
|
name: '应急设备车',
|
||||||
|
deviceType: '应急装备',
|
||||||
|
location: '待命中',
|
||||||
|
estimatedArrival: '待启动',
|
||||||
|
isPathStartMarker: true,
|
||||||
|
pathId: 'path2'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 路径3起点(人员组2)
|
||||||
|
const path3Start = new Cesium.Cartesian3(-1707020.4106680197, 5248239.972166492, 3187000.301322692);
|
||||||
|
const path3StartEntity = viewer.entities.add({
|
||||||
|
position: path3Start,
|
||||||
|
billboard: {
|
||||||
|
image: soldierIcon,
|
||||||
|
width: 36,
|
||||||
|
height: 40,
|
||||||
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||||
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||||
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
type: 'soldier',
|
||||||
|
name: '应急救援队员',
|
||||||
|
department: '应急救援队',
|
||||||
|
location: '待命中',
|
||||||
|
estimatedArrival: '待启动',
|
||||||
|
isPathStartMarker: true,
|
||||||
|
pathId: 'path3'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('[index.vue] 已添加 3 个路径起点标记');
|
||||||
|
|
||||||
|
|
||||||
// camera.setView({
|
// camera.setView({
|
||||||
// ...DEFAULT_CAMERA_VIEW,
|
// ...DEFAULT_CAMERA_VIEW,
|
||||||
// });
|
// });
|
||||||
@ -886,6 +988,7 @@ const handleMapToolChange = async ({ tool, active }) => {
|
|||||||
// 弹窗状态
|
// 弹窗状态
|
||||||
const showPersonnelDetail = ref(false);
|
const showPersonnelDetail = ref(false);
|
||||||
const showCenterDetail = ref(false);
|
const showCenterDetail = ref(false);
|
||||||
|
const showStretchableModal = ref(false);
|
||||||
|
|
||||||
// 选中的数据
|
// 选中的数据
|
||||||
const selectedPersonnel = ref({
|
const selectedPersonnel = ref({
|
||||||
@ -978,7 +1081,7 @@ const handleMapTooltipClose = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 路径线实体引用
|
// 路径线实体引用
|
||||||
const pathLineEntity = ref(null);
|
const pathLineEntities = ref([]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 绘制红色路径线
|
* 绘制红色路径线
|
||||||
@ -991,13 +1094,13 @@ const drawRedPathLine = (viewer) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果已存在路径线,先移除
|
// 如果已存在路径线,先移除
|
||||||
if (pathLineEntity.value) {
|
pathLineEntities.value.forEach(entity => {
|
||||||
viewer.entities.remove(pathLineEntity.value);
|
if (entity) viewer.entities.remove(entity);
|
||||||
pathLineEntity.value = null;
|
});
|
||||||
}
|
pathLineEntities.value = [];
|
||||||
|
|
||||||
// 路径坐标点
|
// 路径1坐标点(原有人员路径)
|
||||||
const pathCoordinates = [
|
const pathCoordinates1 = [
|
||||||
{ x: -1706079.1327424292, y: 5247893.165552528, z: 3187993.9339800295 },
|
{ x: -1706079.1327424292, y: 5247893.165552528, z: 3187993.9339800295 },
|
||||||
{ x: -1706116.7863268533, y: 5247923.177994122, z: 3187929.297700776 },
|
{ x: -1706116.7863268533, y: 5247923.177994122, z: 3187929.297700776 },
|
||||||
{ x: -1706131.4939896727, y: 5247956.7916397555, z: 3187865.1250298577 },
|
{ x: -1706131.4939896727, y: 5247956.7916397555, z: 3187865.1250298577 },
|
||||||
@ -1011,27 +1114,70 @@ const drawRedPathLine = (viewer) => {
|
|||||||
{ x: -1706343.1007708232, y: 5248165.925888667, z: 3187382.186124808 }
|
{ x: -1706343.1007708232, y: 5248165.925888667, z: 3187382.186124808 }
|
||||||
];
|
];
|
||||||
|
|
||||||
// 将坐标点转换为 Cartesian3 数组
|
// 路径2坐标点(设备组路径)
|
||||||
|
const pathCoordinates2 = [
|
||||||
|
{ x: -1706935.1617290622, y: 5248501.604252841, z: 3186498.2137252213 },
|
||||||
|
{ x: -1706870.4952433936, y: 5248490.611178698, z: 3186558.182971472 },
|
||||||
|
{ x: -1706837.227189178, y: 5248420.334709732, z: 3186690.581380162 },
|
||||||
|
{ x: -1706838.280444158, y: 5248431.37975438, z: 3186708.4743944216 },
|
||||||
|
{ x: -1706773.0696515848, y: 5248373.01216906, z: 3186823.943446862 },
|
||||||
|
{ x: -1706727.805384834, y: 5248323.242719114, z: 3186915.9148211516 },
|
||||||
|
{ x: -1706710.854065587, y: 5248281.031240471, z: 3186987.549384787 },
|
||||||
|
{ x: -1706660.1034110512, y: 5248266.553680115, z: 3187047.6342452955 },
|
||||||
|
{ x: -1706604.7906532797, y: 5248253.292764083, z: 3187082.311741225 },
|
||||||
|
{ x: -1706556.3289747096, y: 5248217.41920621, z: 3187155.538613598 },
|
||||||
|
{ x: -1706516.2888762353, y: 5248217.691641104, z: 3187197.012414378 },
|
||||||
|
{ x: -1706436.9808225571, y: 5248198.704904958, z: 3187267.311000274 },
|
||||||
|
{ x: -1706345.7538517928, y: 5248165.481249246, z: 3187379.4674185305 }
|
||||||
|
];
|
||||||
|
|
||||||
|
// 路径3坐标点(人员组2路径)
|
||||||
|
const pathCoordinates3 = [
|
||||||
|
{ x: -1707020.4106680197, y: 5248239.972166492, z: 3187000.301322692 },
|
||||||
|
{ x: -1706846.822522451, y: 5248275.0303560095, z: 3186986.011771928 },
|
||||||
|
{ x: -1706851.3836389545, y: 5248300.681797178, z: 3186942.7035291386 },
|
||||||
|
{ x: -1706844.580179418, y: 5248304.977808034, z: 3186932.891914393 },
|
||||||
|
{ x: -1706818.9276717228, y: 5248278.222342581, z: 3186976.253684671 },
|
||||||
|
{ x: -1706831.3209432173, y: 5248316.447298632, z: 3186957.2249293313 },
|
||||||
|
{ x: -1706806.3169318594, y: 5248307.832821272, z: 3186919.6375066047 },
|
||||||
|
{ x: -1706805.9829374112, y: 5248319.428155191, z: 3186900.13906115 },
|
||||||
|
{ x: -1706795.069820001, y: 5248321.041829423, z: 3186896.925767256 },
|
||||||
|
{ x: -1706797.8593846853, y: 5248325.909509062, z: 3186906.0008906457 },
|
||||||
|
{ x: -1706806.3364665583, y: 5248330.644777029, z: 3186917.773814682 },
|
||||||
|
{ x: -1706778.3135473165, y: 5248282.550132113, z: 3186963.408730601 },
|
||||||
|
{ x: -1706750.0602398354, y: 5248252.482706784, z: 3187012.7495610164 },
|
||||||
|
{ x: -1706738.0772460685, y: 5248263.416236532, z: 3187023.8067475185 },
|
||||||
|
{ x: -1706691.7246759017, y: 5248270.110156667, z: 3187018.588901565 },
|
||||||
|
{ x: -1706599.6318286993, y: 5248240.149769867, z: 3187087.254778178 },
|
||||||
|
{ x: -1706564.1779400432, y: 5248218.9292086465, z: 3187146.335302665 },
|
||||||
|
{ x: -1706486.5598449395, y: 5248207.86588749, z: 3187216.762631293 },
|
||||||
|
{ x: -1706445.7375556522, y: 5248203.1875622, z: 3187252.8650745875 },
|
||||||
|
{ x: -1706409.1757614242, y: 5248182.077526832, z: 3187307.1294440767 },
|
||||||
|
{ x: -1706408.0269636167, y: 5248192.685664652, z: 3187323.2121423096 },
|
||||||
|
{ x: -1706390.7352810341, y: 5248211.637499602, z: 3187344.1570185754 }
|
||||||
|
];
|
||||||
|
|
||||||
|
// 绘制三条路径线
|
||||||
|
const allPaths = [pathCoordinates1, pathCoordinates2, pathCoordinates3];
|
||||||
|
|
||||||
|
allPaths.forEach((pathCoordinates, index) => {
|
||||||
const positions = pathCoordinates.map(coord =>
|
const positions = pathCoordinates.map(coord =>
|
||||||
new Cesium.Cartesian3(coord.x, coord.y, coord.z)
|
new Cesium.Cartesian3(coord.x, coord.y, coord.z)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 创建红色路径线实体
|
const pathEntity = viewer.entities.add({
|
||||||
pathLineEntity.value = viewer.entities.add({
|
|
||||||
polyline: {
|
polyline: {
|
||||||
positions: positions,
|
positions: positions,
|
||||||
width: 5,
|
width: 5,
|
||||||
material: Cesium.Color.RED,
|
material: Cesium.Color.RED,
|
||||||
clampToGround: true,
|
clampToGround: true
|
||||||
// 添加发光效果使路径更醒目
|
|
||||||
// material: new Cesium.PolylineGlowMaterialProperty({
|
|
||||||
// glowPower: 0.3,
|
|
||||||
// color: Cesium.Color.RED
|
|
||||||
// })
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('[index.vue] 红色路径线已绘制');
|
pathLineEntities.value.push(pathEntity);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('[index.vue] 已绘制 3 条红色路径线');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1053,8 +1199,20 @@ const handleStartDispatch = (payload) => {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showLoading.value = false;
|
showLoading.value = false;
|
||||||
|
|
||||||
// 启动应急人员沿路径移动动画
|
|
||||||
if (mapStore.viewer) {
|
if (mapStore.viewer) {
|
||||||
|
// 移除路径起点标记(因为动画实体会从起点开始显示)
|
||||||
|
const entitiesToRemove = [];
|
||||||
|
mapStore.viewer.entities.values.forEach(entity => {
|
||||||
|
if (entity.properties && entity.properties.isPathStartMarker) {
|
||||||
|
entitiesToRemove.push(entity);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
entitiesToRemove.forEach(entity => {
|
||||||
|
mapStore.viewer.entities.remove(entity);
|
||||||
|
});
|
||||||
|
console.log(`[index.vue] 已移除 ${entitiesToRemove.length} 个路径起点标记`);
|
||||||
|
|
||||||
|
// 启动应急人员沿路径移动动画
|
||||||
console.log('[index.vue] 启动应急人员移动动画...');
|
console.log('[index.vue] 启动应急人员移动动画...');
|
||||||
startPersonnelMovement(mapStore.viewer, {
|
startPersonnelMovement(mapStore.viewer, {
|
||||||
duration: 60, // 60秒完成整个路径,移动更清晰可见
|
duration: 60, // 60秒完成整个路径,移动更清晰可见
|
||||||
@ -1062,12 +1220,78 @@ const handleStartDispatch = (payload) => {
|
|||||||
personnelName: '应急救援人员',
|
personnelName: '应急救援人员',
|
||||||
department: '应急救援队'
|
department: '应急救援队'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 同时启动多组移动动画(设备组 + 人员组2)
|
||||||
|
startMultipleMovements(mapStore.viewer, {
|
||||||
|
duration: 60
|
||||||
|
});
|
||||||
|
|
||||||
|
// 缓慢拉近到能看到所有3组移动实体的全景位置
|
||||||
|
const { camera } = mapStore.services();
|
||||||
|
|
||||||
|
console.log('[index.vue] 相机缓慢飞向最佳全景位置...');
|
||||||
|
|
||||||
|
// 收集所有3组路径的关键点(起点和终点),用于计算最佳视角
|
||||||
|
const allPathPoints = [
|
||||||
|
// 路径1(原有人员)
|
||||||
|
new Cesium.Cartesian3(-1706079.1327424292, 5247893.165552528, 3187993.9339800295), // 起点
|
||||||
|
new Cesium.Cartesian3(-1706343.1007708232, 5248165.925888667, 3187382.186124808), // 终点
|
||||||
|
// 路径2(设备组)
|
||||||
|
new Cesium.Cartesian3(-1706935.1617290622, 5248501.604252841, 3186498.2137252213), // 起点
|
||||||
|
new Cesium.Cartesian3(-1706345.7538517928, 5248165.481249246, 3187379.4674185305), // 终点
|
||||||
|
// 路径3(人员组2)
|
||||||
|
new Cesium.Cartesian3(-1707020.4106680197, 5248239.972166492, 3187000.301322692), // 起点
|
||||||
|
new Cesium.Cartesian3(-1706390.7352810341, 5248211.637499602, 3187344.1570185754) // 终点
|
||||||
|
];
|
||||||
|
|
||||||
|
// 转换为经纬度格式
|
||||||
|
const trajectoryPoints = allPathPoints.map(point => {
|
||||||
|
const cartographic = Cesium.Cartographic.fromCartesian(point);
|
||||||
|
return {
|
||||||
|
lon: Cesium.Math.toDegrees(cartographic.longitude),
|
||||||
|
lat: Cesium.Math.toDegrees(cartographic.latitude)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 使用智能聚焦方法飞向能看到所有路径的最佳位置
|
||||||
|
camera.fitBoundsWithTrajectory(trajectoryPoints, {
|
||||||
|
duration: 5, // 5秒缓慢飞行
|
||||||
|
padding: 0.2 // 20% 边距,确保所有点都在视野内
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
console.warn('[index.vue] 地图viewer未就绪,无法启动动画');
|
console.warn('[index.vue] 地图viewer未就绪,无法启动动画');
|
||||||
}
|
}
|
||||||
}, 3000);
|
}, 3000);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理查看智能应急方案事件
|
||||||
|
* 打开智能应急方案弹窗
|
||||||
|
*/
|
||||||
|
const handleViewPlan = (plan) => {
|
||||||
|
console.log('[index.vue] 查看智能应急方案:', plan);
|
||||||
|
showStretchableModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理弹窗中的一键启动按钮点击事件
|
||||||
|
* 关闭弹窗并执行一键启动功能
|
||||||
|
*/
|
||||||
|
const handleModalStartDispatch = () => {
|
||||||
|
console.log('[index.vue] 弹窗中点击一键启动');
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
showStretchableModal.value = false;
|
||||||
|
|
||||||
|
// 执行一键启动逻辑(与handleStartDispatch相同)
|
||||||
|
handleStartDispatch({
|
||||||
|
planName: '智能应急方案',
|
||||||
|
plan: disasterData.forceDispatch.value.plan,
|
||||||
|
responseLevel: disasterData.forceDispatch.value.responseLevel,
|
||||||
|
estimatedClearTime: disasterData.forceDispatch.value.estimatedClearTime
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建或更新范围圈
|
* 创建或更新范围圈
|
||||||
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
|
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user