《我的飞机绕着宝岛飞》第一章(地面接收器与飞机模型之间的纠缠)

477 阅读4分钟

前言

人生一迹,谨以此记录Cesium相关系列知识

问题背景:我的Cesium飞机和地面接收器如何建立联系?

在蔚蓝的海面上,我的经典Cesium飞机绕着美丽的宝岛飞行。绿意盎然的山川和深蓝的海峡在光照下格外耀眼。突然,机内通信设备发出连连响声,我透过窗户看到地面上的接收器正在闪烁着灯光,它们正试图与我建立一个更稳定的连接。

这些接收器是大陆上的神经末梢,它们负责监测岛屿周围的空域情况,并向空中的飞行员提供实时信息。但今天有些不同,它们似乎比平时更加迫切地想要传递某些信息。

我轻按几个按钮,调整了无线电频率,接收器的声音变得清晰起来。它们发来的不是常规导航信号,而是一串急促的代码,让我感到一丝不寻常。

"陈迹陈迹, 这里是1号地面站,你的左侧引擎数据有异常,请立即检查。" 地面控制人员的声音通过频道传来,带着一丝紧张。

我迅速抬头看向仪表盘,那里的指示灯果然亮起了警告。我的心跳随之加快,我知道我必须迅速采取行动。这场地面接收器与飞机之间的纠缠,可能才刚刚开始...

超抽象化:两个模型(A和B),两种状态(动与静)

次抽象化:动态模型A与静态模型B,动态模型A与动态模型B

具象化:卫星与地面接收器,两个飞机空中模拟加油

005-1.gif

一、Cesium加载高德底图

二、场景定位

三、轨迹数据结构

由于书接上回,因此部分内容就不作重复叙述了,又想观看细节的移步:上一篇文章:关于实时往返轨迹线

四、动态模型A与静态模型B(飞机与地面接收器)

4.1 加载静态地面接收器模型

首先创建一个静态实体对象,即用cesium简单加载gltf模型。

// 创建一个静态实体对象
var dish_entity = viewer.entities.add({
    name: 'satellite',
    model: {
        uri: "Data/satellite_dish.glb",
        show: true,
        scale: 5000,
        silhouetteColor:Cesium.Color.RED,
        silhouetteSize:1
    },
    position: Cesium.Cartesian3.fromDegrees(118.82359094,24.9452259)
});

4.2 加载动态飞机模型

也就是创建一个动态实体对象,和加载静态模型唯一的区别就是暂时不设置position的参数,后续设置完采样器之后,动态设置模型的position位置和orientation朝向。

// 创建一个动态飞机实体对象
var air_entity = viewer.entities.add({
    name: 'air_model',
    model: {
        uri: "Data/airPlane_cesium.gltf",
        show: true,
        scale: 2000,
    }
});

定义一个采样器对象,用于差值计算模型运行轨迹中的点。

var positionSampler = new Cesium.SampledPositionProperty();
positionSampler.setInterpolationOptions({
    interpolationDegree: 2,
    interpolationAlgorithm: Cesium.HermitePolynomialApproximation
});

设置初始时间和模型轨迹点位(时间信息和位置信息),注意此处的positionData里面的time是指上一个点位到本点位的时间,单位为秒。

var positionData = [
    { time: 0, longitude: 121.33830361, latitude: 25.54411517 },
    { time: 20, longitude: 119.7230824, latitude:23.027839 },
];
var start = Cesium.JulianDate.fromDate(new Date(2023, 2, 29));

遍历整个模型点位轨迹数组,将时间和点位信息添加到采样器对象中

for (var i = 0; i < positionData.length; i++) {
    var data = positionData[i];
    var time = new Cesium.JulianDate.addSeconds(start, data.time, new Cesium.JulianDate());
    var position = new Cesium.Cartesian3.fromDegrees(data.longitude, data.latitude);
    positionSampler.addSample(time, position);
}

最后一步设置飞机模型的position和orientation,设置orientation的目的是为了保证飞机模型在轨迹转弯时保证路线和模型朝向一致。

air_entity.position = positionSampler;
air_entity.orientation = new Cesium.VelocityOrientationProperty(positionSampler);

4.3 设置时间

此步骤主要是定义Cesium的时间轴相关参数,包含起始时间、终止时间和当前时间

var stop = Cesium.JulianDate.addSeconds(start, 30, new Cesium.JulianDate());

viewer.clock.startTime = start.clone();
viewer.clock.stopTime = stop.clone();
viewer.clock.currentTime = start.clone();
viewer.clock.shouldAnimate = true;
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
viewer.clock.multiplier = 1;

五、全部代码及效果图

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>我的飞机绕着宝岛飞</title>
    <!-- <link rel="stylesheet" href="./CesiumUnminified/Widgets/widgets.css">
    <script type="text/javascript" src="./CesiumUnminified/Cesium.js"></script> -->
    <link rel="stylesheet" href="./Cesium/Widgets/widgets.css">
    <script type="text/javascript" src="./Cesium/Cesium.js"></script>
</head>
<body>
    <div id="cesiumContainer"></div>
    <script type="text/javascript">
        let viewer = new Cesium.Viewer('cesiumContainer');
        // 更换底图
        let imageryLayers = viewer.imageryLayers;
        let map = new Cesium.UrlTemplateImageryProvider({
            url: "https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}", //高德地图
            minimumLevel: 3,
            maximumLevel: 16,
        });
        imageryLayers.addImageryProvider(map); //添加地图贴图

        // 场景定位
        viewer.camera.flyTo({
            destination : Cesium.Cartesian3.fromDegrees(121.195,21.813,738947.02),
            orientation :{
                heading : Cesium.Math.toRadians(355.1),
                pitch : Cesium.Math.toRadians(-75.3),
                roll :0.0
            }
        });

        // 创建一个动态飞机实体对象
        var air_entity = viewer.entities.add({
            name: 'air_model',
            model: {
                uri: "Data/airPlane_cesium.gltf",
                show: true,
                scale: 2000,
            },
        });
        // 创建一个静态实体对象
        var dish_entity = viewer.entities.add({
            name: 'satellite',
            model: {
                uri: "Data/satellite_dish.glb",
                show: true,
                scale: 5000,
                silhouetteColor:Cesium.Color.RED,
                silhouetteSize:1
            },
            position: Cesium.Cartesian3.fromDegrees(118.82359094,24.9452259)
        });

        // 创建一个连接线对象
        var line_entity = viewer.entities.add({
            name: 'line',
            polyline: {
            positions: new Cesium.CallbackProperty( (time) =>{
                var tarpos = air_entity.position.getValue(time);
                if(!tarpos) return
                var cartographic = Cesium.Ellipsoid.WGS84.cartesianToCartographic(tarpos);
                var lon = Cesium.Math.toDegrees(cartographic.longitude);
                var lat = Cesium.Math.toDegrees(cartographic.latitude);
                return  Cesium.Cartesian3.fromDegreesArray([lon,lat,118.82359094,24.9452259])
            }, false),
            width: 10.0,
            material: new Cesium.PolylineGlowMaterialProperty({
                color: Cesium.Color.DEEPSKYBLUE,
                glowPower: 0.25,
            }),
            }
        })

        // 定义一个数组,存储物体运动的时间和位置点
        var positionData = [
            { time: 0, longitude: 121.33830361, latitude: 25.54411517 },
            { time: 5, longitude: 119.7230824, latitude:23.027839 },
        ];

        // 创建一个采样器对象,用于插值计算物体运动的位置
        var positionSampler = new Cesium.SampledPositionProperty();
        positionSampler.setInterpolationOptions({
            interpolationDegree: 2,
            interpolationAlgorithm: Cesium.HermitePolynomialApproximation
        });
        // 起始时间
        var start = Cesium.JulianDate.fromDate(new Date(2023, 2, 29));

        // 遍历数组,将时间和位置点添加到采样器对象中
        for (var i = 0; i < positionData.length; i++) {
            var data = positionData[i];
            var time = new Cesium.JulianDate.addSeconds(start, data.time, new Cesium.JulianDate());
            var position = new Cesium.Cartesian3.fromDegrees(data.longitude, data.latitude);
            positionSampler.addSample(time, position);
        }
        air_entity.position = positionSampler;
        air_entity.orientation = new Cesium.VelocityOrientationProperty(positionSampler); //根据坐标转头

        // 定义时钟参数,设置开始时间、结束时间和当前时间
        var stop = Cesium.JulianDate.addSeconds(start, 5, new Cesium.JulianDate());
        viewer.clock.startTime = start.clone();
        viewer.clock.stopTime = stop.clone();
        viewer.clock.currentTime = start.clone();
        viewer.clock.shouldAnimate = true;
        viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
        viewer.clock.multiplier = 1;

    </script>
</body>
</html>