Ceisum官方教程2 -- 項目實例(workshop)

原文地址:https://cesiumjs.org/tutorials/Cesium-Workshop/
概述
我們很高興歡迎你加入Cesium社區!爲了讓你能基於Cesium開發自己的3d 地圖項目,這個教程將從頭到尾講解一個基礎的Cesium程序的開發過程。這個教程將用到很多重要的CesiumAPI,但是並不是所有的(CesiumJS有很多很多功能)。我們目標是教會你基於Cesium做開發的基本原則和工具,在你的項目裏能舉一反三,解決其他問題。
我們創建一個簡單的程序去可視化紐約市的一些地理位置。我們將加載各種類型各種樣式的二維和三維數據,並且創建若干個相機位置,並且展示一些用戶交互的UI。最後,做爲一個高科技地圖,我們加載了一個無人機三維模型,充分利用3d可視化的優勢去觀察一些地理位置。
在完成教程後,你對Cesium的功能會有幾個基本概念,包括配置viewer、加載數據、創建各種樣式的幾何體、使用3d tiles(三維模型切片)、控制相機、增加鼠標交互事件。
Ceisum官方教程2 -- 項目實例(workshop)

帶交互的可視化紐約城地理位置
步驟
再開發前的幾個必備步驟:

訪問這個頁面確認你的電腦環境適合Cesium Cesium Viewer. 如果沒有看到地球? 點這個鏈接 Troubleshooting.
安裝Node.js.
下載教程代碼 workshop code。使用git clone 或者手動下載zip並解壓縮。
在cmd命令行下,使用cd命令定位到 cesium-workshop目錄下.
運行 npm install。
運行 npm start。

控制檯應該輸出下面信息:
Ceisum官方教程2 -- 項目實例(workshop)
workshop運行
Ceisum官方教程2 -- 項目實例(workshop)
注意不能關閉控制檯窗口,開發中需要保證這個進程運行着。
下一步, 在瀏覽器裏打開 localhost:8080。你應該能看到我們的程序已經運行了。
注意
這個教程裏提到的workshop是基於cesium1.45開發的,裏面的地形服務器已經失效了,導致cesium加載並不成功,使用這個代碼看不到效果。
Ceisum官方教程2 -- 項目實例(workshop)
workshop已經運行不起來了
解決方法也很簡單,我們使用Cesium最新版1.51裏的文件替換到如下目錄
Ceisum官方教程2 -- 項目實例(workshop)
替換cesium庫
再次刷新頁面,就可以了,效果如下:
Ceisum官方教程2 -- 項目實例(workshop)
替換cesiumjs庫後的加載效果
程序目錄
在程序根目錄下,有如下文件和文件夾. 這個程序已經被設計爲儘可能的簡單,只包含cesiumjs的庫。

Source/ : 我們項目的代碼。

ThirdParty/ : 外部js庫,目前只包含cesium。

LICENSE.md : 我們項目的說明條款。

index.html : 主頁,包含項目程序代碼和頁面結構。

server.js : 簡單的基於nodejs的http服務器。

CesiumJS是完全兼容現代javascript 庫和框架,所以放心大但的使用。
下面是一些示例:

Cesium and webpack 教程展示了使用webpack集成cesium去更高效的開發web項目。
React集成
CesiumJS和Threejs集成

頁面結構
下來我們看看index.html。爲cesium的控件創建div,以及一些輸入元素。我們注意到,Cesium的控件就是一個普通的div,它可以被css樣式設置,並且和其他div交互。
有一些關鍵的行:
引入CesiumJS
受限在html的標籤內引用cesium.js。這個定義了Cesium對象,並且包含整個CesiumJS的庫。
Ceisum官方教程2 -- 項目實例(workshop)
爲了減小開發的項目最終的js文件大小,當然你也可以包含ThirdParty/Cesium/Source/目錄下的獨立的Cesium源碼模塊。不過我們爲了簡單的測試API,我們直接包含了整個CesiumJS庫。
HTML結構
在HTML的body部分,有一個div爲了創建Cesium控件。
Ceisum官方教程2 -- 項目實例(workshop)
爲了在div創建成功後再執行其他代碼,可以再HTML的body部分增加script標籤去引用js文件。Ceisum官方教程2 -- 項目實例(workshop)
頁面樣式
使用index.css文件定義了HTML元素的樣式,可以在HTML的head元素裏引用它。
Ceisum官方教程2 -- 項目實例(workshop)
Cesium的所有小控件下面這個CSS來定義樣式。需要在index.css之前引用。
Ceisum官方教程2 -- 項目實例(workshop)
我們的頁面已經有了基本樣式,並且我們在index.css設定的樣式可以覆蓋Cesium默認的控件樣式。
工作流程
步驟如下:

使用你最擅長的文本編輯器(推薦sublime)打開 Source/App.js,並且清空裏面內容。
把文件Source/AppSkeleton.js的內容拷貝到 Source/App.js。
確認你的http服務還在 cesium-workshop 目錄運行着。
使用你的瀏覽器打開 localhost:8080.推薦使用chrome,但是現在瀏覽器都可以. 你應該能看到一個黑色背景。
在代碼裏去掉註釋,保存 Source/App.js,刷新瀏覽器,應該有些效果改變了。

還有問題? 那你先跟着sandcastle去做一個沒有UI的簡單程序:

完整的代碼
註釋的代碼

下來我們真正開始。
創建Viewer
Cesium的最基礎對象就是 Viewer, 一個具有很多功能的3d地球的黑盒子. 使用下面的代碼創建viewer並附着到id爲 "cesiumContainer"`的div上。
Ceisum官方教程2 -- 項目實例(workshop)
這簡單的一行代碼實際包含了很多內容,成功後你應該能看見基礎的地球,像下面一樣:

Ceisum官方教程2 -- 項目實例(workshop)
基礎地球默認情況下這個場景能處理鼠標和觸摸事件。 試下下面的相機控制方法:
左鍵單擊和拖拽 - 沿着地球表面平移(調整相機位置).
右鍵單擊和拖拽 - 相機放大縮小(調整相機距離).
滾輪 - 相機放大縮小(調整相機距離).
中間按下和拖拽 - 圍繞地球表面旋轉相機(調整相機方向)。
除了地球, Viewer還默認包含了一些有用的控件:
Ceisum官方教程2 -- 項目實例(workshop)
Cesium控件
Geocoder : 地理位置查詢定位控件,默認使用bing地圖服務.

HomeButton : 默認相機位置。

SceneModePicker : 3D、2D和哥倫布模式的切換按鈕.

BaseLayerPicker : 選擇地形、影像等圖層。

NavigationHelpButton : 顯示默認的相機控制提示.

Animation : 控制場景動畫的播放速度.

CreditsDisplay : 展示數據版權屬性。

Timeline : 時間滾動條。

FullscreenButton : 全屏切換。

可以傳遞一個options對象做爲配置參數,去控制上面這些控件的顯示或者不顯示。對於示例代碼,刪除第一行,打開後面幾行的註釋,代碼如下:
Ceisum官方教程2 -- 項目實例(workshop)
這幾行代碼創建了一個不包含選擇指示器(selection indicators),基礎底圖選擇控件的viewer。完整的options配置看文檔Viewer 。
影像圖層
影像是Cesium程序一個關鍵元素。它是覆蓋在地表的各種不同精度的圖像集合。根據相機的朝向和距離,Cesium將請求和渲染不同LOD或者縮放級別下的圖像。
Cesium支持多個影像圖層同時加載、刪除、排序和調整。
Cesium爲影像圖層提供了大量方法,類似調整顏色、混合等。下面是Sandcastle中的一些示例代碼:

影像圖層基本效果
影像圖層調整顏色
調整影像圖層順序
影像的屏幕分割(捲簾效果)

Cesium提供了多種影像數據來源 多用影像數據源 。
支持的格式:

WMS
TMS
WMTS (with time dynamic imagery)
ArcGIS
Bing Maps
Google Earth
Mapbox
Open Street Map

Cesium默認使用Bing map的影像圖層。這個影像圖層經常用來做demo演示。爲了使用這個影像,需要創建一個Cesium ion賬戶,並且生成一個訪問token。
(譯者注:考慮到國內的環境,修改了官方的示例,直接加載谷歌地圖的影像)
Ceisum官方教程2 -- 項目實例(workshop)
運行後有如下效果:
Ceisum官方教程2 -- 項目實例(workshop)
添加谷歌底圖效果
後續教程還有一篇專門講影像圖層的 影像圖層教程.
地形圖層
Cesium支持漸進流式加載和渲染全球高精度地形,並且包含海、湖、河等水面效果。相對2D地圖,山峯、山谷等其他地形特徵的更適宜在這種3D地球中展示。和影像圖層一樣,Cesium需要在服務端預先把地形數據處理爲切片形式,在客戶端基於當前相機位置去請求和渲染地形切片。
下面是一些示例和地形數據集以及配置選項:

ArcticDEM : 高精度北極地形。

PAMAP Terrain : 高精度賓夕法尼亞州地形

地形配置 : 地形配置和格式

地形誇張 : 使地形起伏差異更大

支持的格式:

Quantized-mesh, Cesium團隊定義的不規則地形三角網格式。
Heightmap
Google Earth Enterprise
爲了增加一個地形數據,我們需要創建一個 CesiumTerrainProvider, 設置一個url以及很少的幾個配置項,然後把這個provider設置到 viewer.terrainProvider.

這裏,我們使用 Cesium全球地形,這個數據存儲在Cesium ion服務器上,已經默認到你的賬戶裏的“My Assets”中。這種前提下,我們使用createWorldTerrain輔助函數去創建 Cesium全球地形 .
Ceisum官方教程2 -- 項目實例(workshop)
requestWaterMask 和 requestVertexNormals 兩個選項都是可選的,他們告知Cesium去請求額外的水面數據和光照數據。 默認都爲false.

最終,我們有了地形效果,我們可能需要再寫一行代碼,確保地形以下的物體不可見。
Ceisum官方教程2 -- 項目實例(workshop)
紐約的地表非常平,可以漫遊到其他地方去瀏覽. 爲了明顯看到效果,可以到珠峯附近去查看。
Ceisum官方教程2 -- 項目實例(workshop)
珠峯地形
後續有一個地形的詳細教程 地形教程.

場景配置
爲了我們的viewer的展示時間和空間正確,需要一些更多的配置。這部分主要和 viewer.scene打交道, 這個類控制了我們的viewer中所有的圖形元素。
使用下面這句話,開啓全球光照,光照方向依據太陽方向。
Ceisum官方教程2 -- 項目實例(workshop)
隨着時間的變化,光照方向也在變換。如果縮小後,我們能看到一部分的地球是黑色的,因爲這部分此時晚上。
在初始化視圖之前,先學下基本的cesium 類型:

Cartesian3 : 三維笛卡爾(直角)座標 – 當用來表示位置的時候,這個座標指在地固座標系(Earth fixed-frame (ECEF))下,相對地球中心的座標位置,單位是米。

Cartographic :使用經緯度(弧度)和高度(WGS84地球高程)描述的三維座標 。

HeadingPitchRoll :
在ENU(East-North-Up)座標系中,相對座標軸的旋轉(弧度)。Heading 相對負z軸(垂直向下). Pitch 相對負y軸. Roll相對正x軸.

Quaternion :使用四維座標描述的三維旋轉。
這是在Cesium的scene中擺放對象的基本類型,Cesium提供了一系列的方便的轉換函數。具體請查看cesium文檔。

現在,我們把相機定位到我們數據所在的位置--紐約。
相機控制
Camera 是 viewer.scene的一個屬性,用來控制當前可見範圍。使用Cesium Camera API 我們可以直接設置相機的位置和朝向。
一些最常用的方法:

Camera.setView(options) : 立即設置相機位置和朝向。

Camera.zoomIn(amount) : 沿着相機方向移動相機。

Camera.zoomOut(amount) : 沿着相機方向遠離

Camera.flyTo(options) : 創建從一個位置到另一個位置的相機飛行動畫。

Camera.lookAt(target, offset) : 依據目標偏移來設置相機位置和朝向。

Camera.move(direction, amount) : 沿着direction方向移動相機。

Camera.rotate(axis, angle) : 繞着任意軸旋轉相機。

更詳細的可以去學習下面兩個示例:

Camera API示例

自定義相機控制
我們測試一個方法,把相機位置放置到紐約。分別使用一個 Cartesian3表示位置,一個HeadingPitchRoll表示朝向。
個HeadingPitchRoll表示朝向。

// 創建相機初始位置和朝向
var initialPosition = new Cesium.Cartesian3.fromDegrees(-73.998114468289017509, 40.674512895646692812, 2631.082799425431);
var initialOrientation = new Cesium.HeadingPitchRoll.fromDegrees(7.1077496389876024807, -31.987223091598949054, 0.025883251314954971306);
var homeCameraView = {
destination : initialPosition,
orientation : {
heading : initialOrientation.heading,
pitch : initialOrientation.pitch,
roll : initialOrientation.roll
}
};
// 設置視圖
viewer.scene.camera.setView(homeCameraView);
使用一個js對象保存相機的參數,設置後,相機此時是垂直俯視曼哈頓(Manhattan)。
事實上,我們可以使用這個view參數來更改home按鈕的效果。與其設置地球的默認視圖參數,我們還不如重寫這個按鈕,點擊之後飛行到曼哈頓。可以通過其他參數來調節動畫過程,並且可以設置一個事件監聽取消默認的飛行過程,然後調用新的flyto()函數飛到我們設置的位置:
Ceisum官方教程2 -- 項目實例(workshop)
參看這篇教程學習更多相機操作方法 camera教程.
時間控制
下來,我們通過配置viewer的 時鐘(Clock) 和時間線(Timeline) 去控制場景中的時間流逝。
時鐘(clock)API教程.
Cesium使用 JulianDate 描述某個時刻,這個時間存儲了自從公元前4712年1月1日中午的天數。爲了提高精度,這個類裏分開存儲了時刻的日期部分和時刻的秒部分。爲了數學運算的安全和閏秒(leap seconds)的問題,這個時刻是按照國際原子時標準(International Atomic Time standard)存儲的。
下面是一些關於scene中時間的配置選項:
// 設置時鐘和時間線
viewer.clock.shouldAnimate = true; // 當viewer開啓後,啓動動畫
viewer.clock.startTime = Cesium.JulianDate.fromIso8601("2017-07-11T16:00:00Z");
viewer.clock.stopTime = Cesium.JulianDate.fromIso8601("2017-07-11T16:20:00Z");
viewer.clock.currentTime = Cesium.JulianDate.fromIso8601("2017-07-11T16:00:00Z");
viewer.clock.multiplier = 2; // 設置加速倍率
viewer.clock.clockStep = Cesium.ClockStep.SYSTEM_CLOCK_MULTIPLIER; // tick computation mode(還沒理解具體含義)
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; // 循環播放
viewer.timeline.zoomTo(viewer.clock.startTime, viewer.clock.stopTime); // 設置時間的可見範圍
上述代碼設定了場景動畫播放速率,開始和結束時間,並且設置爲循環播放。並且設置了時間線控件在合適的時間範圍。使用這個 示例 去試驗更多時間設置
初始化配置完成了,當你運行代碼,能看到如下效果
Ceisum官方教程2 -- 項目實例(workshop)
初始化程序
Entities加載和樣式配置
上面我們程序裏已經添加了viewer 、影像圖層、地形圖層。下來重點說項目裏的示例點位數據(the sample geocache data)。
爲了更方便的可視化,Cesium支持流行的矢量格式GeoJson和KML,同時也支持我們團隊定義的一種格式 CZML.
無論最初是什麼格式,所有的空間矢量數據在Cesium裏都是使用Entity 相關API去展示的。Entity API 使用了靈活高效的可視化渲染方式。 Entity是一種對幾何圖形做空間和時間展示的數據對象。sandcastle 裏提供了很多簡單的entity。
爲了能快速的學習Entity API,建議先花點時間去讀下 空間數據可視化教程 。
下面一些使用Entity API的示例:

Polygon
Polyline
Billboard
Label

一旦你已經理解了Entity是什麼東西,使用Cesium加載數據就很容易理解了。爲了讀取數據文件,需要根據你的數據格式創建一個合適的 DataSource ,它將負責解析你配置的url裏的數據,然後創建一個[EntityCollection]用來存儲從數據里加載的每一個Entity 。DataSource 只是定義一些接口,依據數據格式的不同會有不同的解析過程。比如,KML使用KmlDataSource。如下面代碼:
var kmlOptions = {
camera : viewer.scene.camera,
canvas : viewer.scene.canvas,
clampToGround : true
};
// 從這個KML的url里加載POI點位 : http://catalog.opendata.city/dataset/pediacities-nyc-neighborhoods/resource/91778048-3c58-449c-a3f9-365ed203e914
var geocachePromise = Cesium.KmlDataSource.load('./Source/SampleData/sampleGeocacheLocations.kml', kmlOptions);

這段代碼使用 KmlDataSource.load(optinos) 來從KML文件中讀取點位數據。 對於KmlDataSource,camera 和 canvas 選項必須要配置。clampToGround 選項控制數據是否貼地, 貼地效果是最常見的矢量數據可視化效果,保證數據緊貼地形起伏,而不是僅僅相對WGS84絕對球表面。
因爲數據是異步加載的,所以這個函數實際返回一個 Promise , 最後使用KmlDataSource 存儲我們新創建的Entity。
Promise 是一種異步處理機制,這裏的“異步”是指需要在.then函數裏操作數據,而不是直接在 .load函數之後立即操作。爲了能在scene中使用這些載入的entity,只有當這個promise的then回調中纔可以把KmlDataSource添加到 viewer.datasources。
Ceisum官方教程2 -- 項目實例(workshop)
這些新加入到場景的entity默認有很多功能。單擊它們會在 Infobox 顯示屬性, 雙擊它相機轉換爲居中觀察模式(look at). 使用HOME按鈕或者infobox旁邊的相機按鈕可以停止這種模式。下來我們來自定義樣式。
KML和CZML格式,在文件內有明確的樣式定義。爲了學習,我們手動去創建樣式。數據載入之後,我們依據這個 示例 遍歷所有entity修改或者增加屬性。我們的POI點默認都是使用 Billboards 和 Labels 顯示, 根據下面的代碼來修改某些entity的顯示樣式:
Ceisum官方教程2 -- 項目實例(workshop)
通過調整錨點(anchor point)來改進顯示效果,並且爲了避免雜亂刪除了文字標註(labels),最後設置了 displayDistanceCondition 控制只顯示和相機一定距離內的點.
if (Cesium.defined(entity.billboard)) {
// 調整垂直方向的原點,保證圖標裏的針尖對着地表位置
entity.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM;
// 去掉文字的顯示
entity.label = undefined;
// 設置可見距離
entity.billboard.distanceDisplayCondition = new Cesium.DistanceDisplayCondition(10.0, 20000.0);
}
關於distanceDisplayCondition,可以學習下 sandcastle 示例.
下來,我們改進下 Infobox 。Infobox的標題欄顯示的是entity的name屬性, 它的內容顯示的是description屬性(使用HTML文本顯示)。
你發現我們這個數據默認的description屬性沒什麼意義,我們把這個屬性更改爲顯示每個點的經緯度。
首先我們把entity的position屬性轉換爲Cartographic,然後把經度和緯度構造一個HTML的table並賦值到description屬性裏。 現在單擊我們的點在 Infobox 會顯示一個格式規整的信息。
if (Cesium.defined(entity.billboard)) {

        entity.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM; 
        entity.label = undefined; 
        entity.billboard.distanceDisplayCondition = new Cesium.DistanceDisplayCondition(10.0, 20000.0);
        // 計算經度和緯度(角度表示)
        var cartographicPosition = Cesium.Cartographic.fromCartesian(entity.position.getValue(Cesium.JulianDate.now()));
        var longitude = Cesium.Math.toDegrees(cartographicPosition.longitude);
        var latitude = Cesium.Math.toDegrees(cartographicPosition.latitude);
        // 修改描述信息 
        var description = '<table class="cesium-infoBox-defaultTable cesium-infoBox-defaultTable-lighter"><tbody>' +
            '<tr><th>' + "經度" + '</th><td>' + longitude.toFixed(5) + '</td></tr>' +
            '<tr><th>' + "緯度" + '</th><td>' + latitude.toFixed(5) + '</td></tr>' +
            '</tbody></table>';
        entity.description = description;
    }

最後效果:
Ceisum官方教程2 -- 項目實例(workshop)
修改description屬性或許把每個POI點所在的行政區展示出來非常有用。我們試着通過一個GeoJson文件來創建NYC的所有行政區域多邊形。加載GeoJson和上面加載KML基本沒什麼區別,只是使用 GeoJsonDataSource 。和前面一樣,也必須在promise的then函數裏把數據添加到viewer.datasources 中,數據才能顯示。
var geojsonOptions = {
clampToGround : true
};
// 從geojson文件加載行政區多邊形邊界數據
var neighborhoodsPromise = Cesium.GeoJsonDataSource.load('./Source/SampleData/neighborhoods.geojson', geojsonOptions);

var neighborhoods;
neighborhoodsPromise.then(function(dataSource) {
viewer.dataSources.add(dataSource);
});
下來設置多邊形數據的樣式。和上面調整billboard樣式一樣,我們設置行政區域多邊形也必須在數據完全載入後去做。
Ceisum官方教程2 -- 項目實例(workshop)
首先,我們重新設置每個entity的name屬性和行政區的名稱相同。原始的GeoJson文件有一個neighborhood的屬性。Cesium使用entity.properties來存儲GeoJson的屬性。所以我們這麼設置:
Ceisum官方教程2 -- 項目實例(workshop)
爲了避免所有多邊形顏色都相同,可以使用一個隨機顏色 Color去設置每個多邊形的 ColorMaterialProperty屬性。
// 設置一個隨機半透明顏色
entity.polygon.material = Cesium.Color.fromRandom({
red : 0.1,
maximumGreen : 0.5,
minimumBlue : 0.5,
alpha : 0.6
});
// 設置這個屬性讓多邊形貼地,ClassificationType.CESIUM_3D_TILE 是貼模型,ClassificationType.BOTH是貼模型和貼地
entity.polygon.classificationType = Cesium.ClassificationType.TERRAIN;

最後,我們再創建一個基本的文字標註 Label。 爲了保證顯示效果清晰,我們設置了一個 disableDepthTestDistance 確保這個標註不會被其他對象蓋住。
可是,Label需要通過entity.position屬性設置位置。但是Polygon 是有一個positions列表組成的邊界,我們使用這個positions列表的中心點來計算。
// 獲取多邊形的positions列表 並計算它的中心點
var polyPositions = entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()).positions;
var polyCenter = Cesium.BoundingSphere.fromPoints(polyPositions).center;
polyCenter = Cesium.Ellipsoid.WGS84.scaleToGeodeticSurface(polyCenter);
entity.position = polyCenter;
// 生成文字標註
entity.label = {
text : entity.name,
showBackground : true,
scale : 0.6,
horizontalOrigin : Cesium.HorizontalOrigin.CENTER,
verticalOrigin : Cesium.VerticalOrigin.BOTTOM,
distanceDisplayCondition : new Cesium.DistanceDisplayCondition(10.0, 8000.0),
disableDepthTestDistance : 100.0
};
最終效果:Ceisum官方教程2 -- 項目實例(workshop)
多邊形的文字標註
最後,增加一個無人機飛躍城市上空的高科技效果。
因爲飛行路徑只是一系列帶着時間屬性的位置點,我們通過CZML 文件來加載。CZML是一種在Cesium裏描述時序圖形場景的文件格式。它包含折線(lines)、點(points)、圖標(billboards)、模型(models)和其他圖形元素,以及他們隨時間變化的屬性。如同Google Earth的KML,CZML通過一種描述性語言(基於json格式)來存儲Cesium大部分的功能。
我們得CZML文件定義一個包含不同時刻得一個位置列表Entity(默認顯示爲一個point)。在Entity API中有一些處理時間序列數據的屬性類型。參考下面的示例:

Property Types 示例
// 從CZML中載入無人機軌跡
var dronePromise = Cesium.CzmlDsataSource.load('./Source/SampleData/SampleFlight.czml');

dronePromise.then(function(dataSource) {
viewer.dataSources.add(dataSource);
});
這個CZML中使用 Path去展示無人機軌跡, 以及一個展示不同時刻位置的屬性.。使用插值算法把一個路徑的離散點鏈接爲一個連續的折線。
我們繼續改進下無人機的顯示樣式。我們可以用一個三維模型去表示我們的無人機,並把它設置到entity上,而不是僅僅用一個簡單的點。

三維模型示例
三維模型帶顏色示例

Cesium支持加載glTF格式的三維模型格式。glTF是一個由Cesium團隊和 Khronos group一起開發的開源三維模型格式,這種格式儘量減少傳輸和實時處理過程中的模型數據量。如果沒有glTF模型,我們提供了一個 在線轉換工具 把DAE,obj等格式轉爲glTF。
我們載入一個效果不錯的,又帶動畫的無人機模型 Model :
Ceisum官方教程2 -- 項目實例(workshop)
現在我們的模型看起來還不錯,不像最初那個簡單的點效果,這個無人機模型有方向,但是效果有點奇怪,並沒有朝向無人機的前進方向。幸好,Cesium提供了VelocityOrientationProperty ,這個會根據entity的位置點信息和時間來自動計算朝向。
Ceisum官方教程2 -- 項目實例(workshop)
現在我們的無人機模型朝向正確了。我們還可以改進下無人機飛行效果。Cesium依據離散點,使用線形插值構造了一條折線,雖然遠處看不明顯,但是這些折線段讓路徑看着不自然。有一些插值配置選項:

插值示例
爲了飛行路徑更平滑,可以如下修改配置 :
Ceisum官方教程2 -- 項目實例(workshop)
Ceisum官方教程2 -- 項目實例(workshop)
平滑的飛行路徑
3D Tiles
我們的團隊有時候描述Cesium像一個使用真實世界數據的三維遊戲引擎。可是,加載真實世界的數據要比遊戲引擎的數據困難很多,主要因爲真實數據有非常高得分辨率,而且要求精確得可視化。幸好,Cesium和開源社區合作開發了3D Tiles格式。它是一個流式載入海量各種類型得空間三維數據的 開放協議 。
使用一種類似Cesium的地形和影像數據切片技術,3d tiles格式使原先那些不可能做可視化交互的大模型數據能夠展示出來,包括建築物數據、CAD(或者BIM)模型,點雲,傾斜模型。

3D Tiles 調試器 ,它是一個能夠查看各種3d tile後臺信息的調試工具。

這是一些不同類型的3d tile模型數據:

傾斜模型
BIM數據
點雲
所有類型

這個項目中,使用 Cesium3DTileset 類添加整個紐約的真實建築物模型,改進了可視化效果的真實性。
// 加載紐約建築物模型
var city = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({ url: Cesium.IonResource.fromAssetId(3839) }));
你會發現這些建築物的高度好像不正確。這個可以簡單修正下。通過一個 modelMatrix,我們可以調整這個數據的位置。
把數據當前的包圍球轉爲Cartographic,就能計算出模型現在相對於地面的偏移,然後增加這個偏移值,然後重設modelMatrix:
// 調整3dtile模型的高度,讓他剛好放在地表
var heightOffset = -32;
city.readyPromise.then(function(tileset) {
// Position tileset
var boundingSphere = tileset.boundingSphere;
var cartographic = Cesium.Cartographic.fromCartesian(boundingSphere.center);
var surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0);
var offset = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, heightOffset);
var translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3());
tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
});
現在我們有了110萬個建築物模型。
3D Tiles 支持使用3D Tiles樣式語言去對一部分數據進行樣式配置。
3D Tiles的樣式依據一個表達式,根據Cesium3DTileFeature模型屬性去修改某一部分甚至某一棟建築物的顏色(RGB和透明度)。這些元素屬性(feature property)通常存儲在每個模型切片的batchtable中。元素屬性可以是任意屬性,比如高度,名稱,座標,創建日期等等。樣式語言使用JSON格式定義,並且支持JavaScript的表達式(a small subset of JavaScript augmented)。另外,樣式語言提供了一些內置的函數,支持數學計算。
Cesium3DTileStyle示例如下:
Ceisum官方教程2 -- 項目實例(workshop)
這個樣式只是簡單的讓紐約的所有建築都可見。把它設置到 city.style就可以看到可視化效果。
Ceisum官方教程2 -- 項目實例(workshop)
Ceisum官方教程2 -- 項目實例(workshop)
默認效果
下面這個樣式讓模型半透明:
Ceisum官方教程2 -- 項目實例(workshop)
Ceisum官方教程2 -- 項目實例(workshop)
半透明效果
所有元素使用相同樣式只是小兒科。我們可以使用屬性對每個元素設置不同樣式。下面是一個依據建築高度去着色的示例:
Ceisum官方教程2 -- 項目實例(workshop)
Ceisum官方教程2 -- 項目實例(workshop)
依據高度着色
爲了在這些樣式之間切換,我們增加一點點代碼去監聽HTML的輸入框變化:
Ceisum官方教程2 -- 項目實例(workshop)
如果想學習更多關於3D Tiles如何配置樣式,請查看這個 示例。
一些其他3D Tiles的示例:

格式
傾斜模型

樣式配置
如果你有各種三維數據需要轉換爲3D tiles,請下載我們的CesiumLab。

交互
最後,我們添加一些鼠標交互。我們改進下效果,當鼠標劃過的時候,高亮圖標。 爲了做出這個效果,我們使用拾取技術(picking),它能夠根據一個屏幕上的像素位置返回三維場景中的對象信息。
有好幾種拾取:

Scene.pick : 返回窗口座標對應的圖元的第一個對象。

Scene.drillPick :返回窗口座標對應的所有對象列表。

Globe.pick : 返回一條射線和地形的相交位置點。

這是一些示例:

拾取示例

3D Tiles 對象拾取
因爲我們想實現鼠標滑過的高亮效果,首先需要創建一個鼠標事件處理器。 ScreenSpaceEventHandler是可以處理一系列的用戶輸入事件的處理器. ScreenSpaceEventHandler.setInputAction()`](/Cesium/Build/Documentation/ScreenSpaceEventHandler.html#setInputAction) 監聽某類型的用戶輸入事件 -- [ScreenSpaceEventType用戶輸入事件類型,做爲一個參數傳遞過去。這裏我們設置一個回調函數來接受鼠標移動事件:
var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function(movement) {}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
下來我們寫高亮函數。我們可以在回調函數裏獲得一個窗口座標,並傳遞到pick()方法裏。 如果拾取到一個billboard對象,我們就知道目前鼠標在一個圖標上了。然後使用我們前面學過的相關Entity接口,去修改它的樣式做高亮效果。
// 當鼠標移到了我們關注的圖標上,修改entity 的billboard 縮放和顏色
handler.setInputAction(function(movement) {
var pickedPrimitive = viewer.scene.pick(movement.endPosition);
var pickedEntity = (Cesium.defined(pickedPrimitive)) ? pickedPrimitive.id : undefined;
// Highlight the currently picked entity
if (Cesium.defined(pickedEntity) && Cesium.defined(pickedEntity.billboard)) {
pickedEntity.billboard.scale = 2.0;
pickedEntity.billboard.color = Cesium.Color.ORANGERED;
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

高亮樣式設置成功了。可是,當鼠標不在圖標上,這個高亮樣式依然有效。爲了解決這個問題,我們使用一個變量來存儲上次的高亮圖標,當鼠標不在它上面的時候,恢復它原來的樣式。
這是包含高亮和不高亮完整功能的代碼:
var previousPickedEntity = undefined;
handler.setInputAction(function(movement) {
var pickedPrimitive = viewer.scene.pick(movement.endPosition);
var pickedEntity = (Cesium.defined(pickedPrimitive)) ? pickedPrimitive.id : undefined;
// 取消上一個高亮對象的高亮效果
if (Cesium.defined(previousPickedEntity)) {
previousPickedEntity.billboard.scale = 1.0;
previousPickedEntity.billboard.color = Cesium.Color.WHITE;
}
// 當前entity高亮
if (Cesium.defined(pickedEntity) && Cesium.defined(pickedEntity.billboard)) {
pickedEntity.billboard.scale = 2.0;
pickedEntity.billboard.color = Cesium.Color.ORANGERED;
previousPickedEntity = pickedEntity;
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
好了,我們添加了完整的圖標entity的鼠標交互響應。
Ceisum官方教程2 -- 項目實例(workshop)
鼠標交互
相機模式
爲了炫耀我們的無人機飛行,我們來實驗下相機模式。在兩種相機模式下可以簡單的切換:

自由模式 :默認的相機控制方式
無人機模式 : 以一個固定距離跟隨無人機
自由模式下不需要任何代碼。無人機跟隨模式下,我們使用viewer內置的跟隨函數,確保相機一直居中觀察無人機。這種模式下,即便對象是移動的,相機也能和目標之間保持一個固定的偏移距離。只需要簡單的設置
viewer.trackedEntity。
切換到自由模式,只需要把viewer.trackedEntity 設置爲undefined,然後可以使用camera.flyTo()返回到初始位置。

這是相機模式代碼:

Ceisum官方教程2 -- 項目實例(workshop)
只需要把這個函數綁定到HTML元素的change事件上。
Ceisum官方教程2 -- 項目實例(workshop)
當我們雙擊entity的時候,就會自動進行跟隨模式。如果用戶通過點擊跟蹤無人機,添加一些處理去自動更新UI界面:
Ceisum官方教程2 -- 項目實例(workshop)
我們可以通過界面自由切換相機模式了:
Ceisum官方教程2 -- 項目實例(workshop)
相機模式
其他
剩下的代碼我們增加一些其他的可視化效果。如同前面提到的HTML元素交互方式,我們可以添加陰影的切換界面,以及行政區多邊形的可見性控制。
首先,簡單的控制下行政區劃的可見性。通常,通過設置Entity.show 屬性來隱藏entity。可是,這個僅僅設置一個entity,我們希望一次性控制所有行政區劃面的可見性。
可以像這個示例一樣,把所有行政區entity放在一個父entity中,或者通過設置EntityCollection的 show 屬性來控制。只需要設置一次neighborhoods.show屬性即可控制所有entity的可見性。
Ceisum官方教程2 -- 項目實例(workshop)
如同切換陰影一樣:Ceisum官方教程2 -- 項目實例(workshop)
因爲3D Tiles數據可能不是瞬間載入,可以添加一個載入指示器,當所有切片都載入後隱藏。
Ceisum官方教程2 -- 項目實例(workshop)
接下來
恭喜!你已經成功完成了CesiumJS項目。在Cesium的培訓過程中,請隨意使用我們提供的代碼去測試和開發。我們很高興歡迎你加入Cesium社區,並且期望看到你基於CesiumJS開發的酷炫程序。
開發資源
爲了你的Cesium開發事業,我們鼓勵你訪問下面的資源:

官方文檔 : 完整的CesiumAPI文檔,包含一些示例代碼

Sandcastle :一個所見即所得的編碼環境,包含大量的代碼示例.

官方教程 :詳細描述基於Cesium開發的方方面面。

官方論壇 : Cesium相關問題的主要討論平臺.

Cesium實驗室 : 中國最專業的Cesium的討論學習QQ羣:595512567。
一旦有問題,上面的資源可能有你的答案。

在cesiumjs.org展示你的項目
我們很樂意去分享所有Cesium社區創建的酷炫項目。遍佈世界的的開發者創建了很多有意思的我們從來沒考慮過的項目。一旦你的項目準備分享給全世界,請跟我們聯繫放到[CesiumJS示例頁面] (https://cesiumjs.org/demos)。具體請閱讀 這個博客提交你的項目示例。
Ceisum官方教程2 -- 項目實例(workshop)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章