事前準(zhǔn)備
獲取個(gè)人token
首先要去Cesium Ion注冊頁面注冊個(gè)人賬號
注冊成功后進(jìn)入token獲取頁面,頁面右邊的Default Token欄 - token框內(nèi)字符即是要用到的個(gè)人token【重要】
一、 配置Cesium并初始化
配置cesium
見同文集下vue+cesium環(huán)境搭建
cesium初始化
首先在初始化的地球場景里添加一點(diǎn)地形嗷,這些東西默認(rèn)配置在Cesium ion賬戶里
- Cesium World Terrain: 精度可達(dá)1米的高分辨率地形庫
- Cesium OSM Buildings: 開源地圖( Open Street Map, OSM) 的建筑物數(shù)據(jù)庫
- Bing Maps Aerial Imagery: 分辨率高達(dá)15cm的全球衛(wèi)星圖像(這個(gè)已經(jīng)用來捏地球了)
官網(wǎng)教程里是加在index.html里的,不過用的是vue+cesium,這里就改成加到App.vue的mounted()生命周期函數(shù)里
其實(shí)就是在初始化地球代碼上面加一行然后下面再加一行的事
原始的初始化地球部分代碼:
<script>
export default {
name: 'App',
mounted () {
const viewer = new Cesium.Viewer('cesiumContainer')
}
}
</script>
改了以后:
<script>
export default {
name: 'App',
mounted () {
// 添加個(gè)人Token以使應(yīng)用程序得以訪問cesiumIon中的所有資源
Cesium.Ion.defaultAccessToken = 'your_token_here'
// 創(chuàng)建觀察器,并附加到id為“cesiumContainer”的div上
// new Cesium.Viewer(container, options) 基本組件,將所有組件組合成可復(fù)用包
const viewer = new Cesium.Viewer('cesiumContainer', {
// 增加Cesium世界地形數(shù)據(jù)
terrainProvider: Cesium.createWorldTerrain()
})
// 將osm建筑物的3D瓦片數(shù)據(jù)添加進(jìn)觀察器中
const osmBuildings = viewer.scene.primitives.add(Cesium.createOsmBuildings()) // eslint-disable-line no-unused-vars
}
}
</script>
總(人)結(jié)(話):在地球儀初始化地球上捏出來相應(yīng)地形變成凹凸不平的地球儀,然后把建筑物放上去
二、數(shù)據(jù)坐標(biāo)可視化
跟著新手教程入門就直接用官網(wǎng)的數(shù)據(jù)和代碼了,官網(wǎng)說其實(shí)還可以下載航班的原始數(shù)據(jù),或者自己修改代碼,從服務(wù)器拿數(shù)據(jù)然后動(dòng)態(tài)顯示實(shí)時(shí)空中交通情況
這部分是在地球上放一個(gè)點(diǎn),然后拉近鏡頭到那個(gè)點(diǎn)上
單擊那個(gè)點(diǎn)能看到描述信息,比如位置啊時(shí)間啊什么的
接在放建筑物后面:
// 從飛機(jī)身上收集到的第一個(gè)雷達(dá)坐標(biāo).用經(jīng)緯度和高度來描述飛機(jī)在哪
const dataPoint = { longitude: -122.38985, latitude: 37.61864, height: -27.32 }
// 在地球上把這個(gè)坐標(biāo)用紅點(diǎn)標(biāo)出來.
const pointEntity = viewer.entities.add({
// 給出描述信息【這里要用tab鍵上的`包起來】
description: `First data point at (${dataPoint.longitude}, ${dataPoint.latitude})`,
// 給出位置信息 要把經(jīng)緯度和高度轉(zhuǎn)化成地心地固坐標(biāo)系(Earth-Centered, Earth-Fixed,ECEF),即一種笛卡爾坐標(biāo)系
position: Cesium.Cartesian3.fromDegrees(dataPoint.longitude, dataPoint.latitude, dataPoint.height),
// 這個(gè)點(diǎn)具體有多大,上什么色
point: { pixelSize: 10, color: Cesium.Color.RED }
})
// 讓相機(jī)鏡頭拉近到這個(gè)點(diǎn)這里.
viewer.flyTo(pointEntity)
畫一個(gè)點(diǎn)大概就是這樣啦
是不是很簡單
好 然后我們開始學(xué)高數(shù)把所有坐標(biāo)都用點(diǎn)表示出來
//用這個(gè)替換上面只畫一個(gè)點(diǎn)的
// 用JSON.parse方法把從服務(wù)器收到的字符串轉(zhuǎn)換為JS對象
const flightData = JSON.parse(
// 數(shù)據(jù)太多了 麻煩自己去官網(wǎng)復(fù)制好吧
'[{"longitude":-122.39053,"latitude":37.61779,"height":-27.32}]'
);
// 根據(jù)每個(gè)坐標(biāo)畫點(diǎn).沒什么好說的 就是用for函數(shù)遍歷一遍
for (let i = 0; i < flightData.length; i++) {
const dataPoint = flightData[i];
viewer.entities.add({
description: `Location: (${dataPoint.longitude}, ${dataPoint.latitude}, ${dataPoint.height})`,
position: Cesium.Cartesian3.fromDegrees(dataPoint.longitude, dataPoint.latitude, dataPoint.height),
point: { pixelSize: 10, color: Cesium.Color.RED }
});
}
這步寫好之后就能看到從舊金山停機(jī)坪一直到哥本哈根的飛行軌跡了
就是先把軌跡用點(diǎn)畫出來
拜托 超酷的好嗎
三、動(dòng)態(tài)軌跡可視化
好 現(xiàn)在已經(jīng)畫好軌跡了 然后要把飛機(jī)放上去讓它順著軌跡跑了
CesiumJS內(nèi)置了對隨時(shí)間推移進(jìn)行插值采樣的支持 就很好嗷 就看用戶會不會用了
那么問題來了 要咋整呢
就給每個(gè)位置配一個(gè)時(shí)間戳,告訴飛機(jī)說好 在這個(gè)時(shí)間點(diǎn)你該飛到這里了
那么問題又來了 怎么寫代碼呢
哈哈 這次把除了token到收服務(wù)器數(shù)據(jù)以外的代碼都要?jiǎng)h掉 驚不驚喜意不意外不是 注掉就行 萬一還要排查問題呢
/* 初始化取景器計(jì)時(shí)器
* 設(shè)所有雷達(dá)坐標(biāo)的間隔均為30s,以此計(jì)算總的航班持續(xù)時(shí)間
* 算出航班的開始時(shí)間和結(jié)束時(shí)間,其中開始時(shí)間為已知的航班出發(fā)時(shí)間,結(jié)束時(shí)間是開始時(shí)間和總持續(xù)時(shí)間的總和
* 通過將航班的開始和結(jié)束時(shí)間設(shè)置為起點(diǎn)和終點(diǎn)來初始化取景器的計(jì)時(shí)器
* 同時(shí)將取景器的當(dāng)前時(shí)間設(shè)置為計(jì)時(shí)器開始時(shí)間 */
// 時(shí)間間隔30s
const timeStepInSeconds = 30
// 總時(shí)長為時(shí)間間隔 * (雷達(dá)點(diǎn)數(shù) - 1)
const totalSeconds = timeStepInSeconds * (flightData.length - 1)
// 設(shè)置開始時(shí)間,使用根據(jù)國際標(biāo)準(zhǔn)ISO8601時(shí)間轉(zhuǎn)換的儒略歷時(shí)間
const start = Cesium.JulianDate.fromIso8601('2020-03-09T23:10:00Z')
// 新建一個(gè)實(shí)例并將其設(shè)置為結(jié)束時(shí)間,結(jié)束時(shí)間為起點(diǎn)加上總時(shí)長
const stop = Cesium.JulianDate.addSeconds(start, totalSeconds, new Cesium.JulianDate())
// 生成開始時(shí)間的副本,并令其為取景器計(jì)時(shí)器的起點(diǎn)
viewer.clock.startTime = start.clone()
viewer.clock.stopTime = stop.clone()
// 令取景器的當(dāng)前時(shí)間為開始時(shí)間
viewer.clock.currentTime = start.clone()
// 用開始時(shí)間和結(jié)束時(shí)間設(shè)置取景器的時(shí)間線
viewer.timeline.zoomTo(start, stop)
// 將播放速度設(shè)置為50倍
viewer.clock.multiplier = 50
// 允許加速播放
viewer.clock.shouldAnimate = true
// 取樣位置 相當(dāng)于一個(gè)集合
const positionProperty = new Cesium.SampledPositionProperty()
// 為每個(gè)雷達(dá)坐標(biāo)建立對應(yīng)時(shí)間和位置信息的實(shí)體點(diǎn)
for (let i = 0; i < flightData.length; i++) {
const dataPoint = flightData[i]
const time = Cesium.JulianDate.addSeconds(start, i * timeStepInSeconds, new Cesium.JulianDate())
const position = Cesium.Cartesian3.fromDegrees(dataPoint.longitude, dataPoint.latitude, dataPoint.height)
// 添加位置,和時(shí)間對應(yīng)
positionProperty.addSample(time, position)
viewer.entities.add({
description: `Location: (${dataPoint.longitude}, ${dataPoint.latitude}, ${dataPoint.height})`,
position: position,
point: { pixelSize: 10, color: Cesium.Color.RED }
})
}
// 添加飛機(jī)實(shí)體
const airplaneEntity = viewer.entities.add({
availability: new Cesium.TimeIntervalCollection([ new Cesium.TimeInterval({ start: start, stop: stop }) ]),
position: positionProperty,
point: { pixelSize: 30, color: Cesium.Color.GREEN },
path: new Cesium.PathGraphics({ width: 3 })
});
// 設(shè)置鏡頭跟隨飛機(jī)實(shí)體.
viewer.trackedEntity = airplaneEntity;
這一步完成的就是給軌跡上的點(diǎn)標(biāo)出先后順序,起點(diǎn)終點(diǎn),規(guī)定每個(gè)點(diǎn)之間要用多少秒跑完,告訴飛機(jī)每個(gè)點(diǎn)的信息,然后把飛機(jī)放到軌跡上讓它開始跑,同時(shí)鏡頭跟隨飛機(jī)
但是還沒設(shè)置飛行模型,所以現(xiàn)在還看不到飛機(jī),只能看到代表飛機(jī)的綠點(diǎn)在跑
不過還是超酷的好嗎
四、創(chuàng)建并載入飛機(jī)模型
最后一步就是用飛機(jī)模型替代綠點(diǎn)了!
首先把綠點(diǎn)那部分的代碼刪掉或者注掉
網(wǎng)絡(luò)狀況優(yōu)秀的話這么整
網(wǎng)好的話可以直接下載模型,然后把文件拖進(jìn)自己的acconut dashboard頁面,在下拉框內(nèi)選3D Model (Convert to glTF),然后點(diǎn)擊上傳Upload.
上傳完以后可以在asset列表里點(diǎn)選模型,在預(yù)覽preview窗口記住Name框右邊(ID:xxxxxx)里的數(shù)字 然后用數(shù)字替換創(chuàng)建airplaneUri代碼部分的your_asset_id
// 設(shè)置飛機(jī)模型
async function loadModel () {
// 獲取模型數(shù)據(jù)路徑
const airplaneUri = await Cesium.IonResource.fromAssetId(your_asset_id)
// 將模型添加到軌跡上
const airplaneEntity = viewer.entities.add({
// 和時(shí)間軸關(guān)聯(lián)
availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({start: start, stop: stop})]),
// 設(shè)置位置
position: positionProperty,
// 模型數(shù)據(jù)
model: { uri: airplaneUri },
// 根據(jù)所提供的速度計(jì)算模型朝向
orientation: new Cesium.VelocityOrientationProperty(positionProperty),
// 飛行路徑
path: new Cesium.PathGraphics({width: 3})
})
// 令取景器視角固定跟隨飛機(jī)
viewer.trackedEntity = airplaneEntity
}
// 上傳飛機(jī)模型
loadModel()
網(wǎng)絡(luò)狀況不好的話這么整
網(wǎng)不好的話就不容易拿到在線資源對不對
那把它放到本地不就得了!
把下載下來的包解壓到項(xiàng)目文件夾 - static文件夾里,然后把模型數(shù)據(jù)路徑改成后綴為.gltf的文件所在的路徑
也就是改這行:
const airplaneUri = 'static/xxxx.gltf' // 改成自己放gltf文件的路徑
然后就真的完事了!
恭喜你獲得一架沿著軌跡從舊金山咻咻咻飛到哥本哈根的小飛機(jī)!
五、完整代碼
本來是想放的
但是那個(gè)數(shù)據(jù)量太多了
去[官網(wǎng)教程](Build a Flight Tracker – Cesium)那里看Complete source code吧
踩到的坑
-
ESLint: Unexpected template string expression. (no-template-curly-in-string)
-
原因:
const pointEntity = viewer.entities.add({ // eslint-disable-line no-unused-vars description: 'First data point at (${dataPoint.longitude}, $(dataPoint.latitude))',其中description冒號后應(yīng)該用tab鍵上的`括起來,而不是用'
-
辦法:
用`替換'
-
-
設(shè)置飛機(jī)模型的時(shí)候無法顯示
原因:使用cesium Ion - My assets里的在線資源作為模型,然而網(wǎng)速太慢 卡住了
-
辦法:用本地模型替換在線資源
在項(xiàng)目的static文件夾里新建文件夾并導(dǎo)入本地資源,然后修改模型數(shù)據(jù)路徑為相應(yīng)路徑
題外話
今天是國際勞動(dòng)?jì)D女節(jié),祝所有勞動(dòng)?jì)D女特別是程序媛們節(jié)日快樂:)
春有約 花不誤 年年歲歲不相負(fù)