Cesium入門9 - Loading and Styling Entities - 載入和樣式化實體
Cesium入門9 - Loading and Styling Entities - 載入和樣式化實體
Cesium中文網:http://cesiumcn.org/ | 國內快速訪問:http://cesium.coinidea.com/
現在我們已經為我們的應用程式設定了Viewer配置、imagery和terrain的階段,我們可以新增我們的應用程式的主要焦點——geocache資料。
為了便於視覺化,Cesium支援流行的向量格式GeoJson和KML,以及一種我們團隊自己開源的格式,我們專門開發用於描述Cesium場景的[]CZML](https://github.com/AnalyticalGraphicsInc/czml-writer/wiki/CZML-Guide
無論初始格式如何,Cesium中的所有空間資料都使用Entity API來表示。Entity API以一種有效提供靈活的視覺化的方式,以便對Cesium進行渲染。Cesium Entity是可以與樣式化圖形表示配對並定位在空間和時間上的資料物件。測試沙盒中提供了許多簡單Entity的例子。為了在Entity API的基礎上加快速度,從這個應用程式中休息一下,然後閱讀視覺化的空間資料教程 Visualizing Spatial Data tutorial。
以下有一些關於不同entity型別的例子:
一旦你掌握了一個Entity的樣子,用Cesium裝載資料集將是變得容易理解。要讀取資料檔案,需要建立適合於資料格式的資料來源DataSource,該資料來源將解析在指定URL中承載的資料檔案,併為資料集中的每個地理空間物件建立包含Entity的EntityCollection。DataSource只是定義了一個介面——您需要的資料來源的確切型別將取決於資料格式。例如,KML使用KmlDataSource原始碼。比如:
var kmlOptions = { camera : viewer.scene.camera, canvas : viewer.scene.canvas, clampToGround : true }; // Load geocache points of interest from a KML file // Data from : http://catalog.opendata.city/dataset/pediacities-nyc-neighborhoods/resource/91778048-3c58-449c-a3f9-365ed203e914 var geocachePromise = Cesium.KmlDataSource.load('./Source/SampleData/sampleGeocacheLocations.kml', kmlOptions);
上述程式碼讀取我們樣例的geocahce點,從一個KML檔案中,呼叫KmlDataSource.load(optinos)帶一些配置。針對一個KmlDataSource,相機和Canvas配置項是必須的。clamptoGround選項激活了ground clamping,一種流行的描述配置用於是地面的幾何entities比如多邊形和橢圓符合地形而且遵從WGS84橢圓面。
由於這些資料是非同步載入的,因此針對KmlDataSource返回一個的Promise,它將包含我們所有新建立的entities。
如果您不熟悉使用非同步函式的PromiseAPI,這裡的“非同步”基本上意味著您應該在所提供的回撥函式中完成所需的資料.then.為了實際地將這些實體集合新增到場景中,我們必須等待直到promise完成,然後將KmlDataSource新增viewer.datasrouces。取消以下幾行註釋:
// Add geocache billboard entities to scene and style them
geocachePromise.then(function(dataSource) {
// Add the new data as entities to the viewer
viewer.dataSources.add(dataSource);
});
預設情況下,這些新建立的實體具有有用的功能。單擊將顯示與實體相關的元資料的資訊框Infobox,並雙擊縮放並檢視實體。若要停止檢視該實體,請單擊“home”按鈕,或單擊“資訊框”上的“劃出”相機圖示。接下來,我們將新增自定義樣式來改善我們的應用程式的外觀style。
對於KML和CZML檔案,可以在檔案中建立宣告式樣式。然而,對於這個應用,讓我們練習手動設計我們的實體。要做到這一點,我們將採取類似的方法來處理這個樣式示例,等待我們的資料來源載入,然後迭代資料來源集合中的所有實體,修改和新增屬性。預設情況下,我們的geocache點標記被建立為Billboards和Labels,所以為了修改這些實體的外觀,我們這樣做:
// Add geocache billboard entities to scene and style them
geocachePromise.then(function(dataSource) {
// Add the new data as entities to the viewer
viewer.dataSources.add(dataSource);
// Get the array of entities
var geocacheEntities = dataSource.entities.values;
for (var i = 0; i < geocacheEntities.length; i++) {
var entity = geocacheEntities[i];
if (Cesium.defined(entity.billboard)) {
// Entity styling code here
}
}
});
我們可以通過調整它們的錨點、去除標籤來減少clutter和設定isplayDistanceCondition來改善標記的外觀,使得只有在距相機的一定距離內的點是可見的。
// Add geocache billboard entities to scene and style them
if (Cesium.defined(entity.billboard)) {
// Adjust the vertical origin so pins sit on terrain
entity.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM;
// Disable the labels to reduce clutter
entity.label = undefined;
// Add distance display condition
entity.billboard.distanceDisplayCondition = new Cesium.DistanceDisplayCondition(10.0, 20000.0);
}
有關distanceDisplayCondition的更多幫助,請參見sandcastle example。
接下來,讓我們為每個geocache實體改進資訊框Infobox。資訊框的標題是實體名稱,內容是實體描述,顯示為HTML。
你會發現預設的描述並不是很有幫助。由於我們正在顯示geocache 位置,讓我們更新它們來顯示點的經度和緯度。
首先,我們將實體的位置轉換成地圖,然後從Cartographic中讀取經度和緯度,並將其新增到HTML表中的描述中。
在單擊時,我們的geocache 實體現在將顯示一個格式良好的資訊框Infobox,只需要我們所需要的資料。
// Add geocache billboard entities to scene and style them
if (Cesium.defined(entity.billboard)) {
// Adjust the vertical origin so pins sit on terrain
entity.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM;
// Disable the labels to reduce clutter
entity.label = undefined;
// Add distance display condition
entity.billboard.distanceDisplayCondition = new Cesium.DistanceDisplayCondition(10.0, 20000.0);
// Compute longitude and latitude in degrees
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);
// Modify description
// Modify description
var description = '<table class="cesium-infoBox-defaultTable cesium-infoBox-defaultTable-lighter"><tbody>' +
'<tr><th>' + "Longitude" + '</th><td>' + longitude.toFixed(5) + '</td></tr>' +
'<tr><th>' + "Latitude" + '</th><td>' + latitude.toFixed(5) + '</td></tr>' +
'</tbody></table>';
entity.description = description;
}
我們的geocache標記現在應該看起來像這樣:
對於我們的地理應用程式來說,視覺化特定點的鄰域也會有幫助。讓我們試著為每個紐約街區記載一個包含多邊形的GeoJson檔案。載入GeoJson檔案最終非常類似於我們剛剛用於KML的載入過程。但是在這種情況下,我們使用GeoJsonDataSource。與前一個數據源一樣,我們需要將它新增到viewer.datasources中,以便實際新增資料到場景中。
var geojsonOptions = {
clampToGround : true
};
// Load neighborhood boundaries from KML file
var neighborhoodsPromise = Cesium.GeoJsonDataSource.load('./Source/SampleData/neighborhoods.geojson', geojsonOptions);
// Save an new entity collection of neighborhood data
var neighborhoods;
neighborhoodsPromise.then(function(dataSource) {
// Add the new data as entities to the viewer
viewer.dataSources.add(dataSource);
});
讓我們來調整我們載入的neighborhood多邊形。就像我們剛才做的billboard樣式一樣,我們首先在資料來源載入後迭代遍歷neighborhood 資料來源實體,這次檢查每個實體的多邊形被定義:
// Save an new entity collection of neighborhood data
var neighborhoods;
neighborhoodsPromise.then(function(dataSource) {
// Add the new data as entities to the viewer
viewer.dataSources.add(dataSource);
neighborhoods = dataSource.entities;
// Get the array of entities
var neighborhoodEntities = dataSource.entities.values;
for (var i = 0; i < neighborhoodEntities.length; i++) {
var entity = neighborhoodEntities[i];
if (Cesium.defined(entity.polygon)) {
// entity styling code here
}
}
});
既然我們正在顯示neighborhood,讓我們重新命名每個實體使用neighborhood作為它的名字。我們所讀的neighborhood中原始GeoJson檔案作為屬性。Cesium將GeoJson屬性儲存在enty.properties中,這樣我們就可以設定這樣的neighborhood名稱:
// entity styling code here
// Use geojson neighborhood value as entity name
entity.name = entity.properties.neighborhood;
我們可以把每一個多邊形分配給一個新的顏色材料屬性,通過ColorMaterialProperty設定隨機顏色Color,而不是把所有的區域都設定成一樣的顏色。
// entity styling code here
// Set the polygon material to a random, translucent color.
entity.polygon.material = Cesium.Color.fromRandom({
red : 0.1,
maximumGreen : 0.5,
minimumBlue : 0.5,
alpha : 0.6
});
// Tells the polygon to color the terrain. ClassificationType.CESIUM_3D_TILE will color the 3D tileset, and ClassificationType.BOTH will color both the 3d tiles and terrain (BOTH is the default)
entity.polygon.classificationType = Cesium.ClassificationType.TERRAIN;
最後,讓我們為每個實體生成一個帶有一些基本樣式選項的標籤Label。為了保持整潔,我們可以使用disableDepthTestDistance讓Cesium總是把標籤放在任何3D物體可能遮擋的地方。
然而,請注意,標籤總是位於entity.position。多邊形Polygon是由一個未定義的位置建立的,因為它有一個定義多邊形邊界的位置列表。我們可以通過取多邊形位置的中心來生成一個位置:
// entity styling code here
// Generate Polygon position
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;
// Generate labels
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
};
這給我們標出了看起來像這樣的多邊形:
最後,讓我們通過在城市上空新增無人機飛行來增加我們的NYC geocaches 的高科技視角。
由於飛行路徑只是一系列隨時間變化的位置,所以我們可以從CZML檔案中新增這些資料。CZML是一種用於描述時間動態圖形場景的格式,主要用於在執行Cesium的Web瀏覽器中顯示。它描述了線、點、billboards、模型和其他圖形原語,並指定它們如何隨時間變化。CZML之於Cesium,相當於KML之於谷歌地球的標準格式,它允許大多數Cesium功能特性通過宣告式樣式語言(在這種情況下是JSON模式)使用。
我們的CZML檔案定義了一個實體(預設為視覺化的一個點),其位置被定義為在不同時間點的一系列位置。實體API中有幾種屬性型別可用於處理時間動態資料。參見下面的演示示例:
// Load a drone flight path from a CZML file
var dronePromise = Cesium.CzmlDataSource.load('./Source/SampleData/SampleFlight.czml');
dronePromise.then(function(dataSource) {
viewer.dataSources.add(dataSource);
});
CZML檔案使用Cesium來顯示無人機飛行,該路徑是實體隨時間顯示其位置的屬性。一條路徑用插值法將離散點連線到一條連續的直線上進行視覺化。
最後,讓我們改善無人機飛行的外觀。首先,而不是簡單地解決問題,我們可以載入一個3D模型來表示我們的無人機並將其附加到實體上。
Cesium支援基於glTF(GL傳輸格式)載入3D模型,這是Cesium團隊與Khronos group一起開發的開放規範,用於通過最小化檔案大小和執行時間處理來有效地載入應用程式的3D模型。沒有gLTF模型嗎?我們提供了一個線上轉換器,將COLLADA和OBJ檔案轉換為glTF格式。
讓我們載入一個無人機模型Model,具有良好的基於物理的陰影和一些動畫:
var drone;
dronePromise.then(function(dataSource) {
viewer.dataSources.add(dataSource);
// Get the entity using the id defined in the CZML data
drone = dataSource.entities.getById('Aircraft/Aircraft1');
// Attach a 3D model
drone.model = {
uri : './Source/SampleData/Models/CesiumDrone.gltf',
minimumPixelSize : 128,
maximumScale : 1000,
silhouetteColor : Cesium.Color.WHITE,
silhouetteSize : 2
};
});
現在我們的模型看起來不錯,但與原來的點不同,無人機模型具有方向性,當無人駕駛飛機向前移動時,它看起來很奇怪。幸運的是,Cesium提供了一種VelocityOrientationProperty,它將根據一個實體向前和向後取樣的位置自動計算方向:
// Add computed orientation based on sampled positions
drone.orientation = new Cesium.VelocityOrientationProperty(drone.position);
現在我們的無人駕駛飛機模型將如期進行。
還有一件事我們可以做的是改善我們的無人機飛行的外觀。從遠處看,它可能並不明顯,但無人機的路徑是由看起來不自然的線段組成的,這是因為Cesium使用線性插值來構建從預設取樣點的路徑。然而,可以配置插值選項。
為了獲得更平滑的飛行路徑,我們可以改變這樣的插值選項:
// Smooth path interpolation
drone.position.setInterpolationOptions({
interpolationDegree : 3,
interpolationAlgorithm : Cesium.HermitePolynomialApproximation
});
Cesium中文網交流QQ群:807482793
Cesium中文網:http://cesiumcn.org/ | 國內快速訪問:http://cesium.coinidea.com/