https://www.jianshu.com/p/f0b47997224c
前言
Cesium官方教程中有一篇叫《空間數據可視化》(Visualizing Spatial Data)。該文文末簡單提到了Cesium的Property機制,然后話鋒一轉,宣告此教程的第二部分將重點講解Property機制。但是呢,第二部分還沒有寫好,說在等待的過程中,可以先看下Cesium對影像和地形的支持。。
可以看官方教程中的說法,如下圖所示:

於是,我苦等了一年啦。。官方教程的第二部分還是沒能看到。。畢竟這是Cesium官方推薦使用的Entity API中最重要的部分之一。。居然這么久了也不給更新下。。
我想還是自己總結一下得好。。
為什么要用Property?
還是舉個例子來說吧。
比如我想在地球上的某個位置加一個盒子,可以這樣寫代碼:
// 創建盒子 var blueBox = viewer.entities.add({ name : 'Blue box', position: Cesium.Cartesian3.fromDegrees(-114.0, 40.0, 300000.0), box : { dimensions : new Cesium.Cartesian3(400000.0, 300000.0, 500000.0), material : Cesium.Color.BLUE, outline: true, } });
最終的效果如圖所示:

但是呢,如果我想讓這個盒子逐漸變長,該怎么操作呢?如下圖所示:

方法是有的,就是可以不停地去修改blueBox.position,類似這樣:
setInterval(function(){ blueBox.box.dimensions = xxx; }, 3000);
如果場景中有很多物體,在不同的時間段要發生各種走走停停地運動時,這樣操作可能會很累人。那么Cesium就提供一種機制,讓dimensions可以隨時間自動發生變化,自動賦予不同的數值(位置)。這也就是property的作用了。以下代碼的加入,就可以讓盒子如上圖所示做線性運動了。
var property = new Cesium.SampledProperty(Cesium.Cartesian3); property.addSample(Cesium.JulianDate.fromIso8601('2019-01-01T00:00:00.00Z'), new Cesium.Cartesian3(400000.0, 300000.0, 200000.0)); property.addSample(Cesium.JulianDate.fromIso8601('2019-01-03T00:00:00.00Z'), new Cesium.Cartesian3(400000.0, 300000.0, 700000.0)); blueBox.box.dimensions = property;
以上代碼的意思就是在兩個不同的時間點分別賦予不同的位置,用SampledProperty包裝成一個property,最后賦給blueBox.box.dimensions。
由此可見,Property最大的特點是和時間相互關聯,在不同的時間可以動態地返回不同的屬性值。而Entity則可以感知這些Property的變化,在不同的時間驅動物體進行動態展示。
Cesium宣稱自己是數據驅動和time-dynamic visualization,這些可都是仰仗Property系統來實現的。
當然,Property可不只是這么簡單,以下再詳細論述。
Property的分類
Cesium的Property不止有剛才示例代碼中的SampleProperty,還有很多其他的類型。如果搜索一下Cesium的API文檔,會有很多。。如下圖所示:

我們簡單分類一下

Property虛基類
Property是所有Property類型的虛基類。它定義了以下接口。

getValue 是一個方法,用來獲取某個時間點的特定屬性值。它有兩個參數:第一個是time,用來傳遞一個時間點;第二個是result,用來存儲屬性值,當然也可以是undefined。這個result是Cesium的scratch機制,主要是用來避免頻繁創建和銷毀對象而導致內存碎片。Cesium就是通過調用getValue類似的一些函數來感知Property的變化的,當然這個方法我們在外部也是可以使用的。
isConstant 用來判斷該屬性是否會隨時間變化,是一個布爾值。Cesium會通過這個變量來決定是否需要在場景更新的每一幀中都獲取該屬性的數值,從而來更新三維場景中的物體。如果isConstant為true,則只會獲取一次數值,除非definitionChanged事件被觸發。
definitionChanged 是一個事件,可以通過該事件,來監聽該Property自身所發生的變化,比如數值發生修改。
equals 是一個方法,用來檢測屬性值是否相等。
基本Property類型
SampleProperty
我們最早在上述示例中使用的就是它,用來通過給定多個不同時間點的Sample,然后在每兩個時間點之間進行線性插值的一種Property。代碼寫法如下:
var property = new Cesium.SampledProperty(Cesium.Cartesian3); property.addSample(Cesium.JulianDate.fromIso8601('2019-01-01T00:00:00.00Z'), new Cesium.Cartesian3(400000.0, 300000.0, 200000.0)); property.addSample(Cesium.JulianDate.fromIso8601('2019-01-03T00:00:00.00Z'), new Cesium.Cartesian3(400000.0, 300000.0, 700000.0)); blueBox.box.dimensions = property;
效果如下所示:

TimeIntervalCollectionProperty
該Property用來指定各個具體的時間段的屬性值,每個時間段內的屬性值是恆定的,並不會發生變化,除非已經進入到下一個時間段。拿創建的盒子示例來說,表現出來的特點就是盒子尺寸的變化時跳躍式的。效果如下:

代碼如下:
var property = new Cesium.TimeIntervalCollectionProperty(Cesium.Cartesian3); property.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ iso8601 : '2019-01-01T00:00:00.00Z/2019-01-01T12:00:00.00Z', isStartIncluded : true, isStopIncluded : false, data : new Cesium.Cartesian3(400000.0, 300000.0, 200000.0) })); property.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ iso8601 : '2019-01-01T12:00:01.00Z/2019-01-02T00:00:00.00Z', isStartIncluded : true, isStopIncluded : false, data : new Cesium.Cartesian3(400000.0, 300000.0, 400000.0) })); property.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ iso8601 : '2019-01-02T00:00:01.00Z/2019-01-02T12:00:00.00Z', isStartIncluded : true, isStopIncluded : false, data : new Cesium.Cartesian3(400000.0, 300000.0, 500000.0) })); property.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ iso8601 : '2019-01-02T12:00:01.00Z/2019-01-03T00:00:00.00Z', isStartIncluded : true, isStopIncluded : true, data : new Cesium.Cartesian3(400000.0, 300000.0, 700000.0) })); blueBox.box.dimensions = property;
ConstantProperty
通過對TimeIntervalCollectionProperty和SampleProperty的描述,讀者應該基本了解Property的特點。我們回過頭來說下ConstantProperty,其實這才是最常用的Property。
示例代碼如下:
blueBox.box.dimensions = new Cesium.Cartesian3(400000.0, 300000.0, 200000.0);
以上代碼貌似沒有使用ConstantProperty,實際上他是等同於:
blueBox.box.dimensions = new ConstantProperty(new Cesium.Cartesian3(400000.0, 300000.0, 200000.0));
也就是Entity的box.dimensions類型並不是Cartesian3,而是一個Property。雖然我們賦值了一個Cartesian3,但是Cesium內部會隱晦地轉化成了一個ConstantProperty。注意只會隱晦地轉化成ConstantProperty,而不是SampleProperty,更不是TimeIntervalCollectionProperty。
雖然叫ConstantProperty,但是,這里Constant的意思並不是說這個Property不可改變,而是說它不會隨時間發生變化。
舉個例子,我們可以通過 property.getValue(viewer.clock.currentTime) 方法來獲取某個時間點property的屬性值。如果property是SampleProperty或者TimeIntervalCollectionProperty的話,不同的時間點,可能getValue出不同的數值。但是如果這個property是ConstantProperty,那么無論什么時間(getValue的第一個參數不起作用),最后返回的數值都是一樣的。
但是不會隨時間變化,並不代表不可改變。ConstantProperty還有一個setValue的方法,開發者可以通過調用它,來在適當的時候改變property的值。
比如,我可以通過點擊按鈕來修改ConstantProperty,代碼如下:
blueBox.box.dimensions.setValue(new Cesium.Cartesian3(400000.0, 300000.0, 700000.0));
需要注意的是,雖然最終效果一樣,但是以下兩種寫法的意義是不一樣的。
blueBox.box.dimensions = new Cesium.Cartesian3(400000.0, 300000.0, 200000.0);
blueBox.box.dimensions.setValue(new Cesium.Cartesian3(400000.0, 300000.0, 700000.0));
前者會創建一個新的ConstantProperty,后者則會修改原有的ConstantProperty的值。
CompositeProperty
CompositeProperty的意思是組合的Property,可以把多種不同類型的ConstantProperty、SampleProperty、TimeIntervalCollectionProperty等Property組合在一起來操作。比如前一個時間段需要線性運動,后一段時間再跳躍式運動。則可以使用類似下面這段代碼來實現。
// 1 sampledProperty var sampledProperty = new Cesium.SampledProperty(Cesium.Cartesian3); sampledProperty.addSample(Cesium.JulianDate.fromIso8601('2019-01-01T00:00:00.00Z'), new Cesium.Cartesian3(400000.0, 300000.0, 200000.0)); sampledProperty.addSample(Cesium.JulianDate.fromIso8601('2019-01-02T00:00:00.00Z'), new Cesium.Cartesian3(400000.0, 300000.0, 400000.0)); // 2 ticProperty var ticProperty = new Cesium.TimeIntervalCollectionProperty(); ticProperty.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ iso8601 : '2019-01-02T00:00:00.00Z/2019-01-02T06:00:00.00Z', isStartIncluded : true, isStopIncluded : false, data : new Cesium.Cartesian3(400000.0, 300000.0, 400000.0) })); ticProperty.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ iso8601 : '2019-01-02T06:00:00.00Z/2019-01-02T12:00:00.00Z', isStartIncluded : true, isStopIncluded : false, data : new Cesium.Cartesian3(400000.0, 300000.0, 500000.0) })); ticProperty.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ iso8601 : '2019-01-02T12:00:00.00Z/2019-01-02T18:00:00.00Z', isStartIncluded : true, isStopIncluded : false, data : new Cesium.Cartesian3(400000.0, 300000.0, 600000.0) })); ticProperty.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ iso8601 : '2019-01-02T18:00:00.00Z/2019-01-03T23:00:00.00Z', isStartIncluded : true, isStopIncluded : true, data : new Cesium.Cartesian3(400000.0, 300000.0, 700000.0) })); // 3 compositeProperty var compositeProperty = new Cesium.CompositeProperty(); compositeProperty.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ iso8601 : '2019-01-01T00:00:00.00Z/2019-01-02T00:00:00.00Z', data : sampledProperty })); compositeProperty.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ iso8601 : '2019-01-02T00:00:00.00Z/2019-01-03T00:00:00.00Z', isStartIncluded : false, isStopIncluded : false, data : ticProperty })); // 4 設置position blueBox