groundVehicle - cesium中实现贴地车辆

1 实现要求

  • 1 地面车辆按照规定的起点和终点运行
  • 2 地面车辆必须贴地运动
  • 3 地面车辆的必须有俯仰角的变化
  • 4 循环播放,且车辆经过路径动态高亮,下一次循环清除高亮

2 实现效果

在这里插入图片描述

3 实现代码

将如下代码替换到cesium的一个例子中即可:https://sandcastle.cesium.com/index.html?src=Interpolation.html

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
	var viewer = new Cesium.Viewer("cesiumContainer", {
infoBox: false, //Disable InfoBox widget
selectionIndicator: false, //Disable selection indicator
shouldAnimate: true, // Enable animations
terrainProvider: Cesium.createWorldTerrain(),
});

//Enable lighting based on the sun position
viewer.scene.globe.enableLighting = true;

//Enable depth testing so things behind the terrain disappear.
viewer.scene.globe.depthTestAgainstTerrain = true;

//Set the random number seed for consistent results.
Cesium.Math.setRandomNumberSeed(3);

let times = [
Cesium.JulianDate.fromIso8601("2018-07-19T15:18:00Z"),
Cesium.JulianDate.fromIso8601("2018-07-19T15:24:00Z")
];
let stTime = times[0];
let endTime = times[1];
var start = stTime.clone();
var stop = endTime.clone();

//Make sure viewer is at the desired time.
viewer.clock.startTime = start.clone();
viewer.clock.stopTime = stop.clone();
viewer.clock.currentTime = start.clone();
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; //Loop at the end
viewer.clock.multiplier = 30;

//Set timeline to simulation bounds
viewer.timeline.zoomTo(start, stop);

//Generate a random circular pattern with varying heights.
function computeCirclularFlight(lon, lat, radius) {
var property = new Cesium.SampledPositionProperty();
for (var i = 0; i <= 360; i += 45) {
var radians = Cesium.Math.toRadians(i);
var time = Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
var position = Cesium.Cartesian3.fromDegrees(
lon + radius * 1.5 * Math.cos(radians),
lat + radius * Math.sin(radians),
Cesium.Math.nextRandomNumber() * 500 + 1750
);
property.addSample(time, position);
console.log(time.toString(), " -> ", position.toString());

//Also create a point for each sample we generate.
viewer.entities.add({
position: position,
point: {
pixelSize: 8,
color: Cesium.Color.TRANSPARENT,
outlineColor: Cesium.Color.YELLOW,
outlineWidth: 3,
},
});
}
return property;
}

let positions = [
// new Cartesian3(1216348.1632364073, -4736348.958775471, 4081284.5528982095),
// new Cartesian3(1216369.1229444197, -4736377.467107148, 4081240.888485707)
new Cesium.Cartesian3(
-2358138.847340281,
-3744072.459541374,
4581158.5714175375
),
new Cesium.Cartesian3(
-2357231.4925370603,
-3745103.7886602185,
4580702.9757762635
),
];
let stPos = positions[0];
let endPos = positions[1];

// sampled postion's time resolution
let timeOfResolution = 6;

// using sampled property to get sampled data
let oriSamples = new Cesium.SampledProperty(Cesium.Cartesian3);
oriSamples.addSamples(times, positions);

// get sampled data, ervery "distanceOfResolution" we take a sample
let geodesic = new Cesium.EllipsoidGeodesic(
Cesium.Cartographic.fromCartesian(stPos),
Cesium.Cartographic.fromCartesian(endPos)
);
let lenInMeters = Math.ceil(geodesic.surfaceDistance); // avoid overflow when take samples
let samplesNum = Math.floor(
Cesium.JulianDate.secondsDifference(endTime, stTime) / timeOfResolution
);
//let secondsInterval = Math.floor(Cesium.JulianDate.secondsDifference(endTime, stTime) / samplesNum);
console.log(
"len: ",
lenInMeters,
"samplesNum",
samplesNum,
"secondsInterval",
timeOfResolution
);

// get sampled data, ervery "timeOfResolution" passed, we take a sample
let sampledPositions = [];
let sampledTimes = [];
for (let i = 0; i < samplesNum + 1; i++) {
let sampleTime = Cesium.JulianDate.addSeconds(
stTime,
i * timeOfResolution,
new Cesium.JulianDate()
);
let tmpPos = oriSamples.getValue(sampleTime);
console.log(sampleTime.toString(), " -> || -> ", tmpPos.toString());
sampledPositions.push(Cesium.Cartographic.fromCartesian(tmpPos));
sampledTimes.push(sampleTime);
}

let promise = Cesium.sampleTerrainMostDetailed(
viewer.terrainProvider,
sampledPositions
).then(() => {
console.log(
"start adding!",
"time and pos size: ",
sampledTimes.length,
sampledPositions.length
);

let carPositionProperty = new Cesium.SampledPositionProperty();

// add positions which are clamped to ground to the carPositionProperty
for (let i = 0; i < samplesNum + 1; i++) {
carPositionProperty.addSample(
sampledTimes[i],
// new Cesium.Cartesian3.fromDegrees( // this way of changing pos is not right, all should be under WGS84
// sampledPositions[i].longitude,
// sampledPositions[i].latitude,
// sampledPositions[i].height));
Cesium.Ellipsoid.WGS84.cartographicToCartesian(sampledPositions[i])
);
// console.log(sampledTimes[i], " ------->>> ", sampledPositions[i]);
}

// after the clamped to ground data computed, dynamically show the path
let isConstant = false;
let curSegmentNo = 0; // the polyLine are divided into samplesNum's segements
let lastSegementNo = -1;
let p2 = Cesium.Ellipsoid.WGS84.cartographicToCartesian(sampledPositions[1]);
let curPolyline = [stPos]; // represent the polyline
console.log("init 2 points are: ", stPos.toString(), " ", p2.toString());
//let timeNow = Cesium.JulianDate.now().clone();

console.log("starting add entity");
viewer.entities.add({
polyline: {
// This callback updates positions each frame.
// Ellipsoid.WGS84.cartographicArrayToCartesianArray(sampledPositions),
positions: new Cesium.CallbackProperty(function (time, result) {
//console.log("len: ", lenInMeters, "samplesNum", samplesNum, "timeOfResolution", timeOfResolution);

//let st
curSegmentNo = Math.floor(
Cesium.JulianDate.secondsDifference(time, stTime) / timeOfResolution
);
console.log(curSegmentNo);
if (curSegmentNo !== lastSegementNo) {
//console.log("curSegmentNo is: ", curSegmentNo.toString(), "\ncurTime: ", time.toString(), "\nstTime: ", stTime.toString());
// tmmP => curPolyine[lastSegementNo+1 : CurSegmentNo]
let tmpP = Cesium.Ellipsoid.WGS84.cartographicToCartesian(
sampledPositions[curSegmentNo]
);
//console.log("adding new points: ", tmpP.toString(), "\nsize is:", curPolyline.length);
curPolyline.push(tmpP);
lastSegementNo = curSegmentNo;
}
// if reach the end of sampled positions, clear the polyline's positions
if (curSegmentNo === samplesNum - 1) {
curSegmentNo = 0;
curPolyline = [];
console.log("cleared!");
}

return curPolyline;
}, isConstant),
//clampToGround: true,
width: 5,
material: Cesium.Color.RED,
availability: new Cesium.TimeIntervalCollection([
new Cesium.TimeInterval({
start: stTime,
stop: endTime,
}),
]),
},
});
console.log("end adding polyline");

//Compute the entity position property.
//var position = computeCirclularFlight(-112.110693, 36.0994841, 0.03);
var position = carPositionProperty;
//Actually create the entity
var entity = viewer.entities.add({
//Set the entity availability to the same interval as the simulation time.
availability: new Cesium.TimeIntervalCollection([
new Cesium.TimeInterval({
start: start,
stop: stop,
}),
]),

//Use our computed positions
position: position,

//Automatically compute orientation based on position movement.
orientation: new Cesium.VelocityOrientationProperty(position),

//Load the Cesium plane model to represent the entity
model: {
uri: "../SampleData/models/CesiumAir/Cesium_Air.glb",
minimumPixelSize: 64,
},

//Show the path as a pink line sampled in 1 second increments.
path: {
resolution: 1,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.1,
color: Cesium.Color.YELLOW,
}),
width: 1,
},
});

//Add button to view the path from the top down
Sandcastle.addDefaultToolbarButton("View Top Down", function () {
viewer.trackedEntity = undefined;
viewer.zoomTo(
viewer.entities,
new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-90))
);
});

//Add button to view the path from the side
Sandcastle.addToolbarButton("View Side", function () {
viewer.trackedEntity = undefined;
viewer.zoomTo(
viewer.entities,
new Cesium.HeadingPitchRange(
Cesium.Math.toRadians(-90),
Cesium.Math.toRadians(-15),
7500
)
);
});

//Add button to track the entity as it moves
Sandcastle.addToolbarButton("View Aircraft", function () {
// viewer.trackedEntity = entity;
viewer.zoomTo(
viewer.entities,
new Cesium.HeadingPitchRange(
Cesium.Math.toRadians(15),
Cesium.Math.toRadians(-45),
2500
)
);
});

//Add a combo box for selecting each interpolation mode.
Sandcastle.addToolbarMenu(
[
{
text: "Interpolation: Linear Approximation",
onselect: function () {
entity.position.setInterpolationOptions({
interpolationDegree: 1,
interpolationAlgorithm: Cesium.LinearApproximation,
});
},
},
{
text: "Interpolation: Lagrange Polynomial Approximation",
onselect: function () {
entity.position.setInterpolationOptions({
interpolationDegree: 5,
interpolationAlgorithm: Cesium.LagrangePolynomialApproximation,
});
},
},
{
text: "Interpolation: Hermite Polynomial Approximation",
onselect: function () {
entity.position.setInterpolationOptions({
interpolationDegree: 2,
interpolationAlgorithm: Cesium.HermitePolynomialApproximation,
});
},
},
],
"interpolationMenu"
);
});

4 参考