bruce-cesium 5.4.4 → 5.4.6

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 (26) hide show
  1. package/dist/bruce-cesium.es5.js +1772 -1782
  2. package/dist/bruce-cesium.es5.js.map +1 -1
  3. package/dist/bruce-cesium.umd.js +1770 -1780
  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-gatherer.js +327 -0
  7. package/dist/lib/rendering/entity-gatherer.js.map +1 -0
  8. package/dist/lib/rendering/render-managers/entities/entities-ids-render-manager.js +7 -4
  9. package/dist/lib/rendering/render-managers/entities/entities-ids-render-manager.js.map +1 -1
  10. package/dist/lib/rendering/render-managers/tilesets/tileset-arb-render-manager.js +6 -2
  11. package/dist/lib/rendering/render-managers/tilesets/tileset-arb-render-manager.js.map +1 -1
  12. package/dist/lib/rendering/render-managers/tilesets/tileset-cad-render-manager.js +5 -3
  13. package/dist/lib/rendering/render-managers/tilesets/tileset-cad-render-manager.js.map +1 -1
  14. package/dist/lib/rendering/render-managers/tilesets/tileset-entities-render-manager.js +6 -2
  15. package/dist/lib/rendering/render-managers/tilesets/tileset-entities-render-manager.js.map +1 -1
  16. package/dist/lib/rendering/tileset-render-engine.js +5 -899
  17. package/dist/lib/rendering/tileset-render-engine.js.map +1 -1
  18. package/dist/lib/rendering/tileset-styler.js +555 -0
  19. package/dist/lib/rendering/tileset-styler.js.map +1 -0
  20. package/dist/lib/utils/entity-utils.js +11 -11
  21. package/dist/lib/utils/entity-utils.js.map +1 -1
  22. package/dist/types/bruce-cesium.d.ts +1 -1
  23. package/dist/types/rendering/entity-gatherer.d.ts +36 -0
  24. package/dist/types/rendering/tileset-render-engine.d.ts +3 -99
  25. package/dist/types/rendering/tileset-styler.d.ts +80 -0
  26. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import { BruceEvent, Cartes, Entity as Entity$1, Carto, Geometry, MathUtils, LRUCache, Api, Calculator, ClientFile, EntityTag, EntityType, ObjectUtils, Style, ProjectViewTile, DelayQueue, EntityLod, Bounds, ZoomControl, EntityRelationType, ENVIRONMENT, EntityHistoricData, Tileset, EntityCoords, DataLab, EntitySource, MenuItem, EntityRelation, ProgramKey, ProjectView, ProjectViewBookmark, Camera, ProjectViewLegacyTile, EntityAttachment, EntityAttachmentType, EntityAttribute, AbstractApi, Session } from 'bruce-models';
2
2
  import * as Cesium from 'cesium';
3
- import { Cartographic, Cartesian2, Math as Math$1, Cartesian3, CallbackProperty, Color, HeightReference, Rectangle, JulianDate, Entity, DistanceDisplayCondition, HorizontalOrigin, VerticalOrigin, ConstantProperty, ClassificationType, ConstantPositionProperty, ArcType, CornerType, ShadowMode, PolygonHierarchy, PolylineGraphics, ColorMaterialProperty, ColorBlendMode, HeadingPitchRoll, Transforms, Model, SceneMode, Primitive, Cesium3DTileFeature, GeoJsonDataSource, Cesium3DTileStyle, Cesium3DTileColorBlendMode, HeadingPitchRange, Ion, KmlDataSource, SceneTransforms, OrthographicFrustum, EasingFunction, NearFarScalar, EllipsoidTerrainProvider, CesiumInspector, defined, ClockRange, Cesium3DTileset, Matrix4, Matrix3, IonResource, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, EllipsoidGeodesic, sampleTerrainMostDetailed, PolygonPipeline, BoundingSphere, GeometryInstance, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, Quaternion, ScreenSpaceEventHandler, ScreenSpaceEventType, CzmlDataSource, Intersect, Fullscreen } from 'cesium';
3
+ import { Cartographic, Cartesian2, Math as Math$1, Cartesian3, CallbackProperty, Color, HeightReference, Rectangle, JulianDate, Entity, DistanceDisplayCondition, ClassificationType, ArcType, CornerType, ShadowMode, ConstantProperty, ConstantPositionProperty, PolygonHierarchy, PolylineGraphics, ColorMaterialProperty, ColorBlendMode, HeadingPitchRoll, Transforms, Model, HorizontalOrigin, VerticalOrigin, Primitive, Cesium3DTileFeature, SceneMode, GeoJsonDataSource, Cesium3DTileStyle, Cesium3DTileColorBlendMode, HeadingPitchRange, Ion, KmlDataSource, SceneTransforms, Cesium3DTileset, Matrix4, Matrix3, IonResource, EllipsoidTerrainProvider, CesiumInspector, OrthographicFrustum, defined, ClockRange, EasingFunction, NearFarScalar, EllipsoidGeodesic, sampleTerrainMostDetailed, PolygonPipeline, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, BoundingSphere, GeometryInstance, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, ScreenSpaceEventHandler, ScreenSpaceEventType, Quaternion, CzmlDataSource, Intersect, Fullscreen } from 'cesium';
4
4
 
5
5
  const TIME_LAG = 300;
6
6
  const POSITION_CHECK_TIMER = 950;
@@ -2809,8 +2809,8 @@ var EntityUtils;
2809
2809
  if (location) {
2810
2810
  const latitude = EnsureNumber(location.latitude);
2811
2811
  const longitude = EnsureNumber(location.longitude);
2812
- // Disallowing exact 0.
2813
- if (latitude || longitude) {
2812
+ // Disallowing 0.
2813
+ if (latitude && longitude) {
2814
2814
  const pos3d = Cartesian3.fromDegrees(longitude, latitude, EnsureNumber(location.altitude));
2815
2815
  const toAdjust = [pos3d];
2816
2816
  await ensureHeightRefs(toAdjust, sample);
@@ -2982,8 +2982,8 @@ var EntityUtils;
2982
2982
  if (location && typeof location == "object" && Carto.ValidateCarto(location)) {
2983
2983
  const latitude = EnsureNumber(location.latitude);
2984
2984
  const longitude = EnsureNumber(location.longitude);
2985
- // Disallowing exact 0.
2986
- if (latitude || longitude) {
2985
+ // Disallowing 0.
2986
+ if (latitude && longitude) {
2987
2987
  const pos3d = Cartesian3.fromDegrees(longitude, latitude, EnsureNumber(location.altitude));
2988
2988
  posses.push(pos3d);
2989
2989
  }
@@ -3034,8 +3034,8 @@ var EntityUtils;
3034
3034
  longitude: (EnsureNumber(boundaries.minLongitude) + EnsureNumber(boundaries.maxLongitude)) / 2,
3035
3035
  altitude: 0
3036
3036
  };
3037
- // Disallowing exact 0.
3038
- if (point.latitude || point.longitude) {
3037
+ // Disallowing 0.
3038
+ if (point.latitude && point.longitude) {
3039
3039
  const bPosses = [
3040
3040
  Cartesian3.fromDegrees(EnsureNumber(boundaries.minLongitude), EnsureNumber(boundaries.minLatitude), EnsureNumber(boundaries.minAltitude)),
3041
3041
  Cartesian3.fromDegrees(EnsureNumber(boundaries.maxLongitude), EnsureNumber(boundaries.maxLatitude), EnsureNumber(boundaries.maxAltitude))
@@ -3297,8 +3297,8 @@ var EntityUtils;
3297
3297
  if (location && typeof location == "object" && Carto.ValidateCarto(location)) {
3298
3298
  const latitude = EnsureNumber(location.latitude);
3299
3299
  const longitude = EnsureNumber(location.longitude);
3300
- // Disallowing exact 0.
3301
- if (latitude || longitude) {
3300
+ // Disallowing 0.
3301
+ if (latitude && longitude) {
3302
3302
  return Cartesian3.fromDegrees(longitude, latitude, EnsureNumber(location.altitude));
3303
3303
  }
3304
3304
  }
@@ -3360,8 +3360,8 @@ var EntityUtils;
3360
3360
  longitude: (EnsureNumber(boundaries.minLongitude) + EnsureNumber(boundaries.maxLongitude)) / 2,
3361
3361
  altitude: 0
3362
3362
  };
3363
- // Disallowing exact 0.
3364
- if (point.latitude || point.longitude) {
3363
+ // Disallowing 0.
3364
+ if (point.latitude && point.longitude) {
3365
3365
  if (Carto.ValidateCarto(point)) {
3366
3366
  return Cartesian3.fromDegrees(point.longitude, point.latitude, point.altitude);
3367
3367
  }
@@ -3417,7 +3417,7 @@ var EntityUtils;
3417
3417
  catch (e) {
3418
3418
  console.error(e);
3419
3419
  }
3420
- if (entity && (isNaN(lat) || isNaN(lon) || (lat == 0 && lon == 0))) {
3420
+ if (entity && (isNaN(lat) || isNaN(lon) || (lat == 0 || lon == 0))) {
3421
3421
  try {
3422
3422
  entity = (await Entity$1.Get({
3423
3423
  api,
@@ -14314,8 +14314,11 @@ var EntitiesIdsRenderManager;
14314
14314
  return;
14315
14315
  }
14316
14316
  let entitiesHistoric = {};
14317
+ let changed = false;
14317
14318
  if ((this.item.BruceEntity.historic || this.item.BruceEntity.historicAttrKey) && entities.length) {
14318
- entitiesHistoric = await this.getHistoricInfo(entities);
14319
+ const res = await this.getHistoricInfo(entities);
14320
+ changed = res[0];
14321
+ entitiesHistoric = res[1];
14319
14322
  }
14320
14323
  const { updated, entities: cEntities } = await EntityRenderEngine.Render({
14321
14324
  viewer: this.viewer,
@@ -14326,7 +14329,7 @@ var EntitiesIdsRenderManager;
14326
14329
  entitiesHistoric: entitiesHistoric,
14327
14330
  entityHistoricDrawTrack: this.item.historicDrawTrack,
14328
14331
  zoomControl: this.item.CameraZoomSettings,
14329
- force
14332
+ force: force || changed
14330
14333
  });
14331
14334
  for (let i = 0; i < entities.length; i++) {
14332
14335
  if (this.disposed) {
@@ -14406,7 +14409,7 @@ var EntitiesIdsRenderManager;
14406
14409
  const newStop = stopTmp.getTime();
14407
14410
  // Complete overlap - we already have all the data.
14408
14411
  if (newStart >= cachedStart && newStop <= cachedStop) {
14409
- return this.entitiesHistoric;
14412
+ return [false, this.entitiesHistoric];
14410
14413
  }
14411
14414
  // Calculate missing ranges.
14412
14415
  rangesToRequest = [];
@@ -14481,7 +14484,7 @@ var EntitiesIdsRenderManager;
14481
14484
  this.lastHistoricMax = stopTmp.toISOString();
14482
14485
  this.entitiesHistoric = combined;
14483
14486
  }
14484
- return combined;
14487
+ return [rangesToRequest.length > 0, combined];
14485
14488
  }
14486
14489
  viewerDateTimeSub() {
14487
14490
  var _a, _b;
@@ -14700,1002 +14703,890 @@ var EntityRenderManager;
14700
14703
  EntityRenderManager.Manager = Manager;
14701
14704
  })(EntityRenderManager || (EntityRenderManager = {}));
14702
14705
 
14703
- /**
14704
- * This is a helper for making entity requests based on a grid area.
14705
- * It will return grid cells based on given view-area then will remember when stuff-
14706
- * was fully-fetched for those cells to avoid making duplicate requests.
14707
- */
14708
- var EntityGlobe;
14709
- (function (EntityGlobe) {
14710
- class Cell {
14711
- constructor() {
14712
- this.Fetched = false;
14713
- this.IsFetched = null;
14714
- this.FetchPageIndex = 0;
14715
- // Optional URL to use instead of default URL generation.
14716
- // This is typically a response from the API to use this specific URL for the next page.
14717
- this.FetchURL = null;
14718
- this.Boundaries = null;
14719
- this.Fetching = false;
14706
+ class EntityGatherer {
14707
+ get OnQueueProgress() {
14708
+ if (!this._onQueueProgress) {
14709
+ this._onQueueProgress = new BruceEvent();
14720
14710
  }
14721
- GetBounds() {
14722
- // Entity data works in -180 to 180 range.
14723
- function prepareRangeForBounds(range) {
14724
- if (range > 180) {
14725
- return range - 360;
14711
+ return this._onQueueProgress;
14712
+ }
14713
+ constructor(api, viewer, emitEntities) {
14714
+ this.disposed = false;
14715
+ this.expandSources = false;
14716
+ this.historic = null;
14717
+ this.hDisposals = [];
14718
+ // Last date-time taken from the clock.
14719
+ this.lastDateTime = null;
14720
+ // Last date-time range taken from the clock.
14721
+ this.lastDateTimeMin = null;
14722
+ this.lastDateTimeMax = null;
14723
+ // Entity ID -> last resolved date time.
14724
+ // Helps determine if a slow resolved request can still be applied while we wait for the next one.
14725
+ this.lastFoundDateTimes = new Map();
14726
+ // ID of Entity IDs that need to be requested.
14727
+ this.eIdQueue = [];
14728
+ // All IDs that have ever been queued.
14729
+ // When nothing is in queue, we'll re-request the existing IDs based on timeline changes.
14730
+ this.allEIds = new Set();
14731
+ // Indicates a tick is active.
14732
+ // When true, we won't bother calling onTick again.
14733
+ this.runningTick = false;
14734
+ // Entity ID -> Entity blob.
14735
+ // This is a tool that lets developers set their own JSON blobs instead of requesting data from API.
14736
+ this.entityDataOverrides = new Map();
14737
+ // 0 - 100 % progress callback for the queue.
14738
+ // Only called if at least 1 Entity is in the queue at start of the tick.
14739
+ this._onQueueProgress = null;
14740
+ this.api = api;
14741
+ this.viewer = viewer;
14742
+ this.emitEntities = emitEntities;
14743
+ }
14744
+ SetEntityDataOverride(eId, entity) {
14745
+ if (this.disposed) {
14746
+ return;
14747
+ }
14748
+ if (entity) {
14749
+ this.entityDataOverrides.set(eId, entity);
14750
+ }
14751
+ else {
14752
+ this.entityDataOverrides.delete(eId);
14753
+ }
14754
+ }
14755
+ /**
14756
+ * Initializes or updates the manager with given new state settings.
14757
+ * @param historic
14758
+ * @param expandSources
14759
+ * @returns
14760
+ */
14761
+ Init(historic, expandSources) {
14762
+ if (historic == null || historic == undefined) {
14763
+ historic = this.historic;
14764
+ }
14765
+ if (expandSources == null || expandSources == undefined) {
14766
+ expandSources = this.expandSources;
14767
+ }
14768
+ if (historic === this.historic && expandSources === this.expandSources) {
14769
+ return;
14770
+ }
14771
+ this.historic = historic;
14772
+ this.expandSources = expandSources;
14773
+ if (this.historic) {
14774
+ // 4 ticks per second.
14775
+ // The fastest data we are working with is 5 times per second.
14776
+ let tickDelay = new DelayQueue(() => {
14777
+ this.onTick(false);
14778
+ }, 250, true);
14779
+ this.hDisposals.push(() => {
14780
+ if (tickDelay) {
14781
+ tickDelay.Dispose();
14782
+ }
14783
+ tickDelay = null;
14784
+ });
14785
+ // React to clock changes and request new Entities.
14786
+ this.hDisposals.push(this.viewer.clock.onTick.addEventListener(() => {
14787
+ if (tickDelay) {
14788
+ tickDelay.Call();
14726
14789
  }
14727
- return range;
14728
- }
14729
- // Add minor decimal as API crashes when giving it whole numbers.
14730
- const maxLon = prepareRangeForBounds(this.Boundaries.maxLongitude);
14731
- const minLon = prepareRangeForBounds(this.Boundaries.minLongitude);
14732
- const maxLat = prepareRangeForBounds(this.Boundaries.maxLatitude);
14733
- const minLat = prepareRangeForBounds(this.Boundaries.minLatitude);
14734
- return {
14735
- east: maxLon,
14736
- north: maxLat,
14737
- south: minLat,
14738
- west: minLon
14739
- };
14790
+ }));
14791
+ // Since the timeline range can change even without it ticking we have to check it occasionally.
14792
+ let rangeCheckInterval = setInterval(() => {
14793
+ this.checkRange(false);
14794
+ }, 800);
14795
+ this.hDisposals.push(() => {
14796
+ if (rangeCheckInterval) {
14797
+ clearInterval(rangeCheckInterval);
14798
+ }
14799
+ rangeCheckInterval = null;
14800
+ });
14801
+ }
14802
+ else {
14803
+ this.hDisposals.forEach((d) => d());
14804
+ this.hDisposals = [];
14740
14805
  }
14806
+ this.checkRange(true);
14741
14807
  }
14742
- EntityGlobe.Cell = Cell;
14743
- class Grid {
14744
- constructor() {
14745
- this.cells = {};
14808
+ Empty() {
14809
+ if (this.disposed) {
14810
+ return;
14746
14811
  }
14747
- GetCellsForView(cameraPos, viewRect) {
14748
- const cells = [];
14749
- const minLat = viewRect.south;
14750
- const minLon = viewRect.west;
14751
- const maxLat = viewRect.north;
14752
- const maxLon = viewRect.east;
14753
- const MAX_CELLS = 150;
14754
- const cellDegreeSize = getCellSizeFromHeight(viewRect.alt);
14755
- // console.log("cell size", cellDegreeSize, "height", viewRect.alt);
14756
- let curMinLon = floorValueToCellSize(cellDegreeSize, minLon);
14757
- let curMinLat = floorValueToCellSize(cellDegreeSize, minLat);
14758
- // For larger views we add additional padding because our view-area culling is too strong.
14759
- if (cellDegreeSize >= 2) {
14760
- curMinLon -= cellDegreeSize;
14761
- curMinLat -= cellDegreeSize;
14762
- }
14763
- let centerX = cameraPos === null || cameraPos === void 0 ? void 0 : cameraPos.longitude;
14764
- let centerY = cameraPos === null || cameraPos === void 0 ? void 0 : cameraPos.latitude;
14765
- if (isNaN(centerX) || isNaN(centerY)) {
14766
- centerX = (minLon + maxLon) / 2;
14767
- centerY = (minLat + maxLat) / 2;
14768
- }
14769
- let width = Math.ceil((maxLon - curMinLon) / cellDegreeSize);
14770
- let height = Math.ceil((maxLat - curMinLat) / cellDegreeSize);
14771
- // For larger views we add additional padding because our view-area culling is too strong.
14772
- if (cellDegreeSize >= 2) {
14773
- width += 1;
14774
- height += 1;
14775
- }
14776
- const cellDistances = [];
14777
- for (let x = 0; x < width; x++) {
14778
- for (let y = 0; y < height; y++) {
14779
- const lon = x * cellDegreeSize + curMinLon;
14780
- const lat = y * cellDegreeSize + curMinLat;
14781
- const cellCenterX = lon + cellDegreeSize / 2;
14782
- const cellCenterY = lat + cellDegreeSize / 2;
14783
- const dist = Math.sqrt(Math.pow(cellCenterX - centerX, 2) + Math.pow(cellCenterY - centerY, 2));
14784
- cellDistances.push({ x, y, dist });
14812
+ this.eIdQueue = [];
14813
+ this.allEIds.clear();
14814
+ this.lastFoundDateTimes.clear();
14815
+ this.lastDateTime = null;
14816
+ }
14817
+ ReQueueAll() {
14818
+ if (this.disposed) {
14819
+ return;
14820
+ }
14821
+ this.eIdQueue = Array.from(this.allEIds);
14822
+ }
14823
+ Queue(entityIds, topOfQueue = false) {
14824
+ if (this.disposed || !(entityIds === null || entityIds === void 0 ? void 0 : entityIds.length)) {
14825
+ return;
14826
+ }
14827
+ let changes = 0;
14828
+ for (let i = 0; i < entityIds.length; i++) {
14829
+ const eId = entityIds[i];
14830
+ const index = this.eIdQueue.indexOf(eId);
14831
+ if (topOfQueue) {
14832
+ // If we want it to be at the top of the queue, remove it from the queue first.
14833
+ if (index !== -1) {
14834
+ this.eIdQueue.splice(index, 1);
14785
14835
  }
14836
+ this.eIdQueue.unshift(eId);
14837
+ changes += 1;
14786
14838
  }
14787
- cellDistances.sort((a, b) => a.dist - b.dist);
14788
- for (const { x, y } of cellDistances) {
14789
- const lon = x * cellDegreeSize + curMinLon;
14790
- const lat = y * cellDegreeSize + curMinLat;
14791
- const [id, cell] = getOrCreateCell(this.cells, cellDegreeSize, lon, lon + cellDegreeSize, lat, lat + cellDegreeSize);
14792
- cells.push(cell);
14793
- if (cells.length >= MAX_CELLS) {
14794
- break;
14795
- }
14839
+ else if (index === -1) {
14840
+ this.eIdQueue.push(eId);
14841
+ changes += 1;
14796
14842
  }
14797
- return cells;
14843
+ // Flag that we've seen this ID.
14844
+ this.allEIds.add(entityIds[i]);
14798
14845
  }
14846
+ if (!changes) {
14847
+ return;
14848
+ }
14849
+ this.onTick(false);
14799
14850
  }
14800
- EntityGlobe.Grid = Grid;
14801
- })(EntityGlobe || (EntityGlobe = {}));
14802
- function floorValueToCellSize(size, value) {
14803
- return Math.floor(value / size) * size;
14804
- }
14805
- function getCellSizeFromHeight(height) {
14806
- if (height < 1000) {
14807
- return 0.01;
14808
- }
14809
- if (height < 5000) {
14810
- return 0.04;
14851
+ Dispose() {
14852
+ if (this.disposed) {
14853
+ return;
14854
+ }
14855
+ this.disposed = true;
14856
+ this.hDisposals.forEach((d) => d());
14857
+ this.hDisposals = [];
14811
14858
  }
14812
- else if (height < 10000) {
14813
- return 0.15;
14859
+ checkRange(force) {
14860
+ const min = this.viewer.clock.startTime.toString();
14861
+ const max = this.viewer.clock.stopTime.toString();
14862
+ if (force || this.lastDateTimeMin !== min || this.lastDateTimeMax !== max) {
14863
+ this.lastDateTimeMin = min;
14864
+ this.lastDateTimeMax = max;
14865
+ // Changed, so we can pretend it's a clock tick and request new Entities.
14866
+ this.onTick(true);
14867
+ }
14814
14868
  }
14815
- else if (height < 30000) {
14816
- return 0.5;
14869
+ onTick(force) {
14870
+ if (this.runningTick) {
14871
+ return;
14872
+ }
14873
+ const rTime = this.viewer.clock.currentTime.toString();
14874
+ if (!force && !this.eIdQueue.length && this.historic && this.lastDateTime === rTime) {
14875
+ return;
14876
+ }
14877
+ this.lastDateTime = rTime;
14878
+ (async () => {
14879
+ this.runningTick = true;
14880
+ let rerun = false;
14881
+ try {
14882
+ const handleResponse = async (entities) => {
14883
+ var _a, _b;
14884
+ if (!(entities === null || entities === void 0 ? void 0 : entities.length)) {
14885
+ return;
14886
+ }
14887
+ // Lazy substitution of the entity data overrides.
14888
+ if (this.entityDataOverrides.size) {
14889
+ for (let i = 0; i < entities.length; i++) {
14890
+ const entity = entities[i];
14891
+ const eId = entity.Bruce.ID;
14892
+ if (this.entityDataOverrides.has(eId)) {
14893
+ entities[i] = this.entityDataOverrides.get(eId);
14894
+ }
14895
+ }
14896
+ }
14897
+ // Gather all Tag IDs from found Entities.
14898
+ let tagIds = new Set();
14899
+ for (let i = 0; i < entities.length; i++) {
14900
+ const entity = entities[i];
14901
+ if ((_b = (_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a["Layer.ID"]) === null || _b === void 0 ? void 0 : _b.length) {
14902
+ const eTagIds = entity.Bruce["Layer.ID"];
14903
+ for (let j = 0; j < eTagIds.length; j++) {
14904
+ const tagId = eTagIds[j];
14905
+ if (tagId) {
14906
+ tagIds.add(tagId);
14907
+ }
14908
+ }
14909
+ }
14910
+ }
14911
+ // Gather records.
14912
+ let tags = [];
14913
+ if (tagIds.size) {
14914
+ tags = (await EntityTag.GetListByIds({
14915
+ tagIds: Array.from(tagIds),
14916
+ api: this.api
14917
+ })).tags;
14918
+ }
14919
+ // 0 = rTime is still the current time.
14920
+ // 1 = rTime is in the future.
14921
+ // -1 = rTime is in the past.
14922
+ let changeDir = 0;
14923
+ if (this.lastDateTime !== this.viewer.clock.currentTime.toString()) {
14924
+ changeDir = this.lastDateTime < this.viewer.clock.currentTime.toString() ? 1 : -1;
14925
+ }
14926
+ let toEmit;
14927
+ if (this.historic) {
14928
+ // We can emit Entities if the last resolved time (per Entity) isn't newer than rTime.
14929
+ // We compare using the changeDir to determine if we should emit or not.
14930
+ toEmit = entities.filter((e) => {
14931
+ const lastFoundDateTime = this.lastFoundDateTimes.get(e.id);
14932
+ if (lastFoundDateTime) {
14933
+ if (changeDir === 1) {
14934
+ return lastFoundDateTime <= rTime;
14935
+ }
14936
+ else if (changeDir === -1) {
14937
+ return lastFoundDateTime >= rTime;
14938
+ }
14939
+ }
14940
+ return true;
14941
+ });
14942
+ // Update the last found date times for the filtered Entities.
14943
+ toEmit.forEach((e) => {
14944
+ this.lastFoundDateTimes.set(e.id, rTime);
14945
+ });
14946
+ }
14947
+ else {
14948
+ // If we're not historic, we can just emit everything.
14949
+ toEmit = entities;
14950
+ }
14951
+ this.emitEntities(toEmit, tags);
14952
+ };
14953
+ const QUEUE_BATCH_SIZE = 500;
14954
+ const requestedIds = [];
14955
+ // If we have a queue, we need to request those first.
14956
+ if (this.eIdQueue.length) {
14957
+ const total = this.eIdQueue.length;
14958
+ let done = 0;
14959
+ while (this.eIdQueue.length && !this.disposed) {
14960
+ if (this.historic && this.lastDateTime !== rTime) {
14961
+ break;
14962
+ }
14963
+ const batch = this.eIdQueue.splice(0, QUEUE_BATCH_SIZE);
14964
+ const { entities } = await Entity$1.GetListByIds({
14965
+ entityIds: batch,
14966
+ historicPoint: this.historic ? rTime : null,
14967
+ expandSources: this.expandSources,
14968
+ api: this.api
14969
+ });
14970
+ handleResponse(entities);
14971
+ requestedIds.push(...batch);
14972
+ done += batch.length;
14973
+ if (this._onQueueProgress) {
14974
+ let progress = (done / total) * 100;
14975
+ if (progress > 100) {
14976
+ progress = 100;
14977
+ }
14978
+ if (progress < 0) {
14979
+ progress = 0;
14980
+ }
14981
+ this._onQueueProgress.Trigger(progress);
14982
+ }
14983
+ }
14984
+ if (this._onQueueProgress) {
14985
+ this._onQueueProgress.Trigger(100);
14986
+ }
14987
+ }
14988
+ // Now run through all IDs that we've seen and request them.
14989
+ // We'll skip those that we just requested.
14990
+ let allEIds = Array.from(this.allEIds);
14991
+ if (requestedIds.length) {
14992
+ allEIds = allEIds.filter((eId) => requestedIds.indexOf(eId) === -1);
14993
+ }
14994
+ if (allEIds.length) {
14995
+ while (allEIds.length && !this.disposed && !this.eIdQueue.length) {
14996
+ if (this.historic && this.lastDateTime !== rTime) {
14997
+ break;
14998
+ }
14999
+ const batch = allEIds.splice(0, QUEUE_BATCH_SIZE);
15000
+ const { entities } = await Entity$1.GetListByIds({
15001
+ entityIds: batch,
15002
+ historicPoint: this.historic ? rTime : null,
15003
+ expandSources: this.expandSources,
15004
+ api: this.api
15005
+ });
15006
+ handleResponse(entities);
15007
+ requestedIds.push(...batch);
15008
+ }
15009
+ }
15010
+ // If we had leftovers because we stopped early, we need to re-run the tick.
15011
+ if (this.eIdQueue.length || allEIds.length) {
15012
+ rerun = true;
15013
+ }
15014
+ }
15015
+ catch (e) {
15016
+ console.error(e);
15017
+ }
15018
+ finally {
15019
+ this.runningTick = false;
15020
+ }
15021
+ if (rerun) {
15022
+ this.onTick(true);
15023
+ }
15024
+ })();
14817
15025
  }
14818
- else if (height < 70000) {
14819
- return 0.5;
15026
+ }
15027
+
15028
+ // ND-1641. BOOKMARKS - (DEMO) View does not match bookmark.
15029
+ // We have some evil hard-coded style mappings that need to be fixed.
15030
+ // These exist within legacy project views.
15031
+ function correctStyle(style) {
15032
+ if (style && !style.Settings && !style.ID) {
15033
+ return {
15034
+ ID: -1,
15035
+ Name: "Unknown",
15036
+ Settings: {
15037
+ ...style
15038
+ },
15039
+ Type: Style.EType.Entity
15040
+ };
14820
15041
  }
14821
- else if (height < 100000) {
14822
- return 1;
15042
+ return style;
15043
+ }
15044
+ function colorToCColor$3(color) {
15045
+ return new Color(color.red ? color.red / 255 : 0, color.green ? color.green / 255 : 0, color.blue ? color.blue / 255 : 0, color.alpha);
15046
+ }
15047
+ class TilesetStyler {
15048
+ constructor() {
15049
+ this.disposed = false;
15050
+ // Indicates if the styler has been loaded/initialized and is ready to style.
15051
+ this.loaded = false;
15052
+ // Indicates that all mappings have been loaded.
15053
+ this.styleMappingLoaded = false;
15054
+ // Map of Entity Type ID -> boolean to indicate if the style mapping has been loaded.
15055
+ this.styleMappingsLoaded = new Map();
15056
+ // Dictionary of Entity Type ID -> Set of Entity IDs that are pending styling.
15057
+ // Once the style is loaded, we can queue the set for styling.
15058
+ this.stylePendingEntityIds = new Map();
15059
+ this.fallbackStyle = null;
15060
+ this.loadingCounter = 0;
15061
+ // Entity IDs -> boolean to indicate if we should override the feature colour.
15062
+ this.overrideFeatureColor = new Map();
15063
+ this.scenario = 0;
15064
+ // Indicates that we should expand the sources of the Entity.
15065
+ this.expandSources = false;
15066
+ // Indicates that we are retrieving historic records.
15067
+ // This means that the current scene's time is included in the request.
15068
+ this.historic = false;
15069
+ // More expensive process.
15070
+ // When an Entity is styled, the rego is reviewed and updated if needed.
15071
+ // This is currently used for scenarios, and off by default to keep other processes faster.
15072
+ this.shouldUpdateRegoStates = false;
15073
+ // Entity ID -> styled status.
15074
+ // Helps us emit progress events.
15075
+ this.styledEntityIds = new Map();
15076
+ // % progress for how many Entities have been styled so far.
15077
+ // This can change as Tiles load in and more queue.
15078
+ this._styleProgress = 0;
15079
+ this._styleProgressQueue = new DelayQueue(() => {
15080
+ this.updateStyleProgress();
15081
+ }, 250);
15082
+ // Event for when the style progress changes.
15083
+ this.OnStyleProgress = new BruceEvent();
14823
15084
  }
14824
- else if (height < 150000) {
14825
- return 1;
15085
+ get Disposed() {
15086
+ return this.disposed;
14826
15087
  }
14827
- else if (height < 200000) {
14828
- return 1.5;
15088
+ get Loaded() {
15089
+ return this.loaded;
14829
15090
  }
14830
- else if (height < 300000) {
14831
- return 1.5;
15091
+ get StyleProgress() {
15092
+ return this._styleProgress;
14832
15093
  }
14833
- else if (height < 500000) {
14834
- return 3;
15094
+ Init(params) {
15095
+ var _a;
15096
+ let { viewer, api, cTileset, fallbackStyleId, styleMapping, expandSources, menuItemId, register, scenario, historic } = params;
15097
+ this.viewer = viewer;
15098
+ this.api = api;
15099
+ this.cTileset = cTileset;
15100
+ this.register = register;
15101
+ this.menuItemId = menuItemId;
15102
+ this.historic = Boolean(historic);
15103
+ if (expandSources != null) {
15104
+ this.expandSources = expandSources;
15105
+ }
15106
+ if (fallbackStyleId != null) {
15107
+ this.fallbackStyleId = fallbackStyleId;
15108
+ }
15109
+ if (styleMapping) {
15110
+ this.styleMapping = styleMapping;
15111
+ if (this.styleMapping) {
15112
+ // Dereference.
15113
+ this.styleMapping = JSON.parse(JSON.stringify(this.styleMapping));
15114
+ }
15115
+ // ND-1641. BOOKMARKS - (DEMO) View does not match bookmark.
15116
+ // We have some evil hard-coded style mappings that need to be fixed.
15117
+ // These exist within legacy project views.
15118
+ // Update: This now also lets people have a style mapping without a real style record.
15119
+ if ((_a = this.styleMapping) === null || _a === void 0 ? void 0 : _a.length) {
15120
+ for (let i = 0; i < this.styleMapping.length; i++) {
15121
+ const mapItem = this.styleMapping[i];
15122
+ const mapStyle = mapItem.style ? mapItem.style : mapItem.Style;
15123
+ this.styleMapping[i].style = correctStyle(mapStyle);
15124
+ }
15125
+ }
15126
+ }
15127
+ if (scenario != null) {
15128
+ this.scenario = scenario;
15129
+ }
15130
+ if (!!scenario) {
15131
+ this.shouldUpdateRegoStates = true;
15132
+ }
15133
+ else if (this.historic) {
15134
+ this.shouldUpdateRegoStates = true;
15135
+ }
15136
+ this.entityGatherer = new EntityGatherer(this.api, this.viewer, (entities, tags) => {
15137
+ if (!(entities === null || entities === void 0 ? void 0 : entities.length)) {
15138
+ return;
15139
+ }
15140
+ for (let i = 0; i < entities.length; i++) {
15141
+ const entity = entities[i];
15142
+ const feature = this.getEntityRego(entity.Bruce.ID);
15143
+ if (feature) {
15144
+ const eTags = tags.filter(t => { var _a, _b, _c, _d; return ((_b = (_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a["Layer.ID"]) === null || _b === void 0 ? void 0 : _b.length) && ((_d = (_c = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _c === void 0 ? void 0 : _c["Layer.ID"]) === null || _d === void 0 ? void 0 : _d.includes(t.ID)); });
15145
+ this.styleTilesetFeatureFullData(feature, entity, eTags);
15146
+ }
15147
+ }
15148
+ });
15149
+ this.loaded = true;
15150
+ this.loadStyles();
14835
15151
  }
14836
- else if (height < 1000000) {
14837
- return 3;
14838
- }
14839
- else if (height < 1200000) {
14840
- return 4;
14841
- }
14842
- else if (height < 2000000) {
14843
- return 6;
14844
- }
14845
- else if (height < 3000000) {
14846
- return 20;
14847
- }
14848
- return 35;
14849
- }
14850
- function isCellFetched(cell) {
14851
- if (cell.Fetched) {
14852
- return true;
14853
- }
14854
- return false;
14855
- }
14856
- function getOrCreateCell(cells, cellSize, lon, maxLon, lat, maxLat) {
14857
- const id = cellSize + "_" + lon + "_" + maxLon + "_" + lat + "_" + maxLat;
14858
- let cell = cells[id];
14859
- if (!cell) {
14860
- cell = cells[id] = new EntityGlobe.Cell();
14861
- cell.Boundaries = {
14862
- minLatitude: lat,
14863
- maxLatitude: maxLat,
14864
- minLongitude: lon,
14865
- maxLongitude: maxLon
14866
- };
14867
- cell.IsFetched = () => isCellFetched(cell);
14868
- }
14869
- return [id, cell];
14870
- }
14871
-
14872
- const MAX_AREA_IN_DEGREES$1 = 90;
14873
- const MAX_RETRY_ATTEMPTS = 1;
14874
- const RETRY_DELAY_INCREMENT = 500;
14875
- const REQUEST_PAGE_DELAY = 50;
14876
- class regMenuItemGetter {
14877
- constructor(typeId, tagIds, minHeight, maxHeight) {
14878
- this.TypeId = typeId;
14879
- this.TagIds = tagIds;
14880
- this.MinHeight = minHeight;
14881
- this.MaxHeight = maxHeight;
14882
- }
14883
- }
14884
- async function delay(milliseconds) {
14885
- return new Promise((res) => {
14886
- setTimeout(() => {
14887
- res();
14888
- }, milliseconds);
14889
- });
14890
- }
14891
- /**
14892
- * This is a batched entity getter.
14893
- * It will scan for entity records in a view-area and emit them in batches.
14894
- * It will restart scanning if the camera moves.
14895
- */
14896
- var EntityFilterGetter;
14897
- (function (EntityFilterGetter) {
14898
- let EStatus;
14899
- (function (EStatus) {
14900
- EStatus["Scanning"] = "SCANNING";
14901
- EStatus["Loading"] = "LOADING";
14902
- })(EStatus = EntityFilterGetter.EStatus || (EntityFilterGetter.EStatus = {}));
14903
- class Getter {
14904
- get OnUpdate() {
14905
- if (!this.onUpdate) {
14906
- this.onUpdate = new BruceEvent();
15152
+ /**
15153
+ * Updates style mapping and fallback style.
15154
+ * This will trigger a re-style of all entities, and will stop existing style loads.
15155
+ * @param params
15156
+ */
15157
+ UpdateStyleMapping(params) {
15158
+ var _a;
15159
+ if (params.styleMapping) {
15160
+ // Dereference.
15161
+ params.styleMapping = JSON.parse(JSON.stringify(params.styleMapping));
15162
+ this.styleMapping = params.styleMapping;
15163
+ // ND-1641. BOOKMARKS - (DEMO) View does not match bookmark.
15164
+ // We have some evil hard-coded style mappings that need to be fixed.
15165
+ // These exist within legacy project views.
15166
+ // Update: This now also lets people have a style mapping without a real style record.
15167
+ if ((_a = this.styleMapping) === null || _a === void 0 ? void 0 : _a.length) {
15168
+ for (let i = 0; i < this.styleMapping.length; i++) {
15169
+ const mapItem = this.styleMapping[i];
15170
+ const mapStyle = mapItem.style ? mapItem.style : mapItem.Style;
15171
+ this.styleMapping[i].style = correctStyle(mapStyle);
15172
+ }
14907
15173
  }
14908
- return this.onUpdate;
14909
15174
  }
14910
- get OnStateUpdate() {
14911
- if (!this.onStateUpdate) {
14912
- this.onStateUpdate = new BruceEvent();
15175
+ if (!isNaN(params.fallbackStyleId) && params.fallbackStyleId != null) {
15176
+ this.fallbackStyleId = params.fallbackStyleId;
15177
+ }
15178
+ if (params.fallbackStyle) {
15179
+ this.fallbackStyle = params.fallbackStyle;
15180
+ }
15181
+ else {
15182
+ this.fallbackStyle = null;
15183
+ }
15184
+ if (params.scenario != null) {
15185
+ this.scenario = params.scenario;
15186
+ if (!this.shouldUpdateRegoStates && !!this.scenario) {
15187
+ this.shouldUpdateRegoStates = true;
14913
15188
  }
14914
- return this.onStateUpdate;
14915
15189
  }
14916
- get OnScanUpdate() {
14917
- if (!this.onScanUpdate) {
14918
- this.onScanUpdate = new BruceEvent();
15190
+ if (params.historic != null) {
15191
+ this.historic = params.historic;
15192
+ if (params.historic || this.historic != params.historic) {
15193
+ this.shouldUpdateRegoStates = true;
14919
15194
  }
14920
- return this.onScanUpdate;
14921
15195
  }
14922
- get isLooping() {
14923
- return this.looping > 0;
15196
+ // Reload styles.
15197
+ if (this.loaded) {
15198
+ this.styledEntityIds.clear();
15199
+ this.styleMappingsLoaded.clear();
15200
+ this.loadStyles();
15201
+ this._styleProgressQueue.Call(true);
15202
+ // Putting all rendered things back into the queue.
15203
+ const regos = this.register.GetRegos({
15204
+ menuItemId: this.menuItemId
15205
+ });
15206
+ this.QueueEntities(regos);
14924
15207
  }
14925
- constructor(params) {
14926
- this.onUpdate = null;
14927
- this.LastStateUpdates = {};
14928
- this.onStateUpdate = null;
14929
- this.onScanUpdate = null;
14930
- this.viewPortChangeRemoval = null;
14931
- this.viewPortDelayQueue = null;
14932
- this.viewerDateTimeChangeRemoval = null;
14933
- this.cells = null;
14934
- this.registeredItems = {};
14935
- this.getterLoopId = 0;
14936
- this.getterLoopAbortControllers = {};
14937
- this.looping = 0;
14938
- this.tagIds = null;
14939
- this.minHeight = 0;
14940
- this.maxHeight = 100000;
14941
- this.viewRect = null;
14942
- this.viewCenter = null;
14943
- this.scenario = 0;
14944
- this.historicRefreshAbortController = null;
14945
- // Entity IDs found for the latest integrity.
14946
- // We use this for refreshing historic data without having to repeat geographic queries.
14947
- this.gatheredIntegrity = null;
14948
- this.gatheredEntityIds = [];
14949
- const { api, viewer, viewPort, typeIds, schemaId, batchSize, attrFilter, historicAttrKey, historicInterpolation, historic, viaCdn, scenario } = params;
14950
- this.api = api;
14951
- this.typeIds = typeIds;
14952
- this.schemaId = schemaId;
14953
- this.historic = Boolean(historic);
14954
- this.historicAttrKey = historicAttrKey;
14955
- this.historicInterpolation = Boolean(historicInterpolation);
14956
- this.viaCdn = Boolean(viaCdn);
14957
- this.batchSize = isNaN(batchSize) ? 300 : batchSize;
14958
- this.viewPort = viewPort;
14959
- this.attrFilter = attrFilter;
14960
- this.viewer = viewer;
14961
- this.scenario = scenario ? scenario : 0;
14962
- this.updateBounds();
15208
+ }
15209
+ /**
15210
+ * Updates a cache of Entity objects to use when styling.
15211
+ * When a cache is available, a request won't be performed to get that Entity's data.
15212
+ * @param entityIds
15213
+ * @param entities If an object is not available for a supplied Entity ID, then the cache for it is removed.
15214
+ */
15215
+ SetEntityCache(entityIds, entities) {
15216
+ // Turn the Entities array into an accessible dictionary.
15217
+ const newDataMap = new Map();
15218
+ if (entities) {
15219
+ for (let i = 0; i < entities.length; i++) {
15220
+ const entity = entities[i];
15221
+ if (entity.Bruce) {
15222
+ newDataMap.set(entity.Bruce.ID, entity);
15223
+ }
15224
+ }
14963
15225
  }
14964
- /**
14965
- * Returns id that represents the combined menu item parameters.
14966
- * If integrity changes while a request is running, the request will not emit a response.
14967
- * @returns
14968
- */
14969
- getIntegrityId() {
14970
- let integrity = this.tagIds == null ? "" : this.tagIds.join();
14971
- if (this.historicAttrKey) {
14972
- integrity += "isHistoric";
14973
- integrity += this.historicAttrKey;
15226
+ // Update the cache for each supplied Entity ID.
15227
+ for (let i = 0; i < entityIds.length; i++) {
15228
+ const entityId = entityIds[i];
15229
+ this.entityGatherer.SetEntityDataOverride(entityId, newDataMap.get(entityId));
15230
+ }
15231
+ }
15232
+ QueueEntities(regos, highPriority = false) {
15233
+ if (!this.loaded) {
15234
+ // Mark as pending?
15235
+ return;
15236
+ }
15237
+ for (let i = 0; i < regos.length; i++) {
15238
+ const rego = regos[i];
15239
+ this.overrideFeatureColor.set(rego.entityId, true);
15240
+ // We set a default colour right away to avoid race conditions at later times.
15241
+ CesiumEntityStyler.BakeDefaultColor({
15242
+ entity: rego.visual,
15243
+ viewer: this.viewer,
15244
+ override: false
15245
+ });
15246
+ }
15247
+ this.queueTilesetFeatureStyle(regos, highPriority);
15248
+ }
15249
+ /**
15250
+ * Calculates the current progress % and updates the progress if there is a change.
15251
+ */
15252
+ updateStyleProgress() {
15253
+ if (this.disposed) {
15254
+ return;
15255
+ }
15256
+ const total = this.overrideFeatureColor.size;
15257
+ const styled = this.styledEntityIds.size;
15258
+ let progress = 100;
15259
+ if (styled < total) {
15260
+ progress = Math.round((styled / total) * 100);
15261
+ if (progress < 0) {
15262
+ progress = 0;
14974
15263
  }
14975
- else if (this.historic) {
14976
- integrity += "isHistoric";
15264
+ else if (progress > 100) {
15265
+ progress = 100;
14977
15266
  }
14978
- if (this.scenario) {
14979
- integrity += this.scenario;
15267
+ }
15268
+ if (this._styleProgress != progress) {
15269
+ this._styleProgress = progress;
15270
+ this.OnStyleProgress.Trigger(progress);
15271
+ }
15272
+ }
15273
+ getEntityRego(entityId) {
15274
+ return this.register.GetRego({
15275
+ entityId,
15276
+ menuItemId: this.menuItemId
15277
+ });
15278
+ }
15279
+ Dispose() {
15280
+ if (this.disposed) {
15281
+ return;
15282
+ }
15283
+ this.disposed = true;
15284
+ this._styleProgressQueue.Dispose();
15285
+ this.disposeGatherer();
15286
+ }
15287
+ async loadStyles() {
15288
+ var _a, _b;
15289
+ const counter = ++this.loadingCounter;
15290
+ this.styleMappingLoaded = false;
15291
+ this.styleMappingsLoaded.clear();
15292
+ // Apply state changes.
15293
+ this.entityGatherer.Empty();
15294
+ this.entityGatherer.Init(this.historic, this.expandSources);
15295
+ let fallbackStyleId = this.fallbackStyleId;
15296
+ if (fallbackStyleId && fallbackStyleId > 0) {
15297
+ try {
15298
+ let { style: data } = await Style.Get({
15299
+ api: this.api,
15300
+ styleId: fallbackStyleId
15301
+ });
15302
+ this.fallbackStyle = data;
14980
15303
  }
14981
- if (this.typeIds) {
14982
- integrity += this.typeIds.join();
15304
+ catch (e) {
15305
+ console.error(e);
14983
15306
  }
14984
- return integrity;
14985
15307
  }
14986
- viewAreaSub() {
14987
- this.viewAreaDispose();
14988
- // We'll avoid restarting the scanner too often.
14989
- this.viewPortDelayQueue = new DelayQueue(() => {
14990
- this.updateBounds();
14991
- this.startGetterLoop();
14992
- }, 2000);
14993
- this.viewPortChangeRemoval = this.viewPort.Updated().Subscribe(() => {
14994
- var _a;
14995
- (_a = this.viewPortDelayQueue) === null || _a === void 0 ? void 0 : _a.Call();
14996
- });
15308
+ if (this.loadingCounter != counter) {
15309
+ return;
14997
15310
  }
14998
- viewAreaDispose() {
14999
- var _a, _b;
15000
- (_a = this.viewPortChangeRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
15001
- this.viewPortChangeRemoval = null;
15002
- (_b = this.viewPortDelayQueue) === null || _b === void 0 ? void 0 : _b.Dispose();
15003
- this.viewPortDelayQueue = null;
15311
+ // Load styles in the style mapping
15312
+ let styleMapping = this.styleMapping;
15313
+ if (!styleMapping) {
15314
+ styleMapping = [];
15004
15315
  }
15005
- /**
15006
- * Monitors the Cesium viewer and updates the historic data filter values.
15007
- * If there is no historic attr set, this will do nothing.
15008
- */
15009
- viewerDateTimeSub() {
15010
- if ((!this.historicAttrKey && !this.historic) || this.viewerDateTimeChangeRemoval) {
15011
- return;
15316
+ this.styleMapping = styleMapping;
15317
+ // Append all found entity-types.
15318
+ // That way we can mark them as loaded instead of just the ones in the style mapping.
15319
+ try {
15320
+ const modelTree = (_b = (_a = this.cTileset) === null || _a === void 0 ? void 0 : _a.extensions) === null || _b === void 0 ? void 0 : _b.modelTree;
15321
+ if (modelTree) {
15322
+ const entityTypeIds = this.getEntityTypeIdsFromModelTree(modelTree);
15323
+ for (let i = 0; i < entityTypeIds.length; i++) {
15324
+ const entityTypeId = entityTypeIds[i];
15325
+ if (styleMapping.findIndex(x => x.EntityTypeID == entityTypeId) <= -1) {
15326
+ styleMapping.push({
15327
+ EntityTypeID: entityTypeId,
15328
+ StyleID: this.fallbackStyle ? fallbackStyleId : 0,
15329
+ style: this.fallbackStyle
15330
+ });
15331
+ }
15332
+ }
15012
15333
  }
15013
- // This is multiplied by the speed of animation to figure
15014
- // out how many animation "ticks" before we allow an update.
15015
- let INTERVAL_WHILE_ANIMATING = 2.5 * 1000;
15016
- let INTERVAL_WHILE_NOT_ANIMATING = 1000;
15017
- if (this.historicInterpolation) {
15018
- INTERVAL_WHILE_ANIMATING = 6 * 1000;
15019
- INTERVAL_WHILE_NOT_ANIMATING = 3.5 * 1000;
15334
+ }
15335
+ catch (e) {
15336
+ console.error(e);
15337
+ }
15338
+ // Before we start the loop we'll do a single request for the needed Entity Types.
15339
+ // This will be aimed at rows that don't specify a Style and if the default fallback-
15340
+ //is set to 0 (using the default of the Entity Type).
15341
+ const typeMap = new Map();
15342
+ const typeIds = styleMapping.map(x => x.StyleID == -1 || Boolean(x.style) ? null : x.EntityTypeID).filter(x => !!x);
15343
+ if (typeIds.length) {
15344
+ // We'll split up into batches of 50.
15345
+ // Since we add the type IDs as query params I am paranoid of hitting the limit.
15346
+ const splits = Math.ceil(typeIds.length / 50);
15347
+ const batchSize = 50;
15348
+ for (let i = 0; i < splits; i++) {
15349
+ const batch = typeIds.slice(i * batchSize, (i + 1) * batchSize);
15350
+ const { entityTypes } = await EntityType.GetList({
15351
+ api: this.api,
15352
+ entityTypeIds: batch
15353
+ });
15354
+ for (let i = 0; i < entityTypes.length; i++) {
15355
+ const entityType = entityTypes[i];
15356
+ typeMap.set(entityType.ID, entityType);
15357
+ }
15020
15358
  }
15021
- let lastUpdateTime = null;
15022
- let delayQueue = new DelayQueue(() => {
15023
- try {
15024
- // If the timeline is animating then we'll wait longer to update.
15025
- if (this.viewer.clock.shouldAnimate && lastUpdateTime) {
15026
- if (Math.abs(new Date().getTime() - lastUpdateTime) < INTERVAL_WHILE_ANIMATING) {
15027
- return;
15359
+ }
15360
+ for (let i = 0; i < styleMapping.length; i++) {
15361
+ if (this.disposed) {
15362
+ break;
15363
+ }
15364
+ let styleMap = styleMapping[i];
15365
+ if (!styleMap.style && styleMap.StyleID != -1) {
15366
+ let styleId = styleMap.StyleID;
15367
+ // Get default style of the entity type, if
15368
+ //no default is already specified.
15369
+ if (!styleId && !this.fallbackStyle) {
15370
+ try {
15371
+ let entityType = typeMap.get(styleMap.EntityTypeID);
15372
+ // If the map excluded the type for whatever reason we'll load it now.
15373
+ if (!entityType) {
15374
+ entityType = (await EntityType.Get({
15375
+ api: this.api,
15376
+ entityTypeId: styleMap.EntityTypeID
15377
+ })).entityType;
15028
15378
  }
15379
+ styleId = entityType === null || entityType === void 0 ? void 0 : entityType["DisplaySetting.ID"];
15029
15380
  }
15030
- const current = this.historicAttrDateTime;
15031
- this.updateHistoricDateTime();
15032
- if (current != this.historicAttrDateTime) {
15033
- this.emitHistoricData();
15381
+ catch (e) {
15382
+ console.error(e);
15034
15383
  }
15035
15384
  }
15036
- catch (e) {
15037
- console.error(e);
15385
+ if (styleId) {
15386
+ try {
15387
+ let { style: data } = await Style.Get({
15388
+ api: this.api,
15389
+ styleId
15390
+ });
15391
+ if (data) {
15392
+ styleMap.style = data;
15393
+ styleMap.StyleID = styleId;
15394
+ }
15395
+ }
15396
+ catch (e) {
15397
+ console.error(e);
15398
+ }
15038
15399
  }
15039
- }, INTERVAL_WHILE_NOT_ANIMATING);
15040
- let postUpdateRemoval = this.viewer.scene.postUpdate.addEventListener(() => {
15041
- if (delayQueue) {
15042
- delayQueue.Call();
15400
+ if (this.loadingCounter != counter) {
15401
+ return;
15043
15402
  }
15044
- });
15045
- this.viewerDateTimeChangeRemoval = () => {
15046
- delayQueue === null || delayQueue === void 0 ? void 0 : delayQueue.Dispose();
15047
- postUpdateRemoval === null || postUpdateRemoval === void 0 ? void 0 : postUpdateRemoval();
15048
- delayQueue = null;
15049
- postUpdateRemoval = null;
15050
- };
15051
- }
15052
- updateHistoricDateTime() {
15053
- if (!this.historicAttrKey && !this.historic) {
15054
- this.historicAttrDateTime = null;
15055
- return;
15056
15403
  }
15057
- const isChanged = (before, after) => {
15058
- if (before && !after) {
15059
- return true;
15060
- }
15061
- if (!before && after) {
15062
- return true;
15404
+ this.styleMappingsLoaded.set(styleMap.EntityTypeID, true);
15405
+ if (this.loaded) {
15406
+ // If we have a bunch of Entities that were waiting for the style to load,
15407
+ // then we can pass them to the gatherer now.
15408
+ const relatedIds = this.stylePendingEntityIds.get(styleMap.EntityTypeID);
15409
+ if (relatedIds) {
15410
+ this.entityGatherer.Queue(Array.from(relatedIds));
15411
+ this.stylePendingEntityIds.delete(styleMap.EntityTypeID);
15063
15412
  }
15064
- // Change must be at least 0.1 seconds.
15065
- return Math.abs(before.getTime() - after.getTime()) > 100;
15066
- };
15067
- const oldDateTime = this.historicAttrDateTime ? new Date(this.historicAttrDateTime) : null;
15068
- const newDateTime = JulianDate.toDate(this.viewer.clock.currentTime);
15069
- if (isChanged(oldDateTime, newDateTime)) {
15070
- this.historicAttrDateTime = newDateTime.toISOString();
15071
15413
  }
15072
15414
  }
15073
- viewerDateTimeDispose() {
15074
- var _a;
15075
- (_a = this.viewerDateTimeChangeRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
15076
- this.viewerDateTimeChangeRemoval = null;
15077
- }
15078
- GetMenuItems() {
15079
- return Object.keys(this.registeredItems);
15415
+ if (this.loadingCounter != counter) {
15416
+ return;
15080
15417
  }
15081
- IncludeMenuItem(menuItemId, typeId, layerIds, minHeight, maxHeight) {
15082
- this.registeredItems[menuItemId] = new regMenuItemGetter(typeId, layerIds, minHeight, maxHeight);
15083
- this.updateState();
15418
+ this.styleMappingLoaded = true;
15419
+ if (!this.disposed && this.loaded) {
15420
+ if (this.loadingCounter != counter) {
15421
+ return;
15422
+ }
15423
+ // Queue all Entities that are pending styling.
15424
+ const pendingKeys = this.stylePendingEntityIds.keys();
15425
+ for (const key of pendingKeys) {
15426
+ const relatedIds = this.stylePendingEntityIds[key];
15427
+ if (relatedIds) {
15428
+ this.entityGatherer.Queue(Array.from(relatedIds));
15429
+ }
15430
+ }
15431
+ this.stylePendingEntityIds.clear();
15084
15432
  }
15085
- ExcludeMenuItem(menuItemId) {
15086
- this.registeredItems[menuItemId] = null;
15087
- delete this.registeredItems[menuItemId];
15088
- this.updateState(true);
15433
+ }
15434
+ disposeGatherer() {
15435
+ if (this.entityGatherer) {
15436
+ this.entityGatherer.Dispose();
15437
+ this.entityGatherer = null;
15089
15438
  }
15090
- updateBounds() {
15091
- const viewRect = this.viewPort.GetBounds();
15092
- const poi = this.viewPort.GetTarget();
15093
- if (viewRect && poi) {
15094
- if (Math.abs(viewRect.west - viewRect.east) > MAX_AREA_IN_DEGREES$1) {
15095
- return;
15096
- }
15097
- if (Math.abs(viewRect.south - viewRect.north) > MAX_AREA_IN_DEGREES$1) {
15098
- return;
15439
+ }
15440
+ getEntityTypeIdsFromModelTree(modelTree) {
15441
+ const entityTypeIds = [];
15442
+ this.digEntityTypeIdsFromModelTreeBranch(modelTree, entityTypeIds);
15443
+ return entityTypeIds;
15444
+ }
15445
+ digEntityTypeIdsFromModelTreeBranch(branch, arr) {
15446
+ if (branch) {
15447
+ // Does not yet include this entity type id.
15448
+ if (branch.typeId && !arr.includes(branch.typeId)) {
15449
+ arr.push(branch.typeId);
15450
+ }
15451
+ if (branch.children) {
15452
+ for (let i = 0; i < branch.children.length; i++) {
15453
+ let child = branch.children[i];
15454
+ this.digEntityTypeIdsFromModelTreeBranch(child, arr);
15099
15455
  }
15100
- this.viewRect = viewRect;
15101
- this.viewCenter = poi;
15102
15456
  }
15103
15457
  }
15104
- updateState(onlyIfLooping = false) {
15105
- const tagIds = [];
15106
- const typeIds = [];
15107
- const menuItemIds = this.GetMenuItems();
15108
- let minHeight = null;
15109
- let maxHeight = null;
15110
- for (let i = 0; i < menuItemIds.length; i++) {
15111
- const menuItem = this.registeredItems[menuItemIds[i]];
15112
- if (menuItem) {
15113
- if (maxHeight == null || maxHeight < menuItem.MaxHeight) {
15114
- maxHeight = menuItem.MaxHeight;
15115
- }
15116
- if (minHeight == null || minHeight > menuItem.MinHeight) {
15117
- minHeight = menuItem.MinHeight;
15118
- }
15119
- const itemLayerIds = menuItem.TagIds;
15120
- if (itemLayerIds) {
15121
- for (let j = 0; j < itemLayerIds.length; j++) {
15122
- const itemLayerId = itemLayerIds[j];
15123
- if (!tagIds.includes(itemLayerId)) {
15124
- tagIds.push(itemLayerId);
15125
- }
15126
- }
15127
- }
15128
- const itemTypeId = menuItem.TypeId;
15129
- if (itemTypeId) {
15130
- if (!typeIds.includes(itemTypeId)) {
15131
- typeIds.push(itemTypeId);
15132
- }
15133
- }
15134
- }
15458
+ }
15459
+ queueTilesetFeatureStyle(entities, highPriority) {
15460
+ const needsDataIds = [];
15461
+ for (const rego of entities) {
15462
+ if (!this.overrideFeatureColor.has(rego.entityId)) {
15463
+ this.overrideFeatureColor.set(rego.entityId, false);
15135
15464
  }
15136
- if (menuItemIds.length > 0 && (!onlyIfLooping || this.isLooping)) {
15137
- // Reset cells so none are marked as fetched.
15138
- this.cells = new EntityGlobe.Grid();
15139
- this.tagIds = tagIds;
15140
- this.typeIds = typeIds;
15141
- this.minHeight = minHeight;
15142
- this.maxHeight = maxHeight;
15143
- this.updateBounds();
15144
- this.updateHistoricDateTime();
15145
- this.startGetterLoop();
15146
- this.viewAreaSub();
15147
- this.viewerDateTimeSub();
15465
+ const typeId = rego.entityTypeId || "";
15466
+ if (this.styleMappingLoaded || this.styleMappingsLoaded.get(typeId)) {
15467
+ if (this.getTilesetFeatureNeedsFullData(typeId)) {
15468
+ needsDataIds.push(rego.entityId);
15469
+ }
15470
+ else {
15471
+ this.styleTilesetFeature(rego);
15472
+ }
15148
15473
  }
15149
15474
  else {
15150
- this.getterLoopId += 1;
15151
- this.viewAreaDispose();
15152
- this.viewerDateTimeDispose();
15475
+ if (!this.stylePendingEntityIds.has(typeId)) {
15476
+ this.stylePendingEntityIds.set(typeId, new Set());
15477
+ }
15478
+ this.stylePendingEntityIds.get(typeId).add(rego.entityId);
15153
15479
  }
15154
15480
  }
15155
- postStatus(status) {
15156
- var _a;
15157
- this.LastStateUpdates[status.msg] = status;
15158
- (_a = this.onStateUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(status);
15481
+ if (needsDataIds.length) {
15482
+ this.entityGatherer.Queue(needsDataIds, highPriority);
15159
15483
  }
15160
- startGetterLoop() {
15161
- // Increase id so that existing loops stop.
15162
- this.getterLoopId += 1;
15163
- const loopId = this.getterLoopId;
15164
- const loopIntegrity = this.getIntegrityId();
15165
- // Abort any existing loops that don't match the current loop.
15166
- // We tried using integrity, however we want to interrupt when user moves camera quickly.
15167
- // So it's better to use the loop ID.
15168
- const abortId = String(loopId);
15169
- {
15170
- const newAbortControllers = {};
15171
- for (const key in this.getterLoopAbortControllers) {
15172
- this.getterLoopAbortControllers[key].abort();
15173
- }
15174
- this.getterLoopAbortControllers = newAbortControllers;
15484
+ }
15485
+ styleTilesetFeature(entity) {
15486
+ this.styleTilesetFeatureFullData(entity, null, []);
15487
+ }
15488
+ styleTilesetFeatureFullData(entity, data, tags) {
15489
+ var _a, _b, _c, _d, _e, _f, _g, _h;
15490
+ const visual = entity.visual;
15491
+ if (!visual || !(visual instanceof Cesium3DTileFeature)) {
15492
+ return;
15493
+ }
15494
+ const style = this.getTilesetFeatureStyle(entity.entityTypeId);
15495
+ const bColor = style && ((_a = style.modelStyle) === null || _a === void 0 ? void 0 : _a.fillColor) ? Calculator.GetColor(style.modelStyle.fillColor, data, tags) : null;
15496
+ let cColor = null;
15497
+ if (bColor == null) {
15498
+ cColor = Color.WHITE;
15499
+ }
15500
+ else {
15501
+ cColor = colorToCColor$3(bColor);
15502
+ }
15503
+ const override = this.overrideFeatureColor.get(entity.entityId) == true;
15504
+ CesiumEntityStyler.SetDefaultColor({
15505
+ color: cColor,
15506
+ entity: visual,
15507
+ viewer: this.viewer,
15508
+ override: override
15509
+ });
15510
+ this.overrideFeatureColor.set(entity.entityId, true);
15511
+ this.styledEntityIds.set(entity.entityId, true);
15512
+ this._styleProgressQueue.Call();
15513
+ // Since we only need to update it for scenarios right now.
15514
+ // We'll avoid doing it if not needed, eg: first render and no scenario (same state as default).
15515
+ if (this.shouldUpdateRegoStates && (override || ((_b = data === null || data === void 0 ? void 0 : data.Bruce) === null || _b === void 0 ? void 0 : _b.Scenario) || this.historic)) {
15516
+ // Update the Entity's rego state.
15517
+ let changed = false;
15518
+ // Changed scenario.
15519
+ if (entity.scenario != ((_c = data === null || data === void 0 ? void 0 : data.Bruce) === null || _c === void 0 ? void 0 : _c.Scenario)) {
15520
+ entity.scenario = (_d = data === null || data === void 0 ? void 0 : data.Bruce) === null || _d === void 0 ? void 0 : _d.Scenario;
15521
+ changed = true;
15175
15522
  }
15176
- const abortController = this.getterLoopAbortControllers[abortId] = new AbortController();
15177
- this.looping += 1;
15178
- (async () => {
15179
- var _a, _b, _c, _d, _e, _f, _g, _h;
15180
- // Larger initial delay for the first loops because terrain is likely loading in.
15181
- // We also delay because if we enable 50 Menu Items at the same time, common requests we be made if we wait a bit.
15182
- // Eg: same entity type will be grouped into the same filter getter instance.
15183
- await delay(loopId <= 3 ? 800 : 300);
15184
- const MIN_HEIGHT = this.minHeight;
15185
- const MAX_HEIGHT = this.maxHeight;
15186
- const PAGE_SIZE = this.batchSize;
15187
- let retryAttempts = MAX_RETRY_ATTEMPTS;
15188
- let retryDelay = 0;
15189
- let prevFirstId = "";
15190
- let prevLastId = "";
15191
- let prevTicks = 0;
15192
- while ((!this.viewCenter || !this.viewRect) && this.getterLoopId == loopId) {
15193
- await delay(RETRY_DELAY_INCREMENT);
15194
- }
15195
- if (this.getterLoopId != loopId) {
15196
- return;
15197
- }
15198
- const alt = this.viewRect.alt;
15199
- if (alt > MAX_HEIGHT || (alt < MIN_HEIGHT && MIN_HEIGHT > 0)) {
15200
- return;
15201
- }
15202
- const cells = this.cells.GetCellsForView(this.viewCenter, this.viewRect);
15203
- (_a = this.onScanUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(cells);
15204
- let curCellIndex = cells.length > 0 ? 0 : null;
15205
- let postedScanning = false;
15206
- let postedLoading = false;
15207
- let total = 0;
15208
- while (retryAttempts > 0 && curCellIndex != null) {
15209
- if (retryDelay > 0) {
15210
- await delay(retryDelay);
15211
- }
15212
- if (this.getterLoopId != loopId) {
15213
- break;
15214
- }
15215
- if (!postedScanning) {
15216
- this.postStatus({ msg: EStatus.Scanning, revoking: false });
15217
- postedScanning = true;
15218
- }
15219
- const curCell = cells[curCellIndex];
15220
- if (curCell.IsFetched()) {
15221
- curCell.Fetching = false;
15222
- curCellIndex += 1;
15223
- if (cells[curCellIndex]) {
15224
- cells[curCellIndex].Fetching = true;
15225
- }
15226
- else {
15227
- curCellIndex = null;
15228
- }
15229
- (_b = this.onScanUpdate) === null || _b === void 0 ? void 0 : _b.Trigger(cells);
15230
- continue;
15231
- }
15232
- try {
15233
- let response = {
15234
- entities: [],
15235
- nextPage: false,
15236
- nextPageUrl: null
15237
- };
15238
- await SharedGetters.Queue.Run("Loading Entities from Menu Item that loads Entity Type: " + this.typeIds, async () => {
15239
- var _a;
15240
- if (abortController.signal.aborted || !((_a = this.GetMenuItems()) === null || _a === void 0 ? void 0 : _a.length)) {
15241
- return;
15242
- }
15243
- // API gave us a URL to use.
15244
- if (curCell.FetchURL) {
15245
- const tmpResponse = await this.api.get(curCell.FetchURL, {
15246
- abortSignal: abortController.signal,
15247
- noCache: true
15248
- });
15249
- // Same mapping as bruce-models doing Entity.GetList.
15250
- response = {
15251
- entities: tmpResponse.Items ? tmpResponse.Items : [],
15252
- nextPage: tmpResponse.NextPage,
15253
- nextPageUrl: tmpResponse.NextPageURL
15254
- };
15255
- }
15256
- else {
15257
- response = await Entity$1.GetList({
15258
- api: this.api,
15259
- scenario: this.scenario,
15260
- historicKey: this.historicAttrKey,
15261
- historicPoint: (this.historicAttrKey || this.historic) ? this.historicAttrDateTime : null,
15262
- schemaId: this.schemaId,
15263
- filter: {
15264
- pageSize: PAGE_SIZE,
15265
- pageIndex: curCell.FetchPageIndex,
15266
- entityTypeId: this.typeIds,
15267
- layerIds: this.tagIds,
15268
- // Any tag specified will be allowed.
15269
- layerIdsOperator: "in",
15270
- bounds: curCell.GetBounds(),
15271
- sortOrder: Api.ESortOrder.Asc,
15272
- entityTypeConditions: this.attrFilter
15273
- },
15274
- viaCdn: this.viaCdn,
15275
- migrated: true,
15276
- // If we're taking 2+ minutes to make a query, it's a dud.
15277
- // This is a timeout imposed on our DB and not external sources.
15278
- // Honestly could lower down to 30 seconds, but we'll keep it high for now.
15279
- maxSearchTimeSec: 60 * 2,
15280
- req: {
15281
- // If we are passing in an abort, we MUST pass in noCache.
15282
- // Otherwise we will cache an aborted request.
15283
- noCache: true,
15284
- abortSignal: abortController.signal
15285
- }
15286
- });
15287
- }
15288
- });
15289
- const entities = response.entities;
15290
- const integrity = this.getIntegrityId();
15291
- if (loopIntegrity == integrity && entities) {
15292
- (_c = this.onUpdate) === null || _c === void 0 ? void 0 : _c.Trigger(entities);
15293
- }
15294
- if (this.gatheredIntegrity != integrity) {
15295
- this.gatheredIntegrity = integrity;
15296
- this.gatheredEntityIds = [];
15297
- }
15298
- // Add to the integrity list for any new IDs found.
15299
- // This lets us keep track of all IDs we've found within the same integrity for historic data.
15300
- if (this.historicAttrKey || this.historic) {
15301
- for (let i = 0; i < entities.length; i++) {
15302
- const entity = entities[i];
15303
- if (!this.gatheredEntityIds.includes(entity.Bruce.ID)) {
15304
- this.gatheredEntityIds.push(entity.Bruce.ID);
15305
- }
15306
- }
15307
- }
15308
- if (this.getterLoopId != loopId) {
15309
- break;
15310
- }
15311
- if (entities.length) {
15312
- total += entities.length;
15313
- }
15314
- if (!postedLoading) {
15315
- this.postStatus({ msg: EStatus.Loading, revoking: false });
15316
- postedLoading = true;
15317
- }
15318
- // Only mark as fetched when ALL pages are done.
15319
- // Known issue where external sources may return less than page size.
15320
- // Right now we're making it as fetched as we're siding with the majority use-case.
15321
- if (
15322
- // API explicity says no more pages.
15323
- response.nextPage === false ||
15324
- // API didn't explicity say anything so we guess based on size of response.
15325
- (response.nextPage == null &&
15326
- (entities.length <= 0 || entities.length < PAGE_SIZE))) {
15327
- curCell.Fetched = true;
15328
- curCell.Fetching = false;
15329
- (_d = this.onScanUpdate) === null || _d === void 0 ? void 0 : _d.Trigger(cells);
15330
- continue;
15331
- }
15332
- // Checking to make sure it's not just the same batch over and over again.
15333
- if (entities.length > 0) {
15334
- const first = (_f = (_e = entities[0]) === null || _e === void 0 ? void 0 : _e.Bruce) === null || _f === void 0 ? void 0 : _f.ID;
15335
- const last = (_h = (_g = entities[entities.length - 1]) === null || _g === void 0 ? void 0 : _g.Bruce) === null || _h === void 0 ? void 0 : _h.ID;
15336
- if (prevFirstId == first && prevLastId == last) {
15337
- prevTicks += 1;
15338
- if (prevTicks > 3) {
15339
- break;
15340
- }
15341
- }
15342
- else {
15343
- prevFirstId = first;
15344
- prevLastId = last;
15345
- prevTicks = 0;
15346
- }
15347
- }
15348
- // Using URL if specified.
15349
- // If not then we'll just manually paginate.
15350
- curCell.FetchURL = response.nextPageUrl;
15351
- curCell.FetchPageIndex++;
15352
- // Request passed so let's assume it was server hiccup and refresh counts.
15353
- retryAttempts = MAX_RETRY_ATTEMPTS;
15354
- retryDelay = 0;
15355
- }
15356
- catch (e) {
15357
- // Ignore abort errors.
15358
- if (e && typeof e === "object" && e.name == "AbortError") {
15359
- // console.debug("Aborted entity-filter-getter request.");
15360
- break;
15361
- }
15362
- console.error(e);
15363
- if (this.getterLoopId != loopId) {
15364
- break;
15365
- }
15366
- // Request failed so let's add a delay and try again soon.
15367
- retryDelay += RETRY_DELAY_INCREMENT;
15368
- retryAttempts -= 1;
15369
- }
15370
- await delay(REQUEST_PAGE_DELAY);
15371
- }
15372
- if (postedLoading) {
15373
- this.postStatus({ msg: EStatus.Loading, revoking: true });
15374
- }
15375
- if (postedScanning) {
15376
- this.postStatus({ msg: EStatus.Scanning, revoking: true });
15377
- }
15378
- })().then(() => {
15379
- this.looping -= 1;
15380
- }).catch(() => {
15381
- this.looping -= 1;
15382
- });
15383
- }
15384
- /**
15385
- * Gets the historic state of found Entities for the current date times and emits them.
15386
- * Since geometry searches are tied to the base Entity, we don't have to re-scan the viewport.
15387
- */
15388
- emitHistoricData() {
15389
- if (!this.GetMenuItems().length) {
15390
- return;
15391
- }
15392
- let integrity = this.getIntegrityId();
15393
- // Gathered ID does't match current one.
15394
- if (this.gatheredIntegrity != integrity) {
15395
- return;
15523
+ // Changed historic.
15524
+ if ((data && isHistoricMetadataChanged(entity, data)) || (!data && ((_e = entity.historicLayers) === null || _e === void 0 ? void 0 : _e.length) && !((_g = (_f = data.Bruce) === null || _f === void 0 ? void 0 : _f.HistoricLayers) === null || _g === void 0 ? void 0 : _g.length))) {
15525
+ entity.historicLayers = (_h = data === null || data === void 0 ? void 0 : data.Bruce) === null || _h === void 0 ? void 0 : _h.HistoricLayers;
15526
+ changed = true;
15396
15527
  }
15397
- const historicAttrDateTime = this.historicAttrDateTime;
15398
- const SCAN_BATCH_SIZE = 1000;
15399
- if (this.historicRefreshAbortController) {
15400
- this.historicRefreshAbortController.abort();
15401
- this.historicRefreshAbortController = null;
15528
+ // Something changed, trigger a rego update.
15529
+ // This lets UI know when the rego has changed.
15530
+ if (changed) {
15531
+ this.register.OnUpdate.Trigger({
15532
+ type: VisualsRegister.EVisualUpdateType.Update,
15533
+ entityId: entity.entityId,
15534
+ rego: entity
15535
+ });
15402
15536
  }
15403
- (async () => {
15404
- var _a;
15405
- try {
15406
- // Loop through all IDs we've found and get their historic records.
15407
- for (let i = 0; i < this.gatheredEntityIds.length; i += SCAN_BATCH_SIZE) {
15408
- let batch = this.gatheredEntityIds.slice(i, i + SCAN_BATCH_SIZE);
15409
- if (!batch.length) {
15410
- break;
15411
- }
15412
- // Controller we can use to abort the request when a new loop starts.
15413
- const controller = this.historicRefreshAbortController = new AbortController();
15414
- let entities = [];
15415
- await SharedGetters.Queue.Run("Refreshing historic data in Menu Item that loads Entity Type: " + this.typeIds, async () => {
15416
- var _a;
15417
- if (controller.signal.aborted || !((_a = this.GetMenuItems()) === null || _a === void 0 ? void 0 : _a.length)) {
15418
- return;
15419
- }
15420
- entities = (await Entity$1.GetList({
15421
- api: this.api,
15422
- scenario: this.scenario,
15423
- historicKey: this.historicAttrKey,
15424
- historicPoint: historicAttrDateTime,
15425
- schemaId: this.schemaId,
15426
- filter: {
15427
- pageSize: batch.length,
15428
- pageIndex: 0,
15429
- entityTypeId: this.typeIds,
15430
- layerIds: this.tagIds,
15431
- layerIdsOperator: "in",
15432
- sortOrder: Api.ESortOrder.Asc,
15433
- entityTypeConditions: {
15434
- "ID": {
15435
- "in": batch
15436
- }
15437
- },
15438
- },
15439
- viaCdn: this.viaCdn,
15440
- migrated: true,
15441
- // If we're taking 5+ minutes to make a query, it's a dud.
15442
- // This is a timeout imposed on our DB and not external sources.
15443
- // Honestly could lower down to 30 seconds, but we'll keep it high for now.
15444
- maxSearchTimeSec: 60 * 5,
15445
- req: {
15446
- // If we are passing in an abort, we MUST pass in noCache.
15447
- // Otherwise we will cache an aborted request.
15448
- noCache: true,
15449
- abortSignal: controller.signal
15450
- }
15451
- })).entities;
15452
- });
15453
- // Date changed.
15454
- if (this.historicAttrDateTime != historicAttrDateTime) {
15455
- break;
15456
- }
15457
- // Integrity changed.
15458
- if (this.gatheredIntegrity != integrity) {
15459
- break;
15460
- }
15461
- // No Menu Items.
15462
- if (!this.GetMenuItems().length) {
15463
- break;
15464
- }
15465
- (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(entities);
15466
- }
15467
- }
15468
- catch (e) {
15469
- // Ignore abort errors.
15470
- if (e && typeof e === "object" && e.name == "AbortError") {
15471
- // console.debug("Aborted entity-filter-getter historic refresh request.");
15472
- return;
15473
- }
15474
- console.error(e);
15475
- }
15476
- })();
15477
15537
  }
15478
15538
  }
15479
- EntityFilterGetter.Getter = Getter;
15480
- })(EntityFilterGetter || (EntityFilterGetter = {}));
15539
+ getTilesetFeatureStyle(entityTypeId) {
15540
+ var _a, _b, _c, _d;
15541
+ // Locate what style is applicable to the feature.
15542
+ let style = null;
15543
+ if (entityTypeId) {
15544
+ style = ((_b = (_a = this.styleMapping.find(x => x.EntityTypeID == entityTypeId)) === null || _a === void 0 ? void 0 : _a.style) === null || _b === void 0 ? void 0 : _b.Settings);
15545
+ }
15546
+ if (!style) {
15547
+ style = ((_c = this.fallbackStyle) === null || _c === void 0 ? void 0 : _c.Settings);
15548
+ }
15549
+ if (!style || !((_d = style === null || style === void 0 ? void 0 : style.modelStyle) === null || _d === void 0 ? void 0 : _d.customize)) {
15550
+ return null;
15551
+ }
15552
+ return style;
15553
+ }
15554
+ getTilesetFeatureNeedsFullData(entityTypeId) {
15555
+ var _a;
15556
+ if (this.historic) {
15557
+ // Unfortunately when we are working with historic we have to request the entity.
15558
+ // This is because we need to know if a record exists at the point in time.
15559
+ return true;
15560
+ }
15561
+ const style = this.getTilesetFeatureStyle(entityTypeId);
15562
+ if (!style) {
15563
+ return false;
15564
+ }
15565
+ const fill = (_a = style === null || style === void 0 ? void 0 : style.modelStyle) === null || _a === void 0 ? void 0 : _a.fillColor;
15566
+ if (!fill || fill.length <= 0) {
15567
+ return false;
15568
+ }
15569
+ return fill[0].type != 0;
15570
+ }
15571
+ }
15481
15572
 
15482
- function createFilterGetterCacheKey(params) {
15483
- let cacheKey = "";
15484
- cacheKey += params.api.GetBaseUrl();
15485
- // Not including Type ID in the cache key now.
15486
- // This allows us to re-use the same getter between Entity Types.
15487
- // cacheKey += params.typeId;
15488
- cacheKey += params.batchSize;
15489
- cacheKey += String(params.cdn);
15490
- cacheKey += params.schemaId ? params.schemaId : "";
15491
- cacheKey += JSON.stringify(params.tagIds ? params.tagIds : []);
15492
- cacheKey += params.historicAttrKey ? params.historicAttrKey : "";
15493
- cacheKey += params.historic ? "true" : "false";
15494
- cacheKey += params.scenario ? params.scenario : 0;
15495
- if (params.historicAttrKey) {
15496
- cacheKey += params.historicInterpolation ? "true" : "false";
15573
+ /**
15574
+ * Returns if a given tileset is alive and in the scene.
15575
+ * @param viewer
15576
+ * @param cTileset
15577
+ * @returns
15578
+ */
15579
+ function isAlive$2(viewer, cTileset) {
15580
+ if (!(viewer === null || viewer === void 0 ? void 0 : viewer.scene)) {
15581
+ return false;
15497
15582
  }
15498
- // This could potentially crash, but if it crashes here then it would crash during API request anyways.
15499
- cacheKey += JSON.stringify(params.attrFilter ? params.attrFilter : {});
15500
- return cacheKey;
15501
- }
15502
- var SharedGetters;
15503
- (function (SharedGetters) {
15504
- class Cache {
15505
- constructor() {
15506
- this.data = {};
15507
- }
15508
- GetOrCreateFilterGetter(params) {
15509
- params.cdn = Boolean(params.cdn);
15510
- const cacheKey = createFilterGetterCacheKey(params);
15511
- let getter = this.data[cacheKey];
15512
- if (!getter) {
15513
- getter = new EntityFilterGetter.Getter({
15514
- api: params.api,
15515
- viewer: params.viewer,
15516
- viewPort: params.monitor,
15517
- typeIds: !params.typeId ? null : typeof params.typeId == "string" ? [params.typeId] : params.typeId,
15518
- schemaId: params.schemaId,
15519
- batchSize: params.batchSize,
15520
- attrFilter: params.attrFilter,
15521
- historic: params.historic,
15522
- historicAttrKey: params.historicAttrKey,
15523
- historicInterpolation: params.historicInterpolation,
15524
- viaCdn: params.cdn,
15525
- scenario: params.scenario,
15526
- });
15527
- this.data[cacheKey] = getter;
15528
- /**
15529
- * Debug option.
15530
- * This will display the bounds of the cells that are being fetched.
15531
- */
15532
- if (params.viewer && params.debugShowBounds) {
15533
- // Cell id -> entity.
15534
- const cellCache = {};
15535
- const cellPrefix = ObjectUtils.UId(10) + "_";
15536
- getter.OnScanUpdate.Subscribe((cells) => {
15537
- if (window.ON_SCAN_UPDATE_PAUSED == true) {
15538
- return;
15539
- }
15540
- const curCellIds = [];
15541
- let fetchingCellId = null;
15542
- const fetchedCells = {};
15543
- cells.forEach((cell) => {
15544
- var _a;
15545
- const bounds = cell.GetBounds();
15546
- const id = cellPrefix + bounds.east + "_" + bounds.north + "_" + bounds.south + "_" + bounds.west;
15547
- curCellIds.push(id);
15548
- fetchedCells[id] = cell.IsFetched();
15549
- fetchingCellId = cell.Fetching ? id : fetchingCellId;
15550
- let material = null;
15551
- if (fetchedCells[id]) {
15552
- material = Color.LIGHTGREEN.clone().withAlpha(0.3);
15553
- }
15554
- else if (fetchingCellId == id) {
15555
- material = Color.LIGHTBLUE.clone().withAlpha(0.3);
15556
- }
15557
- else {
15558
- material = Color.GOLD.clone().withAlpha(0.3);
15559
- }
15560
- if (cellCache[id]) {
15561
- const rect = (_a = cellCache[id]) === null || _a === void 0 ? void 0 : _a.rectangle;
15562
- if (rect) {
15563
- rect.material = material;
15564
- }
15565
- return;
15566
- }
15567
- const rect = new Rectangle(Math$1.toRadians(bounds.west), Math$1.toRadians(bounds.south), Math$1.toRadians(bounds.east), Math$1.toRadians(bounds.north));
15568
- const entity = params.viewer.entities.add(new Entity({
15569
- id: id,
15570
- rectangle: {
15571
- coordinates: rect,
15572
- fill: true,
15573
- //material: Cesium.Color.fromRandom().withAlpha(0.3),
15574
- material: material,
15575
- zIndex: 0
15576
- },
15577
- // point: {
15578
- // pixelSize: 15,
15579
- // outlineColor: Cesium.Color.WHITE,
15580
- // outlineWidth: 2,
15581
- // color: Cesium.Color.fromRandom().withAlpha(0.5),
15582
- // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
15583
- // },
15584
- position: Cartesian3.fromRadians((rect.east + rect.west) / 2, (rect.north + rect.south) / 2)
15585
- }));
15586
- cellCache[id] = entity;
15587
- });
15588
- Object.keys(cellCache).forEach((id) => {
15589
- if (curCellIds.indexOf(id) == -1) {
15590
- const entity = cellCache[id];
15591
- if (entity && params.viewer.entities.contains(entity)) {
15592
- params.viewer.entities.remove(entity);
15593
- }
15594
- delete cellCache[id];
15595
- }
15596
- });
15597
- });
15598
- }
15599
- }
15600
- return getter;
15601
- }
15602
- }
15603
- SharedGetters.Cache = Cache;
15604
- /**
15605
- * To avoid performing multiple expensive queries at the same time,
15606
- * we'll have the getters work through a queue of requests.
15607
- */
15608
- let Queue;
15609
- (function (Queue) {
15610
- const queue = [];
15611
- let isProcessing = false;
15612
- /**
15613
- * Called to run a provided function.
15614
- * When the function is complete, the next function in the queue will be called.
15615
- * This function will resolve when the provided function is complete.
15616
- * If the provided function crashes, the error bubbles up to the caller.
15617
- * @param name
15618
- * @param call
15619
- */
15620
- function Run(name, call) {
15621
- // console.debug(`Queueing: ${name}`);
15622
- return new Promise((resolve, reject) => {
15623
- queue.push(async () => {
15624
- try {
15625
- // console.debug(`Running: ${name}`);
15626
- const result = await call();
15627
- resolve(result);
15628
- }
15629
- catch (error) {
15630
- reject(error);
15631
- }
15632
- finally {
15633
- // Process the next function in the queue.
15634
- queue.shift();
15635
- // If there are more functions in the queue, process the next one.
15636
- if (queue.length > 0) {
15637
- processNext();
15638
- }
15639
- // No more items to process.
15640
- else {
15641
- isProcessing = false;
15642
- }
15643
- }
15644
- });
15645
- if (!isProcessing) {
15646
- // If not currently processing, start processing the queue.
15647
- isProcessing = true;
15648
- processNext();
15649
- }
15650
- });
15651
- }
15652
- Queue.Run = Run;
15653
- async function processNext() {
15654
- const nextCall = queue[0];
15655
- if (nextCall) {
15656
- // Call the next function in the queue.
15657
- await nextCall();
15658
- }
15659
- }
15660
- })(Queue = SharedGetters.Queue || (SharedGetters.Queue = {}));
15661
- })(SharedGetters || (SharedGetters = {}));
15662
-
15663
- function colorToCColor$3(color) {
15664
- return new Color(color.red ? color.red / 255 : 0, color.green ? color.green / 255 : 0, color.blue ? color.blue / 255 : 0, color.alpha);
15665
- }
15666
- /**
15667
- * Returns if a given tileset is alive and in the scene.
15668
- * @param viewer
15669
- * @param cTileset
15670
- * @returns
15671
- */
15672
- function isAlive$2(viewer, cTileset) {
15673
- if (!(viewer === null || viewer === void 0 ? void 0 : viewer.scene)) {
15674
- return false;
15675
- }
15676
- if (viewer.isDestroyed()) {
15677
- return false;
15678
- }
15679
- if (cTileset.isDestroyed()) {
15680
- return false;
15681
- }
15682
- return viewer.scene.primitives.contains(cTileset);
15683
- }
15684
- // ND-1641. BOOKMARKS - (DEMO) View does not match bookmark.
15685
- // We have some evil hard-coded style mappings that need to be fixed.
15686
- // These exist within legacy project views.
15687
- function correctStyle(style) {
15688
- if (style && !style.Settings && !style.ID) {
15689
- return {
15690
- ID: -1,
15691
- Name: "Unknown",
15692
- Settings: {
15693
- ...style
15694
- },
15695
- Type: Style.EType.Entity
15696
- };
15697
- }
15698
- return style;
15583
+ if (viewer.isDestroyed()) {
15584
+ return false;
15585
+ }
15586
+ if (cTileset.isDestroyed()) {
15587
+ return false;
15588
+ }
15589
+ return viewer.scene.primitives.contains(cTileset);
15699
15590
  }
15700
15591
  const VIEWER_WATCH_KEY = "bruce-viewer-watch";
15701
15592
  const WATCH_KEY = "bruce-tileset-watch";
@@ -16165,585 +16056,788 @@ var TilesetRenderEngine;
16165
16056
  return cTileset;
16166
16057
  }
16167
16058
  TilesetRenderEngine.RenderLegacy = RenderLegacy;
16168
- class Styler {
16059
+ // Util for styling Tileset features.
16060
+ // Also used to flag historic metadata based on timeline changes.
16061
+ class Styler extends TilesetStyler {
16062
+ }
16063
+ TilesetRenderEngine.Styler = Styler;
16064
+ /**
16065
+ * The maximum memory in MB that can be used by all tilesets.
16066
+ * This is distributed evenly between all loaded tilesets.
16067
+ */
16068
+ TilesetRenderEngine.MAX_TILESET_MEMORY = 1024;
16069
+ /**
16070
+ * Watches tilesets in the viewer.
16071
+ * This will regulate their max memory param.
16072
+ * As more get watched their memory will be reduced.
16073
+ */
16074
+ class MemoryWatcher {
16075
+ constructor(viewer) {
16076
+ this.watched = [];
16077
+ this.viewer = viewer;
16078
+ }
16079
+ distributeMemory() {
16080
+ this.clean();
16081
+ // Total 1gb as default.
16082
+ // We'll need to allow user to change this somehow.
16083
+ const maxMemory = TilesetRenderEngine.MAX_TILESET_MEMORY;
16084
+ // Minimum memory in MB per tileset.
16085
+ const minMemory = 80;
16086
+ const totalPerTileset = Math.max(this.watched.length ? maxMemory / this.watched.length : maxMemory, minMemory);
16087
+ this.watched.forEach(x => {
16088
+ // Newer Cesium killed this property.
16089
+ // TODO: Check if it's needed then.
16090
+ x["maximumMemoryUsage"] = totalPerTileset;
16091
+ });
16092
+ }
16093
+ destroy() {
16094
+ this.watched = [];
16095
+ }
16096
+ /**
16097
+ * Remove all dead tilesets.
16098
+ */
16099
+ clean() {
16100
+ // Remove all dead tilesets.
16101
+ this.watched = this.watched.filter(x => isAlive$2(this.viewer, x));
16102
+ // Check if viewer is destroyed.
16103
+ if (!this.viewer || this.viewer.isDestroyed()) {
16104
+ this.destroy();
16105
+ }
16106
+ }
16107
+ Watch(tileset) {
16108
+ if (!tileset) {
16109
+ return;
16110
+ }
16111
+ if (!tileset[WATCH_KEY]) {
16112
+ tileset[WATCH_KEY] = ObjectUtils.UId();
16113
+ }
16114
+ const index = this.watched.findIndex(x => x[WATCH_KEY] === tileset[WATCH_KEY]);
16115
+ if (index >= 0) {
16116
+ return;
16117
+ }
16118
+ this.watched.push(tileset);
16119
+ this.distributeMemory();
16120
+ }
16121
+ Unwatch(tileset) {
16122
+ if (!tileset) {
16123
+ return;
16124
+ }
16125
+ if (!tileset[WATCH_KEY]) {
16126
+ tileset[WATCH_KEY] = ObjectUtils.UId();
16127
+ }
16128
+ const index = this.watched.findIndex(x => (x === null || x === void 0 ? void 0 : x[WATCH_KEY]) === tileset[WATCH_KEY]);
16129
+ if (index > -1) {
16130
+ this.watched.splice(index, 1);
16131
+ }
16132
+ this.distributeMemory();
16133
+ }
16134
+ }
16135
+ TilesetRenderEngine.MemoryWatcher = MemoryWatcher;
16136
+ function GetMemoryWatcher(viewer) {
16137
+ // If viewer is dead return nothing.
16138
+ if (!viewer || viewer.isDestroyed()) {
16139
+ return null;
16140
+ }
16141
+ if (!viewer[VIEWER_WATCH_KEY]) {
16142
+ viewer[VIEWER_WATCH_KEY] = new MemoryWatcher(viewer);
16143
+ }
16144
+ return viewer[VIEWER_WATCH_KEY];
16145
+ }
16146
+ TilesetRenderEngine.GetMemoryWatcher = GetMemoryWatcher;
16147
+ })(TilesetRenderEngine || (TilesetRenderEngine = {}));
16148
+
16149
+ /**
16150
+ * This is a helper for making entity requests based on a grid area.
16151
+ * It will return grid cells based on given view-area then will remember when stuff-
16152
+ * was fully-fetched for those cells to avoid making duplicate requests.
16153
+ */
16154
+ var EntityGlobe;
16155
+ (function (EntityGlobe) {
16156
+ class Cell {
16169
16157
  constructor() {
16170
- this.disposed = false;
16171
- // Indicates if the styler has been loaded/initialized and is ready to style.
16172
- this.loaded = false;
16173
- this.styleMappingLoaded = false;
16174
- this.styleMappingsLoaded = {};
16175
- this.fallbackStyle = null;
16176
- this.expandSources = false;
16177
- this.loadingCounter = 0;
16178
- // Dictionary of Entity ID -> boolean to indicate which Entities have been styled.
16179
- // This is used to determine if the Style colour needs to be overriden or not.
16180
- this.styledEntityIds = {};
16181
- // Dictionary of Entity ID -> boolean to indicate which Entities have had full Entity data loaded.
16182
- // This is used to determine what Entities need to be re-requested when the scene time changes.
16183
- this.styledWithAttrEntityIds = {};
16184
- this.scenario = 0;
16185
- // Indicates that we are retrieving historic records.
16186
- // This means that the current scene's time is included in the request.
16187
- this.historic = false;
16188
- this.viewerDateTimeChangeRemoval = null;
16189
- this.historicRefreshAbortController = null;
16190
- // More expensive process.
16191
- // When an Entity is styled, the rego is reviewed and updated if needed.
16192
- // This is currently used for scenarios, and off by default to keep other processes faster.
16193
- this.shouldUpdateRegoStates = false;
16194
- this.runningQueues = 0;
16195
- this.recordLoadQueue = [];
16196
- this.recordCheckQueue = [];
16197
- // % progress for how many Entities have been styled so far.
16198
- // This can change as Tiles load in and more queue.
16199
- this._styleProgress = 0;
16200
- this._styleProgressQueue = new DelayQueue(() => {
16201
- this.updateStyleProgress();
16202
- }, 250);
16203
- // Event for when the style progress changes.
16204
- this.OnStyleProgress = new BruceEvent();
16205
- // Cache of Entity objects to use when styling instead of requesting them.
16206
- this.entityDataCache = {};
16158
+ this.Fetched = false;
16159
+ this.IsFetched = null;
16160
+ this.FetchPageIndex = 0;
16161
+ // Optional URL to use instead of default URL generation.
16162
+ // This is typically a response from the API to use this specific URL for the next page.
16163
+ this.FetchURL = null;
16164
+ this.Boundaries = null;
16165
+ this.Fetching = false;
16166
+ }
16167
+ GetBounds() {
16168
+ // Entity data works in -180 to 180 range.
16169
+ function prepareRangeForBounds(range) {
16170
+ if (range > 180) {
16171
+ return range - 360;
16172
+ }
16173
+ return range;
16174
+ }
16175
+ // Add minor decimal as API crashes when giving it whole numbers.
16176
+ const maxLon = prepareRangeForBounds(this.Boundaries.maxLongitude);
16177
+ const minLon = prepareRangeForBounds(this.Boundaries.minLongitude);
16178
+ const maxLat = prepareRangeForBounds(this.Boundaries.maxLatitude);
16179
+ const minLat = prepareRangeForBounds(this.Boundaries.minLatitude);
16180
+ return {
16181
+ east: maxLon,
16182
+ north: maxLat,
16183
+ south: minLat,
16184
+ west: minLon
16185
+ };
16186
+ }
16187
+ }
16188
+ EntityGlobe.Cell = Cell;
16189
+ class Grid {
16190
+ constructor() {
16191
+ this.cells = {};
16192
+ }
16193
+ GetCellsForView(cameraPos, viewRect) {
16194
+ const cells = [];
16195
+ const minLat = viewRect.south;
16196
+ const minLon = viewRect.west;
16197
+ const maxLat = viewRect.north;
16198
+ const maxLon = viewRect.east;
16199
+ const MAX_CELLS = 150;
16200
+ const cellDegreeSize = getCellSizeFromHeight(viewRect.alt);
16201
+ // console.log("cell size", cellDegreeSize, "height", viewRect.alt);
16202
+ let curMinLon = floorValueToCellSize(cellDegreeSize, minLon);
16203
+ let curMinLat = floorValueToCellSize(cellDegreeSize, minLat);
16204
+ // For larger views we add additional padding because our view-area culling is too strong.
16205
+ if (cellDegreeSize >= 2) {
16206
+ curMinLon -= cellDegreeSize;
16207
+ curMinLat -= cellDegreeSize;
16208
+ }
16209
+ let centerX = cameraPos === null || cameraPos === void 0 ? void 0 : cameraPos.longitude;
16210
+ let centerY = cameraPos === null || cameraPos === void 0 ? void 0 : cameraPos.latitude;
16211
+ if (isNaN(centerX) || isNaN(centerY)) {
16212
+ centerX = (minLon + maxLon) / 2;
16213
+ centerY = (minLat + maxLat) / 2;
16214
+ }
16215
+ let width = Math.ceil((maxLon - curMinLon) / cellDegreeSize);
16216
+ let height = Math.ceil((maxLat - curMinLat) / cellDegreeSize);
16217
+ // For larger views we add additional padding because our view-area culling is too strong.
16218
+ if (cellDegreeSize >= 2) {
16219
+ width += 1;
16220
+ height += 1;
16221
+ }
16222
+ const cellDistances = [];
16223
+ for (let x = 0; x < width; x++) {
16224
+ for (let y = 0; y < height; y++) {
16225
+ const lon = x * cellDegreeSize + curMinLon;
16226
+ const lat = y * cellDegreeSize + curMinLat;
16227
+ const cellCenterX = lon + cellDegreeSize / 2;
16228
+ const cellCenterY = lat + cellDegreeSize / 2;
16229
+ const dist = Math.sqrt(Math.pow(cellCenterX - centerX, 2) + Math.pow(cellCenterY - centerY, 2));
16230
+ cellDistances.push({ x, y, dist });
16231
+ }
16232
+ }
16233
+ cellDistances.sort((a, b) => a.dist - b.dist);
16234
+ for (const { x, y } of cellDistances) {
16235
+ const lon = x * cellDegreeSize + curMinLon;
16236
+ const lat = y * cellDegreeSize + curMinLat;
16237
+ const [id, cell] = getOrCreateCell(this.cells, cellDegreeSize, lon, lon + cellDegreeSize, lat, lat + cellDegreeSize);
16238
+ cells.push(cell);
16239
+ if (cells.length >= MAX_CELLS) {
16240
+ break;
16241
+ }
16242
+ }
16243
+ return cells;
16244
+ }
16245
+ }
16246
+ EntityGlobe.Grid = Grid;
16247
+ })(EntityGlobe || (EntityGlobe = {}));
16248
+ function floorValueToCellSize(size, value) {
16249
+ return Math.floor(value / size) * size;
16250
+ }
16251
+ function getCellSizeFromHeight(height) {
16252
+ if (height < 1000) {
16253
+ return 0.01;
16254
+ }
16255
+ if (height < 5000) {
16256
+ return 0.04;
16257
+ }
16258
+ else if (height < 10000) {
16259
+ return 0.15;
16260
+ }
16261
+ else if (height < 30000) {
16262
+ return 0.5;
16263
+ }
16264
+ else if (height < 70000) {
16265
+ return 0.5;
16266
+ }
16267
+ else if (height < 100000) {
16268
+ return 1;
16269
+ }
16270
+ else if (height < 150000) {
16271
+ return 1;
16272
+ }
16273
+ else if (height < 200000) {
16274
+ return 1.5;
16275
+ }
16276
+ else if (height < 300000) {
16277
+ return 1.5;
16278
+ }
16279
+ else if (height < 500000) {
16280
+ return 3;
16281
+ }
16282
+ else if (height < 1000000) {
16283
+ return 3;
16284
+ }
16285
+ else if (height < 1200000) {
16286
+ return 4;
16287
+ }
16288
+ else if (height < 2000000) {
16289
+ return 6;
16290
+ }
16291
+ else if (height < 3000000) {
16292
+ return 20;
16293
+ }
16294
+ return 35;
16295
+ }
16296
+ function isCellFetched(cell) {
16297
+ if (cell.Fetched) {
16298
+ return true;
16299
+ }
16300
+ return false;
16301
+ }
16302
+ function getOrCreateCell(cells, cellSize, lon, maxLon, lat, maxLat) {
16303
+ const id = cellSize + "_" + lon + "_" + maxLon + "_" + lat + "_" + maxLat;
16304
+ let cell = cells[id];
16305
+ if (!cell) {
16306
+ cell = cells[id] = new EntityGlobe.Cell();
16307
+ cell.Boundaries = {
16308
+ minLatitude: lat,
16309
+ maxLatitude: maxLat,
16310
+ minLongitude: lon,
16311
+ maxLongitude: maxLon
16312
+ };
16313
+ cell.IsFetched = () => isCellFetched(cell);
16314
+ }
16315
+ return [id, cell];
16316
+ }
16317
+
16318
+ const MAX_AREA_IN_DEGREES$1 = 90;
16319
+ const MAX_RETRY_ATTEMPTS = 1;
16320
+ const RETRY_DELAY_INCREMENT = 500;
16321
+ const REQUEST_PAGE_DELAY = 50;
16322
+ class regMenuItemGetter {
16323
+ constructor(typeId, tagIds, minHeight, maxHeight) {
16324
+ this.TypeId = typeId;
16325
+ this.TagIds = tagIds;
16326
+ this.MinHeight = minHeight;
16327
+ this.MaxHeight = maxHeight;
16328
+ }
16329
+ }
16330
+ async function delay(milliseconds) {
16331
+ return new Promise((res) => {
16332
+ setTimeout(() => {
16333
+ res();
16334
+ }, milliseconds);
16335
+ });
16336
+ }
16337
+ /**
16338
+ * This is a batched entity getter.
16339
+ * It will scan for entity records in a view-area and emit them in batches.
16340
+ * It will restart scanning if the camera moves.
16341
+ */
16342
+ var EntityFilterGetter;
16343
+ (function (EntityFilterGetter) {
16344
+ let EStatus;
16345
+ (function (EStatus) {
16346
+ EStatus["Scanning"] = "SCANNING";
16347
+ EStatus["Loading"] = "LOADING";
16348
+ })(EStatus = EntityFilterGetter.EStatus || (EntityFilterGetter.EStatus = {}));
16349
+ class Getter {
16350
+ get OnUpdate() {
16351
+ if (!this.onUpdate) {
16352
+ this.onUpdate = new BruceEvent();
16353
+ }
16354
+ return this.onUpdate;
16207
16355
  }
16208
- get Disposed() {
16209
- return this.disposed;
16356
+ get OnStateUpdate() {
16357
+ if (!this.onStateUpdate) {
16358
+ this.onStateUpdate = new BruceEvent();
16359
+ }
16360
+ return this.onStateUpdate;
16210
16361
  }
16211
- get Loaded() {
16212
- return this.loaded;
16362
+ get OnScanUpdate() {
16363
+ if (!this.onScanUpdate) {
16364
+ this.onScanUpdate = new BruceEvent();
16365
+ }
16366
+ return this.onScanUpdate;
16213
16367
  }
16214
- get StyleProgress() {
16215
- return this._styleProgress;
16368
+ get isLooping() {
16369
+ return this.looping > 0;
16216
16370
  }
16217
- Init(params) {
16218
- var _a;
16219
- let { viewer, api, cTileset, fallbackStyleId, styleMapping, expandSources, menuItemId, register, scenario, historic } = params;
16220
- this.viewer = viewer;
16371
+ constructor(params) {
16372
+ this.onUpdate = null;
16373
+ this.LastStateUpdates = {};
16374
+ this.onStateUpdate = null;
16375
+ this.onScanUpdate = null;
16376
+ this.viewPortChangeRemoval = null;
16377
+ this.viewPortDelayQueue = null;
16378
+ this.viewerDateTimeChangeRemoval = null;
16379
+ this.cells = null;
16380
+ this.registeredItems = {};
16381
+ this.getterLoopId = 0;
16382
+ this.getterLoopAbortControllers = {};
16383
+ this.looping = 0;
16384
+ this.tagIds = null;
16385
+ this.minHeight = 0;
16386
+ this.maxHeight = 100000;
16387
+ this.viewRect = null;
16388
+ this.viewCenter = null;
16389
+ this.scenario = 0;
16390
+ this.historicRefreshAbortController = null;
16391
+ // Entity IDs found for the latest integrity.
16392
+ // We use this for refreshing historic data without having to repeat geographic queries.
16393
+ this.gatheredIntegrity = null;
16394
+ this.gatheredEntityIds = [];
16395
+ const { api, viewer, viewPort, typeIds, schemaId, batchSize, attrFilter, historicAttrKey, historicInterpolation, historic, viaCdn, scenario } = params;
16221
16396
  this.api = api;
16222
- this.cTileset = cTileset;
16223
- this.register = register;
16224
- this.menuItemId = menuItemId;
16397
+ this.typeIds = typeIds;
16398
+ this.schemaId = schemaId;
16225
16399
  this.historic = Boolean(historic);
16226
- if (expandSources != null) {
16227
- this.expandSources = expandSources;
16228
- }
16229
- if (fallbackStyleId != null) {
16230
- this.fallbackStyleId = fallbackStyleId;
16231
- }
16232
- if (styleMapping) {
16233
- this.styleMapping = styleMapping;
16234
- if (this.styleMapping) {
16235
- // Dereference.
16236
- this.styleMapping = JSON.parse(JSON.stringify(this.styleMapping));
16237
- }
16238
- // ND-1641. BOOKMARKS - (DEMO) View does not match bookmark.
16239
- // We have some evil hard-coded style mappings that need to be fixed.
16240
- // These exist within legacy project views.
16241
- // Update: This now also lets people have a style mapping without a real style record.
16242
- if ((_a = this.styleMapping) === null || _a === void 0 ? void 0 : _a.length) {
16243
- for (let i = 0; i < this.styleMapping.length; i++) {
16244
- const mapItem = this.styleMapping[i];
16245
- const mapStyle = mapItem.style ? mapItem.style : mapItem.Style;
16246
- this.styleMapping[i].style = correctStyle(mapStyle);
16247
- }
16248
- }
16249
- }
16250
- if (scenario != null) {
16251
- this.scenario = scenario;
16252
- }
16253
- if (!!scenario) {
16254
- this.shouldUpdateRegoStates = true;
16255
- }
16256
- else if (this.historic) {
16257
- this.shouldUpdateRegoStates = true;
16258
- }
16259
- this.loadStyles();
16260
- this.viewerDateTimeSub();
16261
- this.loaded = true;
16400
+ this.historicAttrKey = historicAttrKey;
16401
+ this.historicInterpolation = Boolean(historicInterpolation);
16402
+ this.viaCdn = Boolean(viaCdn);
16403
+ this.batchSize = isNaN(batchSize) ? 300 : batchSize;
16404
+ this.viewPort = viewPort;
16405
+ this.attrFilter = attrFilter;
16406
+ this.viewer = viewer;
16407
+ this.scenario = scenario ? scenario : 0;
16408
+ this.updateBounds();
16262
16409
  }
16263
16410
  /**
16264
- * Updates style mapping and fallback style.
16265
- * This will trigger a re-style of all entities, and will stop existing style loads.
16266
- * @param params
16411
+ * Returns id that represents the combined menu item parameters.
16412
+ * If integrity changes while a request is running, the request will not emit a response.
16413
+ * @returns
16267
16414
  */
16268
- UpdateStyleMapping(params) {
16269
- var _a;
16270
- if (params.styleMapping) {
16271
- // Dereference.
16272
- params.styleMapping = JSON.parse(JSON.stringify(params.styleMapping));
16273
- this.styleMapping = params.styleMapping;
16274
- // ND-1641. BOOKMARKS - (DEMO) View does not match bookmark.
16275
- // We have some evil hard-coded style mappings that need to be fixed.
16276
- // These exist within legacy project views.
16277
- // Update: This now also lets people have a style mapping without a real style record.
16278
- if ((_a = this.styleMapping) === null || _a === void 0 ? void 0 : _a.length) {
16279
- for (let i = 0; i < this.styleMapping.length; i++) {
16280
- const mapItem = this.styleMapping[i];
16281
- const mapStyle = mapItem.style ? mapItem.style : mapItem.Style;
16282
- this.styleMapping[i].style = correctStyle(mapStyle);
16283
- }
16284
- }
16285
- }
16286
- if (!isNaN(params.fallbackStyleId) && params.fallbackStyleId != null) {
16287
- this.fallbackStyleId = params.fallbackStyleId;
16288
- }
16289
- if (params.fallbackStyle) {
16290
- this.fallbackStyle = params.fallbackStyle;
16291
- }
16292
- else {
16293
- this.fallbackStyle = null;
16294
- }
16295
- if (params.expandSources != null) {
16296
- this.expandSources = params.expandSources;
16415
+ getIntegrityId() {
16416
+ let integrity = this.tagIds == null ? "" : this.tagIds.join();
16417
+ if (this.historicAttrKey) {
16418
+ integrity += "isHistoric";
16419
+ integrity += this.historicAttrKey;
16297
16420
  }
16298
- if (params.scenario != null) {
16299
- this.scenario = params.scenario;
16300
- if (!this.shouldUpdateRegoStates && !!this.scenario) {
16301
- this.shouldUpdateRegoStates = true;
16302
- }
16421
+ else if (this.historic) {
16422
+ integrity += "isHistoric";
16303
16423
  }
16304
- if (params.historic != null) {
16305
- this.historic = params.historic;
16306
- if (params.historic || this.historic != params.historic) {
16307
- this.shouldUpdateRegoStates = true;
16308
- }
16309
- if (params.historic) {
16310
- this.viewerDateTimeSub();
16311
- }
16312
- else {
16313
- this.viewerDateTimeDispose();
16314
- }
16424
+ if (this.scenario) {
16425
+ integrity += this.scenario;
16315
16426
  }
16316
- if (this.loaded) {
16317
- // Empty queues.
16318
- // Requeue all.
16319
- this.recordLoadQueue = [];
16320
- this.recordCheckQueue = [];
16321
- const regos = this.register.GetRegos({
16322
- menuItemId: this.menuItemId
16323
- });
16324
- // Reset progress.
16325
- this._styleProgressQueue.Call(true);
16326
- this.styleMappingLoaded = false;
16327
- this.styleMappingsLoaded = {};
16328
- this.QueueEntities(regos);
16329
- this.loadStyles();
16427
+ if (this.typeIds) {
16428
+ integrity += this.typeIds.join();
16330
16429
  }
16430
+ return integrity;
16431
+ }
16432
+ viewAreaSub() {
16433
+ this.viewAreaDispose();
16434
+ // We'll avoid restarting the scanner too often.
16435
+ this.viewPortDelayQueue = new DelayQueue(() => {
16436
+ this.updateBounds();
16437
+ this.startGetterLoop();
16438
+ }, 2000);
16439
+ this.viewPortChangeRemoval = this.viewPort.Updated().Subscribe(() => {
16440
+ var _a;
16441
+ (_a = this.viewPortDelayQueue) === null || _a === void 0 ? void 0 : _a.Call();
16442
+ });
16443
+ }
16444
+ viewAreaDispose() {
16445
+ var _a, _b;
16446
+ (_a = this.viewPortChangeRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
16447
+ this.viewPortChangeRemoval = null;
16448
+ (_b = this.viewPortDelayQueue) === null || _b === void 0 ? void 0 : _b.Dispose();
16449
+ this.viewPortDelayQueue = null;
16331
16450
  }
16332
16451
  /**
16333
- * Updates a cache of Entity objects to use when styling.
16334
- * When a cache is available, a request won't be performed to get that Entity's data.
16335
- * @param entityIds
16336
- * @param entities If an object is not available for a supplied Entity ID, then the cache for it is removed.
16452
+ * Monitors the Cesium viewer and updates the historic data filter values.
16453
+ * If there is no historic attr set, this will do nothing.
16337
16454
  */
16338
- SetEntityCache(entityIds, entities) {
16339
- // Turn the Entities array into an accessible dictionary.
16340
- const newDataMap = {};
16341
- if (entities) {
16342
- for (let i = 0; i < entities.length; i++) {
16343
- const entity = entities[i];
16344
- if (entity.Bruce) {
16345
- newDataMap[entity.Bruce.ID] = entity;
16455
+ viewerDateTimeSub() {
16456
+ if ((!this.historicAttrKey && !this.historic) || this.viewerDateTimeChangeRemoval) {
16457
+ return;
16458
+ }
16459
+ // This is multiplied by the speed of animation to figure
16460
+ // out how many animation "ticks" before we allow an update.
16461
+ let INTERVAL_WHILE_ANIMATING = 2.5 * 1000;
16462
+ let INTERVAL_WHILE_NOT_ANIMATING = 1000;
16463
+ if (this.historicInterpolation) {
16464
+ INTERVAL_WHILE_ANIMATING = 6 * 1000;
16465
+ INTERVAL_WHILE_NOT_ANIMATING = 3.5 * 1000;
16466
+ }
16467
+ let lastUpdateTime = null;
16468
+ let delayQueue = new DelayQueue(() => {
16469
+ try {
16470
+ // If the timeline is animating then we'll wait longer to update.
16471
+ if (this.viewer.clock.shouldAnimate && lastUpdateTime) {
16472
+ if (Math.abs(new Date().getTime() - lastUpdateTime) < INTERVAL_WHILE_ANIMATING) {
16473
+ return;
16474
+ }
16475
+ }
16476
+ const current = this.historicAttrDateTime;
16477
+ this.updateHistoricDateTime();
16478
+ if (current != this.historicAttrDateTime) {
16479
+ this.emitHistoricData();
16346
16480
  }
16347
16481
  }
16348
- }
16349
- // Update the cache for each supplied Entity ID.
16350
- for (let i = 0; i < entityIds.length; i++) {
16351
- const entityId = entityIds[i];
16352
- if (newDataMap[entityId]) {
16353
- this.entityDataCache[entityId] = newDataMap[entityId];
16482
+ catch (e) {
16483
+ console.error(e);
16354
16484
  }
16355
- else {
16356
- delete this.entityDataCache[entityId];
16485
+ }, INTERVAL_WHILE_NOT_ANIMATING);
16486
+ let postUpdateRemoval = this.viewer.scene.postUpdate.addEventListener(() => {
16487
+ if (delayQueue) {
16488
+ delayQueue.Call();
16357
16489
  }
16358
- }
16490
+ });
16491
+ this.viewerDateTimeChangeRemoval = () => {
16492
+ delayQueue === null || delayQueue === void 0 ? void 0 : delayQueue.Dispose();
16493
+ postUpdateRemoval === null || postUpdateRemoval === void 0 ? void 0 : postUpdateRemoval();
16494
+ delayQueue = null;
16495
+ postUpdateRemoval = null;
16496
+ };
16359
16497
  }
16360
- QueueEntities(entities, highPriority = false) {
16361
- if (!this.loaded) {
16498
+ updateHistoricDateTime() {
16499
+ if (!this.historicAttrKey && !this.historic) {
16500
+ this.historicAttrDateTime = null;
16362
16501
  return;
16363
16502
  }
16364
- // We set a default colour right away to avoid race conditions at later times.
16365
- for (let i = 0; i < entities.length; i++) {
16366
- const entity = entities[i];
16367
- this.styledEntityIds[entity.entityId] = true;
16368
- CesiumEntityStyler.BakeDefaultColor({
16369
- entity: entity.visual,
16370
- viewer: this.viewer,
16371
- override: false
16372
- });
16373
- }
16374
- for (let i = 0; i < entities.length; i++) {
16375
- const entity = entities[i];
16376
- this.queueTilesetFeatureStyle(entity, highPriority);
16377
- }
16378
- if (this.recordLoadQueue.length > 0) {
16379
- this.processQueue();
16503
+ const isChanged = (before, after) => {
16504
+ if (before && !after) {
16505
+ return true;
16506
+ }
16507
+ if (!before && after) {
16508
+ return true;
16509
+ }
16510
+ // Change must be at least 0.1 seconds.
16511
+ return Math.abs(before.getTime() - after.getTime()) > 100;
16512
+ };
16513
+ const oldDateTime = this.historicAttrDateTime ? new Date(this.historicAttrDateTime) : null;
16514
+ const newDateTime = JulianDate.toDate(this.viewer.clock.currentTime);
16515
+ if (isChanged(oldDateTime, newDateTime)) {
16516
+ this.historicAttrDateTime = newDateTime.toISOString();
16380
16517
  }
16381
16518
  }
16382
- async processQueue() {
16383
- var _a, _b;
16384
- const MAX_BATCHES = 2;
16385
- const BATCH_DELAY = 200;
16386
- if (this.runningQueues >= MAX_BATCHES) {
16387
- return;
16388
- }
16389
- else if (this.disposed) {
16390
- return;
16519
+ viewerDateTimeDispose() {
16520
+ var _a;
16521
+ (_a = this.viewerDateTimeChangeRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
16522
+ this.viewerDateTimeChangeRemoval = null;
16523
+ }
16524
+ GetMenuItems() {
16525
+ return Object.keys(this.registeredItems);
16526
+ }
16527
+ IncludeMenuItem(menuItemId, typeId, layerIds, minHeight, maxHeight) {
16528
+ this.registeredItems[menuItemId] = new regMenuItemGetter(typeId, layerIds, minHeight, maxHeight);
16529
+ this.updateState();
16530
+ }
16531
+ ExcludeMenuItem(menuItemId) {
16532
+ this.registeredItems[menuItemId] = null;
16533
+ delete this.registeredItems[menuItemId];
16534
+ this.updateState(true);
16535
+ }
16536
+ updateBounds() {
16537
+ const viewRect = this.viewPort.GetBounds();
16538
+ const poi = this.viewPort.GetTarget();
16539
+ if (viewRect && poi) {
16540
+ if (Math.abs(viewRect.west - viewRect.east) > MAX_AREA_IN_DEGREES$1) {
16541
+ return;
16542
+ }
16543
+ if (Math.abs(viewRect.south - viewRect.north) > MAX_AREA_IN_DEGREES$1) {
16544
+ return;
16545
+ }
16546
+ this.viewRect = viewRect;
16547
+ this.viewCenter = poi;
16391
16548
  }
16392
- this.runningQueues += 1;
16393
- let batch = [];
16394
- let rerun = false;
16395
- try {
16396
- batch = this.getEntityIdsForQueue();
16397
- if (batch.length > 0) {
16398
- let entities = [];
16399
- {
16400
- // Constructing new array of IDs for any ones that don't have cached data.
16401
- const batchToRequest = [];
16402
- // Check cache for available Entities.
16403
- for (let i = 0; i < batch.length; i++) {
16404
- const entityId = batch[i];
16405
- const entity = this.entityDataCache[entityId];
16406
- if (entity) {
16407
- entities.push(entity);
16408
- }
16409
- else {
16410
- batchToRequest.push(entityId);
16411
- }
16412
- }
16413
- if (batchToRequest.length) {
16414
- const dateTime = this.historic ? this.historicAttrDateTime : null;
16415
- const { entities: response } = await Entity$1.GetListByIds({
16416
- api: this.api,
16417
- entityIds: batchToRequest,
16418
- migrated: true,
16419
- expandSources: this.expandSources,
16420
- scenario: this.scenario,
16421
- historicPoint: dateTime
16422
- });
16423
- // See if the historic date is the same.
16424
- // If not, then we have to re-add the Entities to the queue and re-run.
16425
- const curDateTime = this.historic ? this.historicAttrDateTime : null;
16426
- if (curDateTime == dateTime) {
16427
- // Add all found Entities to the list.
16428
- entities.push(...response);
16429
- }
16430
- else {
16431
- rerun = true;
16432
- }
16433
- }
16549
+ }
16550
+ updateState(onlyIfLooping = false) {
16551
+ const tagIds = [];
16552
+ const typeIds = [];
16553
+ const menuItemIds = this.GetMenuItems();
16554
+ let minHeight = null;
16555
+ let maxHeight = null;
16556
+ for (let i = 0; i < menuItemIds.length; i++) {
16557
+ const menuItem = this.registeredItems[menuItemIds[i]];
16558
+ if (menuItem) {
16559
+ if (maxHeight == null || maxHeight < menuItem.MaxHeight) {
16560
+ maxHeight = menuItem.MaxHeight;
16434
16561
  }
16435
- if (rerun) {
16436
- this.recordLoadQueue = this.recordLoadQueue.concat(batch);
16562
+ if (minHeight == null || minHeight > menuItem.MinHeight) {
16563
+ minHeight = menuItem.MinHeight;
16437
16564
  }
16438
- else {
16439
- // Quick access dictionary of Entity ID -> Entities.
16440
- const entityMap = {};
16441
- // Gather all Tag IDs from found Entities.
16442
- let tagIds = [];
16443
- for (let i = 0; i < entities.length; i++) {
16444
- const entity = entities[i];
16445
- entityMap[entity.Bruce.ID] = entity;
16446
- if ((_b = (_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a["Layer.ID"]) === null || _b === void 0 ? void 0 : _b.length) {
16447
- const eTagIds = entity.Bruce["Layer.ID"];
16448
- for (let j = 0; j < eTagIds.length; j++) {
16449
- tagIds.push(eTagIds[j]);
16450
- }
16565
+ const itemLayerIds = menuItem.TagIds;
16566
+ if (itemLayerIds) {
16567
+ for (let j = 0; j < itemLayerIds.length; j++) {
16568
+ const itemLayerId = itemLayerIds[j];
16569
+ if (!tagIds.includes(itemLayerId)) {
16570
+ tagIds.push(itemLayerId);
16451
16571
  }
16452
16572
  }
16453
- // Turn into unique list.
16454
- if (tagIds.length) {
16455
- tagIds = tagIds.filter((v, i, a) => a.indexOf(v) === i);
16456
- }
16457
- // Gather records.
16458
- let tags = [];
16459
- if (tagIds.length) {
16460
- tags = (await EntityTag.GetListByIds({
16461
- tagIds: tagIds,
16462
- api: this.api
16463
- })).tags;
16464
- }
16465
- for (let i = 0; i < batch.length; i++) {
16466
- const entityId = batch[i];
16467
- const record = entityMap[entityId];
16468
- const feature = this.getEntityRego(entityId);
16469
- if (feature) {
16470
- const eTags = tags.filter(t => { var _a, _b, _c, _d; return ((_b = (_a = record === null || record === void 0 ? void 0 : record.Bruce) === null || _a === void 0 ? void 0 : _a["Layer.ID"]) === null || _b === void 0 ? void 0 : _b.length) && ((_d = (_c = record === null || record === void 0 ? void 0 : record.Bruce) === null || _c === void 0 ? void 0 : _c["Layer.ID"]) === null || _d === void 0 ? void 0 : _d.includes(t.ID)); });
16471
- this.styleTilesetFeatureFullData(feature, record, eTags);
16472
- }
16573
+ }
16574
+ const itemTypeId = menuItem.TypeId;
16575
+ if (itemTypeId) {
16576
+ if (!typeIds.includes(itemTypeId)) {
16577
+ typeIds.push(itemTypeId);
16473
16578
  }
16474
- rerun = rerun || batch.length > 0;
16475
- this.viewer.scene.requestRender();
16476
16579
  }
16477
16580
  }
16478
16581
  }
16479
- finally {
16480
- setTimeout(() => {
16481
- this.runningQueues -= 1;
16482
- if (rerun || this.recordLoadQueue.length > 0) {
16483
- this.processQueue();
16484
- }
16485
- }, BATCH_DELAY);
16486
- }
16487
- this.viewer.scene.requestRender();
16488
- }
16489
- getEntityIdsForQueue() {
16490
- const BATCH_SIZE = this.expandSources ? 100 : 500;
16491
- return this.recordLoadQueue.splice(0, BATCH_SIZE);
16492
- }
16493
- /**
16494
- * Calculates the current progress % and updates the progress if there is a change.
16495
- */
16496
- updateStyleProgress() {
16497
- if (this.disposed) {
16498
- return;
16499
- }
16500
- let progress = 100; // Done when idling.
16501
- if (this.recordCheckQueue.length || this.recordLoadQueue.length) {
16502
- const total = Object.keys(this.styledEntityIds).length;
16503
- // Done is the total minus the amount left.
16504
- // We have to ensure the same ID isn't counted within or across these arrays as well.
16505
- const uniqueSet = new Set(this.recordCheckQueue.concat(this.recordLoadQueue));
16506
- const done = total - uniqueSet.size;
16507
- progress = done / total * 100;
16508
- // Round to 2dp, 0, or 100.
16509
- if (progress < 0) {
16510
- progress = 0;
16511
- }
16512
- else if (progress < 100) {
16513
- progress = Math.round(progress * 100) / 100;
16514
- }
16515
- else if (progress > 100) {
16516
- progress = 100;
16517
- }
16582
+ if (menuItemIds.length > 0 && (!onlyIfLooping || this.isLooping)) {
16583
+ // Reset cells so none are marked as fetched.
16584
+ this.cells = new EntityGlobe.Grid();
16585
+ this.tagIds = tagIds;
16586
+ this.typeIds = typeIds;
16587
+ this.minHeight = minHeight;
16588
+ this.maxHeight = maxHeight;
16589
+ this.updateBounds();
16590
+ this.updateHistoricDateTime();
16591
+ this.startGetterLoop();
16592
+ this.viewAreaSub();
16593
+ this.viewerDateTimeSub();
16518
16594
  }
16519
- if (this._styleProgress != progress) {
16520
- this._styleProgress = progress;
16521
- this.OnStyleProgress.Trigger(progress);
16595
+ else {
16596
+ this.getterLoopId += 1;
16597
+ this.viewAreaDispose();
16598
+ this.viewerDateTimeDispose();
16522
16599
  }
16523
16600
  }
16524
- getEntityRego(entityId) {
16525
- return this.register.GetRego({
16526
- entityId,
16527
- menuItemId: this.menuItemId
16528
- });
16529
- }
16530
- Dispose() {
16531
- if (this.disposed) {
16532
- return;
16533
- }
16534
- this.disposed = true;
16535
- clearInterval(this.queueLoadInterval);
16536
- clearInterval(this.queueCheckInterval);
16537
- this._styleProgressQueue.Dispose();
16538
- this.viewerDateTimeDispose();
16601
+ postStatus(status) {
16602
+ var _a;
16603
+ this.LastStateUpdates[status.msg] = status;
16604
+ (_a = this.onStateUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(status);
16539
16605
  }
16540
- async loadStyles() {
16541
- var _a, _b;
16542
- const counter = ++this.loadingCounter;
16543
- this.styleMappingLoaded = false;
16544
- this.styleMappingsLoaded = {};
16545
- let fallbackStyleId = this.fallbackStyleId;
16546
- if (fallbackStyleId && fallbackStyleId > 0) {
16547
- try {
16548
- let { style: data } = await Style.Get({
16549
- api: this.api,
16550
- styleId: fallbackStyleId
16551
- });
16552
- this.fallbackStyle = data;
16553
- }
16554
- catch (e) {
16555
- console.error(e);
16606
+ startGetterLoop() {
16607
+ // Increase id so that existing loops stop.
16608
+ this.getterLoopId += 1;
16609
+ const loopId = this.getterLoopId;
16610
+ const loopIntegrity = this.getIntegrityId();
16611
+ // Abort any existing loops that don't match the current loop.
16612
+ // We tried using integrity, however we want to interrupt when user moves camera quickly.
16613
+ // So it's better to use the loop ID.
16614
+ const abortId = String(loopId);
16615
+ {
16616
+ const newAbortControllers = {};
16617
+ for (const key in this.getterLoopAbortControllers) {
16618
+ this.getterLoopAbortControllers[key].abort();
16556
16619
  }
16620
+ this.getterLoopAbortControllers = newAbortControllers;
16557
16621
  }
16558
- if (this.loadingCounter != counter) {
16559
- return;
16560
- }
16561
- // Load styles in the style mapping
16562
- let styleMapping = this.styleMapping;
16563
- if (!styleMapping) {
16564
- styleMapping = [];
16565
- }
16566
- this.styleMapping = styleMapping;
16567
- // Append all found entity-types.
16568
- // That way we can mark them as loaded instead of just the ones in the style mapping.
16569
- try {
16570
- const modelTree = (_b = (_a = this.cTileset) === null || _a === void 0 ? void 0 : _a.extensions) === null || _b === void 0 ? void 0 : _b.modelTree;
16571
- if (modelTree) {
16572
- const entityTypeIds = this.getEntityTypeIdsFromModelTree(modelTree);
16573
- for (let i = 0; i < entityTypeIds.length; i++) {
16574
- const entityTypeId = entityTypeIds[i];
16575
- if (styleMapping.findIndex(x => x.EntityTypeID == entityTypeId) <= -1) {
16576
- styleMapping.push({
16577
- EntityTypeID: entityTypeId,
16578
- StyleID: this.fallbackStyle ? fallbackStyleId : 0,
16579
- style: this.fallbackStyle
16580
- });
16581
- }
16582
- }
16622
+ const abortController = this.getterLoopAbortControllers[abortId] = new AbortController();
16623
+ this.looping += 1;
16624
+ (async () => {
16625
+ var _a, _b, _c, _d, _e, _f, _g, _h;
16626
+ // Larger initial delay for the first loops because terrain is likely loading in.
16627
+ // We also delay because if we enable 50 Menu Items at the same time, common requests we be made if we wait a bit.
16628
+ // Eg: same entity type will be grouped into the same filter getter instance.
16629
+ await delay(loopId <= 3 ? 800 : 300);
16630
+ const MIN_HEIGHT = this.minHeight;
16631
+ const MAX_HEIGHT = this.maxHeight;
16632
+ const PAGE_SIZE = this.batchSize;
16633
+ let retryAttempts = MAX_RETRY_ATTEMPTS;
16634
+ let retryDelay = 0;
16635
+ let prevFirstId = "";
16636
+ let prevLastId = "";
16637
+ let prevTicks = 0;
16638
+ while ((!this.viewCenter || !this.viewRect) && this.getterLoopId == loopId) {
16639
+ await delay(RETRY_DELAY_INCREMENT);
16583
16640
  }
16584
- }
16585
- catch (e) {
16586
- console.error(e);
16587
- }
16588
- // Before we start the loop we'll do a single request for the needed Entity Types.
16589
- // This will be aimed at rows that don't specify a Style and if the default fallback-
16590
- //is set to 0 (using the default of the Entity Type).
16591
- const typeMap = new Map();
16592
- const typeIds = styleMapping.map(x => x.StyleID == -1 || Boolean(x.style) ? null : x.EntityTypeID).filter(x => !!x);
16593
- if (typeIds.length) {
16594
- // We'll split up into batches of 50.
16595
- // Since we add the type IDs as query params I am paranoid of hitting the limit.
16596
- const splits = Math.ceil(typeIds.length / 50);
16597
- const batchSize = 50;
16598
- for (let i = 0; i < splits; i++) {
16599
- const batch = typeIds.slice(i * batchSize, (i + 1) * batchSize);
16600
- const { entityTypes } = await EntityType.GetList({
16601
- api: this.api,
16602
- entityTypeIds: batch
16603
- });
16604
- for (let i = 0; i < entityTypes.length; i++) {
16605
- const entityType = entityTypes[i];
16606
- typeMap.set(entityType.ID, entityType);
16607
- }
16641
+ if (this.getterLoopId != loopId) {
16642
+ return;
16608
16643
  }
16609
- }
16610
- for (let i = 0; i < styleMapping.length; i++) {
16611
- if (this.disposed) {
16612
- break;
16644
+ const alt = this.viewRect.alt;
16645
+ if (alt > MAX_HEIGHT || (alt < MIN_HEIGHT && MIN_HEIGHT > 0)) {
16646
+ return;
16613
16647
  }
16614
- let styleMap = styleMapping[i];
16615
- if (!styleMap.style && styleMap.StyleID != -1) {
16616
- let styleId = styleMap.StyleID;
16617
- // Get default style of the entity type, if
16618
- //no default is already specified.
16619
- if (!styleId && !this.fallbackStyle) {
16620
- try {
16621
- let entityType = typeMap.get(styleMap.EntityTypeID);
16622
- // If the map excluded the type for whatever reason we'll load it now.
16623
- if (!entityType) {
16624
- entityType = (await EntityType.Get({
16648
+ const cells = this.cells.GetCellsForView(this.viewCenter, this.viewRect);
16649
+ (_a = this.onScanUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(cells);
16650
+ let curCellIndex = cells.length > 0 ? 0 : null;
16651
+ let postedScanning = false;
16652
+ let postedLoading = false;
16653
+ let total = 0;
16654
+ while (retryAttempts > 0 && curCellIndex != null) {
16655
+ if (retryDelay > 0) {
16656
+ await delay(retryDelay);
16657
+ }
16658
+ if (this.getterLoopId != loopId) {
16659
+ break;
16660
+ }
16661
+ if (!postedScanning) {
16662
+ this.postStatus({ msg: EStatus.Scanning, revoking: false });
16663
+ postedScanning = true;
16664
+ }
16665
+ const curCell = cells[curCellIndex];
16666
+ if (curCell.IsFetched()) {
16667
+ curCell.Fetching = false;
16668
+ curCellIndex += 1;
16669
+ if (cells[curCellIndex]) {
16670
+ cells[curCellIndex].Fetching = true;
16671
+ }
16672
+ else {
16673
+ curCellIndex = null;
16674
+ }
16675
+ (_b = this.onScanUpdate) === null || _b === void 0 ? void 0 : _b.Trigger(cells);
16676
+ continue;
16677
+ }
16678
+ try {
16679
+ let response = {
16680
+ entities: [],
16681
+ nextPage: false,
16682
+ nextPageUrl: null
16683
+ };
16684
+ await SharedGetters.Queue.Run("Loading Entities from Menu Item that loads Entity Type: " + this.typeIds, async () => {
16685
+ var _a;
16686
+ if (abortController.signal.aborted || !((_a = this.GetMenuItems()) === null || _a === void 0 ? void 0 : _a.length)) {
16687
+ return;
16688
+ }
16689
+ // API gave us a URL to use.
16690
+ if (curCell.FetchURL) {
16691
+ const tmpResponse = await this.api.get(curCell.FetchURL, {
16692
+ abortSignal: abortController.signal,
16693
+ noCache: true
16694
+ });
16695
+ // Same mapping as bruce-models doing Entity.GetList.
16696
+ response = {
16697
+ entities: tmpResponse.Items ? tmpResponse.Items : [],
16698
+ nextPage: tmpResponse.NextPage,
16699
+ nextPageUrl: tmpResponse.NextPageURL
16700
+ };
16701
+ }
16702
+ else {
16703
+ response = await Entity$1.GetList({
16625
16704
  api: this.api,
16626
- entityTypeId: styleMap.EntityTypeID
16627
- })).entityType;
16705
+ scenario: this.scenario,
16706
+ historicKey: this.historicAttrKey,
16707
+ historicPoint: (this.historicAttrKey || this.historic) ? this.historicAttrDateTime : null,
16708
+ schemaId: this.schemaId,
16709
+ filter: {
16710
+ pageSize: PAGE_SIZE,
16711
+ pageIndex: curCell.FetchPageIndex,
16712
+ entityTypeId: this.typeIds,
16713
+ layerIds: this.tagIds,
16714
+ // Any tag specified will be allowed.
16715
+ layerIdsOperator: "in",
16716
+ bounds: curCell.GetBounds(),
16717
+ sortOrder: Api.ESortOrder.Asc,
16718
+ entityTypeConditions: this.attrFilter
16719
+ },
16720
+ viaCdn: this.viaCdn,
16721
+ migrated: true,
16722
+ // If we're taking 2+ minutes to make a query, it's a dud.
16723
+ // This is a timeout imposed on our DB and not external sources.
16724
+ // Honestly could lower down to 30 seconds, but we'll keep it high for now.
16725
+ maxSearchTimeSec: 60 * 2,
16726
+ req: {
16727
+ // If we are passing in an abort, we MUST pass in noCache.
16728
+ // Otherwise we will cache an aborted request.
16729
+ noCache: true,
16730
+ abortSignal: abortController.signal
16731
+ }
16732
+ });
16628
16733
  }
16629
- styleId = entityType === null || entityType === void 0 ? void 0 : entityType["DisplaySetting.ID"];
16734
+ });
16735
+ const entities = response.entities;
16736
+ const integrity = this.getIntegrityId();
16737
+ if (loopIntegrity == integrity && entities) {
16738
+ (_c = this.onUpdate) === null || _c === void 0 ? void 0 : _c.Trigger(entities);
16630
16739
  }
16631
- catch (e) {
16632
- console.error(e);
16740
+ if (this.gatheredIntegrity != integrity) {
16741
+ this.gatheredIntegrity = integrity;
16742
+ this.gatheredEntityIds = [];
16743
+ }
16744
+ // Add to the integrity list for any new IDs found.
16745
+ // This lets us keep track of all IDs we've found within the same integrity for historic data.
16746
+ if (this.historicAttrKey || this.historic) {
16747
+ for (let i = 0; i < entities.length; i++) {
16748
+ const entity = entities[i];
16749
+ if (!this.gatheredEntityIds.includes(entity.Bruce.ID)) {
16750
+ this.gatheredEntityIds.push(entity.Bruce.ID);
16751
+ }
16752
+ }
16753
+ }
16754
+ if (this.getterLoopId != loopId) {
16755
+ break;
16756
+ }
16757
+ if (entities.length) {
16758
+ total += entities.length;
16759
+ }
16760
+ if (!postedLoading) {
16761
+ this.postStatus({ msg: EStatus.Loading, revoking: false });
16762
+ postedLoading = true;
16763
+ }
16764
+ // Only mark as fetched when ALL pages are done.
16765
+ // Known issue where external sources may return less than page size.
16766
+ // Right now we're making it as fetched as we're siding with the majority use-case.
16767
+ if (
16768
+ // API explicity says no more pages.
16769
+ response.nextPage === false ||
16770
+ // API didn't explicity say anything so we guess based on size of response.
16771
+ (response.nextPage == null &&
16772
+ (entities.length <= 0 || entities.length < PAGE_SIZE))) {
16773
+ curCell.Fetched = true;
16774
+ curCell.Fetching = false;
16775
+ (_d = this.onScanUpdate) === null || _d === void 0 ? void 0 : _d.Trigger(cells);
16776
+ continue;
16633
16777
  }
16634
- }
16635
- if (styleId) {
16636
- try {
16637
- let { style: data } = await Style.Get({
16638
- api: this.api,
16639
- styleId
16640
- });
16641
- if (data) {
16642
- styleMap.style = data;
16643
- styleMap.StyleID = styleId;
16778
+ // Checking to make sure it's not just the same batch over and over again.
16779
+ if (entities.length > 0) {
16780
+ const first = (_f = (_e = entities[0]) === null || _e === void 0 ? void 0 : _e.Bruce) === null || _f === void 0 ? void 0 : _f.ID;
16781
+ const last = (_h = (_g = entities[entities.length - 1]) === null || _g === void 0 ? void 0 : _g.Bruce) === null || _h === void 0 ? void 0 : _h.ID;
16782
+ if (prevFirstId == first && prevLastId == last) {
16783
+ prevTicks += 1;
16784
+ if (prevTicks > 3) {
16785
+ break;
16786
+ }
16787
+ }
16788
+ else {
16789
+ prevFirstId = first;
16790
+ prevLastId = last;
16791
+ prevTicks = 0;
16644
16792
  }
16645
16793
  }
16646
- catch (e) {
16647
- console.error(e);
16648
- }
16649
- }
16650
- if (this.loadingCounter != counter) {
16651
- return;
16794
+ // Using URL if specified.
16795
+ // If not then we'll just manually paginate.
16796
+ curCell.FetchURL = response.nextPageUrl;
16797
+ curCell.FetchPageIndex++;
16798
+ // Request passed so let's assume it was server hiccup and refresh counts.
16799
+ retryAttempts = MAX_RETRY_ATTEMPTS;
16800
+ retryDelay = 0;
16652
16801
  }
16653
- }
16654
- this.styleMappingsLoaded[styleMap.EntityTypeID] = true;
16655
- if (this.loaded) {
16656
- this.processTilesetFeatureCheckQueue();
16657
- this.processQueue();
16658
- }
16659
- }
16660
- if (this.loadingCounter != counter) {
16661
- return;
16662
- }
16663
- this.styleMappingLoaded = true;
16664
- if (!this.disposed && this.loaded) {
16665
- await this.processTilesetFeatureCheckQueue();
16666
- if (this.loadingCounter != counter) {
16667
- return;
16668
- }
16669
- this.processQueue();
16670
- }
16671
- }
16672
- /**
16673
- * Monitors the Cesium viewer and updates the historic data filter values.
16674
- * If there is no historic attr set, this will do nothing.
16675
- */
16676
- viewerDateTimeSub() {
16677
- if (!this.historic || this.viewerDateTimeChangeRemoval) {
16678
- return;
16679
- }
16680
- // This is multiplied by the speed of animation to figure
16681
- // out how many animation "ticks" before we allow an update.
16682
- const INTERVAL_WHILE_ANIMATING = 2.5 * 1000;
16683
- const INTERVAL_WHILE_NOT_ANIMATING = 1000;
16684
- let lastUpdateTime = null;
16685
- let delayQueue = new DelayQueue(() => {
16686
- try {
16687
- // If the timeline is animating then we'll wait longer to update.
16688
- if (this.viewer.clock.shouldAnimate && lastUpdateTime) {
16689
- if (Math.abs(new Date().getTime() - lastUpdateTime) < INTERVAL_WHILE_ANIMATING) {
16690
- return;
16802
+ catch (e) {
16803
+ // Ignore abort errors.
16804
+ if (e && typeof e === "object" && e.name == "AbortError") {
16805
+ // console.debug("Aborted entity-filter-getter request.");
16806
+ break;
16691
16807
  }
16808
+ console.error(e);
16809
+ if (this.getterLoopId != loopId) {
16810
+ break;
16811
+ }
16812
+ // Request failed so let's add a delay and try again soon.
16813
+ retryDelay += RETRY_DELAY_INCREMENT;
16814
+ retryAttempts -= 1;
16692
16815
  }
16693
- const current = this.historicAttrDateTime;
16694
- this.updateHistoricDateTime();
16695
- if (current != this.historicAttrDateTime) {
16696
- this.emitHistoricData();
16697
- }
16816
+ await delay(REQUEST_PAGE_DELAY);
16698
16817
  }
16699
- catch (e) {
16700
- console.error(e);
16818
+ if (postedLoading) {
16819
+ this.postStatus({ msg: EStatus.Loading, revoking: true });
16701
16820
  }
16702
- }, INTERVAL_WHILE_NOT_ANIMATING);
16703
- let postUpdateRemoval = this.viewer.scene.postUpdate.addEventListener(() => {
16704
- if (delayQueue) {
16705
- delayQueue.Call();
16821
+ if (postedScanning) {
16822
+ this.postStatus({ msg: EStatus.Scanning, revoking: true });
16706
16823
  }
16824
+ })().then(() => {
16825
+ this.looping -= 1;
16826
+ }).catch(() => {
16827
+ this.looping -= 1;
16707
16828
  });
16708
- this.viewerDateTimeChangeRemoval = () => {
16709
- delayQueue === null || delayQueue === void 0 ? void 0 : delayQueue.Dispose();
16710
- postUpdateRemoval === null || postUpdateRemoval === void 0 ? void 0 : postUpdateRemoval();
16711
- delayQueue = null;
16712
- postUpdateRemoval = null;
16713
- };
16714
- }
16715
- updateHistoricDateTime() {
16716
- if (!this.historic) {
16717
- this.historicAttrDateTime = null;
16718
- return;
16719
- }
16720
- const isChanged = (before, after) => {
16721
- if (before && !after) {
16722
- return true;
16723
- }
16724
- if (!before && after) {
16725
- return true;
16726
- }
16727
- // Change must be at least 0.1 seconds.
16728
- return Math.abs(before.getTime() - after.getTime()) > 100;
16729
- };
16730
- const oldDateTime = this.historicAttrDateTime ? new Date(this.historicAttrDateTime) : null;
16731
- const newDateTime = JulianDate.toDate(this.viewer.clock.currentTime);
16732
- if (isChanged(oldDateTime, newDateTime)) {
16733
- this.historicAttrDateTime = newDateTime.toISOString();
16734
- }
16735
- }
16736
- viewerDateTimeDispose() {
16737
- var _a;
16738
- (_a = this.viewerDateTimeChangeRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
16739
- this.viewerDateTimeChangeRemoval = null;
16740
16829
  }
16741
16830
  /**
16742
16831
  * Gets the historic state of found Entities for the current date times and emits them.
16743
16832
  * Since geometry searches are tied to the base Entity, we don't have to re-scan the viewport.
16744
16833
  */
16745
16834
  emitHistoricData() {
16746
- if (this.disposed) {
16835
+ if (!this.GetMenuItems().length) {
16836
+ return;
16837
+ }
16838
+ let integrity = this.getIntegrityId();
16839
+ // Gathered ID does't match current one.
16840
+ if (this.gatheredIntegrity != integrity) {
16747
16841
  return;
16748
16842
  }
16749
16843
  const historicAttrDateTime = this.historicAttrDateTime;
@@ -16753,29 +16847,34 @@ var TilesetRenderEngine;
16753
16847
  this.historicRefreshAbortController = null;
16754
16848
  }
16755
16849
  (async () => {
16756
- var _a, _b;
16850
+ var _a;
16757
16851
  try {
16758
- const gatheredEntityIds = Object.keys(this.styledWithAttrEntityIds);
16759
16852
  // Loop through all IDs we've found and get their historic records.
16760
- for (let i = 0; i < gatheredEntityIds.length; i += SCAN_BATCH_SIZE) {
16761
- let batch = gatheredEntityIds.slice(i, i + SCAN_BATCH_SIZE);
16853
+ for (let i = 0; i < this.gatheredEntityIds.length; i += SCAN_BATCH_SIZE) {
16854
+ let batch = this.gatheredEntityIds.slice(i, i + SCAN_BATCH_SIZE);
16762
16855
  if (!batch.length) {
16763
16856
  break;
16764
16857
  }
16765
16858
  // Controller we can use to abort the request when a new loop starts.
16766
16859
  const controller = this.historicRefreshAbortController = new AbortController();
16767
16860
  let entities = [];
16768
- await SharedGetters.Queue.Run("Refreshing historic data in Menu Item that loads a Tileset.", async () => {
16769
- if (controller.signal.aborted || this.disposed) {
16861
+ await SharedGetters.Queue.Run("Refreshing historic data in Menu Item that loads Entity Type: " + this.typeIds, async () => {
16862
+ var _a;
16863
+ if (controller.signal.aborted || !((_a = this.GetMenuItems()) === null || _a === void 0 ? void 0 : _a.length)) {
16770
16864
  return;
16771
16865
  }
16772
16866
  entities = (await Entity$1.GetList({
16773
16867
  api: this.api,
16774
16868
  scenario: this.scenario,
16869
+ historicKey: this.historicAttrKey,
16775
16870
  historicPoint: historicAttrDateTime,
16871
+ schemaId: this.schemaId,
16776
16872
  filter: {
16777
16873
  pageSize: batch.length,
16778
16874
  pageIndex: 0,
16875
+ entityTypeId: this.typeIds,
16876
+ layerIds: this.tagIds,
16877
+ layerIdsOperator: "in",
16779
16878
  sortOrder: Api.ESortOrder.Asc,
16780
16879
  entityTypeConditions: {
16781
16880
  "ID": {
@@ -16783,6 +16882,7 @@ var TilesetRenderEngine;
16783
16882
  }
16784
16883
  },
16785
16884
  },
16885
+ viaCdn: this.viaCdn,
16786
16886
  migrated: true,
16787
16887
  // If we're taking 5+ minutes to make a query, it's a dud.
16788
16888
  // This is a timeout imposed on our DB and not external sources.
@@ -16800,331 +16900,211 @@ var TilesetRenderEngine;
16800
16900
  if (this.historicAttrDateTime != historicAttrDateTime) {
16801
16901
  break;
16802
16902
  }
16803
- // Gather all Tag IDs from found Entities.
16804
- let tagIds = [];
16805
- for (let i = 0; i < entities.length; i++) {
16806
- const entity = entities[i];
16807
- if ((_b = (_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a["Layer.ID"]) === null || _b === void 0 ? void 0 : _b.length) {
16808
- const eTagIds = entity.Bruce["Layer.ID"];
16809
- for (let j = 0; j < eTagIds.length; j++) {
16810
- tagIds.push(eTagIds[j]);
16811
- }
16812
- }
16813
- }
16814
- // Turn into unique list.
16815
- if (tagIds.length) {
16816
- tagIds = tagIds.filter((v, i, a) => a.indexOf(v) === i);
16817
- }
16818
- // Gather records.
16819
- let tags = [];
16820
- if (tagIds.length) {
16821
- tags = (await EntityTag.GetListByIds({
16822
- tagIds: tagIds,
16823
- api: this.api
16824
- })).tags;
16903
+ // Integrity changed.
16904
+ if (this.gatheredIntegrity != integrity) {
16905
+ break;
16825
16906
  }
16826
- // Date changed.
16827
- if (this.historicAttrDateTime != historicAttrDateTime) {
16907
+ // No Menu Items.
16908
+ if (!this.GetMenuItems().length) {
16828
16909
  break;
16829
16910
  }
16830
- // Style the Entities.
16831
- for (let i = 0; i < entities.length; i++) {
16832
- const entity = entities[i];
16833
- const feature = this.getEntityRego(entity.Bruce.ID);
16834
- if (feature) {
16835
- const eTags = tags.filter(t => { var _a, _b, _c, _d; return ((_b = (_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a["Layer.ID"]) === null || _b === void 0 ? void 0 : _b.length) && ((_d = (_c = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _c === void 0 ? void 0 : _c["Layer.ID"]) === null || _d === void 0 ? void 0 : _d.includes(t.ID)); });
16836
- this.styleTilesetFeatureFullData(feature, entity, eTags);
16911
+ (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(entities);
16912
+ }
16913
+ }
16914
+ catch (e) {
16915
+ // Ignore abort errors.
16916
+ if (e && typeof e === "object" && e.name == "AbortError") {
16917
+ // console.debug("Aborted entity-filter-getter historic refresh request.");
16918
+ return;
16919
+ }
16920
+ console.error(e);
16921
+ }
16922
+ })();
16923
+ }
16924
+ }
16925
+ EntityFilterGetter.Getter = Getter;
16926
+ })(EntityFilterGetter || (EntityFilterGetter = {}));
16927
+
16928
+ function createFilterGetterCacheKey(params) {
16929
+ let cacheKey = "";
16930
+ cacheKey += params.api.GetBaseUrl();
16931
+ // Not including Type ID in the cache key now.
16932
+ // This allows us to re-use the same getter between Entity Types.
16933
+ // cacheKey += params.typeId;
16934
+ cacheKey += params.batchSize;
16935
+ cacheKey += String(params.cdn);
16936
+ cacheKey += params.schemaId ? params.schemaId : "";
16937
+ cacheKey += JSON.stringify(params.tagIds ? params.tagIds : []);
16938
+ cacheKey += params.historicAttrKey ? params.historicAttrKey : "";
16939
+ cacheKey += params.historic ? "true" : "false";
16940
+ cacheKey += params.scenario ? params.scenario : 0;
16941
+ if (params.historicAttrKey) {
16942
+ cacheKey += params.historicInterpolation ? "true" : "false";
16943
+ }
16944
+ // This could potentially crash, but if it crashes here then it would crash during API request anyways.
16945
+ cacheKey += JSON.stringify(params.attrFilter ? params.attrFilter : {});
16946
+ return cacheKey;
16947
+ }
16948
+ var SharedGetters;
16949
+ (function (SharedGetters) {
16950
+ class Cache {
16951
+ constructor() {
16952
+ this.data = {};
16953
+ }
16954
+ GetOrCreateFilterGetter(params) {
16955
+ params.cdn = Boolean(params.cdn);
16956
+ const cacheKey = createFilterGetterCacheKey(params);
16957
+ let getter = this.data[cacheKey];
16958
+ if (!getter) {
16959
+ getter = new EntityFilterGetter.Getter({
16960
+ api: params.api,
16961
+ viewer: params.viewer,
16962
+ viewPort: params.monitor,
16963
+ typeIds: !params.typeId ? null : typeof params.typeId == "string" ? [params.typeId] : params.typeId,
16964
+ schemaId: params.schemaId,
16965
+ batchSize: params.batchSize,
16966
+ attrFilter: params.attrFilter,
16967
+ historic: params.historic,
16968
+ historicAttrKey: params.historicAttrKey,
16969
+ historicInterpolation: params.historicInterpolation,
16970
+ viaCdn: params.cdn,
16971
+ scenario: params.scenario,
16972
+ });
16973
+ this.data[cacheKey] = getter;
16974
+ /**
16975
+ * Debug option.
16976
+ * This will display the bounds of the cells that are being fetched.
16977
+ */
16978
+ if (params.viewer && params.debugShowBounds) {
16979
+ // Cell id -> entity.
16980
+ const cellCache = {};
16981
+ const cellPrefix = ObjectUtils.UId(10) + "_";
16982
+ getter.OnScanUpdate.Subscribe((cells) => {
16983
+ if (window.ON_SCAN_UPDATE_PAUSED == true) {
16984
+ return;
16985
+ }
16986
+ const curCellIds = [];
16987
+ let fetchingCellId = null;
16988
+ const fetchedCells = {};
16989
+ cells.forEach((cell) => {
16990
+ var _a;
16991
+ const bounds = cell.GetBounds();
16992
+ const id = cellPrefix + bounds.east + "_" + bounds.north + "_" + bounds.south + "_" + bounds.west;
16993
+ curCellIds.push(id);
16994
+ fetchedCells[id] = cell.IsFetched();
16995
+ fetchingCellId = cell.Fetching ? id : fetchingCellId;
16996
+ let material = null;
16997
+ if (fetchedCells[id]) {
16998
+ material = Color.LIGHTGREEN.clone().withAlpha(0.3);
16837
16999
  }
16838
- }
16839
- }
16840
- }
16841
- catch (e) {
16842
- // Ignore abort errors.
16843
- if (e && typeof e === "object" && e.name == "AbortError") {
16844
- return;
16845
- }
16846
- console.error(e);
16847
- }
16848
- })();
16849
- }
16850
- getEntityTypeIdsFromModelTree(modelTree) {
16851
- const entityTypeIds = [];
16852
- this.digEntityTypeIdsFromModelTreeBranch(modelTree, entityTypeIds);
16853
- return entityTypeIds;
16854
- }
16855
- digEntityTypeIdsFromModelTreeBranch(branch, arr) {
16856
- if (branch) {
16857
- // Does not yet include this entity type id.
16858
- if (branch.typeId && !arr.includes(branch.typeId)) {
16859
- arr.push(branch.typeId);
16860
- }
16861
- if (branch.children) {
16862
- for (let i = 0; i < branch.children.length; i++) {
16863
- let child = branch.children[i];
16864
- this.digEntityTypeIdsFromModelTreeBranch(child, arr);
16865
- }
16866
- }
16867
- }
16868
- }
16869
- async processTilesetFeatureCheckQueue() {
16870
- const BATCH_CHECK_SIZE = 1000;
16871
- return new Promise((res) => {
16872
- clearInterval(this.queueCheckInterval);
16873
- this.queueCheckInterval = setInterval(() => {
16874
- if (this.disposed) {
16875
- clearInterval(this.queueCheckInterval);
16876
- res();
16877
- return;
16878
- }
16879
- // Construct batch for loaded styles.
16880
- let batch = [];
16881
- if (this.styleMappingLoaded) {
16882
- batch = this.recordCheckQueue.splice(0, BATCH_CHECK_SIZE);
16883
- }
16884
- else {
16885
- for (let i = 0; i < this.recordCheckQueue.length; i++) {
16886
- const entityId = this.recordCheckQueue[i];
16887
- const entity = this.getEntityRego(entityId);
16888
- if (entity) {
16889
- const entityTypeId = entity.entityTypeId;
16890
- if (entityTypeId) {
16891
- if (this.styleMappingsLoaded[entityTypeId] == true) {
16892
- batch.push(entityId);
16893
- }
16894
- }
17000
+ else if (fetchingCellId == id) {
17001
+ material = Color.LIGHTBLUE.clone().withAlpha(0.3);
16895
17002
  }
16896
- if (batch.length >= BATCH_CHECK_SIZE) {
16897
- break;
17003
+ else {
17004
+ material = Color.GOLD.clone().withAlpha(0.3);
16898
17005
  }
16899
- }
16900
- for (let i = 0; i < batch.length; i++) {
16901
- const entityId = batch[i];
16902
- const index = this.recordCheckQueue.findIndex(x => x == entityId);
16903
- if (index > -1) {
16904
- this.recordCheckQueue.splice(index, 1);
17006
+ if (cellCache[id]) {
17007
+ const rect = (_a = cellCache[id]) === null || _a === void 0 ? void 0 : _a.rectangle;
17008
+ if (rect) {
17009
+ rect.material = material;
17010
+ }
17011
+ return;
16905
17012
  }
16906
- }
16907
- }
16908
- if (batch.length) {
16909
- for (let i = 0; i < batch.length; i++) {
16910
- const entityId = batch[i];
16911
- const entity = this.getEntityRego(entityId);
16912
- if (entity) {
16913
- this.queueTilesetFeatureStyle(entity, false);
17013
+ const rect = new Rectangle(Math$1.toRadians(bounds.west), Math$1.toRadians(bounds.south), Math$1.toRadians(bounds.east), Math$1.toRadians(bounds.north));
17014
+ const entity = params.viewer.entities.add(new Entity({
17015
+ id: id,
17016
+ rectangle: {
17017
+ coordinates: rect,
17018
+ fill: true,
17019
+ //material: Cesium.Color.fromRandom().withAlpha(0.3),
17020
+ material: material,
17021
+ zIndex: 0
17022
+ },
17023
+ // point: {
17024
+ // pixelSize: 15,
17025
+ // outlineColor: Cesium.Color.WHITE,
17026
+ // outlineWidth: 2,
17027
+ // color: Cesium.Color.fromRandom().withAlpha(0.5),
17028
+ // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
17029
+ // },
17030
+ position: Cartesian3.fromRadians((rect.east + rect.west) / 2, (rect.north + rect.south) / 2)
17031
+ }));
17032
+ cellCache[id] = entity;
17033
+ });
17034
+ Object.keys(cellCache).forEach((id) => {
17035
+ if (curCellIds.indexOf(id) == -1) {
17036
+ const entity = cellCache[id];
17037
+ if (entity && params.viewer.entities.contains(entity)) {
17038
+ params.viewer.entities.remove(entity);
17039
+ }
17040
+ delete cellCache[id];
16914
17041
  }
16915
- }
16916
- this.viewer.scene.requestRender();
16917
- }
16918
- else {
16919
- clearInterval(this.queueCheckInterval);
16920
- res();
16921
- }
16922
- }, 50);
16923
- });
16924
- }
16925
- queueTilesetFeatureStyle(entity, highPriority) {
16926
- // Add to the style dict if not already there.
16927
- // This helps us know the styling progress for things that have loaded in so far.
16928
- if (!this.styledEntityIds[entity.entityId]) {
16929
- this.styledEntityIds[entity.entityId] = null;
16930
- }
16931
- if (this.styleMappingLoaded || this.styleMappingsLoaded[entity.entityTypeId] == true) {
16932
- const needsData = this.getTilesetFeatureNeedsFullData(entity.entityId, entity.entityTypeId);
16933
- if (needsData) {
16934
- if (!this.recordLoadQueue.find(x => x == entity.entityId)) {
16935
- this.recordLoadQueue.push(entity.entityId);
16936
- }
16937
- }
16938
- else {
16939
- this.styleTilesetFeature(entity);
16940
- }
16941
- }
16942
- else {
16943
- if (highPriority) {
16944
- this.recordCheckQueue.unshift(entity.entityId);
16945
- }
16946
- else {
16947
- this.recordCheckQueue.push(entity.entityId);
16948
- }
16949
- }
16950
- }
16951
- styleTilesetFeature(entity) {
16952
- this.styleTilesetFeatureFullData(entity, null, []);
16953
- }
16954
- styleTilesetFeatureFullData(entity, data, tags) {
16955
- var _a, _b, _c, _d, _e, _f, _g, _h;
16956
- const visual = entity.visual;
16957
- if (!visual || !(visual instanceof Cesium3DTileFeature)) {
16958
- return;
16959
- }
16960
- const style = this.getTilesetFeatureStyle(entity.entityId, entity.entityTypeId);
16961
- const bColor = style && ((_a = style.modelStyle) === null || _a === void 0 ? void 0 : _a.fillColor) ? Calculator.GetColor(style.modelStyle.fillColor, data, tags) : null;
16962
- let cColor = null;
16963
- if (bColor == null) {
16964
- cColor = Color.WHITE;
16965
- }
16966
- else {
16967
- cColor = colorToCColor$3(bColor);
16968
- }
16969
- const override = this.styledEntityIds[entity.entityId] == true;
16970
- CesiumEntityStyler.SetDefaultColor({
16971
- color: cColor,
16972
- entity: visual,
16973
- viewer: this.viewer,
16974
- override: override
16975
- });
16976
- this.styledEntityIds[entity.entityId] = true;
16977
- if (data) {
16978
- this.styledWithAttrEntityIds[entity.entityId] = true;
16979
- }
16980
- else {
16981
- delete this.styledWithAttrEntityIds[entity.entityId];
16982
- }
16983
- this._styleProgressQueue.Call();
16984
- // Since we only need to update it for scenarios right now.
16985
- // We'll avoid doing it if not needed, eg: first render and no scenario (same state as default).
16986
- if (this.shouldUpdateRegoStates && (override || ((_b = data === null || data === void 0 ? void 0 : data.Bruce) === null || _b === void 0 ? void 0 : _b.Scenario) || this.historic)) {
16987
- // Update the Entity's rego state.
16988
- let changed = false;
16989
- // Changed scenario.
16990
- if (entity.scenario != ((_c = data === null || data === void 0 ? void 0 : data.Bruce) === null || _c === void 0 ? void 0 : _c.Scenario)) {
16991
- entity.scenario = (_d = data === null || data === void 0 ? void 0 : data.Bruce) === null || _d === void 0 ? void 0 : _d.Scenario;
16992
- changed = true;
16993
- }
16994
- // Changed historic.
16995
- if ((data && isHistoricMetadataChanged(entity, data)) || (!data && ((_e = entity.historicLayers) === null || _e === void 0 ? void 0 : _e.length) && !((_g = (_f = data.Bruce) === null || _f === void 0 ? void 0 : _f.HistoricLayers) === null || _g === void 0 ? void 0 : _g.length))) {
16996
- entity.historicLayers = (_h = data === null || data === void 0 ? void 0 : data.Bruce) === null || _h === void 0 ? void 0 : _h.HistoricLayers;
16997
- changed = true;
16998
- }
16999
- // Something changed, trigger a rego update.
17000
- // This lets UI know when the rego has changed.
17001
- if (changed) {
17002
- this.register.OnUpdate.Trigger({
17003
- type: VisualsRegister.EVisualUpdateType.Update,
17004
- entityId: entity.entityId,
17005
- rego: entity
17042
+ });
17006
17043
  });
17007
17044
  }
17008
17045
  }
17009
- }
17010
- getTilesetFeatureStyle(entityId, entityTypeId) {
17011
- var _a, _b, _c, _d;
17012
- // Locate what style is applicable to the feature.
17013
- let style = null;
17014
- if (entityTypeId) {
17015
- style = ((_b = (_a = this.styleMapping.find(x => x.EntityTypeID == entityTypeId)) === null || _a === void 0 ? void 0 : _a.style) === null || _b === void 0 ? void 0 : _b.Settings);
17016
- }
17017
- if (!style) {
17018
- style = ((_c = this.fallbackStyle) === null || _c === void 0 ? void 0 : _c.Settings);
17019
- }
17020
- if (!style || !((_d = style === null || style === void 0 ? void 0 : style.modelStyle) === null || _d === void 0 ? void 0 : _d.customize)) {
17021
- return null;
17022
- }
17023
- return style;
17024
- }
17025
- getTilesetFeatureNeedsFullData(entityId, entityTypeId) {
17026
- var _a;
17027
- if (this.historic) {
17028
- // Unfortunately when we are working with historic we have to request the entity.
17029
- // This is because we need to know if a record exists at the point in time.
17030
- return true;
17031
- }
17032
- const style = this.getTilesetFeatureStyle(entityId, entityTypeId);
17033
- if (!style) {
17034
- return false;
17035
- }
17036
- const fill = (_a = style === null || style === void 0 ? void 0 : style.modelStyle) === null || _a === void 0 ? void 0 : _a.fillColor;
17037
- if (!fill || fill.length <= 0) {
17038
- return false;
17039
- }
17040
- return fill[0].type != 0;
17046
+ return getter;
17041
17047
  }
17042
17048
  }
17043
- TilesetRenderEngine.Styler = Styler;
17044
- /**
17045
- * The maximum memory in MB that can be used by all tilesets.
17046
- * This is distributed evenly between all loaded tilesets.
17047
- */
17048
- TilesetRenderEngine.MAX_TILESET_MEMORY = 1024;
17049
+ SharedGetters.Cache = Cache;
17049
17050
  /**
17050
- * Watches tilesets in the viewer.
17051
- * This will regulate their max memory param.
17052
- * As more get watched their memory will be reduced.
17051
+ * To avoid performing multiple expensive queries at the same time,
17052
+ * we'll have the getters work through a queue of requests.
17053
17053
  */
17054
- class MemoryWatcher {
17055
- constructor(viewer) {
17056
- this.watched = [];
17057
- this.viewer = viewer;
17058
- }
17059
- distributeMemory() {
17060
- this.clean();
17061
- // Total 1gb as default.
17062
- // We'll need to allow user to change this somehow.
17063
- const maxMemory = TilesetRenderEngine.MAX_TILESET_MEMORY;
17064
- // Minimum memory in MB per tileset.
17065
- const minMemory = 80;
17066
- const totalPerTileset = Math.max(this.watched.length ? maxMemory / this.watched.length : maxMemory, minMemory);
17067
- this.watched.forEach(x => {
17068
- // Newer Cesium killed this property.
17069
- // TODO: Check if it's needed then.
17070
- x["maximumMemoryUsage"] = totalPerTileset;
17071
- });
17072
- }
17073
- destroy() {
17074
- this.watched = [];
17075
- }
17054
+ let Queue;
17055
+ (function (Queue) {
17056
+ const queue = [];
17057
+ let isProcessing = false;
17076
17058
  /**
17077
- * Remove all dead tilesets.
17059
+ * Called to run a provided function.
17060
+ * When the function is complete, the next function in the queue will be called.
17061
+ * This function will resolve when the provided function is complete.
17062
+ * If the provided function crashes, the error bubbles up to the caller.
17063
+ * @param name
17064
+ * @param call
17078
17065
  */
17079
- clean() {
17080
- // Remove all dead tilesets.
17081
- this.watched = this.watched.filter(x => isAlive$2(this.viewer, x));
17082
- // Check if viewer is destroyed.
17083
- if (!this.viewer || this.viewer.isDestroyed()) {
17084
- this.destroy();
17085
- }
17086
- }
17087
- Watch(tileset) {
17088
- if (!tileset) {
17089
- return;
17090
- }
17091
- if (!tileset[WATCH_KEY]) {
17092
- tileset[WATCH_KEY] = ObjectUtils.UId();
17093
- }
17094
- const index = this.watched.findIndex(x => x[WATCH_KEY] === tileset[WATCH_KEY]);
17095
- if (index >= 0) {
17096
- return;
17097
- }
17098
- this.watched.push(tileset);
17099
- this.distributeMemory();
17066
+ function Run(name, call) {
17067
+ // console.debug(`Queueing: ${name}`);
17068
+ return new Promise((resolve, reject) => {
17069
+ queue.push(async () => {
17070
+ try {
17071
+ // console.debug(`Running: ${name}`);
17072
+ const result = await call();
17073
+ resolve(result);
17074
+ }
17075
+ catch (error) {
17076
+ reject(error);
17077
+ }
17078
+ finally {
17079
+ // Process the next function in the queue.
17080
+ queue.shift();
17081
+ // If there are more functions in the queue, process the next one.
17082
+ if (queue.length > 0) {
17083
+ processNext();
17084
+ }
17085
+ // No more items to process.
17086
+ else {
17087
+ isProcessing = false;
17088
+ }
17089
+ }
17090
+ });
17091
+ if (!isProcessing) {
17092
+ // If not currently processing, start processing the queue.
17093
+ isProcessing = true;
17094
+ processNext();
17095
+ }
17096
+ });
17100
17097
  }
17101
- Unwatch(tileset) {
17102
- if (!tileset) {
17103
- return;
17104
- }
17105
- if (!tileset[WATCH_KEY]) {
17106
- tileset[WATCH_KEY] = ObjectUtils.UId();
17107
- }
17108
- const index = this.watched.findIndex(x => (x === null || x === void 0 ? void 0 : x[WATCH_KEY]) === tileset[WATCH_KEY]);
17109
- if (index > -1) {
17110
- this.watched.splice(index, 1);
17098
+ Queue.Run = Run;
17099
+ async function processNext() {
17100
+ const nextCall = queue[0];
17101
+ if (nextCall) {
17102
+ // Call the next function in the queue.
17103
+ await nextCall();
17111
17104
  }
17112
- this.distributeMemory();
17113
- }
17114
- }
17115
- TilesetRenderEngine.MemoryWatcher = MemoryWatcher;
17116
- function GetMemoryWatcher(viewer) {
17117
- // If viewer is dead return nothing.
17118
- if (!viewer || viewer.isDestroyed()) {
17119
- return null;
17120
- }
17121
- if (!viewer[VIEWER_WATCH_KEY]) {
17122
- viewer[VIEWER_WATCH_KEY] = new MemoryWatcher(viewer);
17123
17105
  }
17124
- return viewer[VIEWER_WATCH_KEY];
17125
- }
17126
- TilesetRenderEngine.GetMemoryWatcher = GetMemoryWatcher;
17127
- })(TilesetRenderEngine || (TilesetRenderEngine = {}));
17106
+ })(Queue = SharedGetters.Queue || (SharedGetters.Queue = {}));
17107
+ })(SharedGetters || (SharedGetters = {}));
17128
17108
 
17129
17109
  /**
17130
17110
  * Manager for rendering CAD tilesets.
@@ -17350,6 +17330,7 @@ var TilesetCadRenderManager;
17350
17330
  if (!content) {
17351
17331
  return;
17352
17332
  }
17333
+ const regosToQueue = new Map();
17353
17334
  for (let i = 0; i < content.featuresLength; i++) {
17354
17335
  const feature = content.getFeature(i);
17355
17336
  let rego = this.mapTilesetFeature(feature, load);
@@ -17358,9 +17339,10 @@ var TilesetCadRenderManager;
17358
17339
  feature.show = true;
17359
17340
  continue;
17360
17341
  }
17361
- if (this.styler) {
17362
- this.styler.QueueEntities([rego]);
17363
- }
17342
+ regosToQueue.set(rego.entityId, rego);
17343
+ }
17344
+ if (this.styler && regosToQueue.size) {
17345
+ this.styler.QueueEntities(Array.from(regosToQueue.values()));
17364
17346
  }
17365
17347
  this.viewer.scene.requestRender();
17366
17348
  }
@@ -18589,13 +18571,17 @@ var TilesetEntitiesRenderManager;
18589
18571
  if (!content) {
18590
18572
  return;
18591
18573
  }
18574
+ const regosToQueue = new Map();
18592
18575
  for (let i = 0; i < content.featuresLength; i++) {
18593
18576
  const feature = content.getFeature(i);
18594
18577
  const rego = this.mapTilesetFeature(feature);
18595
- if ((rego === null || rego === void 0 ? void 0 : rego.entityId) && this.styler) {
18596
- this.styler.QueueEntities([rego]);
18578
+ if (rego === null || rego === void 0 ? void 0 : rego.entityId) {
18579
+ regosToQueue.set(rego.entityId, rego);
18597
18580
  }
18598
18581
  }
18582
+ if (this.styler && regosToQueue.size) {
18583
+ this.styler.QueueEntities(Array.from(regosToQueue.values()));
18584
+ }
18599
18585
  this.viewer.scene.requestRender();
18600
18586
  }
18601
18587
  mapTilesetFeature(feature) {
@@ -19148,13 +19134,17 @@ var TilesetArbRenderManager;
19148
19134
  if (!content) {
19149
19135
  return;
19150
19136
  }
19137
+ const regosToQueue = new Map();
19151
19138
  for (let i = 0; i < content.featuresLength; i++) {
19152
19139
  const feature = content.getFeature(i);
19153
19140
  let rego = this.mapTilesetFeature(feature);
19154
- if ((rego === null || rego === void 0 ? void 0 : rego.entityId) && this.styler) {
19155
- this.styler.QueueEntities([rego]);
19141
+ if (rego === null || rego === void 0 ? void 0 : rego.entityId) {
19142
+ regosToQueue.set(rego.entityId, rego);
19156
19143
  }
19157
19144
  }
19145
+ if (this.styler && regosToQueue.size) {
19146
+ this.styler.QueueEntities(Array.from(regosToQueue.values()));
19147
+ }
19158
19148
  this.viewer.scene.requestRender();
19159
19149
  }
19160
19150
  mapTilesetFeature(feature) {
@@ -30641,7 +30631,7 @@ class WidgetViewBar extends Widget.AWidget {
30641
30631
  }
30642
30632
  }
30643
30633
 
30644
- const VERSION = "5.4.4";
30634
+ const VERSION = "5.4.6";
30645
30635
 
30646
30636
  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 };
30647
30637
  //# sourceMappingURL=bruce-cesium.es5.js.map