huweili-cesium 1.2.70 → 1.2.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/js/geometry.js +270 -0
- package/js/movePath.js +14 -6
- package/js/movePoint.js +102 -30
- package/package.json +1 -1
package/js/geometry.js
CHANGED
|
@@ -15,6 +15,272 @@ export function geometryConfig() {
|
|
|
15
15
|
// 获取地图store实例
|
|
16
16
|
const mapStore = useMapStore()
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* 创建锥形波效果
|
|
20
|
+
* @param {Object} options - 锥形波配置选项
|
|
21
|
+
* @param {string} options.id - 效果唯一标识符
|
|
22
|
+
* @param {number[]} options.positions - 位置数组 [lng, lat, height]
|
|
23
|
+
* @param {number} options.heading - 指向方向(度)
|
|
24
|
+
* @param {number} options.pitch - 俯仰角度(度)
|
|
25
|
+
* @param {number} options.length - 圆锥高
|
|
26
|
+
* @param {number} options.bottomRadius - 底部半径
|
|
27
|
+
* @param {number} options.thickness - 厚度
|
|
28
|
+
* @param {string} options.color - 颜色(默认 '#00FFFF')
|
|
29
|
+
* @param {string} options.mapId - 地图实例ID
|
|
30
|
+
* @returns {Cesium.Primitive|null} 创建的锥形波实体,若创建失败则返回null
|
|
31
|
+
*/
|
|
32
|
+
const conicalWave = (options) => {
|
|
33
|
+
const map = mapStore.getMap(options.mapId)
|
|
34
|
+
if (!map) {
|
|
35
|
+
console.error('地图实例不存在')
|
|
36
|
+
return null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (mapStore.getGraphicMap(options.id, options.mapId)) {
|
|
40
|
+
console.warn(`id: ${options.id} 效果已存在`)
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const [lng, lat, height = 0] = options.positions
|
|
45
|
+
const vertexPosition = Cesium.Cartesian3.fromDegrees(lng, lat, height)
|
|
46
|
+
const heading = Cesium.Math.toRadians(options.heading || 0)
|
|
47
|
+
const pitch = Cesium.Math.toRadians(options.pitch || 0)
|
|
48
|
+
const hpr = new Cesium.HeadingPitchRoll(heading, pitch, 0)
|
|
49
|
+
const modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(vertexPosition, hpr)
|
|
50
|
+
const translationMatrix = Cesium.Matrix4.fromTranslation(new Cesium.Cartesian3(0, 0, -(options.length || 0) / 2))
|
|
51
|
+
Cesium.Matrix4.multiply(modelMatrix, translationMatrix, modelMatrix)
|
|
52
|
+
|
|
53
|
+
const primitive = new Cesium.Primitive({
|
|
54
|
+
geometryInstances: new Cesium.GeometryInstance({
|
|
55
|
+
geometry: new Cesium.CylinderGeometry({
|
|
56
|
+
length: options.length,
|
|
57
|
+
topRadius: 0,
|
|
58
|
+
bottomRadius: options.bottomRadius
|
|
59
|
+
})
|
|
60
|
+
}),
|
|
61
|
+
appearance: new Cesium.MaterialAppearance({
|
|
62
|
+
material: new Cesium.Material({
|
|
63
|
+
fabric: {
|
|
64
|
+
uniforms: {
|
|
65
|
+
color: Cesium.Color.fromCssColorString(options.color || '#00FFFF').withAlpha(0.7),
|
|
66
|
+
duration: 6000,
|
|
67
|
+
repeat: 30,
|
|
68
|
+
offset: 0,
|
|
69
|
+
thickness: options.thickness || 0.3
|
|
70
|
+
},
|
|
71
|
+
source: `
|
|
72
|
+
uniform vec4 color;
|
|
73
|
+
uniform float duration;
|
|
74
|
+
uniform float repeat;
|
|
75
|
+
uniform float offset;
|
|
76
|
+
uniform float thickness;
|
|
77
|
+
|
|
78
|
+
czm_material czm_getMaterial(czm_materialInput materialInput) {
|
|
79
|
+
czm_material material = czm_getDefaultMaterial(materialInput);
|
|
80
|
+
float sp = 1.0/repeat;
|
|
81
|
+
vec2 st = materialInput.st;
|
|
82
|
+
float dis = distance(st, vec2(0.5));
|
|
83
|
+
float time = mod((czm_frameNumber / 60.0) / (duration / 1000.0), 1.0);
|
|
84
|
+
float m = mod(dis + offset - time, sp);
|
|
85
|
+
float a = step(sp*(1.0-thickness), m);
|
|
86
|
+
material.diffuse = color.rgb;
|
|
87
|
+
material.alpha = a * color.a;
|
|
88
|
+
return material;
|
|
89
|
+
}
|
|
90
|
+
`
|
|
91
|
+
},
|
|
92
|
+
translucent: true
|
|
93
|
+
}),
|
|
94
|
+
translucent: true
|
|
95
|
+
}),
|
|
96
|
+
modelMatrix,
|
|
97
|
+
asynchronous: false
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
primitive._originalOptions = { ...options }
|
|
101
|
+
mapStore.setGraphicMap(options.id, primitive, options.mapId)
|
|
102
|
+
map.scene.primitives.add(primitive)
|
|
103
|
+
return primitive
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 更新圆锥体高度 / 位置
|
|
108
|
+
* @param options
|
|
109
|
+
* id 圆锥体唯一标识
|
|
110
|
+
* length 新的高度(米)(可选)
|
|
111
|
+
* positions 新的 [lng, lat, height](可选)
|
|
112
|
+
* mapId 地图实例ID
|
|
113
|
+
* @returns boolean 成功 true / 失败 false
|
|
114
|
+
*/
|
|
115
|
+
const updateConeLengthOrPosition = (options) => {
|
|
116
|
+
const { id, length, positions, mapId } = options
|
|
117
|
+
const map = mapStore.getMap(mapId)
|
|
118
|
+
if (!map) {
|
|
119
|
+
console.error('地图实例不存在')
|
|
120
|
+
return false
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (length === undefined && positions === undefined) {
|
|
124
|
+
console.error('更新圆锥体高度 / 位置:必须提供高度或位置')
|
|
125
|
+
return false
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const oldPrimitive = mapStore.getGraphicMap(id, mapId)
|
|
129
|
+
if (!oldPrimitive) {
|
|
130
|
+
console.error(`id: ${id} 圆锥体不存在`)
|
|
131
|
+
return false
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
const opts = { ...oldPrimitive._originalOptions }
|
|
136
|
+
if (length !== undefined) opts.length = length
|
|
137
|
+
if (positions !== undefined) opts.positions = positions
|
|
138
|
+
|
|
139
|
+
const [lng, lat, height = 0] = opts.positions
|
|
140
|
+
const vertexPos = Cesium.Cartesian3.fromDegrees(lng, lat, height)
|
|
141
|
+
const hpr = new Cesium.HeadingPitchRoll(
|
|
142
|
+
Cesium.Math.toRadians(opts.heading || 0),
|
|
143
|
+
Cesium.Math.toRadians(opts.pitch || 0),
|
|
144
|
+
0
|
|
145
|
+
)
|
|
146
|
+
const modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(vertexPos, hpr)
|
|
147
|
+
const trans = Cesium.Matrix4.fromTranslation(new Cesium.Cartesian3(0, 0, -(opts.length || 0) / 2))
|
|
148
|
+
Cesium.Matrix4.multiply(modelMatrix, trans, modelMatrix)
|
|
149
|
+
|
|
150
|
+
const newPrimitive = new Cesium.Primitive({
|
|
151
|
+
geometryInstances: new Cesium.GeometryInstance({
|
|
152
|
+
geometry: new Cesium.CylinderGeometry({
|
|
153
|
+
length: opts.length,
|
|
154
|
+
topRadius: 0,
|
|
155
|
+
bottomRadius: opts.bottomRadius
|
|
156
|
+
})
|
|
157
|
+
}),
|
|
158
|
+
appearance: oldPrimitive.appearance,
|
|
159
|
+
modelMatrix,
|
|
160
|
+
asynchronous: false
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
newPrimitive._originalOptions = opts
|
|
164
|
+
map.scene.primitives.remove(oldPrimitive)
|
|
165
|
+
map.scene.primitives.add(newPrimitive)
|
|
166
|
+
mapStore.setGraphicMap(id, newPrimitive, mapId)
|
|
167
|
+
return true
|
|
168
|
+
} catch (e) {
|
|
169
|
+
console.error('更新圆锥体失败:', e)
|
|
170
|
+
return false
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 更新圆锥体姿态
|
|
176
|
+
* @param {Object} options 更新配置参数
|
|
177
|
+
* @param {string} options.id - 圆锥体ID
|
|
178
|
+
* @param {number} options.heading - 新的指向方向(度)
|
|
179
|
+
* @param {number} options.pitch - 新的俯仰角度(度)
|
|
180
|
+
* @param {string} options.mapId - 地图实例ID
|
|
181
|
+
* @returns {boolean} 更新成功返回true,否则返回false
|
|
182
|
+
*/
|
|
183
|
+
const updateConePose = (options) => {
|
|
184
|
+
const { id, heading = 0, pitch = 0, mapId } = options
|
|
185
|
+
const map = mapStore.getMap(mapId)
|
|
186
|
+
if (!map) {
|
|
187
|
+
console.error('地图实例不存在')
|
|
188
|
+
return false
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const primitive = mapStore.getGraphicMap(id, mapId)
|
|
192
|
+
if (!primitive) {
|
|
193
|
+
console.error(`id: ${id} 圆锥体不存在`)
|
|
194
|
+
return false
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const opts = primitive._originalOptions || {}
|
|
199
|
+
const [lng, lat, height = 0] = opts.positions || [0, 0, 0]
|
|
200
|
+
const vertexPos = Cesium.Cartesian3.fromDegrees(lng, lat, height)
|
|
201
|
+
const hpr = new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(heading), Cesium.Math.toRadians(pitch), 0)
|
|
202
|
+
const modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(vertexPos, hpr)
|
|
203
|
+
const trans = Cesium.Matrix4.fromTranslation(new Cesium.Cartesian3(0, 0, -(opts.length || 0) / 2))
|
|
204
|
+
Cesium.Matrix4.multiply(modelMatrix, trans, modelMatrix)
|
|
205
|
+
|
|
206
|
+
primitive.modelMatrix = modelMatrix
|
|
207
|
+
opts.heading = heading
|
|
208
|
+
opts.pitch = pitch
|
|
209
|
+
mapStore.setGraphicMap(id, primitive, mapId)
|
|
210
|
+
return true
|
|
211
|
+
} catch (error) {
|
|
212
|
+
console.error(`更新圆锥体姿态失败:`, error)
|
|
213
|
+
return false
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 让圆锥顶点固定,朝向指定经纬度目标旋转
|
|
219
|
+
* @param {Object} options 配置参数
|
|
220
|
+
* @param {string} options.id 圆锥体ID
|
|
221
|
+
* @param {number[]} options.positions 固定顶点位置 [lng, lat, height]
|
|
222
|
+
* @param {number} options.targetLng 目标经度
|
|
223
|
+
* @param {number} options.targetLat 目标纬度
|
|
224
|
+
* @param {number} [options.targetHeight=0] 目标高度
|
|
225
|
+
* @param {string} options.mapId 地图实例ID
|
|
226
|
+
* @returns {boolean} 更新成功返回true,否则返回false
|
|
227
|
+
*/
|
|
228
|
+
const updateConeLookAtTarget = (options) => {
|
|
229
|
+
const { id, positions, targetLng, targetLat, targetHeight = 0, mapId } = options
|
|
230
|
+
const map = mapStore.getMap(mapId)
|
|
231
|
+
if (!map) {
|
|
232
|
+
console.error('地图实例不存在')
|
|
233
|
+
return false
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const primitive = mapStore.getGraphicMap(id, mapId)
|
|
237
|
+
if (!primitive) {
|
|
238
|
+
console.error(`id: ${id} 圆锥体不存在`)
|
|
239
|
+
return false
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (!positions || positions.length < 2) {
|
|
243
|
+
console.error('圆锥顶点位置 positions 无效')
|
|
244
|
+
return false
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const [lng, lat, height = 0] = positions
|
|
248
|
+
const vertexPos = Cesium.Cartesian3.fromDegrees(lng, lat, height)
|
|
249
|
+
const targetPos = Cesium.Cartesian3.fromDegrees(Number(targetLng), Number(targetLat), Number(targetHeight))
|
|
250
|
+
|
|
251
|
+
if (!Number.isFinite(targetPos.x) || !Number.isFinite(targetPos.y) || !Number.isFinite(targetPos.z)) {
|
|
252
|
+
console.error('目标经纬度无效')
|
|
253
|
+
return false
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
const transform = Cesium.Transforms.eastNorthUpToFixedFrame(vertexPos)
|
|
258
|
+
const inverseTransform = Cesium.Matrix4.inverseTransformation(transform, new Cesium.Matrix4())
|
|
259
|
+
const localTarget = Cesium.Matrix4.multiplyByPoint(inverseTransform, targetPos, new Cesium.Cartesian3())
|
|
260
|
+
|
|
261
|
+
const heading = Math.atan2(localTarget.x, localTarget.y)
|
|
262
|
+
const horizontalDistance = Math.sqrt(localTarget.x * localTarget.x + localTarget.y * localTarget.y)
|
|
263
|
+
const pitch = Math.atan2(localTarget.z, horizontalDistance)
|
|
264
|
+
const hpr = new Cesium.HeadingPitchRoll(heading, pitch, 0)
|
|
265
|
+
|
|
266
|
+
const modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(vertexPos, hpr)
|
|
267
|
+
const trans = Cesium.Matrix4.fromTranslation(new Cesium.Cartesian3(0, 0, -(primitive._originalOptions?.length || 0) / 2))
|
|
268
|
+
Cesium.Matrix4.multiply(modelMatrix, trans, modelMatrix)
|
|
269
|
+
|
|
270
|
+
primitive.modelMatrix = modelMatrix
|
|
271
|
+
primitive._originalOptions = primitive._originalOptions || {}
|
|
272
|
+
primitive._originalOptions.positions = positions
|
|
273
|
+
primitive._originalOptions.targetLng = targetLng
|
|
274
|
+
primitive._originalOptions.targetLat = targetLat
|
|
275
|
+
primitive._originalOptions.targetHeight = targetHeight
|
|
276
|
+
mapStore.setGraphicMap(id, primitive, mapId)
|
|
277
|
+
return true
|
|
278
|
+
} catch (error) {
|
|
279
|
+
console.error('更新圆锥朝向目标失败:', error)
|
|
280
|
+
return false
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
18
284
|
/**
|
|
19
285
|
* 创建四棱锥波效果
|
|
20
286
|
* @param {string} options.id - 效果唯一标识符
|
|
@@ -293,6 +559,10 @@ export function geometryConfig() {
|
|
|
293
559
|
};
|
|
294
560
|
|
|
295
561
|
return {
|
|
562
|
+
conicalWave,
|
|
563
|
+
updateConeLengthOrPosition,
|
|
564
|
+
updateConePose,
|
|
565
|
+
updateConeLookAtTarget,
|
|
296
566
|
rectangularPyramidWave,
|
|
297
567
|
updateRectangularPyramidWavePose,
|
|
298
568
|
updateRectangularPyramidPoseFast,
|
package/js/movePath.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import * as Cesium from 'cesium'
|
|
12
12
|
import { useMapStore } from './stores/mapStore.js'
|
|
13
|
+
import { DroneConfig } from './config/index.js'
|
|
13
14
|
|
|
14
15
|
const scratchCutoffTime = new Cesium.JulianDate()
|
|
15
16
|
|
|
@@ -47,18 +48,25 @@ export function movePathConfig() {
|
|
|
47
48
|
|
|
48
49
|
const positions = trailData.positions
|
|
49
50
|
const renderPositions = trailData.renderPositions || (trailData.renderPositions = positions.map(point => point.position).filter(Boolean))
|
|
51
|
+
const lastPoint = positions[positions.length - 1]
|
|
52
|
+
const lastPosition = lastPoint?.position
|
|
53
|
+
const minTrailDistanceSquared = trailData.minTrailDistanceSquared || (trailData.minTrailDistanceSquared = DroneConfig.minTrailDistance * DroneConfig.minTrailDistance)
|
|
50
54
|
|
|
51
|
-
//
|
|
55
|
+
// 降低轨迹追加频率:距离小于5米且距离上次追加不足300ms时,不追加新轨迹点。
|
|
56
|
+
if (lastPoint && lastPosition) {
|
|
57
|
+
const distanceSquared = Cesium.Cartesian3.distanceSquared(lastPosition, newPosition)
|
|
58
|
+
const elapsedSeconds = Cesium.JulianDate.secondsDifference(currentTime, lastPoint.timestamp)
|
|
59
|
+
if (distanceSquared < minTrailDistanceSquared || elapsedSeconds < DroneConfig.minTrailIntervalSeconds) {
|
|
60
|
+
return trailData
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 添加新的轨迹点,包含位置和时间戳
|
|
52
65
|
const clonedPosition = Cesium.Cartesian3.clone(newPosition)
|
|
53
66
|
positions.push({ position: clonedPosition, timestamp: Cesium.JulianDate.clone(currentTime) })
|
|
54
67
|
renderPositions.push(clonedPosition)
|
|
55
68
|
trailData.lastUpdateTime = currentTime
|
|
56
69
|
|
|
57
|
-
// 立刻通知 Cesium 重新取轨迹数组,避免 CallbackProperty 在下一帧才刷新。
|
|
58
|
-
if (trailData.entity?.polyline) {
|
|
59
|
-
trailData.entity.polyline.positions = renderPositions
|
|
60
|
-
}
|
|
61
|
-
|
|
62
70
|
// 清理旧轨迹点,只保留最近指定时间的轨迹
|
|
63
71
|
const trailTime = mapStore.getTrailTime(mapId)
|
|
64
72
|
if (trailTime === -1 || positions.length <= 1) return trailData
|
package/js/movePoint.js
CHANGED
|
@@ -240,6 +240,12 @@ export function movePointConfig(baseUrl) {
|
|
|
240
240
|
return modelEntity
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
// 初始化复用对象,避免上百个无人机高频移动时反复创建临时对象
|
|
244
|
+
modelEntity.scratchPosition ||= new Cesium.Cartesian3()
|
|
245
|
+
modelEntity.scratchTargetPosition ||= new Cesium.Cartesian3()
|
|
246
|
+
modelEntity.scratchFlightEndTime ||= new Cesium.JulianDate()
|
|
247
|
+
modelEntity.scratchFarFutureTime ||= new Cesium.JulianDate()
|
|
248
|
+
|
|
243
249
|
// 如果存在实体,确保实体属性实时同步
|
|
244
250
|
const info = modelEntity.info || (modelEntity.info = {})
|
|
245
251
|
info.pointId = pointId
|
|
@@ -254,46 +260,112 @@ export function movePointConfig(baseUrl) {
|
|
|
254
260
|
info.distance = options.distance || ''
|
|
255
261
|
info.receiveTime = options.receiveTime || ''
|
|
256
262
|
|
|
257
|
-
//
|
|
258
|
-
|
|
259
|
-
|
|
263
|
+
// ========== 实时位置获取开始 ==========
|
|
264
|
+
const positionProperty = modelEntity.positionProperty || modelEntity.entity?.position
|
|
265
|
+
let currentRealPosition = positionProperty?.getValue?.(currentTime, modelEntity.scratchPosition)
|
|
266
|
+
|
|
267
|
+
if (!currentRealPosition && modelEntity.currentPosition) {
|
|
268
|
+
// 插值属性取不到值时,回退到上一次缓存的真实位置。
|
|
269
|
+
currentRealPosition = Cesium.Cartesian3.clone(modelEntity.currentPosition, modelEntity.scratchPosition)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (!currentRealPosition) {
|
|
273
|
+
// 仍没有当前位置时,使用本次传入坐标作为起点。
|
|
274
|
+
currentRealPosition = Cesium.Cartesian3.fromDegrees(lng, lat, height, modelEntity.scratchPosition)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// getValue 复用 scratchPosition,后续 targetPosition 也会复用其它 scratch,先把当前位置固定到独立对象,避免被后续计算覆盖。
|
|
278
|
+
currentRealPosition = Cesium.Cartesian3.clone(currentRealPosition, modelEntity.currentPosition || new Cesium.Cartesian3())
|
|
279
|
+
modelEntity.currentPosition = currentRealPosition
|
|
280
|
+
// ========== 实时位置获取结束 ==========
|
|
281
|
+
|
|
282
|
+
// ========== 保留轨迹 ==========
|
|
283
|
+
// 在飞行开始前,先记录当前点,保证轨迹不会从新目标点突兀开始。
|
|
284
|
+
moveDroneTrail({
|
|
285
|
+
pointId,
|
|
286
|
+
newPosition: currentRealPosition,
|
|
287
|
+
mapId
|
|
288
|
+
})
|
|
260
289
|
|
|
261
|
-
//
|
|
290
|
+
// ========== 终止当前所有飞行状态 ==========
|
|
291
|
+
// 终止旧飞行监听;飞行插值和轨迹更新都在同一个 onTick 中处理。
|
|
262
292
|
if (modelEntity.flightEndListener) {
|
|
263
293
|
map.clock.onTick.removeEventListener(modelEntity.flightEndListener)
|
|
264
294
|
modelEntity.flightEndListener = null
|
|
265
295
|
}
|
|
266
296
|
|
|
267
297
|
modelEntity.isFlying = false
|
|
268
|
-
|
|
269
|
-
modelEntity.positionProperty =
|
|
270
|
-
|
|
298
|
+
// 每次移动都重建采样属性,只保留“当前位置 -> 新目标点”的最新飞行段。
|
|
299
|
+
modelEntity.positionProperty = new Cesium.SampledPositionProperty()
|
|
300
|
+
modelEntity.positionProperty.forwardExtrapolationType = Cesium.ExtrapolationType.HOLD
|
|
301
|
+
modelEntity.positionProperty.backwardExtrapolationType = Cesium.ExtrapolationType.HOLD
|
|
302
|
+
modelEntity.positionProperty.forwardExtrapolationDuration = Number.POSITIVE_INFINITY
|
|
303
|
+
modelEntity.positionProperty.backwardExtrapolationDuration = Number.POSITIVE_INFINITY
|
|
304
|
+
modelEntity.positionProperty.addSample(currentTime, currentRealPosition)
|
|
305
|
+
modelEntity.entity.position = modelEntity.positionProperty
|
|
306
|
+
|
|
307
|
+
// ========== 计算新的飞行路径 ==========
|
|
308
|
+
const targetPosition = Cesium.Cartesian3.fromDegrees(lng, lat, height)
|
|
309
|
+
const distanceSquared = Cesium.Cartesian3.distanceSquared(currentRealPosition, targetPosition)
|
|
310
|
+
|
|
311
|
+
// 距离过近则直接定位
|
|
312
|
+
if (distanceSquared < MIN_DRONE_MOVE_DISTANCE_SQUARED) {
|
|
313
|
+
modelEntity.currentPosition = Cesium.Cartesian3.clone(targetPosition, modelEntity.currentPosition)
|
|
314
|
+
modelEntity.positionProperty.addSample(currentTime, modelEntity.currentPosition)
|
|
315
|
+
|
|
316
|
+
moveDroneTrail({
|
|
317
|
+
pointId,
|
|
318
|
+
newPosition: modelEntity.currentPosition,
|
|
319
|
+
mapId
|
|
320
|
+
})
|
|
321
|
+
return modelEntity
|
|
271
322
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
modelEntity.info.flyingHandPhone = options.flyingHandPhone || ''
|
|
280
|
-
modelEntity.info.flyingHandPosition = options.flyingHandPosition || ''
|
|
281
|
-
modelEntity.info.frequencyBand = options.frequencyBand || ''
|
|
282
|
-
modelEntity.info.distance = options.distance || ''
|
|
283
|
-
modelEntity.info.receiveTime = options.receiveTime || ''
|
|
284
|
-
|
|
285
|
-
// 直接使用当前点覆盖实体位置,不再依赖 SampledPositionProperty 做插值。
|
|
286
|
-
if (modelEntity.entity) {
|
|
287
|
-
modelEntity.entity.position = targetPosition
|
|
323
|
+
|
|
324
|
+
// 按距离和速度计算本段飞行时长,由 SampledPositionProperty 自动插值位置。
|
|
325
|
+
const flightDuration = Math.sqrt(distanceSquared) / speed
|
|
326
|
+
const flightEndTime = Cesium.JulianDate.addSeconds(currentTime, flightDuration, new Cesium.JulianDate())
|
|
327
|
+
|
|
328
|
+
if (!map.clock.shouldAnimate) {
|
|
329
|
+
map.clock.shouldAnimate = true
|
|
288
330
|
}
|
|
289
|
-
modelEntity.positionProperty = null
|
|
290
331
|
|
|
291
|
-
//
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
332
|
+
// 不再为每个无人机改全局 clock.startTime/stopTime,避免多无人机互相抢时钟范围。
|
|
333
|
+
modelEntity.positionProperty.addSample(flightEndTime, targetPosition)
|
|
334
|
+
modelEntity.isFlying = true
|
|
335
|
+
|
|
336
|
+
const flightTickListener = (clock) => {
|
|
337
|
+
if (!modelEntity.isFlying) return
|
|
338
|
+
|
|
339
|
+
const isFinished = Cesium.JulianDate.compare(clock.currentTime, flightEndTime) >= 0
|
|
340
|
+
const tickPosition = isFinished
|
|
341
|
+
? targetPosition
|
|
342
|
+
: modelEntity.positionProperty.getValue(clock.currentTime, modelEntity.scratchPosition)
|
|
343
|
+
|
|
344
|
+
if (tickPosition) {
|
|
345
|
+
// 每帧缓存插值后的真实位置,并同步追加轨迹点。
|
|
346
|
+
modelEntity.currentPosition = Cesium.Cartesian3.clone(tickPosition, modelEntity.currentPosition)
|
|
347
|
+
moveDroneTrail({
|
|
348
|
+
pointId,
|
|
349
|
+
newPosition: modelEntity.currentPosition,
|
|
350
|
+
mapId
|
|
351
|
+
})
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (!isFinished) return
|
|
355
|
+
|
|
356
|
+
modelEntity.currentPosition = Cesium.Cartesian3.clone(targetPosition, modelEntity.currentPosition)
|
|
357
|
+
modelEntity.isFlying = false
|
|
358
|
+
|
|
359
|
+
// 到达目标后追加远期采样点,让实体在终点保持不动。
|
|
360
|
+
const farFutureTime = Cesium.JulianDate.addSeconds(flightEndTime, DEFAULT_FAR_FUTURE_SECONDS, new Cesium.JulianDate())
|
|
361
|
+
modelEntity.positionProperty.addSample(farFutureTime, modelEntity.currentPosition)
|
|
362
|
+
|
|
363
|
+
clock.onTick.removeEventListener(flightTickListener)
|
|
364
|
+
modelEntity.flightEndListener = null
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
modelEntity.flightEndListener = flightTickListener
|
|
368
|
+
map.clock.onTick.addEventListener(flightTickListener)
|
|
297
369
|
|
|
298
370
|
return modelEntity
|
|
299
371
|
};
|