原文链接及内容

实现效果如下:

示例代码如下:

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
const viewer = new Cesium.Viewer("cesiumContainer", {
shouldAnimate: true,
});

const statusDisplay = document.createElement("div");
const fuelDisplay = document.createElement("div");
const czmlPath = "../SampleData/";
let vehicleEntity;

// 添加一个空白的 CzmlDataSource 来保存我们的多部分实体。
const dataSource = new Cesium.CzmlDataSource();
viewer.dataSources.add(dataSource);

// 该演示展示了如何将单个路径分解为多个 CZML 流(streams)。
const partsToLoad = [
{
url: "MultipartVehicle_part1.czml",
range: [0, 1500],
requested: false,
loaded: false,
},
{
url: "MultipartVehicle_part2.czml",
range: [1500, 3000],
requested: false,
loaded: false,
},
{
url: "MultipartVehicle_part3.czml",
range: [3000, 4500],
requested: false,
loaded: false,
},
];

function updateStatusDisplay() {
let msg = "";
partsToLoad.forEach(function (part) {
msg += `${part.url} - `;
if (part.loaded) {
msg += "已加载.<br/>";
} else if (part.requested) {
msg += "正在...<br/>";
} else {
msg += "暂时不需要.<br/>";
}
});
statusDisplay.innerHTML = msg;
}

// Helper 函数将某个部分标记为已请求,并将其处理到 dataSource 中。
function processPart(part) {
part.requested = true;
updateStatusDisplay();
/**
* load(czml, options): 加载提供的 URL 或 CZML 对象,替换任何现有数据。
* process(czml, options): 处理提供的 URL 或 CZML 对象,而不清除任何现有数据。
*/
dataSource.process(czmlPath + part.url).then(function () {
part.loaded = true;
updateStatusDisplay();

// 使用摄像头跟踪车辆。
if (!viewer.trackedEntity) {
viewer.trackedEntity = vehicleEntity =
dataSource.entities.getById("Vehicle");
}
});
}

// 预先加载第一部分数据。
processPart(partsToLoad[0]);

// 在时钟自然到达之前加载新的部分。请注意,这无法预测用户何时可以快进到该部分。
const preloadTimeInSeconds = 100;

viewer.clock.onTick.addEventListener(function (clock) {
// 此示例使用从开始的时间偏移来识别哪些部分需要加载。
/**
* Cesium.JulianDate.secondsDifference(left, right): 计算提供的实例之间的秒数差。
*/
const timeOffset = Cesium.JulianDate.secondsDifference(
clock.currentTime,
clock.startTime
);

// 筛选出当前需要加载的 czml 流(stream)列表。然后,处理每个需要加载的czml 流。
partsToLoad
.filter(function (part) {
return (
!part.requested &&
timeOffset >= part.range[0] - preloadTimeInSeconds &&
timeOffset <= part.range[1]
);
})
.forEach(function (part) {
processPart(part);
});

if (vehicleEntity) {
const fuel = vehicleEntity.properties.fuel_remaining.getValue(
clock.currentTime
);
if (Cesium.defined(fuel)) {
fuelDisplay.textContent = `燃油: ${fuel.toFixed(2)} 加仑`;
}
}
});

// 为方便起见,添加重置按钮。
Sandcastle.addToolbarButton("重置示例", function () {
viewer.clock.currentTime = viewer.clock.startTime;
viewer.clock.shouldAnimate = true;

partsToLoad.forEach(function (part) {
part.requested = false;
part.loaded = false;
});

dataSource.entities.removeAll();
processPart(partsToLoad[0]);
});

// 在重置按钮下方显示状态信息。
statusDisplay.style.background = "rgba(42, 42, 42, 0.7)";
statusDisplay.style.padding = "5px 10px";
document.getElementById("toolbar").appendChild(statusDisplay);

// 显示从 CZML 读取的多部分自定义属性。
fuelDisplay.style.background = "rgba(42, 42, 42, 0.7)";
fuelDisplay.style.padding = "5px 10px";
fuelDisplay.style.marginTop = "5px";
document.getElementById("toolbar").appendChild(fuelDisplay);

我们打开其中某个CZML文档,内容如下:代码中针对个别属性做了注释。

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
106
107
108
109
[
{
"id":"document",
"version":"1.0"
},
{
"id":"Vehicle",
//availability属性表示数据可用的时间区间,可以是一个指定单个区间的字符串,也可以是一个表示区间的字符串数组。
"availability":"2012-08-04T16:00:00Z/2012-08-04T17:04:54.9962195740191Z",
//黄色填充黑色轮廓字体,并在指定时间段内显示(show属性的interval属性)
"label":{
"fillColor":[
{
"interval":"2012-08-04T16:00:00Z/2012-08-04T18:00:00Z",
"rgba":[
255,255,0,255
]
}
],
"font":"bold 10pt Segoe UI Semibold",
"horizontalOrigin":"CENTER",
"outlineColor":{
"rgba":[
0,0,0,255
]
},
"pixelOffset":{
"cartesian2":[
0.0,20.0
]
},
"scale":1.0,
"show":[
{
"interval":"2012-08-04T16:00:00Z/2012-08-04T18:00:00Z",
"boolean":true
}
],
"style":"FILL",
"text":"Test Vehicle",
"verticalOrigin":"CENTER"
},
"model":{
"gltf":"models/CesiumMilkTruck/CesiumMilkTruck.glb",
"minimumPixelSize":100,
"maximumScale":50
},
//orientation:基于速度向量(velocityReference: #position),表示车辆方向随运动轨迹变化。
"orientation" : {
"velocityReference": "#position"
},
//viewFrom:视角,相机相对于车辆的偏移量:[-2080, -1715, 779](笛卡尔坐标)
"viewFrom": {
"cartesian": [ -2080, -1715, 779 ]
},
"properties" : {
//燃料剩余量,时间为0秒是还剩22.5加仑,时间为1500秒时还剩21.2加仑
"fuel_remaining" : {
"epoch":"2012-08-04T16:00:00Z",
"number": [
0, 22.5,
1500, 21.2
]
}
},
//黄颜色线宽为4的路径
"path":{
"material":{
"solidColor":{
"color":{
"interval":"2012-08-04T16:00:00Z/2012-08-04T18:00:00Z",
"rgba":[
255,255,0,255
]
}
}
},
"width":[
{
"interval":"2012-08-04T16:00:00Z/2012-08-04T18:00:00Z",
"number":5.0
}
],
"show":[
{
"interval":"2012-08-04T16:00:00Z/2012-08-04T18:00:00Z",
"boolean":true
}
]
},
"position":{
"interpolationAlgorithm":"LAGRANGE",//插值算法:拉格朗日插值算法。
"interpolationDegree":1,
"epoch":"2012-08-04T16:00:00Z",
"cartesian":[
//每10秒一个点
0.0,-2379754.6637012,-4665332.88013588,3628133.68924173,
10.0,-2379510.08905552,-4665419.64840452,3628182.20006795,
20.0,-2379568.4769522,-4665555.3441867,3627970.83323261,
30.0,-2379638.93786855,-4665691.63561896,3627750.82085873,
/.../
1470.0,-2345636.65904236,-4677231.51689469,3634979.51967852,
1480.0,-2345392.56990766,-4677273.29176384,3635082.57200902,
1490.0,-2345148.47663614,-4677315.05835029,3635185.61789803,
1.5e3,-2344904.37922829,-4677356.81665397,3635288.65734536
]
}
}
]