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
}));