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.
@@ -1,6 +1,6 @@
1
- import { BruceEvent, Cartes, Entity as Entity$1, ProjectViewTile, Carto, Geometry, MathUtils, LRUCache, Api, Calculator, ClientFile, EntityTag, EntityType, ObjectUtils, Style, DelayQueue, EntityLod, Bounds, ZoomControl, EntityRelationType, ENVIRONMENT, EntityHistoricData, Tileset, EntityCoords, DataLab, EntitySource, MenuItem, EntityRelation, ProgramKey, ProjectView, ProjectViewBookmark, Camera, ProjectViewLegacyTile, EntityAttachment, EntityAttachmentType, EntityAttribute, AbstractApi, Session } from 'bruce-models';
1
+ import { BruceEvent, Cartes, Entity as Entity$1, ProjectViewTile, Carto, Geometry, MathUtils, LRUCache, Api, Calculator, ClientFile, EntityTag, EntityType, ObjectUtils, Style, DelayQueue, EntityLod, Bounds, ZoomControl, EntityRelationType, ENVIRONMENT, EntityHistoricData, Tileset, EntityCoords, DataLab, EntitySource, MenuItem, EntityRelation, ProgramKey, ProjectView, ProjectViewBookmark, Camera, ProjectViewLegacyTile, AbstractApi, Session, EntityAttachment, EntityAttachmentType, EntityAttribute } from 'bruce-models';
2
2
  import * as Cesium from 'cesium';
3
- import { Cartographic, Cartesian2, Math as Math$1, Cartesian3, CallbackProperty, Color, HeightReference, Rectangle, JulianDate, Entity, DistanceDisplayCondition, ClassificationType, ArcType, CornerType, ShadowMode, ConstantProperty, ConstantPositionProperty, HorizontalOrigin, VerticalOrigin, ColorBlendMode, HeadingPitchRoll, Transforms, Model, PolygonHierarchy, PolylineGraphics, ColorMaterialProperty, Primitive, Cesium3DTileFeature, SceneMode, GeoJsonDataSource, Cesium3DTileStyle, HeadingPitchRange, Cesium3DTileColorBlendMode, Ion, KmlDataSource, Quaternion, Matrix3, Matrix4, OrthographicFrustum, EasingFunction, NearFarScalar, SceneTransforms, Cesium3DTileset, IonResource, EllipsoidTerrainProvider, CesiumInspector, defined, ClockRange, EllipsoidGeodesic, sampleTerrainMostDetailed, PolygonPipeline, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, ScreenSpaceEventHandler, ScreenSpaceEventType, BoundingSphere, GeometryInstance, CzmlDataSource, Intersect, Fullscreen } from 'cesium';
3
+ import { Cartographic, Cartesian2, Math as Math$1, Cartesian3, CallbackProperty, Color, HeightReference, Rectangle, JulianDate, Entity, DistanceDisplayCondition, ClassificationType, ArcType, CornerType, ShadowMode, ConstantProperty, ConstantPositionProperty, PolygonHierarchy, PolylineGraphics, ColorMaterialProperty, ColorBlendMode, HeadingPitchRoll, Transforms, Model, HorizontalOrigin, VerticalOrigin, Primitive, Cesium3DTileFeature, SceneMode, GeoJsonDataSource, Cesium3DTileStyle, Cesium3DTileColorBlendMode, HeadingPitchRange, Ion, KmlDataSource, Quaternion, Matrix3, Matrix4, SceneTransforms, OrthographicFrustum, EasingFunction, NearFarScalar, EllipsoidTerrainProvider, CesiumInspector, defined, ClockRange, EllipsoidGeodesic, sampleTerrainMostDetailed, Cesium3DTileset, PolygonPipeline, IonResource, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, BoundingSphere, GeometryInstance, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, ScreenSpaceEventHandler, ScreenSpaceEventType, CzmlDataSource, Intersect, Fullscreen } from 'cesium';
4
4
 
5
5
  const TIME_LAG = 300;
6
6
  const POSITION_CHECK_TIMER = 950;
@@ -1144,19 +1144,18 @@ var CesiumAnimatedProperty;
1144
1144
  */
1145
1145
  class AnimatePositionSeries {
1146
1146
  constructor(params) {
1147
- // Animation state.
1148
- this.currentAnimatedPos = null;
1149
- this.actualTargetPos = null;
1150
- this.animationStartPos = null;
1151
- this.animationStartTime = null;
1152
- this.animationDuration = 350;
1153
- // Cached data for performance.
1147
+ this.currentPos = null;
1148
+ this.currentVelocity = new Cartesian3(0, 0, 0);
1149
+ this.smoothingFactor = 0.5;
1150
+ this.positionHistory = [];
1151
+ this.maxHistorySize = 10;
1152
+ this.averageSpeed = 0;
1153
+ this.averageTimeInterval = 0;
1154
+ this.lastUpdateTime = null;
1154
1155
  this.lastDesiredPosIndex = -1;
1155
1156
  this.lastDesiredNextIndex = -1;
1156
- // Series data for rendering path
1157
1157
  this.lastCalcSeriesPos3d = [];
1158
1158
  this.lastCalcSeriesTime = null;
1159
- // Orientation cache.
1160
1159
  this.lastCalcOrient = null;
1161
1160
  this.lastCalcOrientTime = null;
1162
1161
  this.viewer = params.viewer;
@@ -1165,25 +1164,23 @@ var CesiumAnimatedProperty;
1165
1164
  this.roll = params.roll || 0;
1166
1165
  this.processHeadings();
1167
1166
  this.sortPositions();
1168
- // Initialize animation from starting position if provided.
1169
- if (params.animateFromPos3d) {
1170
- this.animationStartPos = params.animateFromPos3d;
1171
- const currentTime = Date.now();
1172
- const providedTime = params.animateFromPos3dTimeStart || 0;
1173
- // Check if the provided timestamp is stale.
1174
- if (!providedTime || (currentTime - providedTime) >= this.animationDuration) {
1175
- this.animationStartTime = currentTime;
1176
- }
1177
- else {
1178
- this.animationStartTime = providedTime;
1179
- }
1180
- }
1181
1167
  }
1182
1168
  AddPosition(pos) {
1183
1169
  if (!pos || !pos.pos3d || !pos.dateTime) {
1184
1170
  console.warn("Invalid position provided to AnimatePositionSeries.");
1185
1171
  return;
1186
1172
  }
1173
+ const now = Date.now();
1174
+ const posTime = pos.dateTime.getTime();
1175
+ this.positionHistory.push({
1176
+ pos: pos.pos3d.clone(),
1177
+ time: posTime,
1178
+ realTime: now
1179
+ });
1180
+ if (this.positionHistory.length > this.maxHistorySize) {
1181
+ this.positionHistory.shift();
1182
+ }
1183
+ this.analyzeMovementPatterns();
1187
1184
  this.positions.push(pos);
1188
1185
  this.processHeadings();
1189
1186
  this.sortPositions();
@@ -1194,12 +1191,70 @@ var CesiumAnimatedProperty;
1194
1191
  this.roll = roll;
1195
1192
  this.invalidateCache();
1196
1193
  }
1197
- GetAnimateFromDateTime() {
1198
- return this.animationStartTime;
1199
- }
1200
1194
  GetPositions() {
1201
1195
  return this.positions;
1202
1196
  }
1197
+ analyzeMovementPatterns() {
1198
+ if (this.positionHistory.length < 2) {
1199
+ return;
1200
+ }
1201
+ let totalDistance = 0;
1202
+ let totalTimeSpan = 0;
1203
+ let totalRealTimeSpan = 0;
1204
+ let validSegments = 0;
1205
+ for (let i = 1; i < this.positionHistory.length; i++) {
1206
+ const prev = this.positionHistory[i - 1];
1207
+ const curr = this.positionHistory[i];
1208
+ const distance = Cartesian3.distance(prev.pos, curr.pos);
1209
+ const timeSpan = Math.abs(curr.time - prev.time) / 1000.0;
1210
+ const realTimeSpan = Math.abs(curr.realTime - prev.realTime) / 1000.0;
1211
+ if (distance > 0.1 && timeSpan > 0.01) {
1212
+ totalDistance += distance;
1213
+ totalTimeSpan += timeSpan;
1214
+ totalRealTimeSpan += realTimeSpan;
1215
+ validSegments++;
1216
+ }
1217
+ }
1218
+ if (validSegments > 0) {
1219
+ this.averageSpeed = totalDistance / totalTimeSpan;
1220
+ this.averageTimeInterval = totalRealTimeSpan / validSegments;
1221
+ this.averageSpeed = Math.max(0.1, Math.min(this.averageSpeed, 1000));
1222
+ this.averageTimeInterval = Math.max(0.01, Math.min(this.averageTimeInterval, 10));
1223
+ }
1224
+ }
1225
+ calculateExpectedPosition(viewerTimeMs) {
1226
+ const desired = this.calculateDesiredPosition(viewerTimeMs);
1227
+ if (!desired.position) {
1228
+ return { position: null, timeDelta: 0 };
1229
+ }
1230
+ let timeDelta = 0;
1231
+ if (this.positionHistory.length >= 2) {
1232
+ // Calculate how far behind we are based on viewer time vs real time.
1233
+ const now = Date.now();
1234
+ const timeDiffFromNow = (viewerTimeMs - now) / 1000.0;
1235
+ // Negative means we're behind, positive means ahead.
1236
+ timeDelta = timeDiffFromNow;
1237
+ }
1238
+ return { position: desired.position, timeDelta };
1239
+ }
1240
+ calculateAutonomousSpeed(distanceToTarget, timeDelta) {
1241
+ let baseSpeed = this.averageSpeed || 50;
1242
+ let speedMultiplier = 1.0;
1243
+ // Calculate speed multiplier based on distance and time lag.
1244
+ if (distanceToTarget > 50) {
1245
+ speedMultiplier *= Math.min(100.0, distanceToTarget / 10.0);
1246
+ }
1247
+ else if (distanceToTarget > 10) {
1248
+ const distanceMultiplier = Math.min(20.0, 1.0 + (distanceToTarget / 5.0));
1249
+ speedMultiplier *= distanceMultiplier;
1250
+ }
1251
+ else if (Math.abs(timeDelta) > 0.1) {
1252
+ const timeLagMultiplier = Math.max(0.1, 1.0 - (timeDelta * 5.0));
1253
+ speedMultiplier *= Math.min(50.0, timeLagMultiplier);
1254
+ }
1255
+ // Minimum 10% speed.
1256
+ return Math.max(baseSpeed * 0.1, baseSpeed * speedMultiplier);
1257
+ }
1203
1258
  sortPositions() {
1204
1259
  if (this.positions.length > 0) {
1205
1260
  this.positions.sort((a, b) => a.dateTime.getTime() - b.dateTime.getTime());
@@ -1209,10 +1264,6 @@ var CesiumAnimatedProperty;
1209
1264
  this.lastCalcSeriesTime = null;
1210
1265
  this.lastCalcOrientTime = null;
1211
1266
  }
1212
- /**
1213
- * Pre-process headings in the series.
1214
- * If all are null or 0, then we assume all are null.
1215
- */
1216
1267
  processHeadings() {
1217
1268
  if (!this.positions || this.positions.length === 0) {
1218
1269
  return;
@@ -1234,15 +1285,10 @@ var CesiumAnimatedProperty;
1234
1285
  easeInOutQuad(t) {
1235
1286
  return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
1236
1287
  }
1237
- /**
1238
- * Calculate the desired position based on current time without any animation smoothing.
1239
- */
1240
1288
  calculateDesiredPosition(timeMs) {
1241
- // No positions available.
1242
1289
  if (!this.positions || this.positions.length === 0) {
1243
1290
  return { position: null, lastIndex: -1, nextIndex: -1 };
1244
1291
  }
1245
- // Only one position..
1246
1292
  if (this.positions.length === 1) {
1247
1293
  return {
1248
1294
  position: this.positions[0].pos3d || null,
@@ -1250,7 +1296,6 @@ var CesiumAnimatedProperty;
1250
1296
  nextIndex: 0
1251
1297
  };
1252
1298
  }
1253
- // Before first position - use first two positions for orientation.
1254
1299
  if (timeMs <= this.positions[0].dateTime.getTime()) {
1255
1300
  return {
1256
1301
  position: this.positions[0].pos3d || null,
@@ -1258,7 +1303,6 @@ var CesiumAnimatedProperty;
1258
1303
  nextIndex: Math.min(1, this.positions.length - 1)
1259
1304
  };
1260
1305
  }
1261
- // After last position - use last two positions for orientation.
1262
1306
  const lastIdx = this.positions.length - 1;
1263
1307
  if (timeMs >= this.positions[lastIdx].dateTime.getTime()) {
1264
1308
  return {
@@ -1267,12 +1311,10 @@ var CesiumAnimatedProperty;
1267
1311
  nextIndex: lastIdx
1268
1312
  };
1269
1313
  }
1270
- // Find positions to interpolate between.
1271
1314
  for (let i = 0; i < this.positions.length - 1; i++) {
1272
1315
  const current = this.positions[i];
1273
1316
  const next = this.positions[i + 1];
1274
1317
  if (timeMs >= current.dateTime.getTime() && timeMs < next.dateTime.getTime()) {
1275
- // Exact match on current position - still use current and next for orientation.
1276
1318
  if (timeMs === current.dateTime.getTime()) {
1277
1319
  return {
1278
1320
  position: current.pos3d || null,
@@ -1280,7 +1322,6 @@ var CesiumAnimatedProperty;
1280
1322
  nextIndex: i + 1
1281
1323
  };
1282
1324
  }
1283
- // Interpolate between current and next.
1284
1325
  if (!current.pos3d || !next.pos3d) {
1285
1326
  return {
1286
1327
  position: current.pos3d || next.pos3d || null,
@@ -1307,16 +1348,12 @@ var CesiumAnimatedProperty;
1307
1348
  }
1308
1349
  }
1309
1350
  }
1310
- // Fallback to last position with previous position for orientation.
1311
1351
  return {
1312
1352
  position: this.positions[lastIdx].pos3d || null,
1313
1353
  lastIndex: Math.max(0, lastIdx - 1),
1314
1354
  nextIndex: lastIdx
1315
1355
  };
1316
1356
  }
1317
- /**
1318
- * Main method to get the current position based on time.
1319
- */
1320
1357
  GetValue() {
1321
1358
  let viewerTime = this.viewer.scene.lastRenderTime;
1322
1359
  if (!viewerTime) {
@@ -1324,88 +1361,46 @@ var CesiumAnimatedProperty;
1324
1361
  }
1325
1362
  const viewerTimeMs = JulianDate.toDate(viewerTime).getTime();
1326
1363
  const currentRealTimeMs = Date.now();
1327
- // Calculate the desired position based on time.
1328
- const desired = this.calculateDesiredPosition(viewerTimeMs);
1329
- if (!desired.position) {
1330
- this.currentAnimatedPos = null;
1331
- this.actualTargetPos = null;
1364
+ const expected = this.calculateExpectedPosition(viewerTimeMs);
1365
+ if (!expected.position) {
1366
+ this.currentPos = null;
1367
+ this.currentVelocity = new Cartesian3(0, 0, 0);
1332
1368
  return new Cartesian3();
1333
1369
  }
1370
+ const desired = this.calculateDesiredPosition(viewerTimeMs);
1334
1371
  this.lastDesiredPosIndex = desired.lastIndex;
1335
1372
  this.lastDesiredNextIndex = desired.nextIndex;
1336
- // Determine the desired position without interpolation.
1337
- let discreteTarget;
1338
- if (desired.lastIndex === desired.nextIndex) {
1339
- discreteTarget = this.positions[desired.lastIndex].pos3d;
1340
- }
1341
- // Use the next position as the target.
1342
- else {
1343
- discreteTarget = this.positions[desired.nextIndex].pos3d;
1344
- }
1345
- // Check if we have a new actual target
1346
- const actualTargetChanged = !this.actualTargetPos || !Cartesian3.equals(this.actualTargetPos, discreteTarget);
1347
- // First time or no previous position - start here.
1348
- if (!this.currentAnimatedPos) {
1349
- if (this.animationStartPos) {
1350
- this.actualTargetPos = discreteTarget;
1351
- const progress = this.getAnimationProgress(currentRealTimeMs, this.animationStartTime);
1352
- // Animation from start position complete.
1353
- if (progress >= 1.0) {
1354
- this.currentAnimatedPos = desired.position;
1355
- this.animationStartPos = null;
1356
- this.animationStartTime = null;
1357
- return desired.position;
1358
- }
1359
- // Still animating from initial position.
1360
- else {
1361
- const easedProgress = this.easeInOutQuad(progress);
1362
- this.currentAnimatedPos = Cartesian3.lerp(this.animationStartPos, desired.position, easedProgress, new Cartesian3());
1363
- return this.currentAnimatedPos;
1364
- }
1365
- }
1366
- // No initial animation needed.
1367
- else {
1368
- this.currentAnimatedPos = desired.position;
1369
- this.actualTargetPos = discreteTarget;
1370
- return desired.position;
1371
- }
1372
- }
1373
- // Target changed.
1374
- if (actualTargetChanged) {
1375
- this.animationStartPos = this.currentAnimatedPos;
1376
- this.animationStartTime = currentRealTimeMs;
1377
- this.actualTargetPos = discreteTarget;
1378
- }
1379
- // Continue or start animation to target if we have an active animation.
1380
- if (this.animationStartPos && this.animationStartTime) {
1381
- const progress = this.getAnimationProgress(currentRealTimeMs, this.animationStartTime);
1382
- // Animation complete.
1383
- if (progress >= 1.0) {
1384
- this.currentAnimatedPos = desired.position;
1385
- this.animationStartPos = null;
1386
- this.animationStartTime = null;
1387
- return desired.position;
1388
- }
1389
- // Continue animation to interpolated position.
1390
- else {
1391
- const easedProgress = this.easeInOutQuad(progress);
1392
- this.currentAnimatedPos = Cartesian3.lerp(this.animationStartPos, desired.position, easedProgress, new Cartesian3());
1393
- return this.currentAnimatedPos;
1394
- }
1395
- }
1396
- // No active animation - follow interpolated path directly.
1397
- this.currentAnimatedPos = desired.position;
1398
- return desired.position;
1399
- }
1400
- getAnimationProgress(currentTime, startTime) {
1401
- const elapsed = currentTime - startTime;
1402
- return Math.min(elapsed / this.animationDuration, 1.0);
1373
+ if (!this.currentPos) {
1374
+ this.currentPos = expected.position.clone();
1375
+ this.currentVelocity = new Cartesian3(0, 0, 0);
1376
+ this.lastUpdateTime = currentRealTimeMs;
1377
+ return this.currentPos;
1378
+ }
1379
+ const deltaTime = this.lastUpdateTime ? (currentRealTimeMs - this.lastUpdateTime) / 1000.0 : 0.016;
1380
+ this.lastUpdateTime = currentRealTimeMs;
1381
+ const clampedDeltaTime = Math.min(deltaTime, 0.1);
1382
+ const targetDistance = Cartesian3.distance(this.currentPos, expected.position);
1383
+ if (targetDistance < 0.5) {
1384
+ this.currentPos = expected.position.clone();
1385
+ this.currentVelocity = new Cartesian3(0, 0, 0);
1386
+ return this.currentPos;
1387
+ }
1388
+ // Check if we're really far behind and need to teleport.
1389
+ if (targetDistance > 500) {
1390
+ this.currentPos = expected.position.clone();
1391
+ this.currentVelocity = new Cartesian3(0, 0, 0);
1392
+ return this.currentPos;
1393
+ }
1394
+ const direction = Cartesian3.subtract(expected.position, this.currentPos, new Cartesian3());
1395
+ Cartesian3.normalize(direction, direction);
1396
+ const targetSpeed = this.calculateAutonomousSpeed(targetDistance, expected.timeDelta);
1397
+ const targetVelocity = Cartesian3.multiplyByScalar(direction, targetSpeed, new Cartesian3());
1398
+ this.currentVelocity = Cartesian3.lerp(this.currentVelocity, targetVelocity, this.smoothingFactor, new Cartesian3());
1399
+ const velocityDelta = Cartesian3.multiplyByScalar(this.currentVelocity, clampedDeltaTime, new Cartesian3());
1400
+ this.currentPos = Cartesian3.add(this.currentPos, velocityDelta, new Cartesian3());
1401
+ return this.currentPos;
1403
1402
  }
1404
- /**
1405
- * Returns a series of positions to use for rendering the path.
1406
- */
1407
1403
  GetSeries() {
1408
- // Update at 30 fps.
1409
1404
  let doUpdate = this.lastCalcSeriesTime == null;
1410
1405
  if (!doUpdate && this.lastCalcSeriesTime && (new Date().getTime() - this.lastCalcSeriesTime) > 1000 / 30) {
1411
1406
  doUpdate = true;
@@ -1413,33 +1408,27 @@ var CesiumAnimatedProperty;
1413
1408
  if (!doUpdate) {
1414
1409
  return this.lastCalcSeriesPos3d;
1415
1410
  }
1416
- // Ensure position indices are up-to-date.
1417
1411
  this.GetValue();
1418
1412
  let now = this.viewer.scene.lastRenderTime;
1419
1413
  if (!now) {
1420
1414
  now = this.viewer.clock.currentTime;
1421
1415
  }
1422
1416
  const nowDate = JulianDate.toDate(now);
1423
- // Get total duration.
1424
1417
  if (!this.positions || this.positions.length < 2) {
1425
1418
  this.lastCalcSeriesTime = nowDate.getTime();
1426
1419
  this.lastCalcSeriesPos3d = [];
1427
1420
  return [];
1428
1421
  }
1429
1422
  const totalDuration = this.positions[this.positions.length - 1].dateTime.getTime() - this.positions[0].dateTime.getTime();
1430
- // Percentage of the polyline to be visible before and after each point.
1431
- const visibilityPercentage = 0.05; // 5%
1423
+ const visibilityPercentage = 0.05;
1432
1424
  const visibilityDuration = totalDuration * visibilityPercentage;
1433
- // Gather positions that fall within the visibility duration.
1434
1425
  const newPosses = [];
1435
1426
  for (let i = 0; i < this.positions.length; i++) {
1436
1427
  const pos = this.positions[i];
1437
1428
  if (!pos.pos3d) {
1438
1429
  continue;
1439
1430
  }
1440
- let add = nowDate >= new Date(pos.dateTime.getTime() - visibilityDuration / 2) &&
1441
- nowDate <= new Date(pos.dateTime.getTime() + visibilityDuration / 2);
1442
- // Also include the segment we're currently traversing.
1431
+ let add = nowDate >= new Date(pos.dateTime.getTime() - visibilityDuration / 2) && nowDate <= new Date(pos.dateTime.getTime() + visibilityDuration / 2);
1443
1432
  if (!add && this.lastDesiredPosIndex > -1 && this.lastDesiredNextIndex > -1) {
1444
1433
  add = i >= this.lastDesiredPosIndex && i <= this.lastDesiredNextIndex;
1445
1434
  }
@@ -1451,11 +1440,7 @@ var CesiumAnimatedProperty;
1451
1440
  this.lastCalcSeriesPos3d = newPosses;
1452
1441
  return newPosses;
1453
1442
  }
1454
- /**
1455
- * Returns the orientation based on current position and heading.
1456
- */
1457
1443
  GetOrient() {
1458
- // Update at 30 fps.
1459
1444
  let doUpdate = this.lastCalcOrientTime == null;
1460
1445
  if (!doUpdate && this.lastCalcOrientTime && (new Date().getTime() - this.lastCalcOrientTime) > 1000 / 30) {
1461
1446
  doUpdate = true;
@@ -1463,26 +1448,34 @@ var CesiumAnimatedProperty;
1463
1448
  if (!doUpdate && this.lastCalcOrient) {
1464
1449
  return this.lastCalcOrient;
1465
1450
  }
1466
- // Default quaternion to return if we can't calculate.
1467
1451
  const defaultQuaternion = new Quaternion();
1468
1452
  if (!this.positions || this.positions.length === 0) {
1469
1453
  return defaultQuaternion;
1470
1454
  }
1471
- // Ensure position is up-to-date.
1472
1455
  const currentPosition = this.GetValue();
1473
1456
  if (!currentPosition) {
1474
1457
  return defaultQuaternion;
1475
1458
  }
1476
- let now = this.viewer.scene.lastRenderTime;
1477
- if (!now) {
1478
- now = this.viewer.clock.currentTime;
1479
- }
1480
- const nowTime = JulianDate.toDate(now).getTime();
1481
1459
  try {
1482
- // Get current position indices.
1460
+ const velocityMagnitude = Cartesian3.magnitude(this.currentVelocity);
1461
+ const minimumSpeedForVelocityOrientation = Math.max(1.0, this.averageSpeed * 0.1);
1462
+ if (velocityMagnitude > minimumSpeedForVelocityOrientation) {
1463
+ const normalizedVelocity = Cartesian3.normalize(this.currentVelocity, new Cartesian3());
1464
+ const rotationMatrix = Transforms.rotationMatrixFromPositionVelocity(currentPosition, normalizedVelocity);
1465
+ const quaternion = Quaternion.fromRotationMatrix(rotationMatrix);
1466
+ const hpr = new HeadingPitchRoll(0, Math$1.toRadians(this.pitch), Math$1.toRadians(this.roll));
1467
+ const pitchRollQuaternion = Quaternion.fromHeadingPitchRoll(hpr);
1468
+ this.lastCalcOrient = Quaternion.multiply(quaternion, pitchRollQuaternion, new Quaternion());
1469
+ this.lastCalcOrientTime = Date.now();
1470
+ return this.lastCalcOrient;
1471
+ }
1472
+ let now = this.viewer.scene.lastRenderTime;
1473
+ if (!now) {
1474
+ now = this.viewer.clock.currentTime;
1475
+ }
1476
+ const nowTime = JulianDate.toDate(now).getTime();
1483
1477
  const lastIndex = this.lastDesiredPosIndex;
1484
1478
  const nextIndex = this.lastDesiredNextIndex;
1485
- // Invalid indices.
1486
1479
  if (lastIndex < 0 || nextIndex < 0 ||
1487
1480
  lastIndex >= this.positions.length || nextIndex >= this.positions.length) {
1488
1481
  return this.lastCalcOrient || defaultQuaternion;
@@ -1492,74 +1485,53 @@ var CesiumAnimatedProperty;
1492
1485
  if (!lastPos || !nextPos) {
1493
1486
  return this.lastCalcOrient || defaultQuaternion;
1494
1487
  }
1495
- // Single position case - use its heading if available.
1496
1488
  if (lastIndex === nextIndex) {
1497
1489
  if (lastPos.heading !== null) {
1498
1490
  this.lastCalcOrient = Transforms.headingPitchRollQuaternion(currentPosition, new HeadingPitchRoll(Math$1.toRadians(lastPos.heading), Math$1.toRadians(this.pitch), Math$1.toRadians(this.roll)));
1499
1491
  this.lastCalcOrientTime = Date.now();
1500
1492
  return this.lastCalcOrient;
1501
1493
  }
1502
- // No heading data and single position, return previous or default.
1503
1494
  return this.lastCalcOrient || defaultQuaternion;
1504
1495
  }
1505
- // Two different positions - we can calculate orientation.
1506
- // Use explicit heading values if available.
1507
1496
  if (lastPos.heading !== null && nextPos.heading !== null) {
1508
- // Calculate interpolated heading.
1509
1497
  let deltaHeading = nextPos.heading - lastPos.heading;
1510
- // Handle wrap-around between 359° and 0°.
1511
1498
  if (deltaHeading > 180) {
1512
1499
  deltaHeading -= 360;
1513
1500
  }
1514
1501
  else if (deltaHeading < -180) {
1515
1502
  deltaHeading += 360;
1516
1503
  }
1517
- // Calculate interpolation factor.
1518
1504
  let factor = 0;
1519
1505
  if (lastPos.dateTime.getTime() !== nextPos.dateTime.getTime()) {
1520
- factor = (nowTime - lastPos.dateTime.getTime()) /
1521
- (nextPos.dateTime.getTime() - lastPos.dateTime.getTime());
1506
+ factor = (nowTime - lastPos.dateTime.getTime()) / (nextPos.dateTime.getTime() - lastPos.dateTime.getTime());
1522
1507
  factor = Math.max(0, Math.min(1, factor));
1523
1508
  }
1524
- // Apply easing for smoother rotation.
1525
1509
  factor = this.easeInOutQuad(factor);
1526
- // Calculate final heading.
1527
1510
  let interpolatedHeading = lastPos.heading + factor * deltaHeading;
1528
1511
  interpolatedHeading = (interpolatedHeading + 360) % 360;
1529
- // Create quaternion from heading, pitch, roll.
1530
1512
  this.lastCalcOrient = Transforms.headingPitchRollQuaternion(currentPosition, new HeadingPitchRoll(Math$1.toRadians(interpolatedHeading), Math$1.toRadians(this.pitch), Math$1.toRadians(this.roll)));
1531
1513
  }
1532
- // Calculate heading from position changes if heading data not available.
1533
1514
  else {
1534
1515
  if (!lastPos.pos3d || !nextPos.pos3d) {
1535
1516
  return this.lastCalcOrient || defaultQuaternion;
1536
1517
  }
1537
- // Flatten positions to avoid altitude-related heading changes.
1538
1518
  const adjustedPointPrev = Cartographic.fromCartesian(lastPos.pos3d);
1539
1519
  const adjustedPos3dPrev = Cartesian3.fromRadians(adjustedPointPrev.longitude, adjustedPointPrev.latitude, 0);
1540
1520
  const adjustedPointNext = Cartographic.fromCartesian(nextPos.pos3d);
1541
1521
  const adjustedPos3dNext = Cartesian3.fromRadians(adjustedPointNext.longitude, adjustedPointNext.latitude, 0);
1542
- // Skip if positions are too close (less than 5cm).
1543
1522
  const distance = Cartesian3.distance(adjustedPos3dPrev, adjustedPos3dNext);
1544
1523
  if (distance < 0.05) {
1545
1524
  return this.lastCalcOrient || defaultQuaternion;
1546
1525
  }
1547
- // Calculate direction vector.
1548
1526
  const direction = Cartesian3.subtract(adjustedPos3dNext, adjustedPos3dPrev, new Cartesian3());
1549
- // Skip if no movement.
1550
1527
  if (direction.x === 0 && direction.y === 0 && direction.z === 0) {
1551
1528
  return this.lastCalcOrient || defaultQuaternion;
1552
1529
  }
1553
- // Normalize the direction vector.
1554
1530
  Cartesian3.normalize(direction, direction);
1555
- // Calculate rotation based on movement direction.
1556
1531
  const rotationMatrix = Transforms.rotationMatrixFromPositionVelocity(currentPosition, direction);
1557
- // Convert to quaternion.
1558
1532
  const quaternion = Quaternion.fromRotationMatrix(rotationMatrix);
1559
- // Add pitch and roll adjustments.
1560
1533
  const hpr = new HeadingPitchRoll(0, Math$1.toRadians(this.pitch), Math$1.toRadians(this.roll));
1561
1534
  const pitchRollQuaternion = Quaternion.fromHeadingPitchRoll(hpr);
1562
- // Combine quaternions.
1563
1535
  this.lastCalcOrient = Quaternion.multiply(quaternion, pitchRollQuaternion, new Quaternion());
1564
1536
  }
1565
1537
  }
@@ -1570,6 +1542,52 @@ var CesiumAnimatedProperty;
1570
1542
  this.lastCalcOrientTime = Date.now();
1571
1543
  return this.lastCalcOrient;
1572
1544
  }
1545
+ GetCurrentVelocity() {
1546
+ return this.currentVelocity ? this.currentVelocity.clone() : new Cartesian3(0, 0, 0);
1547
+ }
1548
+ SupplementSeries(newSeries) {
1549
+ if (!newSeries || newSeries.length === 0) {
1550
+ return;
1551
+ }
1552
+ for (const pos of newSeries) {
1553
+ if (!pos || !pos.pos3d || !pos.dateTime) {
1554
+ continue;
1555
+ }
1556
+ const existingIndex = this.positions.findIndex(p => p.dateTime.getTime() === pos.dateTime.getTime());
1557
+ if (existingIndex >= 0) {
1558
+ this.positions[existingIndex] = pos;
1559
+ }
1560
+ else {
1561
+ this.AddPosition(pos);
1562
+ }
1563
+ }
1564
+ }
1565
+ UpdatePositionForDateTime(pos3d, dateTime, heading) {
1566
+ if (!pos3d || !dateTime) {
1567
+ return;
1568
+ }
1569
+ const existingIndex = this.positions.findIndex(p => p.dateTime.getTime() === dateTime.getTime());
1570
+ const newPos = {
1571
+ pos3d: pos3d,
1572
+ dateTime: dateTime,
1573
+ heading: heading !== undefined ? heading : null
1574
+ };
1575
+ if (existingIndex >= 0) {
1576
+ this.positions[existingIndex] = newPos;
1577
+ this.processHeadings();
1578
+ this.sortPositions();
1579
+ this.invalidateCache();
1580
+ }
1581
+ else {
1582
+ this.AddPosition(newPos);
1583
+ }
1584
+ }
1585
+ HasPositionForDateTime(dateTime) {
1586
+ return this.positions.some(p => p.dateTime.getTime() === dateTime.getTime());
1587
+ }
1588
+ GetPositionCount() {
1589
+ return this.positions.length;
1590
+ }
1573
1591
  }
1574
1592
  CesiumAnimatedProperty.AnimatePositionSeries = AnimatePositionSeries;
1575
1593
  function GetSeriesPossesForHistoricEntity(viewer, dataHeightRef, heightRef, historic) {
@@ -5452,7 +5470,7 @@ var EntityRenderEnginePoint;
5452
5470
  * @returns
5453
5471
  */
5454
5472
  async function Render(params) {
5455
- var _a, _b, _c, _d;
5473
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
5456
5474
  const entity = params.entity;
5457
5475
  if (!params.entityHistoric) {
5458
5476
  params.entityHistoric = [];
@@ -5653,39 +5671,69 @@ var EntityRenderEnginePoint;
5653
5671
  // Unset width/height.
5654
5672
  cEntity.billboard.width = undefined;
5655
5673
  cEntity.billboard.height = undefined;
5674
+ const pos3d = EntityUtils.GetPos({
5675
+ viewer: params.viewer,
5676
+ entity,
5677
+ recordHeightRef: heightRef,
5678
+ returnHeightRef: heightRef,
5679
+ allowRendered: false
5680
+ });
5656
5681
  const prevPos3d = GetCValue(params.viewer, cEntity.position);
5657
- let prevStartTime = null;
5658
5682
  if (cEntity.position && cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"]) {
5659
- prevStartTime = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"].GetAnimateFromDateTime();
5660
- }
5661
- // If we have a series of time-based positions then we'll animate as time changes.
5662
- const series = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
5663
- if (series.length > 1) {
5664
- animatePosition = new CesiumAnimatedProperty.AnimatePositionSeries({
5665
- posses: series,
5666
- viewer: params.viewer,
5667
- animateFromPos3d: prevPos3d,
5668
- animateFromPos3dTimeStart: prevStartTime
5669
- });
5670
- cEntity.position = new CallbackProperty(() => animatePosition.GetValue(), false);
5671
- cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
5683
+ animatePosition = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"];
5684
+ const animateSeries = animatePosition;
5685
+ const series = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
5686
+ if (series.length) {
5687
+ animateSeries.SupplementSeries(series);
5688
+ }
5689
+ 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;
5690
+ const dateTime = dateTimeStr ? new Date(dateTimeStr) : null;
5691
+ if (dateTime) {
5692
+ animateSeries.UpdatePositionForDateTime(pos3d, dateTime);
5693
+ }
5694
+ // We don't have a date-stamped incoming position.
5695
+ if (!dateTime && !series.length) {
5696
+ animatePosition = null;
5697
+ }
5672
5698
  }
5673
- else {
5674
- const pos3d = EntityUtils.GetPos({
5675
- viewer: params.viewer,
5676
- entity,
5677
- recordHeightRef: heightRef,
5678
- returnHeightRef: heightRef,
5679
- allowRendered: false
5680
- });
5681
- if (!prevPos3d || !Cartesian3.equals(prevPos3d, pos3d)) {
5682
- animatePosition = new CesiumAnimatedProperty.AnimatePosition({
5683
- durationMs: 200,
5684
- targetPos3d: pos3d,
5685
- viewer: params.viewer,
5686
- startPos3d: prevPos3d
5699
+ if (!animatePosition) {
5700
+ // If we've loaded a set of series positions then we'll animate through them.
5701
+ const series = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
5702
+ if (series.length > 1) {
5703
+ animatePosition = new CesiumAnimatedProperty.AnimatePositionSeries({
5704
+ posses: series,
5705
+ viewer: params.viewer
5687
5706
  });
5688
5707
  cEntity.position = new CallbackProperty(() => animatePosition.GetValue(), false);
5708
+ cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
5709
+ }
5710
+ else {
5711
+ 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;
5712
+ const dateTime = dateTimeStr ? new Date(dateTimeStr) : null;
5713
+ const posChanged = !prevPos3d || !Cartesian3.equals(prevPos3d, pos3d);
5714
+ if (posChanged) {
5715
+ let posses = [];
5716
+ const isLive = ViewUtils.GetTimeDetails({ viewer: params.viewer }).isLive;
5717
+ if (prevPos3d && isLive) {
5718
+ posses.push({
5719
+ pos3d: prevPos3d,
5720
+ // Guess so that we can determine a direction of movement right away :)
5721
+ dateTime: new Date(dateTime.getTime() - 1000),
5722
+ heading: null
5723
+ });
5724
+ }
5725
+ posses.push({
5726
+ pos3d: pos3d,
5727
+ dateTime: dateTime,
5728
+ heading: null
5729
+ });
5730
+ animatePosition = new CesiumAnimatedProperty.AnimatePositionSeries({
5731
+ posses: posses,
5732
+ viewer: params.viewer
5733
+ });
5734
+ cEntity.position = new CallbackProperty(() => animatePosition.GetValue(), false);
5735
+ cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
5736
+ }
5689
5737
  }
5690
5738
  }
5691
5739
  // We'll use "SetDefaultColor" to updating the internal reference and to allow for an animation.
@@ -5984,39 +6032,52 @@ var EntityRenderEnginePoint;
5984
6032
  cEntity.billboard.heightReference = new ConstantProperty(heightRef);
5985
6033
  cEntity.billboard.distanceDisplayCondition = new ConstantProperty(EntityRenderEngine.GetDisplayCondition(params.minDistance, params.maxDistance));
5986
6034
  cEntity.billboard.disableDepthTestDistance = new ConstantProperty(disableDepthTest ? Number.POSITIVE_INFINITY : undefined);
6035
+ const pos3d = EntityUtils.GetPos({
6036
+ viewer: params.viewer,
6037
+ entity,
6038
+ recordHeightRef: heightRef,
6039
+ returnHeightRef: heightRef,
6040
+ allowRendered: false
6041
+ });
5987
6042
  const prevPos3d = GetCValue(params.viewer, cEntity.position);
5988
- let prevStartTime = null;
5989
6043
  if (cEntity.position && cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"]) {
5990
- prevStartTime = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"].GetAnimateFromDateTime();
5991
- }
5992
- // If we have a series of time-based positions then we'll animate as time changes.
5993
- const series = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
5994
- if (series.length > 1) {
5995
- animatePosition = new CesiumAnimatedProperty.AnimatePositionSeries({
5996
- posses: series,
5997
- viewer: params.viewer,
5998
- animateFromPos3d: prevPos3d,
5999
- animateFromPos3dTimeStart: prevStartTime
6000
- });
6001
- cEntity.position = new CallbackProperty(() => animatePosition.GetValue(), false);
6002
- cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
6003
- }
6004
- else {
6005
- const pos3d = EntityUtils.GetPos({
6006
- viewer: params.viewer,
6007
- entity,
6008
- recordHeightRef: heightRef,
6009
- returnHeightRef: heightRef,
6010
- allowRendered: false
6011
- });
6012
- if (!prevPos3d || !Cartesian3.equals(prevPos3d, pos3d)) {
6013
- animatePosition = new CesiumAnimatedProperty.AnimatePosition({
6014
- durationMs: 200,
6015
- targetPos3d: pos3d,
6016
- viewer: params.viewer,
6017
- startPos3d: prevPos3d
6044
+ animatePosition = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"];
6045
+ const animateSeries = animatePosition;
6046
+ const series = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
6047
+ if (series.length) {
6048
+ animateSeries.SupplementSeries(series);
6049
+ }
6050
+ 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;
6051
+ const dateTime = dateTimeStr ? new Date(dateTimeStr) : null;
6052
+ if (dateTime) {
6053
+ animateSeries.UpdatePositionForDateTime(pos3d, dateTime);
6054
+ }
6055
+ // We don't have a date-stamped incoming position.
6056
+ if (!dateTime && !series.length) {
6057
+ animatePosition = null;
6058
+ }
6059
+ }
6060
+ if (!animatePosition) {
6061
+ // If we have a series of time-based positions then we'll animate as time changes.
6062
+ const series = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
6063
+ if (series.length > 1) {
6064
+ animatePosition = new CesiumAnimatedProperty.AnimatePositionSeries({
6065
+ posses: series,
6066
+ viewer: params.viewer
6018
6067
  });
6019
6068
  cEntity.position = new CallbackProperty(() => animatePosition.GetValue(), false);
6069
+ cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
6070
+ }
6071
+ else {
6072
+ if (!prevPos3d || !Cartesian3.equals(prevPos3d, pos3d)) {
6073
+ animatePosition = new CesiumAnimatedProperty.AnimatePosition({
6074
+ durationMs: 200,
6075
+ targetPos3d: pos3d,
6076
+ viewer: params.viewer,
6077
+ startPos3d: prevPos3d
6078
+ });
6079
+ cEntity.position = new CallbackProperty(() => animatePosition.GetValue(), false);
6080
+ }
6020
6081
  }
6021
6082
  }
6022
6083
  // We'll use "SetDefaultColor" to updating the internal reference and to allow for an animation.
@@ -6035,7 +6096,7 @@ var EntityRenderEnginePoint;
6035
6096
  // Generate a polyline 'track' for the historic data.
6036
6097
  // We do this for historic data that exists and is moving.
6037
6098
  if (shouldShowTrack && animatePosition && animatePosition instanceof CesiumAnimatedProperty.AnimatePositionSeries && animatePosition.GetSeries) {
6038
- const lStyle = (_d = (_c = params.fullStyle) === null || _c === void 0 ? void 0 : _c.polylineStyle) !== null && _d !== void 0 ? _d : {};
6099
+ const lStyle = (_k = (_j = params.fullStyle) === null || _j === void 0 ? void 0 : _j.polylineStyle) !== null && _k !== void 0 ? _k : {};
6039
6100
  const bColor = lStyle.lineColor ? Calculator.GetColor(lStyle.lineColor, entity, params.tags) : null;
6040
6101
  const cColor = bColor ? ColorToCColor(bColor) : Color.fromCssColorString("rgba(255, 193, 7, 0.8)");
6041
6102
  let width = lStyle.lineWidth ? EnsureNumber(Calculator.GetNumber(lStyle.lineWidth, entity, params.tags)) : 2;
@@ -7055,7 +7116,7 @@ var EntityRenderEngineModel3d;
7055
7116
  * @returns
7056
7117
  */
7057
7118
  function Render(params) {
7058
- var _a, _b, _c, _d;
7119
+ var _a, _b, _c, _d, _e, _f;
7059
7120
  const entity = params.entity;
7060
7121
  if (!params.entityHistoric) {
7061
7122
  params.entityHistoric = [];
@@ -7247,40 +7308,41 @@ var EntityRenderEngineModel3d;
7247
7308
  cEntity.model.colorBlendAmount = new ConstantProperty(blendAmount);
7248
7309
  cEntity.model.colorBlendMode = new ConstantProperty(blendMode);
7249
7310
  cEntity.model.distanceDisplayCondition = new ConstantProperty(EntityRenderEngine.GetDisplayCondition(params.minDistance, params.maxDistance));
7250
- let prevStartTime = null;
7251
7311
  if (cEntity.position && cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"]) {
7252
- prevStartTime = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"].GetAnimateFromDateTime();
7253
- }
7254
- // If we've loaded a set of series positions then we'll animate through them.
7255
- const series = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
7256
- if (series.length > 1) {
7257
- animatePosition = new CesiumAnimatedProperty.AnimatePositionSeries({
7258
- posses: series,
7259
- viewer: params.viewer,
7260
- pitch: pitch,
7261
- roll: roll,
7262
- animateFromPos3d: prevPos3d,
7263
- animateFromPos3dTimeStart: prevStartTime
7264
- });
7265
- cEntity.position = new CallbackProperty(() => animatePosition.GetValue(), false);
7266
- cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
7267
- }
7268
- else {
7312
+ animatePosition = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"];
7313
+ const animateSeries = animatePosition;
7314
+ const series = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
7315
+ if (series.length) {
7316
+ animateSeries.SupplementSeries(series);
7317
+ }
7269
7318
  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;
7270
7319
  const dateTime = dateTimeStr ? new Date(dateTimeStr) : null;
7271
- const posChanged = !prevPos3d || !Cartesian3.equals(prevPos3d, pos3d);
7272
- if (posChanged) {
7273
- if (cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"]) {
7274
- const prevAnimatePosition = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"];
7275
- animatePosition = prevAnimatePosition;
7276
- animatePosition.AddPosition({
7277
- pos3d: pos3d,
7278
- dateTime: dateTime,
7279
- heading: !EnsureNumber(transform === null || transform === void 0 ? void 0 : transform.heading) ? null : heading
7280
- });
7281
- animatePosition.UpdatePitchRoll(pitch, roll);
7282
- }
7283
- else {
7320
+ if (dateTime) {
7321
+ animateSeries.UpdatePositionForDateTime(pos3d, dateTime, !EnsureNumber(transform === null || transform === void 0 ? void 0 : transform.heading) ? null : heading);
7322
+ }
7323
+ // We don't have a date-stamped incoming position.
7324
+ if (!dateTime && !series.length) {
7325
+ animatePosition = null;
7326
+ }
7327
+ }
7328
+ if (!animatePosition) {
7329
+ // If we've loaded a set of series positions then we'll animate through them.
7330
+ const series = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
7331
+ if (series.length > 1) {
7332
+ animatePosition = new CesiumAnimatedProperty.AnimatePositionSeries({
7333
+ posses: series,
7334
+ viewer: params.viewer,
7335
+ pitch: pitch,
7336
+ roll: roll
7337
+ });
7338
+ cEntity.position = new CallbackProperty(() => animatePosition.GetValue(), false);
7339
+ cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
7340
+ }
7341
+ else {
7342
+ 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;
7343
+ const dateTime = dateTimeStr ? new Date(dateTimeStr) : null;
7344
+ const posChanged = !prevPos3d || !Cartesian3.equals(prevPos3d, pos3d);
7345
+ if (posChanged) {
7284
7346
  let posses = [];
7285
7347
  const isLive = ViewUtils.GetTimeDetails({ viewer: params.viewer }).isLive;
7286
7348
  if (prevPos3d && isLive) {
@@ -7300,9 +7362,7 @@ var EntityRenderEngineModel3d;
7300
7362
  posses: posses,
7301
7363
  viewer: params.viewer,
7302
7364
  pitch: pitch,
7303
- roll: roll,
7304
- animateFromPos3d: prevPos3d && !isLive ? prevPos3d : null,
7305
- animateFromPos3dTimeStart: null
7365
+ roll: roll
7306
7366
  });
7307
7367
  cEntity.position = new CallbackProperty(() => animatePosition.GetValue(), false);
7308
7368
  cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
@@ -7455,7 +7515,7 @@ var EntityRenderEngineModel3d;
7455
7515
  // Generate a polyline 'track' for the historic data.
7456
7516
  // We do this for historic data that exists and is moving.
7457
7517
  if (shouldShowTrack && animatePosition && animatePosition instanceof CesiumAnimatedProperty.AnimatePositionSeries && animatePosition.GetSeries) {
7458
- const lStyle = (_d = (_c = params.fullStyle) === null || _c === void 0 ? void 0 : _c.polylineStyle) !== null && _d !== void 0 ? _d : {};
7518
+ const lStyle = (_f = (_e = params.fullStyle) === null || _e === void 0 ? void 0 : _e.polylineStyle) !== null && _f !== void 0 ? _f : {};
7459
7519
  const bColor = lStyle.lineColor ? Calculator.GetColor(lStyle.lineColor, entity, params.tags) : null;
7460
7520
  const cColor = bColor ? ColorToCColor(bColor) : Color.fromCssColorString("rgba(255, 193, 7, 0.8)");
7461
7521
  let width = lStyle.lineWidth ? EnsureNumber(Calculator.GetNumber(lStyle.lineWidth, entity, params.tags)) : 2;
@@ -10684,6 +10744,12 @@ var CesiumAnimatedInOut;
10684
10744
  CesiumAnimatedInOut.AnimateOut = AnimateOut;
10685
10745
  })(CesiumAnimatedInOut || (CesiumAnimatedInOut = {}));
10686
10746
 
10747
+ // Constants for update source types
10748
+ const UPDATE_SOURCES = {
10749
+ TREE_CASCADE: "tree-cascade",
10750
+ TILESET_DISPOSE: "tileset-dispose",
10751
+ OSM_TILESET_DISPOSE: "osm-tileset-dispose"
10752
+ };
10687
10753
  /**
10688
10754
  * Returns if a given visual is alive and in the scene.
10689
10755
  * @param viewer
@@ -11159,7 +11225,7 @@ var VisualsRegister;
11159
11225
  });
11160
11226
  }
11161
11227
  // Trigger update event.
11162
- if (this.onUpdate && source !== "tree-cascade") {
11228
+ if (this.onUpdate && source !== UPDATE_SOURCES.TREE_CASCADE) {
11163
11229
  const update = {
11164
11230
  type: EVisualUpdateType.Update,
11165
11231
  entityId: state.entityId
@@ -11250,7 +11316,7 @@ var VisualsRegister;
11250
11316
  }
11251
11317
  }
11252
11318
  // Trigger update event.
11253
- if (this.onUpdate && source !== "tree-cascade") {
11319
+ if (this.onUpdate && source !== UPDATE_SOURCES.TREE_CASCADE) {
11254
11320
  const keys = Object.keys(states);
11255
11321
  for (let i = 0; i < keys.length; i++) {
11256
11322
  const key = keys[i];
@@ -11532,7 +11598,7 @@ var VisualsRegister;
11532
11598
  if (source) {
11533
11599
  update.source = source;
11534
11600
  }
11535
- if (source !== "tree-cascade") {
11601
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
11536
11602
  (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(update);
11537
11603
  }
11538
11604
  }
@@ -11698,7 +11764,7 @@ var VisualsRegister;
11698
11764
  if (source) {
11699
11765
  update.source = source;
11700
11766
  }
11701
- if (source !== "tree-cascade") {
11767
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
11702
11768
  (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(update);
11703
11769
  }
11704
11770
  }
@@ -11744,7 +11810,7 @@ var VisualsRegister;
11744
11810
  if (params === null || params === void 0 ? void 0 : params.source) {
11745
11811
  update.source = params.source;
11746
11812
  }
11747
- if ((params === null || params === void 0 ? void 0 : params.source) !== "tree-cascade") {
11813
+ if ((params === null || params === void 0 ? void 0 : params.source) !== UPDATE_SOURCES.TREE_CASCADE) {
11748
11814
  (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(update);
11749
11815
  }
11750
11816
  if ((params === null || params === void 0 ? void 0 : params.requestRender) != false) {
@@ -11894,7 +11960,7 @@ var VisualsRegister;
11894
11960
  if (source) {
11895
11961
  update.source = source;
11896
11962
  }
11897
- if (source !== "tree-cascade") {
11963
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
11898
11964
  (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(update);
11899
11965
  }
11900
11966
  }
@@ -12006,7 +12072,7 @@ var VisualsRegister;
12006
12072
  if (params === null || params === void 0 ? void 0 : params.source) {
12007
12073
  update.source = params.source;
12008
12074
  }
12009
- if ((params === null || params === void 0 ? void 0 : params.source) !== "tree-cascade") {
12075
+ if ((params === null || params === void 0 ? void 0 : params.source) !== UPDATE_SOURCES.TREE_CASCADE) {
12010
12076
  (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(update);
12011
12077
  }
12012
12078
  }
@@ -12046,7 +12112,7 @@ var VisualsRegister;
12046
12112
  if (source) {
12047
12113
  update.source = source;
12048
12114
  }
12049
- if (source !== "tree-cascade") {
12115
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
12050
12116
  (_b = this.onUpdate) === null || _b === void 0 ? void 0 : _b.Trigger(update);
12051
12117
  }
12052
12118
  if (requestRender != false) {
@@ -12168,7 +12234,7 @@ var VisualsRegister;
12168
12234
  if (source) {
12169
12235
  update.source = source;
12170
12236
  }
12171
- if (source !== "tree-cascade") {
12237
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
12172
12238
  (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(update);
12173
12239
  }
12174
12240
  if (doesInclude) {
@@ -12207,7 +12273,7 @@ var VisualsRegister;
12207
12273
  if (source) {
12208
12274
  update.source = source;
12209
12275
  }
12210
- if (source !== "tree-cascade") {
12276
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
12211
12277
  (_b = this.onUpdate) === null || _b === void 0 ? void 0 : _b.Trigger(update);
12212
12278
  }
12213
12279
  this.rego[entityId] = entityRegos.filter(r => r.menuItemId !== menuItemId);
@@ -12242,7 +12308,7 @@ var VisualsRegister;
12242
12308
  if (source) {
12243
12309
  update.source = source;
12244
12310
  }
12245
- if (source !== "tree-cascade") {
12311
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
12246
12312
  (_c = this.onUpdate) === null || _c === void 0 ? void 0 : _c.Trigger(update);
12247
12313
  }
12248
12314
  if (doUpdate && menuItemId) {
@@ -12349,7 +12415,7 @@ var VisualsRegister;
12349
12415
  if (source) {
12350
12416
  update.source = source;
12351
12417
  }
12352
- if (source !== "tree-cascade") {
12418
+ if (source !== UPDATE_SOURCES.TREE_CASCADE) {
12353
12419
  (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(update);
12354
12420
  }
12355
12421
  this.queueUpdate({
@@ -18683,7 +18749,7 @@ var TilesetCadRenderManager;
18683
18749
  requestRender: false,
18684
18750
  menuItemId: this.item.id,
18685
18751
  doRemove: false,
18686
- source: "tileset-dispose"
18752
+ source: UPDATE_SOURCES.TILESET_DISPOSE
18687
18753
  });
18688
18754
  // Might have to do something smarter since siblings could still be OK.
18689
18755
  this.loadedCesiumEntities[rego.entityId] = null;
@@ -18711,6 +18777,7 @@ var TilesetCadRenderManager;
18711
18777
  ];
18712
18778
  while (stack.length > 0) {
18713
18779
  const { node, parentId, firstFoundCollapsedBranch } = stack.pop();
18780
+ let newFirstFoundCollapsedBranch = firstFoundCollapsedBranch;
18714
18781
  if (firstFoundCollapsedBranch) {
18715
18782
  this.treeNodeByGeomId[node.geomId] = firstFoundCollapsedBranch;
18716
18783
  this.treeNodeByEntityId[node.id] = firstFoundCollapsedBranch;
@@ -18725,19 +18792,19 @@ var TilesetCadRenderManager;
18725
18792
  };
18726
18793
  this.treeNodeByGeomId[node.geomId] = cache;
18727
18794
  this.treeNodeByEntityId[node.id] = cache;
18728
- let newFirstFoundCollapsedBranch = firstFoundCollapsedBranch;
18729
18795
  if (!firstFoundCollapsedBranch && node.collapsed) {
18730
18796
  newFirstFoundCollapsedBranch = cache;
18731
18797
  }
18732
- // Push children to stack in reverse order to maintain correct traversal.
18733
- if (node.children) {
18734
- for (let i = node.children.length - 1; i >= 0; i--) {
18735
- stack.push({
18736
- node: node.children[i],
18737
- parentId: node.id,
18738
- firstFoundCollapsedBranch: newFirstFoundCollapsedBranch
18739
- });
18740
- }
18798
+ }
18799
+ // Always process children regardless of collapse state
18800
+ // This ensures all nodes are mapped, even those under collapsed branches
18801
+ if (node.children) {
18802
+ for (let i = node.children.length - 1; i >= 0; i--) {
18803
+ stack.push({
18804
+ node: node.children[i],
18805
+ parentId: node.id,
18806
+ firstFoundCollapsedBranch: newFirstFoundCollapsedBranch
18807
+ });
18741
18808
  }
18742
18809
  }
18743
18810
  }
@@ -18966,7 +19033,7 @@ var TilesetCadRenderManager;
18966
19033
  this.visualsManager.RemoveRegos({
18967
19034
  menuItemId: this.item.id,
18968
19035
  doRemove: false,
18969
- source: "tileset-dispose"
19036
+ source: UPDATE_SOURCES.TILESET_DISPOSE
18970
19037
  });
18971
19038
  this.viewerDateTimeDispose();
18972
19039
  }
@@ -19467,7 +19534,7 @@ var TilesetOsmRenderManager;
19467
19534
  }
19468
19535
  this.visualsManager.RemoveRegos({
19469
19536
  menuItemId: this.item.id,
19470
- source: "osm-tileset-dispose"
19537
+ source: UPDATE_SOURCES.OSM_TILESET_DISPOSE
19471
19538
  });
19472
19539
  this.featureQueue = [];
19473
19540
  }
@@ -33249,7 +33316,7 @@ class WidgetViewBar extends Widget.AWidget {
33249
33316
  }
33250
33317
  }
33251
33318
 
33252
- const VERSION = "5.9.7";
33319
+ const VERSION = "5.9.9";
33253
33320
 
33254
- export { VERSION, CesiumViewMonitor, ViewerUtils, ViewerEventTracker, MenuItemManager, isOutlineChanged, EntityRenderEngine, EntityRenderEnginePoint, EntityRenderEnginePolyline, EntityRenderEnginePolygon, EntityRenderEngineModel3d, MenuItemCreator, VisualsRegister, RenderManager, EntitiesIdsRenderManager, DataLabRenderManager, EntitiesLoadedRenderManager, EntitiesRenderManager, EntityRenderManager, TilesetCadRenderManager, TilesetArbRenderManager, TilesetEntitiesRenderManager, TilesetOsmRenderManager, TilesetPointcloudRenderManager, TilesetGooglePhotosRenderManager, DataSourceStaticKmlManager, GoogleSearchRenderManager, AssemblyRenderManager, RelationsRenderManager, SharedGetters, CesiumParabola, EntityLabel, ViewRenderEngine, TileRenderEngine, TilesetRenderEngine, CESIUM_INSPECTOR_KEY, CESIUM_TIMELINE_KEY, CESIUM_TIMELINE_LIVE_KEY, CESIUM_TIMELINE_LIVE_PADDING_KEY, CESIUM_TIMELINE_INTERVAL_KEY, DEFAULT_LIVE_PADDING_SECONDS, ViewUtils, DrawingUtils, MeasureUtils, EntityUtils, CesiumEntityStyler, CesiumAnimatedProperty, CesiumAnimatedInOut, Draw3dPolygon, Draw3dPolyline, MeasureCreator, Walkthrough, Widget, VIEWER_BOOKMARKS_WIDGET_KEY, WidgetBookmarks, WidgetBranding, WidgetCursorBar, WidgetEmbeddedInfoView, WidgetInfoView, WidgetNavCompass$$1 as WidgetNavCompass, VIEWER_VIEW_BAR_WIDGET_KEY, WidgetViewBar, WidgetControlViewBar, WidgetControlViewBarSearch, VIEWER_LEFT_PANEL_WIDGET_KEY, VIEWER_LEFT_PANEL_CSS_VAR_LEFT, WidgetLeftPanel, WidgetLeftPanelTab, WidgetLeftPanelTabBookmarks };
33321
+ export { VERSION, CesiumViewMonitor, ViewerUtils, ViewerEventTracker, MenuItemManager, isOutlineChanged, EntityRenderEngine, EntityRenderEnginePoint, EntityRenderEnginePolyline, EntityRenderEnginePolygon, EntityRenderEngineModel3d, MenuItemCreator, UPDATE_SOURCES, VisualsRegister, RenderManager, EntitiesIdsRenderManager, DataLabRenderManager, EntitiesLoadedRenderManager, EntitiesRenderManager, EntityRenderManager, TilesetCadRenderManager, TilesetArbRenderManager, TilesetEntitiesRenderManager, TilesetOsmRenderManager, TilesetPointcloudRenderManager, TilesetGooglePhotosRenderManager, DataSourceStaticKmlManager, GoogleSearchRenderManager, AssemblyRenderManager, RelationsRenderManager, SharedGetters, CesiumParabola, EntityLabel, ViewRenderEngine, TileRenderEngine, TilesetRenderEngine, CESIUM_INSPECTOR_KEY, CESIUM_TIMELINE_KEY, CESIUM_TIMELINE_LIVE_KEY, CESIUM_TIMELINE_LIVE_PADDING_KEY, CESIUM_TIMELINE_INTERVAL_KEY, DEFAULT_LIVE_PADDING_SECONDS, ViewUtils, DrawingUtils, MeasureUtils, EntityUtils, CesiumEntityStyler, CesiumAnimatedProperty, CesiumAnimatedInOut, Draw3dPolygon, Draw3dPolyline, MeasureCreator, Walkthrough, Widget, VIEWER_BOOKMARKS_WIDGET_KEY, WidgetBookmarks, WidgetBranding, WidgetCursorBar, WidgetEmbeddedInfoView, WidgetInfoView, WidgetNavCompass$$1 as WidgetNavCompass, VIEWER_VIEW_BAR_WIDGET_KEY, WidgetViewBar, WidgetControlViewBar, WidgetControlViewBarSearch, VIEWER_LEFT_PANEL_WIDGET_KEY, VIEWER_LEFT_PANEL_CSS_VAR_LEFT, WidgetLeftPanel, WidgetLeftPanelTab, WidgetLeftPanelTabBookmarks };
33255
33322
  //# sourceMappingURL=bruce-cesium.es5.js.map