[3D地图-CesiumJS系列] 二、建立飞航轨迹及动画

本篇文章请搭配
[3D地图-CesiumJS系列] 一、快速上手


不知道大家在飞机上时会不会好奇飞机行进到哪里?
飞行高度多少?飞行轨迹如何?
身为好奇宝宝的我们,常常会盯着座位萤幕前的3D飞航示意图呢!
今天就要来介绍怎麽使用CesiumJS来制作一个飞航轨迹图。

本篇文章使用官方范例来介绍。

初始化3D地图

复习一下昨天介绍的初始化地图的方式吧!如果还不会CesiumJS专案建置的人,请参考昨天的文章
在根目录下建立一个html页面,取名为Cesium_airplane.html。

↓ 建立一个存放地图的div

    <div id="cmap"></div>

↓ 引入Cesium.js

    <script src="../Build/Cesium/Cesium.js"></script>

↓ 引入css

    <link rel="stylesheet" href="../Build/Cesium/Widgets/widgets.css" />

↓ css让地图满版

    <style>
        html,
        body,
        #cmap {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
    </style>

↓ 设定API Key,如果想要载入自己上传的图层或模型,就要载入自己的key,官网注册连结

        Cesium.Ion.defaultAccessToken = yourAPIKey;

↓ 初始化地图,新建一个Cesium.Viewer的物件,第一个参数存放地图的容器Id,第二个参数为设定(选填)。

        const viewer = new Cesium.Viewer('cmap', {
            terrainProvider: Cesium.createWorldTerrain()  // 建立地形
        });

↓ 结果
https://ithelp.ithome.com.tw/upload/images/20201013/20130604mgY1KdXK7D.png

  • terrain

↓ 花东纵谷,未使用Cesium.createWorldTerrain()
https://ithelp.ithome.com.tw/upload/images/20201013/20130604jrGCvFVZmq.jpg

↓ 花东纵谷,使用Cesium.createWorldTerrain()
https://ithelp.ithome.com.tw/upload/images/20201013/20130604bw3SPT4FQR.jpg
明显较能感受到地形高低起伏。

  • Buildings

↓ 建立3D建物的图层

    const osmBuildings = viewer.scene.primitives.add(Cesium.createOsmBuildings());

↓ 台北信义区,未使用Cesium.createOsmBuildings()
https://ithelp.ithome.com.tw/upload/images/20201013/20130604pswzuksuRr.jpg
顶多出现高楼层建筑物的阴影。

↓ 台北信义区,使用Cesium.createOsmBuildings()
https://ithelp.ithome.com.tw/upload/images/20201013/20130604tidOB0Dzrc.jpg
只要有登记3D建模的建筑物都会清楚呈现。

航行路线资料及设定时间

这边为求方便,直接使用范例航班数据下载

↓ 资料格式

        const data = [{ "longitude": 121.523333, "latitude": 25.15, "height": 3000 }, { "longitude": 120.3508, "latitude": 23, "height": 12000 }]

↓ 把读取的json资料格式转为物件/阵列後,即可使用。

        const flightData = JSON.parse(data);

↓ 设定时间区间、起始时间、时间轴动画

        const timeSpan = 30;
        const totalSeconds = timeSpan * (flightData.length - 1);
        const start = Cesium.JulianDate.fromIso8601("2020-10-13T21:10:00Z");
        const stop = Cesium.JulianDate.addSeconds(start, totalSeconds, new Cesium.JulianDate());
       
        viewer.clock.startTime = start.clone();
        viewer.clock.stopTime = stop.clone();
        viewer.clock.currentTime = start.clone();
        viewer.timeline.zoomTo(start, stop);
        viewer.clock.multiplier = 50;
        viewer.clock.shouldAnimate = true;

↓ 时间轴由设定的时间开始跑
图片

飞航轨迹点

↓ 刚刚设定的时间区间及每个点座标资讯,用Cesium.SampledPositionProperty()建立的物件来储存。

        const timeSpan = 30;
        const positionProperty = new Cesium.SampledPositionProperty();

↓ 跑回圈,设定每个点的时间区间、座标、高度,并把它们加入positionProperty物件中,最後并把点加入地图(viewer)中。

        flightData.forEach((item, index) => {
            const time = Cesium.JulianDate.addSeconds(start, index * timeSpan, new Cesium.JulianDate());
            const position = Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude, item.height);
            positionProperty.addSample(time, position);

            viewer.entities.add({
                description: `经度: ${item.longitude}, 纬度: ${item.latitude}, 海拔高度: ${item.height})`,
                position: position,
                point: { pixelSize: 10, color: Cesium.Color.ORANGE }
            });
        });

↓ 飞航轨迹
https://ithelp.ithome.com.tw/upload/images/20201013/20130604dce1bKYWTJ.jpg

↓ 从侧面看,飞航轨迹横越大西洋
https://ithelp.ithome.com.tw/upload/images/20201013/20130604jfelMSWMFO.jpg

↓ 把地图投影成平面後,会发现最短距离在平面地图上是弧形的
https://ithelp.ithome.com.tw/upload/images/20201013/20130604emdsDtn8UG.jpg

载入飞机模型

可以在Cesium的官方平台上,上传飞机模型,上传完毕後会得到一组飞行模型的Id。

↓ 上传介面
https://ithelp.ithome.com.tw/upload/images/20201013/20130604Rezw3WXpl6.jpg

↓ 选择GITF格式
https://ithelp.ithome.com.tw/upload/images/20201013/20130604S94l0Xq9pt.jpg

↓ 上传成功,获得该飞行模型的Id。
https://ithelp.ithome.com.tw/upload/images/20201013/201306045AuKGGg2Si.jpg

↓ 地图载入飞机模型,并将它设定至飞航轨迹的实体。

        const airPlaneId = 164161;
        async function LoadAirPlaneModel() {
            const airplaneUri = await Cesium.IonResource.fromAssetId(airPlaneId);
            const airplane = {
                availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({ start: start, stop: stop })]),
                position: positionProperty,
                model: { uri: airplaneUri },
                orientation: new Cesium.VelocityOrientationProperty(positionProperty),
                path: new Cesium.PathGraphics({ width: 3 })
            }

            viewer.trackedEntity = viewer.entities.add(airplane);
        }

↓ airplaneUri也可以直接读取官网的范例飞机模型连结。

const airplaneUri = 'https://sandcastle.cesium.com/SampleData/models/CesiumAir/Cesium_Air.glb';

↓ 呼叫

         LoadAirPlaneModel();

展示

↓ 机场中滑行
图片

↓ 空中转向
图片

↓ 快速飞越陆地
图片

↓ 海面上飞行
图片


看着飞机再飞是不是很疗癒呢?
大家赶快也来玩看看吧!

/images/emoticon/emoticon42.gif


<<:  资安这条路 28 - [作业系统] Windows、Linux

>>:  [Day 28] - Gatsby feat. EC ( 下 )

Kotlin Android 第11天,从 0 到 ML - Kotlin Coroutine

前言: Kotlin Coroutine , Coroutin (中文翻 “协程” )这个词在and...

[Day23]ISO 27001 附录 A.11 实体及环境安全

这个章节的重点在於资讯管理系统的实体环境的保护。 不是这种保护 XD 不过,比较常跟受稽方讨论到乖乖...

bug : pyinstaller 打包後的文件是shared library

原因: pyinstaller 的一些版本是这样的; 打包的是PIE executable的执行档案...

Day28 资料流重新导向I

今天要来介绍的部分是资料流重新导向的部分,这个东西其实就是字面上的意思,就是将某个指令成功执行後所要...

【RPA应用实例-文件类】SmartOCR + UiPath 打造文件处理流程自动化

一、认识RPA之後,最想处理文件类型的自动化 已经研究了解RPA基本概念与逻辑的企业,接下来都会开始...