使用cesium实现模型按路线移动(轨迹回放)

使用cesium实现模型按路线移动(轨迹回放)

        cesium常用于军事推演场景下使用,在军推时会有实时显示我军人员、车辆、飞行器位置与行进路线,cesium内置api中可以实现该功能。

一、准备工作

1.1、cesium安装与基础使用

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>

转载请说明出处内容投诉
CSS教程网 » 使用cesium实现模型按路线移动(轨迹回放)

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买