bruce-cesium 6.1.8 → 6.2.0

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.
@@ -1,6 +1,6 @@
1
1
  import { BruceEvent, Cartes, Entity as Entity$1, ProjectViewTile, Carto, Geometry, MathUtils, LRUCache, Api, Calculator, ClientFile, EntityTag, EntityType, ObjectUtils, Style, 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, Primitive, Cesium3DTileFeature, SceneMode, GeoJsonDataSource, Cesium3DTileStyle, Cesium3DTileColorBlendMode, HeadingPitchRange, Ion, KmlDataSource, Quaternion, Matrix3, Matrix4, SceneTransforms, NearFarScalar, OrthographicFrustum, EasingFunction, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, IonResource, Cesium3DTileset, CesiumInspector, defined, ClockRange, EllipsoidGeodesic, sampleTerrainMostDetailed, PolygonPipeline, BoundingSphere, GeometryInstance, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, ScreenSpaceEventHandler, ScreenSpaceEventType, CzmlDataSource, Intersect, Fullscreen } from 'cesium';
3
+ import { Cartographic, Cartesian2, Math as Math$1, Cartesian3, CallbackProperty, Color, HeightReference, Rectangle, JulianDate, Entity, DistanceDisplayCondition, HorizontalOrigin, VerticalOrigin, ConstantProperty, ClassificationType, ConstantPositionProperty, PolygonHierarchy, ShadowMode, PolylineGraphics, ArcType, CornerType, ColorMaterialProperty, ColorBlendMode, HeadingPitchRoll, Transforms, Model, Primitive, Cesium3DTileFeature, SceneMode, GeoJsonDataSource, Cesium3DTileStyle, HeadingPitchRange, Cesium3DTileColorBlendMode, Ion, KmlDataSource, Quaternion, Matrix3, Matrix4, SceneTransforms, NearFarScalar, OrthographicFrustum, EasingFunction, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, IonResource, Cesium3DTileset, CesiumInspector, defined, ClockRange, EllipsoidGeodesic, sampleTerrainMostDetailed, PolygonPipeline, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, BoundingSphere, GeometryInstance, ScreenSpaceEventHandler, ScreenSpaceEventType, CzmlDataSource, Intersect, Fullscreen } from 'cesium';
4
4
 
5
5
  const TIME_LAG = 300;
6
6
  const POSITION_CHECK_TIMER = 950;
@@ -10798,6 +10798,19 @@ var CesiumAnimatedInOut;
10798
10798
  CesiumAnimatedInOut.AnimateOut = AnimateOut;
10799
10799
  })(CesiumAnimatedInOut || (CesiumAnimatedInOut = {}));
10800
10800
 
10801
+ function isTilesetFeatureAlive(visual) {
10802
+ const cTileset = visual === null || visual === void 0 ? void 0 : visual.tileset;
10803
+ if (!cTileset) {
10804
+ return false;
10805
+ }
10806
+ else if (cTileset.isDestroyed() || !cTileset.show || !cTileset.root) {
10807
+ return false;
10808
+ }
10809
+ else if (visual.primitive && visual.primitive.isDestroyed()) {
10810
+ return false;
10811
+ }
10812
+ return true;
10813
+ }
10801
10814
  /**
10802
10815
  * Returns if a given visual is alive and in the scene.
10803
10816
  * @param viewer
@@ -10893,23 +10906,25 @@ function updateEntityShow(viewer, rego, show) {
10893
10906
  }
10894
10907
  }
10895
10908
  else if (visual instanceof Cesium3DTileFeature) {
10896
- // No way to tell if changing show will crash it!
10897
- // ^ We need to improve removing visuals from the register when tiles unload to avoid this :)
10898
- try {
10899
- visual.show = show;
10900
- }
10901
- catch (e) {
10902
- console.error(e);
10903
- }
10904
- const ent = visual;
10905
- if (ent._siblingGraphics) {
10906
- for (let i = 0; i < ent._siblingGraphics.length; i++) {
10907
- try {
10908
- const sibling = ent._siblingGraphics[i];
10909
- sibling.show = show;
10910
- }
10911
- catch (e) {
10912
- console.error(e);
10909
+ if (isTilesetFeatureAlive(visual)) {
10910
+ // No way to tell if changing show will crash it!
10911
+ // ^ We need to improve removing visuals from the register when tiles unload to avoid this :)
10912
+ try {
10913
+ visual.show = show;
10914
+ }
10915
+ catch (e) {
10916
+ console.error(e);
10917
+ }
10918
+ const ent = visual;
10919
+ if (ent._siblingGraphics) {
10920
+ for (let i = 0; i < ent._siblingGraphics.length; i++) {
10921
+ try {
10922
+ const sibling = ent._siblingGraphics[i];
10923
+ sibling.show = show;
10924
+ }
10925
+ catch (e) {
10926
+ console.error(e);
10927
+ }
10913
10928
  }
10914
10929
  }
10915
10930
  }
@@ -20157,6 +20172,24 @@ var TilesetEntitiesRenderManager;
20157
20172
  this.disposed = false;
20158
20173
  this.cTileset = null;
20159
20174
  this.styler = new TilesetRenderEngine.Styler();
20175
+ // Entity ID -> rego.
20176
+ // We retain this information as a quick look-up on what has been registered.
20177
+ // This lets us properly assign siblings to the same rego when hierarchy items are marked as collapsed.
20178
+ this.loadedCesiumEntities = {};
20179
+ // Cache of 'something isolated' that we detect and use to calculate a default show state.
20180
+ // It's pretty expensive so we save it for a few seconds inbetween checks.
20181
+ this.somethingIsolated = null;
20182
+ this.somethingIsolatedDateTime = null;
20183
+ // Queue of loaded in features that we haven't processed yet.
20184
+ this.featureQueue = [];
20185
+ // State is true for 'add' or 'false' for 'remove'.
20186
+ this.featureQueueStates = new Map();
20187
+ this.featureQueueInterval = null;
20188
+ // Cache of feature properties we wanted to find vs what we found.
20189
+ // Eg: "entityId" -> "entityId" | "bruceId" | "id" | null.
20190
+ // Saves having to do a case-insensitive lookup every time.
20191
+ this.featurePropCache = new Map();
20192
+ this.featurePropsChecked = 0;
20160
20193
  const { viewer, register: visualsManager, getters: apiGetter, item } = params;
20161
20194
  this.viewer = viewer;
20162
20195
  this.getters = apiGetter;
@@ -20283,7 +20316,20 @@ var TilesetEntitiesRenderManager;
20283
20316
  }
20284
20317
  });
20285
20318
  cTileset.tileLoad.addEventListener((tile) => {
20286
- this.mapCTile(tile);
20319
+ try {
20320
+ this.queueTile(tile, true);
20321
+ }
20322
+ catch (e) {
20323
+ console.error(e);
20324
+ }
20325
+ });
20326
+ cTileset.tileUnload.addEventListener((tile) => {
20327
+ try {
20328
+ this.queueTile(tile, false);
20329
+ }
20330
+ catch (e) {
20331
+ console.error(e);
20332
+ }
20287
20333
  });
20288
20334
  }
20289
20335
  // Scaling points by default otherwise they're 1px in size.
@@ -20306,6 +20352,118 @@ var TilesetEntitiesRenderManager;
20306
20352
  });
20307
20353
  })();
20308
20354
  }
20355
+ /**
20356
+ * @param tile
20357
+ * @param load indicates if we are loading or unloading the tile.
20358
+ * @returns
20359
+ */
20360
+ queueTile(tile, load) {
20361
+ const content = tile === null || tile === void 0 ? void 0 : tile.content;
20362
+ if (!content) {
20363
+ return;
20364
+ }
20365
+ for (let i = 0; i < content.featuresLength; i++) {
20366
+ const feature = content.getFeature(i);
20367
+ if (!this.featureQueue.includes(feature)) {
20368
+ this.featureQueue.push(feature);
20369
+ }
20370
+ this.featureQueueStates.set(feature, load);
20371
+ }
20372
+ this.pingFeatureQueue();
20373
+ }
20374
+ /**
20375
+ * Pings the feature queue to process any queued features.
20376
+ * Does nothing if we're already processing.
20377
+ * @returns
20378
+ */
20379
+ pingFeatureQueue() {
20380
+ if (!this.featureQueue.length || this.featureQueueInterval || this.disposed) {
20381
+ return;
20382
+ }
20383
+ this.featureQueueInterval = setInterval(() => {
20384
+ if (this.disposed) {
20385
+ clearInterval(this.featureQueueInterval);
20386
+ this.featureQueueInterval = null;
20387
+ return;
20388
+ }
20389
+ this.processFeatureQueueBatch();
20390
+ if (!this.featureQueue) {
20391
+ clearInterval(this.featureQueueInterval);
20392
+ this.featureQueueInterval = null;
20393
+ }
20394
+ }, 10);
20395
+ }
20396
+ /**
20397
+ * Process a batch of features from the feature queue.
20398
+ */
20399
+ processFeatureQueueBatch() {
20400
+ if (!this.featureQueue.length) {
20401
+ return;
20402
+ }
20403
+ const BATCH_SIZE = 5000;
20404
+ const batch = this.featureQueue.splice(0, BATCH_SIZE);
20405
+ const regosToStyle = new Map();
20406
+ const featuresToRemove = [];
20407
+ for (const feature of batch) {
20408
+ const load = this.featureQueueStates.get(feature);
20409
+ this.featureQueueStates.delete(feature);
20410
+ if (!load) {
20411
+ featuresToRemove.push(feature);
20412
+ continue;
20413
+ }
20414
+ const result = this.mapTilesetFeature(feature);
20415
+ if (result === null) {
20416
+ // Override the default hide state set by the style.
20417
+ feature.show = true;
20418
+ continue;
20419
+ }
20420
+ else if (result) {
20421
+ regosToStyle.set(result.entityId, result);
20422
+ }
20423
+ }
20424
+ if (featuresToRemove.length) {
20425
+ const { removedEntityIds } = this.visualsManager.RemoveRegosByVisuals({
20426
+ visuals: featuresToRemove,
20427
+ requestRender: false,
20428
+ menuItemId: this.item.id,
20429
+ doRemove: false
20430
+ });
20431
+ for (const entityId of removedEntityIds) {
20432
+ delete this.loadedCesiumEntities[entityId];
20433
+ }
20434
+ }
20435
+ if (this.styler && regosToStyle.size) {
20436
+ this.styler.QueueEntities(Array.from(regosToStyle.values()));
20437
+ }
20438
+ }
20439
+ evaluateFeatureProps(feature) {
20440
+ var _a;
20441
+ // If we've checked 10 features then we should have a good handle on what props are available.
20442
+ if (this.featurePropsChecked >= 10) {
20443
+ return;
20444
+ }
20445
+ this.featurePropsChecked++;
20446
+ const featureAny = feature;
20447
+ const props = featureAny.getPropertyNames ? featureAny.getPropertyNames() : (_a = featureAny.getPropertyIds) === null || _a === void 0 ? void 0 : _a.call(featureAny);
20448
+ if (!props) {
20449
+ return;
20450
+ }
20451
+ for (const prop of props) {
20452
+ if (!prop) {
20453
+ continue;
20454
+ }
20455
+ const lowered = prop.toLowerCase();
20456
+ if (lowered === "bruceid" || lowered === "id" || lowered === "entityid") {
20457
+ this.featurePropCache.set("entityId", prop);
20458
+ }
20459
+ else if (lowered === "brucepath") {
20460
+ this.featurePropCache.set("BrucePath", prop);
20461
+ }
20462
+ else if (lowered === "building_id" || lowered === "buildingid") {
20463
+ this.featurePropCache.set("BuildingID", prop);
20464
+ }
20465
+ }
20466
+ }
20309
20467
  Dispose() {
20310
20468
  if (this.disposed) {
20311
20469
  return;
@@ -20315,6 +20473,11 @@ var TilesetEntitiesRenderManager;
20315
20473
  }
20316
20474
  doDispose() {
20317
20475
  var _a, _b;
20476
+ if (this.featureQueueInterval) {
20477
+ clearInterval(this.featureQueueInterval);
20478
+ this.featureQueueInterval = null;
20479
+ this.featureQueue = [];
20480
+ }
20318
20481
  if (this.cTileset) {
20319
20482
  const viewer = this.viewer;
20320
20483
  if (!(viewer === null || viewer === void 0 ? void 0 : viewer.isDestroyed()) && this.viewer.scene.primitives.contains(this.cTileset)) {
@@ -20335,60 +20498,84 @@ var TilesetEntitiesRenderManager;
20335
20498
  this.viewer.zoomTo(this.cTileset, new HeadingPitchRange(0.0, -0.5, this.cTileset.boundingSphere.radius / 4.0));
20336
20499
  }
20337
20500
  }
20338
- mapCTile(tile) {
20339
- const content = tile === null || tile === void 0 ? void 0 : tile.content;
20340
- if (!content) {
20341
- return;
20342
- }
20343
- const regosToQueue = new Map();
20344
- for (let i = 0; i < content.featuresLength; i++) {
20345
- const feature = content.getFeature(i);
20346
- const rego = this.mapTilesetFeature(feature);
20347
- if (rego === null || rego === void 0 ? void 0 : rego.entityId) {
20348
- regosToQueue.set(rego.entityId, rego);
20349
- }
20350
- }
20351
- if (this.styler && regosToQueue.size) {
20352
- this.styler.QueueEntities(Array.from(regosToQueue.values()));
20353
- }
20354
- this.viewer.scene.requestRender();
20355
- }
20356
20501
  mapTilesetFeature(feature) {
20357
20502
  var _a, _b, _c;
20358
- // Version =1 tilesets.
20359
- let id = feature.getProperty("BruceId");
20503
+ this.evaluateFeatureProps(feature);
20504
+ const propToUse = this.featurePropCache.get("entityId");
20505
+ let id = propToUse ? feature.getProperty(propToUse) : null;
20506
+ if (id && typeof id == "string") {
20507
+ // Perhaps a relic of UTF-16 encoding after we stopped using it?
20508
+ if (id.endsWith("\u0000")) {
20509
+ id = id.substring(0, id.length - 1);
20510
+ }
20511
+ }
20360
20512
  if (!id) {
20361
- // Version >1 tilesets.
20362
- id = feature.getProperty("entityId");
20363
- if (id && typeof id == "string") {
20364
- // Perhaps a relic of UTF-16 encoding after we stopped using it?
20365
- if (id.endsWith("\u0000")) {
20366
- id = id.substring(0, id.length - 1);
20367
- }
20513
+ return null;
20514
+ }
20515
+ const accountId = (_b = (_a = this.item.tileset) === null || _a === void 0 ? void 0 : _a.ClientAccountID) !== null && _b !== void 0 ? _b : this.getters.GetAccountId();
20516
+ const canEdit = accountId === this.getters.GetAccountId();
20517
+ let rego = {
20518
+ canEdit: canEdit,
20519
+ entityId: id,
20520
+ entityTypeId: this.typeId,
20521
+ menuItemId: this.item.id,
20522
+ menuItemType: this.item.Type,
20523
+ priority: this.renderPriority,
20524
+ visual: feature,
20525
+ tilesetId: (_c = this.item.tileset) === null || _c === void 0 ? void 0 : _c.TilesetID,
20526
+ tilesetType: Tileset.EType.EntitiesSet,
20527
+ accountId: accountId
20528
+ };
20529
+ // Get the initial hide/show state.
20530
+ const state = this.visualsManager.GetState({
20531
+ entityId: rego.entityId,
20532
+ menuItemId: this.item.id,
20533
+ });
20534
+ if (this.somethingIsolated == null ||
20535
+ // 5s cache.
20536
+ (this.somethingIsolatedDateTime && this.somethingIsolatedDateTime.getTime() + 5000 < new Date().getTime())) {
20537
+ this.somethingIsolated = this.visualsManager.GetIsIsolatedAny();
20538
+ this.somethingIsolatedDateTime = new Date();
20539
+ }
20540
+ // Override the default hide state set by the style.
20541
+ let hide = false;
20542
+ if (state) {
20543
+ if (state.hidden) {
20544
+ hide = true;
20545
+ }
20546
+ else if (state.opacity === 0) {
20547
+ hide = true;
20548
+ }
20549
+ else if (!state.isolated && this.somethingIsolated) {
20550
+ hide = true;
20368
20551
  }
20369
20552
  }
20370
- if (id) {
20371
- const accountId = (_b = (_a = this.item.tileset) === null || _a === void 0 ? void 0 : _a.ClientAccountID) !== null && _b !== void 0 ? _b : this.getters.GetAccountId();
20372
- const canEdit = accountId === this.getters.GetAccountId();
20373
- const rego = {
20374
- canEdit: canEdit,
20375
- entityId: id,
20376
- entityTypeId: this.typeId,
20377
- menuItemId: this.item.id,
20378
- menuItemType: this.item.Type,
20379
- priority: this.renderPriority,
20380
- visual: feature,
20381
- tilesetId: (_c = this.item.tileset) === null || _c === void 0 ? void 0 : _c.TilesetID,
20382
- tilesetType: Tileset.EType.EntitiesSet,
20383
- accountId: accountId
20384
- };
20385
- this.visualsManager.AddRego({
20386
- rego,
20387
- requestRender: false
20388
- });
20389
- return rego;
20553
+ feature.show = !hide;
20554
+ // Already exists, so we add this graphic as a sibling.
20555
+ if (this.loadedCesiumEntities[rego.entityId]) {
20556
+ rego = this.loadedCesiumEntities[rego.entityId];
20557
+ if (!rego.visual) {
20558
+ // No parent graphic.
20559
+ rego.visual = feature;
20560
+ }
20561
+ else if (rego.visual == feature) ;
20562
+ else {
20563
+ const visual = rego.visual;
20564
+ // Sibling graphic.
20565
+ if (!visual._siblingGraphics) {
20566
+ visual._siblingGraphics = [];
20567
+ }
20568
+ if (visual._siblingGraphics.indexOf(feature) < 0) {
20569
+ visual._siblingGraphics.push(feature);
20570
+ }
20571
+ }
20390
20572
  }
20391
- return null;
20573
+ this.loadedCesiumEntities[rego.entityId] = rego;
20574
+ this.visualsManager.AddRego({
20575
+ rego,
20576
+ requestRender: false
20577
+ });
20578
+ return rego;
20392
20579
  }
20393
20580
  async ReRender(params) {
20394
20581
  let { entityIds, force, entities } = params;
@@ -20671,6 +20858,24 @@ var TilesetArbRenderManager;
20671
20858
  this.cTileset = null;
20672
20859
  this.styler = new TilesetRenderEngine.Styler();
20673
20860
  this.tilesetType = null;
20861
+ // Entity ID -> rego.
20862
+ // We retain this information as a quick look-up on what has been registered.
20863
+ // This lets us properly assign siblings to the same rego when hierarchy items are marked as collapsed.
20864
+ this.loadedCesiumEntities = {};
20865
+ // Cache of 'something isolated' that we detect and use to calculate a default show state.
20866
+ // It's pretty expensive so we save it for a few seconds inbetween checks.
20867
+ this.somethingIsolated = null;
20868
+ this.somethingIsolatedDateTime = null;
20869
+ // Queue of loaded in features that we haven't processed yet.
20870
+ this.featureQueue = [];
20871
+ // State is true for 'add' or 'false' for 'remove'.
20872
+ this.featureQueueStates = new Map();
20873
+ this.featureQueueInterval = null;
20874
+ // Cache of feature properties we wanted to find vs what we found.
20875
+ // Eg: "entityId" -> "entityId" | "bruceId" | "id" | null.
20876
+ // Saves having to do a case-insensitive lookup every time.
20877
+ this.featurePropCache = new Map();
20878
+ this.featurePropsChecked = 0;
20674
20879
  this.viewer = params.viewer;
20675
20880
  this.getters = params.getters;
20676
20881
  this.visualsManager = params.register;
@@ -20847,11 +21052,16 @@ var TilesetArbRenderManager;
20847
21052
  }
20848
21053
  });
20849
21054
  this.cTileset.tileLoad.addEventListener((tile) => {
20850
- if (this.disposed || this.viewer.isDestroyed()) {
20851
- return;
21055
+ try {
21056
+ this.queueTile(tile, true);
20852
21057
  }
21058
+ catch (e) {
21059
+ console.error(e);
21060
+ }
21061
+ });
21062
+ this.cTileset.tileUnload.addEventListener((tile) => {
20853
21063
  try {
20854
- this.mapCTile(tile);
21064
+ this.queueTile(tile, false);
20855
21065
  }
20856
21066
  catch (e) {
20857
21067
  console.error(e);
@@ -20859,6 +21069,118 @@ var TilesetArbRenderManager;
20859
21069
  });
20860
21070
  })();
20861
21071
  }
21072
+ /**
21073
+ * @param tile
21074
+ * @param load indicates if we are loading or unloading the tile.
21075
+ * @returns
21076
+ */
21077
+ queueTile(tile, load) {
21078
+ const content = tile === null || tile === void 0 ? void 0 : tile.content;
21079
+ if (!content) {
21080
+ return;
21081
+ }
21082
+ for (let i = 0; i < content.featuresLength; i++) {
21083
+ const feature = content.getFeature(i);
21084
+ if (!this.featureQueue.includes(feature)) {
21085
+ this.featureQueue.push(feature);
21086
+ }
21087
+ this.featureQueueStates.set(feature, load);
21088
+ }
21089
+ this.pingFeatureQueue();
21090
+ }
21091
+ /**
21092
+ * Pings the feature queue to process any queued features.
21093
+ * Does nothing if we're already processing.
21094
+ * @returns
21095
+ */
21096
+ pingFeatureQueue() {
21097
+ if (!this.featureQueue.length || this.featureQueueInterval || this.disposed) {
21098
+ return;
21099
+ }
21100
+ this.featureQueueInterval = setInterval(() => {
21101
+ if (this.disposed) {
21102
+ clearInterval(this.featureQueueInterval);
21103
+ this.featureQueueInterval = null;
21104
+ return;
21105
+ }
21106
+ this.processFeatureQueueBatch();
21107
+ if (!this.featureQueue) {
21108
+ clearInterval(this.featureQueueInterval);
21109
+ this.featureQueueInterval = null;
21110
+ }
21111
+ }, 10);
21112
+ }
21113
+ /**
21114
+ * Process a batch of features from the feature queue.
21115
+ */
21116
+ processFeatureQueueBatch() {
21117
+ if (!this.featureQueue.length) {
21118
+ return;
21119
+ }
21120
+ const BATCH_SIZE = 5000;
21121
+ const batch = this.featureQueue.splice(0, BATCH_SIZE);
21122
+ const regosToStyle = new Map();
21123
+ const featuresToRemove = [];
21124
+ for (const feature of batch) {
21125
+ const load = this.featureQueueStates.get(feature);
21126
+ this.featureQueueStates.delete(feature);
21127
+ if (!load) {
21128
+ featuresToRemove.push(feature);
21129
+ continue;
21130
+ }
21131
+ const result = this.mapTilesetFeature(feature);
21132
+ if (result === null) {
21133
+ // Override the default hide state set by the style.
21134
+ feature.show = true;
21135
+ continue;
21136
+ }
21137
+ else if (result) {
21138
+ regosToStyle.set(result.entityId, result);
21139
+ }
21140
+ }
21141
+ if (featuresToRemove.length) {
21142
+ const { removedEntityIds } = this.visualsManager.RemoveRegosByVisuals({
21143
+ visuals: featuresToRemove,
21144
+ requestRender: false,
21145
+ menuItemId: this.item.id,
21146
+ doRemove: false
21147
+ });
21148
+ for (const entityId of removedEntityIds) {
21149
+ delete this.loadedCesiumEntities[entityId];
21150
+ }
21151
+ }
21152
+ if (this.styler && regosToStyle.size) {
21153
+ this.styler.QueueEntities(Array.from(regosToStyle.values()));
21154
+ }
21155
+ }
21156
+ evaluateFeatureProps(feature) {
21157
+ var _a;
21158
+ // If we've checked 10 features then we should have a good handle on what props are available.
21159
+ if (this.featurePropsChecked >= 10) {
21160
+ return;
21161
+ }
21162
+ this.featurePropsChecked++;
21163
+ const featureAny = feature;
21164
+ const props = featureAny.getPropertyNames ? featureAny.getPropertyNames() : (_a = featureAny.getPropertyIds) === null || _a === void 0 ? void 0 : _a.call(featureAny);
21165
+ if (!props) {
21166
+ return;
21167
+ }
21168
+ for (const prop of props) {
21169
+ if (!prop) {
21170
+ continue;
21171
+ }
21172
+ const lowered = prop.toLowerCase();
21173
+ if (lowered === "bruceid" || lowered === "id" || lowered === "entityid") {
21174
+ this.featurePropCache.set("entityId", prop);
21175
+ }
21176
+ else if (lowered === "brucepath") {
21177
+ this.featurePropCache.set("BrucePath", prop);
21178
+ }
21179
+ else if (lowered === "building_id" || lowered === "buildingid") {
21180
+ this.featurePropCache.set("BuildingID", prop);
21181
+ }
21182
+ }
21183
+ }
20862
21184
  onCTilesetLoad() {
20863
21185
  if (this.item.FlyTo) {
20864
21186
  this.viewer.zoomTo(this.cTileset, new HeadingPitchRange(0.0, -0.5, this.cTileset.boundingSphere.radius / 4.0));
@@ -20873,6 +21195,11 @@ var TilesetArbRenderManager;
20873
21195
  }
20874
21196
  doDispose() {
20875
21197
  var _a, _b;
21198
+ if (this.featureQueueInterval) {
21199
+ clearInterval(this.featureQueueInterval);
21200
+ this.featureQueueInterval = null;
21201
+ this.featureQueue = [];
21202
+ }
20876
21203
  if (this.cTileset) {
20877
21204
  const viewer = this.viewer;
20878
21205
  if (!(viewer === null || viewer === void 0 ? void 0 : viewer.isDestroyed()) && this.viewer.scene.primitives.contains(this.cTileset)) {
@@ -20889,7 +21216,10 @@ var TilesetArbRenderManager;
20889
21216
  });
20890
21217
  }
20891
21218
  async ReRender(params) {
20892
- const { entityIds, force } = params;
21219
+ let { entityIds, force, entities } = params;
21220
+ if (entities && !entityIds) {
21221
+ entityIds = entities.map(x => { var _a; return (_a = x.Bruce) === null || _a === void 0 ? void 0 : _a.ID; });
21222
+ }
20893
21223
  if (!this.styler) {
20894
21224
  return;
20895
21225
  }
@@ -20899,25 +21229,9 @@ var TilesetArbRenderManager;
20899
21229
  if (entityIds != null) {
20900
21230
  regos = regos.filter(r => entityIds.indexOf(r.entityId) >= 0);
20901
21231
  }
20902
- this.styler.QueueEntities(regos);
20903
- }
20904
- mapCTile(tile) {
20905
- const content = tile === null || tile === void 0 ? void 0 : tile.content;
20906
- if (!content) {
20907
- return;
20908
- }
20909
- const regosToQueue = new Map();
20910
- for (let i = 0; i < content.featuresLength; i++) {
20911
- const feature = content.getFeature(i);
20912
- let rego = this.mapTilesetFeature(feature);
20913
- if (rego === null || rego === void 0 ? void 0 : rego.entityId) {
20914
- regosToQueue.set(rego.entityId, rego);
20915
- }
20916
- }
20917
- if (this.styler && regosToQueue.size) {
20918
- this.styler.QueueEntities(Array.from(regosToQueue.values()));
20919
- }
20920
- this.viewer.scene.requestRender();
21232
+ // Update the cache so we use that data instead of requesting the records.
21233
+ this.styler.SetEntityCache(entityIds, entities);
21234
+ this.styler.QueueEntities(regos, true);
20921
21235
  }
20922
21236
  mapTilesetFeature(feature) {
20923
21237
  var _a, _b, _c, _d;
@@ -20935,32 +21249,76 @@ var TilesetArbRenderManager;
20935
21249
  tilesetId: (_c = this.item.tileset) === null || _c === void 0 ? void 0 : _c.TilesetID,
20936
21250
  tilesetType: this.tilesetType
20937
21251
  };
20938
- // Two different methods for two different Cesium versions...
20939
- const props = feature.getPropertyNames ? feature.getPropertyNames([]) :
20940
- feature.getPropertyIds ? feature.getPropertyIds() : [];
20941
- if (props.find(x => x == "BrucePath")) {
20942
- const path = (_d = feature.getProperty("BrucePath")) === null || _d === void 0 ? void 0 : _d.split("|");
20943
- if (path === null || path === void 0 ? void 0 : path.length) {
20944
- rego.entityId = path[path.length - 1];
21252
+ this.evaluateFeatureProps(feature);
21253
+ if (!rego.entityId) {
21254
+ const propToUse = this.featurePropCache.get("entityId");
21255
+ if (propToUse) {
21256
+ rego.entityId = feature.getProperty(propToUse);
20945
21257
  }
20946
21258
  }
20947
21259
  if (!rego.entityId) {
20948
- // 'Building' ones are for legacy CC3D data.
20949
- const ACCEPTABLE_PROPS = [
20950
- "Building_ID", "BuildingID", "BruceId", "entityId", "bruceId"
20951
- ];
20952
- for (let acceptableId of ACCEPTABLE_PROPS) {
20953
- if (props.indexOf(acceptableId) > -1) {
20954
- rego.entityId = feature.getProperty(acceptableId);
20955
- if (rego.entityId) {
20956
- break;
20957
- }
21260
+ const propToUse = this.featurePropCache.get("BrucePath");
21261
+ if (propToUse) {
21262
+ const path = (_d = feature.getProperty(propToUse)) === null || _d === void 0 ? void 0 : _d.split("|");
21263
+ if (path === null || path === void 0 ? void 0 : path.length) {
21264
+ rego.entityId = path[path.length - 1];
20958
21265
  }
20959
21266
  }
20960
21267
  }
21268
+ if (!rego.entityId) {
21269
+ const propToUse = this.featurePropCache.get("BuildingID");
21270
+ if (propToUse) {
21271
+ rego.entityId = feature.getProperty(propToUse);
21272
+ }
21273
+ }
20961
21274
  if (!rego.entityId) {
20962
21275
  return null;
20963
21276
  }
21277
+ // Get the initial hide/show state.
21278
+ const state = this.visualsManager.GetState({
21279
+ entityId: rego.entityId,
21280
+ menuItemId: this.item.id,
21281
+ });
21282
+ if (this.somethingIsolated == null ||
21283
+ // 5s cache.
21284
+ (this.somethingIsolatedDateTime && this.somethingIsolatedDateTime.getTime() + 5000 < new Date().getTime())) {
21285
+ this.somethingIsolated = this.visualsManager.GetIsIsolatedAny();
21286
+ this.somethingIsolatedDateTime = new Date();
21287
+ }
21288
+ // Override the default hide state set by the style.
21289
+ let hide = false;
21290
+ if (state) {
21291
+ if (state.hidden) {
21292
+ hide = true;
21293
+ }
21294
+ else if (state.opacity === 0) {
21295
+ hide = true;
21296
+ }
21297
+ else if (!state.isolated && this.somethingIsolated) {
21298
+ hide = true;
21299
+ }
21300
+ }
21301
+ feature.show = !hide;
21302
+ // Already exists, so we add this graphic as a sibling.
21303
+ if (this.loadedCesiumEntities[rego.entityId]) {
21304
+ rego = this.loadedCesiumEntities[rego.entityId];
21305
+ if (!rego.visual) {
21306
+ // No parent graphic.
21307
+ rego.visual = feature;
21308
+ }
21309
+ else if (rego.visual == feature) ;
21310
+ else {
21311
+ const visual = rego.visual;
21312
+ // Sibling graphic.
21313
+ if (!visual._siblingGraphics) {
21314
+ visual._siblingGraphics = [];
21315
+ }
21316
+ if (visual._siblingGraphics.indexOf(feature) < 0) {
21317
+ visual._siblingGraphics.push(feature);
21318
+ }
21319
+ }
21320
+ }
21321
+ this.loadedCesiumEntities[rego.entityId] = rego;
20964
21322
  this.visualsManager.AddRego({
20965
21323
  rego,
20966
21324
  requestRender: false
@@ -32831,6 +33189,9 @@ const colors = {};
32831
33189
  const names = {};
32832
33190
  const entities = {};
32833
33191
  const viewers = {};
33192
+ // Global shift to move cursor + label down in pixels
33193
+ const CURSOR_SHIFT_Y = 22;
33194
+ const CURSOR_SHIFT_X = 10;
32834
33195
  function createDOMLabel(viewer, id, name, colorCss) {
32835
33196
  var _a;
32836
33197
  const label = document.createElement("div");
@@ -32885,8 +33246,8 @@ function updateDOMLabel(viewer, label, pos3d) {
32885
33246
  // Keep full opacity; no distance-based fading
32886
33247
  // Keep the label close to the cursor regardless of zoom
32887
33248
  // Use small, constant pixel offsets to bottom-right
32888
- const offsetX = 12; // px
32889
- const offsetY = 10; // px
33249
+ const offsetX = 6 + CURSOR_SHIFT_X; // px
33250
+ const offsetY = 4 + CURSOR_SHIFT_Y; // px (shift label further down)
32890
33251
  let leftPx = screenPos.x + offsetX;
32891
33252
  let topPx = screenPos.y + offsetY;
32892
33253
  const pad = 8;
@@ -32937,7 +33298,9 @@ var LiveCursor;
32937
33298
  distanceDisplayCondition: new DistanceDisplayCondition(0, 30000),
32938
33299
  scaleByDistance: new NearFarScalar(100, 1.5, 10000, 0.8),
32939
33300
  verticalOrigin: VerticalOrigin.BOTTOM,
32940
- horizontalOrigin: HorizontalOrigin.CENTER
33301
+ horizontalOrigin: HorizontalOrigin.CENTER,
33302
+ // Shift billboard down; Cesium's +Y is up so use negative
33303
+ pixelOffset: new Cartesian2(CURSOR_SHIFT_X, CURSOR_SHIFT_Y)
32941
33304
  } : undefined,
32942
33305
  ellipse: showEllipse ? {
32943
33306
  semiMajorAxis: 50.0,
@@ -32975,6 +33338,10 @@ var LiveCursor;
32975
33338
  viewer: viewer
32976
33339
  });
32977
33340
  entity.position = new CallbackProperty(() => animated.GetValue(), false);
33341
+ // Ensure existing billboard is shifted consistently
33342
+ if (entity.billboard) {
33343
+ entity.billboard.pixelOffset = new Cartesian2(CURSOR_SHIFT_X, CURSOR_SHIFT_Y);
33344
+ }
32978
33345
  }
32979
33346
  if (!updaters[id]) {
32980
33347
  const remover = viewer.scene.postUpdate.addEventListener(() => {
@@ -33002,6 +33369,14 @@ var LiveCursor;
33002
33369
  }
33003
33370
  LiveCursor.Upsert = Upsert;
33004
33371
  function Remove(id) {
33372
+ // Also remove any camera visuals tracked for this cursor
33373
+ const viewer = viewers[id];
33374
+ if (viewer) {
33375
+ try {
33376
+ RemoveCameraVisuals(viewer, id);
33377
+ }
33378
+ catch { }
33379
+ }
33005
33380
  const label = labels[id];
33006
33381
  if (label && label.parentElement) {
33007
33382
  label.parentElement.removeChild(label);
@@ -33019,7 +33394,6 @@ var LiveCursor;
33019
33394
  }
33020
33395
  // Remove entity if present
33021
33396
  const entity = entities[id];
33022
- const viewer = viewers[id];
33023
33397
  if (entity && viewer && viewer.entities.contains(entity)) {
33024
33398
  viewer.entities.remove(entity);
33025
33399
  }
@@ -33027,6 +33401,136 @@ var LiveCursor;
33027
33401
  delete viewers[id];
33028
33402
  }
33029
33403
  LiveCursor.Remove = Remove;
33404
+ const cameraVisuals = {};
33405
+ const cameraUpdateBuffer = {};
33406
+ const CAMERA_UPDATE_INTERVAL = 500; // ms
33407
+ const CAMERA_BUFFER_TIMEOUT = 1000; // ms
33408
+ function UpsertCameraVisuals(params) {
33409
+ if (params.throttled) {
33410
+ return throttledUpsertCameraVisuals(params);
33411
+ }
33412
+ return internalUpsertCameraVisuals(params);
33413
+ }
33414
+ LiveCursor.UpsertCameraVisuals = UpsertCameraVisuals;
33415
+ function throttledUpsertCameraVisuals(params) {
33416
+ var _a, _b;
33417
+ const { viewer, id, camera, color } = params;
33418
+ const now = Date.now();
33419
+ cameraUpdateBuffer[id] = {
33420
+ camera: { ...camera },
33421
+ color,
33422
+ lastBufferTime: now
33423
+ };
33424
+ const existing = cameraVisuals[id];
33425
+ if (!(existing === null || existing === void 0 ? void 0 : existing.cone) || (now - ((_a = existing.lastUpdate) !== null && _a !== void 0 ? _a : 0)) >= CAMERA_UPDATE_INTERVAL) {
33426
+ internalUpsertCameraVisuals(params);
33427
+ return;
33428
+ }
33429
+ setTimeout(() => {
33430
+ const buffered = cameraUpdateBuffer[id];
33431
+ if (buffered && (now - buffered.lastBufferTime) < CAMERA_BUFFER_TIMEOUT) {
33432
+ internalUpsertCameraVisuals({
33433
+ viewer,
33434
+ id,
33435
+ camera: buffered.camera,
33436
+ color: buffered.color
33437
+ });
33438
+ }
33439
+ }, Math.max(0, CAMERA_UPDATE_INTERVAL - (now - ((_b = existing.lastUpdate) !== null && _b !== void 0 ? _b : 0))));
33440
+ }
33441
+ function internalUpsertCameraVisuals(params) {
33442
+ var _a, _b, _c, _d, _e, _f, _g, _h;
33443
+ const { viewer, id, camera, color } = params;
33444
+ if (!viewer || viewer.isDestroyed() || !(camera === null || camera === void 0 ? void 0 : camera.pos)) {
33445
+ return;
33446
+ }
33447
+ const camPos = new Cartesian3(camera.pos.x, camera.pos.y, camera.pos.z);
33448
+ // Adjust heading by -90 degrees to correct coordinate system mismatch
33449
+ const adjustedHeading = ((_a = camera.headingDeg) !== null && _a !== void 0 ? _a : 0) - 90;
33450
+ const headingRad = Math$1.toRadians(adjustedHeading);
33451
+ const pitchRad = Math$1.toRadians((_b = camera.pitchDeg) !== null && _b !== void 0 ? _b : 0);
33452
+ const rollRad = Math$1.toRadians((_c = camera.rollDeg) !== null && _c !== void 0 ? _c : 0);
33453
+ const hpr = new HeadingPitchRoll(headingRad, pitchRad, rollRad);
33454
+ const baseOrientation = Transforms.headingPitchRollQuaternion(camPos, hpr);
33455
+ // Cylinder axis is local +Z, but HPR 'forward' is along +X. Tilt so Z -> X.
33456
+ const tilt = Quaternion.fromAxisAngle(Cartesian3.UNIT_Y, Math$1.toRadians(-90));
33457
+ const orientation = Quaternion.multiply(baseOrientation, tilt, new Quaternion());
33458
+ const vFovDeg = Math.max(1, Math.min(170, (_d = camera.fovDeg) !== null && _d !== void 0 ? _d : 60));
33459
+ const aspect = (viewer.canvas && viewer.canvas.clientHeight > 0)
33460
+ ? (viewer.canvas.clientWidth / viewer.canvas.clientHeight)
33461
+ : 16 / 9;
33462
+ const hFovDeg = Math$1.toDegrees(2 * Math.atan(Math.tan(Math$1.toRadians(vFovDeg) / 2) * aspect));
33463
+ const length = Math.max(10, (_e = camera.rangeMeters) !== null && _e !== void 0 ? _e : 200);
33464
+ // Use horizontal FOV to approximate viewing cone width
33465
+ const bottomRadius = Math.tan(Math$1.toRadians(hFovDeg / 2)) * length;
33466
+ // Offset position so cone extends forward from camera
33467
+ const rotation = Matrix3.fromQuaternion(orientation);
33468
+ const forward = Matrix3.multiplyByVector(rotation, Cartesian3.UNIT_Z, new Cartesian3());
33469
+ const offset = Cartesian3.multiplyByScalar(forward, length / 2, new Cartesian3());
33470
+ const conePos = Cartesian3.add(camPos, offset, new Cartesian3());
33471
+ const coneId = `user-camera-cone-${id}`;
33472
+ const now = Date.now();
33473
+ const existing = (_f = cameraVisuals[id]) !== null && _f !== void 0 ? _f : {};
33474
+ // Upsert cone entity
33475
+ if (!existing.cone) {
33476
+ const cone = viewer.entities.add({
33477
+ id: coneId,
33478
+ position: conePos,
33479
+ orientation: orientation,
33480
+ cylinder: {
33481
+ length,
33482
+ topRadius: 0,
33483
+ bottomRadius,
33484
+ material: color.withAlpha(0.25),
33485
+ outline: true,
33486
+ outlineColor: color.withAlpha(0.8),
33487
+ numberOfVerticalLines: 0,
33488
+ slices: 64,
33489
+ distanceDisplayCondition: new DistanceDisplayCondition(0, 50000)
33490
+ }
33491
+ });
33492
+ existing.cone = cone;
33493
+ existing.lastUpdate = now;
33494
+ }
33495
+ else {
33496
+ const needsUpdate = (!Cartesian3.equals(((_g = existing.cone.position) === null || _g === void 0 ? void 0 : _g.getValue(JulianDate.now())) || new Cartesian3(), conePos) ||
33497
+ !Quaternion.equals(((_h = existing.cone.orientation) === null || _h === void 0 ? void 0 : _h.getValue(JulianDate.now())) || new Quaternion(), orientation));
33498
+ if (needsUpdate) {
33499
+ viewer.entities.suspendEvents();
33500
+ try {
33501
+ existing.cone.position = conePos;
33502
+ existing.cone.orientation = orientation;
33503
+ const cylinder = existing.cone.cylinder;
33504
+ if (cylinder) {
33505
+ cylinder.length = length;
33506
+ cylinder.bottomRadius = bottomRadius;
33507
+ cylinder.material = color.withAlpha(0.25);
33508
+ cylinder.outlineColor = color.withAlpha(0.8);
33509
+ }
33510
+ }
33511
+ finally {
33512
+ viewer.entities.resumeEvents();
33513
+ }
33514
+ }
33515
+ existing.lastUpdate = now;
33516
+ }
33517
+ cameraVisuals[id] = existing;
33518
+ }
33519
+ function RemoveCameraVisuals(viewer, id) {
33520
+ const existing = cameraVisuals[id];
33521
+ if (!existing) {
33522
+ return;
33523
+ }
33524
+ if (existing.cone) {
33525
+ try {
33526
+ viewer.entities.remove(existing.cone);
33527
+ }
33528
+ catch { }
33529
+ }
33530
+ delete cameraVisuals[id];
33531
+ delete cameraUpdateBuffer[id];
33532
+ }
33533
+ LiveCursor.RemoveCameraVisuals = RemoveCameraVisuals;
33030
33534
  })(LiveCursor || (LiveCursor = {}));
33031
33535
 
33032
33536
  var WidgetControlViewBar;
@@ -33801,7 +34305,7 @@ class WidgetViewBar extends Widget.AWidget {
33801
34305
  }
33802
34306
  }
33803
34307
 
33804
- const VERSION = "6.1.8";
34308
+ const VERSION = "6.2.0";
33805
34309
 
33806
34310
  export { VERSION, CesiumViewMonitor, ViewerUtils, ViewerEventTracker, MenuItemManager, isOutlineChanged, EntityRenderEngine, EntityRenderEnginePoint, EntityRenderEnginePolyline, EntityRenderEnginePolygon, EntityRenderEngineModel3d, MenuItemCreator, VisualsRegister, RenderManager, EntitiesIdsRenderManager, DataLabRenderManager, EntitiesLoadedRenderManager, EntitiesRenderManager, EntityRenderManager, TilesetCadRenderManager, TilesetArbRenderManager, TilesetEntitiesRenderManager, TilesetOsmRenderManager, TilesetPointcloudRenderManager, TilesetGooglePhotosRenderManager, DataSourceStaticKmlManager, GoogleSearchRenderManager, AssemblyRenderManager, RelationsRenderManager, SharedGetters, CesiumParabola, EntityLabel, LiveCursor, ViewRenderEngine, TileRenderEngine, TilesetRenderEngine, CESIUM_INSPECTOR_KEY, CESIUM_TIMELINE_KEY, CESIUM_TIMELINE_LIVE_KEY, CESIUM_TIMELINE_LIVE_PADDING_KEY, CESIUM_TIMELINE_INTERVAL_KEY, CESIUM_MODEL_SPACE_KEY, DEFAULT_LIVE_PADDING_SECONDS, 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 };
33807
34311
  //# sourceMappingURL=bruce-cesium.es5.js.map