原文链接及内容

效果如下图所示:

示例代码如下:

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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
/**
* 此类是自定义数据源(custom DataSource)的示例。
* 它加载 Google WebGL Globe (https://github.com/dataarts/webgl-globe) 定义的 JSON 数据。
*
* @alias WebGLGlobeDataSource
* @constructor
*
* @param {string} [name] 此数据源的名称。如果未定义,则将从 URL 中获取名称。
*
* @example 使用示例如下:
* const dataSource = new Cesium.WebGLGlobeDataSource();
* dataSource.loadUrl('sample.json');
* viewer.dataSources.add(dataSource);
*/
function WebGLGlobeDataSource(name) {
//所有公共配置都定义为 ES5 属性,这些只是“私有”变量及其默认值。
this._name = name;
this._changed = new Cesium.Event();
this._error = new Cesium.Event();
this._isLoading = false;
this._loading = new Cesium.Event();
this._entityCollection = new Cesium.EntityCollection();
this._seriesNames = [];
this._seriesToDisplay = undefined;
this._heightScale = 10000000;
this._entityCluster = new Cesium.EntityCluster();
}

Object.defineProperties(WebGLGlobeDataSource.prototype, {
//所有 DataSource 实例必须实现以下属性

/**
* 获取此实例的可读名称。
* @memberof WebGLGlobeDataSource.prototype
* @type {String}
*/
name: {
get: function () {
return this._name;
},
},
/**
* 由于 WebGL 地球 JSON 不是时间动态的,因此此属性始终为 undefined。
* @memberof WebGLGlobeDataSource.prototype
* @type {DataSourceClock}
*/
clock: {
value: undefined,
writable: false,
},
/**
* 获取 Entity 实例的集合。
* @memberof WebGLGlobeDataSource.prototype
* @type {EntityCollection}
*/
entities: {
get: function () {
return this._entityCollection;
},
},
/**
* 获取一个值,指示数据源当前是否正在加载数据。
* @memberof WebGLGlobeDataSource.prototype
* @type {Boolean}
*/
isLoading: {
get: function () {
return this._isLoading;
},
},
/**
* 获取一个事件,当底层数据发生变化时会触发该事件。
* @memberof WebGLGlobeDataSource.prototype
* @type {Event}
*/
changedEvent: {
get: function () {
return this._changed;
},
},
/**
* 获取一个事件,该事件将在处理过程中遇到错误时触发。
* @memberof WebGLGlobeDataSource.prototype
* @type {Event}
*/
errorEvent: {
get: function () {
return this._error;
},
},
/**
* 获取一个事件,该事件将在数据源开始或停止加载时触发。
* @memberof WebGLGlobeDataSource.prototype
* @type {Event}
*/
loadingEvent: {
get: function () {
return this._loading;
},
},

//这些属性仅适用于此数据源。

/**
* 获取系列名称数组。
* @memberof WebGLGlobeDataSource.prototype
* @type {string[]}
*/
seriesNames: {
get: function () {
return this._seriesNames;
},
},
/**
* 获取或设置要显示的系列名称。WebGL JSON 设计为一次仅查看一个系列。有效值在 seriesNames 属性中定义。
* @memberof WebGLGlobeDataSource.prototype
* @type {String}
*/
seriesToDisplay: {
get: function () {
return this._seriesToDisplay;
},
set: function (value) {
this._seriesToDisplay = value;

//遍历所有实体,如果它们属于当前系列,则将它们的 show 属性设置为 true。
const collection = this._entityCollection;
const entities = collection.values;
collection.suspendEvents();
for (let i = 0; i < entities.length; i++) {
const entity = entities[i];
entity.show = value === entity.seriesName;
}
collection.resumeEvents();
},
},
/**
* 获取或设置应用于每条线的高度的比例因子。
* @memberof WebGLGlobeDataSource.prototype
* @type {Number}
*/
heightScale: {
get: function () {
return this._heightScale;
},
set: function (value) {
if (value <= 0) {
throw new Cesium.DeveloperError("值必须大于 0");
}
this._heightScale = value;
},
},
/**
* 获取是否应显示此数据源。
* @memberof WebGLGlobeDataSource.prototype
* @type {Boolean}
*/
show: {
get: function () {
return this._entityCollection;
},
set: function (value) {
this._entityCollection = value;
},
},
/**
* 获取或设置此数据源的聚类选项。该对象可以在多个数据源之间共享。
* @memberof WebGLGlobeDataSource.prototype
* @type {EntityCluster}
*/
clustering: {
get: function () {
return this._entityCluster;
},
set: function (value) {
if (!Cesium.defined(value)) {
throw new Cesium.DeveloperError("value must be defined.");
}
this._entityCluster = value;
},
},
});

/**
* 在提供的 URL 处异步加载 GeoJSON,替换任何现有数据。
* @param {object} url 要处理的 URL。
* @returns {Promise} 一个 Promise,该 Promise 将在加载 GeoJSON 时解析。
*/
WebGLGlobeDataSource.prototype.loadUrl = function (url) {
if (!Cesium.defined(url)) {
throw new Cesium.DeveloperError("url is required.");
}

//根据 URL 创建名称
const name = Cesium.getFilenameFromUri(url);

//如果名称与当前名称不同,请设置名称。
if (this._name !== name) {
this._name = name;
this._changed.raiseEvent(this);
}

//将 URL 加载到 json 对象中,然后使用`load`函数处理。
const that = this;
return Cesium.Resource.fetchJson(url)
.then(function (json) {
return that.load(json, url);
})
.catch(function (error) {
/**
* 否则将在处理 Promise 期间捕获任何错误或异常。
* 当这种情况发生时,我们将触发 error 事件并拒绝 Promise。
*/
this._setLoading(false);
that._error.raiseEvent(that, error);
return Promise.reject(error);
});
};

/**
* 加载提供的数据,替换任何现有数据。
* @param {Array} data 要处理的对象。
*/
WebGLGlobeDataSource.prototype.load = function (data) {
//>>includeStart('debug', pragmas.debug);
if (!Cesium.defined(data)) {
throw new Cesium.DeveloperError("data is required.");
}
//>>includeEnd('debug');

//清除可能已存在的任何数据。
this._setLoading(true);
this._seriesNames.length = 0;
this._seriesToDisplay = undefined;

const heightScale = this.heightScale;
const entities = this._entityCollection;

/**
* 在对大量实体进行更改时,需要暂停事件处理。
* 这会将事件批量处理为最少数量的函数调用,
* 并在处理结束时统一触发(即调用 resumeEvents 时)。
*/
entities.suspendEvents();
entities.removeAll();

/**
* WebGL Globe JSON 是一个系列数组,其中每个系列本身是一个包含两个元素的数组,
* 第一个元素包含系列名称,第二个元素是一个重复的纬度、经度、高度值数组。
*
* 这里有一个更直观的例子。
* [["series1",[latitude, longitude, height, ... ]
* ["series2",[latitude, longitude, height, ... ]]
*/
// 遍历每个系列(series)
for (let x = 0; x < data.length; x++) {
const series = data[x];
const seriesName = series[0];
const coordinates = series[1];

//将系列名称添加到可能的值列表中。
this._seriesNames.push(seriesName);

// 默认将第一个系列设为可见系列
const show = x === 0;
if (show) {
this._seriesToDisplay = seriesName;
}

//现在遍历系列中的每个坐标,并从数据中创建实体。
for (let i = 0; i < coordinates.length; i += 3) {
const latitude = coordinates[i];
const longitude = coordinates[i + 1];
const height = coordinates[i + 2];

//忽略高度为零的线。
if (height === 0) {
continue;
}

const color = Cesium.Color.fromHsl(0.6 - height * 0.5, 1.0, 0.5);
const surfacePosition = Cesium.Cartesian3.fromDegrees(
longitude,
latitude,
0,
);
const heightPosition = Cesium.Cartesian3.fromDegrees(
longitude,
latitude,
height * heightScale,
);

//WebGL Globe 仅包含线,因此这是我们创建的唯一图形。
const polyline = new Cesium.PolylineGraphics();
polyline.material = new Cesium.ColorMaterialProperty(color);
polyline.width = new Cesium.ConstantProperty(2);
polyline.arcType = new Cesium.ConstantProperty(Cesium.ArcType.NONE);
polyline.positions = new Cesium.ConstantProperty([
surfacePosition,
heightPosition,
]);

//多段线实例本身需要依附于某个实体。
const entity = new Cesium.Entity({
id: `${seriesName} index ${i.toString()}`,
show: show,
polyline: polyline,
seriesName: seriesName, //自定义属性,用于指示系列名称
});

//将实体添加到集合中。
entities.add(entity);
}
}

//所有数据处理完成后,调用 resumeEvents 方法并触发 changed 事件。
entities.resumeEvents();
this._changed.raiseEvent(this);
this._setLoading(false);
};

WebGLGlobeDataSource.prototype._setLoading = function (isLoading) {
if (this._isLoading !== isLoading) {
this._isLoading = isLoading;
this._loading.raiseEvent(this, isLoading);
}
};

//现在我们已经定义了自己的 DataSource,我们可以使用它来加载任何格式化为 WebGL Globe 的 JSON 数据。
const dataSource = new WebGLGlobeDataSource();
dataSource.loadUrl("../SampleData/population909500.json").then(function () {
//初始加载完成后,创建按钮让用户切换不同的系列。这里是切换不同年份的数据
function createSeriesSetter(seriesName) {
return function () {
dataSource.seriesToDisplay = seriesName;
};
}

for (let i = 0; i < dataSource.seriesNames.length; i++) {
const seriesName = dataSource.seriesNames[i];
Sandcastle.addToolbarButton(seriesName, createSeriesSetter(seriesName));
}
});

//创建一个Viewer实例对象,并添加自定义数据源(DataSource)对象。
const viewer = new Cesium.Viewer("cesiumContainer", {
geocoder: false,
sceneModePicker: false,
homeButton: false,
navigationHelpButton: false,
navigationInstructionsInitiallyVisible: false,
fullscreenButton: false,
selectionIndicator: false,
skyBox: false,
timeline: false,
animation: false,
shouldAnimate: true,
baseLayerPicker: false,
});
viewer.cesiumWidget.creditContainer.style.display = "none";
viewer.clock.shouldAnimate = false;
viewer.dataSources.add(dataSource);