Cesium基礎使用介紹
前言
最近折騰了一下三維地球,本文簡單為大家介紹一款開源的三維地球軟件——Cesium,以及如何快速上手Cesium。當然三維地球重要的肯定不是數據顯示,這只是數據可視化的一小部分,重要的應該是背後的數據生成及處理等。本文先為大家介紹這簡單的部分。
一、 Cesium簡介
Github地址:https://github.com/AnalyticalGraphicsInc/cesium。官方介紹如下:
An open-source JavaScript library for world-class 3D globes and maps.
非常簡潔:Cesium是一款開源的基於JS的3D地圖框架。具體這裏也不多做介紹,各位可以自行瀏覽其網站。其實他就是一個地圖可視化框架,與Leaft-let以及OpenLayer等沒有本質的區別,只是Cesium支持三維場景,做的更漂亮。
二、 Cesium簡單使用
2.1 安裝及測試
最簡單的安裝方式,就是普通的JS文件加載,只需要從Github中下載其js代碼,放到自己的項目中,在html頁面中引用即可。如下:
當然如果要直接使用其示例等,還是需要按照其文檔使用nodejs一步步安裝。安裝完之後,新建html頁面並引用Cesium.js,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello 3D Earth</title>
<script src="CesiumUnminified/Cesium.js"></script>
<style>
@import url(CesiumUnminified/Widgets/widgets.css);
html, body, #cesiumContainer {
width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
}
</style>
</head>
<body>
<div id="cesiumContainer"></div>
<script src="app.js"></script>
</body>
</html>
app.js只需要一行代碼即可,內容如下:
viewer = new Cesium.Viewer('cesiumContainer');
其中cesiumContainer為html中的地圖顯示div的id。就是這麽簡單,瀏覽器打開上述html頁面,便可看到一個三維地球。底圖為微軟影像只是加載到了三維地球上,包含放大、縮小、平移等基本在線地圖功能,同時還包含了時間軸等與時間有關的控件,這是Cesium的一個特色,其地圖、對象以及場景等能與時間相關聯。
2.2 Viewer和地圖圖層
2.2.1 Viewer
Viewer是Cesium的核心,上面的一行代碼實現了基本框架的加載,我們可以為其添加參數,實現不同類型的地圖加載,如下:
var viewer = new Cesium.Viewer("cesiumContainer", {
animation: true, //是否顯示動畫控件(左下方那個)
baseLayerPicker: true, //是否顯示圖層選擇控件
geocoder: true, //是否顯示地名查找控件
timeline: true, //是否顯示時間線控件
sceneModePicker: true, //是否顯示投影方式控件
navigationHelpButton: false, //是否顯示幫助信息控件
infoBox: true, //是否顯示點擊要素之後顯示的信息
});
這裏面設置了地圖瀏覽中幾個控件的顯示與否。這裏主要介紹baseLayerPicker項,他可以設置圖層選擇空間是否可見,如果設置不可見,則需要設置自定義圖層作為默認圖層。當然設置可見之後也可以更改其中的圖層為自定義圖層。
2.2.2 圖層介紹
Cesium中的圖層分為兩種:一種是普通圖層,包含影像、線劃等普通顯示圖層;還有一種是地形圖層,用於真實的模擬地球表面的場景,Cesium會根據加載到的地形瓦片以三維的方式顯示出山川、大海等。
那麽首先來介紹一下在Cesium中如何創建一個圖層。
第一種方式可以直接在基本圖層上添加一個圖層,如註記等等。方式如下:
//全球影像中文註記服務
var my_layer = viewer.imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({
url: "http://t0.tianditu.com/cia_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=cia&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default.jpg",
layer: "tdtAnnoLayer",
style: "default",
format: "image/jpeg",
tileMatrixSetID: "GoogleMapsCompatible",
show: false
}));
這段代碼實現在影像的基礎上疊加天地圖註記層。當然也可以添加其他已知商業地圖的圖層或者自定義地圖,但是需要做好CORS,詳細請參考geotrellis使用(三十五)Cesium加載geotrellis TMS瓦片。
第二種方式大同小異,如下:
var my_layer = viewer.scene.imageryLayers.addImageryProvider(
new Cesium.UrlTemplateImageryProvider({
url : 'http://my_url/{z}/{x}/{y}',
format: "image/png"
})
);
區別在於不是直接加載到viewer的imageryLayers而是scene的imageryLayers,但是查看Cesium的源代碼你會發現二者是一致的,viewer.imageryLayers返回的正是viewer.scene.imageryLayers。所以二者都可以通過下述方式設置透明度和亮度,防止壓蓋等。
//50%透明度
my_layer.alpha = 0.5;
//兩倍亮度
my_layer.brightness = 2.0;
這裏就已經介紹了Cesium的兩種圖層對象:UrlTemplateImageryProvider、WebMapTileServiceImageryProvider,其還有Cesium.createTileMapServiceImageryProvider、SingleTileImageryProvider等等好幾種,只是url的組織方式不同,可以根據需要自行查閱相關源碼即可。
2.2.3 默認圖層設置
上文已經說了可以設置baseLayerPicker為false或true來控制圖層選擇控件是否可見,當設置為false的時候可以在創建viewer時添加一項來設置默認顯示的底圖,否則仍然顯示微軟的默認影像。與添加圖層的方式基本一致,如下:
imageryProvider : new Cesium.WebMapTileServiceImageryProvider({
url: "http://t0.tianditu.com/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",
layer: "tdtVecBasicLayer",
style: "default",
format: "image/jpeg",
tileMatrixSetID: "GoogleMapsCompatible",
show: false
})
當baseLayerPicker設置為true的時候,我們也可以修改裏面的默認圖層為我們想要的圖層。只需要在創建viewer時再添加一項即可,如下:
imageryProviderViewModels: imageryLayers//設置影像圖列表
terrainProviderViewModels: terrainLayers//設置地形圖列表
其中imageryLayers為影像(普通)圖層數組,terrainLayers為地形圖層數組,有關地圖圖層在下面介紹。
2.3 地形
Cesium中的地形系統是一種由流式瓦片數據生成地形mesh的技術,厲害指出在於其可以自動模擬出地面、海洋的三維效果。創建地形圖層的方式如下:
var terrainProvider = new Cesium.CesiumTerrainProvider({
url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', // 默認立體地表
// 請求照明
requestVertexNormals: true,
// 請求水波紋效果
requestWaterMask: true
});
viewer.terrainProvider = terrainProvider;
Cesium支持兩種類型的地形,STK World Terrain和Small Terrain。
2.3.1 STK World Terrain
STK World Terrain是高分辨率, 基於quantized mesh的地形。這是一種基於網格的地形,可充分利用GL中的Shader來渲染,效果相當逼真。STK World Terrain使用了多種數據源,分別適應不同地區和不同精度時的情形。比如對於美國本土使用National Elevation Dataset (NED)的高程,精度3-30米;對於歐洲使用EU-DEM高程,精度30米;對於澳洲使用Australia SRTM-derived 1 Second DEM高程,精度30米;對於-60至60緯度段使用CGIAR SRTM高程,精度90米;對於整個地球使用GTOPO30,精度1000米。STK World Terrain地形是怎樣生成的是不公開的,如需應用於封閉的局域網時,則需購買AGI的STK terrain server。但是AGI提供了一個webapi可供因特網上調用,並提供了這種地形的格式細節。就是上面的url
2.3.2 Small Terrain
Small Terrain是中等高分辨率基於heightmap的地形,渲染出的地形效果不如quantized mesh的地形,但也基本能接受。
可以由DEM數據生成這種規範的.terrain文件。生成工具見https://groups.google.com/forum/#!topic/cesium-dev/rBieaEBJHi,需要gdal庫和numpy。
2.4 坐標轉換
Cesium其實是一個封裝好的WebGL庫,當然這裏面就牽扯到好幾套坐標問題:屏幕坐標、三維空間坐標、投影坐標。而且坐標轉換肯定是我們在開發任何地理信息系統中經常會碰到的問題,也比較復雜,簡單總結了幾種轉換方式:
2.4.1 坐標系
new Cesium.Cartesian2(1,1) //表示一個二維笛卡爾坐標系,也就是直角坐標系(屏幕坐標系)
new Cesium.Cartesian3(1,1,1) //表示一個三維笛卡爾坐標系,也是直角坐標系(就是真實世界的坐標系)
2.4.2 二維屏幕坐標系到三維坐標系的轉換
var pick1= scene.globe.pick(viewer.camera.getPickRay(pt1), scene) //其中pt1為一個二維屏幕坐標。
2.4.3 三維坐標到地理坐標的轉換
var geoPt1= scene.globe.ellipsoid.cartesianToCartographic(pick1) //其中pick1是一個Cesium.Cartesian3對象。
2.4.4 地理坐標到經緯度坐標的轉換
var point1=[geoPt1.longitude / Math.PI * 180,geoPt1.latitude / Math.PI * 180]; //其中geoPt1是一個地理坐標。
2.4.5 經緯度坐標轉地理坐標(弧度)
var cartographic = Cesium.Cartographic.fromDegree(point) //point是經緯度值
var coord_wgs84 = Cesium.Cartographic.fromDegrees(lng, lat, alt);//單位:度,度,米
2.4.6 經緯度坐標轉世界坐標
var cartesian = Cesium.Cartesian3.fromDegree(point)
2.4.7 計算兩個三維坐標系之間的距離
var d = Cesium.Cartesian3.distance(
new Cesium.Cartesian3(pick1.x, pick1.y, pick1.z),
new Cesium.Cartesian3(pick3.x, pick3.y, pick3.z)
); //pick1、pick3都是三維坐標系
2.5 加載3D對象(Entity)
通過Cesium可以很清楚的將一個三維模型加載到地球中。有兩種方式可以實現此功能。
2.5.1 直接添加
var entity = viewer.entities.add({
position : Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706),
model : { uri : '../Apps/SampleData/models/CesiumGround/Cesium_Ground.gltf' }
});
viewer.trackedEntity = entity; // 鏡頭追蹤,將鏡頭固定在對象上
清晰明了,不做過多介紹。
2.5.2 添加primitives
// 這種方式會以最大最小值為縮放邊界,采用entity的方式會完全根據地圖進行縮放
var scene = viewer.scene;
var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(Cesium.Cartesian3.fromDegrees(-123.0745725, 44.0503706));
var model = scene.primitives.add(Cesium.Model.fromGltf({
url: 'data/Cesium_Ground.gltf',
//以下這些信息也均可在entity中設置
color : Cesium.Color.fromAlpha(Cesium.Color.RED, parseFloat(0.5)),//模型顏色,透明度
silhouetteColor : Cesium.Color.fromAlpha(Cesium.Color.GREEN, parseFloat(0.5)),//輪廓線
colorBlendMode : Cesium.ColorBlendMode.MIX,//模型樣式['Highlight', 'Replace', 'Mix']
modelMatrix: modelMatrix,
minimumPixelSize : 256, // 最小的縮放尺寸,256個像素,就是一個瓦片的尺寸。
maxiumScale: 2 // 最大的縮放倍數
}));
其中modelMatrix定義了對象的位置,第一種添加方式模型會自動按照gltf設置好的動畫進行播放,第二種方式則需要添加下述代碼設置動畫。
//添加動畫
Cesium.when(model.readyPromise).then(function (model) {
model.activeAnimations.addAll({
loop: Cesium.ModelAnimationLoop.REPEAT,//控制重復
speedup: 0.5, // 速度,相對於clock
reverse: true // 動畫反轉
})
});
2.6 加載GeoJson、KML、CZML數據
這幾類數據歸為一類都是矢量數據,所以這裏要介紹的就是如何加載矢量數據,當然數據量特別大的時候就需要考慮矢量瓦片,Cesium也正在開發矢量瓦片相關版本,之前看到一個折中方法是先讀取矢量瓦片而後轉換成GeoJson進行加載,這裏不做過多介紹。那麽這三類數據雖然都是矢量數據,但稍微有些不同,下面逐一介紹。
2.6.1 GeoJson
GeoJson是較為通用的一種網絡矢量數據傳輸方案。其加載方式如下:
viewer.dataSources.add(Cesium.GeoJsonDataSource.load('mydata.geojson', {
stroke: Cesium.Color.BLUE.withAlpha(0.8),
strokeWidth: 2.3,
fill: Cesium.Color.RED.withAlpha(0.3),
clampToGround : true
}
));
Cesium.GeoJsonDataSource.load函數即為加載geojson數據,並配置相關屬性。通過這種方式就可將數據加載到三維地球中,並設置邊線以及填充等,clampToGround用於設置對象是否貼著地形,如為true則對象會隨地勢起伏而變化。當然我們可以為geojson中的各個要素設置不同的渲染方式,如下:
Cesium.Math.setRandomNumberSeed(0);
var promise = Cesium.GeoJsonDataSource.load('data/county3.geojson'); // load完之後即為一個promise對象
promise.then(function(dataSource) { // 此處類似於添加3D對象中的動畫。
viewer.dataSources.add(dataSource); // 先添加對象
var entities = dataSource.entities.values; // 獲取所有對象
var colorHash = {};
for (var i = 0; i < entities.length; i++) { // 逐一遍歷循環
var entity = entities[i];
var name = entity.properties.GB1999; // 取出GB1999屬性內容
var color = colorHash[name]; // 如果GB1999屬性相同,則賦同一個顏色。
if (!color) {
color = Cesium.Color.fromRandom({
alpha : 1.0
});
colorHash[name] = color;
}
entity.polygon.material = color; // 設置polygon對象的填充顏色
entity.polygon.outline = false; // polygon邊線顯示與否
entity.polygon.extrudedHeight = entity.properties.POPU * 1000; // 根據POPU屬性設置polygon的高度
}
});
viewer.zoomTo(promise);
此種方式實現原理為先load數據,而後逐一設置load後數據的entity。geojson中的對象的屬性可以通過entity.properties.GB1999的方式取出,其中GB1999表示屬性名稱。註意數據最好是84投影經緯度坐標,下同。
2.6.2 KML
KML是Google Earth定義的一種矢量數據組織方式,其加載方式與GeoJson基本相同,如下:
var promise = Cesium.KmlDataSource.load('data.kml');
剩下的處理方式與GeoJson相同。
2.6.3 CZML
CZML是Cesium中很重要的一個概念,也是一個亮點,CZML使得cesium很酷很炫地展示動態數據成為可能。CZML是一種JSON格式的字符串,用於描述與時間有關的動畫場景,CZML包含點、線、地標、模型、和其他的一些圖形元素,並指明了這些元素如何隨時間而變化。某種程度上說, Cesium 和 CZML的關系就像 Google Earth 和 KML。
CZML的一個典型結構如下
[
// packet one
{
"id": "GroundControlStation"
"position": { "cartographicDegrees": [-75.5, 40.0, 0.0] },
"point": {
"color": { "rgba": [0, 0, 255, 255] },
}
},
// packet two
{
"id": "PredatorUAV",
// ...
}
]
CZML可以記錄對象與時間的關系,其時間序列相關屬性如下:
{
// ...
"someInterpolatableProperty": {
"cartesian": [
"2012-04-30T12:00Z", 1.0, 2.0, 3.0, //表示當時間為2012-04-30T12:00Z,坐標為(1,2,3)
"2012-04-30T12:01Z", 4.0, 5.0, 6.0, //表示當時間為2012-04-30T12:01Z,坐標為(4,5,6)
"2012-04-30T12:02Z", 7.0, 8.0, 9.0 //表示當時間為2012-04-30T12:02Z,坐標為(7,8,9)
]
}
}
{
// ...
"someInterpolatableProperty": {
"epoch": "2012-04-30T12:00Z", //表示時間起點為2012-04-30T12:00:00
"cartesian": [
0.0, 1.0, 2.0, 3.0, //從起點開始,第0秒時坐標為(1,2,3)
60.0, 4.0, 5.0, 6.0, //從起點開始,第60秒時坐標為(4,5,6)
120.0, 7.0, 8.0, 9.0 //從起點開始,第120秒時坐標為(7,8,9)
]
}
}
{
// ...
"someInterpolatableProperty": {
"epoch": "2012-04-30T12:00Z",
"cartesian": [
0.0, 1.0, 2.0, 3.0,
60.0, 4.0, 5.0, 6.0,
120.0, 7.0, 8.0, 9.0
],
"interpolationAlgorithm": "LAGRANGE", //插值算法為LAGRANGE,還有HERMITE,GEODESIC
"interpolationDegree": 5 //1為線性插值,2為平方插值
},
}
具體的可以查閱相關資料。將CZML數據載入場景的方式與前兩者一致,加載完後處理方式也基本一致,如下:
dataSource = new Cesium.CzmlDataSource();
var czml = 'data/Vehicle.czml';
dataSource.load(czml);
viewer.dataSources.add(dataSource);
2.7 加載3D Tile
3D瓦片可以顯示建築物、地標乃至森林廣告牌等等以及其對應的屬性信息。每個3D瓦片就是一個3D對象,具體的數據範圍等等信息在tileset.json中定義。
2.7.1 加載
var tileSet = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
// 同樣url只定義編號前面的部分,具體的編號(數字或者非數字都有可能)在此URL下的tileset.json文件中定義,包括此3d瓦片圖層的範圍等等。
url: 'https://beta.cesium.com/api/assets/1461?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiMTBjN2E3Mi03ZGZkLTRhYmItOWEzNC1iOTdjODEzMzM5MzgiLCJpZCI6NDQsImlhdCI6MTQ4NjQ4NDM0M30.B3C7Noey3ZPXcf7_FXBEYwirct23fsUecRnS12FltN8&v=1.0'
}));
tileSet.readyPromise.then(function (tileset) {
viewer.camera.viewBoundingSphere(tileset.boundingSphere, new Cesium.HeadingPitchRange(0, -0.5, 0));
viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
});
2.7.2 tileset.json文件
不同分辨率顯示不同3D瓦片,全靠此文件定義。
{
"asset": {
"version": "1.0"
},
"geometricError": 500,
"root": {
"transform": [
96.86356343768793,
24.848542777253734,
0,
0,
-15.986465724980844,
62.317780594908875,
76.5566922962899,
0,
19.02322243409411,
-74.15554020821229,
64.3356267137516,
0,
1215107.7612304366,
-4736682.902037748,
4081926.095098698,
1
],
"boundingVolume": {
"box": [
0,
0,
0,
7.0955,
0,
0,
0,
3.1405,
0,
0,
0,
5.0375
]
},
"geometricError": 100,
"refine": "REPLACE",
"content": {
"url": "dragon_low.b3dm"
},
"children": [
{
"boundingVolume": {
"box": [
0,
0,
0,
7.0955,
0,
0,
0,
3.1405,
0,
0,
0,
5.0375
]
},
"geometricError": 10,
"content": {
"url": "dragon_medium.b3dm"
},
"children": [
{
"boundingVolume": {
"box": [
0,
0,
0,
7.0955,
0,
0,
0,
3.1405,
0,
0,
0,
5.0375
]
},
"geometricError": 0,
"content": {
"url": "dragon_high.b3dm"
}
}
]
}
]
}
}
其中boundingVolume.region 屬性是包含六個元素的數組對象,用於定義邊界地理區域,格式是[west,
south, east, north, minimum height, maximum height]。經度和維度以弧度為單位,高度以米為單位(高於或低於WGS84橢球體)除了 region,也有其他邊界體可以用,比如 box 和 sphere。其余各個字段包含信息可以查閱官方手冊。
2.7.3 支持的格式
b3dm: Batched 3D Model 用於展示城市建築等大規模的3D對象
l3dm: Instanced 3D Model 用於展示模型等。
pnts: Point Cloud 用於展示大量的3D點。
vctr: Vector Data 用於展示矢量元素,代替KML(那麽CZML呢?動畫?)
cmpt: Composite 用於合並異構3D瓦片,如將城市建築的b3dm和樹的i3dm合在一起展示。
2.7.4 Style
可以根據對象的屬性信息進行不同的可視化處理,包括顏色、顯示與否等等。
var styleJson = {
color : {
conditions : [
["${height} > 70.0", "rgb(0, 0, 255)"],
["${height} > 50.0", "rgb(0, 255, 0)"],
["${height} > 30.0", "rgb(0, 255, 255)"],
["${height} > 10.0", "color('purple', 1)"],
["${height} > 1.0", "color('gray', 0.5)"],
["true", "color('blue')"] // conditions
]
},
show : '${height} > 0',
meta : {
description : '"Building id ${id} has height ${height}."'
}
};
tileSet.style = new Cesium.Cesium3DTileStyle(styleJson);
註意conditions中條件必須閉合,不能出現分類不完整,所以一般最後會加一個true項,相當於default。
三、 總結
本文簡單介紹了Cesium三維數據可視化框架以及其簡單的使用,比較籠統細節也並未深究可能也有錯漏,感興趣的可以自行查閱相關資料,隨著學習的深入我也會增加些詳細信息。總之,Cesium是一款不錯的3D地圖數據可視化引擎,值得擁有。
Cesium基礎使用介紹