Cesium的Property机制究竟有多香

前言

这两天在研究Cesium中如何实现沿线飞行或漫游功能,上网查资料发现有好多博主的解决办法都包含了什么什么Property……勾起了我强烈的好奇心,遂去了Cesium官网一探究竟,一下我就被第一句话震惊到了。

All values we define for our entities are stored as Property objects.

实体的所有值都被维护成了Property对象,这让我不得不往下看,但我越看越气,这么重要的东西,你这啥也没讲啊。算了还是我自己研究吧。

image-20210603163511040.png

什么是Property机制

个人感觉Cesium的Property本质上与Object.defineProperties类似,defineProperties直接封装了基本类型,如果是Object,则是引用形式。通过Property的封装,将引用或复制的权力交给了设计者,同时提供一些特殊的功能以满足需求。

为什么要用Property

首先用一个例子来简单展示一下Property的作用。

1
2
3
4
5
6
7
8
// 创建盒子
var box = viewer.entities.add({
name: "box",
position: Cesium.Cartesian3.fromDegrees(121.54035, 38.92146, 2000),
box: {
dimensions: new Cesium.Cartesian3(1000.0, 1000.0, 1000.0),
},
});

假如我们想实现盒子的大小随时间变化而变化,第一想到的应该就是用setInterval去改变盒子的dimensions,但这样对性能是个不小的挑战。而Cesium提供一种机制,可以让其随时间自动变化并赋值,这就是Property。以下代码是在5秒内让盒子大小变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var property = new Cesium.SampledProperty(Cesium.Cartesian3);

property.addSample(
Cesium.JulianDate.fromDate(new Date()),
new Cesium.Cartesian3(1000.0, 1000.0, 1000.0)
);

property.addSample(
Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
5,
new Cesium.JulianDate()
),
new Cesium.Cartesian3(2000.0, 2000.0, 2000.0)
);

box.box.dimensions = property;

我们通过addSample向Property实例添加关键帧并定义想要修改的属性,最后赋值给box的dimensions,效果如下

property1.gif

所以说,Property可以和时间轴进行关联,并根据时间返回对应的属性值,而Entity则可以通过返回的值动态改变实体的位置、大小等。

Property分类及使用方法

上文中我们举例使用的SampledProperty提供了插值功能,还有很多Property的类型,我们可以在API文档中搜索一下Property,整整有29个之多。

image-20210604095802707.png

简单归类一下可以分为几类:

  • 基本类型:ConstantPropertySampledPropertyTimeIntervalCollectionPropertyCompositeProperty
  • 其他类型:CallbackPropertyReferencePropertyPropertyArrayPropertyBagVelocityOrientationPropertyVelocityVectorProperty
  • 材质类型:MaterialProperty及带material字样的
  • 位置类型:带Position字样的

同时Cesium提供了一个Property类作为所有类型的基类,并定义了几个公共属性及接口。

getValue(time, result) → Cartesian3

获取特定时间点下的属性值

Name Type Description
time JulianDate 检索值的时间
result Cartesian3 将值存储到的对象。若缺省则创建并返回新实例

equal

用来判断属性值是否相等。

接下来我们就一些常用的Property做一下讲解和代码实现。

基本类型

SampledProperty

第一个例子中我们使用的就是它,通过添加不同时间点的Sample,在每两个时间点之间进行线性插值,这里不做演示,代码和效果都在第一个例子中了。

TimeIntervalCollectionProperty

用来指定具体时间段内的属性值,每个时间段内属性值不变。所以和SampledProperty不同,它呈现出的变化为跳跃式。

property2.gif

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
var property = new Cesium.TimeIntervalCollectionProperty(
Cesium.Cartesian3
);


property.intervals.addInterval(
new Cesium.TimeInterval({
start: Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
2,
new Cesium.JulianDate()
),
stop: Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
20,
new Cesium.JulianDate()
),
isStartIncluded:true,
isStopIncluded:false,
data: new Cesium.Cartesian3(1000.0, 1000.0, 1000.0),
})
);
property.intervals.addInterval(
new Cesium.TimeInterval({
start: Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
6,
new Cesium.JulianDate()
),
stop: Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
20,
new Cesium.JulianDate()
),
isStartIncluded:true,
isStopIncluded:false,
data: new Cesium.Cartesian3(2000.0, 2000.0, 2000.0),
})
);
property.intervals.addInterval(
new Cesium.TimeInterval({
start: Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
10,
new Cesium.JulianDate()
),
stop: Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
20,
new Cesium.JulianDate()
),
isStartIncluded:true,
isStopIncluded:true,
data: new Cesium.Cartesian3(3000.0, 3000.0, 3000.0),
})
);

box.box.dimensions = property;

ConstantProperty

不随时间的变化而变化的属性。

相对于上述两种Property,更加常用的可能就是这个ConstantProperty了,在我们平常设置实体的属性时一般都是如下设置:

1
box.box.dimensions = new Cesium.Cartesian3(100, 100, 100);

但是实际上其实完整的写法应该是:

1
box.box.dimensions = new ConstantProperty(new Cesium.Cartesian3(100, 100, 100));

这么看我们可以发现,box中的dismensions属性其实是Property类型,在Cesium内部偷偷的将我们传入的Cartesian3转化成了ConstantProperty类型。

ConstantProperty也并非不能更改,它提供了setValue方法去修改属性值,利用setValue会修改原有的属性值,而非创建新的ConstantProperty

CompositeProperty

property3.gif

顾名思义这是个复合属性,它可以将多种Property进行组合操作,例如在一段时间内需要跳跃性变化,然后进行平滑变化,则可以使用这种类型。看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
var lineProperty = new Cesium.SampledProperty(Cesium.Cartesian3);

lineProperty.addSample(
Cesium.JulianDate.fromDate(new Date()),
new Cesium.Cartesian3(1000.0, 1000.0, 1000.0)
);

lineProperty.addSample(
Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
5,
new Cesium.JulianDate()
),
new Cesium.Cartesian3(3000.0, 3000.0, 3000.0)
);

var timeProperty = new Cesium.TimeIntervalCollectionProperty(
Cesium.Cartesian3
);

timeProperty.intervals.addInterval(
new Cesium.TimeInterval({
start: Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
5,
new Cesium.JulianDate()
),
stop: Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
20,
new Cesium.JulianDate()
),
isStartIncluded: true,
isStopIncluded: false,
data: new Cesium.Cartesian3(3000.0, 3000.0, 3000.0),
})
);
timeProperty.intervals.addInterval(
new Cesium.TimeInterval({
start: Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
10,
new Cesium.JulianDate()
),
stop: Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
20,
new Cesium.JulianDate()
),
isStartIncluded: true,
isStopIncluded: false,
data: new Cesium.Cartesian3(4000.0, 4000.0, 4000.0),
})
);
timeProperty.intervals.addInterval(
new Cesium.TimeInterval({
start: Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
15,
new Cesium.JulianDate()
),
stop: Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
20,
new Cesium.JulianDate()
),
isStartIncluded: true,
isStopIncluded: true,
data: new Cesium.Cartesian3(5000.0, 5000.0, 5000.0),
})
);

var compositeProperty = new Cesium.CompositeProperty();
compositeProperty.intervals.addInterval(
new Cesium.TimeInterval({
start: Cesium.JulianDate.fromDate(new Date()),
stop: Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
5,
new Cesium.JulianDate()
),
isStartIncluded: false,
isStopIncluded: false,
data: lineProperty,
})
);
compositeProperty.intervals.addInterval(
new Cesium.TimeInterval({
start: Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
5,
new Cesium.JulianDate()
),
stop: Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
20,
new Cesium.JulianDate()
),
isStartIncluded: false,
isStopIncluded: false,
data: timeProperty,
})
);

box.box.dimensions = compositeProperty;

位置类型

PositionProperty

同Property,PositionProperty是一个虚基类,不能直接实例化,它增加了referenceFrame,只能表示position

referenceFrame用来获取position的参考系,目前Cesium提供两种参考系FIXEDINERTIAL。默认使用的FIXED参考系,即坐标在地球上的位置是固定的。

基于PositionProperty的类型有以下几种:

  • CompositePositionProperty
  • ConstantPositionProperty
  • PositionProperty
  • PositionPropertyArray
  • SampledPositionProperty
  • TimeIntervalCollectionPositionProperty

用法上和基本类型基本相同,只不过它们专门用来表示位置信息。这里不做举例。

材质类型

MaterialProperty

专门用来表示材质,扩展了getType方法来获取材质类型。

同样它也有很多派生类,比如ColorMaterialPropertyImageMaterialProperty等等,我们在平时的demo和项目中也都有使用。

我们可以利用基本类型和材质类型实现一个颜色的动态效果。

property4.gif

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var colorProperty = new Cesium.SampledProperty(Cesium.Color);

colorProperty.addSample(
Cesium.JulianDate.fromDate(new Date()),
new Cesium.Color(0, 1, 0)
);

colorProperty.addSample(
Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
5,
new Cesium.JulianDate()
),
new Cesium.Color(0, 0, 1)
);

box.box.material = new Cesium.ColorMaterialProperty(colorProperty);

其他类型

CallbackProperty

这里主要介绍一下CallbackProperty,它是自由度最高的一种类型,我们只需要提供一个回调函数来返回我们需要的值即可,在回调函数中我们可以随意进行操作。在这我们实现一个随机变化颜色并且不断增高的box。

property5.gif

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let l = 2000.0;
box.box.dimensions = new Cesium.CallbackProperty(function (time, result) {
result = result || new Cesium.Cartesian3(0, 0, 0);

l += 20.0;
if (l > 7000.0) {
l = 2000.0;
}

result.x = 4000.0;
result.y = 3000.0;
result.z = l;
return result;
}, false);
box.box.material = new Cesium.ColorMaterialProperty(
new Cesium.CallbackProperty(function () {
return Cesium.Color.fromRandom({
alpha: 1.0,
});
}, false)
);

ReferenceProperty

该property可以直接链接到另一个对象的Property,做引用效果。

参数 类型 描述
targetCollection EntityCollection 将用于解析引用的实体集合。
targetId String 被引用的实体的 id。
targetPropertyNames Array. 将使用的目标实体上的属性名称。

PropertyBag

它用来对一个对象进行包装,使得该对象的每一个属性都可作为一个动态的Property进行修改。比如之前修改dimensions的话,dimensions是作为一个Cartesian3类型变量整体封装到Property中去的,如果我们只想修改dimensions的x。则可以使用PropertyBag来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
var zp = new Cesium.SampledProperty(Number);
zp.addSample(Cesium.JulianDate.fromDate(new Date()), 2000.0);
zp.addSample(Cesium.JulianDate.addSeconds(
Cesium.JulianDate.fromDate(new Date()),
5,
new Cesium.JulianDate()
),, 7000.0);

box.box.dimensions = new Cesium.PropertyBag({
x: 4000.0,
y: 3000.0,
z: zp
});

VelocityOrientationProperty

该Property用来Entity的position的位置变化,来计算出移动的方向,最后把速度方向输出成Orientation。Cesium自带的示例中有一个Interpolation中有其用法,不再赘述。

使用场景

Property机制很强大,我们可以在很多场景中使用它,比如实现一些沿线飞行、路径漫游或者实体的大小属性变化等。可以说只要有修改属性的地方我们都可以用到它。


Cesium的Property机制究竟有多香
https://moewang0321.github.io/2021/06/07/Cesium的Property机制究竟有多香/
作者
Moe Wang
发布于
2021年6月7日
许可协议