空间数据可视化

空间数据可视化

我们在之前的 Cesium 中的数据加载 章节当中介绍了如何使用 Cesium 加载影像数据、地形数据、以及矢量数据,但是作为一个完整的三维系统,仅仅包括这些数据还是远远不够的,当然还需要一些比如空间可视化数据、三维数据数据等,今天我们先从空间数据的可视化开始看起

Cesium 在空间数据可视化方面提供了两种类型的 API,如下

  • 一种是面向图形开发人员的低级(原始)API,通过 Primitive 类来进行实现
  • 另一种是用于数据驱动的高级(实体)API,通过 Entity 类实现

但是相对于 Primitive API 来说,Entity API 实现起来更简单一些,也比较容易上手,Entity API 实际上是对 Primitive API 的二次封装,底层调用的仍然是 Primitive API,目的就是为我们提供灵活的、易学、易用的高性能可视化界面

本章内容主要介绍的是 Entity API,关于 Primitive API 这里只是简单提及,后续会详细来进行介绍

Entity 支持的图形类型

通过文档查看 Entity 类的构造函数 我们可知,Entity 支持的图形类型都是以 Graphics 结尾的,一共有十七种类型,我们这里简单汇总一下,下图列出了 Entity 所支持的图形类型以及对应的类

下面我们就来简单看一下每种类型的 Graphic 添加方式

billboard 广告牌

描述位于包含 Entity 的位置的二维图标,详细见 new Cesium.BillboardGraphics(options)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var entity = viewer.entities.add({
name: 'billboard',
position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
billboard: {
show: true,
image: './images/Cesium_Logo_overlay.png',
scale: 2.0,
pixelOffset: new Cesium.Cartesian2(0, -50), // 像素偏移
eyeOffset: new Cesium.Cartesian3(0.0, 0.0, 0.0), // 眼睛偏移
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // 水平对齐方式
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 垂直对齐方式
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, // 获取或设置此广告牌的高度参考
color: Cesium.Color.LIME,
rotation: Cesium.Math.PI_OVER_FOUR, // 获取或设置以弧度为单位的旋转角度
alignedAxis: Cesium.Cartesian3.ZERO, // 获取或设置世界空间中的对齐轴
width: 100,
height: 25,
scaleByDistance: new Cesium.NearFarScalar(1.0e3, 2.0, 2.0e3, 1.0), // 根据广告牌与相机的距离获取或设置广告牌的近和远缩放属性
translucencyByDistance: new Cesium.NearFarScalar(1.0e3, 1.0, 1.5e6, 0.5), // 根据广告牌到相机的距离,获取或设置广告牌的近和远半透明属性
pixelOffsetScaleByDistance: new Cesium.NearFarScalar(1.0e3, 1.0, 1.5e6, 0.0), // 根据广告牌与摄像头的距离,获取或设置广告牌的近像素偏移量和远像素偏移量缩放属性
disableDepthTestDistance: Number.POSITIVE_INFINITY, // 指定要禁用深度测试的距相机的距离
}
})

这里关于两种对齐方式简单说明一下,其中水平对齐方式

  • 默认值为 HorizontalOrigin.CENTER
  • 其中 CENTER 表示原点在对象的水平中心
  • LEFT 表示原点在对象的左侧
  • RIGHT 表示原点在对象的右侧

而垂直对齐方式

  • 默认值为 VerticalOrigin.CENTER
  • BASELINE 表示如果对象包含文本,则原点位于文本的基线,否则原点位于对象的底部
  • CENTER 表示原点位于 BASELINETOP 之间的垂直中心
  • TOP 表示原点在对象的顶部
  • BOTTOM 表示原点在对象的底部

box 盒子

描述一个盒子,中心位置和方向由包含的 Entity 确定,详细见 new Cesium.BoxGraphics(options)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var entity = viewer.entities.add({
name: 'box',
position: Cesium.Cartesian3.fromDegrees(-107.0, 40.0, 300000.0),
box: {
show: true,
dimensions: new Cesium.Cartesian3(400000.0, 300000.0, 500000.0), // 用于指定框的长度,宽度和高度
heightReference: Cesium.HeightReference.NONE, // 指定距实体位置的高度是相对于什么的高度
fill: true,
material: Cesium.Color.RED.withAlpha(0.5),
outline: true,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 1.0,
shadows: Cesium.ShadowMode.DISABLED
}
})

这里的 heightReference 有三个取值

  • Cesium.HeightReference.NONE 表示位置绝对
  • Cesium.HeightReference.CLAMP_TO_GROUND 表示位置固定在地形上
  • Cesium.HeightReference.RELATIVE_TO_GROUND 表示位置高度是指地形上方的高度

corridor 走廊

走廊是由中心线和宽度定义的形状符合地球的曲率,它可以放置在地面上或高空并可以选择挤出成一个体积,详细见 new Cesium.CorridorGraphics(options)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var entity = viewer.entities.add({
name: 'corridor',
corridor: {
positions: Cesium.Cartesian3.fromDegreesArray( // 指定定义走廊中心线的 Cartesian3 位置的数组
[-80.0, 40.0, -85.0, 40.0, -85.0, 35.0,]
),
width: 200000.0,
height: 200000.0,
heightReference: Cesium.HeightReference.NONE,
extrudedHeight: 100000.0,
extrudedHeightReference: Cesium.HeightReference.NONE,
cornerType: Cesium.CornerType.ROUNDED, // 拐角的样式,其中 OUNDED 角有光滑的边缘,MITERED 拐角点是相邻边的交点,BEVELED 角被修剪
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离
fill: true,
material: Cesium.Color.BLUE.withAlpha(0.5), // 材质
outline: true,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 1.0,
shadows: Cesium.ShadowMode.DISABLED,
classificationType: Cesium.ClassificationType.BOTH, // TERRAIN 将仅对地形进行分类,CESIUM_3D_TILE 将仅对 3D Tiles 进行分类,BOTH 将同时对 Terrain 和 3D Tiles 进行分类
},
})

cylinder 圆柱、圆锥

描述圆柱体,圆锥台或由长度,顶部半径和底部半径定义的圆锥,中心位置和方向由包含的 Entity 确定,详细见 new Cesium.CylinderGraphics(options)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var entity = viewer.entities.add({
name: 'cylinder',
position: Cesium.Cartesian3.fromDegrees(-105.0, 40.0, 200000.0),
cylinder: {
length: 400000.0, // 圆柱体长度
topRadius: 200000.0, // 圆柱体顶部半径
bottomRadius: 200000.0, // 圆柱体底部半径
heightReference: Cesium.HeightReference.NONE,
fill: true,
material: Cesium.Color.GREEN.withAlpha(0.5),
outline: true,
outlineColor: Cesium.Color.DARK_GREEN,
outlineWidth: 1.0,
numberOfVerticalLines: 16, // 沿轮廓的周长绘制的垂直线的数量
shadows: Cesium.ShadowMode.DISABLED,
slices: 128, // 圆柱周围的边缘数量
},
})

ellipse 椭圆或拉伸的椭圆

描述由中心点,半长轴和半短轴定义的椭圆,椭圆符合地球的曲率,可以放置在表面或可以选择将其挤出成一定体积,中心点由包含的 Entity 确定,详细见 new Cesium.EllipseGraphics(options)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var entity = viewer.entities.add({
name: 'Circles and Ellipses',
position: Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 100000.0),
ellipse: {
show: true,
semiMajorAxis: 300000.0, // 长半轴距离
semiMinorAxis: 150000.0, // 短半轴距离

height: 20000.0,
heightReference: Cesium.HeightReference.NONE,
extrudedHeight: 20000.0,
extrudedHeightReference: Cesium.HeightReference.NONE,
stRotation: 0.0, // 纹理从北方逆时针旋转
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 椭圆上各点之间的角距离
material: Cesium.Color.BLUE.withAlpha(0.5),
fill: true,
outline: true,
outlineColor: Cesium.Color.DARK_GREEN,
outlineWidth: 1.0,
numberOfVerticalLines: 16, // 沿轮廓的周长绘制的垂直线的数量
shadows: Cesium.ShadowMode.DISABLED,
classificationType: Cesium.ClassificationType.BOTH
}
})

ellipsoid 椭球体

描述一个椭球或球体,中心位置和方向由包含的 Entity 确定,详细见 new Cesium.EllipsoidGraphics(options)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var entity = viewer.entities.add({
name: 'Spheres and Ellipsoids',
position: Cesium.Cartesian3.fromDegrees(-100.0, 40.0, 300000.0),
ellipsoid: {
show: true,
radii: new Cesium.Cartesian3(200000.0, 200000.0, 300000.0), // 椭球半径
minimumClock: 0.0, // 最小时钟角度
maximumClock: 2 * Math.PI, // 最大时钟角度
minimumCone: 0.0, // 最小圆锥角
maximumCone: Math.PI, // 最大圆锥角
heightReference: Cesium.HeightReference.NONE,
fill: true,
material: Cesium.Color.BLUE.withAlpha(0.5),
outline: true,
outlineColor: Cesium.Color.YELLOW,
outlineWidth: 1.0,
stackPartitions: 64, // 延纬度线切割的次数
slicePartitions: 64, // 延经度线切割的次数
subdivisions: 128, // 每个轮廓环的样本数,确定曲率的粒度
shadows: Cesium.ShadowMode.DISABLED,
},
})

label 标签

描述位于包含位置的二维标签,详细见 new Cesium.LabelGraphics(options)

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
var entity = viewer.entities.add({
name: 'label',
position: Cesium.Cartesian3.fromDegrees(-75.1641667, 39.9522222, 300000.0),
label: {
show: true,
text: 'label标签',
font: '24px Helvetica',
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
scale: 1.0,
showBackground: true,
backgroundColor: Cesium.Color.BLUE,
backgroundPadding: new Cartesian2(7, 5),
pixelOffset: Cartesian2.ZERO,
eyeOffset: Cartesian3.ZERO,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.CENTER,
heightReference: Cesium.HeightReference.NONE,
fillColor: Cesium.Color.SKYBLUE,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2,
translucencyByDistance: new Cesium.NearFarScalar(1.0e3, 1.0, 1.5e6, 0.5),
pixelOffsetScaleByDistance: new Cesium.NearFarScalar(1.0e3, 1.0, 1.5e6, 0.0),
scaleByDistance: new Cesium.NearFarScalar(1.0e3, 2.0, 2.0e3, 1.0),
disableDepthTestDistance: Number.POSITIVE_INFINITY,
},
})

model 模型

基于 glTF3D 模型,模型的位置和方向由包含的 Entity 确定,不过 Cesium 虽然包括对 glTF 几何,材质,动画和蒙皮的支持,但是目前不支持照相机和灯光,详细见 new Cesium.ModelGraphics(options)

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
var position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, 5000.0)
var heading = Cesium.Math.toRadians(135)
var pitch = 0
var roll = 0
var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll)
var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr)
var url = './data/models/CesiumAir/Cesium_Air.glb'

var entity = viewer.entities.add({
name: 'model',
position: position,
orientation: orientation,
model: {
show: true,
uri: url,
scale: 1.0,
minimumPixelSize: 128, // 模型的最小最小像素大小,而不考虑缩放
maximumScale: 20000, // 模型的最大比例尺大小
incrementallyLoadTextures: true, // 确定在加载模型后纹理是否可以继续流入
runAnimations: true, // 是否应启动模型中指定的 glTF 动画
clampAnimations: true, // glTF 动画是否应在没有关键帧的持续时间内保持最后一个姿势
shadows: Cesium.ShadowMode.DISABLED,
heightReference: Cesium.HeightReference.NONE,
silhouetteColor: Cesium.Color.RED, // 轮廓的颜色
silhouetteSize: 0.0, // 轮廓的宽度
color: Cesium.Color.WHITE, // 模型的颜色
colorBlendMode: Cesium.ColorBlendMode.HIGHLIGHT, // 目标颜色和图元的源颜色之间混合的不同模式,HIGHLIGHT 表示将源颜色乘以目标颜色,REPLACE 表示将源颜色替换为目标颜色,MIX 表示将源颜色和目标颜色混合在一起
colorBlendAmount: 0.5, // 用于指定 colorBlendMode 为 MIX 时的颜色强度,值 0.0 会产生模型的着色,而值 1.0 会导致纯色,介于两者之间的任何值都会导致两者混合
imageBasedLightingFactor: new Cesium.Cartesian2(1.0, 1.0), // 指定基于漫反射和镜面反射的图像照明的贡献
lightColor: undefined // 为模型着色时指定浅色的属性,如果为 undefined 则使用场景的浅色
}
})

path 路径

描述一条折线,该折线定义为 Entity 随着时间的推移所形成的路径,详细见 new Cesium.PathGraphics(options)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var entityPath = viewer.entities.add({
position: pathPosition,
name: 'path',
path: {
show: true,
leadTime: 0,
trailTime: 60,
width: 10,
resolution: 1,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.3,
taperPower: 0.3,
color: Cesium.Color.PALEGOLDENROD,
}),
},
})

plane 平面

描述一处平面,详细见 new Cesium.PlaneGraphics(options)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var bluePlane = viewer.entities.add({
name: 'Blue plane',
position: Cesium.Cartesian3.fromDegrees(-114.0, 40.0, 300000.0),
plane: {
show: true,
plane: new Cesium.Plane(Cesium.Cartesian3.UNIT_X, 0.0), // 用于指定平面的法线和距离
dimensions: new Cesium.Cartesian2(400000.0, 300000.0), // 指定平面的宽度和高度
fill: true,
material: Cesium.Color.BLUE,
outline: false,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 1.0,
shadows: Cesium.ShadowMode.DISABLED,
},
})

point 点

描述位于包含 Entity 的位置的图形点,详细见 new Cesium.PointGraphics(options)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var point = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
point: {
show: true,
pixelSize: 10, // 像素大小
heightReference: Cesium.HeightReference.NONE,
color: Cesium.Color.YELLOW,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 0,
scaleByDistance: new Cesium.NearFarScalar(1.0e3, 10.0, 2.0e3, 1.0),
translucencyByDistance: new Cesium.NearFarScalar(1.0e3, 1.0, 1.5e6, 0.5),
disableDepthTestDistance: Number.POSITIVE_INFINITY,
},
})

polygon 多边形

描述由构成外部形状和任何嵌套孔的线性环的层次结构定义的多边形,多边形符合地球的曲率,可以放置在表面或可以选择将其挤出成一定体积,详细见 new Cesium.PolygonGraphics(options)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var redPolygon = viewer.entities.add({
name: 'Red polygon on surface',
polygon: {
show: true,
hierarchy: Cesium.Cartesian3.fromDegreesArray([-115.0, 37.0, -115.0, 32.0, -107.0, 33.0, -102.0, 31.0, -102.0, 35.0]),
height: 0, // 多边形相对于椭球面的高度
heightReference: Cesium.HeightReference.NONE,
stRotation: 0.0, // 多边形纹理从北方逆时针旋转
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度点之间的角距离
fill: true,
material: Cesium.Color.RED,
outline: false,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 1.0,
perPositionHeight: false, // 是否使用每个位置的高度
closeTop: true, // 如果为 false,则将挤出的多边形顶部留空
closeBottom: true, // 如果为 false,则将挤出的多边形的底部保留为开放状态
arcType: Cesium.ArcType.GEODESIC, // 多边形边缘必须遵循的线型
shadows: Cesium.ShadowMode.DISABLED,
classificationType: Cesium.ClassificationType.BOTH,
zIndex: 0, // 指定用于订购地面几何形状的 z 索引,仅在多边形为常数且未指定高度或拉伸高度的情况下才有效
},
})

polyline 多线段

描述折线,前两个位置定义线段,并且每个其他位置都从前一个位置定义了一个线段,细分可以是线性连接点,大弧或固定在地形上,详细见 new Cesium.PolylineGraphics(options)

1
2
3
4
5
6
7
8
9
10
11
12
var redLine = viewer.entities.add({
name: 'Red line on terrain',
polyline: {
show: true,
positions: Cesium.Cartesian3.fromDegreesArray([-75, 35, -125, 35]), // 定义线条的 Cartesian3 位置的数组
width: 5,
material: Cesium.Color.RED,
clampToGround: true, // 是否贴地
shadows: Cesium.ShadowMode.DISABLED, // 折线是投射还是接收光源的阴影
classificationType: Cesium.ClassificationType.BOTH,
},
})

polylineVolume 多线段柱体

描述折线体积,该折线体积定义为线带和沿其拉伸的相应二维形状,生成的体积符合地球的曲率,详细见 new Cesium.PolylineVolumeGraphics(options)

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
function computeCircle(radius) {
var positions = []
for (var i = 0; i < 360; i++) {
var radians = Cesium.Math.toRadians(i)
positions.push(new Cesium.Cartesian2(radius * Math.cos(radians), radius * Math.sin(radians)))
}
return positions
}

var redTube = viewer.entities.add({
name: 'Red tube with rounded corners',
polylineVolume: {
show: true,
positions: Cesium.Cartesian3.fromDegreesArray( // 定义线带的 Cartesian3 位置的数组
[-85.0, 32.0, -85.0, 36.0, -89.0, 36.0]
),
shape: computeCircle(60000.0), // 指定 Cartesian2 位置的数组,这些位置定义了要拉伸的形状
cornerType: Cesium.CornerType.ROUNDED, // 拐角的样式
fill: true,
material: Cesium.Color.RED,
outline: false,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 1.0,
shadows: Cesium.ShadowMode.DISABLED, // 体积是投射还是接收光源的阴影
},
})

rectangle 矩形

描述 Rectangle 的图形,矩形符合地球的曲率,可以放置在表面或可以选择将其挤出成一定体积,详细见 new Cesium.RectangleGraphics(options)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var redRectangle = viewer.entities.add({
name: 'Red translucent rectangle',
rectangle: {
show: true,
coordinates: Cesium.Rectangle.fromDegrees(-110.0, 20.0, -80.0, 25.0),
rotation: 0.0, // 矩形从北方向顺时针方向的旋转
stRotation: 0.0, // 矩形纹理从北方逆时针旋转
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 指定矩形上各点之间的角度距离
fill: true,
material: Cesium.Color.RED.withAlpha(0.5),
outline: false,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 1.0,
shadows: Cesium.ShadowMode.DISABLED,
classificationType: Cesium.ClassificationType.BOTH,
zIndex: 0,
},
})

wall 墙

描述定义为线带和可选的最大和最小高度的二维墙,墙符合地球仪的曲率,可以沿着地面或在高处放置,详细见 new Cesium.WallGraphics(options)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var redWall = viewer.entities.add({
name: 'Red wall at height',
wall: {
show: true,
positions: Cesium.Cartesian3.fromDegreesArrayHeights([-115.0, 44.0, 200000.0, -90.0, 44.0, 200000.0]),
minimumHeights: [100000.0, 100000.0], // 用于墙底而不是地球表面的高度数组
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 指定矩形上各点之间的角度距离
fill: true,
material: Cesium.Color.RED,
outline: false,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 1.0,
shadows: Cesium.ShadowMode.DISABLED,
},
})

tileset 3D Tiles 瓦片集

一个 3D Tiles 的图块集,用于流式传输海量 3D 地理空间数据集,详细见 new Cesium.Cesium3DTileset(options)3d-tiles

1
2
3
4
5
6
7
8
var tileset = viewer.entities.add({
name: '3D Tiles',
position: Cesium.Cartesian3.fromDegrees(-114.0, 40.0, 300000.0),
tileset: {
show: true,
uri: new Cesium.Plane(Cesium.Cartesian3.UNIT_X, 0.0),
},
})

Entity 聚合

同时,针对 BillboardLabelPointCesium 提供了 EntityCluster 类用于实现聚合效果,但必须结合 PinBuilder 类实现,下面为实现聚合效果的核心代码

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
var options = {
camera: viewer.scene.camera,
canvas: viewer.scene.canvas,
}

var dataSourcePromise = viewer.dataSources.add(Cesium.KmlDataSource.load('./data/kml/facilities/facilities.kml', options))

dataSourcePromise.then(function (dataSource) {
var pixelRange = 15
var minimumClusterSize = 3
var enabled = true

dataSource.clustering.enabled = enabled
dataSource.clustering.pixelRange = pixelRange // 扩展屏幕空间边界框的像素范围
dataSource.clustering.minimumClusterSize = minimumClusterSize // 可以聚合的最小屏幕空间对象

var removeListener

var pinBuilder = new Cesium.PinBuilder()
var pin50 = pinBuilder.fromText('50+', Cesium.Color.RED, 48).toDataURL()
var pin40 = pinBuilder.fromText('40+', Cesium.Color.ORANGE, 48).toDataURL()
var pin30 = pinBuilder.fromText('30+', Cesium.Color.YELLOW, 48).toDataURL()
var pin20 = pinBuilder.fromText('20+', Cesium.Color.GREEN, 48).toDataURL()
var pin10 = pinBuilder.fromText('10+', Cesium.Color.BLUE, 48).toDataURL()

var singleDigitPins = new Array(8)
for (var i = 0; i < singleDigitPins.length; ++i) {
singleDigitPins[i] = pinBuilder.fromText('' + (i + 2), Cesium.Color.VIOLET, 48).toDataURL()
}

function customStyle() {
if (Cesium.defined(removeListener)) {
removeListener()
removeListener = undefined
} else {
removeListener = dataSource.clustering.clusterEvent.addEventListener(function (clusteredEntities, cluster) {
cluster.label.show = false
cluster.billboard.show = true
cluster.billboard.id = cluster.label.id
cluster.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM

if (clusteredEntities.length >= 50) {
cluster.billboard.image = pin50
} else if (clusteredEntities.length >= 40) {
cluster.billboard.image = pin40
} else if (clusteredEntities.length >= 30) {
cluster.billboard.image = pin30
} else if (clusteredEntities.length >= 20) {
cluster.billboard.image = pin20
} else if (clusteredEntities.length >= 10) {
cluster.billboard.image = pin10
} else {
cluster.billboard.image = singleDigitPins[clusteredEntities.length - 2]
}
})
}

// force a re-cluster with the new styling
var pixelRange = dataSource.clustering.pixelRange
dataSource.clustering.pixelRange = 0
dataSource.clustering.pixelRange = pixelRange
}

// start with custom style
customStyle()

var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
handler.setInputAction(function (movement) {
var pickedLabel = viewer.scene.pick(movement.position)
if (Cesium.defined(pickedLabel)) {
var ids = pickedLabel.id
if (Array.isArray(ids)) {
for (var i = 0; i < ids.length; ++i) {
ids[i].billboard.color = Cesium.Color.RED
}
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
})

效果是下面这样的

Entity 管理

我们在上面介绍了 Entity 的图形类型,有这么多类型的实体,Cesium 是如何管理这些实体对象的呢?这就需要我们从初始化 Viewer 对象开始说起了

当我们初始化 Viewer 类之后,会得到一个实例化对象 viewer,这个 viewer 会包含一个属性 entities,它的类型是 EntityCollection,也就是 Entity 的集合,它包括了 EntityCollection 类里面的所有属性和方法,所以 Cesium 管理 Entity 本质上是通过 EntityCollection 类进行管理的,比如下面这些方法

  • add - 添加 Entity
  • contains - 是否存在某个 Entity
  • getById - 通过 ID 获取 Entity
  • remove - 移除某个 Entity
  • removeAll - 移除所有的 Entity
  • removeById - 通过 ID 移除 Entity

下面是一个简单的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
var point = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-105.0, 41, 50010),
point: {
show: true,
pixelSize: 10, // 像素大小
},
})

var entity = viewer.entities.getById('entity_1')
viewer.entities.remove(entity)

// viewer.entities.removeById('entity_1')
// viewer.entities.removeAll()

Entity 拾取

在三维场景中,有一种比较常见的交互方式,那就就是鼠标点击三维场景种某一个几何图形,查看该图形的包含的属性信息,并显示在对应的信息窗体中,单击事件通过 ScreenSpaceEventHandler 类注册,拾取到的信息可通过以下两种方式获取

  • scene.pick - 获取窗体坐标处最顶部的实体
  • scene.drillPick - 窗体坐标处的实体列表

下面是一个简单的示例

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
// 添加拾取事件
var handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas)

handler.setInputAction(function (movement) {
var pickedEntity = pickEntity(viewer, movement.position)
console.log(pickedEntity)

var pickedEntities = drillPickEntities(viewer, movement.position)
console.log(pickedEntities)
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)

function pickEntity(viewer, windowPosition) {
var picked = viewer.scene.pick(windowPosition)
if (Cesium.defined(picked)) {
var id = Cesium.defaultValue(picked.id, picked.primitive.id)
if (id instanceof Cesium.Entity) {
return id
}
}
return undefined
}

function drillPickEntities(viewer, windowPosition) {
var i
var entity
var picked
var pickedPrimitives = viewer.scene.drillPick(windowPosition)
var length = pickedPrimitives.length
var result = []
var hash = {}
for (i = 0; i < length; i++) {
picked = pickedPrimitives[i]
entity = Cesium.defaultValue(picked.id, picked.primitive.id)
if (entity instanceof Cesium.Entity && !Cesium.defined(hash[entity.id])) {
result.push(entity)
hash[entity.id] = true
}
}
return result
}

Entity 固定

在实际的应用系统中,会有这样的需求,要求画的实体附着在地形表面、或三维建筑表面(比如视频融合),这时候就需要设置 xxGraphics 的相关属性了,可通过 heightReferenceclassificationType 这两个属性去控制

关于 heightReference 属性值有三个取值

  • NONE - 位置绝对(默认值)
  • CLAMP_TO_GROUND - 位置固定在地形上
  • RELATIVE_TO_GROUND - 位置高度是指地形上方的高度

关于 classificationType 属性值有三个取值

  • TERRAIN - 将仅对地形进行分类;
  • CESIUM_3D_TILE - 将仅对 3D Tiles 进行分类
  • BOTH - 将同时对 Terrain3D Tiles 进行分类

但是有两点需要注意

  • 其中 corridorellipsepolygonpolylinerectangle 可通过设置 classificationType 属性值显示仅贴地、仅贴建筑或者两者都贴的效果
  • billboardboxcorridorcylinderellipseellipsoidlabelmodelpointpolygontectangle 则是通过设置 heightReference 属性值为 CLAMP_TO_GROUND 显示贴地效果

这里需要注意的是,如果是 polyline 则必须设置 clampToGround 属性为 true

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
// 其中 corridor ellipse polygon retangle 会自动贴地
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-122.1958, 46.1915),
billboard: {
image: './images/facility.gif',
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
},
point: {
color: Cesium.Color.SKYBLUE,
pixelSize: 10,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
},
label: {
text: 'Clamped to ground',
font: '14pt sans-serif',
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
verticalOrigin: Cesium.VerticalOrigin.BASELINE,
fillColor: Cesium.Color.BLACK,
showBackground: true,
backgroundColor: new Cesium.Color(1, 1, 1, 0.7),
backgroundPadding: new Cesium.Cartesian2(8, 4),
disableDepthTestDistance: Number.POSITIVE_INFINITY,
},
})

viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-122.1958, 46.1915),
model: {
uri: './data/models/CesiumMan/Cesium_Man.glb',
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
minimumPixelSize: 128,
maximumScale: 100,
},
})

viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-122.195, 46.1915),
corridor: {
positions: Cesium.Cartesian3.fromDegreesArray([-122.19, 46.1914, -122.2, 46.1924, -122.21, 46.193]),
width: 500.0,
material: Cesium.Color.GREEN.withAlpha(0.5),
},
ellipse: {
semiMajorAxis: 300.0, // 长半轴距离
semiMinorAxis: 150.0, // 短半轴距离
material: Cesium.Color.BLUE.withAlpha(0.5),
},
polygon: {
hierarchy: {
positions: [
new Cesium.Cartesian3(-2358138.847340281, -3744072.459541374, 4581158.5714175375),
new Cesium.Cartesian3(-2357231.4925370603, -3745103.7886602185, 4580702.9757762635),
new Cesium.Cartesian3(-2355912.902205431, -3744249.029778454, 4582402.154378103),
new Cesium.Cartesian3(-2357208.0209552636, -3743553.4420488174, 4581961.863286629),
],
},
material: './images/Cesium_Logo_Color.jpg',
},
rectangle: {
coordinates: Cesium.Rectangle.fromDegrees(-122.2, 46.2, -122.1, 46.3),
material: Cesium.Color.RED.withAlpha(0.5),
},
})

// 其中 polyline 必须设置 clampToGround = true
viewer.entities.add({
polyline: {
positions: Cesium.Cartesian3.fromDegreesArray([-122.1968, 46.1915, -122.1968, 46.2015, -122.1968, 46.2015]),
clampToGround: true,
width: 5,
material: Cesium.Color.ORANGE,
depthFailMaterial: Cesium.Color.RED,
},
})

效果是下面这样的

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
var polygon = viewer.entities.add({
polygon: {
hierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromRadiansArray([
-1.3194369277314022, 0.6988062530900625,
-1.3193955980204217, 0.6988091578771254,
-1.3193931220959367, 0.698743632490865,
-1.3194358224045408, 0.6987471965556998,
])
),
material: './images/Cesium_Logo_Color.jpg',
classificationType: Cesium.ClassificationType.CESIUM_3D_TILE,
},
})

var polyline = viewer.entities.add({
polyline: {
positions: Cesium.Cartesian3.fromDegreesArray([
-75.60217330403601, 40.04102882709425,
-75.59968252414251, 40.04093615560871,
-75.598020153828, 40.04079437042357,
-75.59674934074435, 40.040816173283304,
-75.59630042791713, 40.03986900370842,
-75.59563636849978, 40.03930996506271,
-75.59492397899098, 40.03873932846581,
-75.59457991226778, 40.038392701955786,
-75.59424838652453, 40.03775403572295,
-75.59387104290336, 40.03677022167725,
-75.59355000490342, 40.03588760913535,
]),
width: 8,
material: new Cesium.PolylineOutlineMaterialProperty({
color: Cesium.Color.YELLOW,
outlineWidth: 2,
outlineColor: Cesium.Color.BLACK,
}),
clampToGround: true,
classificationType: Cesium.ClassificationType.TERRAIN,
},
})

效果是下面这样的

# GIS

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×