前言
需求是這樣的,我需要在地圖中顯示 08 年到現在的地震情況,地震都是發生在具體的時間點的,那么問題就來了,如何實現地震情況按照時間動態渲染而不是一次全部加載出來。
一、 方案分析
這里面牽扯到兩個問題:第一個是如何加載 GeoJSON 格式的數據,其實也就是矢量數據,因為矢量數據之間是可以任意轉換的;第二個是如何讓加載的數據根據自身的時間顯示。
所以就有兩種解決問題的思路了:第一種,一次加載 GeoJSON 中所有數據,然后逐個設置顯示時間;第二種,逐個加載 GeoJSON 中數據,並設置每個對象的顯示時間。
下面我們就一步步來實現解決方案。
二、 解決方案
先來看一下整體效果:
2.1 加載 GeoJSON 數據
在Cesium基礎使用介紹一文中已經介紹了如何加載多種格式矢量數據,加載 GeoJSON 數據已經寫出了兩種方式,第一種是整體讀取的,明顯無法滿足我們的需求,那么就只能尋求第二種方式了:
Cesium.GeoJsonDataSource.load('data/earthquake.geojson').then(function(dataSource) {
viewer.dataSources.add(dataSource);
var entities = dataSource.entities.values;
for (var i = 0; i < entities.length; i++) {
var entity = entities[i];
entity.billboard = undefined;
entity.point = new Cesium.PointGraphics({
color: Cesium.Color.RED,
pixelSize: 10
});
}
});
這里需要注意一個細節,地震數據為點狀數據,需要先設置 entity.billboard = undefined
,而后再設置 entity.point
來顯示點狀元素,否則會顯示一個圖標而不是點。
這樣看上去是逐一添加了點狀元素,但是我們的問題並沒有解決,所有地震點還是全部顯示出來了,並沒有按照時間顯示。
2.2 空間對象按照時間顯示
查閱了很多資料,發現可以通過設置對象的 availability 屬性來控制對象的顯示時間,這正是我需要的,於是修改如下:
Cesium.GeoJsonDataSource.load('data/earthquake.geojson').then(function(dataSource) {
viewer.dataSources.add(dataSource);
var entities = dataSource.entities.values;
for (var i = 0; i < entities.length; i++) {
var entity = entities[i];
entity.billboard = undefined;
entity.point = new Cesium.PointGraphics({
color: Cesium.Color.RED,
pixelSize: 10
});
entity.availability = new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({
start: Cesium.JulianDate.fromIso8601(entity.properties.date),
stop: addDay(Cesium.JulianDate.fromIso8601(entity.properties.date))
})]);
}
});
});
可以看到只是多加了 entity.availability = ...
一項,這樣就能夠按照時間顯示,主要是其中的 start 和 stop 屬性,控制顯示的時間范圍。date 是 GeoJSON 中數據的一個字段,格式為 '2008-01-01',當然你也可以使用其他格式,在此處進行自定義處理即可,addDay 用於控制顯示一天,此處不用多考慮。
2.3 GeoJSON 的另外一種讀取方式
寫到這里問題已經解決了,但是這里再說一個小插曲。剛開始的時候我將 availability 屬性直接寫到了 point 里,無法得到結果,於是懷疑是此方法走不通,又思考和搜索了片刻,找到了另一種讀取 GeoJSON 的方法,如下:
Cesium.loadJson('data/boundary/earthquake.geojson').then(function(jsonData) {
for (var i =0 ;i<=jsonData.features.length; i++) {
var ifeature = jsonData.features[i];
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(ifeature.geometry.coordinates[0], ifeature.geometry.coordinates[1]),
availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({
start: Cesium.JulianDate.fromIso8601(ifeature.properties.date),
stop: addDay(Cesium.JulianDate.fromIso8601(ifeature.properties.date))
})]),
point: {
pixelSize: 10,
color: Cesium.Color.RED
}
});
}
});
這同樣能達到效果,這就是剛開始討論時描述的逐個讀取數據,這與前一種方式不同的是此處讀取到的是逐個的 feature 對象(前一種直接讀取 entity 對象),根據 feature 生成 entity 對象,再使用 viewer.entities.add
將對象添加到場景中,每個對象單獨根據時間設置 availability 屬性,這樣同樣達到了效果。
當此種方式達到效果的時候,再回頭來看第一種方式豁然開朗,讀取到的 entity 就是一個真實的 entity 對象,於是將 availability 從 point 中移出到外面便達到了效果。
2.4 問題分析
兩種方式都能達到效果,而我在剛開始的時候對細節、對 cesium 的各個對象並沒有理解的那么透徹,只是看到了表面現象,當研究的稍微深入的時候對整個 cesium 框架也就有了更多的理解,於是條條道路通羅馬。
三、 總結
本文簡單介紹了如何動態的根據時間加載 GeoJSON 對象,一定要保持深度思考的習慣,凡事不能只看到表面,應該多一些深入的思考。