bruce-cesium 5.4.6 → 5.4.7

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
1
  import { BruceEvent, Cartes, Entity as Entity$1, Carto, Geometry, MathUtils, LRUCache, Api, Calculator, ClientFile, EntityTag, EntityType, ObjectUtils, Style, ProjectViewTile, 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';
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, PolygonHierarchy, PolylineGraphics, ColorMaterialProperty, ColorBlendMode, HeadingPitchRoll, Transforms, Model, HorizontalOrigin, VerticalOrigin, Primitive, Cesium3DTileFeature, SceneMode, GeoJsonDataSource, Cesium3DTileStyle, Cesium3DTileColorBlendMode, HeadingPitchRange, Ion, KmlDataSource, SceneTransforms, Cesium3DTileset, Matrix4, Matrix3, IonResource, EllipsoidTerrainProvider, CesiumInspector, OrthographicFrustum, defined, ClockRange, EasingFunction, NearFarScalar, EllipsoidGeodesic, sampleTerrainMostDetailed, PolygonPipeline, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, BoundingSphere, GeometryInstance, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, ScreenSpaceEventHandler, ScreenSpaceEventType, Quaternion, 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, HorizontalOrigin, VerticalOrigin, ColorBlendMode, HeadingPitchRoll, Transforms, Model, Primitive, Cesium3DTileFeature, SceneMode, GeoJsonDataSource, Cesium3DTileStyle, HeadingPitchRange, Ion, Cesium3DTileColorBlendMode, KmlDataSource, OrthographicFrustum, EasingFunction, NearFarScalar, SceneTransforms, Cesium3DTileset, Matrix4, Matrix3, IonResource, EllipsoidTerrainProvider, CesiumInspector, defined, ClockRange, EllipsoidGeodesic, sampleTerrainMostDetailed, PolygonPipeline, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, BoundingSphere, GeometryInstance, Quaternion, ScreenSpaceEventHandler, ScreenSpaceEventType, CzmlDataSource, Intersect, Fullscreen } from 'cesium';
4
4
 
5
5
  const TIME_LAG = 300;
6
6
  const POSITION_CHECK_TIMER = 950;
@@ -1152,10 +1152,15 @@ var CesiumAnimatedProperty;
1152
1152
  this.lastCalcSeriesTime = null;
1153
1153
  this.lastCalcOrient = null;
1154
1154
  this.lastCalcOrientTime = null;
1155
+ // Indicates that the first time we get a GetValue position, we need to animate into it.
1156
+ this.animateFromPos3d = null;
1157
+ this.animateFromPos3dTimeStart = null;
1155
1158
  this.viewer = params.viewer;
1156
1159
  this.positions = params.posses;
1157
1160
  this.pitch = params.pitch;
1158
1161
  this.roll = params.roll;
1162
+ this.animateFromPos3d = params.animateFromPos3d;
1163
+ this.animateFromPos3dTimeStart = Date.now();
1159
1164
  this.processHeadings();
1160
1165
  // Order positions by date.
1161
1166
  this.positions.sort((a, b) => {
@@ -1189,19 +1194,28 @@ var CesiumAnimatedProperty;
1189
1194
  if (!now) {
1190
1195
  now = this.viewer.clock.currentTime;
1191
1196
  }
1192
- const nowTime = JulianDate.toDate(now);
1193
- if (this.lastCalcTime == nowTime.getTime()) {
1197
+ const nowTimeMs = JulianDate.toDate(now).getTime();
1198
+ // Skip calculation if time hasn't changed.
1199
+ if (this.lastCalcTime === nowTimeMs) {
1200
+ if (this.animateFromPos3d) {
1201
+ // We emit an interpolated position if we're animating from the start position.
1202
+ const interpolatedPos = this.interpolateToStartPos3d(this.animateFromPos3dTimeStart, Date.now());
1203
+ if (Cartesian3.equals(interpolatedPos, this.animateFromPos3d)) {
1204
+ this.animateFromPos3d = null;
1205
+ }
1206
+ return interpolatedPos;
1207
+ }
1194
1208
  return this.lastCalcPos3d;
1195
1209
  }
1196
1210
  const calculate = () => {
1197
1211
  // See if we're before the first position.
1198
- if (nowTime.getTime() <= this.positions[0].dateTime.getTime()) {
1212
+ if (nowTimeMs <= this.positions[0].dateTime.getTime()) {
1199
1213
  this.lastCalcPosIndexLast = 0;
1200
1214
  this.lastCalcPosIndexNext = 0;
1201
1215
  return this.positions[0].pos3d;
1202
1216
  }
1203
1217
  // See if we're after the last position.
1204
- if (nowTime.getTime() >= this.positions[this.positions.length - 1].dateTime.getTime()) {
1218
+ if (nowTimeMs >= this.positions[this.positions.length - 1].dateTime.getTime()) {
1205
1219
  this.lastCalcPosIndexLast = this.positions.length - 1;
1206
1220
  this.lastCalcPosIndexNext = this.positions.length - 1;
1207
1221
  return this.positions[this.positions.length - 1].pos3d;
@@ -1210,7 +1224,7 @@ var CesiumAnimatedProperty;
1210
1224
  let lastIndex = 0;
1211
1225
  for (let i = 1; i < this.positions.length; i++) {
1212
1226
  let pos = this.positions[i];
1213
- if (nowTime.getTime() >= pos.dateTime.getTime()) {
1227
+ if (nowTimeMs >= pos.dateTime.getTime()) {
1214
1228
  lastIndex = i;
1215
1229
  }
1216
1230
  else {
@@ -1226,12 +1240,43 @@ var CesiumAnimatedProperty;
1226
1240
  return last.pos3d;
1227
1241
  }
1228
1242
  this.lastCalcPosIndexNext = lastIndex + 1;
1229
- let progress = (nowTime.getTime() - last.dateTime.getTime()) / (next.dateTime.getTime() - last.dateTime.getTime());
1243
+ let progress = (nowTimeMs - last.dateTime.getTime()) / (next.dateTime.getTime() - last.dateTime.getTime());
1230
1244
  return Cartesian3.lerp(last.pos3d, next.pos3d, progress, new Cartesian3());
1231
1245
  };
1232
- this.lastCalcTime = nowTime.getTime();
1246
+ this.lastCalcTime = nowTimeMs;
1233
1247
  this.lastCalcPos3d = calculate();
1234
- return this.lastCalcPos3d;
1248
+ let pos3d = this.lastCalcPos3d;
1249
+ // We emit an interpolated position if we're animating from the start position.
1250
+ if (this.animateFromPos3d && Cartes.ValidateCartes3(this.lastCalcPos3d)) {
1251
+ if (Cartesian3.equals(this.animateFromPos3d, this.lastCalcPos3d)) {
1252
+ this.animateFromPos3d = null;
1253
+ }
1254
+ else if (this.animateFromPos3d) {
1255
+ pos3d = this.interpolateToStartPos3d(this.animateFromPos3dTimeStart, Date.now());
1256
+ if (Cartesian3.equals(pos3d, this.animateFromPos3d)) {
1257
+ this.animateFromPos3d = null;
1258
+ }
1259
+ }
1260
+ }
1261
+ return pos3d;
1262
+ }
1263
+ /**
1264
+ * Interpolates between the start position and the last calculated position.
1265
+ * This lets us animate in the series positions.
1266
+ * @param nowMs
1267
+ */
1268
+ interpolateToStartPos3d(startMs, nowMs) {
1269
+ const startPos = this.animateFromPos3d;
1270
+ const targetPos = this.lastCalcPos3d;
1271
+ if (!startPos || !targetPos) {
1272
+ return startPos ? startPos : targetPos;
1273
+ }
1274
+ const duration = nowMs - startMs;
1275
+ let progress = duration / 300; // 300ms duration.
1276
+ if (progress > 1) {
1277
+ progress = 1;
1278
+ }
1279
+ return Cartesian3.lerp(startPos, targetPos, progress, new Cartesian3());
1235
1280
  }
1236
1281
  /**
1237
1282
  * Returns a series of positions to use for rendering the path.
@@ -1414,6 +1459,9 @@ var CesiumAnimatedProperty;
1414
1459
  // Cache for calculated values.
1415
1460
  this.lastCalcTime = null;
1416
1461
  this.lastCalcPos3d = null;
1462
+ this.lastCalcPosApplyOverTime = false;
1463
+ this.lastCalcPos3dApplyOverTimeTarget = null;
1464
+ this.lastCalcPos3dApplyOverTimeTimeStart = null;
1417
1465
  this.lastCalcHeading = null;
1418
1466
  this.removal = null;
1419
1467
  // Track the timeline range for which we have data.
@@ -1511,17 +1559,75 @@ var CesiumAnimatedProperty;
1511
1559
  const nowTimeMs = nowTime.getTime();
1512
1560
  // Skip calculation if time hasn't changed.
1513
1561
  if (this.lastCalcTime === nowTimeMs) {
1562
+ // If we're currently mid-animation, we need to update the position over time.
1563
+ if (this.lastCalcPosApplyOverTime) {
1564
+ const interpolatedPos = this.interpolatePos3dOverTime(this.lastCalcPos3dApplyOverTimeTimeStart, Date.now());
1565
+ if (Cartesian3.equals(interpolatedPos, this.lastCalcPos3dApplyOverTimeTarget)) {
1566
+ this.lastCalcPosApplyOverTime = false;
1567
+ this.lastCalcPos3d = interpolatedPos;
1568
+ }
1569
+ this.onUpdate(interpolatedPos, this.lastCalcHeading);
1570
+ }
1514
1571
  return;
1515
1572
  }
1516
1573
  // Calculate position.
1517
1574
  const position = this.calculatePosition(nowTimeMs);
1575
+ // Calculate what position to emit.
1576
+ let pos3d = null;
1518
1577
  // Update cache values.
1519
1578
  this.lastCalcTime = nowTimeMs;
1520
- this.lastCalcPos3d = position.pos3d;
1579
+ if (position.applyOverTime && Cartes.ValidateCartes3(this.lastCalcPos3d)) {
1580
+ if (Cartesian3.equals(this.lastCalcPos3d, position.pos3d)) {
1581
+ pos3d = position.pos3d;
1582
+ this.lastCalcPosApplyOverTime = false;
1583
+ }
1584
+ else if (Cartesian3.equals(this.lastCalcPos3dApplyOverTimeTarget, position.pos3d)) {
1585
+ pos3d = this.interpolatePos3dOverTime(this.lastCalcPos3dApplyOverTimeTimeStart, Date.now());
1586
+ if (Cartesian3.equals(pos3d, this.lastCalcPos3dApplyOverTimeTarget)) {
1587
+ this.lastCalcPos3d = pos3d;
1588
+ this.lastCalcPosApplyOverTime = false;
1589
+ }
1590
+ }
1591
+ else {
1592
+ if (!this.lastCalcPosApplyOverTime) {
1593
+ this.lastCalcPos3dApplyOverTimeTimeStart = Date.now();
1594
+ }
1595
+ this.lastCalcPosApplyOverTime = true;
1596
+ this.lastCalcPos3dApplyOverTimeTarget = position.pos3d;
1597
+ pos3d = this.interpolatePos3dOverTime(this.lastCalcPos3dApplyOverTimeTimeStart, Date.now());
1598
+ }
1599
+ }
1600
+ else {
1601
+ this.lastCalcPos3d = position.pos3d;
1602
+ pos3d = position.pos3d;
1603
+ }
1521
1604
  // Calculate heading.
1522
- this.lastCalcHeading = this.calculateHeading(nowTimeMs, position.indexLast, position.indexNext);
1605
+ const posses = this.positions;
1606
+ const lastPos = posses[position.indexLast];
1607
+ const nextPos = posses[position.indexNext];
1608
+ if (lastPos && nextPos) {
1609
+ this.lastCalcHeading = this.calculateHeading(nowTimeMs, lastPos, nextPos);
1610
+ }
1523
1611
  // Provide the calculated values to the caller.
1524
- this.onUpdate(this.lastCalcPos3d, this.lastCalcHeading);
1612
+ this.onUpdate(pos3d, this.lastCalcHeading);
1613
+ }
1614
+ /**
1615
+ * Interpolates the position over time between the last and target positions.
1616
+ * This is used when we reach a boundary position so to smoothly animate to it, we move from the current position to it.
1617
+ * @param nowMs
1618
+ */
1619
+ interpolatePos3dOverTime(prevMs, nowMs) {
1620
+ const lastPos = this.lastCalcPos3d;
1621
+ const targetPos = this.lastCalcPos3dApplyOverTimeTarget;
1622
+ if (!lastPos || !targetPos) {
1623
+ return lastPos ? lastPos : targetPos;
1624
+ }
1625
+ const duration = nowMs - prevMs;
1626
+ let progress = duration / 300; // 300ms duration.
1627
+ if (progress > 1) {
1628
+ progress = 1;
1629
+ }
1630
+ return Cartesian3.lerp(lastPos, targetPos, progress, new Cartesian3());
1525
1631
  }
1526
1632
  /**
1527
1633
  * Pre-process headings in the series.
@@ -1553,7 +1659,8 @@ var CesiumAnimatedProperty;
1553
1659
  return {
1554
1660
  pos3d: new Cartesian3(),
1555
1661
  indexLast: -1,
1556
- indexNext: -1
1662
+ indexNext: -1,
1663
+ applyOverTime: false
1557
1664
  };
1558
1665
  }
1559
1666
  // See if we're before the first position.
@@ -1561,7 +1668,8 @@ var CesiumAnimatedProperty;
1561
1668
  return {
1562
1669
  pos3d: posses[0].pos3d,
1563
1670
  indexLast: 0,
1564
- indexNext: 0
1671
+ indexNext: 0,
1672
+ applyOverTime: true
1565
1673
  };
1566
1674
  }
1567
1675
  // See if we're after the last position.
@@ -1570,7 +1678,8 @@ var CesiumAnimatedProperty;
1570
1678
  return {
1571
1679
  pos3d: posses[lastIndex].pos3d,
1572
1680
  indexLast: lastIndex,
1573
- indexNext: lastIndex
1681
+ indexNext: lastIndex,
1682
+ applyOverTime: true
1574
1683
  };
1575
1684
  }
1576
1685
  // Binary search to find the closest position.
@@ -1596,7 +1705,8 @@ var CesiumAnimatedProperty;
1596
1705
  return {
1597
1706
  pos3d: last.pos3d,
1598
1707
  indexLast: lastIndex,
1599
- indexNext: lastIndex
1708
+ indexNext: lastIndex,
1709
+ applyOverTime: true
1600
1710
  };
1601
1711
  }
1602
1712
  // Interpolate between the two closest positions.
@@ -1607,25 +1717,18 @@ var CesiumAnimatedProperty;
1607
1717
  return {
1608
1718
  pos3d: interpolatedPos,
1609
1719
  indexLast: lastIndex,
1610
- indexNext: nextIndex
1720
+ indexNext: nextIndex,
1721
+ applyOverTime: false
1611
1722
  };
1612
1723
  }
1613
1724
  /**
1614
1725
  * Calculate the heading at the given time.
1615
1726
  */
1616
- calculateHeading(currentTimeMs, lastIndex, nextIndex) {
1617
- const posses = this.positions;
1618
- // Ensure valid indices.
1619
- if (lastIndex < 0 || nextIndex < 0 ||
1620
- lastIndex >= posses.length || nextIndex >= posses.length) {
1621
- return null;
1622
- }
1623
- const lastPos = posses[lastIndex];
1624
- const nextPos = posses[nextIndex];
1727
+ calculateHeading(currentTimeMs, lastPos, nextPos) {
1625
1728
  // If the heading is present and not null, interpolate or use it directly.
1626
1729
  if (lastPos.heading !== null && nextPos.heading !== null) {
1627
1730
  // If they're the same position or same time, just return the heading.
1628
- if (lastIndex === nextIndex ||
1731
+ if (lastPos === nextPos ||
1629
1732
  lastPos.dateTime.getTime() === nextPos.dateTime.getTime()) {
1630
1733
  return lastPos.heading;
1631
1734
  }
@@ -1645,16 +1748,16 @@ var CesiumAnimatedProperty;
1645
1748
  }
1646
1749
  // If no valid heading is available, calculate based on movement direction.
1647
1750
  else {
1648
- const previousPos = lastPos.pos3d;
1649
- const currentPos = nextPos.pos3d;
1650
- if (!previousPos || isNaN(previousPos.x) ||
1651
- !currentPos || isNaN(currentPos.x)) {
1751
+ const prevPos3d = lastPos.pos3d;
1752
+ const nextPos3d = nextPos.pos3d;
1753
+ if (!prevPos3d || isNaN(prevPos3d.x) ||
1754
+ !nextPos3d || isNaN(nextPos3d.x)) {
1652
1755
  return null;
1653
1756
  }
1654
1757
  // Flatten to avoid orientation changes due to height differences.
1655
- const adjustedPointPrev = Cartographic.fromCartesian(previousPos);
1758
+ const adjustedPointPrev = Cartographic.fromCartesian(prevPos3d);
1656
1759
  const adjustedPos3dPrev = Cartesian3.fromRadians(adjustedPointPrev.longitude, adjustedPointPrev.latitude, 0);
1657
- const adjustedPointNext = Cartographic.fromCartesian(currentPos);
1760
+ const adjustedPointNext = Cartographic.fromCartesian(nextPos3d);
1658
1761
  const adjustedPos3dNext = Cartesian3.fromRadians(adjustedPointNext.longitude, adjustedPointNext.latitude, 0);
1659
1762
  // Check if the positions are too close.
1660
1763
  if (Cartesian3.distance(adjustedPos3dPrev, adjustedPos3dNext) < 0.05) {
@@ -1665,14 +1768,24 @@ var CesiumAnimatedProperty;
1665
1768
  if (direction.x === 0 && direction.y === 0 && direction.z === 0) {
1666
1769
  return null;
1667
1770
  }
1668
- // Calculate heading from the direction vector.
1771
+ // Normalize the direction vector.
1669
1772
  Cartesian3.normalize(direction, direction);
1670
- // Convert the direction to a heading angle.
1671
- const east = Cartesian3.UNIT_X;
1672
- const north = Cartesian3.UNIT_Y;
1673
- const heading = Math.atan2(Cartesian3.dot(direction, east), Cartesian3.dot(direction, north));
1674
- // Convert to degrees.
1675
- return (Math$1.toDegrees(heading) + 360) % 360;
1773
+ // Calculate heading in the ENU (East-North-Up).
1774
+ // First, get the local ENU frame at the midpoint.
1775
+ const midPoint = Cartesian3.midpoint(adjustedPos3dPrev, adjustedPos3dNext, new Cartesian3());
1776
+ // Convert direction from ECEF to ENU.
1777
+ // We need to create a transform from ECEF to ENU at our position.
1778
+ const transform = Transforms.eastNorthUpToFixedFrame(midPoint);
1779
+ const inverseTransform = Matrix4.inverseTransformation(transform, new Matrix4());
1780
+ // Transform our direction vector to local ENU.
1781
+ const localDirection = Matrix4.multiplyByPointAsVector(inverseTransform, direction, new Cartesian3());
1782
+ Cartesian3.normalize(localDirection, localDirection);
1783
+ // Now calculate the heading in the ENU frame (clockwise from north).
1784
+ // atan2(east, north) gives us the angle in the east-north plane measured clockwise from north.
1785
+ const heading = Math.atan2(localDirection.x, localDirection.y);
1786
+ // Convert to degrees and ensure it's in the range [0, 360].
1787
+ const headingDegrees = (Math$1.toDegrees(heading) + 360) % 360;
1788
+ return headingDegrees;
1676
1789
  }
1677
1790
  }
1678
1791
  }
@@ -4554,12 +4667,14 @@ var EntityRenderEnginePoint;
4554
4667
  // Unset width/height.
4555
4668
  cEntity.billboard.width = undefined;
4556
4669
  cEntity.billboard.height = undefined;
4670
+ const prevPos3d = GetCValue(params.viewer, cEntity.position);
4557
4671
  // If we have a series of time-based positions then we'll animate as time changes.
4558
4672
  const series = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
4559
4673
  if (series.length > 1) {
4560
4674
  animatePosition = new CesiumAnimatedProperty.AnimatePositionSeries({
4561
4675
  posses: series,
4562
- viewer: params.viewer
4676
+ viewer: params.viewer,
4677
+ animateFromPos3d: prevPos3d
4563
4678
  });
4564
4679
  cEntity.position = new CallbackProperty(() => animatePosition.GetValue(), false);
4565
4680
  }
@@ -4571,7 +4686,6 @@ var EntityRenderEnginePoint;
4571
4686
  returnHeightRef: heightRef,
4572
4687
  allowRendered: false
4573
4688
  });
4574
- const prevPos3d = GetCValue(params.viewer, cEntity.position);
4575
4689
  if (!prevPos3d || !Cartesian3.equals(prevPos3d, pos3d)) {
4576
4690
  animatePosition = new CesiumAnimatedProperty.AnimatePosition({
4577
4691
  durationMs: 200,
@@ -4877,12 +4991,14 @@ var EntityRenderEnginePoint;
4877
4991
  cEntity.billboard.heightReference = new ConstantProperty(heightRef);
4878
4992
  cEntity.billboard.distanceDisplayCondition = new ConstantProperty(EntityRenderEngine.GetDisplayCondition(params.minDistance, params.maxDistance));
4879
4993
  cEntity.billboard.disableDepthTestDistance = new ConstantProperty(disableDepthTest ? Number.POSITIVE_INFINITY : undefined);
4994
+ const prevPos3d = GetCValue(params.viewer, cEntity.position);
4880
4995
  // If we have a series of time-based positions then we'll animate as time changes.
4881
4996
  const series = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
4882
4997
  if (series.length > 1) {
4883
4998
  animatePosition = new CesiumAnimatedProperty.AnimatePositionSeries({
4884
4999
  posses: series,
4885
- viewer: params.viewer
5000
+ viewer: params.viewer,
5001
+ animateFromPos3d: prevPos3d
4886
5002
  });
4887
5003
  cEntity.position = new CallbackProperty(() => animatePosition.GetValue(), false);
4888
5004
  }
@@ -4894,7 +5010,6 @@ var EntityRenderEnginePoint;
4894
5010
  returnHeightRef: heightRef,
4895
5011
  allowRendered: false
4896
5012
  });
4897
- const prevPos3d = GetCValue(params.viewer, cEntity.position);
4898
5013
  if (!prevPos3d || !Cartesian3.equals(prevPos3d, pos3d)) {
4899
5014
  animatePosition = new CesiumAnimatedProperty.AnimatePosition({
4900
5015
  durationMs: 200,
@@ -6474,6 +6589,7 @@ var EntityRenderEngineModel3d;
6474
6589
  cEntity.model.colorBlendAmount = new ConstantProperty(blendAmount);
6475
6590
  cEntity.model.colorBlendMode = new ConstantProperty(blendMode);
6476
6591
  cEntity.model.distanceDisplayCondition = new ConstantProperty(EntityRenderEngine.GetDisplayCondition(params.minDistance, params.maxDistance));
6592
+ const prevPos3d = GetCValue(params.viewer, cEntity.position);
6477
6593
  // If we have a series of time-based positions then we'll animate as time changes.
6478
6594
  const series = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
6479
6595
  if (series.length > 1) {
@@ -6481,12 +6597,12 @@ var EntityRenderEngineModel3d;
6481
6597
  posses: series,
6482
6598
  viewer: params.viewer,
6483
6599
  pitch: pitch,
6484
- roll: roll
6600
+ roll: roll,
6601
+ animateFromPos3d: prevPos3d
6485
6602
  });
6486
6603
  cEntity.position = new CallbackProperty(() => animatePosition.GetValue(), false);
6487
6604
  }
6488
6605
  else {
6489
- const prevPos3d = GetCValue(params.viewer, cEntity.position);
6490
6606
  const posChanged = !prevPos3d || !Cartesian3.equals(prevPos3d, pos3d);
6491
6607
  if (posChanged) {
6492
6608
  animatePosition = new CesiumAnimatedProperty.AnimatePosition({
@@ -14395,36 +14511,54 @@ var EntitiesIdsRenderManager;
14395
14511
  * @param entities
14396
14512
  */
14397
14513
  async getHistoricInfo(entities) {
14398
- const startTmp = JulianDate.toDate(this.viewer.clock.startTime);
14399
- const stopTmp = JulianDate.toDate(this.viewer.clock.stopTime);
14514
+ // Time padding in milliseconds (15 seconds).
14515
+ // Helps account for desync between client and server.
14516
+ const TIME_PADDING_MS = 15000;
14517
+ const minDateTimeStr = this.viewer.clock.startTime.toString();
14518
+ const maxDateTimeStr = this.viewer.clock.stopTime.toString();
14519
+ const minDateTime = new Date(minDateTimeStr).getTime();
14520
+ const maxDateTime = new Date(maxDateTimeStr).getTime();
14400
14521
  let rangesToRequest = [{
14401
- start: startTmp.toISOString(),
14402
- stop: stopTmp.toISOString()
14522
+ start: minDateTime,
14523
+ stop: maxDateTime
14403
14524
  }];
14404
14525
  // If we already have cached data, determine what ranges we're missing.
14405
- if (this.lastHistoricMin && this.lastHistoricMax && Object.keys(this.entitiesHistoric).length >= entities.length) {
14406
- const cachedStart = new Date(this.lastHistoricMin).getTime();
14407
- const cachedStop = new Date(this.lastHistoricMax).getTime();
14408
- const newStart = startTmp.getTime();
14409
- const newStop = stopTmp.getTime();
14526
+ if (Object.keys(this.entitiesHistoric).length >= entities.length) {
14527
+ let foundMinDateTime = null;
14528
+ let foundMaxDateTime = null;
14529
+ // Find the min/max based on cached data.
14530
+ // Since we set the values sorted, we only have to check the first and last records for each entity.
14531
+ for (const entityId of Object.keys(this.entitiesHistoric)) {
14532
+ const records = this.entitiesHistoric[entityId] || [];
14533
+ if (records.length) {
14534
+ const dateTime = new Date(records[0].dateTime).getTime();
14535
+ if (foundMinDateTime == null || dateTime < foundMinDateTime) {
14536
+ foundMinDateTime = dateTime;
14537
+ }
14538
+ const dateTime2 = new Date(records[records.length - 1].dateTime).getTime();
14539
+ if (foundMaxDateTime == null || dateTime2 > foundMaxDateTime) {
14540
+ foundMaxDateTime = dateTime2;
14541
+ }
14542
+ }
14543
+ }
14410
14544
  // Complete overlap - we already have all the data.
14411
- if (newStart >= cachedStart && newStop <= cachedStop) {
14545
+ if (foundMinDateTime != null && foundMaxDateTime != null && foundMinDateTime <= minDateTime && foundMaxDateTime >= maxDateTime) {
14412
14546
  return [false, this.entitiesHistoric];
14413
14547
  }
14414
14548
  // Calculate missing ranges.
14415
14549
  rangesToRequest = [];
14416
14550
  // Check if we need data before our cached range.
14417
- if (newStart < cachedStart) {
14551
+ if (foundMinDateTime != null && foundMinDateTime > minDateTime) {
14418
14552
  rangesToRequest.push({
14419
- start: startTmp.toISOString(),
14420
- stop: new Date(cachedStart).toISOString()
14553
+ start: minDateTime,
14554
+ stop: new Date(foundMinDateTime).getTime()
14421
14555
  });
14422
14556
  }
14423
14557
  // Check if we need data after our cached range.
14424
- if (newStop > cachedStop) {
14558
+ if (foundMaxDateTime != null && foundMaxDateTime < maxDateTime) {
14425
14559
  rangesToRequest.push({
14426
- start: new Date(cachedStop).toISOString(),
14427
- stop: stopTmp.toISOString()
14560
+ start: new Date(foundMaxDateTime).getTime(),
14561
+ stop: maxDateTime
14428
14562
  });
14429
14563
  }
14430
14564
  }
@@ -14435,17 +14569,12 @@ var EntitiesIdsRenderManager;
14435
14569
  if (this.disposed) {
14436
14570
  break;
14437
14571
  }
14438
- // Add padding to ensure we get all data.
14439
- const start = new Date(range.start);
14440
- const stop = new Date(range.stop);
14441
- start.setSeconds(start.getSeconds() - 1);
14442
- stop.setSeconds(stop.getSeconds() + 1);
14443
- const paddedStartStr = start.toISOString();
14444
- const paddedStopStr = stop.toISOString();
14572
+ const start = new Date(range.start - TIME_PADDING_MS);
14573
+ const stop = new Date(range.stop + TIME_PADDING_MS);
14445
14574
  const historicData = await EntityHistoricData.GetList({
14446
14575
  attrKey: this.item.BruceEntity.historicAttrKey,
14447
- dateTimeFrom: paddedStartStr,
14448
- dateTimeTo: paddedStopStr,
14576
+ dateTimeFrom: start.toISOString(),
14577
+ dateTimeTo: stop.toISOString(),
14449
14578
  entityIds: entityIds,
14450
14579
  api: this.apiGetter.getApi()
14451
14580
  });
@@ -14479,9 +14608,6 @@ var EntitiesIdsRenderManager;
14479
14608
  }
14480
14609
  }
14481
14610
  if (!this.disposed) {
14482
- // Update our cache boundaries.
14483
- this.lastHistoricMin = startTmp.toISOString();
14484
- this.lastHistoricMax = stopTmp.toISOString();
14485
14611
  this.entitiesHistoric = combined;
14486
14612
  }
14487
14613
  return [rangesToRequest.length > 0, combined];
@@ -17615,6 +17741,9 @@ var TilesetCadRenderManager;
17615
17741
  const api = this.getters.GetBruceApi({
17616
17742
  accountId: accountId
17617
17743
  });
17744
+ // Time padding in milliseconds (15 seconds).
17745
+ // Helps account for desync between client and server.
17746
+ const TIME_PADDING_MS = 15000;
17618
17747
  // 'start-stop' time string that maps to a pending request.
17619
17748
  // Helps us avoid repeated requests that are the same.
17620
17749
  const pendingRequests = new Map();
@@ -17658,29 +17787,32 @@ var TilesetCadRenderManager;
17658
17787
  * @returns
17659
17788
  */
17660
17789
  const checkTimelineRange = async () => {
17661
- const minDateTime = this.viewer.clock.startTime.toString();
17662
- const maxDateTime = this.viewer.clock.stopTime.toString();
17790
+ // Current timeline range.
17791
+ const minDateTime = new Date(this.viewer.clock.startTime.toString()).getTime();
17792
+ const maxDateTime = new Date(this.viewer.clock.stopTime.toString()).getTime();
17793
+ // What we have loaded.
17794
+ const range = this.historicAnimation.getDateRange();
17795
+ const foundMinDateTime = (range === null || range === void 0 ? void 0 : range.minDate) ? range.minDate.getTime() : null;
17796
+ const foundMaxDateTime = (range === null || range === void 0 ? void 0 : range.maxDate) ? range.maxDate.getTime() : null;
17663
17797
  // See if the current range is within the range we already have.
17664
17798
  if (this.historicPosses.length > 0 &&
17665
- this.historicPossesMinDateTime &&
17666
- this.historicPossesMaxDateTime &&
17667
- minDateTime >= this.historicPossesMinDateTime &&
17668
- maxDateTime <= this.historicPossesMaxDateTime) {
17799
+ foundMinDateTime &&
17800
+ foundMaxDateTime &&
17801
+ minDateTime >= foundMinDateTime &&
17802
+ maxDateTime <= foundMaxDateTime) {
17669
17803
  return;
17670
17804
  }
17671
17805
  // See if the requested range is before or after the range we have.
17672
- const fetchBefore = !this.historicPossesMinDateTime || minDateTime < this.historicPossesMinDateTime;
17673
- const fetchAfter = !this.historicPossesMaxDateTime || maxDateTime > this.historicPossesMaxDateTime;
17806
+ const fetchBefore = !foundMinDateTime || minDateTime < foundMinDateTime;
17807
+ const fetchAfter = !foundMaxDateTime || maxDateTime > foundMaxDateTime;
17674
17808
  if (!fetchBefore && !fetchAfter) {
17675
17809
  // Already have the data we need.
17676
17810
  return;
17677
17811
  }
17678
17812
  // No known range so we need to fetch the whole thing.
17679
- if (!this.historicPossesMinDateTime || !this.historicPossesMaxDateTime) {
17680
- const startTmp = JulianDate.toDate(this.viewer.clock.startTime);
17681
- const stopTmp = JulianDate.toDate(this.viewer.clock.stopTime);
17682
- const startStr = new Date(startTmp.getTime() - 1000).toISOString();
17683
- const stopStr = new Date(stopTmp.getTime() + 1000).toISOString();
17813
+ if (!foundMinDateTime || !foundMaxDateTime) {
17814
+ const startStr = new Date(minDateTime - TIME_PADDING_MS).toISOString();
17815
+ const stopStr = new Date(maxDateTime + TIME_PADDING_MS).toISOString();
17684
17816
  const newPositions = await getPossesForRange(startStr, stopStr);
17685
17817
  if (this.disposed) {
17686
17818
  return;
@@ -17688,22 +17820,17 @@ var TilesetCadRenderManager;
17688
17820
  if (this.historicAnimation && this.historicAnimation.addPositions) {
17689
17821
  this.historicAnimation.addPositions(newPositions);
17690
17822
  }
17691
- this.historicPossesMinDateTime = minDateTime;
17692
- this.historicPossesMaxDateTime = maxDateTime;
17693
17823
  }
17694
17824
  else {
17695
17825
  // The data we want is before the range we've currently loaded.
17696
17826
  if (fetchBefore) {
17697
17827
  // Calculate the missing difference and request it.
17698
- const startTmp = JulianDate.toDate(this.viewer.clock.startTime);
17699
- const stopTmp = new Date(this.historicPossesMinDateTime);
17700
- const startStr = new Date(startTmp.getTime() - 1000).toISOString();
17701
- const stopStr = stopTmp.toISOString();
17702
- getPossesForRange(startStr, stopStr).then(newPositions => {
17828
+ const startStr = new Date(minDateTime - TIME_PADDING_MS).toISOString();
17829
+ const stopStr = new Date(foundMinDateTime + TIME_PADDING_MS).toISOString();
17830
+ getPossesForRange(startStr, stopStr).then((newPositions) => {
17703
17831
  if (this.disposed) {
17704
17832
  return;
17705
17833
  }
17706
- this.historicPossesMinDateTime = minDateTime;
17707
17834
  if (this.historicAnimation && this.historicAnimation.addPositions) {
17708
17835
  this.historicAnimation.addPositions(newPositions);
17709
17836
  }
@@ -17712,15 +17839,12 @@ var TilesetCadRenderManager;
17712
17839
  // The data we want is after the range we've currently loaded.
17713
17840
  if (fetchAfter) {
17714
17841
  // Calculate the missing difference and request it.
17715
- const startTmp = new Date(this.historicPossesMaxDateTime);
17716
- const stopTmp = JulianDate.toDate(this.viewer.clock.stopTime);
17717
- const startStr = startTmp.toISOString();
17718
- const stopStr = new Date(stopTmp.getTime() + 1000).toISOString();
17719
- getPossesForRange(startStr, stopStr).then(newPositions => {
17842
+ const startStr = new Date(foundMaxDateTime - TIME_PADDING_MS).toISOString();
17843
+ const stopStr = new Date(maxDateTime + TIME_PADDING_MS).toISOString();
17844
+ getPossesForRange(startStr, stopStr).then((newPositions) => {
17720
17845
  if (this.disposed) {
17721
17846
  return;
17722
17847
  }
17723
- this.historicPossesMaxDateTime = maxDateTime;
17724
17848
  if (this.historicAnimation && this.historicAnimation.addPositions) {
17725
17849
  this.historicAnimation.addPositions(newPositions);
17726
17850
  }
@@ -17734,8 +17858,6 @@ var TilesetCadRenderManager;
17734
17858
  * @returns
17735
17859
  */
17736
17860
  const getInitialPosses = async () => {
17737
- const minDateTime = this.viewer.clock.startTime.toString();
17738
- const maxDateTime = this.viewer.clock.stopTime.toString();
17739
17861
  this.historicPossesLoadingProm = new Promise(async (res) => {
17740
17862
  try {
17741
17863
  if (this.disposed) {
@@ -17752,8 +17874,6 @@ var TilesetCadRenderManager;
17752
17874
  return;
17753
17875
  }
17754
17876
  this.historicPosses = positions;
17755
- this.historicPossesMinDateTime = minDateTime;
17756
- this.historicPossesMaxDateTime = maxDateTime;
17757
17877
  res(positions);
17758
17878
  }
17759
17879
  catch (e) {
@@ -17779,6 +17899,13 @@ var TilesetCadRenderManager;
17779
17899
  if (this.disposed) {
17780
17900
  return;
17781
17901
  }
17902
+ // Reverse heading .
17903
+ // Not sure if calc or model is the issue.
17904
+ // This is all for a single demo until we figure out how we configure assemblies for this anyways.
17905
+ heading = heading + 180;
17906
+ if (heading > 360) {
17907
+ heading = heading - 360;
17908
+ }
17782
17909
  // Jank code that hacks the position to be different.
17783
17910
  // This can mess up our panels so we'll need to disable editing until a better system is in place.
17784
17911
  const location = Cartographic.fromCartesian(pos3d);
@@ -30631,7 +30758,7 @@ class WidgetViewBar extends Widget.AWidget {
30631
30758
  }
30632
30759
  }
30633
30760
 
30634
- const VERSION = "5.4.6";
30761
+ const VERSION = "5.4.7";
30635
30762
 
30636
30763
  export { VERSION, CesiumViewMonitor, ViewerUtils, ViewerEventTracker, MenuItemManager, isHistoricMetadataChanged, EntityRenderEngine, EntityRenderEnginePoint, EntityRenderEnginePolyline, EntityRenderEnginePolygon, EntityRenderEngineModel3d, MenuItemCreator, VisualsRegister, RenderManager, EntitiesIdsRenderManager, DataLabRenderManager, EntitiesLoadedRenderManager, EntitiesRenderManager, EntityRenderManager, TilesetCadRenderManager, TilesetArbRenderManager, TilesetEntitiesRenderManager, TilesetOsmRenderManager, TilesetPointcloudRenderManager, TilesetGooglePhotosRenderManager, DataSourceStaticKmlManager, GoogleSearchRenderManager, RelationsRenderManager, SharedGetters, CesiumParabola, EntityLabel, ViewRenderEngine, TileRenderEngine, TilesetRenderEngine, CESIUM_INSPECTOR_KEY, CESIUM_TIMELINE_KEY, CESIUM_TIMELINE_LIVE_KEY, CESIUM_TIMELINE_LIVE_PADDING_FORWARD_KEY, CESIUM_TIMELINE_LIVE_PADDING_BACKWARD_KEY, CESIUM_TIMELINE_INTERVAL_KEY, 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 };
30637
30764
  //# sourceMappingURL=bruce-cesium.es5.js.map