cesium常用于军事推演场景下使用,在军推时会有实时显示我军人员、车辆、飞行器位置与行进路线,cesium内置api中可以实现该功能。
一、准备工作
1.1、cesium安装与基础使用
二、功能实现
2.1、定义路线变量
let data = [];
data[0] = [{ longitude: 116.405419, dimension: 39.918034, height: 0, time: 0 }, { longitude: 116.2821, dimension: 39.918145, height: 0, time: 40 }, { longitude: 115.497402, dimension: 39.344641, height: 70000, time: 100 }, { longitude: 107.942392, dimension: 29.559967, height: 70000, time: 280 }, { longitude: 106.549265, dimension: 29.559967, height: 0, time: 360 }];
data[1] = [{ longitude: 116.405419, dimension: 39.918034, height: 0, time: 0 }, { longitude: 117.034586, dimension: 39.881202, height: 0, time: 40 }, { longitude: 116.340088, dimension: 38.842224, height: 70000, time: 100 }, { longitude: 113.489176, dimension: 23.464017, height: 70000, time: 280 }, { longitude: 113.262084, dimension: 23.13901, height: 0, time: 360 }];
data[2] = [{ longitude: 118.838979, dimension: 32.073514, height: 0, time: 0 }, { longitude: 118.438838, dimension: 32.03777, height: 0, time: 40 }, { longitude: 117.802406, dimension: 31.91231, height: 70000, time: 100 }, { longitude: 104.043645, dimension: 35.993845, height: 70000, time: 280 }, { longitude: 101.807224, dimension: 36.660972, height: 0, time: 360 }];
此变量定义了每一个对象的行进的经纬度关键点坐标、高度与时间节点
2.2、添加起止时间
// 起始时间
let start = Cesium.JulianDate.fromDate(new Date());
// 结束时间
let stop = Cesium.JulianDate.addSeconds(start, 360, new Cesium.JulianDate());
2.3、设置时钟
// 设置时钟
view.clock.startTime = start.clone();
view.clock.currentTime = start.clone();
view.clock.stopTime = stop.clone();
view.clock.multiplier = 10; // 增加速度以便观察
view.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
// 设置时间轴范围
if (view.timeline) {
view.timeline.zoomTo(start, stop);
}
2.4、添加路径点与路径的可视化
// 调试:添加路径点可视化
addDebugPoints(view, data);
// 先创建所有路径属性
const flightProperties = data.map(path => ***puteFlight(path, start));
2.5、添加模型
// 使用计数器跟踪模型加载状态
let loadedModels = 0;
const totalModels = data.length;
for (let j = 0; j < data.length; j++) {
const property = flightProperties[j];
// 添加模型
let planeModel = view.entities.add({
availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({
start: start,
stop: stop
})]),
position: property,
orientation: new Cesium.VelocityOrientationProperty(property),
model: {
uri: './models/drone.glb',
minimumPixelSize: 64,
maximumScale: 200,
scale: 2.0, // 增大缩放比例
heightReference: Cesium.HeightReference.NONE
},
// 添加路径线
path: {
resolution: 1,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.3,
color: Cesium.Color.fromRandom({ alpha: 1.0 })
}),
width: 5
}
});
2.6、检查模型是否加载成功
// 安全地检查模型加载状态
if (planeModel.model && planeModel.model.readyPromise) {
planeModel.model.readyPromise.then(model => {
console.log(`模型 ${j} 加载成功`);
loadedModels++;
// 所有模型加载完成后,调整相机视角
if (loadedModels === totalModels) {
console.log('所有模型加载完成');
// 飞到第一个路径的起始点
view.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(116.405419, 39.918034, 5000),
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-45),
roll: 0.0
},
duration: 3
});
}
}).catch(error => {
console.error(`模型 ${j} 加载失败:`, error);
// 如果模型加载失败,添加一个点作为替代
view.entities.add({
position: property,
point: {
pixelSize: 15,
color: Cesium.Color.RED,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2
},
label: {
text: `无人机 ${j}`,
font: '14pt monospace',
fillColor: Cesium.Color.WHITE
}
});
loadedModels++;
});
} else {
console.warn(`模型 ${j} 的 readyPromise 不可用,使用替代方案`);
// 添加替代点
view.entities.add({
position: property,
point: {
pixelSize: 15,
color: Cesium.Color.BLUE,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2
},
label: {
text: `无人机 ${j} (替代)`,
font: '14pt monospace',
fillColor: Cesium.Color.WHITE
}
});
loadedModels++;
}
2.7、添加监听器
// 添加时间监听
view.clock.onTick.addEventListener(function() {
const currentTime = view.clock.currentTime;
const seconds = Cesium.JulianDate.secondsDifference(currentTime, start);
// 安全地更新显示
const timeDisplay = document.getElementById('timeDisplay');
if (timeDisplay) {
timeDisplay.innerText = `当前时间: ${seconds.toFixed(1)} 秒`;
}
});
// 启动时钟
view.clock.shouldAnimate = true;
}
2.8、其余函数定义
/**
* 计算飞行路径
*/
function ***puteFlight(source, startTime) {
let property = new Cesium.SampledPositionProperty();
for (let i = 0; i < source.length; i++) {
let time = Cesium.JulianDate.addSeconds(startTime, source[i].time, new Cesium.JulianDate());
let position = Cesium.Cartesian3.fromDegrees(source[i].longitude, source[i].dimension, source[i].height);
property.addSample(time, position);
console.log(`路径点${i}: 经度${source[i].longitude}, 纬度${source[i].dimension}, 高度${source[i].height}米, 时间${source[i].time}秒`);
}
return property;
}
/**
* 添加调试点(可视化路径点)
*/
function addDebugPoints(viewer, data) {
const colors = [Cesium.Color.YELLOW, Cesium.Color.CYAN, Cesium.Color.MAGENTA];
for (let j = 0; j < data.length; j++) {
for (let i = 0; i < data[j].length; i++) {
let point = data[j][i];
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(point.longitude, point.dimension, point.height),
point: {
pixelSize: 10,
color: colors[j],
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2
},
label: {
text: `路径${j+1}-点${i+1}\n时间:${point.time}s\n高度:${point.height}m`,
font: '10pt monospace',
pixelOffset: new Cesium.Cartesian2(0, -25),
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2,
style: Cesium.LabelStyle.FILL_AND_OUTLINE
}
});
}
}
}
2.9、刚进入页面时初始化
onMounted(() => {
nextTick(() => {
init_cesium()
})
})
三、完整代码
<template>
<div id="cesiumContainer" style="width: 100vw; height: 100vh;"></div>
</template>
<script setup>
import * as Cesium from 'cesium'
import { onMounted, nextTick } from 'vue'
const init_cesium = () => {
Cesium.Ion.defaultA***essToken = '自己的token'
let view = new Cesium.Viewer('cesiumContainer', {
geocoder: false,
homeButton: false,
sceneModePicker: false,
baseLayerPicker: false,
navigationHelpButton: false,
animation: false,
infoBox: false,
selectionIndicator: false,
timeline: true,
fullscreenButton: false,
vrButton: false,
enableLighting: true,
shadows: true,
});
// 设置地球光照
view.scene.globe.enableLighting = true;
let data = [];
data[0] = [{ longitude: 116.405419, dimension: 39.918034, height: 0, time: 0 }, { longitude: 116.2821, dimension: 39.918145, height: 0, time: 40 }, { longitude: 115.497402, dimension: 39.344641, height: 70000, time: 100 }, { longitude: 107.942392, dimension: 29.559967, height: 70000, time: 280 }, { longitude: 106.549265, dimension: 29.559967, height: 0, time: 360 }];
data[1] = [{ longitude: 116.405419, dimension: 39.918034, height: 0, time: 0 }, { longitude: 117.034586, dimension: 39.881202, height: 0, time: 40 }, { longitude: 116.340088, dimension: 38.842224, height: 70000, time: 100 }, { longitude: 113.489176, dimension: 23.464017, height: 70000, time: 280 }, { longitude: 113.262084, dimension: 23.13901, height: 0, time: 360 }];
data[2] = [{ longitude: 118.838979, dimension: 32.073514, height: 0, time: 0 }, { longitude: 118.438838, dimension: 32.03777, height: 0, time: 40 }, { longitude: 117.802406, dimension: 31.91231, height: 70000, time: 100 }, { longitude: 104.043645, dimension: 35.993845, height: 70000, time: 280 }, { longitude: 101.807224, dimension: 36.660972, height: 0, time: 360 }];
// 起始时间
let start = Cesium.JulianDate.fromDate(new Date());
// 结束时间
let stop = Cesium.JulianDate.addSeconds(start, 360, new Cesium.JulianDate());
// 设置时钟
view.clock.startTime = start.clone();
view.clock.currentTime = start.clone();
view.clock.stopTime = stop.clone();
view.clock.multiplier = 10; // 增加速度以便观察
view.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
// 设置时间轴范围
if (view.timeline) {
view.timeline.zoomTo(start, stop);
}
// 调试:添加路径点可视化
addDebugPoints(view, data);
// 先创建所有路径属性
const flightProperties = data.map(path => ***puteFlight(path, start));
// 使用计数器跟踪模型加载状态
let loadedModels = 0;
const totalModels = data.length;
for (let j = 0; j < data.length; j++) {
const property = flightProperties[j];
// 添加模型
let planeModel = view.entities.add({
availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({
start: start,
stop: stop
})]),
position: property,
orientation: new Cesium.VelocityOrientationProperty(property),
model: {
uri: './models/drone.glb',
minimumPixelSize: 64,
maximumScale: 200,
scale: 2.0, // 增大缩放比例
heightReference: Cesium.HeightReference.NONE
},
// 添加路径线
path: {
resolution: 1,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.3,
color: Cesium.Color.fromRandom({ alpha: 1.0 })
}),
width: 5
}
});
// 安全地检查模型加载状态
if (planeModel.model && planeModel.model.readyPromise) {
planeModel.model.readyPromise.then(model => {
console.log(`模型 ${j} 加载成功`);
loadedModels++;
// 所有模型加载完成后,调整相机视角
if (loadedModels === totalModels) {
console.log('所有模型加载完成');
// 飞到第一个路径的起始点
view.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(116.405419, 39.918034, 5000),
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-45),
roll: 0.0
},
duration: 3
});
}
}).catch(error => {
console.error(`模型 ${j} 加载失败:`, error);
// 如果模型加载失败,添加一个点作为替代
view.entities.add({
position: property,
point: {
pixelSize: 15,
color: Cesium.Color.RED,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2
},
label: {
text: `无人机 ${j}`,
font: '14pt monospace',
fillColor: Cesium.Color.WHITE
}
});
loadedModels++;
});
} else {
console.warn(`模型 ${j} 的 readyPromise 不可用,使用替代方案`);
// 添加替代点
view.entities.add({
position: property,
point: {
pixelSize: 15,
color: Cesium.Color.BLUE,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2
},
label: {
text: `无人机 ${j} (替代)`,
font: '14pt monospace',
fillColor: Cesium.Color.WHITE
}
});
loadedModels++;
}
}
// 添加时间监听
view.clock.onTick.addEventListener(function() {
const currentTime = view.clock.currentTime;
const seconds = Cesium.JulianDate.secondsDifference(currentTime, start);
// 安全地更新显示
const timeDisplay = document.getElementById('timeDisplay');
if (timeDisplay) {
timeDisplay.innerText = `当前时间: ${seconds.toFixed(1)} 秒`;
}
});
// 启动时钟
view.clock.shouldAnimate = true;
}
/**
* 计算飞行路径
*/
function ***puteFlight(source, startTime) {
let property = new Cesium.SampledPositionProperty();
for (let i = 0; i < source.length; i++) {
let time = Cesium.JulianDate.addSeconds(startTime, source[i].time, new Cesium.JulianDate());
let position = Cesium.Cartesian3.fromDegrees(source[i].longitude, source[i].dimension, source[i].height);
property.addSample(time, position);
console.log(`路径点${i}: 经度${source[i].longitude}, 纬度${source[i].dimension}, 高度${source[i].height}米, 时间${source[i].time}秒`);
}
return property;
}
/**
* 添加调试点(可视化路径点)
*/
function addDebugPoints(viewer, data) {
const colors = [Cesium.Color.YELLOW, Cesium.Color.CYAN, Cesium.Color.MAGENTA];
for (let j = 0; j < data.length; j++) {
for (let i = 0; i < data[j].length; i++) {
let point = data[j][i];
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(point.longitude, point.dimension, point.height),
point: {
pixelSize: 10,
color: colors[j],
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2
},
label: {
text: `路径${j+1}-点${i+1}\n时间:${point.time}s\n高度:${point.height}m`,
font: '10pt monospace',
pixelOffset: new Cesium.Cartesian2(0, -25),
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2,
style: Cesium.LabelStyle.FILL_AND_OUTLINE
}
});
}
}
}
onMounted(() => {
nextTick(() => {
init_cesium()
})
})
</script>
<style lang="scss" scoped></style>