bruce-cesium 7.1.9 → 7.2.1

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 (36) hide show
  1. package/dist/bruce-cesium.es5.js +679 -300
  2. package/dist/bruce-cesium.es5.js.map +1 -1
  3. package/dist/bruce-cesium.umd.js +678 -297
  4. package/dist/bruce-cesium.umd.js.map +1 -1
  5. package/dist/lib/bruce-cesium.js +1 -1
  6. package/dist/lib/rendering/entity-render-engine-model3d.js +2 -2
  7. package/dist/lib/rendering/entity-render-engine-model3d.js.map +1 -1
  8. package/dist/lib/rendering/render-managers/common/historic-utils.js +471 -0
  9. package/dist/lib/rendering/render-managers/common/historic-utils.js.map +1 -0
  10. package/dist/lib/rendering/render-managers/entities/entities-ids-render-manager.js +14 -124
  11. package/dist/lib/rendering/render-managers/entities/entities-ids-render-manager.js.map +1 -1
  12. package/dist/lib/rendering/render-managers/entities/entities-render-manager.js +16 -13
  13. package/dist/lib/rendering/render-managers/entities/entities-render-manager.js.map +1 -1
  14. package/dist/lib/rendering/render-managers/other/assembly-render-manager.js +1 -1
  15. package/dist/lib/rendering/render-managers/other/assembly-render-manager.js.map +1 -1
  16. package/dist/lib/rendering/render-managers/tilesets/tileset-cad-render-manager.js +31 -109
  17. package/dist/lib/rendering/render-managers/tilesets/tileset-cad-render-manager.js.map +1 -1
  18. package/dist/lib/rendering/render-managers/tilesets/tileset-entities-render-manager.js +23 -7
  19. package/dist/lib/rendering/render-managers/tilesets/tileset-entities-render-manager.js.map +1 -1
  20. package/dist/lib/rendering/render-managers/tilesets/tileset-google-photos-render-manager.js +1 -1
  21. package/dist/lib/rendering/render-managers/tilesets/tileset-google-photos-render-manager.js.map +1 -1
  22. package/dist/lib/rendering/tileset-render-engine.js +1 -1
  23. package/dist/lib/rendering/tileset-render-engine.js.map +1 -1
  24. package/dist/lib/rendering/tileset-styler.js +28 -24
  25. package/dist/lib/rendering/tileset-styler.js.map +1 -1
  26. package/dist/lib/utils/view-utils.js +100 -15
  27. package/dist/lib/utils/view-utils.js.map +1 -1
  28. package/dist/types/bruce-cesium.d.ts +1 -1
  29. package/dist/types/rendering/render-managers/common/historic-utils.d.ts +159 -0
  30. package/dist/types/rendering/render-managers/entities/entities-ids-render-manager.d.ts +1 -0
  31. package/dist/types/rendering/render-managers/entities/entities-render-manager.d.ts +5 -0
  32. package/dist/types/rendering/render-managers/tilesets/tileset-cad-render-manager.d.ts +1 -0
  33. package/dist/types/rendering/render-managers/tilesets/tileset-entities-render-manager.d.ts +2 -0
  34. package/dist/types/rendering/tileset-styler.d.ts +2 -0
  35. package/dist/types/utils/view-utils.d.ts +30 -7
  36. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  import * as Cesium from 'cesium';
2
- import { Cartographic, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, ShaderSource, Cartesian3, Math as Math$1, Ellipsoid, Transforms, Matrix4, Geometry, GeometryAttribute, ComponentDatatype, PrimitiveType, BoundingSphere, GeometryPipeline, sampleTerrain, Texture, PixelFormat, Sampler, TextureWrap, TextureMinificationFilter, TextureMagnificationFilter, VertexArray, BufferUsage, ShaderProgram, RenderState, BlendingState, DrawCommand, Pass, Cartesian2, JulianDate, Quaternion, HeadingPitchRoll, HeightReference, ColorBlendMode, ShadowMode, ClassificationType, Model, DistanceDisplayCondition, ArcType, CornerType, ConstantPositionProperty, HorizontalOrigin, VerticalOrigin, ImageMaterialProperty, PolygonHierarchy, PolylineGraphics, Rectangle, SceneTransforms, NearFarScalar, Matrix3, KmlDataSource, GeoJsonDataSource, SceneMode, Cesium3DTileStyle, HeadingPitchRange, Ion, IonResource, Cesium3DTileColorBlendMode, Cesium3DTileset, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, Intersect, OrthographicFrustum, EasingFunction, EllipsoidGeodesic, sampleTerrainMostDetailed, defined, PolygonPipeline, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, CustomDataSource, CesiumInspector, ClockRange, GeometryInstance, ScreenSpaceEventHandler, ScreenSpaceEventType, Fullscreen, CzmlDataSource } from 'cesium';
3
- import { Cartes, Entity as Entity$1, ProjectViewTile, ENVIRONMENT, Calculator, ClientFile, EntityLod, EntityTag, EntityType, LRUCache, ObjectUtils, Style, Carto, Geometry as Geometry$1, MenuItem, ProjectView, ProjectViewBookmark, Tileset, ZoomControl, Api, BruceEvent, EntityCoords, Bounds, Color as Color$1, DataLab, DelayQueue, EntityHistoricData, BruceApi, EntityRelation, AccountConcept, RecordChangeFeed, EntitySource, ProgramKey, ProjectViewLegacyTile, Camera, Account, AccountLimits, Session, UrlUtils, ProjectViewBookmarkGroup, EntityAttachment, EntityAttachmentType, EntityAttribute, MathUtils, EntityRelationType } from 'bruce-models';
2
+ import { Cartographic, ShaderSource, Cartesian3, Math as Math$1, Ellipsoid, Transforms, Matrix4, Geometry, GeometryAttribute, ComponentDatatype, PrimitiveType, BoundingSphere, GeometryPipeline, sampleTerrain, Texture, PixelFormat, Sampler, TextureWrap, TextureMinificationFilter, TextureMagnificationFilter, VertexArray, BufferUsage, ShaderProgram, RenderState, BlendingState, DrawCommand, Pass, Cartesian2, Color, ColorMaterialProperty, Entity, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, DistanceDisplayCondition, JulianDate, Quaternion, HeadingPitchRoll, HeightReference, ColorBlendMode, ShadowMode, ClassificationType, Model, ArcType, CornerType, ConstantPositionProperty, HorizontalOrigin, VerticalOrigin, ImageMaterialProperty, PolygonHierarchy, PolylineGraphics, Rectangle, SceneTransforms, NearFarScalar, Matrix3, KmlDataSource, GeoJsonDataSource, SceneMode, Cesium3DTileStyle, HeadingPitchRange, Ion, IonResource, Cesium3DTileColorBlendMode, Cesium3DTileset, OrthographicFrustum, EasingFunction, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, Intersect, EllipsoidGeodesic, sampleTerrainMostDetailed, defined, PolygonPipeline, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, CustomDataSource, GeometryInstance, CesiumInspector, ClockRange, DynamicEnvironmentMapManager, ScreenSpaceEventHandler, ScreenSpaceEventType, Fullscreen, CzmlDataSource } from 'cesium';
3
+ import { Cartes, Entity as Entity$1, ProjectViewTile, ENVIRONMENT, Calculator, ClientFile, EntityLod, EntityTag, EntityType, LRUCache, ObjectUtils, Style, Carto, Geometry as Geometry$1, Api, MenuItem, ProjectView, ProjectViewBookmark, Tileset, ZoomControl, BruceEvent, EntityCoords, Bounds, Color as Color$1, DelayQueue, DataLab, BruceApi, EntityRelation, AccountConcept, RecordChangeFeed, EntitySource, Camera, ProgramKey, ProjectViewLegacyTile, Account, AccountLimits, Session, UrlUtils, EntityAttachment, EntityAttachmentType, EntityAttribute, MathUtils, ProjectViewBookmarkGroup, EntityRelationType, EntityHistoricData } from 'bruce-models';
4
4
 
5
5
  /**
6
6
  * Ensures a number is returned from a given value.
@@ -2975,9 +2975,30 @@ const CESIUM_TIMELINE_LIVE_PADDING_KEY = "_nextspace_timeline_live_padding";
2975
2975
  const CESIUM_TIMELINE_INTERVAL_KEY = "_nextspace_timeline_interval";
2976
2976
  const CESIUM_MODEL_SPACE_KEY = "_nextspace_model_space";
2977
2977
  const DEFAULT_LIVE_PADDING_SECONDS = 30 * 60;
2978
- const DEFAULT_BRIGHTNESS_SHIFT = 0.2;
2979
- const DEFAULT_LIGHT_INTENSITY = 3;
2978
+ // Atmosphere shifts are left alone by default. Tuning against a CAD scene showed the lift washing the
2979
+ // sky without helping models, which are lit by the environment map rather than by the atmosphere.
2980
+ const DEFAULT_BRIGHTNESS_SHIFT = 0;
2981
+ /*
2982
+ * Environment lighting defaults, arrived at by tuning against a white CAD model on CesiumJS 1.143.
2983
+ */
2984
+ function neutralEnvironmentMapOptions() {
2985
+ return {
2986
+ groundColor: Color.WHITE.clone(),
2987
+ groundAlbedo: 0.26,
2988
+ brightness: 1.5,
2989
+ atmosphereScatteringIntensity: 3.2
2990
+ };
2991
+ }
2992
+ // Sun intensity used when the bookmark's lighting settings name none.
2993
+ const DEFAULT_LIGHT_INTENSITY = 2;
2994
+ // Current shape of a bookmark's lighting settings.
2980
2995
  const LIGHTING_SETTINGS_VERSION = 2;
2996
+ // Settings we apply when version is unset or <=1 to compensate for newer Cesium looking darker and more yellow.
2997
+ const LIGHT_INTENSITY_COMPENSATION = 1.2;
2998
+ const MAX_COMPENSATED_LIGHT_INTENSITY = 3;
2999
+ // Set by a host that wants something other than the defaults above. Undefined means "not overridden".
3000
+ let overriddenEnvironmentMapOptions;
3001
+ let overriddenImageBasedLighting;
2981
3002
  var ViewUtils;
2982
3003
  (function (ViewUtils) {
2983
3004
  function GatherLegacyMapTiles(params) {
@@ -3346,33 +3367,97 @@ var ViewUtils;
3346
3367
  SCENE.skyAtmosphere.brightnessShift = shift;
3347
3368
  }
3348
3369
  }
3370
+ // The environment map is built from the atmosphere, and lighting it from the scene's own light
3371
+ // rather than from straight above is what stops it reading flat.
3372
+ const CESIUM = Cesium;
3373
+ if (EngineShadesModelsDarker() && SCENE.atmosphere && CESIUM.DynamicAtmosphereLightingType) {
3374
+ SCENE.atmosphere.dynamicLighting = CESIUM.DynamicAtmosphereLightingType.SCENE_LIGHT;
3375
+ }
3349
3376
  }
3350
3377
  ViewUtils.ApplyDefaultBrightness = ApplyDefaultBrightness;
3378
+ /**
3379
+ * Options applied to the environment based lighting of every tileset and model this library creates.
3380
+ * Null leaves CesiumJS's own defaults in place.
3381
+ * @param options eg { enabled: false } to switch the lighting off, or { saturation: 0 } to keep the
3382
+ * lift without the ground bounce tinting untextured geometry.
3383
+ */
3384
+ function SetDefaultEnvironmentMapOptions(options) {
3385
+ overriddenEnvironmentMapOptions = options;
3386
+ }
3387
+ ViewUtils.SetDefaultEnvironmentMapOptions = SetDefaultEnvironmentMapOptions;
3388
+ /**
3389
+ * Whether the host app's CesiumJS is a build that shades models darker than the ones these bookmarks were set up against.
3390
+ * DynamicEnvironmentMapManager arrived with that change, so its presence marks
3391
+ */
3392
+ function GetDefaultEnvironmentMapOptions(
3393
+ // Overridable so the tuned values can be asserted without swapping out CesiumJS.
3394
+ engineShadesDarker = EngineShadesModelsDarker()) {
3395
+ if (overriddenEnvironmentMapOptions !== undefined) {
3396
+ return overriddenEnvironmentMapOptions ? { ...overriddenEnvironmentMapOptions } : null;
3397
+ }
3398
+ if (!engineShadesDarker) {
3399
+ return null;
3400
+ }
3401
+ return neutralEnvironmentMapOptions();
3402
+ }
3403
+ ViewUtils.GetDefaultEnvironmentMapOptions = GetDefaultEnvironmentMapOptions;
3404
+ /**
3405
+ * The lighting options every tileset and model this library creates is built with.
3406
+ * Spread into the construction options, since both are read once when a primitive is made.
3407
+ */
3408
+ function DefaultLightingProps(engineShadesDarker = EngineShadesModelsDarker()) {
3409
+ const props = {};
3410
+ const environmentMapOptions = GetDefaultEnvironmentMapOptions(engineShadesDarker);
3411
+ if (environmentMapOptions) {
3412
+ props.environmentMapOptions = environmentMapOptions;
3413
+ }
3414
+ const imageBasedLighting = GetDefaultImageBasedLighting();
3415
+ if (imageBasedLighting) {
3416
+ props.imageBasedLighting = imageBasedLighting;
3417
+ }
3418
+ return props;
3419
+ }
3420
+ ViewUtils.DefaultLightingProps = DefaultLightingProps;
3421
+ /**
3422
+ * ImageBasedLighting applied to every tileset and model this library creates. Null, the default,
3423
+ * leaves CesiumJS's own in place.
3424
+ * @param imageBasedLighting a Cesium.ImageBasedLighting, shared across primitives.
3425
+ */
3426
+ function SetDefaultImageBasedLighting(imageBasedLighting) {
3427
+ overriddenImageBasedLighting = imageBasedLighting;
3428
+ }
3429
+ ViewUtils.SetDefaultImageBasedLighting = SetDefaultImageBasedLighting;
3430
+ function GetDefaultImageBasedLighting() {
3431
+ return overriddenImageBasedLighting || null;
3432
+ }
3433
+ ViewUtils.GetDefaultImageBasedLighting = GetDefaultImageBasedLighting;
3434
+ function EngineShadesModelsDarker() {
3435
+ return typeof DynamicEnvironmentMapManager !== "undefined";
3436
+ }
3437
+ ViewUtils.EngineShadesModelsDarker = EngineShadesModelsDarker;
3351
3438
  /**
3352
3439
  * Resolves the sun colour and intensity to apply for a bookmark's lighting settings.
3353
3440
  * @param lighting the bookmark's lighting settings, or null when it carries none.
3354
3441
  * @returns the colour and intensity to put on the scene light.
3355
3442
  */
3356
- function ResolveLightSettings(lighting) {
3443
+ function ResolveLightSettings(lighting,
3444
+ // Overridable so both branches can be exercised without swapping out CesiumJS.
3445
+ engineShadesDarker = EngineShadesModelsDarker()) {
3357
3446
  const raw = lighting === null || lighting === void 0 ? void 0 : lighting.color;
3358
- // An unreadable or missing colour would otherwise leave the light undefined.
3359
3447
  const parsed = raw ? Color.fromCssColorString(raw) : null;
3360
- const version = Number(lighting === null || lighting === void 0 ? void 0 : lighting.lightingVer);
3361
- const stale = !Number.isFinite(version) || version <= 1;
3362
3448
  const intensity = Number(lighting === null || lighting === void 0 ? void 0 : lighting.intensity);
3449
+ const named = Number.isFinite(intensity) ? intensity : DEFAULT_LIGHT_INTENSITY;
3450
+ // No settings at all means nothing was carried over from the older engine, so there is nothing
3451
+ // to compensate. A settings object with no version in it is the case that predates the field.
3452
+ const version = Number(lighting === null || lighting === void 0 ? void 0 : lighting.lightingVer);
3453
+ const legacy = lighting != null && (!Number.isFinite(version) || version <= 1);
3454
+ const compensated = Math.min(named * LIGHT_INTENSITY_COMPENSATION, MAX_COMPENSATED_LIGHT_INTENSITY);
3363
3455
  return {
3364
3456
  color: parsed || Color.WHITE,
3365
- intensity: stale || !Number.isFinite(intensity) ? DEFAULT_LIGHT_INTENSITY : intensity
3457
+ intensity: legacy && engineShadesDarker ? compensated : named
3366
3458
  };
3367
3459
  }
3368
3460
  ViewUtils.ResolveLightSettings = ResolveLightSettings;
3369
- /**
3370
- * Options handed to every tileset and model this library creates.
3371
- */
3372
- function DefaultEnvironmentMapOptions() {
3373
- return { enabled: false };
3374
- }
3375
- ViewUtils.DefaultEnvironmentMapOptions = DefaultEnvironmentMapOptions;
3376
3461
  function SetGlobeDetails(params) {
3377
3462
  let { viewer, hidden, baseColor } = params;
3378
3463
  const scene = viewer === null || viewer === void 0 ? void 0 : viewer.scene;
@@ -10636,6 +10721,468 @@ class PointClustering {
10636
10721
  }
10637
10722
  }
10638
10723
 
10724
+ /**
10725
+ * How often a historic refresh is worth running, and which Entities it should ask about first.
10726
+ *
10727
+ * A handful of tracked vehicles can afford to re-read on almost every clock tick. Thousands of
10728
+ * parcels cannot, and refreshing them that often was never buying anything since the answer barely
10729
+ * changes between ticks.
10730
+ */
10731
+ var HistoricFetchOrder;
10732
+ (function (HistoricFetchOrder) {
10733
+ // The cadence for a set small enough that latency is what the user notices.
10734
+ HistoricFetchOrder.MIN_INTERVAL_MS = 250;
10735
+ // The cadence for a set large enough that the request itself is what the user notices.
10736
+ HistoricFetchOrder.MAX_INTERVAL_MS = 10000;
10737
+ // Below this many Entities the cadence stays at its fastest.
10738
+ HistoricFetchOrder.SMALL_SET = 30;
10739
+ // At and above this many it stays at its slowest.
10740
+ HistoricFetchOrder.LARGE_SET = 5000;
10741
+ /**
10742
+ * Returns how long to wait between historic refreshes for a set of this size.
10743
+ */
10744
+ function IntervalFor(entityCount, minMs = HistoricFetchOrder.MIN_INTERVAL_MS, maxMs = HistoricFetchOrder.MAX_INTERVAL_MS) {
10745
+ if (!(entityCount > HistoricFetchOrder.SMALL_SET)) {
10746
+ return minMs;
10747
+ }
10748
+ if (entityCount >= HistoricFetchOrder.LARGE_SET) {
10749
+ return maxMs;
10750
+ }
10751
+ // Log scaled, so going from 30 to 300 Entities costs more than going from 3000 to 5000.
10752
+ const progress = Math.log(entityCount / HistoricFetchOrder.SMALL_SET) / Math.log(HistoricFetchOrder.LARGE_SET / HistoricFetchOrder.SMALL_SET);
10753
+ return Math.round(minMs + ((maxMs - minMs) * progress));
10754
+ }
10755
+ HistoricFetchOrder.IntervalFor = IntervalFor;
10756
+ /**
10757
+ * Returns the Entities grouped into bands by distance from the camera, nearest band first.
10758
+ *
10759
+ * Bands rather than a true sort, so a small camera move does not reshuffle the whole queue and
10760
+ * restart it. What the user is looking at resolves first either way.
10761
+ */
10762
+ function ByCameraDistance(params) {
10763
+ var _a;
10764
+ const { viewer, entities } = params;
10765
+ const bands = params.bands || [500, 2000, 10000, 50000];
10766
+ if (!(entities === null || entities === void 0 ? void 0 : entities.length)) {
10767
+ return [];
10768
+ }
10769
+ const cameraPos = (_a = viewer === null || viewer === void 0 ? void 0 : viewer.camera) === null || _a === void 0 ? void 0 : _a.position;
10770
+ if (!cameraPos) {
10771
+ return [entities];
10772
+ }
10773
+ // One band past the named bounds, for everything beyond the last of them.
10774
+ const banded = bands.map(() => []).concat([[]]);
10775
+ for (const entity of entities) {
10776
+ const pos = SafePos(entity, viewer);
10777
+ if (!pos) {
10778
+ banded[banded.length - 1].push(entity);
10779
+ continue;
10780
+ }
10781
+ const distance = Cartesian3.distance(cameraPos, pos);
10782
+ let index = bands.findIndex(bound => distance <= bound);
10783
+ if (index < 0) {
10784
+ index = banded.length - 1;
10785
+ }
10786
+ banded[index].push(entity);
10787
+ }
10788
+ return banded.filter(band => band.length > 0);
10789
+ }
10790
+ HistoricFetchOrder.ByCameraDistance = ByCameraDistance;
10791
+ /*
10792
+ * Returns an Entity's world position, or null when it has none to place it by.
10793
+ */
10794
+ function SafePos(entity, viewer) {
10795
+ try {
10796
+ const pos = EntityUtils.GetPos({
10797
+ entity: entity,
10798
+ viewer: viewer,
10799
+ allowRendered: true
10800
+ });
10801
+ if (!pos || isNaN(pos.x) || isNaN(pos.y) || isNaN(pos.z)) {
10802
+ return null;
10803
+ }
10804
+ return pos;
10805
+ }
10806
+ catch {
10807
+ return null;
10808
+ }
10809
+ }
10810
+ })(HistoricFetchOrder || (HistoricFetchOrder = {}));
10811
+ /**
10812
+ * Decides what slice of history is worth fetching for the clock as it is currently moving.
10813
+ */
10814
+ var HistoricFetchPlan;
10815
+ (function (HistoricFetchPlan) {
10816
+ // How many seconds of playback the detail window tries to stay ahead by.
10817
+ HistoricFetchPlan.DEFAULT_LEAD_SECONDS = 10;
10818
+ // The detail window never shrinks below this, so a paused clock still has something around it.
10819
+ HistoricFetchPlan.DEFAULT_MIN_DETAIL_MS = 60 * 1000;
10820
+ // Nor grows past it, which is what stops a fast scrub asking for the whole archive.
10821
+ HistoricFetchPlan.DEFAULT_MAX_DETAIL_MS = 6 * 60 * 60 * 1000;
10822
+ // Records the coarse pass may spend describing the whole range.
10823
+ HistoricFetchPlan.DEFAULT_SAMPLE = 200;
10824
+ /*
10825
+ * Returns how wide the detail window should be for the rate the clock is moving at.
10826
+ */
10827
+ function DetailSpanFor(trajectory, leadSeconds, minMs, maxMs) {
10828
+ const rate = Math.abs((trajectory === null || trajectory === void 0 ? void 0 : trajectory.ratePerSecondMs) || 0);
10829
+ const span = rate * leadSeconds;
10830
+ if (!(span > minMs)) {
10831
+ return minMs;
10832
+ }
10833
+ return Math.min(span, maxMs);
10834
+ }
10835
+ HistoricFetchPlan.DetailSpanFor = DetailSpanFor;
10836
+ /**
10837
+ * Returns the detail window and the coarse pass for the clock as it stands.
10838
+ */
10839
+ function Plan(params) {
10840
+ var _a;
10841
+ const { currentTime, windowStart, windowStop, trajectory, leadSeconds = HistoricFetchPlan.DEFAULT_LEAD_SECONDS, minDetailMs = HistoricFetchPlan.DEFAULT_MIN_DETAIL_MS, maxDetailMs = HistoricFetchPlan.DEFAULT_MAX_DETAIL_MS, sample = HistoricFetchPlan.DEFAULT_SAMPLE } = params;
10842
+ if (!(windowStop > windowStart)) {
10843
+ return { detail: null, coarse: null };
10844
+ }
10845
+ // A range that fits the detail budget has nothing worth sampling: reading it whole is both
10846
+ // cheaper than two requests and better than an approximation of it.
10847
+ if ((windowStop - windowStart) <= maxDetailMs) {
10848
+ return { detail: { start: windowStart, stop: windowStop }, coarse: null };
10849
+ }
10850
+ const span = DetailSpanFor(trajectory, leadSeconds, minDetailMs, maxDetailMs);
10851
+ const direction = (_a = trajectory === null || trajectory === void 0 ? void 0 : trajectory.direction) !== null && _a !== void 0 ? _a : 0;
10852
+ // Time running forward only ever reveals what is ahead, so the window leads rather than centres.
10853
+ // A little is still kept behind so a record just passed can still be interpolated from.
10854
+ const behind = direction === 0 ? span / 2 : span * 0.2;
10855
+ const ahead = direction === 0 ? span / 2 : span * 0.8;
10856
+ let start = direction < 0 ? currentTime - ahead : currentTime - behind;
10857
+ let stop = direction < 0 ? currentTime + behind : currentTime + ahead;
10858
+ start = Math.max(windowStart, start);
10859
+ stop = Math.min(windowStop, stop);
10860
+ if (!(stop > start)) {
10861
+ return { detail: null, coarse: { start: windowStart, stop: windowStop, sample } };
10862
+ }
10863
+ const detail = { start, stop };
10864
+ // Nothing to be coarse about once the detail window is the whole range.
10865
+ if (start <= windowStart && stop >= windowStop) {
10866
+ return { detail, coarse: null };
10867
+ }
10868
+ return { detail, coarse: { start: windowStart, stop: windowStop, sample } };
10869
+ }
10870
+ HistoricFetchPlan.Plan = Plan;
10871
+ /**
10872
+ * Tracks which way the clock is moving and how fast, from the times it is observed at.
10873
+ * Playback and a user dragging the timeline both look the same here, which is the point.
10874
+ */
10875
+ class TrajectoryTracker {
10876
+ // Smooths the rate so one slow frame does not collapse the window.
10877
+ constructor(smoothing = 0.4) {
10878
+ this.smoothing = smoothing;
10879
+ this.lastTime = null;
10880
+ this.lastObservedAt = null;
10881
+ this.direction = 0;
10882
+ this.ratePerSecondMs = 0;
10883
+ }
10884
+ /**
10885
+ * Records where the clock is now. observedAt is the wall clock, in milliseconds.
10886
+ */
10887
+ Observe(sceneTime, observedAt) {
10888
+ if (this.lastTime == null) {
10889
+ this.lastTime = sceneTime;
10890
+ this.lastObservedAt = observedAt;
10891
+ return;
10892
+ }
10893
+ const elapsed = observedAt - this.lastObservedAt;
10894
+ const moved = sceneTime - this.lastTime;
10895
+ this.lastTime = sceneTime;
10896
+ this.lastObservedAt = observedAt;
10897
+ if (!(elapsed > 0)) {
10898
+ return;
10899
+ }
10900
+ if (moved === 0) {
10901
+ this.direction = 0;
10902
+ this.ratePerSecondMs = 0;
10903
+ return;
10904
+ }
10905
+ this.direction = moved > 0 ? 1 : -1;
10906
+ const rate = Math.abs(moved) / (elapsed / 1000);
10907
+ this.ratePerSecondMs = this.ratePerSecondMs === 0
10908
+ ? rate
10909
+ : (this.ratePerSecondMs * (1 - this.smoothing)) + (rate * this.smoothing);
10910
+ }
10911
+ Get() {
10912
+ return { direction: this.direction, ratePerSecondMs: this.ratePerSecondMs };
10913
+ }
10914
+ Reset() {
10915
+ this.lastTime = null;
10916
+ this.lastObservedAt = null;
10917
+ this.direction = 0;
10918
+ this.ratePerSecondMs = 0;
10919
+ }
10920
+ }
10921
+ HistoricFetchPlan.TrajectoryTracker = TrajectoryTracker;
10922
+ })(HistoricFetchPlan || (HistoricFetchPlan = {}));
10923
+ /**
10924
+ * Tracks which time ranges of historic data have already been requested, and the records they
10925
+ * returned, so moving the clock only fetches the part that is genuinely new.
10926
+ *
10927
+ * Coverage is tracked by the ranges that were asked for, not by the timestamps that came back. A
10928
+ * window holding no records is still covered, otherwise an empty stretch is re-requested forever.
10929
+ */
10930
+ class HistoricRangeCache {
10931
+ // Ranges this far apart are treated as one, so a scrub does not leave a trail of slivers.
10932
+ constructor(mergeToleranceMs = 60000) {
10933
+ this.mergeToleranceMs = mergeToleranceMs;
10934
+ // Requested ranges, kept sorted and non-overlapping.
10935
+ this.held = [];
10936
+ // Ranges covered only by a sampled read, which describe their span without holding every record.
10937
+ this.heldCoarse = [];
10938
+ this.records = {};
10939
+ }
10940
+ /**
10941
+ * Returns the parts of the window that have not been requested yet.
10942
+ */
10943
+ MissingRanges(start, stop) {
10944
+ if (!(stop > start)) {
10945
+ return [];
10946
+ }
10947
+ const missing = [];
10948
+ let cursor = start;
10949
+ for (const range of this.held) {
10950
+ if (range.stop <= cursor) {
10951
+ continue;
10952
+ }
10953
+ if (range.start >= stop) {
10954
+ break;
10955
+ }
10956
+ if (range.start > cursor) {
10957
+ missing.push({ start: cursor, stop: Math.min(range.start, stop) });
10958
+ }
10959
+ cursor = Math.max(cursor, range.stop);
10960
+ if (cursor >= stop) {
10961
+ break;
10962
+ }
10963
+ }
10964
+ if (cursor < stop) {
10965
+ missing.push({ start: cursor, stop: stop });
10966
+ }
10967
+ return this.mergeAdjacent(missing);
10968
+ }
10969
+ /**
10970
+ * Marks a range as requested and merges the records it returned into what is already held.
10971
+ */
10972
+ Absorb(start, stop, recordsByIds, coarse = false) {
10973
+ if (stop > start) {
10974
+ if (coarse) {
10975
+ this.heldCoarse = this.mergeAdjacent([...this.heldCoarse, { start, stop }].sort((a, b) => a.start - b.start));
10976
+ }
10977
+ else {
10978
+ this.held = this.mergeAdjacent([...this.held, { start, stop }].sort((a, b) => a.start - b.start));
10979
+ }
10980
+ }
10981
+ if (!recordsByIds) {
10982
+ return;
10983
+ }
10984
+ for (const entityId of Object.keys(recordsByIds)) {
10985
+ const incoming = recordsByIds[entityId] || [];
10986
+ const current = this.records[entityId] || [];
10987
+ // Keyed by instant so a range fetched twice cannot double up its records.
10988
+ const byInstant = new Map();
10989
+ for (const record of current) {
10990
+ byInstant.set(new Date(record.dateTime).getTime(), record);
10991
+ }
10992
+ for (const record of incoming) {
10993
+ byInstant.set(new Date(record.dateTime).getTime(), record);
10994
+ }
10995
+ this.records[entityId] = Array.from(byInstant.values())
10996
+ .sort((a, b) => new Date(a.dateTime).getTime() - new Date(b.dateTime).getTime());
10997
+ }
10998
+ }
10999
+ GetRecords() {
11000
+ return this.records;
11001
+ }
11002
+ /**
11003
+ * Returns whether the window is already covered in full.
11004
+ */
11005
+ Covers(start, stop) {
11006
+ return this.MissingRanges(start, stop).length === 0;
11007
+ }
11008
+ /**
11009
+ * Returns whether a sampled pass already describes the window.
11010
+ */
11011
+ CoversCoarse(start, stop) {
11012
+ if (!(stop > start)) {
11013
+ return true;
11014
+ }
11015
+ return this.heldCoarse.some(range => range.start <= start && range.stop >= stop);
11016
+ }
11017
+ Clear() {
11018
+ this.held = [];
11019
+ this.heldCoarse = [];
11020
+ this.records = {};
11021
+ }
11022
+ mergeAdjacent(ranges) {
11023
+ if (ranges.length <= 1) {
11024
+ return ranges;
11025
+ }
11026
+ const sorted = [...ranges].sort((a, b) => a.start - b.start);
11027
+ const merged = [];
11028
+ let current = { ...sorted[0] };
11029
+ for (let i = 1; i < sorted.length; i++) {
11030
+ const next = sorted[i];
11031
+ if (next.start - current.stop <= this.mergeToleranceMs) {
11032
+ current.stop = Math.max(current.stop, next.stop);
11033
+ }
11034
+ else {
11035
+ merged.push(current);
11036
+ current = { ...next };
11037
+ }
11038
+ }
11039
+ merged.push(current);
11040
+ return merged;
11041
+ }
11042
+ }
11043
+ /**
11044
+ * Pulls historic records for the visible clock range: what to ask for, in what order, and what has
11045
+ * already been asked for.
11046
+ */
11047
+ class HistoricGatherer {
11048
+ constructor(params) {
11049
+ this.params = params;
11050
+ this.trajectory = new HistoricFetchPlan.TrajectoryTracker();
11051
+ // Range key to the request in flight for it, so the same range is never asked for twice at once.
11052
+ this.inFlight = new Map();
11053
+ this.disposed = false;
11054
+ this.cache = new HistoricRangeCache();
11055
+ }
11056
+ get Records() {
11057
+ return this.cache.GetRecords();
11058
+ }
11059
+ Clear() {
11060
+ this.cache.Clear();
11061
+ this.trajectory.Reset();
11062
+ this.inFlight.clear();
11063
+ }
11064
+ Dispose() {
11065
+ this.disposed = true;
11066
+ this.Clear();
11067
+ }
11068
+ /**
11069
+ * Returns how long to wait between refreshes for a set of this size.
11070
+ */
11071
+ IntervalFor(entityCount) {
11072
+ return HistoricFetchOrder.IntervalFor(entityCount);
11073
+ }
11074
+ /**
11075
+ * Fetches whatever the current clock range needs that is not already held.
11076
+ * Returns whether anything was requested, alongside every record held for these Entities.
11077
+ */
11078
+ async Gather(entities) {
11079
+ var _a;
11080
+ if (this.disposed || !(entities === null || entities === void 0 ? void 0 : entities.length)) {
11081
+ return { fetched: false, recordsByIds: this.cache.GetRecords() };
11082
+ }
11083
+ const { viewer } = this.params;
11084
+ const windowStart = new Date(viewer.clock.startTime.toString()).getTime();
11085
+ const windowStop = new Date(viewer.clock.stopTime.toString()).getTime();
11086
+ const currentTime = new Date(viewer.clock.currentTime.toString()).getTime();
11087
+ this.trajectory.Observe(currentTime, Date.now());
11088
+ const liveEdge = ViewUtils.GetTimeDetails({ viewer }).isLive ? currentTime : null;
11089
+ const liveTail = (_a = this.params.liveTailMs) !== null && _a !== void 0 ? _a : HistoricGatherer.LIVE_TAIL_MS;
11090
+ const plan = HistoricFetchPlan.Plan({
11091
+ currentTime,
11092
+ windowStart,
11093
+ windowStop,
11094
+ trajectory: this.trajectory.Get(),
11095
+ sample: this.params.sample
11096
+ });
11097
+ let fetched = false;
11098
+ if (plan.coarse && !this.cache.CoversCoarse(plan.coarse.start, plan.coarse.stop)) {
11099
+ const records = await this.request(plan.coarse.start, plan.coarse.stop, entities, plan.coarse.sample);
11100
+ if (this.disposed) {
11101
+ return { fetched, recordsByIds: this.cache.GetRecords() };
11102
+ }
11103
+ this.cache.Absorb(plan.coarse.start, this.holdTo(plan.coarse.stop, liveEdge, liveTail), records, true);
11104
+ fetched = true;
11105
+ }
11106
+ if (plan.detail) {
11107
+ const bands = entities.length > HistoricFetchOrder.SMALL_SET
11108
+ ? HistoricFetchOrder.ByCameraDistance({ viewer, entities })
11109
+ : [entities];
11110
+ for (const range of this.cache.MissingRanges(plan.detail.start, plan.detail.stop)) {
11111
+ if (this.disposed) {
11112
+ break;
11113
+ }
11114
+ for (const band of bands) {
11115
+ if (this.disposed) {
11116
+ break;
11117
+ }
11118
+ const records = await this.request(range.start, range.stop, band, 0);
11119
+ if (this.disposed) {
11120
+ break;
11121
+ }
11122
+ // Records land as each band arrives, while the range is only marked held once
11123
+ // every band has been asked for.
11124
+ this.cache.Absorb(0, 0, records);
11125
+ fetched = true;
11126
+ }
11127
+ if (this.disposed) {
11128
+ break;
11129
+ }
11130
+ const heldTo = this.holdTo(range.stop, liveEdge, liveTail);
11131
+ if (heldTo > range.start) {
11132
+ this.cache.Absorb(range.start, heldTo, {});
11133
+ }
11134
+ }
11135
+ }
11136
+ return { fetched, recordsByIds: this.cache.GetRecords() };
11137
+ }
11138
+ /*
11139
+ * Returns how much of a range that was just read may be marked as held.
11140
+ */
11141
+ holdTo(stop, liveEdge, liveTailMs) {
11142
+ if (liveEdge == null) {
11143
+ return stop;
11144
+ }
11145
+ return Math.min(stop, liveEdge - liveTailMs);
11146
+ }
11147
+ /*
11148
+ * Reads one range, joining a request already in flight for it rather than repeating it.
11149
+ */
11150
+ request(start, stop, entities, sample) {
11151
+ var _a;
11152
+ const padding = (_a = this.params.paddingMs) !== null && _a !== void 0 ? _a : 0;
11153
+ const dateTimeFrom = new Date(start - padding).toISOString();
11154
+ const dateTimeTo = new Date(stop + padding).toISOString();
11155
+ const entityIds = entities.map(x => { var _a; return (_a = x.Bruce) === null || _a === void 0 ? void 0 : _a.ID; }).filter(x => !!x);
11156
+ const key = `${dateTimeFrom}|${dateTimeTo}|${sample}|${entityIds.join(",")}`;
11157
+ const existing = this.inFlight.get(key);
11158
+ if (existing) {
11159
+ return existing;
11160
+ }
11161
+ const prom = EntityHistoricData.GetRecordsByEntity({
11162
+ attrKey: this.params.getAttrKey(),
11163
+ dateTimeFrom,
11164
+ dateTimeTo,
11165
+ entityIds,
11166
+ sample: sample > 0 ? sample : undefined,
11167
+ api: this.params.getApi()
11168
+ }).then((res) => {
11169
+ const records = res.recordsByIds || {};
11170
+ if (!this.disposed && this.params.onRecords) {
11171
+ this.params.onRecords(records);
11172
+ }
11173
+ return records;
11174
+ }).finally(() => {
11175
+ this.inFlight.delete(key);
11176
+ });
11177
+ this.inFlight.set(key, prom);
11178
+ return prom;
11179
+ }
11180
+ }
11181
+ // How far back from the live edge is left unheld, so a record written moments ago still falls in
11182
+ // something that gets asked for again. It has to outlast a write, a commit and a refresh, plus
11183
+ // whatever the writer's clock and this one disagree by.
11184
+ HistoricGatherer.LIVE_TAIL_MS = 15000;
11185
+
10639
11186
  const BATCH_SIZE = 500;
10640
11187
  const CHECK_BATCH_SIZE = 250;
10641
11188
  function getValue$2(viewer, obj) {
@@ -10725,6 +11272,13 @@ var EntitiesRenderManager;
10725
11272
  this.item = item;
10726
11273
  this.visualsManager = visualsManager;
10727
11274
  this.onSeriesDiscovered = onSeriesDiscovered;
11275
+ this.historicGatherer = new HistoricGatherer({
11276
+ viewer: this.viewer,
11277
+ getApi: () => this.apiGetter.getApi(),
11278
+ getAttrKey: () => { var _a; return (_a = this.item.BruceEntity) === null || _a === void 0 ? void 0 : _a.historicAttrKey; },
11279
+ // A second either side, to take in records sitting exactly on the boundary.
11280
+ paddingMs: 1000
11281
+ });
10728
11282
  this.useGeojson = item.renderAsGeojson == true;
10729
11283
  if (item.enableClustering) {
10730
11284
  this.clustering = new PointClustering(this.visualsManager, this.item.id);
@@ -11688,6 +12242,13 @@ var EntitiesRenderManager;
11688
12242
  this.renderAsIndividualsChain = next;
11689
12243
  return next;
11690
12244
  }
12245
+ /**
12246
+ * Returns the historic records to interpolate between, fetching only what is not already held.
12247
+ */
12248
+ async gatherHistoric(entities) {
12249
+ const { recordsByIds } = await this.historicGatherer.Gather(entities);
12250
+ return recordsByIds;
12251
+ }
11691
12252
  async doRenderAsIndividuals(entities, force = false) {
11692
12253
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
11693
12254
  // When live we just want to show the latest pos.
@@ -11702,19 +12263,7 @@ var EntitiesRenderManager;
11702
12263
  const isHistoric = ((_a = this.item.BruceEntity) === null || _a === void 0 ? void 0 : _a.historic) || ((_b = this.item.BruceEntity) === null || _b === void 0 ? void 0 : _b.historicAttrKey);
11703
12264
  // If we're interpolating then we request additional records to quickly swap between.
11704
12265
  if (!isLive && isHistoric && entities.length && this.item.historicInterpolation) {
11705
- // We'll add/remove 1 second to ensure we cover all records.
11706
- const startTmp = JulianDate.toDate(this.viewer.clock.startTime);
11707
- const stopTmp = JulianDate.toDate(this.viewer.clock.stopTime);
11708
- const startStr = new Date(startTmp.getTime() - 1000).toISOString();
11709
- const stopStr = new Date(stopTmp.getTime() + 1000).toISOString();
11710
- const historicData = await EntityHistoricData.GetList({
11711
- attrKey: this.item.BruceEntity.historicAttrKey,
11712
- dateTimeFrom: startStr,
11713
- dateTimeTo: stopStr,
11714
- entityIds: entities.map(x => x.Bruce.ID),
11715
- api: this.apiGetter.getApi()
11716
- });
11717
- entitiesHistoric = historicData.recordsByIds;
12266
+ entitiesHistoric = await this.gatherHistoric(entities);
11718
12267
  }
11719
12268
  if (this.disposed) {
11720
12269
  this.doDispose();
@@ -12839,6 +13388,13 @@ var EntitiesIdsRenderManager;
12839
13388
  this.monitor = monitor;
12840
13389
  this.item = item;
12841
13390
  this.visualsManager = visualsManager;
13391
+ this.historicGatherer = new HistoricGatherer({
13392
+ viewer: this.viewer,
13393
+ getApi: () => this.apiGetter.getApi(),
13394
+ getAttrKey: () => { var _a; return (_a = this.item.BruceEntity) === null || _a === void 0 ? void 0 : _a.historicAttrKey; },
13395
+ // Padding either side of a requested range, for clock desync between us and the server.
13396
+ paddingMs: 15000
13397
+ });
12842
13398
  if (this.item.enableClustering) {
12843
13399
  this.clustering = new PointClustering(visualsManager, this.item.id, (_a = this.item) === null || _a === void 0 ? void 0 : _a.clustering);
12844
13400
  }
@@ -13188,138 +13744,22 @@ var EntitiesIdsRenderManager;
13188
13744
  * @param entities
13189
13745
  */
13190
13746
  async getHistoricInfo(entities) {
13191
- // Time padding in milliseconds (15 seconds).
13192
- // Helps account for desync between client and server.
13193
- const TIME_PADDING_MS = 15000;
13194
- const minDateTimeStr = this.viewer.clock.startTime.toString();
13195
- const maxDateTimeStr = this.viewer.clock.stopTime.toString();
13196
- const minDateTime = new Date(minDateTimeStr).getTime();
13197
- const maxDateTime = new Date(maxDateTimeStr).getTime();
13198
- const tDetails = ViewUtils.GetTimeDetails({
13199
- viewer: this.viewer,
13200
- });
13201
- const isLive = tDetails.isLive;
13202
- let rangesToRequest = [{
13203
- start: minDateTime,
13204
- stop: maxDateTime
13205
- }];
13206
- // If we already have cached data, determine what ranges we're missing.
13207
- if (Object.keys(this.entitiesHistoric).length >= entities.length) {
13208
- let foundMinDateTime = null;
13209
- let foundMaxDateTime = null;
13210
- // Find the min/max based on cached data.
13211
- // Since we set the values sorted, we only have to check the first and last records for each entity.
13212
- for (const entityId of Object.keys(this.entitiesHistoric)) {
13213
- const records = this.entitiesHistoric[entityId] || [];
13214
- if (records.length) {
13215
- const dateTime = new Date(records[0].dateTime).getTime();
13216
- if (foundMinDateTime == null || dateTime < foundMinDateTime) {
13217
- foundMinDateTime = dateTime;
13218
- }
13219
- const dateTime2 = new Date(records[records.length - 1].dateTime).getTime();
13220
- if (foundMaxDateTime == null || dateTime2 > foundMaxDateTime) {
13221
- foundMaxDateTime = dateTime2;
13222
- }
13223
- }
13224
- }
13225
- // Complete overlap - we already have all the data.
13226
- if (foundMinDateTime != null && foundMaxDateTime != null && foundMinDateTime <= minDateTime && foundMaxDateTime >= maxDateTime) {
13227
- return [false, this.entitiesHistoric];
13228
- }
13229
- // Calculate missing ranges.
13230
- rangesToRequest = [];
13231
- // Check if we need data before our cached range.
13232
- if (foundMinDateTime != null && foundMinDateTime > minDateTime) {
13233
- // When live we focus on getting the next range rather than old data.
13234
- // Ends up being one less request to perform every second.
13235
- if (!isLive || foundMinDateTime == null) {
13236
- rangesToRequest.push({
13237
- start: minDateTime,
13238
- stop: new Date(foundMinDateTime).getTime()
13239
- });
13240
- }
13241
- }
13242
- // Check if we need data after our cached range.
13243
- if (foundMaxDateTime != null && foundMaxDateTime < maxDateTime) {
13244
- rangesToRequest.push({
13245
- start: new Date(foundMaxDateTime).getTime(),
13246
- stop: maxDateTime
13247
- });
13248
- }
13249
- // If the ranges to request are within a tolerance, combine them.
13250
- if (rangesToRequest.length > 1) {
13251
- const TOLERANCE_MS = 60000; // 1 minute.
13252
- let currentRange = rangesToRequest[0];
13253
- for (let i = 1; i < rangesToRequest.length; i++) {
13254
- const nextRange = rangesToRequest[i];
13255
- if (nextRange.start - currentRange.stop <= TOLERANCE_MS) {
13256
- currentRange.stop = nextRange.stop;
13257
- }
13258
- else {
13259
- currentRange = nextRange;
13260
- }
13261
- }
13262
- }
13263
- }
13264
- const entityIds = entities.map(x => x.Bruce.ID);
13265
- const combined = { ...this.entitiesHistoric };
13266
- // Make requests for each missing range
13267
- for (const range of rangesToRequest) {
13268
- if (this.disposed) {
13269
- break;
13270
- }
13271
- const start = new Date(range.start - TIME_PADDING_MS);
13272
- const stop = new Date(range.stop + TIME_PADDING_MS);
13273
- const historicData = await EntityHistoricData.GetList({
13274
- attrKey: this.item.BruceEntity.historicAttrKey,
13275
- dateTimeFrom: start.toISOString(),
13276
- dateTimeTo: stop.toISOString(),
13277
- entityIds: entityIds,
13278
- api: this.apiGetter.getApi()
13279
- });
13280
- if (this.disposed) {
13281
- break;
13282
- }
13283
- // Merge the new data with existing data
13284
- const records = historicData.recordsByIds;
13285
- const recordsIds = Object.keys(records);
13286
- for (let i = 0; i < recordsIds.length; i++) {
13287
- const entityId = recordsIds[i];
13288
- const latest = records[entityId] || [];
13289
- const current = combined[entityId] || [];
13290
- // Use a Map to de-duplicate by timestamp.
13291
- const tmp = new Map();
13292
- for (let j = 0; j < current.length; j++) {
13293
- const record = current[j];
13294
- const dateTime = new Date(record.dateTime).getTime();
13295
- tmp.set(dateTime, record);
13296
- }
13297
- for (let j = 0; j < latest.length; j++) {
13298
- const record = latest[j];
13299
- const dateTime = new Date(record.dateTime).getTime();
13300
- tmp.set(dateTime, record);
13301
- }
13302
- // Convert to array and sort by date.
13303
- const sorted = Array.from(tmp.values()).sort((a, b) => {
13304
- return new Date(a.dateTime).getTime() - new Date(b.dateTime).getTime();
13305
- });
13306
- combined[entityId] = sorted;
13307
- }
13308
- }
13747
+ const { fetched, recordsByIds } = await this.historicGatherer.Gather(entities);
13309
13748
  if (!this.disposed) {
13310
- this.entitiesHistoric = combined;
13749
+ this.entitiesHistoric = recordsByIds;
13311
13750
  }
13312
- return [rangesToRequest.length > 0, combined];
13751
+ return [fetched, recordsByIds];
13313
13752
  }
13314
13753
  viewerDateTimeSub() {
13315
13754
  var _a, _b;
13316
13755
  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) {
13317
13756
  return;
13318
13757
  }
13319
- const THROTTLE_INTERVAL = 2000;
13758
+ // Scaled by how many Entities are on screen rather than fixed, since the cost of a refresh
13759
+ // is the set size and a large set barely changes between ticks anyway.
13320
13760
  let queue = new DelayQueue(() => {
13321
13761
  this.onGetterUpdate(Object.keys(this.renderedEntities));
13322
- }, THROTTLE_INTERVAL, true);
13762
+ }, this.historicGatherer.IntervalFor(Object.keys(this.renderedEntities).length), true);
13323
13763
  let clockTickRemoval;
13324
13764
  let prevTick = this.viewer.clock.currentTime.toString();
13325
13765
  clockTickRemoval = this.viewer.clock.onTick.addEventListener(() => {
@@ -14011,6 +14451,9 @@ class TilesetStyler {
14011
14451
  // Indicates that we are retrieving historic records.
14012
14452
  // This means that the current scene's time is included in the request.
14013
14453
  this.historic = false;
14454
+ // When false the Entity data is still gathered and the regos still updated, but no colour is
14455
+ // applied. A historic item that did not ask for styling needs the first without the second.
14456
+ this.applyStyles = true;
14014
14457
  this.reRenderState = new EntityReRenderMaintainState();
14015
14458
  // More expensive process.
14016
14459
  // When an Entity is styled, the rego is reviewed and updated if needed.
@@ -14047,13 +14490,16 @@ class TilesetStyler {
14047
14490
  }
14048
14491
  Init(params) {
14049
14492
  var _a, _b;
14050
- let { viewer, api, cTileset, fallbackStyleId, styleMapping, expandSources, menuItemId, register, scenario, historic } = params;
14493
+ let { viewer, api, cTileset, fallbackStyleId, styleMapping, expandSources, menuItemId, register, scenario, historic, applyStyles } = params;
14051
14494
  this.viewer = viewer;
14052
14495
  this.api = api;
14053
14496
  this.cTileset = cTileset;
14054
14497
  this.register = register;
14055
14498
  this.menuItemId = menuItemId;
14056
14499
  this.historic = Boolean(historic);
14500
+ if (applyStyles != null) {
14501
+ this.applyStyles = Boolean(applyStyles);
14502
+ }
14057
14503
  if (expandSources != null) {
14058
14504
  this.expandSources = expandSources;
14059
14505
  }
@@ -14547,33 +14993,31 @@ class TilesetStyler {
14547
14993
  return;
14548
14994
  }
14549
14995
  const { settings: style, styleId: resolvedStyleId } = this.getTilesetFeatureStyleWithId(rego.entityTypeId);
14550
- const fillColorTrace = (style && ((_a = style.modelStyle) === null || _a === void 0 ? void 0 : _a.fillColor)) ? Calculator.TraceGetColor(style.modelStyle.fillColor, data, tags) : { value: null, effective: null };
14551
- const bColor = fillColorTrace.value;
14552
- let cColor = null;
14553
- if (bColor == null) {
14554
- cColor = Color.WHITE;
14555
- }
14556
- else {
14557
- cColor = colorToCColor$3(bColor);
14558
- }
14996
+ const fillColorTrace = (this.applyStyles && style && ((_a = style.modelStyle) === null || _a === void 0 ? void 0 : _a.fillColor)) ? Calculator.TraceGetColor(style.modelStyle.fillColor, data, tags) : { value: null, effective: null };
14997
+ // Without styling the feature keeps whatever the tileset baked in. Falling through would paint
14998
+ // it the default white, which is a visible change for an item that only asked for historic.
14559
14999
  const override = this.overrideFeatureColor.get(rego.entityId) == true;
14560
- CesiumEntityStyler.SetDefaultColor({
14561
- color: cColor,
14562
- entity: visual,
14563
- viewer: this.viewer,
14564
- override: override
14565
- });
14566
- this.overrideFeatureColor.set(rego.entityId, true);
14567
- if (((_b = data === null || data === void 0 ? void 0 : data.Bruce) === null || _b === void 0 ? void 0 : _b.InternalID) != null) {
15000
+ if (this.applyStyles) {
15001
+ const bColor = fillColorTrace.value;
15002
+ const cColor = bColor == null ? Color.WHITE : colorToCColor$3(bColor);
15003
+ CesiumEntityStyler.SetDefaultColor({
15004
+ color: cColor,
15005
+ entity: visual,
15006
+ viewer: this.viewer,
15007
+ override: override
15008
+ });
15009
+ this.overrideFeatureColor.set(rego.entityId, true);
15010
+ if (resolvedStyleId != null) {
15011
+ rego.styleId = resolvedStyleId;
15012
+ }
15013
+ rego.styleEffective = (_b = StyleEffective.Combine([
15014
+ { key: "color", effective: fillColorTrace.effective }
15015
+ ])) !== null && _b !== void 0 ? _b : rego.styleEffective;
15016
+ }
15017
+ if (((_c = data === null || data === void 0 ? void 0 : data.Bruce) === null || _c === void 0 ? void 0 : _c.InternalID) != null) {
14568
15018
  rego.internalId = data.Bruce.InternalID;
14569
15019
  this.styledByInternalId.set(data.Bruce.InternalID, rego.entityId);
14570
15020
  }
14571
- if (resolvedStyleId != null) {
14572
- rego.styleId = resolvedStyleId;
14573
- }
14574
- rego.styleEffective = (_c = StyleEffective.Combine([
14575
- { key: "color", effective: fillColorTrace.effective }
14576
- ])) !== null && _c !== void 0 ? _c : rego.styleEffective;
14577
15021
  this.styledEntityIds.set(rego.entityId, true);
14578
15022
  this._styleProgressQueue.Call();
14579
15023
  // Since we only need to update it for scenarios right now.
@@ -14731,8 +15175,8 @@ async function createTileset(url, props, noMemoryLimit) {
14731
15175
  const defaultProps = {
14732
15176
  // "Optimization option. Reduce the screen space error for tiles that are further away from the camera.".
14733
15177
  dynamicScreenSpaceError: true,
14734
- environmentMapOptions: ViewUtils.DefaultEnvironmentMapOptions()
14735
15178
  };
15179
+ Object.assign(defaultProps, ViewUtils.DefaultLightingProps());
14736
15180
  if (noMemoryLimit != true) {
14737
15181
  // "The maximum amount of memory in MB that can be used by the tileset.".
14738
15182
  defaultProps.maximumMemoryUsage = 512;
@@ -15544,6 +15988,7 @@ var TilesetCadRenderManager;
15544
15988
  // Series of points to help interpolate movement when the timeline changes.
15545
15989
  this.historicPossesInitialLoaded = false;
15546
15990
  this.historicAnimation = null;
15991
+ this.historicGatherer = null;
15547
15992
  this.historicPossesLoadingProm = null;
15548
15993
  // Queue of loaded in features that we haven't processed yet.
15549
15994
  this.featureQueue = [];
@@ -16581,120 +17026,41 @@ var TilesetCadRenderManager;
16581
17026
  const api = this.getters.GetBruceApi({
16582
17027
  accountId: accountId
16583
17028
  });
16584
- // Time padding in milliseconds (15 seconds).
16585
- // Helps account for desync between client and server.
16586
- const TIME_PADDING_MS = 15000;
16587
- // 'start-stop' time string that maps to a pending request.
16588
- // Helps us avoid repeated requests that are the same.
16589
- const pendingRequests = new Map();
16590
- /**
16591
- * Returns a list of historic positions for a given time range.
16592
- * @param startStr
16593
- * @param stopStr
16594
- * @returns
17029
+ // The root Entity is the only one here, so nothing is banded and the plan sees a set of one.
17030
+ const rootEntity = { Bruce: { ID: this.rootId } };
17031
+ /*
17032
+ * Turns historic records into the animated positions this tileset moves along.
16595
17033
  */
16596
- const getPossesForRange = async (startStr, stopStr) => {
16597
- const requestKey = `${startStr}-${stopStr}`;
16598
- if (pendingRequests.has(requestKey)) {
16599
- return pendingRequests.get(requestKey);
16600
- }
16601
- const requestPromise = new Promise(async (res) => {
16602
- try {
16603
- const historicData = await EntityHistoricData.GetList({
16604
- attrKey: null,
16605
- dateTimeFrom: startStr,
16606
- dateTimeTo: stopStr,
16607
- entityIds: [this.rootId],
16608
- api: api
16609
- });
16610
- const posses = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(this.viewer, HeightReference.NONE, HeightReference.CLAMP_TO_GROUND, historicData.recordsByIds[this.rootId]);
16611
- res(posses);
16612
- }
16613
- catch (e) {
16614
- console.error(e);
16615
- res([]);
16616
- }
16617
- finally {
16618
- pendingRequests.delete(requestKey);
16619
- }
16620
- });
16621
- pendingRequests.set(requestKey, requestPromise);
16622
- return requestPromise;
17034
+ const toPosses = (records) => {
17035
+ return CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(this.viewer, HeightReference.NONE, HeightReference.CLAMP_TO_GROUND, records);
16623
17036
  };
17037
+ this.historicGatherer = new HistoricGatherer({
17038
+ viewer: this.viewer,
17039
+ getApi: () => api,
17040
+ // The assembly root snapshots the whole Entity, so no key narrows it.
17041
+ getAttrKey: () => null,
17042
+ // Time padding in milliseconds (15 seconds).
17043
+ // Helps account for desync between client and server.
17044
+ paddingMs: 15000,
17045
+ onRecords: (recordsByIds) => {
17046
+ var _a;
17047
+ const records = recordsByIds[this.rootId];
17048
+ if (!(records === null || records === void 0 ? void 0 : records.length) || !((_a = this.historicAnimation) === null || _a === void 0 ? void 0 : _a.addPositions)) {
17049
+ return;
17050
+ }
17051
+ this.historicAnimation.addPositions(toPosses(records));
17052
+ }
17053
+ });
16624
17054
  /**
16625
- * Checks if the current timeline range is not fully loaded and requests the difference.
16626
- * This will call getPossesForRange() if it needs more data.
17055
+ * Fetches whatever the current timeline range needs that is not already held.
17056
+ * Positions reach the animation through the gatherer's callback as each batch lands.
16627
17057
  * @returns
16628
17058
  */
16629
17059
  const checkTimelineRange = async () => {
16630
- // Current timeline range.
16631
- const minDateTime = new Date(this.viewer.clock.startTime.toString()).getTime();
16632
- const maxDateTime = new Date(this.viewer.clock.stopTime.toString()).getTime();
16633
- // What we have loaded.
16634
- const range = this.historicAnimation.getDateRange();
16635
- const foundMinDateTime = (range === null || range === void 0 ? void 0 : range.minDate) ? range.minDate.getTime() : null;
16636
- const foundMaxDateTime = (range === null || range === void 0 ? void 0 : range.maxDate) ? range.maxDate.getTime() : null;
16637
- // See if the current range is within the range we already have.
16638
- if (this.historicPossesInitialLoaded &&
16639
- foundMinDateTime &&
16640
- foundMaxDateTime &&
16641
- minDateTime >= foundMinDateTime &&
16642
- maxDateTime <= foundMaxDateTime) {
16643
- return;
16644
- }
16645
- const tDetails = ViewUtils.GetTimeDetails({
16646
- viewer: this.viewer,
16647
- });
16648
- const isLive = tDetails.isLive;
16649
- // See if the requested range is before or after the range we have.
16650
- const fetchBefore = !foundMinDateTime || (!isLive && minDateTime < foundMinDateTime);
16651
- const fetchAfter = !foundMaxDateTime || maxDateTime > foundMaxDateTime;
16652
- if (!fetchBefore && !fetchAfter) {
16653
- // Already have the data we need.
17060
+ if (this.disposed) {
16654
17061
  return;
16655
17062
  }
16656
- // No known range so we need to fetch the whole thing.
16657
- if (!foundMinDateTime || !foundMaxDateTime) {
16658
- const startStr = new Date(minDateTime - TIME_PADDING_MS).toISOString();
16659
- const stopStr = new Date(maxDateTime + TIME_PADDING_MS).toISOString();
16660
- const newPositions = await getPossesForRange(startStr, stopStr);
16661
- if (this.disposed) {
16662
- return;
16663
- }
16664
- if (this.historicAnimation && this.historicAnimation.addPositions) {
16665
- this.historicAnimation.addPositions(newPositions);
16666
- }
16667
- }
16668
- else {
16669
- // The data we want is before the range we've currently loaded.
16670
- if (fetchBefore) {
16671
- // Calculate the missing difference and request it.
16672
- const startStr = new Date(minDateTime - TIME_PADDING_MS).toISOString();
16673
- const stopStr = new Date(foundMinDateTime + TIME_PADDING_MS).toISOString();
16674
- getPossesForRange(startStr, stopStr).then((newPositions) => {
16675
- if (this.disposed) {
16676
- return;
16677
- }
16678
- if (this.historicAnimation && this.historicAnimation.addPositions) {
16679
- this.historicAnimation.addPositions(newPositions);
16680
- }
16681
- });
16682
- }
16683
- // The data we want is after the range we've currently loaded.
16684
- if (fetchAfter) {
16685
- // Calculate the missing difference and request it.
16686
- const startStr = new Date(foundMaxDateTime - TIME_PADDING_MS).toISOString();
16687
- const stopStr = new Date(maxDateTime + TIME_PADDING_MS).toISOString();
16688
- getPossesForRange(startStr, stopStr).then((newPositions) => {
16689
- if (this.disposed) {
16690
- return;
16691
- }
16692
- if (this.historicAnimation && this.historicAnimation.addPositions) {
16693
- this.historicAnimation.addPositions(newPositions);
16694
- }
16695
- });
16696
- }
16697
- }
17063
+ await this.historicGatherer.Gather([rootEntity]);
16698
17064
  };
16699
17065
  /**
16700
17066
  * Requests the initial set of historic positions for the timeline range.
@@ -16708,11 +17074,8 @@ var TilesetCadRenderManager;
16708
17074
  res(false);
16709
17075
  return;
16710
17076
  }
16711
- const startTmp = JulianDate.toDate(this.viewer.clock.startTime);
16712
- const stopTmp = JulianDate.toDate(this.viewer.clock.stopTime);
16713
- const startStr = new Date(startTmp.getTime() - 1000).toISOString();
16714
- const stopStr = new Date(stopTmp.getTime() + 1000).toISOString();
16715
- const positions = await getPossesForRange(startStr, stopStr);
17077
+ const { recordsByIds } = await this.historicGatherer.Gather([rootEntity]);
17078
+ const positions = toPosses(recordsByIds[this.rootId] || []);
16716
17079
  if (this.disposed) {
16717
17080
  res(false);
16718
17081
  return;
@@ -19499,12 +19862,11 @@ var TilesetEntitiesRenderManager;
19499
19862
  }
19500
19863
  this.viewer.scene.requestRender();
19501
19864
  TilesetRenderEngine.OnTilesetReady(this.cTileset).then(() => {
19502
- var _a;
19503
19865
  try {
19504
19866
  if (this.disposed || this.viewer.isDestroyed()) {
19505
19867
  return;
19506
19868
  }
19507
- if (this.item.ApplyStyles) {
19869
+ if (Manager.NeedsStyler(this.item)) {
19508
19870
  const api = this.getters.GetBruceApi();
19509
19871
  this.styler.Init({
19510
19872
  viewer: this.viewer,
@@ -19515,7 +19877,8 @@ var TilesetEntitiesRenderManager;
19515
19877
  expandSources: false,
19516
19878
  menuItemId: this.item.id,
19517
19879
  register: this.visualsManager,
19518
- historic: (_a = this.item.BruceEntity) === null || _a === void 0 ? void 0 : _a.historic
19880
+ historic: Manager.IsHistoric(this.item),
19881
+ applyStyles: Boolean(this.item.ApplyStyles)
19519
19882
  });
19520
19883
  }
19521
19884
  this.onCTilesetLoad();
@@ -19722,6 +20085,19 @@ var TilesetEntitiesRenderManager;
19722
20085
  get HandoffKey() {
19723
20086
  return Manager.GetHandoffKey(this.item);
19724
20087
  }
20088
+ /*
20089
+ * Returns whether the item asks for Entities as they were at the scene time.
20090
+ */
20091
+ static IsHistoric(item) {
20092
+ var _a, _b;
20093
+ return Boolean(((_a = item === null || item === void 0 ? void 0 : item.BruceEntity) === null || _a === void 0 ? void 0 : _a.historic) || ((_b = item === null || item === void 0 ? void 0 : item.BruceEntity) === null || _b === void 0 ? void 0 : _b.historicAttrKey));
20094
+ }
20095
+ /*
20096
+ * Returns whether the styler has to run, which it does for historic even without styling.
20097
+ */
20098
+ static NeedsStyler(item) {
20099
+ return Boolean(item === null || item === void 0 ? void 0 : item.ApplyStyles) || Manager.IsHistoric(item);
20100
+ }
19725
20101
  static GetHandoffKey(item) {
19726
20102
  var _a, _b;
19727
20103
  const tilesetId = (_a = item === null || item === void 0 ? void 0 : item.tileset) === null || _a === void 0 ? void 0 : _a.TilesetID;
@@ -19731,7 +20107,10 @@ var TilesetEntitiesRenderManager;
19731
20107
  const accountId = ((_b = item.tileset) === null || _b === void 0 ? void 0 : _b.ClientAccountID) || "";
19732
20108
  // Styled <-> unstyled has no cheap revert path, force a reload instead.
19733
20109
  const applyStyles = Boolean(item.ApplyStyles);
19734
- return `${accountId}:${tilesetId}:${applyStyles}`;
20110
+ // Historic drives a different gather,
20111
+ // so a handoff must not reuse a styler set up for the other mode.
20112
+ const historic = Manager.IsHistoric(item);
20113
+ return `${accountId}:${tilesetId}:${applyStyles}:${historic}`;
19735
20114
  }
19736
20115
  // Returns null (fall back to a normal Dispose()) if the tileset hasn't finished loading yet.
19737
20116
  PrepareHandoff() {
@@ -19771,7 +20150,6 @@ var TilesetEntitiesRenderManager;
19771
20150
  // Takes ownership of a PrepareHandoff() payload instead of loading via Init().
19772
20151
  // Then re-parents every already-registered rego to this menu item and re-runs styling.
19773
20152
  AdoptHandoff(payload) {
19774
- var _a;
19775
20153
  this.cTileset = payload.cTileset;
19776
20154
  this.tileset = payload.tileset;
19777
20155
  this.typeId = payload.typeId;
@@ -19803,7 +20181,7 @@ var TilesetEntitiesRenderManager;
19803
20181
  toPriority: this.renderPriority,
19804
20182
  requestRender: false
19805
20183
  });
19806
- if (this.item.ApplyStyles) {
20184
+ if (Manager.NeedsStyler(this.item)) {
19807
20185
  this.styler.Init({
19808
20186
  viewer: this.viewer,
19809
20187
  api: this.getters.GetBruceApi(),
@@ -19813,7 +20191,8 @@ var TilesetEntitiesRenderManager;
19813
20191
  expandSources: false,
19814
20192
  menuItemId: this.item.id,
19815
20193
  register: this.visualsManager,
19816
- historic: (_a = this.item.BruceEntity) === null || _a === void 0 ? void 0 : _a.historic
20194
+ historic: Manager.IsHistoric(this.item),
20195
+ applyStyles: Boolean(this.item.ApplyStyles)
19817
20196
  });
19818
20197
  this.queueHandoffRestyle(this.visualsManager.GetRegos({ menuItemId: this.item.id }));
19819
20198
  }
@@ -21469,7 +21848,7 @@ var TilesetGooglePhotosRenderManager;
21469
21848
  }
21470
21849
  this.cTileset = await CESIUM.Cesium3DTileset.fromUrl(proxyRootUrl, {
21471
21850
  showCreditsOnScreen: true,
21472
- environmentMapOptions: ViewUtils.DefaultEnvironmentMapOptions()
21851
+ ...ViewUtils.DefaultLightingProps()
21473
21852
  });
21474
21853
  }
21475
21854
  else {
@@ -23013,7 +23392,7 @@ var AssemblyRenderManager;
23013
23392
  const cEntity = this.viewer.entities.add({
23014
23393
  model: {
23015
23394
  uri: lod["ClientFile"].URL,
23016
- environmentMapOptions: ViewUtils.DefaultEnvironmentMapOptions(),
23395
+ ...ViewUtils.DefaultLightingProps(),
23017
23396
  heightReference: HeightReference.NONE
23018
23397
  },
23019
23398
  // point: {
@@ -38768,7 +39147,7 @@ var EntityRenderEngineModel3d;
38768
39147
  id: ObjectUtils.UId(15),
38769
39148
  model: {
38770
39149
  uri: params.lodUrl,
38771
- environmentMapOptions: ViewUtils.DefaultEnvironmentMapOptions(),
39150
+ ...ViewUtils.DefaultLightingProps(),
38772
39151
  heightReference: heightRef,
38773
39152
  scale: new CallbackProperty(() => scale * styleScale, true),
38774
39153
  shadows: ShadowMode.ENABLED,
@@ -39411,7 +39790,7 @@ async function getModelEntity(url, scale) {
39411
39790
  url: url,
39412
39791
  asynchronous: true,
39413
39792
  scale: scale,
39414
- environmentMapOptions: ViewUtils.DefaultEnvironmentMapOptions()
39793
+ ...ViewUtils.DefaultLightingProps()
39415
39794
  });
39416
39795
  }
39417
39796
  // Older versions of Cesium have a fromGltf method.
@@ -43573,7 +43952,7 @@ var StyleUtils;
43573
43952
  StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
43574
43953
  })(StyleUtils || (StyleUtils = {}));
43575
43954
 
43576
- const VERSION = "7.1.9";
43955
+ const VERSION = "7.2.1";
43577
43956
  /**
43578
43957
  * Updates the environment instance used by bruce-cesium to one specified.
43579
43958
  * This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.
@@ -43590,5 +43969,5 @@ const getENVIRONMENT = () => {
43590
43969
  return ENVIRONMENT;
43591
43970
  };
43592
43971
 
43593
- export { VERSION, setENVIRONMENT, getENVIRONMENT, CesiumAnimatedInOut, DisplacedSurfacePrimitive, CesiumAnimatedProperty, isStyleChanged, isOutlineChanged$1 as isOutlineChanged, StyleEffective, EntityRenderEngine, EntityRenderEngineModel3d, EntityRenderEnginePoint, EntityRenderEnginePolygon, EntityRenderEnginePolyline, MenuItemCreator, MenuItemManager, CesiumParabola, EntityLabel, LiveCursor, SharedGetters, DataSourceStaticKmlManager, DataLabRenderManager, EntitiesIdsRenderManager, EntitiesLoadedRenderManager, EntitiesRenderManager, EntityRenderManager, AssemblyRenderManager, GoogleSearchRenderManager, RelationsRenderManager, RenderManager, TilesetArbRenderManager, TilesetCadRenderManager, TilesetEntitiesRenderManager, TilesetGooglePhotosRenderManager, TilesetOsmRenderManager, TilesetPointcloudRenderManager, TileRenderEngine, createTileset, TilesetRenderEngine, ViewRenderEngine, VisualRegisterCuller, VisualsRegister, XGridsRenderEngine, CesiumEntityStyler, DrawingUtils, EntityUtils, MeasureUtils, StyleUtils, CESIUM_INSPECTOR_KEY, CESIUM_TIMELINE_KEY, CESIUM_TIMELINE_LIVE_KEY, CESIUM_TIMELINE_LIVE_PADDING_KEY, CESIUM_TIMELINE_INTERVAL_KEY, CESIUM_MODEL_SPACE_KEY, DEFAULT_LIVE_PADDING_SECONDS, DEFAULT_BRIGHTNESS_SHIFT, DEFAULT_LIGHT_INTENSITY, LIGHTING_SETTINGS_VERSION, ViewUtils, CesiumViewMonitor, ViewGroundArea, ViewerEventTracker, ViewerUtils, Draw3dPolygon, Draw3dPolyline, MeasureCreator, Walkthrough, WidgetControlViewBar, WidgetControlViewBarSearch, WidgetLeftPanelTab, WidgetLeftPanelTabBookmarks, WidgetLeftPanelTabDashboard, EMenuItemState, WidgetLeftPanelTabMenuItems, Widget, VIEWER_BOOKMARKS_WIDGET_KEY, WidgetBookmarks, WidgetBranding, WidgetCursorBar, WidgetEmbeddedInfoView, WidgetInfoView, VIEWER_LEFT_PANEL_WIDGET_KEY, VIEWER_LEFT_PANEL_CSS_VAR_LEFT, WidgetLeftPanel, WidgetNavCompass$$1 as WidgetNavCompass, VIEWER_VIEW_BAR_WIDGET_KEY, WidgetViewBar };
43972
+ export { VERSION, setENVIRONMENT, getENVIRONMENT, CesiumAnimatedInOut, DisplacedSurfacePrimitive, CesiumAnimatedProperty, isStyleChanged, isOutlineChanged$1 as isOutlineChanged, StyleEffective, EntityRenderEngine, EntityRenderEngineModel3d, EntityRenderEnginePoint, EntityRenderEnginePolygon, EntityRenderEnginePolyline, MenuItemCreator, MenuItemManager, CesiumParabola, EntityLabel, LiveCursor, SharedGetters, DataSourceStaticKmlManager, DataLabRenderManager, EntitiesIdsRenderManager, EntitiesLoadedRenderManager, EntitiesRenderManager, EntityRenderManager, AssemblyRenderManager, GoogleSearchRenderManager, RelationsRenderManager, RenderManager, TilesetArbRenderManager, TilesetCadRenderManager, TilesetEntitiesRenderManager, TilesetGooglePhotosRenderManager, TilesetOsmRenderManager, TilesetPointcloudRenderManager, TileRenderEngine, createTileset, TilesetRenderEngine, ViewRenderEngine, VisualRegisterCuller, VisualsRegister, XGridsRenderEngine, CesiumEntityStyler, DrawingUtils, EntityUtils, MeasureUtils, StyleUtils, CESIUM_INSPECTOR_KEY, CESIUM_TIMELINE_KEY, CESIUM_TIMELINE_LIVE_KEY, CESIUM_TIMELINE_LIVE_PADDING_KEY, CESIUM_TIMELINE_INTERVAL_KEY, CESIUM_MODEL_SPACE_KEY, DEFAULT_LIVE_PADDING_SECONDS, DEFAULT_BRIGHTNESS_SHIFT, DEFAULT_LIGHT_INTENSITY, LIGHTING_SETTINGS_VERSION, LIGHT_INTENSITY_COMPENSATION, MAX_COMPENSATED_LIGHT_INTENSITY, ViewUtils, CesiumViewMonitor, ViewGroundArea, ViewerEventTracker, ViewerUtils, Draw3dPolygon, Draw3dPolyline, MeasureCreator, Walkthrough, WidgetControlViewBar, WidgetControlViewBarSearch, WidgetLeftPanelTab, WidgetLeftPanelTabBookmarks, WidgetLeftPanelTabDashboard, EMenuItemState, WidgetLeftPanelTabMenuItems, Widget, VIEWER_BOOKMARKS_WIDGET_KEY, WidgetBookmarks, WidgetBranding, WidgetCursorBar, WidgetEmbeddedInfoView, WidgetInfoView, VIEWER_LEFT_PANEL_WIDGET_KEY, VIEWER_LEFT_PANEL_CSS_VAR_LEFT, WidgetLeftPanel, WidgetNavCompass$$1 as WidgetNavCompass, VIEWER_VIEW_BAR_WIDGET_KEY, WidgetViewBar };
43594
43973
  //# sourceMappingURL=bruce-cesium.es5.js.map