bruce-cesium 5.9.7 → 5.9.9

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.
@@ -1142,19 +1142,18 @@
1142
1142
  */
1143
1143
  class AnimatePositionSeries {
1144
1144
  constructor(params) {
1145
- // Animation state.
1146
- this.currentAnimatedPos = null;
1147
- this.actualTargetPos = null;
1148
- this.animationStartPos = null;
1149
- this.animationStartTime = null;
1150
- this.animationDuration = 350;
1151
- // Cached data for performance.
1145
+ this.currentPos = null;
1146
+ this.currentVelocity = new Cesium.Cartesian3(0, 0, 0);
1147
+ this.smoothingFactor = 0.5;
1148
+ this.positionHistory = [];
1149
+ this.maxHistorySize = 10;
1150
+ this.averageSpeed = 0;
1151
+ this.averageTimeInterval = 0;
1152
+ this.lastUpdateTime = null;
1152
1153
  this.lastDesiredPosIndex = -1;
1153
1154
  this.lastDesiredNextIndex = -1;
1154
- // Series data for rendering path
1155
1155
  this.lastCalcSeriesPos3d = [];
1156
1156
  this.lastCalcSeriesTime = null;
1157
- // Orientation cache.
1158
1157
  this.lastCalcOrient = null;
1159
1158
  this.lastCalcOrientTime = null;
1160
1159
  this.viewer = params.viewer;
@@ -1163,25 +1162,23 @@
1163
1162
  this.roll = params.roll || 0;
1164
1163
  this.processHeadings();
1165
1164
  this.sortPositions();
1166
- // Initialize animation from starting position if provided.
1167
- if (params.animateFromPos3d) {
1168
- this.animationStartPos = params.animateFromPos3d;
1169
- const currentTime = Date.now();
1170
- const providedTime = params.animateFromPos3dTimeStart || 0;
1171
- // Check if the provided timestamp is stale.
1172
- if (!providedTime || (currentTime - providedTime) >= this.animationDuration) {
1173
- this.animationStartTime = currentTime;
1174
- }
1175
- else {
1176
- this.animationStartTime = providedTime;
1177
- }
1178
- }
1179
1165
  }
1180
1166
  AddPosition(pos) {
1181
1167
  if (!pos || !pos.pos3d || !pos.dateTime) {
1182
1168
  console.warn("Invalid position provided to AnimatePositionSeries.");
1183
1169
  return;
1184
1170
  }
1171
+ const now = Date.now();
1172
+ const posTime = pos.dateTime.getTime();
1173
+ this.positionHistory.push({
1174
+ pos: pos.pos3d.clone(),
1175
+ time: posTime,
1176
+ realTime: now
1177
+ });
1178
+ if (this.positionHistory.length > this.maxHistorySize) {
1179
+ this.positionHistory.shift();
1180
+ }
1181
+ this.analyzeMovementPatterns();
1185
1182
  this.positions.push(pos);
1186
1183
  this.processHeadings();
1187
1184
  this.sortPositions();
@@ -1192,12 +1189,70 @@
1192
1189
  this.roll = roll;
1193
1190
  this.invalidateCache();
1194
1191
  }
1195
- GetAnimateFromDateTime() {
1196
- return this.animationStartTime;
1197
- }
1198
1192
  GetPositions() {
1199
1193
  return this.positions;
1200
1194
  }
1195
+ analyzeMovementPatterns() {
1196
+ if (this.positionHistory.length < 2) {
1197
+ return;
1198
+ }
1199
+ let totalDistance = 0;
1200
+ let totalTimeSpan = 0;
1201
+ let totalRealTimeSpan = 0;
1202
+ let validSegments = 0;
1203
+ for (let i = 1; i < this.positionHistory.length; i++) {
1204
+ const prev = this.positionHistory[i - 1];
1205
+ const curr = this.positionHistory[i];
1206
+ const distance = Cesium.Cartesian3.distance(prev.pos, curr.pos);
1207
+ const timeSpan = Math.abs(curr.time - prev.time) / 1000.0;
1208
+ const realTimeSpan = Math.abs(curr.realTime - prev.realTime) / 1000.0;
1209
+ if (distance > 0.1 && timeSpan > 0.01) {
1210
+ totalDistance += distance;
1211
+ totalTimeSpan += timeSpan;
1212
+ totalRealTimeSpan += realTimeSpan;
1213
+ validSegments++;
1214
+ }
1215
+ }
1216
+ if (validSegments > 0) {
1217
+ this.averageSpeed = totalDistance / totalTimeSpan;
1218
+ this.averageTimeInterval = totalRealTimeSpan / validSegments;
1219
+ this.averageSpeed = Math.max(0.1, Math.min(this.averageSpeed, 1000));
1220
+ this.averageTimeInterval = Math.max(0.01, Math.min(this.averageTimeInterval, 10));
1221
+ }
1222
+ }
1223
+ calculateExpectedPosition(viewerTimeMs) {
1224
+ const desired = this.calculateDesiredPosition(viewerTimeMs);
1225
+ if (!desired.position) {
1226
+ return { position: null, timeDelta: 0 };
1227
+ }
1228
+ let timeDelta = 0;
1229
+ if (this.positionHistory.length >= 2) {
1230
+ // Calculate how far behind we are based on viewer time vs real time.
1231
+ const now = Date.now();
1232
+ const timeDiffFromNow = (viewerTimeMs - now) / 1000.0;
1233
+ // Negative means we're behind, positive means ahead.
1234
+ timeDelta = timeDiffFromNow;
1235
+ }
1236
+ return { position: desired.position, timeDelta };
1237
+ }
1238
+ calculateAutonomousSpeed(distanceToTarget, timeDelta) {
1239
+ let baseSpeed = this.averageSpeed || 50;
1240
+ let speedMultiplier = 1.0;
1241
+ // Calculate speed multiplier based on distance and time lag.
1242
+ if (distanceToTarget > 50) {
1243
+ speedMultiplier *= Math.min(100.0, distanceToTarget / 10.0);
1244
+ }
1245
+ else if (distanceToTarget > 10) {
1246
+ const distanceMultiplier = Math.min(20.0, 1.0 + (distanceToTarget / 5.0));
1247
+ speedMultiplier *= distanceMultiplier;
1248
+ }
1249
+ else if (Math.abs(timeDelta) > 0.1) {
1250
+ const timeLagMultiplier = Math.max(0.1, 1.0 - (timeDelta * 5.0));
1251
+ speedMultiplier *= Math.min(50.0, timeLagMultiplier);
1252
+ }
1253
+ // Minimum 10% speed.
1254
+ return Math.max(baseSpeed * 0.1, baseSpeed * speedMultiplier);
1255
+ }
1201
1256
  sortPositions() {
1202
1257
  if (this.positions.length > 0) {
1203
1258
  this.positions.sort((a, b) => a.dateTime.getTime() - b.dateTime.getTime());
@@ -1207,10 +1262,6 @@
1207
1262
  this.lastCalcSeriesTime = null;
1208
1263
  this.lastCalcOrientTime = null;
1209
1264
  }
1210
- /**
1211
- * Pre-process headings in the series.
1212
- * If all are null or 0, then we assume all are null.
1213
- */
1214
1265
  processHeadings() {
1215
1266
  if (!this.positions || this.positions.length === 0) {
1216
1267
  return;
@@ -1232,15 +1283,10 @@
1232
1283
  easeInOutQuad(t) {
1233
1284
  return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
1234
1285
  }
1235
- /**
1236
- * Calculate the desired position based on current time without any animation smoothing.
1237
- */
1238
1286
  calculateDesiredPosition(timeMs) {
1239
- // No positions available.
1240
1287
  if (!this.positions || this.positions.length === 0) {
1241
1288
  return { position: null, lastIndex: -1, nextIndex: -1 };
1242
1289
  }
1243
- // Only one position..
1244
1290
  if (this.positions.length === 1) {
1245
1291
  return {
1246
1292
  position: this.positions[0].pos3d || null,
@@ -1248,7 +1294,6 @@
1248
1294
  nextIndex: 0
1249
1295
  };
1250
1296
  }
1251
- // Before first position - use first two positions for orientation.
1252
1297
  if (timeMs <= this.positions[0].dateTime.getTime()) {
1253
1298
  return {
1254
1299
  position: this.positions[0].pos3d || null,
@@ -1256,7 +1301,6 @@
1256
1301
  nextIndex: Math.min(1, this.positions.length - 1)
1257
1302
  };
1258
1303
  }
1259
- // After last position - use last two positions for orientation.
1260
1304
  const lastIdx = this.positions.length - 1;
1261
1305
  if (timeMs >= this.positions[lastIdx].dateTime.getTime()) {
1262
1306
  return {
@@ -1265,12 +1309,10 @@
1265
1309
  nextIndex: lastIdx
1266
1310
  };
1267
1311
  }
1268
- // Find positions to interpolate between.
1269
1312
  for (let i = 0; i < this.positions.length - 1; i++) {
1270
1313
  const current = this.positions[i];
1271
1314
  const next = this.positions[i + 1];
1272
1315
  if (timeMs >= current.dateTime.getTime() && timeMs < next.dateTime.getTime()) {
1273
- // Exact match on current position - still use current and next for orientation.
1274
1316
  if (timeMs === current.dateTime.getTime()) {
1275
1317
  return {
1276
1318
  position: current.pos3d || null,
@@ -1278,7 +1320,6 @@
1278
1320
  nextIndex: i + 1
1279
1321
  };
1280
1322
  }
1281
- // Interpolate between current and next.
1282
1323
  if (!current.pos3d || !next.pos3d) {
1283
1324
  return {
1284
1325
  position: current.pos3d || next.pos3d || null,
@@ -1305,16 +1346,12 @@
1305
1346
  }
1306
1347
  }
1307
1348
  }
1308
- // Fallback to last position with previous position for orientation.
1309
1349
  return {
1310
1350
  position: this.positions[lastIdx].pos3d || null,
1311
1351
  lastIndex: Math.max(0, lastIdx - 1),
1312
1352
  nextIndex: lastIdx
1313
1353
  };
1314
1354
  }
1315
- /**
1316
- * Main method to get the current position based on time.
1317
- */
1318
1355
  GetValue() {
1319
1356
  let viewerTime = this.viewer.scene.lastRenderTime;
1320
1357
  if (!viewerTime) {
@@ -1322,88 +1359,46 @@
1322
1359
  }
1323
1360
  const viewerTimeMs = Cesium.JulianDate.toDate(viewerTime).getTime();
1324
1361
  const currentRealTimeMs = Date.now();
1325
- // Calculate the desired position based on time.
1326
- const desired = this.calculateDesiredPosition(viewerTimeMs);
1327
- if (!desired.position) {
1328
- this.currentAnimatedPos = null;
1329
- this.actualTargetPos = null;
1362
+ const expected = this.calculateExpectedPosition(viewerTimeMs);
1363
+ if (!expected.position) {
1364
+ this.currentPos = null;
1365
+ this.currentVelocity = new Cesium.Cartesian3(0, 0, 0);
1330
1366
  return new Cesium.Cartesian3();
1331
1367
  }
1368
+ const desired = this.calculateDesiredPosition(viewerTimeMs);
1332
1369
  this.lastDesiredPosIndex = desired.lastIndex;
1333
1370
  this.lastDesiredNextIndex = desired.nextIndex;
1334
- // Determine the desired position without interpolation.
1335
- let discreteTarget;
1336
- if (desired.lastIndex === desired.nextIndex) {
1337
- discreteTarget = this.positions[desired.lastIndex].pos3d;
1338
- }
1339
- // Use the next position as the target.
1340
- else {
1341
- discreteTarget = this.positions[desired.nextIndex].pos3d;
1342
- }
1343
- // Check if we have a new actual target
1344
- const actualTargetChanged = !this.actualTargetPos || !Cesium.Cartesian3.equals(this.actualTargetPos, discreteTarget);
1345
- // First time or no previous position - start here.
1346
- if (!this.currentAnimatedPos) {
1347
- if (this.animationStartPos) {
1348
- this.actualTargetPos = discreteTarget;
1349
- const progress = this.getAnimationProgress(currentRealTimeMs, this.animationStartTime);
1350
- // Animation from start position complete.
1351
- if (progress >= 1.0) {
1352
- this.currentAnimatedPos = desired.position;
1353
- this.animationStartPos = null;
1354
- this.animationStartTime = null;
1355
- return desired.position;
1356
- }
1357
- // Still animating from initial position.
1358
- else {
1359
- const easedProgress = this.easeInOutQuad(progress);
1360
- this.currentAnimatedPos = Cesium.Cartesian3.lerp(this.animationStartPos, desired.position, easedProgress, new Cesium.Cartesian3());
1361
- return this.currentAnimatedPos;
1362
- }
1363
- }
1364
- // No initial animation needed.
1365
- else {
1366
- this.currentAnimatedPos = desired.position;
1367
- this.actualTargetPos = discreteTarget;
1368
- return desired.position;
1369
- }
1370
- }
1371
- // Target changed.
1372
- if (actualTargetChanged) {
1373
- this.animationStartPos = this.currentAnimatedPos;
1374
- this.animationStartTime = currentRealTimeMs;
1375
- this.actualTargetPos = discreteTarget;
1376
- }
1377
- // Continue or start animation to target if we have an active animation.
1378
- if (this.animationStartPos && this.animationStartTime) {
1379
- const progress = this.getAnimationProgress(currentRealTimeMs, this.animationStartTime);
1380
- // Animation complete.
1381
- if (progress >= 1.0) {
1382
- this.currentAnimatedPos = desired.position;
1383
- this.animationStartPos = null;
1384
- this.animationStartTime = null;
1385
- return desired.position;
1386
- }
1387
- // Continue animation to interpolated position.
1388
- else {
1389
- const easedProgress = this.easeInOutQuad(progress);
1390
- this.currentAnimatedPos = Cesium.Cartesian3.lerp(this.animationStartPos, desired.position, easedProgress, new Cesium.Cartesian3());
1391
- return this.currentAnimatedPos;
1392
- }
1393
- }
1394
- // No active animation - follow interpolated path directly.
1395
- this.currentAnimatedPos = desired.position;
1396
- return desired.position;
1397
- }
1398
- getAnimationProgress(currentTime, startTime) {
1399
- const elapsed = currentTime - startTime;
1400
- return Math.min(elapsed / this.animationDuration, 1.0);
1371
+ if (!this.currentPos) {
1372
+ this.currentPos = expected.position.clone();
1373
+ this.currentVelocity = new Cesium.Cartesian3(0, 0, 0);
1374
+ this.lastUpdateTime = currentRealTimeMs;
1375
+ return this.currentPos;
1376
+ }
1377
+ const deltaTime = this.lastUpdateTime ? (currentRealTimeMs - this.lastUpdateTime) / 1000.0 : 0.016;
1378
+ this.lastUpdateTime = currentRealTimeMs;
1379
+ const clampedDeltaTime = Math.min(deltaTime, 0.1);
1380
+ const targetDistance = Cesium.Cartesian3.distance(this.currentPos, expected.position);
1381
+ if (targetDistance < 0.5) {
1382
+ this.currentPos = expected.position.clone();
1383
+ this.currentVelocity = new Cesium.Cartesian3(0, 0, 0);
1384
+ return this.currentPos;
1385
+ }
1386
+ // Check if we're really far behind and need to teleport.
1387
+ if (targetDistance > 500) {
1388
+ this.currentPos = expected.position.clone();
1389
+ this.currentVelocity = new Cesium.Cartesian3(0, 0, 0);
1390
+ return this.currentPos;
1391
+ }
1392
+ const direction = Cesium.Cartesian3.subtract(expected.position, this.currentPos, new Cesium.Cartesian3());
1393
+ Cesium.Cartesian3.normalize(direction, direction);
1394
+ const targetSpeed = this.calculateAutonomousSpeed(targetDistance, expected.timeDelta);
1395
+ const targetVelocity = Cesium.Cartesian3.multiplyByScalar(direction, targetSpeed, new Cesium.Cartesian3());
1396
+ this.currentVelocity = Cesium.Cartesian3.lerp(this.currentVelocity, targetVelocity, this.smoothingFactor, new Cesium.Cartesian3());
1397
+ const velocityDelta = Cesium.Cartesian3.multiplyByScalar(this.currentVelocity, clampedDeltaTime, new Cesium.Cartesian3());
1398
+ this.currentPos = Cesium.Cartesian3.add(this.currentPos, velocityDelta, new Cesium.Cartesian3());
1399
+ return this.currentPos;
1401
1400
  }
1402
- /**
1403
- * Returns a series of positions to use for rendering the path.
1404
- */
1405
1401
  GetSeries() {
1406
- // Update at 30 fps.
1407
1402
  let doUpdate = this.lastCalcSeriesTime == null;
1408
1403
  if (!doUpdate && this.lastCalcSeriesTime && (new Date().getTime() - this.lastCalcSeriesTime) > 1000 / 30) {
1409
1404
  doUpdate = true;
@@ -1411,33 +1406,27 @@
1411
1406
  if (!doUpdate) {
1412
1407
  return this.lastCalcSeriesPos3d;
1413
1408
  }
1414
- // Ensure position indices are up-to-date.
1415
1409
  this.GetValue();
1416
1410
  let now = this.viewer.scene.lastRenderTime;
1417
1411
  if (!now) {
1418
1412
  now = this.viewer.clock.currentTime;
1419
1413
  }
1420
1414
  const nowDate = Cesium.JulianDate.toDate(now);
1421
- // Get total duration.
1422
1415
  if (!this.positions || this.positions.length < 2) {
1423
1416
  this.lastCalcSeriesTime = nowDate.getTime();
1424
1417
  this.lastCalcSeriesPos3d = [];
1425
1418
  return [];
1426
1419
  }
1427
1420
  const totalDuration = this.positions[this.positions.length - 1].dateTime.getTime() - this.positions[0].dateTime.getTime();
1428
- // Percentage of the polyline to be visible before and after each point.
1429
- const visibilityPercentage = 0.05; // 5%
1421
+ const visibilityPercentage = 0.05;
1430
1422
  const visibilityDuration = totalDuration * visibilityPercentage;
1431
- // Gather positions that fall within the visibility duration.
1432
1423
  const newPosses = [];
1433
1424
  for (let i = 0; i < this.positions.length; i++) {
1434
1425
  const pos = this.positions[i];
1435
1426
  if (!pos.pos3d) {
1436
1427
  continue;
1437
1428
  }
1438
- let add = nowDate >= new Date(pos.dateTime.getTime() - visibilityDuration / 2) &&
1439
- nowDate <= new Date(pos.dateTime.getTime() + visibilityDuration / 2);
1440
- // Also include the segment we're currently traversing.
1429
+ let add = nowDate >= new Date(pos.dateTime.getTime() - visibilityDuration / 2) && nowDate <= new Date(pos.dateTime.getTime() + visibilityDuration / 2);
1441
1430
  if (!add && this.lastDesiredPosIndex > -1 && this.lastDesiredNextIndex > -1) {
1442
1431
  add = i >= this.lastDesiredPosIndex && i <= this.lastDesiredNextIndex;
1443
1432
  }
@@ -1449,11 +1438,7 @@
1449
1438
  this.lastCalcSeriesPos3d = newPosses;
1450
1439
  return newPosses;
1451
1440
  }
1452
- /**
1453
- * Returns the orientation based on current position and heading.
1454
- */
1455
1441
  GetOrient() {
1456
- // Update at 30 fps.
1457
1442
  let doUpdate = this.lastCalcOrientTime == null;
1458
1443
  if (!doUpdate && this.lastCalcOrientTime && (new Date().getTime() - this.lastCalcOrientTime) > 1000 / 30) {
1459
1444
  doUpdate = true;
@@ -1461,26 +1446,34 @@
1461
1446
  if (!doUpdate && this.lastCalcOrient) {
1462
1447
  return this.lastCalcOrient;
1463
1448
  }
1464
- // Default quaternion to return if we can't calculate.
1465
1449
  const defaultQuaternion = new Cesium.Quaternion();
1466
1450
  if (!this.positions || this.positions.length === 0) {
1467
1451
  return defaultQuaternion;
1468
1452
  }
1469
- // Ensure position is up-to-date.
1470
1453
  const currentPosition = this.GetValue();
1471
1454
  if (!currentPosition) {
1472
1455
  return defaultQuaternion;
1473
1456
  }
1474
- let now = this.viewer.scene.lastRenderTime;
1475
- if (!now) {
1476
- now = this.viewer.clock.currentTime;
1477
- }
1478
- const nowTime = Cesium.JulianDate.toDate(now).getTime();
1479
1457
  try {
1480
- // Get current position indices.
1458
+ const velocityMagnitude = Cesium.Cartesian3.magnitude(this.currentVelocity);
1459
+ const minimumSpeedForVelocityOrientation = Math.max(1.0, this.averageSpeed * 0.1);
1460
+ if (velocityMagnitude > minimumSpeedForVelocityOrientation) {
1461
+ const normalizedVelocity = Cesium.Cartesian3.normalize(this.currentVelocity, new Cesium.Cartesian3());
1462
+ const rotationMatrix = Cesium.Transforms.rotationMatrixFromPositionVelocity(currentPosition, normalizedVelocity);
1463
+ const quaternion = Cesium.Quaternion.fromRotationMatrix(rotationMatrix);
1464
+ const hpr = new Cesium.HeadingPitchRoll(0, Cesium.Math.toRadians(this.pitch), Cesium.Math.toRadians(this.roll));
1465
+ const pitchRollQuaternion = Cesium.Quaternion.fromHeadingPitchRoll(hpr);
1466
+ this.lastCalcOrient = Cesium.Quaternion.multiply(quaternion, pitchRollQuaternion, new Cesium.Quaternion());
1467
+ this.lastCalcOrientTime = Date.now();
1468
+ return this.lastCalcOrient;
1469
+ }
1470
+ let now = this.viewer.scene.lastRenderTime;
1471
+ if (!now) {
1472
+ now = this.viewer.clock.currentTime;
1473
+ }
1474
+ const nowTime = Cesium.JulianDate.toDate(now).getTime();
1481
1475
  const lastIndex = this.lastDesiredPosIndex;
1482
1476
  const nextIndex = this.lastDesiredNextIndex;
1483
- // Invalid indices.
1484
1477
  if (lastIndex < 0 || nextIndex < 0 ||
1485
1478
  lastIndex >= this.positions.length || nextIndex >= this.positions.length) {
1486
1479
  return this.lastCalcOrient || defaultQuaternion;
@@ -1490,74 +1483,53 @@
1490
1483
  if (!lastPos || !nextPos) {
1491
1484
  return this.lastCalcOrient || defaultQuaternion;
1492
1485
  }
1493
- // Single position case - use its heading if available.
1494
1486
  if (lastIndex === nextIndex) {
1495
1487
  if (lastPos.heading !== null) {
1496
1488
  this.lastCalcOrient = Cesium.Transforms.headingPitchRollQuaternion(currentPosition, new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(lastPos.heading), Cesium.Math.toRadians(this.pitch), Cesium.Math.toRadians(this.roll)));
1497
1489
  this.lastCalcOrientTime = Date.now();
1498
1490
  return this.lastCalcOrient;
1499
1491
  }
1500
- // No heading data and single position, return previous or default.
1501
1492
  return this.lastCalcOrient || defaultQuaternion;
1502
1493
  }
1503
- // Two different positions - we can calculate orientation.
1504
- // Use explicit heading values if available.
1505
1494
  if (lastPos.heading !== null && nextPos.heading !== null) {
1506
- // Calculate interpolated heading.
1507
1495
  let deltaHeading = nextPos.heading - lastPos.heading;
1508
- // Handle wrap-around between 359° and 0°.
1509
1496
  if (deltaHeading > 180) {
1510
1497
  deltaHeading -= 360;
1511
1498
  }
1512
1499
  else if (deltaHeading < -180) {
1513
1500
  deltaHeading += 360;
1514
1501
  }
1515
- // Calculate interpolation factor.
1516
1502
  let factor = 0;
1517
1503
  if (lastPos.dateTime.getTime() !== nextPos.dateTime.getTime()) {
1518
- factor = (nowTime - lastPos.dateTime.getTime()) /
1519
- (nextPos.dateTime.getTime() - lastPos.dateTime.getTime());
1504
+ factor = (nowTime - lastPos.dateTime.getTime()) / (nextPos.dateTime.getTime() - lastPos.dateTime.getTime());
1520
1505
  factor = Math.max(0, Math.min(1, factor));
1521
1506
  }
1522
- // Apply easing for smoother rotation.
1523
1507
  factor = this.easeInOutQuad(factor);
1524
- // Calculate final heading.
1525
1508
  let interpolatedHeading = lastPos.heading + factor * deltaHeading;
1526
1509
  interpolatedHeading = (interpolatedHeading + 360) % 360;
1527
- // Create quaternion from heading, pitch, roll.
1528
1510
  this.lastCalcOrient = Cesium.Transforms.headingPitchRollQuaternion(currentPosition, new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(interpolatedHeading), Cesium.Math.toRadians(this.pitch), Cesium.Math.toRadians(this.roll)));
1529
1511
  }
1530
- // Calculate heading from position changes if heading data not available.
1531
1512
  else {
1532
1513
  if (!lastPos.pos3d || !nextPos.pos3d) {
1533
1514
  return this.lastCalcOrient || defaultQuaternion;
1534
1515
  }
1535
- // Flatten positions to avoid altitude-related heading changes.
1536
1516
  const adjustedPointPrev = Cesium.Cartographic.fromCartesian(lastPos.pos3d);
1537
1517
  const adjustedPos3dPrev = Cesium.Cartesian3.fromRadians(adjustedPointPrev.longitude, adjustedPointPrev.latitude, 0);
1538
1518
  const adjustedPointNext = Cesium.Cartographic.fromCartesian(nextPos.pos3d);
1539
1519
  const adjustedPos3dNext = Cesium.Cartesian3.fromRadians(adjustedPointNext.longitude, adjustedPointNext.latitude, 0);
1540
- // Skip if positions are too close (less than 5cm).
1541
1520
  const distance = Cesium.Cartesian3.distance(adjustedPos3dPrev, adjustedPos3dNext);
1542
1521
  if (distance < 0.05) {
1543
1522
  return this.lastCalcOrient || defaultQuaternion;
1544
1523
  }
1545
- // Calculate direction vector.
1546
1524
  const direction = Cesium.Cartesian3.subtract(adjustedPos3dNext, adjustedPos3dPrev, new Cesium.Cartesian3());
1547
- // Skip if no movement.
1548
1525
  if (direction.x === 0 && direction.y === 0 && direction.z === 0) {
1549
1526
  return this.lastCalcOrient || defaultQuaternion;
1550
1527
  }
1551
- // Normalize the direction vector.
1552
1528
  Cesium.Cartesian3.normalize(direction, direction);
1553
- // Calculate rotation based on movement direction.
1554
1529
  const rotationMatrix = Cesium.Transforms.rotationMatrixFromPositionVelocity(currentPosition, direction);
1555
- // Convert to quaternion.
1556
1530
  const quaternion = Cesium.Quaternion.fromRotationMatrix(rotationMatrix);
1557
- // Add pitch and roll adjustments.
1558
1531
  const hpr = new Cesium.HeadingPitchRoll(0, Cesium.Math.toRadians(this.pitch), Cesium.Math.toRadians(this.roll));
1559
1532
  const pitchRollQuaternion = Cesium.Quaternion.fromHeadingPitchRoll(hpr);
1560
- // Combine quaternions.
1561
1533
  this.lastCalcOrient = Cesium.Quaternion.multiply(quaternion, pitchRollQuaternion, new Cesium.Quaternion());
1562
1534
  }
1563
1535
  }
@@ -1568,6 +1540,52 @@
1568
1540
  this.lastCalcOrientTime = Date.now();
1569
1541
  return this.lastCalcOrient;
1570
1542
  }
1543
+ GetCurrentVelocity() {
1544
+ return this.currentVelocity ? this.currentVelocity.clone() : new Cesium.Cartesian3(0, 0, 0);
1545
+ }
1546
+ SupplementSeries(newSeries) {
1547
+ if (!newSeries || newSeries.length === 0) {
1548
+ return;
1549
+ }
1550
+ for (const pos of newSeries) {
1551
+ if (!pos || !pos.pos3d || !pos.dateTime) {
1552
+ continue;
1553
+ }
1554
+ const existingIndex = this.positions.findIndex(p => p.dateTime.getTime() === pos.dateTime.getTime());
1555
+ if (existingIndex >= 0) {
1556
+ this.positions[existingIndex] = pos;
1557
+ }
1558
+ else {
1559
+ this.AddPosition(pos);
1560
+ }
1561
+ }
1562
+ }
1563
+ UpdatePositionForDateTime(pos3d, dateTime, heading) {
1564
+ if (!pos3d || !dateTime) {
1565
+ return;
1566
+ }
1567
+ const existingIndex = this.positions.findIndex(p => p.dateTime.getTime() === dateTime.getTime());
1568
+ const newPos = {
1569
+ pos3d: pos3d,
1570
+ dateTime: dateTime,
1571
+ heading: heading !== undefined ? heading : null
1572
+ };
1573
+ if (existingIndex >= 0) {
1574
+ this.positions[existingIndex] = newPos;
1575
+ this.processHeadings();
1576
+ this.sortPositions();
1577
+ this.invalidateCache();
1578
+ }
1579
+ else {
1580
+ this.AddPosition(newPos);
1581
+ }
1582
+ }
1583
+ HasPositionForDateTime(dateTime) {
1584
+ return this.positions.some(p => p.dateTime.getTime() === dateTime.getTime());
1585
+ }
1586
+ GetPositionCount() {
1587
+ return this.positions.length;
1588
+ }
1571
1589
  }
1572
1590
  CesiumAnimatedProperty.AnimatePositionSeries = AnimatePositionSeries;
1573
1591
  function GetSeriesPossesForHistoricEntity(viewer, dataHeightRef, heightRef, historic) {
@@ -5438,7 +5456,7 @@
5438
5456
  * @returns
5439
5457
  */
5440
5458
  async function Render(params) {
5441
- var _a, _b, _c, _d;
5459
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
5442
5460
  const entity = params.entity;
5443
5461
  if (!params.entityHistoric) {
5444
5462
  params.entityHistoric = [];
@@ -5639,39 +5657,69 @@
5639
5657
  // Unset width/height.
5640
5658
  cEntity.billboard.width = undefined;
5641
5659
  cEntity.billboard.height = undefined;
5660
+ const pos3d = exports.EntityUtils.GetPos({
5661
+ viewer: params.viewer,
5662
+ entity,
5663
+ recordHeightRef: heightRef,
5664
+ returnHeightRef: heightRef,
5665
+ allowRendered: false
5666
+ });
5642
5667
  const prevPos3d = GetCValue(params.viewer, cEntity.position);
5643
- let prevStartTime = null;
5644
5668
  if (cEntity.position && cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"]) {
5645
- prevStartTime = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"].GetAnimateFromDateTime();
5646
- }
5647
- // If we have a series of time-based positions then we'll animate as time changes.
5648
- const series = exports.CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
5649
- if (series.length > 1) {
5650
- animatePosition = new exports.CesiumAnimatedProperty.AnimatePositionSeries({
5651
- posses: series,
5652
- viewer: params.viewer,
5653
- animateFromPos3d: prevPos3d,
5654
- animateFromPos3dTimeStart: prevStartTime
5655
- });
5656
- cEntity.position = new Cesium.CallbackProperty(() => animatePosition.GetValue(), false);
5657
- cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
5669
+ animatePosition = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"];
5670
+ const animateSeries = animatePosition;
5671
+ const series = exports.CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
5672
+ if (series.length) {
5673
+ animateSeries.SupplementSeries(series);
5674
+ }
5675
+ const dateTimeStr = (_d = (_c = entity.Bruce.Outline) === null || _c === void 0 ? void 0 : _c.find(x => !!x.DateTime)) === null || _d === void 0 ? void 0 : _d.DateTime;
5676
+ const dateTime = dateTimeStr ? new Date(dateTimeStr) : null;
5677
+ if (dateTime) {
5678
+ animateSeries.UpdatePositionForDateTime(pos3d, dateTime);
5679
+ }
5680
+ // We don't have a date-stamped incoming position.
5681
+ if (!dateTime && !series.length) {
5682
+ animatePosition = null;
5683
+ }
5658
5684
  }
5659
- else {
5660
- const pos3d = exports.EntityUtils.GetPos({
5661
- viewer: params.viewer,
5662
- entity,
5663
- recordHeightRef: heightRef,
5664
- returnHeightRef: heightRef,
5665
- allowRendered: false
5666
- });
5667
- if (!prevPos3d || !Cesium.Cartesian3.equals(prevPos3d, pos3d)) {
5668
- animatePosition = new exports.CesiumAnimatedProperty.AnimatePosition({
5669
- durationMs: 200,
5670
- targetPos3d: pos3d,
5671
- viewer: params.viewer,
5672
- startPos3d: prevPos3d
5685
+ if (!animatePosition) {
5686
+ // If we've loaded a set of series positions then we'll animate through them.
5687
+ const series = exports.CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
5688
+ if (series.length > 1) {
5689
+ animatePosition = new exports.CesiumAnimatedProperty.AnimatePositionSeries({
5690
+ posses: series,
5691
+ viewer: params.viewer
5673
5692
  });
5674
5693
  cEntity.position = new Cesium.CallbackProperty(() => animatePosition.GetValue(), false);
5694
+ cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
5695
+ }
5696
+ else {
5697
+ const dateTimeStr = (_f = (_e = entity.Bruce.Outline) === null || _e === void 0 ? void 0 : _e.find(x => !!x.DateTime)) === null || _f === void 0 ? void 0 : _f.DateTime;
5698
+ const dateTime = dateTimeStr ? new Date(dateTimeStr) : null;
5699
+ const posChanged = !prevPos3d || !Cesium.Cartesian3.equals(prevPos3d, pos3d);
5700
+ if (posChanged) {
5701
+ let posses = [];
5702
+ const isLive = exports.ViewUtils.GetTimeDetails({ viewer: params.viewer }).isLive;
5703
+ if (prevPos3d && isLive) {
5704
+ posses.push({
5705
+ pos3d: prevPos3d,
5706
+ // Guess so that we can determine a direction of movement right away :)
5707
+ dateTime: new Date(dateTime.getTime() - 1000),
5708
+ heading: null
5709
+ });
5710
+ }
5711
+ posses.push({
5712
+ pos3d: pos3d,
5713
+ dateTime: dateTime,
5714
+ heading: null
5715
+ });
5716
+ animatePosition = new exports.CesiumAnimatedProperty.AnimatePositionSeries({
5717
+ posses: posses,
5718
+ viewer: params.viewer
5719
+ });
5720
+ cEntity.position = new Cesium.CallbackProperty(() => animatePosition.GetValue(), false);
5721
+ cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
5722
+ }
5675
5723
  }
5676
5724
  }
5677
5725
  // We'll use "SetDefaultColor" to updating the internal reference and to allow for an animation.
@@ -5970,39 +6018,52 @@
5970
6018
  cEntity.billboard.heightReference = new Cesium.ConstantProperty(heightRef);
5971
6019
  cEntity.billboard.distanceDisplayCondition = new Cesium.ConstantProperty(exports.EntityRenderEngine.GetDisplayCondition(params.minDistance, params.maxDistance));
5972
6020
  cEntity.billboard.disableDepthTestDistance = new Cesium.ConstantProperty(disableDepthTest ? Number.POSITIVE_INFINITY : undefined);
6021
+ const pos3d = exports.EntityUtils.GetPos({
6022
+ viewer: params.viewer,
6023
+ entity,
6024
+ recordHeightRef: heightRef,
6025
+ returnHeightRef: heightRef,
6026
+ allowRendered: false
6027
+ });
5973
6028
  const prevPos3d = GetCValue(params.viewer, cEntity.position);
5974
- let prevStartTime = null;
5975
6029
  if (cEntity.position && cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"]) {
5976
- prevStartTime = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"].GetAnimateFromDateTime();
5977
- }
5978
- // If we have a series of time-based positions then we'll animate as time changes.
5979
- const series = exports.CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
5980
- if (series.length > 1) {
5981
- animatePosition = new exports.CesiumAnimatedProperty.AnimatePositionSeries({
5982
- posses: series,
5983
- viewer: params.viewer,
5984
- animateFromPos3d: prevPos3d,
5985
- animateFromPos3dTimeStart: prevStartTime
5986
- });
5987
- cEntity.position = new Cesium.CallbackProperty(() => animatePosition.GetValue(), false);
5988
- cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
5989
- }
5990
- else {
5991
- const pos3d = exports.EntityUtils.GetPos({
5992
- viewer: params.viewer,
5993
- entity,
5994
- recordHeightRef: heightRef,
5995
- returnHeightRef: heightRef,
5996
- allowRendered: false
5997
- });
5998
- if (!prevPos3d || !Cesium.Cartesian3.equals(prevPos3d, pos3d)) {
5999
- animatePosition = new exports.CesiumAnimatedProperty.AnimatePosition({
6000
- durationMs: 200,
6001
- targetPos3d: pos3d,
6002
- viewer: params.viewer,
6003
- startPos3d: prevPos3d
6030
+ animatePosition = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"];
6031
+ const animateSeries = animatePosition;
6032
+ const series = exports.CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
6033
+ if (series.length) {
6034
+ animateSeries.SupplementSeries(series);
6035
+ }
6036
+ const dateTimeStr = (_h = (_g = entity.Bruce.Outline) === null || _g === void 0 ? void 0 : _g.find(x => !!x.DateTime)) === null || _h === void 0 ? void 0 : _h.DateTime;
6037
+ const dateTime = dateTimeStr ? new Date(dateTimeStr) : null;
6038
+ if (dateTime) {
6039
+ animateSeries.UpdatePositionForDateTime(pos3d, dateTime);
6040
+ }
6041
+ // We don't have a date-stamped incoming position.
6042
+ if (!dateTime && !series.length) {
6043
+ animatePosition = null;
6044
+ }
6045
+ }
6046
+ if (!animatePosition) {
6047
+ // If we have a series of time-based positions then we'll animate as time changes.
6048
+ const series = exports.CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
6049
+ if (series.length > 1) {
6050
+ animatePosition = new exports.CesiumAnimatedProperty.AnimatePositionSeries({
6051
+ posses: series,
6052
+ viewer: params.viewer
6004
6053
  });
6005
6054
  cEntity.position = new Cesium.CallbackProperty(() => animatePosition.GetValue(), false);
6055
+ cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
6056
+ }
6057
+ else {
6058
+ if (!prevPos3d || !Cesium.Cartesian3.equals(prevPos3d, pos3d)) {
6059
+ animatePosition = new exports.CesiumAnimatedProperty.AnimatePosition({
6060
+ durationMs: 200,
6061
+ targetPos3d: pos3d,
6062
+ viewer: params.viewer,
6063
+ startPos3d: prevPos3d
6064
+ });
6065
+ cEntity.position = new Cesium.CallbackProperty(() => animatePosition.GetValue(), false);
6066
+ }
6006
6067
  }
6007
6068
  }
6008
6069
  // We'll use "SetDefaultColor" to updating the internal reference and to allow for an animation.
@@ -6021,7 +6082,7 @@
6021
6082
  // Generate a polyline 'track' for the historic data.
6022
6083
  // We do this for historic data that exists and is moving.
6023
6084
  if (shouldShowTrack && animatePosition && animatePosition instanceof exports.CesiumAnimatedProperty.AnimatePositionSeries && animatePosition.GetSeries) {
6024
- const lStyle = (_d = (_c = params.fullStyle) === null || _c === void 0 ? void 0 : _c.polylineStyle) !== null && _d !== void 0 ? _d : {};
6085
+ const lStyle = (_k = (_j = params.fullStyle) === null || _j === void 0 ? void 0 : _j.polylineStyle) !== null && _k !== void 0 ? _k : {};
6025
6086
  const bColor = lStyle.lineColor ? BModels.Calculator.GetColor(lStyle.lineColor, entity, params.tags) : null;
6026
6087
  const cColor = bColor ? ColorToCColor(bColor) : Cesium.Color.fromCssColorString("rgba(255, 193, 7, 0.8)");
6027
6088
  let width = lStyle.lineWidth ? EnsureNumber(BModels.Calculator.GetNumber(lStyle.lineWidth, entity, params.tags)) : 2;
@@ -7037,7 +7098,7 @@
7037
7098
  * @returns
7038
7099
  */
7039
7100
  function Render(params) {
7040
- var _a, _b, _c, _d;
7101
+ var _a, _b, _c, _d, _e, _f;
7041
7102
  const entity = params.entity;
7042
7103
  if (!params.entityHistoric) {
7043
7104
  params.entityHistoric = [];
@@ -7229,40 +7290,41 @@
7229
7290
  cEntity.model.colorBlendAmount = new Cesium.ConstantProperty(blendAmount);
7230
7291
  cEntity.model.colorBlendMode = new Cesium.ConstantProperty(blendMode);
7231
7292
  cEntity.model.distanceDisplayCondition = new Cesium.ConstantProperty(exports.EntityRenderEngine.GetDisplayCondition(params.minDistance, params.maxDistance));
7232
- let prevStartTime = null;
7233
7293
  if (cEntity.position && cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"]) {
7234
- prevStartTime = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"].GetAnimateFromDateTime();
7235
- }
7236
- // If we've loaded a set of series positions then we'll animate through them.
7237
- const series = exports.CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
7238
- if (series.length > 1) {
7239
- animatePosition = new exports.CesiumAnimatedProperty.AnimatePositionSeries({
7240
- posses: series,
7241
- viewer: params.viewer,
7242
- pitch: pitch,
7243
- roll: roll,
7244
- animateFromPos3d: prevPos3d,
7245
- animateFromPos3dTimeStart: prevStartTime
7246
- });
7247
- cEntity.position = new Cesium.CallbackProperty(() => animatePosition.GetValue(), false);
7248
- cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
7249
- }
7250
- else {
7294
+ animatePosition = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"];
7295
+ const animateSeries = animatePosition;
7296
+ const series = exports.CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
7297
+ if (series.length) {
7298
+ animateSeries.SupplementSeries(series);
7299
+ }
7251
7300
  const dateTimeStr = (_b = (_a = entity.Bruce.Outline) === null || _a === void 0 ? void 0 : _a.find(x => !!x.DateTime)) === null || _b === void 0 ? void 0 : _b.DateTime;
7252
7301
  const dateTime = dateTimeStr ? new Date(dateTimeStr) : null;
7253
- const posChanged = !prevPos3d || !Cesium.Cartesian3.equals(prevPos3d, pos3d);
7254
- if (posChanged) {
7255
- if (cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"]) {
7256
- const prevAnimatePosition = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"];
7257
- animatePosition = prevAnimatePosition;
7258
- animatePosition.AddPosition({
7259
- pos3d: pos3d,
7260
- dateTime: dateTime,
7261
- heading: !EnsureNumber(transform === null || transform === void 0 ? void 0 : transform.heading) ? null : heading
7262
- });
7263
- animatePosition.UpdatePitchRoll(pitch, roll);
7264
- }
7265
- else {
7302
+ if (dateTime) {
7303
+ animateSeries.UpdatePositionForDateTime(pos3d, dateTime, !EnsureNumber(transform === null || transform === void 0 ? void 0 : transform.heading) ? null : heading);
7304
+ }
7305
+ // We don't have a date-stamped incoming position.
7306
+ if (!dateTime && !series.length) {
7307
+ animatePosition = null;
7308
+ }
7309
+ }
7310
+ if (!animatePosition) {
7311
+ // If we've loaded a set of series positions then we'll animate through them.
7312
+ const series = exports.CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
7313
+ if (series.length > 1) {
7314
+ animatePosition = new exports.CesiumAnimatedProperty.AnimatePositionSeries({
7315
+ posses: series,
7316
+ viewer: params.viewer,
7317
+ pitch: pitch,
7318
+ roll: roll
7319
+ });
7320
+ cEntity.position = new Cesium.CallbackProperty(() => animatePosition.GetValue(), false);
7321
+ cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
7322
+ }
7323
+ else {
7324
+ const dateTimeStr = (_d = (_c = entity.Bruce.Outline) === null || _c === void 0 ? void 0 : _c.find(x => !!x.DateTime)) === null || _d === void 0 ? void 0 : _d.DateTime;
7325
+ const dateTime = dateTimeStr ? new Date(dateTimeStr) : null;
7326
+ const posChanged = !prevPos3d || !Cesium.Cartesian3.equals(prevPos3d, pos3d);
7327
+ if (posChanged) {
7266
7328
  let posses = [];
7267
7329
  const isLive = exports.ViewUtils.GetTimeDetails({ viewer: params.viewer }).isLive;
7268
7330
  if (prevPos3d && isLive) {
@@ -7282,9 +7344,7 @@
7282
7344
  posses: posses,
7283
7345
  viewer: params.viewer,
7284
7346
  pitch: pitch,
7285
- roll: roll,
7286
- animateFromPos3d: prevPos3d && !isLive ? prevPos3d : null,
7287
- animateFromPos3dTimeStart: null
7347
+ roll: roll
7288
7348
  });
7289
7349
  cEntity.position = new Cesium.CallbackProperty(() => animatePosition.GetValue(), false);
7290
7350
  cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
@@ -7437,7 +7497,7 @@
7437
7497
  // Generate a polyline 'track' for the historic data.
7438
7498
  // We do this for historic data that exists and is moving.
7439
7499
  if (shouldShowTrack && animatePosition && animatePosition instanceof exports.CesiumAnimatedProperty.AnimatePositionSeries && animatePosition.GetSeries) {
7440
- const lStyle = (_d = (_c = params.fullStyle) === null || _c === void 0 ? void 0 : _c.polylineStyle) !== null && _d !== void 0 ? _d : {};
7500
+ const lStyle = (_f = (_e = params.fullStyle) === null || _e === void 0 ? void 0 : _e.polylineStyle) !== null && _f !== void 0 ? _f : {};
7441
7501
  const bColor = lStyle.lineColor ? BModels.Calculator.GetColor(lStyle.lineColor, entity, params.tags) : null;
7442
7502
  const cColor = bColor ? ColorToCColor(bColor) : Cesium.Color.fromCssColorString("rgba(255, 193, 7, 0.8)");
7443
7503
  let width = lStyle.lineWidth ? EnsureNumber(BModels.Calculator.GetNumber(lStyle.lineWidth, entity, params.tags)) : 2;
@@ -10655,6 +10715,12 @@
10655
10715
  CesiumAnimatedInOut.AnimateOut = AnimateOut;
10656
10716
  })(exports.CesiumAnimatedInOut || (exports.CesiumAnimatedInOut = {}));
10657
10717
 
10718
+ // Constants for update source types
10719
+ const UPDATE_SOURCES = {
10720
+ TREE_CASCADE: "tree-cascade",
10721
+ TILESET_DISPOSE: "tileset-dispose",
10722
+ OSM_TILESET_DISPOSE: "osm-tileset-dispose"
10723
+ };
10658
10724
  /**
10659
10725
  * Returns if a given visual is alive and in the scene.
10660
10726
  * @param viewer
@@ -11126,7 +11192,7 @@
11126
11192
  });
11127
11193
  }
11128
11194
  // Trigger update event.
11129
- if (this.onUpdate && source !== "tree-cascade") {
11195
+ if (this.onUpdate && source !== UPDATE_SOURCES.TREE_CASCADE) {
11130
11196
  const update = {
11131
11197
  type: EVisualUpdateType.Update,
11132
11198
  entityId: state.entityId
@@ -11217,7 +11283,7 @@
11217
11283
  }
11218
11284
  }
11219
11285
  // Trigger update event.
11220
- if (this.onUpdate && source !== "tree-cascade") {
11286
+ if (this.onUpdate && source !== UPDATE_SOURCES.TREE_CASCADE) {
11221
11287
  const keys = Object.keys(states);
11222
11288
  for (let i = 0; i < keys.length; i++) {
11223
11289
  const key = keys[i];
@@ -11499,7 +11565,7 @@
11499
11565
  if (source) {
11500
11566
  update.source = source;
11501
11567
  }
11502
- if (source !== "tree-cascade") {
11568
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
11503
11569
  (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(update);
11504
11570
  }
11505
11571
  }
@@ -11665,7 +11731,7 @@
11665
11731
  if (source) {
11666
11732
  update.source = source;
11667
11733
  }
11668
- if (source !== "tree-cascade") {
11734
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
11669
11735
  (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(update);
11670
11736
  }
11671
11737
  }
@@ -11711,7 +11777,7 @@
11711
11777
  if (params === null || params === void 0 ? void 0 : params.source) {
11712
11778
  update.source = params.source;
11713
11779
  }
11714
- if ((params === null || params === void 0 ? void 0 : params.source) !== "tree-cascade") {
11780
+ if ((params === null || params === void 0 ? void 0 : params.source) !== UPDATE_SOURCES.TREE_CASCADE) {
11715
11781
  (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(update);
11716
11782
  }
11717
11783
  if ((params === null || params === void 0 ? void 0 : params.requestRender) != false) {
@@ -11861,7 +11927,7 @@
11861
11927
  if (source) {
11862
11928
  update.source = source;
11863
11929
  }
11864
- if (source !== "tree-cascade") {
11930
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
11865
11931
  (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(update);
11866
11932
  }
11867
11933
  }
@@ -11973,7 +12039,7 @@
11973
12039
  if (params === null || params === void 0 ? void 0 : params.source) {
11974
12040
  update.source = params.source;
11975
12041
  }
11976
- if ((params === null || params === void 0 ? void 0 : params.source) !== "tree-cascade") {
12042
+ if ((params === null || params === void 0 ? void 0 : params.source) !== UPDATE_SOURCES.TREE_CASCADE) {
11977
12043
  (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(update);
11978
12044
  }
11979
12045
  }
@@ -12013,7 +12079,7 @@
12013
12079
  if (source) {
12014
12080
  update.source = source;
12015
12081
  }
12016
- if (source !== "tree-cascade") {
12082
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
12017
12083
  (_b = this.onUpdate) === null || _b === void 0 ? void 0 : _b.Trigger(update);
12018
12084
  }
12019
12085
  if (requestRender != false) {
@@ -12135,7 +12201,7 @@
12135
12201
  if (source) {
12136
12202
  update.source = source;
12137
12203
  }
12138
- if (source !== "tree-cascade") {
12204
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
12139
12205
  (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(update);
12140
12206
  }
12141
12207
  if (doesInclude) {
@@ -12174,7 +12240,7 @@
12174
12240
  if (source) {
12175
12241
  update.source = source;
12176
12242
  }
12177
- if (source !== "tree-cascade") {
12243
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
12178
12244
  (_b = this.onUpdate) === null || _b === void 0 ? void 0 : _b.Trigger(update);
12179
12245
  }
12180
12246
  this.rego[entityId] = entityRegos.filter(r => r.menuItemId !== menuItemId);
@@ -12209,7 +12275,7 @@
12209
12275
  if (source) {
12210
12276
  update.source = source;
12211
12277
  }
12212
- if (source !== "tree-cascade") {
12278
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
12213
12279
  (_c = this.onUpdate) === null || _c === void 0 ? void 0 : _c.Trigger(update);
12214
12280
  }
12215
12281
  if (doUpdate && menuItemId) {
@@ -12316,7 +12382,7 @@
12316
12382
  if (source) {
12317
12383
  update.source = source;
12318
12384
  }
12319
- if (source !== "tree-cascade") {
12385
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
12320
12386
  (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(update);
12321
12387
  }
12322
12388
  this.queueUpdate({
@@ -18623,7 +18689,7 @@
18623
18689
  requestRender: false,
18624
18690
  menuItemId: this.item.id,
18625
18691
  doRemove: false,
18626
- source: "tileset-dispose"
18692
+ source: UPDATE_SOURCES.TILESET_DISPOSE
18627
18693
  });
18628
18694
  // Might have to do something smarter since siblings could still be OK.
18629
18695
  this.loadedCesiumEntities[rego.entityId] = null;
@@ -18651,6 +18717,7 @@
18651
18717
  ];
18652
18718
  while (stack.length > 0) {
18653
18719
  const { node, parentId, firstFoundCollapsedBranch } = stack.pop();
18720
+ let newFirstFoundCollapsedBranch = firstFoundCollapsedBranch;
18654
18721
  if (firstFoundCollapsedBranch) {
18655
18722
  this.treeNodeByGeomId[node.geomId] = firstFoundCollapsedBranch;
18656
18723
  this.treeNodeByEntityId[node.id] = firstFoundCollapsedBranch;
@@ -18665,19 +18732,19 @@
18665
18732
  };
18666
18733
  this.treeNodeByGeomId[node.geomId] = cache;
18667
18734
  this.treeNodeByEntityId[node.id] = cache;
18668
- let newFirstFoundCollapsedBranch = firstFoundCollapsedBranch;
18669
18735
  if (!firstFoundCollapsedBranch && node.collapsed) {
18670
18736
  newFirstFoundCollapsedBranch = cache;
18671
18737
  }
18672
- // Push children to stack in reverse order to maintain correct traversal.
18673
- if (node.children) {
18674
- for (let i = node.children.length - 1; i >= 0; i--) {
18675
- stack.push({
18676
- node: node.children[i],
18677
- parentId: node.id,
18678
- firstFoundCollapsedBranch: newFirstFoundCollapsedBranch
18679
- });
18680
- }
18738
+ }
18739
+ // Always process children regardless of collapse state
18740
+ // This ensures all nodes are mapped, even those under collapsed branches
18741
+ if (node.children) {
18742
+ for (let i = node.children.length - 1; i >= 0; i--) {
18743
+ stack.push({
18744
+ node: node.children[i],
18745
+ parentId: node.id,
18746
+ firstFoundCollapsedBranch: newFirstFoundCollapsedBranch
18747
+ });
18681
18748
  }
18682
18749
  }
18683
18750
  }
@@ -18906,7 +18973,7 @@
18906
18973
  this.visualsManager.RemoveRegos({
18907
18974
  menuItemId: this.item.id,
18908
18975
  doRemove: false,
18909
- source: "tileset-dispose"
18976
+ source: UPDATE_SOURCES.TILESET_DISPOSE
18910
18977
  });
18911
18978
  this.viewerDateTimeDispose();
18912
18979
  }
@@ -19405,7 +19472,7 @@
19405
19472
  }
19406
19473
  this.visualsManager.RemoveRegos({
19407
19474
  menuItemId: this.item.id,
19408
- source: "osm-tileset-dispose"
19475
+ source: UPDATE_SOURCES.OSM_TILESET_DISPOSE
19409
19476
  });
19410
19477
  this.featureQueue = [];
19411
19478
  }
@@ -33145,10 +33212,11 @@
33145
33212
  }
33146
33213
  }
33147
33214
 
33148
- const VERSION = "5.9.7";
33215
+ const VERSION = "5.9.9";
33149
33216
 
33150
33217
  exports.VERSION = VERSION;
33151
33218
  exports.isOutlineChanged = isOutlineChanged;
33219
+ exports.UPDATE_SOURCES = UPDATE_SOURCES;
33152
33220
  exports.CesiumParabola = CesiumParabola;
33153
33221
  exports.CESIUM_INSPECTOR_KEY = CESIUM_INSPECTOR_KEY;
33154
33222
  exports.CESIUM_TIMELINE_KEY = CESIUM_TIMELINE_KEY;