bruce-cesium 5.4.0 → 5.4.2

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.
Files changed (24) hide show
  1. package/dist/bruce-cesium.es5.js +585 -449
  2. package/dist/bruce-cesium.es5.js.map +1 -1
  3. package/dist/bruce-cesium.umd.js +587 -447
  4. package/dist/bruce-cesium.umd.js.map +1 -1
  5. package/dist/lib/bruce-cesium.js +1 -1
  6. package/dist/lib/rendering/cesium-animated-property.js +97 -45
  7. package/dist/lib/rendering/cesium-animated-property.js.map +1 -1
  8. package/dist/lib/rendering/getters/batched-data-getter.js +7 -1
  9. package/dist/lib/rendering/getters/batched-data-getter.js.map +1 -1
  10. package/dist/lib/rendering/render-managers/entities/entities-ids-render-manager.js +237 -357
  11. package/dist/lib/rendering/render-managers/entities/entities-ids-render-manager.js.map +1 -1
  12. package/dist/lib/rendering/render-managers/tilesets/tileset-cad-render-manager.js +172 -37
  13. package/dist/lib/rendering/render-managers/tilesets/tileset-cad-render-manager.js.map +1 -1
  14. package/dist/lib/rendering/view-render-engine.js +4 -2
  15. package/dist/lib/rendering/view-render-engine.js.map +1 -1
  16. package/dist/lib/utils/view-utils.js +62 -2
  17. package/dist/lib/utils/view-utils.js.map +1 -1
  18. package/dist/types/bruce-cesium.d.ts +1 -1
  19. package/dist/types/rendering/cesium-animated-property.d.ts +24 -21
  20. package/dist/types/rendering/getters/batched-data-getter.d.ts +3 -1
  21. package/dist/types/rendering/render-managers/entities/entities-ids-render-manager.d.ts +22 -7
  22. package/dist/types/rendering/render-managers/tilesets/tileset-cad-render-manager.d.ts +2 -2
  23. package/dist/types/utils/view-utils.d.ts +9 -0
  24. package/package.json +2 -2
@@ -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, ProjectViewLegacyTile, Camera, 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, SceneMode, GeoJsonDataSource, Primitive, Cesium3DTileFeature, Cesium3DTileStyle, Cesium3DTileColorBlendMode, HeadingPitchRange, Ion, KmlDataSource, SceneTransforms, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, CesiumInspector, OrthographicFrustum, defined, ClockRange, EasingFunction, NearFarScalar, EllipsoidGeodesic, sampleTerrainMostDetailed, Cesium3DTileset, PolygonPipeline, Matrix4, Matrix3, IonResource, BoundingSphere, GeometryInstance, Quaternion, ScreenSpaceEventHandler, ScreenSpaceEventType, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, CzmlDataSource, Intersect, Fullscreen } from 'cesium';
3
+ import { Cartographic, Cartesian2, Math as Math$1, Cartesian3, CallbackProperty, Color, HeightReference, Rectangle, JulianDate, Entity, DistanceDisplayCondition, HorizontalOrigin, VerticalOrigin, ConstantProperty, ClassificationType, ConstantPositionProperty, ArcType, CornerType, ShadowMode, ColorBlendMode, HeadingPitchRoll, Transforms, Model, PolygonHierarchy, PolylineGraphics, ColorMaterialProperty, Primitive, Cesium3DTileFeature, SceneMode, GeoJsonDataSource, Cesium3DTileStyle, HeadingPitchRange, Cesium3DTileColorBlendMode, Ion, KmlDataSource, SceneTransforms, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, CesiumInspector, OrthographicFrustum, defined, ClockRange, EasingFunction, NearFarScalar, Cesium3DTileset, Matrix4, Matrix3, IonResource, EllipsoidGeodesic, sampleTerrainMostDetailed, PolygonPipeline, BoundingSphere, GeometryInstance, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, ScreenSpaceEventHandler, ScreenSpaceEventType, Quaternion, CzmlDataSource, Intersect, Fullscreen } from 'cesium';
4
4
 
5
5
  const TIME_LAG = 300;
6
6
  const POSITION_CHECK_TIMER = 950;
@@ -1368,6 +1368,9 @@ var CesiumAnimatedProperty;
1368
1368
  }
1369
1369
  CesiumAnimatedProperty.AnimatePositionSeries = AnimatePositionSeries;
1370
1370
  function GetSeriesPossesForHistoricEntity(viewer, heightRef, historic) {
1371
+ if (!historic || !historic.length) {
1372
+ return [];
1373
+ }
1371
1374
  const series = [];
1372
1375
  for (let i = 0; i < historic.length; i++) {
1373
1376
  const item = historic[i];
@@ -1406,25 +1409,6 @@ var CesiumAnimatedProperty;
1406
1409
  return series;
1407
1410
  }
1408
1411
  CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity = GetSeriesPossesForHistoricEntity;
1409
- /**
1410
- * Animates a tileset position and heading from a series of positions over time.
1411
- * Unlike other animation functions that directly modify the tileset,
1412
- * this provides a callback with calculated values for the caller to apply.
1413
- *
1414
- * Example:
1415
- * ```
1416
- * const dispose = CesiumAnimatedProperty.AnimateTPositionSeries({
1417
- * viewer: viewer,
1418
- * posses: positionSeries,
1419
- * onUpdate: (position, heading) => {}
1420
- * });
1421
- *
1422
- * // To dispose:
1423
- * dispose();
1424
- * ```
1425
- * @param params Animation parameters
1426
- * @returns Function to stop the animation
1427
- */
1428
1412
  class AnimateTPositionSeries {
1429
1413
  constructor(params) {
1430
1414
  // Cache for calculated values.
@@ -1432,23 +1416,74 @@ var CesiumAnimatedProperty;
1432
1416
  this.lastCalcPos3d = null;
1433
1417
  this.lastCalcHeading = null;
1434
1418
  this.removal = null;
1419
+ // Track the timeline range for which we have data.
1420
+ this.minDate = null;
1421
+ this.maxDate = null;
1422
+ // Map of positions by timestamp for quick lookup and deduplication.
1423
+ this.positionsMap = new Map();
1435
1424
  this.viewer = params.viewer;
1436
1425
  this.onUpdate = params.onUpdate;
1437
1426
  this.onDone = params.onDone;
1438
- // No positions to animate.
1439
- if (!params.posses || params.posses.length === 0) {
1440
- return;
1427
+ this.onRangeChangeCallback = params.onRangeChange;
1428
+ // Initialize the positions map
1429
+ this.positionsMap = new Map();
1430
+ // Add initial positions
1431
+ if (params.posses && params.posses.length > 0) {
1432
+ this.addPositions(params.posses);
1441
1433
  }
1442
- // Order positions by date.
1443
- const orderedPosses = [...params.posses].sort((a, b) => {
1444
- return a.dateTime.getTime() - b.dateTime.getTime();
1445
- });
1446
- // Process headings - if all are null or 0, assume all are null.
1447
- this.positions = this.processHeadings([...orderedPosses]);
1448
1434
  // Set up the animation loop.
1449
1435
  this.removal = this.viewer.scene.postUpdate.addEventListener(() => this.update());
1450
1436
  this.update();
1451
1437
  }
1438
+ /**
1439
+ * Add new positions to the animation.
1440
+ * Will deduplicate positions with the same timestamp and sort them.
1441
+ * @param newPositions Positions to add
1442
+ */
1443
+ addPositions(newPositions) {
1444
+ if (!newPositions || newPositions.length === 0) {
1445
+ return;
1446
+ }
1447
+ // Add new positions to our map, overwriting any existing positions with the same timestamp
1448
+ for (const pos of newPositions) {
1449
+ const key = pos.dateTime.toISOString();
1450
+ this.positionsMap.set(key, pos);
1451
+ }
1452
+ // Rebuild our sorted array from the map
1453
+ this.positions = Array.from(this.positionsMap.values()).sort((a, b) => {
1454
+ return a.dateTime.getTime() - b.dateTime.getTime();
1455
+ });
1456
+ // Process headings
1457
+ this.positions = this.processHeadings([...this.positions]);
1458
+ // Update our min/max dates
1459
+ if (this.positions.length > 0) {
1460
+ const first = this.positions[0];
1461
+ const last = this.positions[this.positions.length - 1];
1462
+ if (!this.minDate || first.dateTime < this.minDate) {
1463
+ this.minDate = first.dateTime;
1464
+ }
1465
+ if (!this.maxDate || last.dateTime > this.maxDate) {
1466
+ this.maxDate = last.dateTime;
1467
+ }
1468
+ }
1469
+ }
1470
+ /**
1471
+ * Get the current date range covered by our positions.
1472
+ */
1473
+ getDateRange() {
1474
+ return {
1475
+ minDate: this.minDate,
1476
+ maxDate: this.maxDate
1477
+ };
1478
+ }
1479
+ /**
1480
+ * Trigger the onRangeChange callback to request more data.
1481
+ */
1482
+ onRangeChange() {
1483
+ if (this.onRangeChangeCallback) {
1484
+ this.onRangeChangeCallback();
1485
+ }
1486
+ }
1452
1487
  /**
1453
1488
  * Stop the animation and call the onDone callback if provided.
1454
1489
  */
@@ -1462,9 +1497,12 @@ var CesiumAnimatedProperty;
1462
1497
  }
1463
1498
  }
1464
1499
  /**
1465
- * Update function called on each frame.
1500
+ * Update function called on each render frame.
1466
1501
  */
1467
1502
  update() {
1503
+ if (!this.positions || this.positions.length === 0) {
1504
+ return;
1505
+ }
1468
1506
  let now = this.viewer.scene.lastRenderTime;
1469
1507
  if (!now) {
1470
1508
  now = this.viewer.clock.currentTime;
@@ -1506,11 +1544,19 @@ var CesiumAnimatedProperty;
1506
1544
  return posses;
1507
1545
  }
1508
1546
  /**
1509
- * Calculate the position at the given time
1547
+ * Calculate the position at the given time.
1510
1548
  */
1511
1549
  calculatePosition(currentTimeMs) {
1512
1550
  const posses = this.positions;
1513
- // See if we're before the first position
1551
+ // No positions to interpolate
1552
+ if (!posses || posses.length === 0) {
1553
+ return {
1554
+ pos3d: new Cartesian3(),
1555
+ indexLast: -1,
1556
+ indexNext: -1
1557
+ };
1558
+ }
1559
+ // See if we're before the first position.
1514
1560
  if (currentTimeMs <= posses[0].dateTime.getTime()) {
1515
1561
  return {
1516
1562
  pos3d: posses[0].pos3d,
@@ -1518,7 +1564,7 @@ var CesiumAnimatedProperty;
1518
1564
  indexNext: 0
1519
1565
  };
1520
1566
  }
1521
- // See if we're after the last position
1567
+ // See if we're after the last position.
1522
1568
  if (currentTimeMs >= posses[posses.length - 1].dateTime.getTime()) {
1523
1569
  const lastIndex = posses.length - 1;
1524
1570
  return {
@@ -1527,35 +1573,41 @@ var CesiumAnimatedProperty;
1527
1573
  indexNext: lastIndex
1528
1574
  };
1529
1575
  }
1530
- // Find the current position
1576
+ // Binary search to find the closest position.
1577
+ let start = 0;
1578
+ let end = posses.length - 1;
1531
1579
  let lastIndex = 0;
1532
- for (let i = 1; i < posses.length; i++) {
1533
- let pos = posses[i];
1534
- if (currentTimeMs >= pos.dateTime.getTime()) {
1535
- lastIndex = i;
1580
+ while (start <= end) {
1581
+ const mid = Math.floor((start + end) / 2);
1582
+ const midTime = posses[mid].dateTime.getTime();
1583
+ if (midTime <= currentTimeMs) {
1584
+ lastIndex = mid;
1585
+ start = mid + 1;
1536
1586
  }
1537
1587
  else {
1538
- break;
1588
+ end = mid - 1;
1539
1589
  }
1540
1590
  }
1541
1591
  const last = posses[lastIndex];
1542
- const next = posses[lastIndex + 1];
1543
- // If no next position, use the last one
1544
- if (!next) {
1592
+ const nextIndex = Math.min(lastIndex + 1, posses.length - 1);
1593
+ const next = posses[nextIndex];
1594
+ // If we found an exact match or there's no next position.
1595
+ if (currentTimeMs === last.dateTime.getTime() || lastIndex === nextIndex) {
1545
1596
  return {
1546
1597
  pos3d: last.pos3d,
1547
1598
  indexLast: lastIndex,
1548
1599
  indexNext: lastIndex
1549
1600
  };
1550
1601
  }
1551
- // Interpolate the position
1552
- const progress = (currentTimeMs - last.dateTime.getTime()) /
1553
- (next.dateTime.getTime() - last.dateTime.getTime());
1602
+ // Interpolate between the two closest positions.
1603
+ const lastTime = last.dateTime.getTime();
1604
+ const nextTime = next.dateTime.getTime();
1605
+ const progress = (currentTimeMs - lastTime) / (nextTime - lastTime);
1554
1606
  const interpolatedPos = Cartesian3.lerp(last.pos3d, next.pos3d, progress, new Cartesian3());
1555
1607
  return {
1556
1608
  pos3d: interpolatedPos,
1557
1609
  indexLast: lastIndex,
1558
- indexNext: lastIndex + 1
1610
+ indexNext: nextIndex
1559
1611
  };
1560
1612
  }
1561
1613
  /**
@@ -5238,6 +5290,12 @@ async function getStyle(api, entity, styleId) {
5238
5290
 
5239
5291
  const CESIUM_INSPECTOR_KEY = "_nextspace_inspector";
5240
5292
  const CESIUM_TIMELINE_KEY = "_nextspace_timeline";
5293
+ const CESIUM_TIMELINE_LIVE_KEY = "_nextspace_timeline_live";
5294
+ const CESIUM_TIMELINE_LIVE_PADDING_FORWARD_KEY = "_nextspace_timeline_live_padding_forward";
5295
+ const CESIUM_TIMELINE_LIVE_PADDING_BACKWARD_KEY = "_nextspace_timeline_live_padding_backward";
5296
+ const CESIUM_TIMELINE_INTERVAL_KEY = "_nextspace_timeline_interval";
5297
+ const DEFAULT_LIVE_PADDING_FORWARD_SECONDS = 5 * 60;
5298
+ const DEFAULT_LIVE_PADDING_BACKWARD_SECONDS = 25 * 60;
5241
5299
  var ViewUtils;
5242
5300
  (function (ViewUtils) {
5243
5301
  function GatherLegacyMapTiles(params) {
@@ -5460,7 +5518,10 @@ var ViewUtils;
5460
5518
  currentTimeIso: clock.currentTime.toString(),
5461
5519
  stopTime: clock.stopTime,
5462
5520
  stopTimeIso: clock.stopTime.toString(),
5463
- areCesiumValues: clock[CESIUM_TIMELINE_KEY] != true
5521
+ areCesiumValues: clock[CESIUM_TIMELINE_KEY] != true,
5522
+ isLive: clock[CESIUM_TIMELINE_LIVE_KEY] == true,
5523
+ livePaddingForwardSeconds: clock[CESIUM_TIMELINE_LIVE_PADDING_FORWARD_KEY] || DEFAULT_LIVE_PADDING_FORWARD_SECONDS,
5524
+ livePaddingBackwardSeconds: clock[CESIUM_TIMELINE_LIVE_PADDING_BACKWARD_KEY] || DEFAULT_LIVE_PADDING_BACKWARD_SECONDS
5464
5525
  };
5465
5526
  }
5466
5527
  ViewUtils.GetTimeDetails = GetTimeDetails;
@@ -5518,8 +5579,59 @@ var ViewUtils;
5518
5579
  }
5519
5580
  viewer.scene.requestRender();
5520
5581
  clock[CESIUM_TIMELINE_KEY] = true;
5582
+ clock[CESIUM_TIMELINE_LIVE_KEY] = params.isLive == true;
5583
+ clock[CESIUM_TIMELINE_LIVE_PADDING_FORWARD_KEY] = params.livePaddingSeconds || DEFAULT_LIVE_PADDING_FORWARD_SECONDS;
5584
+ clock[CESIUM_TIMELINE_LIVE_PADDING_BACKWARD_KEY] = params.livePaddingSeconds || DEFAULT_LIVE_PADDING_BACKWARD_SECONDS;
5585
+ if (params.isLive) {
5586
+ startLive(viewer);
5587
+ }
5588
+ else {
5589
+ stopLive(viewer);
5590
+ }
5521
5591
  }
5522
5592
  ViewUtils.SetTimeDetails = SetTimeDetails;
5593
+ function startLive(viewer) {
5594
+ if (viewer[CESIUM_TIMELINE_INTERVAL_KEY]) {
5595
+ // Already live.
5596
+ return;
5597
+ }
5598
+ const doUpdate = () => {
5599
+ if (!viewer || viewer.isDestroyed() || !viewer.scene) {
5600
+ stopLive(viewer);
5601
+ return;
5602
+ }
5603
+ // Get the current settings.
5604
+ const tDetails = GetTimeDetails({ viewer });
5605
+ const clock = viewer.clock;
5606
+ if (clock) {
5607
+ clock.shouldAnimate = true;
5608
+ clock.multiplier = 1;
5609
+ clock.currentTime = JulianDate.now();
5610
+ clock.startTime = JulianDate.addSeconds(clock.currentTime, -tDetails.livePaddingBackwardSeconds, new JulianDate());
5611
+ clock.stopTime = JulianDate.addSeconds(clock.currentTime, tDetails.livePaddingForwardSeconds, new JulianDate());
5612
+ clock.clockRange = ClockRange.UNBOUNDED;
5613
+ }
5614
+ };
5615
+ // We'll start an interval that occasionally updates the Cesium clock to stay in the desired settings.
5616
+ // Since it moves live (1 second per second), we don't have to worry about the ranges being out of date quickly.
5617
+ viewer[CESIUM_TIMELINE_INTERVAL_KEY] = setInterval(() => {
5618
+ doUpdate();
5619
+ }, 800);
5620
+ viewer[CESIUM_TIMELINE_LIVE_KEY] = true;
5621
+ // Initial update.
5622
+ doUpdate();
5623
+ }
5624
+ function stopLive(viewer) {
5625
+ if (!viewer) {
5626
+ return;
5627
+ }
5628
+ let interval = viewer[CESIUM_TIMELINE_INTERVAL_KEY];
5629
+ if (interval) {
5630
+ clearInterval(interval);
5631
+ viewer[CESIUM_TIMELINE_INTERVAL_KEY] = null;
5632
+ }
5633
+ viewer[CESIUM_TIMELINE_LIVE_KEY] = false;
5634
+ }
5523
5635
  })(ViewUtils || (ViewUtils = {}));
5524
5636
 
5525
5637
  const MODEL_MIN_RADIUS = 10;
@@ -13169,14 +13281,16 @@ var BatchedDataGetter;
13169
13281
  }
13170
13282
  return this.onUpdate;
13171
13283
  }
13172
- constructor(data, viewPort, batchSize) {
13284
+ constructor(data, viewPort, batchSize, singleEmit = false) {
13173
13285
  this.viewPortRemoval = null;
13174
13286
  this.viewRect = null;
13175
13287
  this.viewCenter = null;
13176
13288
  this.onUpdate = null;
13289
+ this.emitted = false;
13177
13290
  this.data = data;
13178
13291
  this.viewPort = viewPort;
13179
13292
  this.batchSize = batchSize;
13293
+ this.singleEmit = singleEmit;
13180
13294
  }
13181
13295
  Start() {
13182
13296
  this.viewPortRemoval = this.viewPort.Updated().Subscribe(() => {
@@ -13207,6 +13321,10 @@ var BatchedDataGetter;
13207
13321
  }
13208
13322
  }
13209
13323
  startGetterLoop() {
13324
+ if (this.singleEmit && this.emitted) {
13325
+ return;
13326
+ }
13327
+ this.emitted = true;
13210
13328
  clearInterval(this.getterInterval);
13211
13329
  if (!this.viewRect || !this.viewCenter) {
13212
13330
  return;
@@ -13939,19 +14057,6 @@ var EntitiesLoadedRenderManager;
13939
14057
  })(EntitiesLoadedRenderManager || (EntitiesLoadedRenderManager = {}));
13940
14058
 
13941
14059
  const BATCH_SIZE$2 = 500;
13942
- function getValue$4(viewer, obj) {
13943
- if (obj === null || obj === void 0 ? void 0 : obj.getValue) {
13944
- let date = viewer.scene.lastRenderTime;
13945
- if (!date) {
13946
- date = viewer.clock.currentTime;
13947
- }
13948
- return obj.getValue(date);
13949
- }
13950
- return obj;
13951
- }
13952
- function colorToCColor$3(color) {
13953
- return new Color(color.red ? color.red / 255 : 0, color.green ? color.green / 255 : 0, color.blue ? color.blue / 255 : 0, color.alpha);
13954
- }
13955
14060
  /**
13956
14061
  * Render manager for rendering an array of entity ids.
13957
14062
  * This will render them in batches to avoid overloading the viewer.
@@ -13968,23 +14073,27 @@ var EntitiesIdsRenderManager;
13968
14073
  this.getterSub = null;
13969
14074
  this.disposed = false;
13970
14075
  this.renderedEntities = {};
13971
- this.sources = [];
13972
- // Highly experimental flag to try improve rendering large sets of polygons and polylines.
13973
- // Many things are not supported when this is enabled.
13974
- this.useGeojson = false;
14076
+ // Callback to dispose the monitoring of the viewer's timeline for historic data.
14077
+ this.viewerDateTimeChangeRemoval = null;
14078
+ // Entity ID -> historic information on the Entity.
14079
+ // This helps us manage what needs changing whenever we receive the same Entity update, or when the timeline range changes.
14080
+ this.entitiesHistoric = {};
14081
+ // Queue for renderAsIndividuals to avoid concurrent calls.
14082
+ this.renderQueueActive = false;
14083
+ this.renderQueue = [];
14084
+ this.renderQueueForceFlags = [];
13975
14085
  const { viewer, apiGetter, monitor, item, register: visualsManager } = params;
13976
14086
  this.viewer = viewer;
13977
14087
  this.apiGetter = apiGetter;
13978
14088
  this.monitor = monitor;
13979
14089
  this.item = item;
13980
14090
  this.visualsManager = visualsManager;
13981
- this.useGeojson = item.renderAsGeojson == true;
13982
14091
  if (this.item.enableClustering) {
13983
14092
  this.clustering = new PointClustering(visualsManager, this.item.id, (_a = this.item) === null || _a === void 0 ? void 0 : _a.clustering);
13984
14093
  }
13985
14094
  }
13986
14095
  Init() {
13987
- var _a;
14096
+ var _a, _b, _c;
13988
14097
  if (this.disposed) {
13989
14098
  throw (new Error("This item is disposed."));
13990
14099
  }
@@ -14004,15 +14113,19 @@ var EntitiesIdsRenderManager;
14004
14113
  if (this.renderPriority == null) {
14005
14114
  this.renderPriority = 2;
14006
14115
  }
14007
- if (this.renderAsGeojson && this.item.CameraZoomSettings.length > 1) {
14008
- console.warn("Geojson rendering does not support multiple zoom controls. Only the first one will be used.");
14009
- this.item.CameraZoomSettings = [this.item.CameraZoomSettings[0]];
14010
- }
14011
- this.getter = new BatchedDataGetter.Getter(this.item.BruceEntity.EntityIds, this.monitor, BATCH_SIZE$2);
14116
+ this.getter = new BatchedDataGetter.Getter(this.item.BruceEntity.EntityIds, this.monitor, BATCH_SIZE$2,
14117
+ // Don't emit the same Entity multiple times.
14118
+ true);
14012
14119
  this.getterSub = this.getter.OnUpdate.Subscribe((ids) => {
14013
14120
  this.onGetterUpdate(ids);
14014
14121
  });
14015
14122
  this.getter.Start();
14123
+ if (((_b = this.item.BruceEntity) === null || _b === void 0 ? void 0 : _b.historic) || ((_c = this.item.BruceEntity) === null || _c === void 0 ? void 0 : _c.historicAttrKey)) {
14124
+ this.viewerDateTimeSub();
14125
+ }
14126
+ else {
14127
+ this.viewerDateTimeDispose();
14128
+ }
14016
14129
  }
14017
14130
  Dispose() {
14018
14131
  if (this.disposed) {
@@ -14030,11 +14143,11 @@ var EntitiesIdsRenderManager;
14030
14143
  });
14031
14144
  (_b = this.clustering) === null || _b === void 0 ? void 0 : _b.Dispose();
14032
14145
  this.clustering = null;
14033
- for (let i = 0; i < this.sources.length; i++) {
14034
- const source = this.sources[i];
14035
- this.viewer.dataSources.remove(source);
14036
- }
14037
- this.sources = [];
14146
+ this.viewerDateTimeDispose();
14147
+ // Clear render queue when disposed
14148
+ this.renderQueue = [];
14149
+ this.renderQueueForceFlags = [];
14150
+ this.renderQueueActive = false;
14038
14151
  }
14039
14152
  async ReRender(params) {
14040
14153
  let { entityIds, force, entities } = params;
@@ -14069,10 +14182,6 @@ var EntitiesIdsRenderManager;
14069
14182
  }
14070
14183
  if (CameraZoomSettings === null || CameraZoomSettings === void 0 ? void 0 : CameraZoomSettings.length) {
14071
14184
  this.item.CameraZoomSettings = CameraZoomSettings;
14072
- if (this.renderAsGeojson && this.item.CameraZoomSettings.length > 1) {
14073
- console.warn("Geojson rendering does not support multiple zoom controls. Only the first one will be used.");
14074
- this.item.CameraZoomSettings = [this.item.CameraZoomSettings[0]];
14075
- }
14076
14185
  }
14077
14186
  if (queueRerender != false) {
14078
14187
  const entityIds = Object.keys(this.renderedEntities);
@@ -14084,15 +14193,25 @@ var EntitiesIdsRenderManager;
14084
14193
  }
14085
14194
  }
14086
14195
  async onGetterUpdate(entityIds, force = false) {
14087
- if (this.disposed || this.viewer.isDestroyed()) {
14196
+ if (this.disposed || this.viewer.isDestroyed() || !(entityIds === null || entityIds === void 0 ? void 0 : entityIds.length)) {
14088
14197
  return;
14089
14198
  }
14090
14199
  try {
14091
14200
  const api = this.apiGetter.getApi();
14201
+ let newDateTime = null;
14202
+ if (this.item.BruceEntity.historic || this.item.BruceEntity.historicAttrKey) {
14203
+ newDateTime = JulianDate.toDate(this.viewer.clock.currentTime).toISOString();
14204
+ }
14092
14205
  const { entities } = await Entity$1.GetListByIds({
14093
14206
  api,
14094
14207
  entityIds,
14095
- migrated: true
14208
+ historicPoint: newDateTime,
14209
+ historicKey: this.item.BruceEntity.historicAttrKey,
14210
+ migrated: true,
14211
+ // If we're taking 5+ minutes to make a query, it's a dud.
14212
+ // This is a timeout imposed on our DB and not external sources.
14213
+ // Honestly could lower down to 30 seconds, but we'll keep it high for now.
14214
+ maxSearchTimeSec: 60 * 5,
14096
14215
  });
14097
14216
  await this.renderEntities(entities, force);
14098
14217
  }
@@ -14101,347 +14220,82 @@ var EntitiesIdsRenderManager;
14101
14220
  }
14102
14221
  }
14103
14222
  async renderEntities(entities, force = false) {
14104
- if (this.disposed || this.viewer.isDestroyed()) {
14223
+ if (this.disposed || this.viewer.isDestroyed() || !(entities === null || entities === void 0 ? void 0 : entities.length)) {
14105
14224
  return;
14106
14225
  }
14107
14226
  try {
14108
- if (this.useGeojson) {
14109
- const zoomItem = this.item.CameraZoomSettings[0];
14110
- if (zoomItem.DisplayType == ZoomControl.EDisplayType.Point) {
14111
- // We'll just render these as individuals since we don't support point geojson.
14112
- await this.renderAsIndividuals(entities, force);
14113
- }
14114
- else {
14115
- await this.renderAsGeojson(entities, force);
14116
- }
14117
- }
14118
- else {
14119
- await this.renderAsIndividuals(entities, force);
14120
- }
14227
+ await this.queueRenderAsIndividuals(entities, force);
14121
14228
  }
14122
14229
  catch (e) {
14123
14230
  console.error(e);
14124
14231
  }
14125
14232
  }
14126
14233
  /**
14127
- * Our optimized and more stable path.
14128
- * We construct a geojson that we draw in one go.
14129
- * @param entities
14130
- * @param force TODO: This should re-render entities that are already rendered.
14234
+ * Queues a render operation to be processed in sequence.
14235
+ * If an operation is already in progress, this will be added to the queue.
14131
14236
  */
14132
- async renderAsGeojson(entities, force) {
14133
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
14134
- entities = entities.filter((entity) => {
14135
- var _a;
14136
- return !this.renderedEntities[(_a = entity.Bruce) === null || _a === void 0 ? void 0 : _a.ID];
14137
- });
14138
- // Mark these as rendered.
14139
- entities.forEach((entity) => {
14140
- var _a;
14141
- this.renderedEntities[(_a = entity.Bruce) === null || _a === void 0 ? void 0 : _a.ID] = true;
14142
- });
14143
- // This process only supports a single zoom control item.
14144
- const zoomItem = this.item.CameraZoomSettings[0];
14145
- // If we're after 3d models then we'll check if there are LODs and render those as individuals.
14146
- if (zoomItem.DisplayType == ZoomControl.EDisplayType.Model3D && entities.length) {
14147
- const { lods } = await EntityLod.GetLods({
14148
- api: this.apiGetter.getApi(),
14149
- filter: {
14150
- externalSources: false,
14151
- Items: entities.map(x => {
14152
- var _a, _b;
14153
- return {
14154
- entityId: (_a = x.Bruce) === null || _a === void 0 ? void 0 : _a.ID,
14155
- categoryId: (_b = zoomItem.LODCategoryID) !== null && _b !== void 0 ? _b : "GLB",
14156
- group: "DEFAULT",
14157
- level: Number(zoomItem.LODLevel)
14158
- };
14159
- }),
14160
- strict: false
14161
- }
14162
- });
14163
- if (this.disposed) {
14164
- this.doDispose();
14165
- return;
14166
- }
14167
- const withLods = lods.filter(x => x.entityId && !!x.clientFileId).map(x => x.entityId);
14168
- const individuals = entities.filter((entity) => {
14169
- var _a;
14170
- return withLods.includes((_a = entity.Bruce) === null || _a === void 0 ? void 0 : _a.ID);
14171
- });
14172
- if (individuals.length) {
14173
- this.renderAsIndividuals(individuals, force);
14174
- }
14175
- // Now we proceed with what is left.
14176
- entities = entities.filter((entity) => {
14177
- var _a;
14178
- return !withLods.includes((_a = entity.Bruce) === null || _a === void 0 ? void 0 : _a.ID);
14179
- });
14180
- }
14181
- if (!entities.length) {
14237
+ async queueRenderAsIndividuals(entities, force = false) {
14238
+ if (this.disposed) {
14182
14239
  return;
14183
14240
  }
14184
- let style = null;
14185
- if ((zoomItem === null || zoomItem === void 0 ? void 0 : zoomItem.StyleID) && (zoomItem === null || zoomItem === void 0 ? void 0 : zoomItem.StyleID) > -1) {
14186
- try {
14187
- style = (_a = (await Style.Get({
14188
- api: this.apiGetter.getApi(),
14189
- styleId: zoomItem === null || zoomItem === void 0 ? void 0 : zoomItem.StyleID
14190
- })).style) === null || _a === void 0 ? void 0 : _a.Settings;
14191
- }
14192
- // Probably deleted.
14193
- catch (e) {
14194
- console.error(e);
14195
- }
14241
+ // If no active render is happening, start processing immediately.
14242
+ if (!this.renderQueueActive) {
14243
+ this.renderQueueActive = true;
14244
+ await this.processRenderQueue(entities, force);
14245
+ return;
14196
14246
  }
14197
- let entityTypeId = (_b = this.item.BruceEntity) === null || _b === void 0 ? void 0 : _b["EntityType.ID"];
14198
- if (!entityTypeId) {
14199
- entityTypeId = (_d = (_c = entities.find(x => { var _a; return !!((_a = x.Bruce) === null || _a === void 0 ? void 0 : _a["EntityType.ID"]); })) === null || _c === void 0 ? void 0 : _c.Bruce) === null || _d === void 0 ? void 0 : _d["EntityType.ID"];
14247
+ // Otherwise, add to the queue.
14248
+ this.renderQueue.push(entities);
14249
+ this.renderQueueForceFlags.push(force);
14250
+ }
14251
+ /**
14252
+ * Processes the render queue, handling one rendering operation at a time.
14253
+ */
14254
+ async processRenderQueue(initialEntities, initialForce = false) {
14255
+ if (this.disposed) {
14256
+ this.renderQueueActive = false;
14257
+ return;
14200
14258
  }
14201
- // Getting type regardless cause it's needed for name calculations.
14202
- let entityType;
14203
- if (entityTypeId) {
14204
- // Try using the Entity Type default if one is specified in the menu item.
14205
- try {
14206
- entityType = (_e = (await EntityType.Get({
14207
- entityTypeId: entityTypeId,
14208
- api: this.apiGetter.getApi()
14209
- }))) === null || _e === void 0 ? void 0 : _e.entityType;
14210
- if (!style && entityType) {
14211
- if (entityType["DisplaySetting.ID"] && entityType["DisplaySetting.ID"] > 0) {
14212
- style = (_f = (await Style.Get({
14213
- api: this.apiGetter.getApi(),
14214
- styleId: entityType["DisplaySetting.ID"]
14215
- })).style) === null || _f === void 0 ? void 0 : _f.Settings;
14259
+ try {
14260
+ // Process the initial batch.
14261
+ await this.renderAsIndividuals(initialEntities, initialForce);
14262
+ if (this.disposed) {
14263
+ return;
14264
+ }
14265
+ // Process any queued items.
14266
+ while (this.renderQueue.length > 0 && !this.disposed) {
14267
+ // If multiple entities are waiting, combine them to reduce render operations.
14268
+ const allEntities = [];
14269
+ const forces = [];
14270
+ // Collect all pending entities.
14271
+ while (this.renderQueue.length > 0) {
14272
+ const entities = this.renderQueue.shift();
14273
+ const force = this.renderQueueForceFlags.shift();
14274
+ if (entities && entities.length > 0) {
14275
+ allEntities.push(...entities);
14276
+ forces.push(force);
14216
14277
  }
14217
14278
  }
14279
+ if (allEntities.length > 0) {
14280
+ // If any are forced, we need to force the render.
14281
+ const combinedForce = forces.some(f => f === true);
14282
+ const uniqueEntities = this.deduplicateEntities(allEntities);
14283
+ await this.renderAsIndividuals(uniqueEntities, combinedForce);
14284
+ }
14218
14285
  }
14219
- catch (e) {
14220
- console.error(e);
14221
- }
14222
- }
14223
- const pStyle = (_g = style === null || style === void 0 ? void 0 : style.polygonStyle) !== null && _g !== void 0 ? _g : {};
14224
- const lStyle = (_h = style === null || style === void 0 ? void 0 : style.polylineStyle) !== null && _h !== void 0 ? _h : {};
14225
- const polygonsClamped = ((_j = pStyle === null || pStyle === void 0 ? void 0 : pStyle.altitudeOption) === null || _j === void 0 ? void 0 : _j.id) == null ? true : ((_k = pStyle === null || pStyle === void 0 ? void 0 : pStyle.altitudeOption) === null || _k === void 0 ? void 0 : _k.id) == 0;
14226
- const bFillColor = Calculator.GetColor(pStyle.fillColor, {}, []);
14227
- const cFillColor = bFillColor ? colorToCColor$3(bFillColor) : Color.fromCssColorString("rgba(139, 195, 74, 0.8)");
14228
- const bLineColor = Calculator.GetColor(pStyle.lineColor, {}, []);
14229
- const cLineColor = bLineColor ? colorToCColor$3(bLineColor) : Color.fromCssColorString("rgba(80, 80, 80, 0.8)");
14230
- let lineWidthPx = pStyle.lineWidth ? Calculator.GetNumber(pStyle.lineWidth, {}, []) : null;
14231
- if (lineWidthPx == null) {
14232
- lineWidthPx = 1;
14233
14286
  }
14234
- lineWidthPx = EnsureNumber(lineWidthPx);
14235
- if (lineWidthPx < 0.01) {
14236
- lineWidthPx = 0;
14287
+ finally {
14288
+ this.renderQueueActive = false;
14237
14289
  }
14238
- lineWidthPx = Math.round(lineWidthPx);
14239
- const collection = {
14240
- features: [],
14241
- type: "FeatureCollection"
14242
- };
14243
- for (let i = 0; i < entities.length; i++) {
14244
- const entity = entities[i];
14245
- const feature = Geometry.ToGeoJsonFeature({
14246
- geometry: entity.Bruce.VectorGeometry,
14247
- noAltitude: polygonsClamped && lineWidthPx <= 0,
14248
- altitude: lineWidthPx > 0 && polygonsClamped ? 1 : null,
14249
- properties: {
14250
- ...entity,
14251
- Bruce: {
14252
- ...entity.Bruce,
14253
- // Exclude as we just converted it to geojson.
14254
- VectorGeometry: null
14255
- }
14256
- }
14257
- });
14258
- if (feature) {
14259
- collection.features.push(feature);
14290
+ }
14291
+ deduplicateEntities(entities) {
14292
+ const entityMap = new Map();
14293
+ for (const entity of entities) {
14294
+ if (entity && entity.Bruce && entity.Bruce.ID) {
14295
+ entityMap.set(entity.Bruce.ID, entity);
14260
14296
  }
14261
14297
  }
14262
- // Anything that failed to turn into geojson or points we'll render as individuals.
14263
- const individuals = entities.filter((entity) => {
14264
- const feature = collection.features.find(x => { var _a, _b; return ((_b = (_a = x.properties) === null || _a === void 0 ? void 0 : _a.Bruce) === null || _b === void 0 ? void 0 : _b.ID) == entity.Bruce.ID; });
14265
- if (!feature) {
14266
- return true;
14267
- }
14268
- if (feature.geometry.type == "Point") {
14269
- return true;
14270
- }
14271
- return false;
14272
- });
14273
- if (individuals.length) {
14274
- this.renderAsIndividuals(individuals, force);
14275
- }
14276
- // Filter out points (the ones we just rendered as individuals).
14277
- collection.features = collection.features.filter(x => x.geometry.type != "Point");
14278
- // If there is nothing to render now, return.
14279
- if (!collection.features.length) {
14280
- return;
14281
- }
14282
- const source = await GeoJsonDataSource.load(collection, {
14283
- stroke: cLineColor,
14284
- fill: cFillColor,
14285
- strokeWidth: lineWidthPx,
14286
- clampToGround: lineWidthPx <= 0 && polygonsClamped
14287
- });
14288
- this.viewer.dataSources.add(source);
14289
- this.sources.push(source);
14290
- if (this.disposed) {
14291
- this.doDispose();
14292
- return;
14293
- }
14294
- const groups = [];
14295
- /**
14296
- * Applies individual styling to a rendered entity.
14297
- * This will exclude tag styling for now since it's harder to load that on the fly.
14298
- * @param thing
14299
- * @param entityId
14300
- * @param data
14301
- * @returns
14302
- */
14303
- const applyStyle = (thing, entityId, data) => {
14304
- if (thing.polygon) {
14305
- const bFillColor = Calculator.GetColor(pStyle.fillColor, data, []);
14306
- const cFillColor = bFillColor ? colorToCColor$3(bFillColor) : Color.fromCssColorString("rgba(139, 195, 74, 0.8)");
14307
- const bLineColor = Calculator.GetColor(pStyle.lineColor, data, []);
14308
- const cLineColor = bLineColor ? colorToCColor$3(bLineColor) : Color.fromCssColorString("rgba(80, 80, 80, 0.8)");
14309
- let width = pStyle.lineWidth ? Calculator.GetNumber(pStyle.lineWidth, data, []) : null;
14310
- if (width == null) {
14311
- width = 1;
14312
- }
14313
- width = EnsureNumber(width);
14314
- if (width < 0.01) {
14315
- width = 0;
14316
- }
14317
- let curFillColor = getValue$4(this.viewer, thing.polygon.material);
14318
- if (curFillColor && curFillColor instanceof ColorMaterialProperty) {
14319
- curFillColor = curFillColor.color;
14320
- }
14321
- let curLineColor = getValue$4(this.viewer, thing.polygon.outlineColor);
14322
- if (curLineColor && curLineColor instanceof ColorMaterialProperty) {
14323
- curLineColor = curLineColor.color;
14324
- }
14325
- const curWidth = getValue$4(this.viewer, thing.polygon.outlineWidth);
14326
- if ((curFillColor instanceof Color && curFillColor.equals(cFillColor)) &&
14327
- (curLineColor instanceof Color && curLineColor.equals(cLineColor)) &&
14328
- curWidth == width) {
14329
- return;
14330
- }
14331
- thing.polygon.material = new ColorMaterialProperty(cFillColor);
14332
- thing.polygon.outlineColor = new ConstantProperty(cLineColor);
14333
- thing.polygon.outlineWidth = new ConstantProperty(width);
14334
- }
14335
- else if (thing.polyline) {
14336
- const bColor = lStyle.lineColor ? Calculator.GetColor(lStyle.lineColor, data, []) : null;
14337
- const cColor = bColor ? colorToCColor$3(bColor) : Color.fromCssColorString("rgba(255, 193, 7, 0.8)");
14338
- let width = lStyle.lineWidth ? Calculator.GetNumber(lStyle.lineWidth, data, []) : null;
14339
- if (width == null) {
14340
- width = 2;
14341
- }
14342
- width = EnsureNumber(width);
14343
- if (width < 0.01) {
14344
- width = 0;
14345
- }
14346
- let curColor = getValue$4(this.viewer, thing.polyline.material);
14347
- if (curColor && curColor instanceof ColorMaterialProperty) {
14348
- curColor = curColor.color;
14349
- }
14350
- const curWidth = getValue$4(this.viewer, thing.polyline.width);
14351
- if ((curColor instanceof Color && curColor.equals(cColor)) &&
14352
- curWidth == width) {
14353
- return;
14354
- }
14355
- thing.polyline.material = new ColorMaterialProperty(cColor);
14356
- thing.polyline.width = new ConstantProperty(width);
14357
- }
14358
- };
14359
- let toForceUpdate = [];
14360
- /**
14361
- * Registers a given cesium entity produced from rendering geojson.
14362
- * Since one nextspace entity can have multiple cesium entities, there is special logic to group them together.
14363
- * @param thing
14364
- * @returns
14365
- */
14366
- const register = (thing) => {
14367
- var _a, _b, _c, _d;
14368
- // See if the cesium entity already exists in a group.
14369
- let group = groups.find((x) => { var _a; return ((_a = x.visual) === null || _a === void 0 ? void 0 : _a.id) == thing.id || x.siblings.find(x => (x === null || x === void 0 ? void 0 : x.id) == thing.id); });
14370
- if (group) {
14371
- return;
14372
- }
14373
- const metadata = getValue$4(this.viewer, thing === null || thing === void 0 ? void 0 : thing.properties);
14374
- const entityId = (_a = metadata === null || metadata === void 0 ? void 0 : metadata.Bruce) === null || _a === void 0 ? void 0 : _a.ID;
14375
- if (!entityId) {
14376
- return;
14377
- }
14378
- // Find group for the nextspace entity ID.
14379
- group = groups.find((x) => x.entityId == entityId);
14380
- // No group yet. We can designate this as the primary entity and create a new group for it.
14381
- if (!group) {
14382
- group = {
14383
- entityId: entityId,
14384
- visual: thing,
14385
- tagIds: (_b = metadata === null || metadata === void 0 ? void 0 : metadata.Bruce) === null || _b === void 0 ? void 0 : _b["Layer.ID"],
14386
- entityTypeId: (_c = metadata === null || metadata === void 0 ? void 0 : metadata.Bruce) === null || _c === void 0 ? void 0 : _c["EntityType.ID"],
14387
- siblings: [],
14388
- data: entities.find(x => { var _a; return ((_a = x.Bruce) === null || _a === void 0 ? void 0 : _a.ID) == entityId; }),
14389
- rego: null
14390
- };
14391
- groups.push(group);
14392
- applyStyle(thing, entityId, group.data);
14393
- const rego = {
14394
- entityId: entityId,
14395
- menuItemId: this.item.id,
14396
- menuItemType: this.item.Type,
14397
- visual: thing,
14398
- priority: this.renderPriority,
14399
- entityTypeId: group.entityTypeId,
14400
- accountId: this.apiGetter.accountId,
14401
- tagIds: group.tagIds ? [].concat(group.tagIds) : [],
14402
- name: entityType ? (_d = Entity$1.CalculateName({
14403
- entity: group.data,
14404
- type: entityType,
14405
- defaultToId: false
14406
- })) !== null && _d !== void 0 ? _d : "Unnamed Entity" : "Unnamed Entity",
14407
- cdn: this.item.cdnEnabled,
14408
- collection: source.entities
14409
- };
14410
- group.rego = rego;
14411
- this.visualsManager.AddRego({
14412
- rego,
14413
- requestRender: false
14414
- });
14415
- }
14416
- // Found a group. We flag this as a sibling entity of the primary.
14417
- else {
14418
- applyStyle(thing, entityId, group.data);
14419
- group.siblings.push(thing);
14420
- group.visual._siblingGraphics = group.siblings;
14421
- thing._parentEntity = group.visual;
14422
- if (group.rego) {
14423
- this.visualsManager.RefreshMark({
14424
- rego: group.rego
14425
- });
14426
- if (!toForceUpdate.includes(entityId)) {
14427
- toForceUpdate.push(entityId);
14428
- }
14429
- }
14430
- }
14431
- };
14432
- const sEntities = source.entities.values;
14433
- for (let i = 0; i < sEntities.length; i++) {
14434
- const cEntity = sEntities[i];
14435
- register(cEntity);
14436
- }
14437
- if (toForceUpdate.length) {
14438
- this.visualsManager.ForceUpdate({
14439
- entityIds: toForceUpdate,
14440
- refreshColors: true,
14441
- requestRender: false
14442
- });
14443
- }
14444
- this.viewer.scene.requestRender();
14298
+ return Array.from(entityMap.values());
14445
14299
  }
14446
14300
  /**
14447
14301
  * Our default path.
@@ -14452,16 +14306,28 @@ var EntitiesIdsRenderManager;
14452
14306
  */
14453
14307
  async renderAsIndividuals(entities, force = false) {
14454
14308
  var _a;
14309
+ if (this.disposed || this.viewer.isDestroyed() || !entities.length) {
14310
+ return;
14311
+ }
14312
+ let entitiesHistoric = {};
14313
+ if ((this.item.BruceEntity.historic || this.item.BruceEntity.historicAttrKey) && entities.length) {
14314
+ entitiesHistoric = await this.getHistoricInfo(entities);
14315
+ }
14455
14316
  const { updated, entities: cEntities } = await EntityRenderEngine.Render({
14456
14317
  viewer: this.viewer,
14457
14318
  apiGetter: this.apiGetter,
14458
14319
  entities: entities,
14459
14320
  menuItemId: this.item.id,
14460
14321
  visualRegister: this.visualsManager,
14322
+ entitiesHistoric: entitiesHistoric,
14323
+ entityHistoricDrawTrack: this.item.historicDrawTrack,
14461
14324
  zoomControl: this.item.CameraZoomSettings,
14462
14325
  force
14463
14326
  });
14464
14327
  for (let i = 0; i < entities.length; i++) {
14328
+ if (this.disposed) {
14329
+ break;
14330
+ }
14465
14331
  const entity = entities[i];
14466
14332
  const id = entity.Bruce.ID;
14467
14333
  const cEntity = cEntities.get(id);
@@ -14510,11 +14376,144 @@ var EntitiesIdsRenderManager;
14510
14376
  (_a = this.clustering) === null || _a === void 0 ? void 0 : _a.RemoveEntity(id, false);
14511
14377
  }
14512
14378
  }
14513
- this.viewer.scene.requestRender();
14514
- if (this.clustering && entities.length) {
14515
- this.clustering.Update();
14379
+ if (!this.disposed) {
14380
+ this.viewer.scene.requestRender();
14381
+ if (this.clustering && entities.length) {
14382
+ this.clustering.Update();
14383
+ }
14516
14384
  }
14517
14385
  }
14386
+ /**
14387
+ * Returns historic information for the given set of Entities at the current timeline range.
14388
+ * @param entities
14389
+ */
14390
+ async getHistoricInfo(entities) {
14391
+ const startTmp = JulianDate.toDate(this.viewer.clock.startTime);
14392
+ const stopTmp = JulianDate.toDate(this.viewer.clock.stopTime);
14393
+ let rangesToRequest = [{
14394
+ start: startTmp.toISOString(),
14395
+ stop: stopTmp.toISOString()
14396
+ }];
14397
+ // If we already have cached data, determine what ranges we're missing.
14398
+ if (this.lastHistoricMin && this.lastHistoricMax && Object.keys(this.entitiesHistoric).length >= entities.length) {
14399
+ const cachedStart = new Date(this.lastHistoricMin).getTime();
14400
+ const cachedStop = new Date(this.lastHistoricMax).getTime();
14401
+ const newStart = startTmp.getTime();
14402
+ const newStop = stopTmp.getTime();
14403
+ // Complete overlap - we already have all the data.
14404
+ if (newStart >= cachedStart && newStop <= cachedStop) {
14405
+ return this.entitiesHistoric;
14406
+ }
14407
+ // Calculate missing ranges.
14408
+ rangesToRequest = [];
14409
+ // Check if we need data before our cached range.
14410
+ if (newStart < cachedStart) {
14411
+ rangesToRequest.push({
14412
+ start: startTmp.toISOString(),
14413
+ stop: new Date(cachedStart).toISOString()
14414
+ });
14415
+ }
14416
+ // Check if we need data after our cached range.
14417
+ if (newStop > cachedStop) {
14418
+ rangesToRequest.push({
14419
+ start: new Date(cachedStop).toISOString(),
14420
+ stop: stopTmp.toISOString()
14421
+ });
14422
+ }
14423
+ }
14424
+ const entityIds = entities.map(x => x.Bruce.ID);
14425
+ const combined = { ...this.entitiesHistoric };
14426
+ // Make requests for each missing range
14427
+ for (const range of rangesToRequest) {
14428
+ if (this.disposed) {
14429
+ break;
14430
+ }
14431
+ // Add padding to ensure we get all data.
14432
+ const start = new Date(range.start);
14433
+ const stop = new Date(range.stop);
14434
+ start.setSeconds(start.getSeconds() - 1);
14435
+ stop.setSeconds(stop.getSeconds() + 1);
14436
+ const paddedStartStr = start.toISOString();
14437
+ const paddedStopStr = stop.toISOString();
14438
+ const historicData = await EntityHistoricData.GetList({
14439
+ attrKey: this.item.BruceEntity.historicAttrKey,
14440
+ dateTimeFrom: paddedStartStr,
14441
+ dateTimeTo: paddedStopStr,
14442
+ entityIds: entityIds,
14443
+ api: this.apiGetter.getApi()
14444
+ });
14445
+ if (this.disposed) {
14446
+ break;
14447
+ }
14448
+ // Merge the new data with existing data
14449
+ const records = historicData.recordsByIds;
14450
+ const recordsIds = Object.keys(records);
14451
+ for (let i = 0; i < recordsIds.length; i++) {
14452
+ const entityId = recordsIds[i];
14453
+ const latest = records[entityId] || [];
14454
+ const current = combined[entityId] || [];
14455
+ // Use a Map to de-duplicate by timestamp.
14456
+ const tmp = new Map();
14457
+ for (let j = 0; j < current.length; j++) {
14458
+ const record = current[j];
14459
+ const dateTime = new Date(record.dateTime).getTime();
14460
+ tmp.set(dateTime, record);
14461
+ }
14462
+ for (let j = 0; j < latest.length; j++) {
14463
+ const record = latest[j];
14464
+ const dateTime = new Date(record.dateTime).getTime();
14465
+ tmp.set(dateTime, record);
14466
+ }
14467
+ // Convert to array and sort by date.
14468
+ const sorted = Array.from(tmp.values()).sort((a, b) => {
14469
+ return new Date(a.dateTime).getTime() - new Date(b.dateTime).getTime();
14470
+ });
14471
+ combined[entityId] = sorted;
14472
+ }
14473
+ }
14474
+ if (!this.disposed) {
14475
+ // Update our cache boundaries.
14476
+ this.lastHistoricMin = startTmp.toISOString();
14477
+ this.lastHistoricMax = stopTmp.toISOString();
14478
+ this.entitiesHistoric = combined;
14479
+ }
14480
+ return combined;
14481
+ }
14482
+ viewerDateTimeSub() {
14483
+ var _a, _b;
14484
+ if ((!((_a = this.item.BruceEntity) === null || _a === void 0 ? void 0 : _a.historic) && !((_b = this.item.BruceEntity) === null || _b === void 0 ? void 0 : _b.historicAttrKey)) || this.viewerDateTimeChangeRemoval) {
14485
+ return;
14486
+ }
14487
+ const THROTTLE_INTERVAL = 2000;
14488
+ let queue = new DelayQueue(() => {
14489
+ this.onGetterUpdate(Object.keys(this.renderedEntities));
14490
+ }, THROTTLE_INTERVAL, true);
14491
+ let clockTickRemoval;
14492
+ let prevTick = this.viewer.clock.currentTime.toString();
14493
+ clockTickRemoval = this.viewer.clock.onTick.addEventListener(() => {
14494
+ if (this.disposed || this.viewer.isDestroyed()) {
14495
+ return;
14496
+ }
14497
+ const currentTick = this.viewer.clock.currentTime.toString();
14498
+ if (currentTick === prevTick) {
14499
+ return;
14500
+ }
14501
+ queue.Call();
14502
+ });
14503
+ this.viewerDateTimeChangeRemoval = () => {
14504
+ if (clockTickRemoval) {
14505
+ clockTickRemoval();
14506
+ clockTickRemoval = null;
14507
+ }
14508
+ queue.Dispose();
14509
+ queue = null;
14510
+ };
14511
+ }
14512
+ viewerDateTimeDispose() {
14513
+ var _a;
14514
+ (_a = this.viewerDateTimeChangeRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
14515
+ this.viewerDateTimeChangeRemoval = null;
14516
+ }
14518
14517
  }
14519
14518
  EntitiesIdsRenderManager.Manager = Manager;
14520
14519
  })(EntitiesIdsRenderManager || (EntitiesIdsRenderManager = {}));
@@ -15657,7 +15656,7 @@ var SharedGetters;
15657
15656
  })(Queue = SharedGetters.Queue || (SharedGetters.Queue = {}));
15658
15657
  })(SharedGetters || (SharedGetters = {}));
15659
15658
 
15660
- function colorToCColor$4(color) {
15659
+ function colorToCColor$3(color) {
15661
15660
  return new Color(color.red ? color.red / 255 : 0, color.green ? color.green / 255 : 0, color.blue ? color.blue / 255 : 0, color.alpha);
15662
15661
  }
15663
15662
  /**
@@ -16961,7 +16960,7 @@ var TilesetRenderEngine;
16961
16960
  cColor = Color.WHITE;
16962
16961
  }
16963
16962
  else {
16964
- cColor = colorToCColor$4(bColor);
16963
+ cColor = colorToCColor$3(bColor);
16965
16964
  }
16966
16965
  const override = this.styledEntityIds[entity.entityId] == true;
16967
16966
  CesiumEntityStyler.SetDefaultColor({
@@ -17159,7 +17158,7 @@ var TilesetCadRenderManager;
17159
17158
  this.viewerDateTimeChangeRemoval = null;
17160
17159
  // Series of points to help interpolate movement when the timeline changes.
17161
17160
  this.historicPosses = [];
17162
- this.historicPossesLoading = false;
17161
+ this.historicAnimation = null;
17163
17162
  this.historicPossesLoadingProm = null;
17164
17163
  const { viewer, register: visualsManager, getters, item } = params;
17165
17164
  this.viewer = viewer;
@@ -17616,7 +17615,7 @@ var TilesetCadRenderManager;
17616
17615
  /**
17617
17616
  * Monitors the Cesium Viewer and updates the root Entity based on the scene time changing.
17618
17617
  * If the root Entity is historic, this can allow for movement of the assembly as a whole.
17619
- * @parma tileset
17618
+ * @param tileset
17620
17619
  */
17621
17620
  viewerDateTimeSub(tileset) {
17622
17621
  var _a, _b;
@@ -17630,60 +17629,172 @@ var TilesetCadRenderManager;
17630
17629
  const api = this.getters.GetBruceApi({
17631
17630
  accountId: accountId
17632
17631
  });
17633
- // Returns a series of positions to use for position interpolation based on time.
17634
- // If the timeline range is different to the previous query, we'll re-request the data.
17635
- const getSeriesPosses = async () => {
17632
+ // 'start-stop' time string that maps to a pending request.
17633
+ // Helps us avoid repeated requests that are the same.
17634
+ const pendingRequests = new Map();
17635
+ /**
17636
+ * Returns a list of historic positions for a given time range.
17637
+ * @param startStr
17638
+ * @param stopStr
17639
+ * @returns
17640
+ */
17641
+ const getPossesForRange = async (startStr, stopStr) => {
17642
+ const requestKey = `${startStr}-${stopStr}`;
17643
+ if (pendingRequests.has(requestKey)) {
17644
+ return pendingRequests.get(requestKey);
17645
+ }
17646
+ const requestPromise = new Promise(async (res) => {
17647
+ try {
17648
+ const historicData = await EntityHistoricData.GetList({
17649
+ attrKey: null,
17650
+ dateTimeFrom: startStr,
17651
+ dateTimeTo: stopStr,
17652
+ entityIds: [this.rootId],
17653
+ api: api
17654
+ });
17655
+ const posses = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(this.viewer, HeightReference.NONE, historicData.recordsByIds[this.rootId]);
17656
+ res(posses);
17657
+ }
17658
+ catch (e) {
17659
+ console.error(e);
17660
+ res([]);
17661
+ }
17662
+ finally {
17663
+ pendingRequests.delete(requestKey);
17664
+ }
17665
+ });
17666
+ pendingRequests.set(requestKey, requestPromise);
17667
+ return requestPromise;
17668
+ };
17669
+ /**
17670
+ * Checks if the current timeline range is not fully loaded and requests the difference.
17671
+ * This will call getPossesForRange() if it needs more data.
17672
+ * @returns
17673
+ */
17674
+ const checkTimelineRange = async () => {
17636
17675
  const minDateTime = this.viewer.clock.startTime.toString();
17637
17676
  const maxDateTime = this.viewer.clock.stopTime.toString();
17638
- if (this.historicPossesMinDateTime == minDateTime &&
17639
- this.historicPossesMaxDateTime == maxDateTime) {
17640
- if (this.historicPossesLoading) {
17641
- if (!await this.historicPossesLoadingProm) {
17642
- return false;
17643
- }
17644
- }
17645
- return this.historicPosses;
17677
+ // See if the current range is within the range we already have.
17678
+ if (this.historicPosses.length > 0 &&
17679
+ this.historicPossesMinDateTime &&
17680
+ this.historicPossesMaxDateTime &&
17681
+ minDateTime >= this.historicPossesMinDateTime &&
17682
+ maxDateTime <= this.historicPossesMaxDateTime) {
17683
+ return;
17646
17684
  }
17647
- this.historicPossesMinDateTime = minDateTime;
17648
- this.historicPossesMaxDateTime = maxDateTime;
17649
- this.historicPossesLoading = true;
17650
- this.historicPossesLoadingProm = new Promise(async (res) => {
17651
- // We'll add/remove 1 second to ensure we cover all records.
17685
+ // See if the requested range is before or after the range we have.
17686
+ const fetchBefore = !this.historicPossesMinDateTime || minDateTime < this.historicPossesMinDateTime;
17687
+ const fetchAfter = !this.historicPossesMaxDateTime || maxDateTime > this.historicPossesMaxDateTime;
17688
+ if (!fetchBefore && !fetchAfter) {
17689
+ // Already have the data we need.
17690
+ return;
17691
+ }
17692
+ // No known range so we need to fetch the whole thing.
17693
+ if (!this.historicPossesMinDateTime || !this.historicPossesMaxDateTime) {
17652
17694
  const startTmp = JulianDate.toDate(this.viewer.clock.startTime);
17653
17695
  const stopTmp = JulianDate.toDate(this.viewer.clock.stopTime);
17654
17696
  const startStr = new Date(startTmp.getTime() - 1000).toISOString();
17655
17697
  const stopStr = new Date(stopTmp.getTime() + 1000).toISOString();
17656
- const historicData = await EntityHistoricData.GetList({
17657
- attrKey: null,
17658
- dateTimeFrom: startStr,
17659
- dateTimeTo: stopStr,
17660
- entityIds: [this.rootId],
17661
- api: api
17662
- });
17663
- const posses = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(this.viewer, HeightReference.NONE, historicData.recordsByIds[this.rootId]);
17664
- if (this.historicPossesMinDateTime == minDateTime &&
17665
- this.historicPossesMaxDateTime == maxDateTime) {
17666
- this.historicPosses = posses;
17698
+ const newPositions = await getPossesForRange(startStr, stopStr);
17699
+ if (this.disposed) {
17700
+ return;
17701
+ }
17702
+ if (this.historicAnimation && this.historicAnimation.addPositions) {
17703
+ this.historicAnimation.addPositions(newPositions);
17704
+ }
17705
+ this.historicPossesMinDateTime = minDateTime;
17706
+ this.historicPossesMaxDateTime = maxDateTime;
17707
+ }
17708
+ else {
17709
+ // The data we want is before the range we've currently loaded.
17710
+ if (fetchBefore) {
17711
+ // Calculate the missing difference and request it.
17712
+ const startTmp = JulianDate.toDate(this.viewer.clock.startTime);
17713
+ const stopTmp = new Date(this.historicPossesMinDateTime);
17714
+ const startStr = new Date(startTmp.getTime() - 1000).toISOString();
17715
+ const stopStr = stopTmp.toISOString();
17716
+ getPossesForRange(startStr, stopStr).then(newPositions => {
17717
+ if (this.disposed) {
17718
+ return;
17719
+ }
17720
+ this.historicPossesMinDateTime = minDateTime;
17721
+ if (this.historicAnimation && this.historicAnimation.addPositions) {
17722
+ this.historicAnimation.addPositions(newPositions);
17723
+ }
17724
+ });
17725
+ }
17726
+ // The data we want is after the range we've currently loaded.
17727
+ if (fetchAfter) {
17728
+ // Calculate the missing difference and request it.
17729
+ const startTmp = new Date(this.historicPossesMaxDateTime);
17730
+ const stopTmp = JulianDate.toDate(this.viewer.clock.stopTime);
17731
+ const startStr = startTmp.toISOString();
17732
+ const stopStr = new Date(stopTmp.getTime() + 1000).toISOString();
17733
+ getPossesForRange(startStr, stopStr).then(newPositions => {
17734
+ if (this.disposed) {
17735
+ return;
17736
+ }
17737
+ this.historicPossesMaxDateTime = maxDateTime;
17738
+ if (this.historicAnimation && this.historicAnimation.addPositions) {
17739
+ this.historicAnimation.addPositions(newPositions);
17740
+ }
17741
+ });
17742
+ }
17743
+ }
17744
+ };
17745
+ /**
17746
+ * Requests the initial set of historic positions for the timeline range.
17747
+ * This is called when the tileset is loaded and the timeline range is set.
17748
+ * @returns
17749
+ */
17750
+ const getInitialPosses = async () => {
17751
+ const minDateTime = this.viewer.clock.startTime.toString();
17752
+ const maxDateTime = this.viewer.clock.stopTime.toString();
17753
+ this.historicPossesLoadingProm = new Promise(async (res) => {
17754
+ try {
17755
+ if (this.disposed) {
17756
+ res(false);
17757
+ return;
17758
+ }
17759
+ const startTmp = JulianDate.toDate(this.viewer.clock.startTime);
17760
+ const stopTmp = JulianDate.toDate(this.viewer.clock.stopTime);
17761
+ const startStr = new Date(startTmp.getTime() - 1000).toISOString();
17762
+ const stopStr = new Date(stopTmp.getTime() + 1000).toISOString();
17763
+ const positions = await getPossesForRange(startStr, stopStr);
17764
+ if (this.disposed) {
17765
+ res(false);
17766
+ return;
17767
+ }
17768
+ this.historicPosses = positions;
17667
17769
  this.historicPossesMinDateTime = minDateTime;
17668
17770
  this.historicPossesMaxDateTime = maxDateTime;
17669
- this.historicPossesLoading = false;
17670
- res(posses);
17771
+ res(positions);
17772
+ }
17773
+ catch (e) {
17774
+ console.error(e);
17775
+ res(false);
17671
17776
  }
17672
- res(false);
17673
17777
  });
17674
17778
  return await this.historicPossesLoadingProm;
17675
17779
  };
17676
- getSeriesPosses().then((posses) => {
17780
+ // Last known timeline range. Helps us detect changes.
17781
+ let lastStartTime = this.viewer.clock.startTime.toString();
17782
+ let lastStopTime = this.viewer.clock.stopTime.toString();
17783
+ let clockTickRemoval;
17784
+ getInitialPosses().then((posses) => {
17677
17785
  if (this.disposed || posses === false) {
17678
17786
  return;
17679
17787
  }
17680
- let animation = new CesiumAnimatedProperty.AnimateTPositionSeries({
17788
+ // Generate animation utility for the initial set of positions.
17789
+ this.historicAnimation = new CesiumAnimatedProperty.AnimateTPositionSeries({
17681
17790
  viewer: this.viewer,
17682
17791
  posses: posses,
17683
17792
  onUpdate: (pos3d, heading) => {
17684
17793
  if (this.disposed) {
17685
17794
  return;
17686
17795
  }
17796
+ // Jank code that hacks the position to be different.
17797
+ // This can mess up our panels so we'll need to disable editing until a better system is in place.
17687
17798
  const location = Cartographic.fromCartesian(pos3d);
17688
17799
  const lat = Math$1.toDegrees(location.latitude);
17689
17800
  const lon = Math$1.toDegrees(location.longitude);
@@ -17710,12 +17821,35 @@ var TilesetCadRenderManager;
17710
17821
  }
17711
17822
  };
17712
17823
  this.applyCoords(tileset, coords);
17824
+ },
17825
+ onRangeChange: () => {
17826
+ checkTimelineRange();
17827
+ },
17828
+ onDone: () => {
17829
+ if (this.viewerDateTimeChangeRemoval) {
17830
+ this.viewerDateTimeChangeRemoval();
17831
+ this.viewerDateTimeChangeRemoval = null;
17832
+ }
17833
+ }
17834
+ });
17835
+ // When the clock changes we'll see if the range is different and queue a request for the difference.
17836
+ clockTickRemoval = this.viewer.clock.onTick.addEventListener(() => {
17837
+ const startTime = this.viewer.clock.startTime.toString();
17838
+ const stopTime = this.viewer.clock.stopTime.toString();
17839
+ if (startTime !== lastStartTime || stopTime !== lastStopTime) {
17840
+ lastStartTime = startTime;
17841
+ lastStopTime = stopTime;
17842
+ this.historicAnimation.onRangeChange();
17713
17843
  }
17714
17844
  });
17715
17845
  this.viewerDateTimeChangeRemoval = () => {
17716
- if (animation) {
17717
- animation.stop();
17718
- animation = null;
17846
+ if (this.historicAnimation) {
17847
+ this.historicAnimation.stop();
17848
+ this.historicAnimation = null;
17849
+ }
17850
+ if (clockTickRemoval) {
17851
+ clockTickRemoval();
17852
+ clockTickRemoval = null;
17719
17853
  }
17720
17854
  };
17721
17855
  });
@@ -24676,7 +24810,8 @@ async function renderNavigator(iteration, params, bookmark, view, getters) {
24676
24810
  end: null,
24677
24811
  playing: false,
24678
24812
  speed: 1,
24679
- start: null
24813
+ start: null,
24814
+ live: false
24680
24815
  };
24681
24816
  }
24682
24817
  ViewUtils.SetTimeDetails({
@@ -24685,7 +24820,8 @@ async function renderNavigator(iteration, params, bookmark, view, getters) {
24685
24820
  currentTimeIso: dateTime,
24686
24821
  startTimeIso: timeline.start,
24687
24822
  stopTimeIso: timeline.end,
24688
- isAnimating: timeline.playing
24823
+ isAnimating: timeline.playing,
24824
+ isLive: timeline.live
24689
24825
  });
24690
24826
  // Newer version that has states per Entity ID + Menu Item ID.
24691
24827
  let states = bSettings === null || bSettings === void 0 ? void 0 : bSettings.states;
@@ -30501,7 +30637,7 @@ class WidgetViewBar extends Widget.AWidget {
30501
30637
  }
30502
30638
  }
30503
30639
 
30504
- const VERSION = "5.4.0";
30640
+ const VERSION = "5.4.2";
30505
30641
 
30506
- 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, 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 };
30642
+ 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 };
30507
30643
  //# sourceMappingURL=bruce-cesium.es5.js.map