bruce-cesium 7.4.4 → 7.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 (36) hide show
  1. package/dist/bruce-cesium.es5.js +307 -36
  2. package/dist/bruce-cesium.es5.js.map +1 -1
  3. package/dist/bruce-cesium.umd.js +305 -34
  4. package/dist/bruce-cesium.umd.js.map +1 -1
  5. package/dist/lib/bruce-cesium.js +1 -1
  6. package/dist/lib/rendering/cesium-animated-property.js +16 -1
  7. package/dist/lib/rendering/cesium-animated-property.js.map +1 -1
  8. package/dist/lib/rendering/entity-render-engine-model3d.js +2 -1
  9. package/dist/lib/rendering/entity-render-engine-model3d.js.map +1 -1
  10. package/dist/lib/rendering/render-managers/common/entity-label.js +28 -4
  11. package/dist/lib/rendering/render-managers/common/entity-label.js.map +1 -1
  12. package/dist/lib/rendering/render-managers/entities/entities-loaded-render-manager.js +12 -4
  13. package/dist/lib/rendering/render-managers/entities/entities-loaded-render-manager.js.map +1 -1
  14. package/dist/lib/rendering/render-managers/entities/entities-render-manager.js +12 -4
  15. package/dist/lib/rendering/render-managers/entities/entities-render-manager.js.map +1 -1
  16. package/dist/lib/rendering/render-managers/tilesets/tileset-arb-render-manager.js +35 -2
  17. package/dist/lib/rendering/render-managers/tilesets/tileset-arb-render-manager.js.map +1 -1
  18. package/dist/lib/rendering/render-managers/tilesets/tileset-cad-render-manager.js +36 -3
  19. package/dist/lib/rendering/render-managers/tilesets/tileset-cad-render-manager.js.map +1 -1
  20. package/dist/lib/rendering/render-managers/tilesets/tileset-entities-render-manager.js +36 -3
  21. package/dist/lib/rendering/render-managers/tilesets/tileset-entities-render-manager.js.map +1 -1
  22. package/dist/lib/rendering/render-managers/tilesets/tileset-google-photos-render-manager.js +19 -0
  23. package/dist/lib/rendering/render-managers/tilesets/tileset-google-photos-render-manager.js.map +1 -1
  24. package/dist/lib/rendering/render-managers/tilesets/tileset-osm-render-manager.js +55 -2
  25. package/dist/lib/rendering/render-managers/tilesets/tileset-osm-render-manager.js.map +1 -1
  26. package/dist/lib/rendering/visuals-register.js +48 -9
  27. package/dist/lib/rendering/visuals-register.js.map +1 -1
  28. package/dist/lib/utils/cesium-entity-styler.js +5 -0
  29. package/dist/lib/utils/cesium-entity-styler.js.map +1 -1
  30. package/dist/types/bruce-cesium.d.ts +1 -1
  31. package/dist/types/rendering/render-managers/tilesets/tileset-arb-render-manager.d.ts +1 -0
  32. package/dist/types/rendering/render-managers/tilesets/tileset-cad-render-manager.d.ts +1 -0
  33. package/dist/types/rendering/render-managers/tilesets/tileset-entities-render-manager.d.ts +1 -0
  34. package/dist/types/rendering/render-managers/tilesets/tileset-google-photos-render-manager.d.ts +1 -0
  35. package/dist/types/rendering/render-managers/tilesets/tileset-osm-render-manager.d.ts +2 -0
  36. package/package.json +1 -1
@@ -1516,6 +1516,7 @@
1516
1516
  * @returns
1517
1517
  */
1518
1518
  function isFeatureAlive(viewer, feature) {
1519
+ var _a, _b;
1519
1520
  if (!(viewer === null || viewer === void 0 ? void 0 : viewer.scene) || viewer.isDestroyed()) {
1520
1521
  return false;
1521
1522
  }
@@ -1526,6 +1527,10 @@
1526
1527
  if (cTileset.isDestroyed() || !viewer.scene.primitives.contains(cTileset)) {
1527
1528
  return false;
1528
1529
  }
1530
+ // A tile unloads its content while the tileset lives on, and the feature's colour is then unreadable.
1531
+ if (feature._bruceUnloaded || ((_b = (_a = feature.content) === null || _a === void 0 ? void 0 : _a.isDestroyed) === null || _b === void 0 ? void 0 : _b.call(_a))) {
1532
+ return false;
1533
+ }
1529
1534
  return true;
1530
1535
  }
1531
1536
  let _lastMark = 0;
@@ -1715,7 +1720,17 @@
1715
1720
  removal = null;
1716
1721
  return;
1717
1722
  }
1718
- if (!assertColorMark(mark, feature.color)) {
1723
+ // A throw here is inside Cesium's frame and stops rendering, so an unreadable feature ends the animation.
1724
+ let current;
1725
+ try {
1726
+ current = feature.color;
1727
+ }
1728
+ catch (e) {
1729
+ removal === null || removal === void 0 ? void 0 : removal();
1730
+ removal = null;
1731
+ return;
1732
+ }
1733
+ if (!assertColorMark(mark, current)) {
1719
1734
  removal === null || removal === void 0 ? void 0 : removal();
1720
1735
  removal = null;
1721
1736
  onDone === null || onDone === void 0 ? void 0 : onDone();
@@ -2934,6 +2949,7 @@
2934
2949
  * @returns
2935
2950
  */
2936
2951
  function isAlive(viewer, visual) {
2952
+ var _a, _b;
2937
2953
  if (!(viewer === null || viewer === void 0 ? void 0 : viewer.scene) || viewer.isDestroyed()) {
2938
2954
  return false;
2939
2955
  }
@@ -2951,6 +2967,10 @@
2951
2967
  if (cTileset.isDestroyed() || !viewer.scene.primitives.contains(cTileset)) {
2952
2968
  return false;
2953
2969
  }
2970
+ // Its tile unloaded, so reading or writing the feature's colour throws.
2971
+ if (visual._bruceUnloaded || ((_b = (_a = visual.content) === null || _a === void 0 ? void 0 : _a.isDestroyed) === null || _b === void 0 ? void 0 : _b.call(_a))) {
2972
+ return false;
2973
+ }
2954
2974
  return true;
2955
2975
  }
2956
2976
  return false;
@@ -5789,6 +5809,8 @@
5789
5809
  this.element = document.createElement("div");
5790
5810
  this.coordinates = { x: 0, y: 0, scaleX: 1, scaleY: 1 };
5791
5811
  this.participants = new Set();
5812
+ // Participants whose failure has been logged, so a label that throws every frame logs once.
5813
+ this.failed = new WeakSet();
5792
5814
  this.placementMemory = new PlacementMemory();
5793
5815
  this.forwardingWheel = false;
5794
5816
  this.pendingDrag = null;
@@ -5989,15 +6011,35 @@
5989
6011
  x: (pointer.x - rect.left) * this.viewer.canvas.clientWidth / rect.width,
5990
6012
  y: (pointer.y - rect.top) * this.viewer.canvas.clientHeight / rect.height
5991
6013
  } : undefined;
6014
+ // This runs inside Cesium's frame, where one throwing label would stop rendering for the whole scene.
5992
6015
  const projected = new Map();
5993
6016
  for (const participant of this.participants) {
5994
- projected.set(participant, participant.project());
6017
+ let frame = null;
6018
+ try {
6019
+ frame = participant.project();
6020
+ }
6021
+ catch (e) {
6022
+ this.logFailure(participant, e);
6023
+ }
6024
+ projected.set(participant, frame);
5995
6025
  }
5996
6026
  const placements = new Map(Layout(Array.from(projected.values()).filter(Boolean), this.placementMemory).map(item => [item.frame.id, item]));
5997
6027
  for (const [participant, frame] of projected) {
5998
- participant.apply(frame ? placements.get(frame.id) : undefined);
6028
+ try {
6029
+ participant.apply(frame ? placements.get(frame.id) : undefined);
6030
+ }
6031
+ catch (e) {
6032
+ this.logFailure(participant, e);
6033
+ }
5999
6034
  }
6000
6035
  }
6036
+ logFailure(participant, error) {
6037
+ if (this.failed.has(participant)) {
6038
+ return;
6039
+ }
6040
+ this.failed.add(participant);
6041
+ console.error("EntityLabel: a label failed to update and is skipped this frame.", error);
6042
+ }
6001
6043
  }
6002
6044
  let nextLabelId = 0;
6003
6045
  class Label {
@@ -6733,8 +6775,10 @@
6733
6775
  const relative = Cesium.Cartesian3.subtract(this._pos3d, cameraPosition, new Cesium.Cartesian3());
6734
6776
  const depth = Cesium.Cartesian3.dot(relative, camera.directionWC || camera.direction);
6735
6777
  const distance = Cesium.Cartesian3.magnitude(relative);
6778
+ // If we're labelling from a temp label (mouse hover) don't drive opacity by cam distance.
6779
+ const hoverOnly = this.Transient && !this.Persistent;
6736
6780
  if (!Number.isFinite(distance) || depth <= 0 || distance < ((_c = this.options.near) !== null && _c !== void 0 ? _c : 0) ||
6737
- distance > ((_d = this.options.far) !== null && _d !== void 0 ? _d : Infinity) || (!this.prePositioned && distance >= MAX_DISTANCE)) {
6781
+ distance > ((_d = this.options.far) !== null && _d !== void 0 ? _d : Infinity) || (!this.prePositioned && !hoverOnly && distance >= MAX_DISTANCE)) {
6738
6782
  return null;
6739
6783
  }
6740
6784
  const transforms = Cesium.SceneTransforms;
@@ -6863,7 +6907,7 @@
6863
6907
  return null;
6864
6908
  }
6865
6909
  const state = (_p = this.visualRegister) === null || _p === void 0 ? void 0 : _p.GetState({ entityId: (_q = this.rego) === null || _q === void 0 ? void 0 : _q.entityId, menuItemId: (_r = this.rego) === null || _r === void 0 ? void 0 : _r.menuItemId });
6866
- const opacity = this.prePositioned || MAX_DISTANCE === Infinity ? 1 : 1 - distance / MAX_DISTANCE;
6910
+ const opacity = this.prePositioned || hoverOnly || MAX_DISTANCE === Infinity ? 1 : 1 - distance / MAX_DISTANCE;
6867
6911
  return {
6868
6912
  canFlip: leader && presentation !== "icon", hasLeader: leader,
6869
6913
  textWrapped: ((sourceNaturalText === null || sourceNaturalText === void 0 ? void 0 : sourceNaturalText.offsetWidth) || 0) > TEXT_MAX_WIDTH,
@@ -9265,6 +9309,7 @@
9265
9309
  })(exports.VisualRegisterCuller || (exports.VisualRegisterCuller = {}));
9266
9310
 
9267
9311
  function isTilesetFeatureAlive(visual) {
9312
+ var _a, _b;
9268
9313
  const cTileset = visual === null || visual === void 0 ? void 0 : visual.tileset;
9269
9314
  if (!cTileset) {
9270
9315
  return false;
@@ -9275,8 +9320,32 @@
9275
9320
  else if (visual.primitive && visual.primitive.isDestroyed()) {
9276
9321
  return false;
9277
9322
  }
9323
+ // A tile unloads its content while the tileset lives on, and every read through the feature then throws.
9324
+ else if (visual._bruceUnloaded || ((_b = (_a = visual.content) === null || _a === void 0 ? void 0 : _a.isDestroyed) === null || _b === void 0 ? void 0 : _b.call(_a))) {
9325
+ return false;
9326
+ }
9278
9327
  return true;
9279
9328
  }
9329
+ /*
9330
+ * Returns if a visual is currently shown, reading a tileset feature only while its tile is loaded.
9331
+ */
9332
+ function isVisualShown(visual) {
9333
+ if (!visual) {
9334
+ return true;
9335
+ }
9336
+ if (visual instanceof Cesium.Cesium3DTileFeature) {
9337
+ if (!isTilesetFeatureAlive(visual)) {
9338
+ return false;
9339
+ }
9340
+ try {
9341
+ return visual.show !== false;
9342
+ }
9343
+ catch (e) {
9344
+ return false;
9345
+ }
9346
+ }
9347
+ return visual.show !== false;
9348
+ }
9280
9349
  /**
9281
9350
  * Returns if a given visual is alive and in the scene.
9282
9351
  * @param viewer
@@ -9284,6 +9353,7 @@
9284
9353
  * @returns
9285
9354
  */
9286
9355
  function isAlive$1(viewer, visual) {
9356
+ var _a, _b;
9287
9357
  if (!(viewer === null || viewer === void 0 ? void 0 : viewer.scene) || viewer.isDestroyed()) {
9288
9358
  return false;
9289
9359
  }
@@ -9304,6 +9374,10 @@
9304
9374
  else if (visual.primitive && visual.primitive.isDestroyed()) {
9305
9375
  return false;
9306
9376
  }
9377
+ // Its tile unloaded, so every read through the feature throws.
9378
+ else if (visual._bruceUnloaded || ((_b = (_a = visual.content) === null || _a === void 0 ? void 0 : _a.isDestroyed) === null || _b === void 0 ? void 0 : _b.call(_a))) {
9379
+ return false;
9380
+ }
9307
9381
  return true;
9308
9382
  }
9309
9383
  return false;
@@ -9762,6 +9836,13 @@
9762
9836
  // Adding is asynchronous, so ordering has to wait until it is actually in the collection.
9763
9837
  Promise.resolve(this.viewer.dataSources.add(source))
9764
9838
  .then(() => {
9839
+ // Released or disposed before the add landed, when the remove found nothing to take out.
9840
+ if (this.menuItemSources.get(menuItemId) !== record) {
9841
+ if (!this.viewer.isDestroyed() && this.viewer.dataSources.contains(source)) {
9842
+ this.viewer.dataSources.remove(source, true);
9843
+ }
9844
+ return;
9845
+ }
9765
9846
  record.added = true;
9766
9847
  this.applySourceOrder();
9767
9848
  })
@@ -9798,7 +9879,9 @@
9798
9879
  if (this.disposed || !this.sourceOrderDirty || !((_a = this.viewer) === null || _a === void 0 ? void 0 : _a.dataSources)) {
9799
9880
  return;
9800
9881
  }
9801
- const records = Array.from(this.menuItemSources.values()).filter(x => x.added);
9882
+ // raiseToTop throws for a source the collection does not hold.
9883
+ const dataSources = this.viewer.dataSources;
9884
+ const records = Array.from(this.menuItemSources.values()).filter(x => x.added && dataSources.contains(x.source));
9802
9885
  if (records.length < 2) {
9803
9886
  // Nothing to order against yet, so leave the flag set for when there is.
9804
9887
  return;
@@ -9808,7 +9891,7 @@
9808
9891
  records.sort((a, b) => a.maxZIndex - b.maxZIndex);
9809
9892
  for (const record of records) {
9810
9893
  try {
9811
- this.viewer.dataSources.raiseToTop(record.source);
9894
+ dataSources.raiseToTop(record.source);
9812
9895
  }
9813
9896
  catch (e) {
9814
9897
  console.error(e);
@@ -12148,7 +12231,7 @@
12148
12231
  return (hover === null || hover === void 0 ? void 0 : hover.entityId) === entityId && (hover.menuItemId == null || hover.menuItemId === menuItemId) ? hover.position : undefined;
12149
12232
  }
12150
12233
  SyncLabelHover() {
12151
- var _a, _b, _c, _d;
12234
+ var _a, _b, _c;
12152
12235
  const hover = this.labelHover;
12153
12236
  if (!hover || this.disposed) {
12154
12237
  return;
@@ -12173,7 +12256,7 @@
12173
12256
  }
12174
12257
  const existing = rego && exports.EntityLabel.GetLabel({ rego });
12175
12258
  const state = this.GetState(hover);
12176
- if ((!rego && !group) || state.hidden || ((_b = rego === null || rego === void 0 ? void 0 : rego.visual) === null || _b === void 0 ? void 0 : _b.show) === false ||
12259
+ if ((!rego && !group) || state.hidden || ((rego === null || rego === void 0 ? void 0 : rego.visual) && !isVisualShown(rego.visual)) ||
12177
12260
  (this.GetIsIsolatedAny() && !state.isolated && this.GetIsolationOpacity() == null) ||
12178
12261
  (existing && existing !== this.hoverLabel && existing.HasText) ||
12179
12262
  (group && group.options.presentation !== "icon")) {
@@ -12185,7 +12268,7 @@
12185
12268
  return;
12186
12269
  }
12187
12270
  // Query-only icon groups have coordinates but no registered scene visual to attach to.
12188
- if (!rego && group && [group.item.longitude, group.item.latitude, (_c = group.item.altitude) !== null && _c !== void 0 ? _c : 0].every(Number.isFinite)) {
12271
+ if (!rego && group && [group.item.longitude, group.item.latitude, (_b = group.item.altitude) !== null && _b !== void 0 ? _b : 0].every(Number.isFinite)) {
12189
12272
  rego = {
12190
12273
  entityId: hover.entityId,
12191
12274
  menuItemId: hover.menuItemId,
@@ -12207,7 +12290,7 @@
12207
12290
  rego,
12208
12291
  viewer: this.viewer,
12209
12292
  visualRegister: this,
12210
- api: (_d = this.ApiGetters) === null || _d === void 0 ? void 0 : _d.GetBruceApi(),
12293
+ api: (_c = this.ApiGetters) === null || _c === void 0 ? void 0 : _c.GetBruceApi(),
12211
12294
  persistent: false,
12212
12295
  transient: true,
12213
12296
  hoverRingState: this.hoverRingState,
@@ -12217,10 +12300,10 @@
12217
12300
  selectOnClick: false
12218
12301
  },
12219
12302
  visible: () => {
12220
- var _a, _b;
12303
+ var _a;
12221
12304
  const current = this.GetState(hover);
12222
- return (!this.disposed && !current.hidden && ((_a = rego.visual) === null || _a === void 0 ? void 0 : _a.show) !== false &&
12223
- Boolean(this.GetRego(hover) || ((_b = this.labelGroups) === null || _b === void 0 ? void 0 : _b.Has(hover.entityId, hover.menuItemId))) &&
12305
+ return (!this.disposed && !current.hidden && isVisualShown(rego.visual) &&
12306
+ Boolean(this.GetRego(hover) || ((_a = this.labelGroups) === null || _a === void 0 ? void 0 : _a.Has(hover.entityId, hover.menuItemId))) &&
12224
12307
  (!this.GetIsIsolatedAny() || current.isolated || this.GetIsolationOpacity() != null));
12225
12308
  }
12226
12309
  };
@@ -14835,12 +14918,20 @@
14835
14918
  strokeWidth: lineWidthPx,
14836
14919
  clampToGround: lineWidthPx <= 0 && polygonsClamped
14837
14920
  });
14838
- this.viewer.dataSources.add(source);
14839
- this.sources.push(source);
14840
- if (this.disposed) {
14841
- this.doDispose();
14921
+ // Dispose already cleared what was added before this, and this one has not been added.
14922
+ if (this.disposed || this.viewer.isDestroyed()) {
14842
14923
  return;
14843
14924
  }
14925
+ const viewer = this.viewer;
14926
+ this.sources.push(source);
14927
+ // The add lands asynchronously, so a dispose in between removes nothing and would orphan the source.
14928
+ Promise.resolve(viewer.dataSources.add(source)).then(() => {
14929
+ if (this.disposed && !viewer.isDestroyed() && viewer.dataSources.contains(source)) {
14930
+ viewer.dataSources.remove(source);
14931
+ }
14932
+ }).catch((e) => {
14933
+ console.error(e);
14934
+ });
14844
14935
  const groups = [];
14845
14936
  /**
14846
14937
  * Applies individual styling to a rendered entity.
@@ -15876,12 +15967,20 @@
15876
15967
  strokeWidth: lineWidthPx,
15877
15968
  clampToGround: lineWidthPx <= 0 && polygonsClamped
15878
15969
  });
15879
- this.viewer.dataSources.add(source);
15880
- this.sources.push(source);
15881
- if (this.disposed) {
15882
- this.doDispose();
15970
+ // Dispose already cleared what was added before this, and this one has not been added.
15971
+ if (this.disposed || this.viewer.isDestroyed()) {
15883
15972
  return;
15884
15973
  }
15974
+ const viewer = this.viewer;
15975
+ this.sources.push(source);
15976
+ // The add lands asynchronously, so a dispose in between removes nothing and would orphan the source.
15977
+ Promise.resolve(viewer.dataSources.add(source)).then(() => {
15978
+ if (this.disposed && !viewer.isDestroyed() && viewer.dataSources.contains(source)) {
15979
+ viewer.dataSources.remove(source);
15980
+ }
15981
+ }).catch((e) => {
15982
+ console.error(e);
15983
+ });
15885
15984
  const groups = [];
15886
15985
  /**
15887
15986
  * Applies individual styling to a rendered entity.
@@ -19561,7 +19660,7 @@
19561
19660
  });
19562
19661
  this.tileUnloadRemoval = cTileset.tileUnload.addEventListener((tile) => {
19563
19662
  try {
19564
- this.queueTile(tile, false);
19663
+ this.unloadTile(tile);
19565
19664
  }
19566
19665
  catch (e) {
19567
19666
  console.error(e);
@@ -19745,7 +19844,7 @@
19745
19844
  });
19746
19845
  this.tileUnloadRemoval = this.cTileset.tileUnload.addEventListener((tile) => {
19747
19846
  try {
19748
- this.queueTile(tile, false);
19847
+ this.unloadTile(tile);
19749
19848
  }
19750
19849
  catch (e) {
19751
19850
  console.error(e);
@@ -19810,6 +19909,35 @@
19810
19909
  }
19811
19910
  this.pingFeatureQueue();
19812
19911
  }
19912
+ /*
19913
+ * Removes a tile's regos and marks its features dead, while the tile's content can still be read.
19914
+ */
19915
+ unloadTile(tile) {
19916
+ const content = tile === null || tile === void 0 ? void 0 : tile.content;
19917
+ if (!content) {
19918
+ return;
19919
+ }
19920
+ const features = [];
19921
+ for (let i = 0; i < content.featuresLength; i++) {
19922
+ const feature = content.getFeature(i);
19923
+ // Drops a load still queued for it, the batch skips anything marked.
19924
+ this.featureQueueStates.delete(feature);
19925
+ feature._bruceUnloaded = true;
19926
+ features.push(feature);
19927
+ }
19928
+ if (!features.length) {
19929
+ return;
19930
+ }
19931
+ const { removedEntityIds } = this.visualsManager.RemoveRegosByVisuals({
19932
+ visuals: features,
19933
+ requestRender: false,
19934
+ menuItemId: this.item.id,
19935
+ doRemove: false
19936
+ });
19937
+ for (const entityId of removedEntityIds) {
19938
+ delete this.loadedCesiumEntities[entityId];
19939
+ }
19940
+ }
19813
19941
  /**
19814
19942
  * Pings the feature queue to process any queued features.
19815
19943
  * Does nothing if we're already processing.
@@ -19826,7 +19954,7 @@
19826
19954
  return;
19827
19955
  }
19828
19956
  this.processFeatureQueueBatch();
19829
- if (!this.featureQueue) {
19957
+ if (!this.featureQueue.length) {
19830
19958
  clearInterval(this.featureQueueInterval);
19831
19959
  this.featureQueueInterval = null;
19832
19960
  }
@@ -19858,6 +19986,10 @@
19858
19986
  const regosToStyle = new Map();
19859
19987
  const featuresToRemove = [];
19860
19988
  for (const feature of batch) {
19989
+ // Its tile unloaded while it waited, unloadTile already removed whatever it registered.
19990
+ if (feature._bruceUnloaded) {
19991
+ continue;
19992
+ }
19861
19993
  const load = this.featureQueueStates.get(feature);
19862
19994
  this.featureQueueStates.delete(feature);
19863
19995
  if (!load) {
@@ -23025,6 +23157,7 @@
23025
23157
  this.processingQueue = 0;
23026
23158
  this._osmIdToBruceId = {};
23027
23159
  this.tileLoadRemoval = null;
23160
+ this.tileUnloadRemoval = null;
23028
23161
  const { viewer, register: visualsManager, getters: apiGetter, monitor, item } = params;
23029
23162
  this.viewer = viewer;
23030
23163
  this.getters = apiGetter;
@@ -23033,7 +23166,7 @@
23033
23166
  this.monitor = monitor;
23034
23167
  }
23035
23168
  Dispose() {
23036
- var _a;
23169
+ var _a, _b;
23037
23170
  if (this.disposed) {
23038
23171
  return;
23039
23172
  }
@@ -23041,6 +23174,8 @@
23041
23174
  clearInterval(this.queueCheckInterval);
23042
23175
  (_a = this.tileLoadRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
23043
23176
  this.tileLoadRemoval = null;
23177
+ (_b = this.tileUnloadRemoval) === null || _b === void 0 ? void 0 : _b.call(this);
23178
+ this.tileUnloadRemoval = null;
23044
23179
  if (this.cTileset) {
23045
23180
  const viewer = this.viewer;
23046
23181
  if (!(viewer === null || viewer === void 0 ? void 0 : viewer.isDestroyed()) && this.viewer.scene.primitives.contains(this.cTileset)) {
@@ -23136,6 +23271,14 @@
23136
23271
  console.error(e);
23137
23272
  }
23138
23273
  });
23274
+ this.tileUnloadRemoval = this.cTileset.tileUnload.addEventListener((tile) => {
23275
+ try {
23276
+ this.unloadTile(tile);
23277
+ }
23278
+ catch (e) {
23279
+ console.error(e);
23280
+ }
23281
+ });
23139
23282
  this.queueCheckInterval = setInterval(() => {
23140
23283
  if (this.featureQueue.length > 0) {
23141
23284
  this.processQueue();
@@ -23172,6 +23315,34 @@
23172
23315
  this.processQueue();
23173
23316
  }
23174
23317
  }
23318
+ /*
23319
+ * Removes a tile's regos and marks its features dead, while the tile's content can still be read.
23320
+ */
23321
+ unloadTile(tile) {
23322
+ const content = tile === null || tile === void 0 ? void 0 : tile.content;
23323
+ if (!content) {
23324
+ return;
23325
+ }
23326
+ const features = [];
23327
+ for (let i = 0; i < content.featuresLength; i++) {
23328
+ const feature = content.getFeature(i);
23329
+ feature._bruceUnloaded = true;
23330
+ features.push(feature);
23331
+ }
23332
+ if (!features.length) {
23333
+ return;
23334
+ }
23335
+ this.featureQueue = this.featureQueue.filter(x => !x._bruceUnloaded);
23336
+ const { removedEntityIds } = this.visualsManager.RemoveRegosByVisuals({
23337
+ visuals: features,
23338
+ requestRender: false,
23339
+ menuItemId: this.item.id,
23340
+ doRemove: false
23341
+ });
23342
+ for (const entityId of removedEntityIds) {
23343
+ delete this._loadedCesiumEntities[entityId];
23344
+ }
23345
+ }
23175
23346
  async processQueue() {
23176
23347
  if (this.processingQueue >= MAX_BATCHES) {
23177
23348
  return;
@@ -23304,6 +23475,10 @@
23304
23475
  bruceId = BModels.ObjectUtils.UId();
23305
23476
  }
23306
23477
  const feature = featureMap[i];
23478
+ // Its tile unloaded while the ID lookup was in flight.
23479
+ if (feature._bruceUnloaded) {
23480
+ continue;
23481
+ }
23307
23482
  if (this.item.ghostLoading == true) {
23308
23483
  feature.color = feature._orgColor;
23309
23484
  }
@@ -23395,7 +23570,7 @@
23395
23570
  return `osm:${sourceId}:${entityTypeId}`;
23396
23571
  }
23397
23572
  PrepareHandoff() {
23398
- var _a;
23573
+ var _a, _b;
23399
23574
  if (this.disposed || !this.cTileset || this.cTileset.isDestroyed()) {
23400
23575
  return null;
23401
23576
  }
@@ -23403,6 +23578,8 @@
23403
23578
  this.queueCheckInterval = null;
23404
23579
  (_a = this.tileLoadRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
23405
23580
  this.tileLoadRemoval = null;
23581
+ (_b = this.tileUnloadRemoval) === null || _b === void 0 ? void 0 : _b.call(this);
23582
+ this.tileUnloadRemoval = null;
23406
23583
  const payload = {
23407
23584
  cTileset: this.cTileset,
23408
23585
  loadedCesiumEntities: this._loadedCesiumEntities,
@@ -23450,6 +23627,14 @@
23450
23627
  console.error(e);
23451
23628
  }
23452
23629
  });
23630
+ this.tileUnloadRemoval = this.cTileset.tileUnload.addEventListener((tile) => {
23631
+ try {
23632
+ this.unloadTile(tile);
23633
+ }
23634
+ catch (e) {
23635
+ console.error(e);
23636
+ }
23637
+ });
23453
23638
  this.queueCheckInterval = setInterval(() => {
23454
23639
  if (this.featureQueue.length > 0) {
23455
23640
  this.processQueue();
@@ -23639,7 +23824,7 @@
23639
23824
  });
23640
23825
  this.tileUnloadRemoval = cTileset.tileUnload.addEventListener((tile) => {
23641
23826
  try {
23642
- this.queueTile(tile, false);
23827
+ this.unloadTile(tile);
23643
23828
  }
23644
23829
  catch (e) {
23645
23830
  console.error(e);
@@ -23685,6 +23870,35 @@
23685
23870
  }
23686
23871
  this.pingFeatureQueue();
23687
23872
  }
23873
+ /*
23874
+ * Removes a tile's regos and marks its features dead, while the tile's content can still be read.
23875
+ */
23876
+ unloadTile(tile) {
23877
+ const content = tile === null || tile === void 0 ? void 0 : tile.content;
23878
+ if (!content) {
23879
+ return;
23880
+ }
23881
+ const features = [];
23882
+ for (let i = 0; i < content.featuresLength; i++) {
23883
+ const feature = content.getFeature(i);
23884
+ // Drops a load still queued for it, the batch skips anything marked.
23885
+ this.featureQueueStates.delete(feature);
23886
+ feature._bruceUnloaded = true;
23887
+ features.push(feature);
23888
+ }
23889
+ if (!features.length) {
23890
+ return;
23891
+ }
23892
+ const { removedEntityIds } = this.visualsManager.RemoveRegosByVisuals({
23893
+ visuals: features,
23894
+ requestRender: false,
23895
+ menuItemId: this.item.id,
23896
+ doRemove: false
23897
+ });
23898
+ for (const entityId of removedEntityIds) {
23899
+ delete this.loadedCesiumEntities[entityId];
23900
+ }
23901
+ }
23688
23902
  /**
23689
23903
  * Pings the feature queue to process any queued features.
23690
23904
  * Does nothing if we're already processing.
@@ -23701,7 +23915,7 @@
23701
23915
  return;
23702
23916
  }
23703
23917
  this.processFeatureQueueBatch();
23704
- if (!this.featureQueue) {
23918
+ if (!this.featureQueue.length) {
23705
23919
  clearInterval(this.featureQueueInterval);
23706
23920
  this.featureQueueInterval = null;
23707
23921
  }
@@ -23719,6 +23933,10 @@
23719
23933
  const regosToStyle = new Map();
23720
23934
  const featuresToRemove = [];
23721
23935
  for (const feature of batch) {
23936
+ // Its tile unloaded while it waited, unloadTile already removed whatever it registered.
23937
+ if (feature._bruceUnloaded) {
23938
+ continue;
23939
+ }
23722
23940
  const load = this.featureQueueStates.get(feature);
23723
23941
  this.featureQueueStates.delete(feature);
23724
23942
  if (!load) {
@@ -23924,7 +24142,7 @@
23924
24142
  });
23925
24143
  this.tileUnloadRemoval = this.cTileset.tileUnload.addEventListener((tile) => {
23926
24144
  try {
23927
- this.queueTile(tile, false);
24145
+ this.unloadTile(tile);
23928
24146
  }
23929
24147
  catch (e) {
23930
24148
  console.error(e);
@@ -24878,7 +25096,7 @@
24878
25096
  });
24879
25097
  this.cTileset.tileUnload.addEventListener((tile) => {
24880
25098
  try {
24881
- this.queueTile(tile, false);
25099
+ this.unloadTile(tile);
24882
25100
  }
24883
25101
  catch (e) {
24884
25102
  console.error(e);
@@ -24906,6 +25124,35 @@
24906
25124
  }
24907
25125
  this.pingFeatureQueue();
24908
25126
  }
25127
+ /*
25128
+ * Removes a tile's regos and marks its features dead, while the tile's content can still be read.
25129
+ */
25130
+ unloadTile(tile) {
25131
+ const content = tile === null || tile === void 0 ? void 0 : tile.content;
25132
+ if (!content) {
25133
+ return;
25134
+ }
25135
+ const features = [];
25136
+ for (let i = 0; i < content.featuresLength; i++) {
25137
+ const feature = content.getFeature(i);
25138
+ // Drops a load still queued for it, the batch skips anything marked.
25139
+ this.featureQueueStates.delete(feature);
25140
+ feature._bruceUnloaded = true;
25141
+ features.push(feature);
25142
+ }
25143
+ if (!features.length) {
25144
+ return;
25145
+ }
25146
+ const { removedEntityIds } = this.visualsManager.RemoveRegosByVisuals({
25147
+ visuals: features,
25148
+ requestRender: false,
25149
+ menuItemId: this.item.id,
25150
+ doRemove: false
25151
+ });
25152
+ for (const entityId of removedEntityIds) {
25153
+ delete this.loadedCesiumEntities[entityId];
25154
+ }
25155
+ }
24909
25156
  /**
24910
25157
  * Pings the feature queue to process any queued features.
24911
25158
  * Does nothing if we're already processing.
@@ -24922,7 +25169,7 @@
24922
25169
  return;
24923
25170
  }
24924
25171
  this.processFeatureQueueBatch();
24925
- if (!this.featureQueue) {
25172
+ if (!this.featureQueue.length) {
24926
25173
  clearInterval(this.featureQueueInterval);
24927
25174
  this.featureQueueInterval = null;
24928
25175
  }
@@ -24940,6 +25187,10 @@
24940
25187
  const regosToStyle = new Map();
24941
25188
  const featuresToRemove = [];
24942
25189
  for (const feature of batch) {
25190
+ // Its tile unloaded while it waited, unloadTile already removed whatever it registered.
25191
+ if (feature._bruceUnloaded) {
25192
+ continue;
25193
+ }
24943
25194
  const load = this.featureQueueStates.get(feature);
24944
25195
  this.featureQueueStates.delete(feature);
24945
25196
  if (!load) {
@@ -25556,6 +25807,8 @@
25556
25807
  constructor(params) {
25557
25808
  this.disposed = false;
25558
25809
  this.cTileset = null;
25810
+ // Set while a tileset is being created, so a second Init in the meantime cannot add another.
25811
+ this.initializing = false;
25559
25812
  const { viewer, item, getters } = params;
25560
25813
  this.viewer = viewer;
25561
25814
  this.item = item;
@@ -25576,6 +25829,10 @@
25576
25829
  this.viewer.scene.primitives.remove(this.cTileset);
25577
25830
  this.viewer.scene.requestRender();
25578
25831
  }
25832
+ // Disposed before it was added, so nothing else will ever destroy it.
25833
+ else if (!this.cTileset.isDestroyed()) {
25834
+ this.cTileset.destroy();
25835
+ }
25579
25836
  this.cTileset = null;
25580
25837
  }
25581
25838
  }
@@ -25594,6 +25851,11 @@
25594
25851
  this.viewer.scene.requestRender();
25595
25852
  return;
25596
25853
  }
25854
+ // The creation in flight applies the latest item's colour mask when it lands.
25855
+ if (this.initializing) {
25856
+ return;
25857
+ }
25858
+ this.initializing = true;
25597
25859
  try {
25598
25860
  const api = this.getters.GetBruceApi();
25599
25861
  await api.Loading;
@@ -25679,6 +25941,14 @@
25679
25941
  console.error("Failed to load Google Photorealistic 3D tileset:", e);
25680
25942
  return;
25681
25943
  }
25944
+ finally {
25945
+ this.initializing = false;
25946
+ }
25947
+ // Creation can come back empty, and the viewer can go while it loads.
25948
+ if (this.disposed || !this.cTileset || this.cTileset.isDestroyed() || !this.viewer || this.viewer.isDestroyed()) {
25949
+ this.doDispose();
25950
+ return;
25951
+ }
25682
25952
  this.viewer.scene.primitives.add(this.cTileset);
25683
25953
  this.applyColorMask();
25684
25954
  this.viewer.scene.requestRender();
@@ -43122,7 +43392,8 @@
43122
43392
  const hpr = new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(heading), Cesium.Math.toRadians(pitch), Cesium.Math.toRadians(roll));
43123
43393
  getOrient = new Cesium.CallbackProperty(() => {
43124
43394
  const currentPos = position ? GetCValue(params.viewer, position) : pos3d;
43125
- return Cesium.Transforms.headingPitchRollQuaternion(currentPos, hpr);
43395
+ // A live position can read as null mid-update, and throwing here would stop Cesium's frame.
43396
+ return Cesium.Transforms.headingPitchRollQuaternion(currentPos || pos3d, hpr);
43126
43397
  }, false);
43127
43398
  }
43128
43399
  cEntity = new Cesium.Entity({
@@ -50432,7 +50703,7 @@ void main() {
50432
50703
  StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
50433
50704
  })(exports.StyleUtils || (exports.StyleUtils = {}));
50434
50705
 
50435
- const VERSION = "7.4.4";
50706
+ const VERSION = "7.4.6";
50436
50707
  /**
50437
50708
  * Updates the environment instance used by bruce-cesium to one specified.
50438
50709
  * This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.