vue2 使用 cesium 篇
今天好好寫一篇哈,之前寫的半死不活的。首先說(shuō)明:這篇博文是我邊做邊寫的,小白也是,實(shí)現(xiàn)效果會(huì)同時(shí)發(fā)布截圖,如果沒(méi)有實(shí)現(xiàn)也會(huì)說(shuō)明,僅僅作為技術(shù)積累,選擇性分享,不做教學(xué)哈。不好別噴。
安裝 cesium
這個(gè)就很簡(jiǎn)單,只需要一句簡(jiǎn)簡(jiǎn)單單的命令就可以實(shí)現(xiàn)在 vue 項(xiàng)目中安裝 cesium 了。
npm install cesium --save
然后等待安裝完成就可以了兄弟們??!

這個(gè)樣子嘞,就是安裝完成了,會(huì)開發(fā) vue 的都曉得哈。

看一下依賴包里面,也成功下載了 cesium 的依賴,非常棒?。?/p>
接入項(xiàng)目 cesium
接下來(lái)就是使用,這個(gè)步驟很不好整,cesium 的官方文檔寫的很不友好,許多新手小白很難入門,甚至是理解都難,查文檔都不會(huì),就比如我。但是!這里的步驟我都是自己測(cè)試過(guò)可以的哈!首先呢,我是自己創(chuàng)建的 vue2 項(xiàng)目,使用的腳手架是 cli3 以上的。
首先第一步,從 node_modules 依賴包里面,找到我們剛剛安裝的 cesium ,在文件夾里面有一個(gè) Build 文件夾,里面有一個(gè) Cesium 文件夾,把這個(gè) Cesium 文件夾復(fù)制一份。
然后呢,在項(xiàng)目的 public 文件夾下,粘貼出來(lái)。

好的第一步完成!
然后第二步,在項(xiàng)目 index.html 文件中,head 標(biāo)簽里面,引入 cesium 的全局樣式。
<link rel="stylesheet" href="./Cesium/Widgets/widgets.css">
還是在這個(gè)文件中,在 body 最后,引入 cesium 源碼。
<script type="text/javascript" src="./Cesium/Cesium.js"></script>
就是下面這個(gè)樣子哈。

好的,完成之后我們重新啟動(dòng)一下項(xiàng)目,記得哈,重啟一下項(xiàng)目!其實(shí)不重啟也可以,啊哈哈哈哈。
使用 cesium
接下來(lái)的工作就很簡(jiǎn)單了嘛,我們寫一個(gè)頁(yè)面哈,用來(lái)顯示藍(lán)星。這個(gè)頁(yè)面就隨便寫了哈,沒(méi)有啥技術(shù)含量,咱就不啰嗦了。
<template>
<div id="my-map"></div>
</template>
<script>
export default {
name: "HomeView",
};
</script>
<style scoped>
#my-map {
width: 100%;
height: 100%;
background-color: black;
}
</style>
寫一個(gè)黑色的頁(yè)面,so 簡(jiǎn)單哈!

非常好,效果出來(lái)了,到現(xiàn)在還沒(méi)有用到一點(diǎn)兒的 cesium 別急,下面開始哈。
我們寫一個(gè) init() 方法,然后在 init() 方法里面呢,實(shí)現(xiàn)把這個(gè) div 渲染成一個(gè)藍(lán)星。
init() {
this.viewer = new Cesium.Viewer('my-map', {
homeButton: false,
sceneModePicker: false,
baseLayerPicker: false, // 影像切換
animation: true, // 是否顯示動(dòng)畫控件
infoBox: false, // 是否顯示點(diǎn)擊要素之后顯示的信息
selectionIndicator: false, // 要素選中框
geocoder: false, // 是否顯示地名查找控件
timeline: true, // 是否顯示時(shí)間線控件
fullscreenButton: false,
shouldAnimate: false,
navigationHelpButton: false, // 是否顯示幫助信息控件
});
}
完成!藍(lán)星加載出來(lái)了!

這樣初始化藍(lán)星就差不多了是吧!好的下面說(shuō)一些其他的事情。
cesium 相關(guān)資料
首先分享一些資料給各位,希望有用:
Cesium 官網(wǎng):https://cesium.com/
Cesium 官網(wǎng) API:https://cesium.com/learn/cesiumjs/ref-doc/
Cesium 中文API: http://cesium.xin/cesium/cn/Documentation1.62/
Cesium 官方案例:https://sandcastle.cesium.com/?
Cesium 技能樹:https://www.wenjiangs.com/doc/egyaeyav
Cesium 中文社區(qū):http://cesium.xin/
3D 模型下載網(wǎng)站:https://sketchfab.com/feed
上邊這部分網(wǎng)站呢,也許對(duì)你有用,需要的話可以看一下。
優(yōu)化上面代碼
為了保證代碼稍微稍微的整潔一點(diǎn)點(diǎn),我們把 cesium 有關(guān)的代碼抽成一個(gè) js 文件哈,統(tǒng)一的放在一起,這樣的話呢,方便管理一下,嘿嘿!
首先在組件同級(jí)創(chuàng)建一個(gè) js 文件夾,里面放一個(gè) TCesium.js 文件,里面的代碼就是下面的樣子:
export class TCesium {
viewer = null;
scene = null;
/**
* 構(gòu)造器函數(shù):實(shí)例化cesium
* @param {*} dom 節(jié)點(diǎn)id
*/
constructor(dom) {
this.viewer = new Cesium.Viewer(dom, {
homeButton: false,
sceneModePicker: false,
baseLayerPicker: false, // 影像切換
animation: true, // 是否顯示動(dòng)畫控件
infoBox: false, // 是否顯示點(diǎn)擊要素之后顯示的信息
selectionIndicator: false, // 要素選中框
geocoder: false, // 是否顯示地名查找控件
timeline: true, // 是否顯示時(shí)間線控件
fullscreenButton: false,
shouldAnimate: false,
navigationHelpButton: false, // 是否顯示幫助信息控件
});
this.scene = this.viewer.scene
}
}
然后在組件中呢,引入一下這個(gè)文件。
import { TCesium } from './js/TCesium'
然后嘞,init() 方法就可以直接實(shí)例化了。
init() {
this.mapObject = new TCesium('my-map') // 注意,這個(gè)my-map就是我們div的id
}
然后呢,效果是一樣的。

很棒!
cesium token 申請(qǐng)和設(shè)置
接下來(lái)我們說(shuō)一下 cesium 的 token。使用cesium需要申請(qǐng)一個(gè)token值,這個(gè)地方就和百度地圖或者是高德地圖一樣,需要一個(gè) token 秘鑰來(lái)進(jìn)行操作,確保在使用 cesium 的過(guò)程中不會(huì)出現(xiàn) token 過(guò)期造成地圖加載不出來(lái)的問(wèn)題。當(dāng)然了,現(xiàn)在看我們是一點(diǎn)問(wèn)題沒(méi)有,藍(lán)星地球可以正常加載,但是隨著我們測(cè)試編寫的時(shí)間增長(zhǎng),調(diào)用 cesium 圖層次數(shù)過(guò)多,就會(huì)出現(xiàn)圖層加載不出來(lái),就是超次數(shù),這個(gè)是時(shí)候,我們地球可能就白了,因?yàn)闆](méi)有實(shí)時(shí)圖層返回了,這個(gè)時(shí)候就需要 token,所以說(shuō)我們現(xiàn)在就設(shè)置上 token,防止這種事情的發(fā)生。
首先我們需要去 cesium 官網(wǎng)去申請(qǐng)一個(gè) token,方法很簡(jiǎn)單:
cesium申請(qǐng)token請(qǐng)點(diǎn)擊 這里

然后嘞,輸入用戶名、密碼登錄一下子。沒(méi)有用戶名密碼的可以創(chuàng)建一個(gè)新的賬號(hào)來(lái)完成操作喲~!
然后就可以創(chuàng)建一個(gè)新的 token 來(lái)玩。

然后我們就成功的獲取到了一個(gè) cesium 的秘鑰,太棒了親們!
然后我們?cè)?TCesium.js 文件中的構(gòu)造器函數(shù)中,加載這個(gè) token 就可以了。
Cesium.Ion.defaultAccessToken = '你申請(qǐng)的cesium的token'
就像下面這個(gè)樣子:

好的,這樣子的話, token 就申請(qǐng)使用完成了。
cesium 基礎(chǔ)配置
我們?cè)谏厦娴拇a配置了一些基本的設(shè)置項(xiàng),稍微過(guò)一下子哈。
this.viewer = new Cesium.Viewer(dom, {
homeButton: false,
sceneModePicker: false,
baseLayerPicker: false, // 影像切換
animation: false, // 是否顯示動(dòng)畫控件
infoBox: false, // 是否顯示點(diǎn)擊要素之后顯示的信息
selectionIndicator: false, // 要素選中框
geocoder: false, // 是否顯示地名查找控件
timeline: false, // 是否顯示時(shí)間線控件
fullscreenButton: false,
shouldAnimate: false,
navigationHelpButton: false, // 是否顯示幫助信息控件
});
就是上面這一塊,我寫了幾個(gè)常用的,先全部設(shè)置為 false,就是不使用,我們稍微說(shuō)幾個(gè)常用的看一下哈。
homeButton
我們先看 homeButton 參數(shù),我們?nèi)绻O(shè)置為 true。這個(gè)是主頁(yè)按鈕,啥意思呢,就是我們加載出藍(lán)星之后,我們可以鼠標(biāo)轉(zhuǎn)動(dòng)嘛、放大縮小啥的。如果我們?cè)O(shè)置 homeButton 為 true 之后,在頁(yè)面的右上角就會(huì)出現(xiàn)一個(gè)主頁(yè)按鈕,當(dāng)我們改變過(guò)藍(lán)星之后,點(diǎn)擊這個(gè)按鈕,會(huì)回到我們最開始的視角。

sceneModePicker
sceneModePicker 是地圖顯示的維度控制,他提供 二維平面 和 三維球體 兩種方式。當(dāng)設(shè)置為 true 的時(shí)候,右上角會(huì)提供一個(gè)維度切換的按鈕,幫助我們進(jìn)行藍(lán)星維度的展示切換功能。

baseLayerPicker
Cesium 為我們提供了一些底圖,這個(gè) baseLayerPicker 設(shè)置為 true 的時(shí)候,右上角會(huì)有一個(gè)圖層切換的按鈕,但是我覺(jué)得沒(méi)用,不是沒(méi)用,不好用,一般都是自己寫,不用他默認(rèn)提供的。

animation 和 timeline
這兩個(gè)是時(shí)間軸相關(guān)的,一般用不到,但是不保險(xiǎn),需要的時(shí)候沒(méi)有還真不行,這兩個(gè)要同時(shí)設(shè)置,true 的時(shí)候都 true,false 的時(shí)候都是 false。當(dāng)然也不絕對(duì)哈,就是 timeline 的時(shí)候呢,就開始時(shí)間軸功能,animation 就是顯示時(shí)間軸的控件,可以直觀的控制時(shí)間軸的進(jìn)度、速度之類的。

selectionIndicator
selectionIndicator 是要素選中框,啥意思呢,就比如說(shuō)我的藍(lán)星上有模型,那么我點(diǎn)擊藍(lán)星上模型的時(shí)候呢,就會(huì)出現(xiàn)一個(gè)框框把模型給框住,我覺(jué)得沒(méi)必要。
fullscreenButton
這個(gè)是全屏按鈕,設(shè)置為 true 的時(shí)候呢,右下角就會(huì)出現(xiàn)一個(gè)全屏按鈕,點(diǎn)擊之后,cesium 就會(huì)進(jìn)行全屏顯示。

infoBox
這個(gè)是要素信息框,一般都是 false,啥呢,就是藍(lán)星上面有一個(gè)模型,點(diǎn)擊模型的時(shí)候會(huì)顯示這個(gè)模型的信息之類的,一般不用他自帶的,一般是自己寫或者是自己改,一般設(shè)置 false 就可以。
geocoder
這個(gè)是地名查找組件,開啟后,右上角會(huì)出現(xiàn)一個(gè)查詢地址的組件,但是不好用,一般情況下,不顯示就行。

navigationHelpButton
這是一個(gè)幫助按鈕,如果設(shè)置為 true 的時(shí)候,右上角會(huì)有一個(gè)幫助的提示,這個(gè)關(guān)閉就可以。

好了,這就是常見的幾個(gè)設(shè)置,當(dāng)然如果需要的話,可以根據(jù)官方文檔進(jìn)行相應(yīng)的設(shè)置,這個(gè)玩意兒很多設(shè)置,這只是其中幾個(gè)。
多余樣式隱藏
我們使用 cesium 的時(shí)候發(fā)現(xiàn)哈,左下角會(huì)有一些版權(quán)信息,我們想隱藏的話也很簡(jiǎn)單。
只需要在 viewer 創(chuàng)建完成之后,把他的版權(quán) DOM 設(shè)置為 none 就可以了。
this.viewer._cesiumWidget._creditContainer.style.display = 'none'
這樣界面就干凈多了,除了藍(lán)星啥都沒(méi)有了。

地形數(shù)據(jù)
我們看到,我們加載的藍(lán)星是三維的,所以說(shuō)嘞,和百度地圖、高德地圖不一樣,他能體現(xiàn)出一個(gè)地區(qū)的地形數(shù)據(jù),比如說(shuō)我們找一下四川山區(qū)那邊看一下子。

很明顯,看到了山地,我們?cè)谏晕⒎糯笠稽c(diǎn)兒。

盡管圖層顯示有山地,但是還想只是圖像有山,但是實(shí)際上還是平面的!沒(méi)關(guān)系哈!我們可以向 cesium 中添加一下地形數(shù)據(jù),這樣,他就是立體的了!
首先說(shuō)明一下,我們添加的地形數(shù)據(jù)是公開的,不是私密的,也就是說(shuō),他可能不是很詳細(xì)很具體,但是是有的,比如有的單位需要做山東的 gis,那么人家會(huì)提供山東地形數(shù)據(jù),這種數(shù)據(jù)肯定都是涉密的,這個(gè)數(shù)據(jù)相當(dāng)具體,數(shù)據(jù)相當(dāng)龐大,哪里高哪里底都很詳細(xì)。但是我們沒(méi)有,所以說(shuō)呢,我們使用的,只能展示大概的地形。
怎么使用很簡(jiǎn)單。我們?cè)跇?gòu)造器函數(shù)中創(chuàng)建一個(gè)地形對(duì)象:
let terrain = new Cesium.createWorldTerrain({
requestWaterMask: true,
requestVertexNormals: true
})
創(chuàng)建完成,然后在 viewer 中把這個(gè)地形數(shù)據(jù)添加進(jìn)去就可以了。
this.viewer.terrainProvider = terrain // 加入世界地形圖
好的,這個(gè)時(shí)候我們?cè)賮?lái)看一下山區(qū)能不能顯示出高度來(lái)。

誒!地形添加出來(lái)了! 厲害!
有兩個(gè)地方需要說(shuō)一下。
this.viewer.scene.globe.depthTestAgainstTerrain = true // 地形遮擋
this.viewer.scene.postProcessStages.fxaa.enabled = true // 開啟抗鋸齒
首先第一個(gè)是地形遮擋,這是啥意思呢,就是說(shuō)如果我們?cè)谒{(lán)星添加一個(gè)模型,如果這個(gè)模型被山體給擋住的話,那么這個(gè)模型就看不到了,不會(huì)透視的看到模型。在比如說(shuō),如果模型在地下,不在地表或者是地上,這個(gè)模型如果符合實(shí)際的話,應(yīng)該不能被看到的,但是加載上發(fā)現(xiàn)我們能夠看到地底的模型,如果不想看到的話,開啟一下地形遮擋,這樣的話模型會(huì)被地形給擋起來(lái),符合實(shí)際。
第二個(gè)是抗鋸齒,啥是抗鋸齒呢,做過(guò) threejs 的應(yīng)該接觸過(guò),我解釋不好,意思就是渲染的更精致,當(dāng)然性能消耗的也會(huì)大一些。
這兩個(gè)設(shè)置根據(jù)自己的實(shí)際情況選擇是否開啟哈,好了,不多說(shuō)了。
添加第三方底圖
我們到現(xiàn)在使用的底圖都是 cesium 默認(rèn)提供的,如果我們需要用自己的底圖也很簡(jiǎn)單,無(wú)論是 geoserve 自己發(fā)布的還是 高德底圖 、百度底圖、天地圖底圖都是可以的,但是有一點(diǎn)需要說(shuō)一下哈,就是這個(gè)高德、百度、google 底圖都是有偏移的,直接放進(jìn)來(lái)可能會(huì)有偏差,所以說(shuō)建議使用天地圖的。
我們先創(chuàng)建一個(gè)底圖對(duì)象,使用天地圖需要申請(qǐng) tk 值,這個(gè)自己去申請(qǐng),如果不會(huì)的話,看我關(guān)于天地圖的博文,里面有介紹和步驟。
// 矢量圖
TDT_SL = new Cesium.WebMapTileServiceImageryProvider({
url: 'http://{s}.tianditu.gov.cn/vec_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=vec&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=這是你申請(qǐng)的天地圖的tk值',
layer: 'vec',
style: 'default',
format: 'tiles',
tileMatrixSetID: 'w',
subdomains: ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7'],
maximumLevel: 18
})
然后創(chuàng)建完成添加到 cesium 就可以了。
// 添加天地圖矢量圖 (底圖對(duì)象,層級(jí)) 返回圖層
this.TDL_YX_LAY = this.viewer.imageryLayers.addImageryProvider(this.TDL_SL, 1)
這樣就添加進(jìn)來(lái)了。

放大看一下哈,當(dāng)然,天地圖是中國(guó)的,所以說(shuō)只有中國(guó)的詳細(xì)信息。

都是可以的哈。移除的話也很簡(jiǎn)單的啦。
this.viewer.imageryLayers.remove(this.TDL_YX_LAY) // 移除某個(gè)圖層(注意傳的參數(shù))
或者
this.viewer.imageryLayers.removeAll(true) // 移除所有圖層并銷毀
好了,這就是關(guān)于添加第三方圖層相關(guān)操作了。
銷毀 cesium
創(chuàng)建說(shuō)完了,現(xiàn)在說(shuō)一下銷毀,銷毀的話就很簡(jiǎn)單了,就是一句話:
this.viewer.destroy()
執(zhí)行上面這句話就直接銷毀了,這個(gè)一般是組件注銷了,不展示 cesium 的時(shí)候使用,防止一直占用資源啥的,具體情況具體分析哈!
地圖放大縮小
其實(shí) cesium 的放大縮小和百度地圖他們不一樣。cesium 的放大縮小其實(shí)就是相機(jī)高度,相機(jī)高度變小就是放大,相機(jī)高度變大就是縮小。理解吧?相機(jī)高了,視角大了,藍(lán)星上面東西就小了。
首先是放大:
/**
* 地圖放大
*/
zoomIn() {
// viewer 為 Viewer 對(duì)象
let position = this.viewer.camera.position;
let cameraHeight = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(position).height;
// 每次縮小 20 倍,參數(shù)可改
let moveRate = cameraHeight / 20.0;
this.viewer.camera.moveForward(moveRate);
}
然后是縮?。?/p>
/**
* 地圖縮小
*/
zoomOut() {
// viewer 為 Viewer 對(duì)象
let position = this.viewer.camera.position;
let cameraHeight = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(position).height;
// 每次縮小 20 倍,參數(shù)可改
let moveRate = cameraHeight / 20.0;
this.viewer.camera.moveBackward(moveRate);
}
道理都是一樣的!
可以寫兩個(gè)按鈕分別是“放大”和“縮小”,點(diǎn)擊按鈕的時(shí)候,執(zhí)行這兩個(gè)方法就可以了!
獲取可視區(qū)域、高度、層級(jí)
獲取可視區(qū)域這個(gè)很簡(jiǎn)單呀,一行代碼完事:
let rectangle = this.viewer.camera.computeViewRectangle();
我們可以看一下返回的數(shù)據(jù):

我們看到哈,打印出來(lái)的坐標(biāo)不是地理坐標(biāo),轉(zhuǎn)換一下就可以了。
let rectangle = this.viewer.camera.computeViewRectangle();
let east = Cesium.Math.toDegrees(rectangle.east).toFixed(6); // 轉(zhuǎn)地理坐標(biāo)
console.log(rectangle, east)

如果需要的話,挨個(gè)轉(zhuǎn)換一下也可以。
獲取相機(jī)高度的話更簡(jiǎn)單了。
let height = Math.ceil(this.viewer.camera.positionCartographic.height).toFixed(0);
console.log("相機(jī)高度----->> ", height)
直接打印一下看結(jié)果:

這個(gè)單位是米哈。
然后是層級(jí),就和百度高德一樣,層級(jí)顯示:
let zoom = this.heightToZoom(height).toFixed(0)
console.log("層級(jí)----->> ", zoom)
這是需要用到的一個(gè)方法,為啥里面是這樣,我也不知道,我也是從網(wǎng)上抄的這個(gè)方法:
heightToZoom(height) {
let A = 40487.57;
let B = 0.00007096758;
let C = 91610.74;
let D = -40467.74;
return Math.round(D + (A - D) / (1 + Math.pow(height / C, B)));
}
然后看一下結(jié)果:

真棒!