bruce-cesium 7.2.3 → 7.2.5

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 (35) hide show
  1. package/dist/bruce-cesium.es5.js +344 -39
  2. package/dist/bruce-cesium.es5.js.map +1 -1
  3. package/dist/bruce-cesium.umd.js +342 -37
  4. package/dist/bruce-cesium.umd.js.map +1 -1
  5. package/dist/lib/bruce-cesium.js +1 -1
  6. package/dist/lib/internal/cesium-utils.js +32 -1
  7. package/dist/lib/internal/cesium-utils.js.map +1 -1
  8. package/dist/lib/rendering/entity-gatherer.js +111 -0
  9. package/dist/lib/rendering/entity-gatherer.js.map +1 -1
  10. package/dist/lib/rendering/entity-render-engine-polygon.js +4 -3
  11. package/dist/lib/rendering/entity-render-engine-polygon.js.map +1 -1
  12. package/dist/lib/rendering/menu-item-manager.js +1 -0
  13. package/dist/lib/rendering/menu-item-manager.js.map +1 -1
  14. package/dist/lib/rendering/render-managers/common/historic-utils.js +91 -24
  15. package/dist/lib/rendering/render-managers/common/historic-utils.js.map +1 -1
  16. package/dist/lib/rendering/render-managers/entities/entities-ids-render-manager.js +1 -0
  17. package/dist/lib/rendering/render-managers/entities/entities-ids-render-manager.js.map +1 -1
  18. package/dist/lib/rendering/render-managers/entities/entities-render-manager.js +1 -0
  19. package/dist/lib/rendering/render-managers/entities/entities-render-manager.js.map +1 -1
  20. package/dist/lib/rendering/render-managers/tilesets/tileset-entities-render-manager.js +61 -3
  21. package/dist/lib/rendering/render-managers/tilesets/tileset-entities-render-manager.js.map +1 -1
  22. package/dist/lib/rendering/texture-frame-series-animator.js +28 -3
  23. package/dist/lib/rendering/texture-frame-series-animator.js.map +1 -1
  24. package/dist/lib/rendering/texture-value-labels.js +6 -2
  25. package/dist/lib/rendering/texture-value-labels.js.map +1 -1
  26. package/dist/lib/rendering/tileset-styler.js +10 -1
  27. package/dist/lib/rendering/tileset-styler.js.map +1 -1
  28. package/dist/types/bruce-cesium.d.ts +1 -1
  29. package/dist/types/internal/cesium-utils.d.ts +9 -0
  30. package/dist/types/rendering/entity-gatherer.d.ts +23 -0
  31. package/dist/types/rendering/render-managers/common/historic-utils.d.ts +30 -5
  32. package/dist/types/rendering/render-managers/tilesets/tileset-entities-render-manager.d.ts +5 -0
  33. package/dist/types/rendering/texture-frame-series-animator.d.ts +6 -0
  34. package/dist/types/rendering/tileset-styler.d.ts +3 -0
  35. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import * as Cesium from 'cesium';
2
- import { Cartographic, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, Math as Math$1, Cartesian3, JulianDate, Quaternion, Transforms, HeadingPitchRoll, Matrix4, ShaderSource, Ellipsoid, Geometry, GeometryAttribute, ComponentDatatype, PrimitiveType, BoundingSphere, GeometryPipeline, sampleTerrain, Texture, PixelFormat, Sampler, TextureWrap, TextureMinificationFilter, TextureMagnificationFilter, VertexArray, BufferUsage, ShaderProgram, RenderState, BlendingState, DrawCommand, Pass, Cartesian2, HeightReference, ColorBlendMode, ShadowMode, ClassificationType, Model, DistanceDisplayCondition, ArcType, CornerType, ConstantPositionProperty, ImageMaterialProperty, PolygonHierarchy, PolylineGraphics, Rectangle, HorizontalOrigin, VerticalOrigin, SceneTransforms, NearFarScalar, Matrix3, KmlDataSource, GeoJsonDataSource, SceneMode, Cesium3DTileStyle, HeadingPitchRange, Ion, IonResource, Cesium3DTileColorBlendMode, Cesium3DTileset, Intersect, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, OrthographicFrustum, EasingFunction, EllipsoidGeodesic, sampleTerrainMostDetailed, defined, PolygonPipeline, CustomDataSource, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, GeometryInstance, CesiumInspector, ClockRange, DynamicEnvironmentMapManager, ScreenSpaceEventHandler, ScreenSpaceEventType, Fullscreen, CzmlDataSource } from 'cesium';
3
- import { Cartes, Entity as Entity$1, ProjectViewTile, ENVIRONMENT, Calculator, ClientFile, EntityLod, EntityTag, EntityType, LRUCache, ObjectUtils, Style, Carto, Geometry as Geometry$1, Bounds, Color as Color$1, Api, BruceEvent, EntityCoords, MenuItem, Tileset, ProjectView, ProjectViewBookmark, ZoomControl, DelayQueue, DataLab, AccountConcept, RecordChangeFeed, BruceApi, EntityRelation, EntitySource, ProgramKey, ProjectViewLegacyTile, Camera, Account, AccountLimits, Session, UrlUtils, EntityAttachment, EntityAttachmentType, EntityAttribute, MathUtils, ProjectViewBookmarkGroup, EntityRelationType, EntityHistoricData } from 'bruce-models';
2
+ import { Cartographic, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, Math as Math$1, Cartesian3, JulianDate, Quaternion, Transforms, HeadingPitchRoll, Matrix4, ShaderSource, Ellipsoid, Geometry, GeometryAttribute, ComponentDatatype, PrimitiveType, BoundingSphere, GeometryPipeline, sampleTerrain, Texture, PixelFormat, Sampler, TextureWrap, TextureMinificationFilter, TextureMagnificationFilter, VertexArray, BufferUsage, ShaderProgram, RenderState, BlendingState, DrawCommand, Pass, Cartesian2, DistanceDisplayCondition, HeightReference, HorizontalOrigin, VerticalOrigin, ClassificationType, ConstantPositionProperty, ArcType, CornerType, ShadowMode, ColorBlendMode, Model, SceneTransforms, ImageMaterialProperty, PolygonHierarchy, PolylineGraphics, Rectangle, NearFarScalar, Matrix3, KmlDataSource, GeoJsonDataSource, SceneMode, Cesium3DTileStyle, HeadingPitchRange, Ion, IonResource, Cesium3DTileset, Cesium3DTileColorBlendMode, OrthographicFrustum, EasingFunction, Intersect, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, EllipsoidGeodesic, sampleTerrainMostDetailed, defined, PolygonPipeline, CesiumInspector, ClockRange, DynamicEnvironmentMapManager, GeometryInstance, CustomDataSource, ScreenSpaceEventHandler, ScreenSpaceEventType, Fullscreen, CzmlDataSource } from 'cesium';
3
+ import { Cartes, Entity as Entity$1, ProjectViewTile, ENVIRONMENT, Api, Calculator, ClientFile, EntityTag, EntityType, LRUCache, ObjectUtils, Style, Carto, Geometry as Geometry$1, EntityLod, Color as Color$1, Bounds, MenuItem, ProjectView, ProjectViewBookmark, Tileset, ZoomControl, BruceEvent, EntityCoords, DelayQueue, DataLab, BruceApi, EntityRelation, AccountConcept, RecordChangeFeed, EntitySource, Camera, ProgramKey, ProjectViewLegacyTile, Account, AccountLimits, Session, UrlUtils, ProjectViewBookmarkGroup, EntityAttachment, EntityAttachmentType, EntityAttribute, MathUtils, EntityRelationType, EntityHistoricData } from 'bruce-models';
4
4
 
5
5
  /**
6
6
  * Ensures a number is returned from a given value.
@@ -2889,6 +2889,36 @@ function SetCPropIfChanged(params) {
2889
2889
  function ColorToCColor(color) {
2890
2890
  return new Color(color.red ? color.red / 255 : 0, color.green ? color.green / 255 : 0, color.blue ? color.blue / 255 : 0, color.alpha);
2891
2891
  }
2892
+ const degreesCache = new WeakMap();
2893
+ /**
2894
+ * Returns a world position's latitude and longitude in degrees, or null when it has none.
2895
+ * @param pos
2896
+ */
2897
+ function CartesianToDegrees(pos) {
2898
+ if (!pos) {
2899
+ return null;
2900
+ }
2901
+ const cached = degreesCache.get(pos);
2902
+ if (cached) {
2903
+ return cached;
2904
+ }
2905
+ let carto;
2906
+ try {
2907
+ carto = Cartographic.fromCartesian(pos);
2908
+ }
2909
+ catch {
2910
+ return null;
2911
+ }
2912
+ if (!carto) {
2913
+ return null;
2914
+ }
2915
+ const degrees = {
2916
+ lat: Math$1.toDegrees(carto.latitude),
2917
+ lon: Math$1.toDegrees(carto.longitude)
2918
+ };
2919
+ degreesCache.set(pos, degrees);
2920
+ return degrees;
2921
+ }
2892
2922
  /**
2893
2923
  * Removes duplicate points in a row and returns a new array.
2894
2924
  * Passed array is not mutated.
@@ -10758,41 +10788,107 @@ var HistoricFetchOrder;
10758
10788
  return Math.round(minMs + ((maxMs - minMs) * progress));
10759
10789
  }
10760
10790
  HistoricFetchOrder.IntervalFor = IntervalFor;
10791
+ // Upper bounds in metres for the distance bands, nearest first.
10792
+ HistoricFetchOrder.DEFAULT_BANDS = [500, 2000, 10000, 50000];
10761
10793
  /**
10762
- * Returns the Entities grouped into bands by distance from the camera, nearest band first.
10763
- *
10764
- * Bands rather than a true sort, so a small camera move does not reshuffle the whole queue and
10765
- * restart it. What the user is looking at resolves first either way.
10794
+ * Resolves what to order by from the view monitor, falling back to the camera itself.
10766
10795
  */
10767
- function ByCameraDistance(params) {
10796
+ function FocusFrom(params) {
10768
10797
  var _a;
10769
- const { viewer, entities } = params;
10770
- const bands = params.bands || [500, 2000, 10000, 50000];
10771
- if (!(entities === null || entities === void 0 ? void 0 : entities.length)) {
10798
+ const { viewer, monitor } = params;
10799
+ let origin = null;
10800
+ let bounds = null;
10801
+ try {
10802
+ // Null whenever the view has no ground intersection, eg. a camera aimed at the sky.
10803
+ const target = monitor === null || monitor === void 0 ? void 0 : monitor.GetTarget();
10804
+ if (target && isFinite(target.latitude) && isFinite(target.longitude)) {
10805
+ origin = Cartesian3.fromDegrees(target.longitude, target.latitude);
10806
+ }
10807
+ bounds = (monitor === null || monitor === void 0 ? void 0 : monitor.GetBounds()) || null;
10808
+ }
10809
+ catch {
10810
+ origin = null;
10811
+ bounds = null;
10812
+ }
10813
+ if (!origin) {
10814
+ origin = (_a = viewer === null || viewer === void 0 ? void 0 : viewer.camera) === null || _a === void 0 ? void 0 : _a.position;
10815
+ }
10816
+ if (!origin) {
10817
+ return null;
10818
+ }
10819
+ return bounds ? { origin, bounds } : { origin };
10820
+ }
10821
+ HistoricFetchOrder.FocusFrom = FocusFrom;
10822
+ /**
10823
+ * Returns the Entities grouped nearest first, on screen ahead of off screen.
10824
+ */
10825
+ function ByCameraDistance(params) {
10826
+ const { viewer, entities, monitor } = params;
10827
+ return ByCameraDistanceOf({
10828
+ focus: FocusFrom({ viewer, monitor }),
10829
+ items: entities,
10830
+ posOf: (entity) => SafePos(entity, viewer),
10831
+ bands: params.bands
10832
+ });
10833
+ }
10834
+ HistoricFetchOrder.ByCameraDistance = ByCameraDistance;
10835
+ /**
10836
+ * Bands anything that can name its own world position, on the same bounds as the Entity form.
10837
+ */
10838
+ function ByCameraDistanceOf(params) {
10839
+ const { focus, items, posOf } = params;
10840
+ const bands = params.bands || HistoricFetchOrder.DEFAULT_BANDS;
10841
+ if (!(items === null || items === void 0 ? void 0 : items.length)) {
10772
10842
  return [];
10773
10843
  }
10774
- const cameraPos = (_a = viewer === null || viewer === void 0 ? void 0 : viewer.camera) === null || _a === void 0 ? void 0 : _a.position;
10775
- if (!cameraPos) {
10776
- return [entities];
10844
+ if (!(focus === null || focus === void 0 ? void 0 : focus.origin)) {
10845
+ return [items];
10777
10846
  }
10778
- // One band past the named bounds, for everything beyond the last of them.
10779
- const banded = bands.map(() => []).concat([[]]);
10780
- for (const entity of entities) {
10781
- const pos = SafePos(entity, viewer);
10847
+ // One band past the named bounds, for everything beyond the last of them. Then the same
10848
+ // again for what is off screen, so nothing visible waits behind something that is not.
10849
+ const perTier = bands.length + 1;
10850
+ const offScreenTier = focus.bounds ? perTier : 0;
10851
+ const placeless = offScreenTier + perTier;
10852
+ const grouped = [];
10853
+ for (let i = 0; i <= placeless; i++) {
10854
+ grouped.push([]);
10855
+ }
10856
+ for (const item of items) {
10857
+ // An item with no position falls to the last group rather than blocking the near ones.
10858
+ const pos = posOf(item);
10782
10859
  if (!pos) {
10783
- banded[banded.length - 1].push(entity);
10860
+ grouped[placeless].push(item);
10784
10861
  continue;
10785
10862
  }
10786
- const distance = Cartesian3.distance(cameraPos, pos);
10787
- let index = bands.findIndex(bound => distance <= bound);
10788
- if (index < 0) {
10789
- index = banded.length - 1;
10863
+ const distance = Cartesian3.distance(focus.origin, pos);
10864
+ let band = bands.findIndex(bound => distance <= bound);
10865
+ if (band < 0) {
10866
+ band = bands.length;
10790
10867
  }
10791
- banded[index].push(entity);
10868
+ grouped[(focus.bounds && !InBounds(pos, focus.bounds)) ? offScreenTier + band : band].push(item);
10792
10869
  }
10793
- return banded.filter(band => band.length > 0);
10870
+ return grouped.filter(group => group.length > 0);
10794
10871
  }
10795
- HistoricFetchOrder.ByCameraDistance = ByCameraDistance;
10872
+ HistoricFetchOrder.ByCameraDistanceOf = ByCameraDistanceOf;
10873
+ /*
10874
+ * Returns whether a world position sits inside a ground rectangle given in degrees.
10875
+ */
10876
+ function InBounds(pos, bounds) {
10877
+ const degrees = CartesianToDegrees(pos);
10878
+ if (!degrees) {
10879
+ return false;
10880
+ }
10881
+ const { lat, lon } = degrees;
10882
+ if (lat < bounds.south || lat > bounds.north) {
10883
+ return false;
10884
+ }
10885
+ // A view straddling the antimeridian reports west east of east, so the span is the outside.
10886
+ if (bounds.west > bounds.east) {
10887
+ return lon >= bounds.west || lon <= bounds.east;
10888
+ }
10889
+ return lon >= bounds.west && lon <= bounds.east;
10890
+ }
10891
+ HistoricFetchOrder.InBounds = InBounds;
10796
10892
  /*
10797
10893
  * Returns an Entity's world position, or null when it has none to place it by.
10798
10894
  */
@@ -11110,7 +11206,7 @@ class HistoricGatherer {
11110
11206
  }
11111
11207
  if (plan.detail) {
11112
11208
  const bands = entities.length > HistoricFetchOrder.SMALL_SET
11113
- ? HistoricFetchOrder.ByCameraDistance({ viewer, entities })
11209
+ ? HistoricFetchOrder.ByCameraDistance({ viewer, entities, monitor: this.params.monitor })
11114
11210
  : [entities];
11115
11211
  for (const range of this.cache.MissingRanges(plan.detail.start, plan.detail.stop)) {
11116
11212
  if (this.disposed) {
@@ -11279,6 +11375,7 @@ var EntitiesRenderManager;
11279
11375
  this.onSeriesDiscovered = onSeriesDiscovered;
11280
11376
  this.historicGatherer = new HistoricGatherer({
11281
11377
  viewer: this.viewer,
11378
+ monitor: this.monitor,
11282
11379
  getApi: () => this.apiGetter.getApi(),
11283
11380
  getAttrKey: () => { var _a; return (_a = this.item.BruceEntity) === null || _a === void 0 ? void 0 : _a.historicAttrKey; },
11284
11381
  // A second either side, to take in records sitting exactly on the boundary.
@@ -13395,6 +13492,7 @@ var EntitiesIdsRenderManager;
13395
13492
  this.visualsManager = visualsManager;
13396
13493
  this.historicGatherer = new HistoricGatherer({
13397
13494
  viewer: this.viewer,
13495
+ monitor: this.monitor,
13398
13496
  getApi: () => this.apiGetter.getApi(),
13399
13497
  getAttrKey: () => { var _a; return (_a = this.item.BruceEntity) === null || _a === void 0 ? void 0 : _a.historicAttrKey; },
13400
13498
  // Padding either side of a requested range, for clock desync between us and the server.
@@ -14033,6 +14131,16 @@ class EntityGatherer {
14033
14131
  this.lastFoundDateTimes = new Map();
14034
14132
  // ID of Entity IDs that need to be requested.
14035
14133
  this.eIdQueue = [];
14134
+ // Resolves where an Entity sits without reading its record, when the caller can say.
14135
+ // Unset leaves the queue in insertion order, which is what sources that bake no coordinates get.
14136
+ this.positionOf = null;
14137
+ // How many IDs at the front of the queue were explicitly asked for first.
14138
+ // Camera ordering leaves those alone, since an explicit re-render outranks proximity.
14139
+ this.priorityCount = 0;
14140
+ // View focus the queue was last banded from.
14141
+ this.lastOrderFocus = null;
14142
+ // Reports what the camera is aimed at, rather than only where it sits.
14143
+ this.monitor = null;
14036
14144
  // All IDs that have ever been queued.
14037
14145
  // When nothing is in queue, we'll re-request the existing IDs based on timeline changes.
14038
14146
  this.allEIds = new Set();
@@ -14132,6 +14240,8 @@ class EntityGatherer {
14132
14240
  return;
14133
14241
  }
14134
14242
  this.eIdQueue = [];
14243
+ this.priorityCount = 0;
14244
+ this.lastOrderFocus = null;
14135
14245
  this.allEIds.clear();
14136
14246
  this.lastFoundDateTimes.clear();
14137
14247
  this.lastDateTime = null;
@@ -14141,6 +14251,89 @@ class EntityGatherer {
14141
14251
  return;
14142
14252
  }
14143
14253
  this.eIdQueue = Array.from(this.allEIds);
14254
+ this.priorityCount = 0;
14255
+ this.lastOrderFocus = null;
14256
+ }
14257
+ /**
14258
+ * Supplies a lookup from Entity ID to world position, letting the queue resolve what the camera
14259
+ * is nearest first. Sources that cannot answer should not call this, and are served in order.
14260
+ */
14261
+ SetPositionSource(positionOf) {
14262
+ this.positionOf = positionOf;
14263
+ this.lastOrderFocus = null;
14264
+ }
14265
+ /**
14266
+ * Supplies the view monitor, so the order follows the ground the camera is aimed at and prefers
14267
+ * what is on screen. Without one the ordering falls back to distance from the camera itself.
14268
+ */
14269
+ SetViewMonitor(monitor) {
14270
+ this.monitor = monitor;
14271
+ this.lastOrderFocus = null;
14272
+ }
14273
+ /**
14274
+ * Re-bands the pending queue so the nearest Entities are requested first.
14275
+ */
14276
+ orderQueueByCamera() {
14277
+ if (!this.positionOf || this.eIdQueue.length < EntityGatherer.MIN_ORDER_QUEUE) {
14278
+ return;
14279
+ }
14280
+ const focus = HistoricFetchOrder.FocusFrom({ viewer: this.viewer, monitor: this.monitor });
14281
+ if (!focus) {
14282
+ return;
14283
+ }
14284
+ // Re-banding on every batch would cost a full pass per request for no change in the answer.
14285
+ // Measured on the focus, so panning across the ground counts even when the eye barely moves.
14286
+ if (this.lastOrderFocus && !EntityGatherer.boundsChanged(this.lastOrderFocus, focus) &&
14287
+ Cartesian3.distance(this.lastOrderFocus.origin, focus.origin) < EntityGatherer.REORDER_MOVE_METRES) {
14288
+ return;
14289
+ }
14290
+ this.lastOrderFocus = focus;
14291
+ const priority = this.priorityCount > 0 ? this.eIdQueue.slice(0, this.priorityCount) : [];
14292
+ const rest = this.priorityCount > 0 ? this.eIdQueue.slice(this.priorityCount) : this.eIdQueue;
14293
+ if (!rest.length) {
14294
+ return;
14295
+ }
14296
+ this.eIdQueue = priority.concat(this.bandIds(rest, focus));
14297
+ }
14298
+ /*
14299
+ * Returns whether the visible rectangle differs enough to change what counts as on screen.
14300
+ */
14301
+ static boundsChanged(a, b) {
14302
+ if (!a.bounds || !b.bounds) {
14303
+ return Boolean(a.bounds) !== Boolean(b.bounds);
14304
+ }
14305
+ return (a.bounds.north !== b.bounds.north ||
14306
+ a.bounds.south !== b.bounds.south ||
14307
+ a.bounds.east !== b.bounds.east ||
14308
+ a.bounds.west !== b.bounds.west);
14309
+ }
14310
+ /*
14311
+ * Returns the IDs reordered nearest band first, or unchanged when there is nothing to order by.
14312
+ */
14313
+ bandIds(entityIds, focus) {
14314
+ const from = focus || HistoricFetchOrder.FocusFrom({ viewer: this.viewer, monitor: this.monitor });
14315
+ if (!this.positionOf || !from || !entityIds.length) {
14316
+ return entityIds;
14317
+ }
14318
+ const banded = HistoricFetchOrder.ByCameraDistanceOf({
14319
+ focus: from,
14320
+ items: entityIds,
14321
+ posOf: (entityId) => {
14322
+ try {
14323
+ return this.positionOf(entityId);
14324
+ }
14325
+ catch {
14326
+ return null;
14327
+ }
14328
+ }
14329
+ });
14330
+ const ordered = [];
14331
+ for (const band of banded) {
14332
+ for (const entityId of band) {
14333
+ ordered.push(entityId);
14334
+ }
14335
+ }
14336
+ return ordered;
14144
14337
  }
14145
14338
  Queue(entityIds, topOfQueue = false) {
14146
14339
  if (this.disposed || !(entityIds === null || entityIds === void 0 ? void 0 : entityIds.length)) {
@@ -14154,8 +14347,12 @@ class EntityGatherer {
14154
14347
  // If we want it to be at the top of the queue, remove it from the queue first.
14155
14348
  if (index !== -1) {
14156
14349
  this.eIdQueue.splice(index, 1);
14350
+ if (index < this.priorityCount) {
14351
+ this.priorityCount -= 1;
14352
+ }
14157
14353
  }
14158
14354
  this.eIdQueue.unshift(eId);
14355
+ this.priorityCount += 1;
14159
14356
  changes += 1;
14160
14357
  }
14161
14358
  else if (index === -1) {
@@ -14306,7 +14503,9 @@ class EntityGatherer {
14306
14503
  if (this.historic && this.lastDateTime !== rTime) {
14307
14504
  break;
14308
14505
  }
14506
+ this.orderQueueByCamera();
14309
14507
  const batch = this.eIdQueue.splice(0, QUEUE_BATCH_SIZE);
14508
+ this.priorityCount = Math.max(0, this.priorityCount - batch.length);
14310
14509
  const { entities } = await Entity$1.GetListByIds({
14311
14510
  entityIds: batch,
14312
14511
  historicPoint: this.historic ? rTime : null,
@@ -14339,6 +14538,9 @@ class EntityGatherer {
14339
14538
  allEIds = allEIds.filter((eId) => requestedIds.indexOf(eId) === -1);
14340
14539
  }
14341
14540
  if (allEIds.length) {
14541
+ // A historic tick re-reads the whole set, so the near ones are worth resolving
14542
+ // first here even more than in the queue above.
14543
+ allEIds = this.bandIds(allEIds);
14342
14544
  while (allEIds.length && !this.disposed && !this.eIdQueue.length) {
14343
14545
  if (this.historic && this.lastDateTime !== rTime) {
14344
14546
  break;
@@ -14372,6 +14574,11 @@ class EntityGatherer {
14372
14574
  })();
14373
14575
  }
14374
14576
  }
14577
+ // Below this the whole queue clears in one or two requests, so ordering it buys nothing.
14578
+ EntityGatherer.MIN_ORDER_QUEUE = 500;
14579
+ // How far the point being looked at has to move before the queue is worth re-banding, in metres.
14580
+ // Under the nearest band's width, so a move that could change what is nearest still triggers one.
14581
+ EntityGatherer.REORDER_MOVE_METRES = 250;
14375
14582
 
14376
14583
  // ND-1641. BOOKMARKS - (DEMO) View does not match bookmark.
14377
14584
  // We have some evil hard-coded style mappings that need to be fixed.
@@ -14495,7 +14702,8 @@ class TilesetStyler {
14495
14702
  }
14496
14703
  Init(params) {
14497
14704
  var _a, _b;
14498
- let { viewer, api, cTileset, fallbackStyleId, styleMapping, expandSources, menuItemId, register, scenario, historic, applyStyles } = params;
14705
+ let { viewer, api, cTileset, fallbackStyleId, styleMapping, expandSources, menuItemId, register, scenario, historic, applyStyles, monitor } = params;
14706
+ this.monitor = monitor;
14499
14707
  this.viewer = viewer;
14500
14708
  this.api = api;
14501
14709
  this.cTileset = cTileset;
@@ -14551,6 +14759,14 @@ class TilesetStyler {
14551
14759
  }
14552
14760
  }
14553
14761
  });
14762
+ // Only Entities Sets bake coordinates into their features, so for any other source this
14763
+ // resolves nothing and the gather stays in insertion order.
14764
+ this.entityGatherer.SetViewMonitor(this.monitor);
14765
+ this.entityGatherer.SetPositionSource((entityId) => {
14766
+ var _a;
14767
+ const visual = (_a = this.getEntityRego(entityId)) === null || _a === void 0 ? void 0 : _a.visual;
14768
+ return (visual === null || visual === void 0 ? void 0 : visual._bakedPos) || null;
14769
+ });
14554
14770
  this.loaded = true;
14555
14771
  this.loadStyles();
14556
14772
  const feed = (_b = this.api) === null || _b === void 0 ? void 0 : _b.RecordChangeFeed;
@@ -19774,7 +19990,8 @@ var TilesetEntitiesRenderManager;
19774
19990
  // Saves having to do a case-insensitive lookup every time.
19775
19991
  this.featurePropCache = new Map();
19776
19992
  this.featurePropsChecked = 0;
19777
- const { viewer, register: visualsManager, getters: apiGetter, item, initQueue } = params;
19993
+ const { viewer, register: visualsManager, getters: apiGetter, item, initQueue, monitor } = params;
19994
+ this.monitor = monitor;
19778
19995
  this.viewer = viewer;
19779
19996
  this.getters = apiGetter;
19780
19997
  this.item = item;
@@ -19883,7 +20100,8 @@ var TilesetEntitiesRenderManager;
19883
20100
  menuItemId: this.item.id,
19884
20101
  register: this.visualsManager,
19885
20102
  historic: Manager.IsHistoric(this.item),
19886
- applyStyles: Boolean(this.item.ApplyStyles)
20103
+ applyStyles: Boolean(this.item.ApplyStyles),
20104
+ monitor: this.monitor
19887
20105
  });
19888
20106
  }
19889
20107
  this.onCTilesetLoad();
@@ -20043,6 +20261,16 @@ var TilesetEntitiesRenderManager;
20043
20261
  else if (lowered === "internalid") {
20044
20262
  this.featurePropCache.set("internalId", prop);
20045
20263
  }
20264
+ else if (lowered === "latitude") {
20265
+ this.featurePropCache.set("latitude", prop);
20266
+ }
20267
+ else if (lowered === "longitude") {
20268
+ this.featurePropCache.set("longitude", prop);
20269
+ }
20270
+ // b3dm names the baked height TerrainHeight, the glTF metadata table calls it Altitude.
20271
+ else if (lowered === "terrainheight" || lowered === "altitude") {
20272
+ this.featurePropCache.set("height", prop);
20273
+ }
20046
20274
  }
20047
20275
  }
20048
20276
  Dispose() {
@@ -20197,7 +20425,8 @@ var TilesetEntitiesRenderManager;
20197
20425
  menuItemId: this.item.id,
20198
20426
  register: this.visualsManager,
20199
20427
  historic: Manager.IsHistoric(this.item),
20200
- applyStyles: Boolean(this.item.ApplyStyles)
20428
+ applyStyles: Boolean(this.item.ApplyStyles),
20429
+ monitor: this.monitor
20201
20430
  });
20202
20431
  this.queueHandoffRestyle(this.visualsManager.GetRegos({ menuItemId: this.item.id }));
20203
20432
  }
@@ -20240,6 +20469,45 @@ var TilesetEntitiesRenderManager;
20240
20469
  }
20241
20470
  }, 10);
20242
20471
  }
20472
+ /*
20473
+ * Reads a numeric feature property, returning null for anything that is not one.
20474
+ */
20475
+ static readNumber(feature, prop) {
20476
+ const raw = feature.getProperty(prop);
20477
+ // Coercing straight to a number would turn null, undefined and "" into a valid zero,
20478
+ // which for a coordinate is a real place off the coast of Africa.
20479
+ if (raw == null || raw === "" || typeof raw === "boolean") {
20480
+ return null;
20481
+ }
20482
+ const value = +raw;
20483
+ return isFinite(value) ? value : null;
20484
+ }
20485
+ /*
20486
+ * Returns the world position an Entities Set bakes into each feature, or null when it has none.
20487
+ */
20488
+ readBakedPos(feature) {
20489
+ const latProp = this.featurePropCache.get("latitude");
20490
+ const lonProp = this.featurePropCache.get("longitude");
20491
+ if (!latProp || !lonProp) {
20492
+ return null;
20493
+ }
20494
+ try {
20495
+ const lat = Manager.readNumber(feature, latProp);
20496
+ const lon = Manager.readNumber(feature, lonProp);
20497
+ // Degrees, per the batch table the generator writes. The range check doubles as a
20498
+ // guard against a source that bakes radians, which would otherwise read as valid.
20499
+ if (lat == null || lon == null || Math.abs(lat) > 90 || Math.abs(lon) > 180) {
20500
+ return null;
20501
+ }
20502
+ // A height that will not read is worth losing, the position it belongs to is not.
20503
+ const heightProp = this.featurePropCache.get("height");
20504
+ const height = heightProp ? Manager.readNumber(feature, heightProp) : 0;
20505
+ return Cartesian3.fromDegrees(lon, lat, height !== null && height !== void 0 ? height : 0);
20506
+ }
20507
+ catch {
20508
+ return null;
20509
+ }
20510
+ }
20243
20511
  mapTilesetFeature(feature) {
20244
20512
  var _a, _b, _c;
20245
20513
  this.evaluateFeatureProps(feature);
@@ -20320,6 +20588,12 @@ var TilesetEntitiesRenderManager;
20320
20588
  rego.internalId = parsed;
20321
20589
  }
20322
20590
  }
20591
+ // Stamped on the feature the properties came from, so a sibling keeps its own position
20592
+ // and nothing has to be cleaned up when the tile unloads.
20593
+ const bakedPos = this.readBakedPos(feature);
20594
+ if (bakedPos) {
20595
+ feature._bakedPos = bakedPos;
20596
+ }
20323
20597
  this.loadedCesiumEntities[rego.entityId] = rego;
20324
20598
  this.visualsManager.AddRego({
20325
20599
  rego,
@@ -24205,6 +24479,7 @@ var MenuItemManager;
24205
24479
  viewer: this.viewer,
24206
24480
  register: this.visualsRegister,
24207
24481
  getters: this.getters,
24482
+ monitor: this.sharedMonitor,
24208
24483
  item: params.item,
24209
24484
  initQueue: this.tilesetInitQueue
24210
24485
  });
@@ -40562,6 +40837,14 @@ var TextureFrameSeriesAnimator;
40562
40837
  return { scale, offset: SourceValueMin - ValueMin * scale };
40563
40838
  }
40564
40839
  TextureFrameSeriesAnimator.SourceMap = SourceMap;
40840
+ /**
40841
+ * Whether anything was applied to the values on the way out of the source.
40842
+ * @param metadata the archive's Data.generation
40843
+ */
40844
+ function IsTransformed(metadata) {
40845
+ return Boolean(metadata && metadata.Transform);
40846
+ }
40847
+ TextureFrameSeriesAnimator.IsTransformed = IsTransformed;
40565
40848
  /**
40566
40849
  * A delivered value in the source's own terms, or unchanged when it cannot be turned back.
40567
40850
  * @param map from SourceMap(), null when the archive does not say enough
@@ -40991,9 +41274,11 @@ var TextureFrameSeriesAnimator;
40991
41274
  return;
40992
41275
  }
40993
41276
  let next = -1;
41277
+ let isTarget = false;
40994
41278
  if (this.pendingIndex !== -1) {
40995
41279
  next = this.pendingIndex;
40996
41280
  this.pendingIndex = -1;
41281
+ isTarget = true;
40997
41282
  }
40998
41283
  else {
40999
41284
  next = this.nearestUncachedFrame();
@@ -41002,7 +41287,7 @@ var TextureFrameSeriesAnimator;
41002
41287
  return;
41003
41288
  }
41004
41289
  this.inFlightIndex = next;
41005
- this.fetchAndTint(next)
41290
+ this.fetchAndTint(next, isTarget)
41006
41291
  .catch(() => {
41007
41292
  // Eating it.
41008
41293
  })
@@ -41013,7 +41298,7 @@ var TextureFrameSeriesAnimator;
41013
41298
  }
41014
41299
  });
41015
41300
  }
41016
- async fetchAndTint(idx) {
41301
+ async fetchAndTint(idx, isTarget = true) {
41017
41302
  const tiles = this.tiles;
41018
41303
  // Every tile's frame for one timestep sits contiguously, because the generator writes
41019
41304
  // frame major. So a tiled frame is still ONE range request, not one per tile.
@@ -41045,10 +41330,25 @@ var TextureFrameSeriesAnimator;
41045
41330
  this.frameCache[idx] = decoded.pixels;
41046
41331
  this.frameDims[idx] = { width: decoded.width, height: decoded.height };
41047
41332
  this.valueCache[idx] = decoded.valuePixels || null;
41048
- if (idx === this.currentFrameIndex) {
41333
+ if (this.shouldPresent(idx, isTarget)) {
41049
41334
  this.beginCrossfadeTo(idx);
41050
41335
  }
41051
41336
  }
41337
+ /*
41338
+ * Whether a frame that has just decoded is worth putting on screen.
41339
+ */
41340
+ shouldPresent(idx, isTarget) {
41341
+ if (idx === this.currentFrameIndex) {
41342
+ return true;
41343
+ }
41344
+ // A prefetch is for a frame nobody asked to see, so it stays in the cache.
41345
+ if (!isTarget) {
41346
+ return false;
41347
+ }
41348
+ // Dragging the timeline retargets faster than a frame can be fetched, so the frame that
41349
+ // lands is rarely the one now under the playhead.
41350
+ return !this.frameCache[this.currentFrameIndex];
41351
+ }
41052
41352
  /*
41053
41353
  * Draws every tile of one frame into a single raster covering the drape extent.
41054
41354
  */
@@ -41537,6 +41837,9 @@ var TextureValueLabels;
41537
41837
  : TextureFrameSeriesAnimator.SourceMap(metadata);
41538
41838
  const inSource = (value) => TextureFrameSeriesAnimator.ToSource(toSource, value);
41539
41839
  const reading = { value: inSource(lo + normalised * (hi - lo)) };
41840
+ if (TextureFrameSeriesAnimator.IsTransformed(metadata)) {
41841
+ return reading;
41842
+ }
41540
41843
  const eLo = metadata && metadata.ExtremesValueMin;
41541
41844
  const eHi = metadata && metadata.ExtremesValueMax;
41542
41845
  if (typeof eLo === "number" && typeof eHi === "number") {
@@ -42021,10 +42324,11 @@ var TextureValueLabels;
42021
42324
  sample.push(valueLine("-000.00", LabelUnits(this.metadata)));
42022
42325
  }
42023
42326
  const range = [];
42024
- if (parts.indexOf("floor") >= 0) {
42327
+ const showsRange = !TextureFrameSeriesAnimator.IsTransformed(this.metadata);
42328
+ if (showsRange && parts.indexOf("floor") >= 0) {
42025
42329
  range.push("min: -000.00");
42026
42330
  }
42027
- if (parts.indexOf("ceiling") >= 0) {
42331
+ if (showsRange && parts.indexOf("ceiling") >= 0) {
42028
42332
  range.push("max: -000.00");
42029
42333
  }
42030
42334
  if (range.length) {
@@ -43954,9 +44258,10 @@ function syncTextureValueLabels(params) {
43954
44258
  let lastVersion = -1;
43955
44259
  let appliedFloor = null;
43956
44260
  let appliedCeiling = null;
43957
- // The extremes are what turn an anomaly frame back into a reading, so they are worth two extra
43958
- // Range GETs here even though nothing else in the render path wants them.
43959
- animator.EnsureExtremes();
44261
+ // Two extra Range GETs, and worth it only because a label draws the cell's own range from them.
44262
+ if (!TextureFrameSeriesAnimator.IsTransformed(metadata)) {
44263
+ animator.EnsureExtremes();
44264
+ }
43960
44265
  const remove = viewer.scene.preRender.addEventListener(() => {
43961
44266
  if (animator.IsDisposed()) {
43962
44267
  return;
@@ -45606,7 +45911,7 @@ var StyleUtils;
45606
45911
  StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
45607
45912
  })(StyleUtils || (StyleUtils = {}));
45608
45913
 
45609
- const VERSION = "7.2.3";
45914
+ const VERSION = "7.2.5";
45610
45915
  /**
45611
45916
  * Updates the environment instance used by bruce-cesium to one specified.
45612
45917
  * This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.