bruce-cesium 2.1.7 → 2.1.8

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.
@@ -1515,6 +1515,23 @@
1515
1515
  return zoomItem.MinZoom + "-" + zoomItem.MaxZoom + "-" + shouldApplyFlatFix(terrain);
1516
1516
  }
1517
1517
  var _fileValidationCache = {};
1518
+ function getDisplayCondition(min, max) {
1519
+ // Max is required.
1520
+ if (isNaN(+max)) {
1521
+ return undefined;
1522
+ }
1523
+ // Min is optional.
1524
+ if (isNaN(+min)) {
1525
+ min = 0;
1526
+ }
1527
+ // Adjusting slightly because I distrust our initial calculation vs Cesium's one.
1528
+ max = (+max) * 1.2;
1529
+ min = +min;
1530
+ if (min > 0) {
1531
+ min = (+min) * 0.8;
1532
+ }
1533
+ return new Cesium.DistanceDisplayCondition(min, max);
1534
+ }
1518
1535
  (function (EntityRenderEngine) {
1519
1536
  function Render(params) {
1520
1537
  var _a, _b, _c, _d, _e, _f, _g, _h;
@@ -1845,7 +1862,10 @@
1845
1862
  verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
1846
1863
  image: iconUrl,
1847
1864
  heightReference: getHeightRef(style),
1848
- scale: iconScale
1865
+ scale: iconScale,
1866
+ distanceDisplayCondition: getDisplayCondition(params.minDistance, params.maxDistance),
1867
+ // Would be great once we have a setting for this.
1868
+ // translucencyByDistance: getTranslucencyByDistance(params.minDistance, params.maxDistance),
1849
1869
  },
1850
1870
  position: exports.EntityUtils.GetPos({
1851
1871
  viewer: params.viewer,
@@ -1896,7 +1916,8 @@
1896
1916
  extrudedHeight: fillHeight,
1897
1917
  heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
1898
1918
  extrudedHeightReference: exHeightRef,
1899
- zIndex: 1
1919
+ zIndex: 1,
1920
+ distanceDisplayCondition: getDisplayCondition(params.minDistance, params.maxDistance)
1900
1921
  },
1901
1922
  position: pos === null || pos === void 0 ? void 0 : pos.clone(),
1902
1923
  show: false
@@ -1916,7 +1937,8 @@
1916
1937
  extrudedHeight: outlineHeight,
1917
1938
  heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
1918
1939
  extrudedHeightReference: exHeightRef,
1919
- zIndex: 2
1940
+ zIndex: 2,
1941
+ distanceDisplayCondition: getDisplayCondition(params.minDistance, params.maxDistance)
1920
1942
  },
1921
1943
  position: pos === null || pos === void 0 ? void 0 : pos.clone()
1922
1944
  }));
@@ -1938,7 +1960,8 @@
1938
1960
  point: {
1939
1961
  pixelSize: size,
1940
1962
  color: cColor,
1941
- heightReference: getHeightRef(style)
1963
+ heightReference: getHeightRef(style),
1964
+ distanceDisplayCondition: getDisplayCondition(params.minDistance, params.maxDistance)
1942
1965
  },
1943
1966
  position: exports.EntityUtils.GetPos({
1944
1967
  viewer: params.viewer,
@@ -1999,7 +2022,9 @@
1999
2022
  style: pStyle,
2000
2023
  tags: tags,
2001
2024
  viewer: params.viewer,
2002
- api: api
2025
+ api: api,
2026
+ maxDistance: zoomItem.MaxZoom,
2027
+ minDistance: zoomItem.MinZoom
2003
2028
  })];
2004
2029
  case 5:
2005
2030
  cEntity = _d.sent();
@@ -2090,7 +2115,8 @@
2090
2115
  classificationType: Cesium.ClassificationType.TERRAIN,
2091
2116
  arcType: Cesium.ArcType.GEODESIC,
2092
2117
  zIndex: getZIndex(style, entity, params.tags),
2093
- clampToGround: heightRef == Cesium.HeightReference.CLAMP_TO_GROUND
2118
+ clampToGround: heightRef == Cesium.HeightReference.CLAMP_TO_GROUND,
2119
+ distanceDisplayCondition: getDisplayCondition(params.minDistance, params.maxDistance)
2094
2120
  },
2095
2121
  position: exports.EntityUtils.GetPos({
2096
2122
  viewer: params.viewer,
@@ -2138,7 +2164,9 @@
2138
2164
  entity: entity,
2139
2165
  style: lStyle,
2140
2166
  tags: tags,
2141
- viewer: params.viewer
2167
+ viewer: params.viewer,
2168
+ maxDistance: zoomItem.MaxZoom,
2169
+ minDistance: zoomItem.MinZoom
2142
2170
  });
2143
2171
  if (cEntity) {
2144
2172
  cEntity._renderGroup = getRenderGroupId(zoomItem, (_c = params.viewer) === null || _c === void 0 ? void 0 : _c.terrainProvider);
@@ -2210,7 +2238,8 @@
2210
2238
  heightReference: heightRef,
2211
2239
  classificationType: Cesium.ClassificationType.BOTH,
2212
2240
  perPositionHeight: heightRef == Cesium.HeightReference.CLAMP_TO_GROUND ? false : true,
2213
- zIndex: zIndex
2241
+ zIndex: zIndex,
2242
+ distanceDisplayCondition: getDisplayCondition(params.minDistance, params.maxDistance)
2214
2243
  },
2215
2244
  position: exports.EntityUtils.GetPos({
2216
2245
  viewer: params.viewer,
@@ -2235,7 +2264,8 @@
2235
2264
  clampToGround: heightRef == Cesium.HeightReference.CLAMP_TO_GROUND,
2236
2265
  classificationType: Cesium.ClassificationType.TERRAIN,
2237
2266
  arcType: Cesium.ArcType.GEODESIC,
2238
- zIndex: zIndex
2267
+ zIndex: zIndex,
2268
+ distanceDisplayCondition: getDisplayCondition(params.minDistance, params.maxDistance)
2239
2269
  }),
2240
2270
  show: false
2241
2271
  });
@@ -2253,7 +2283,8 @@
2253
2283
  clampToGround: heightRef == Cesium.HeightReference.CLAMP_TO_GROUND,
2254
2284
  classificationType: Cesium.ClassificationType.TERRAIN,
2255
2285
  arcType: Cesium.ArcType.GEODESIC,
2256
- zIndex: zIndex
2286
+ zIndex: zIndex,
2287
+ distanceDisplayCondition: getDisplayCondition(params.minDistance, params.maxDistance)
2257
2288
  }),
2258
2289
  show: false
2259
2290
  });
@@ -2300,7 +2331,9 @@
2300
2331
  entity: entity,
2301
2332
  style: pStyle,
2302
2333
  tags: tags,
2303
- viewer: params.viewer
2334
+ viewer: params.viewer,
2335
+ maxDistance: zoomItem.MaxZoom,
2336
+ minDistance: zoomItem.MinZoom
2304
2337
  });
2305
2338
  if (cEntity) {
2306
2339
  cEntity._renderGroup = getRenderGroupId(zoomItem, (_c = params.viewer) === null || _c === void 0 ? void 0 : _c.terrainProvider);
@@ -2377,7 +2410,8 @@
2377
2410
  shadows: Cesium.ShadowMode.ENABLED,
2378
2411
  colorBlendAmount: blendAmount,
2379
2412
  colorBlendMode: blendMode,
2380
- color: color
2413
+ color: color,
2414
+ distanceDisplayCondition: getDisplayCondition(params.minDistance, params.maxDistance)
2381
2415
  },
2382
2416
  orientation: orientation,
2383
2417
  position: pos,
@@ -2491,7 +2525,9 @@
2491
2525
  api: api,
2492
2526
  fileId: lod.clientFileId
2493
2527
  }),
2494
- lodClientFileId: lod.clientFileId
2528
+ lodClientFileId: lod.clientFileId,
2529
+ maxDistance: zoomItem.MaxZoom,
2530
+ minDistance: zoomItem.MinZoom
2495
2531
  });
2496
2532
  if (cEntity) {
2497
2533
  cEntity._renderGroup = getRenderGroupId(zoomItem, (_e = params.viewer) === null || _e === void 0 ? void 0 : _e.terrainProvider);
@@ -2521,6 +2557,602 @@
2521
2557
  })(Model3d = EntityRenderEngine.Model3d || (EntityRenderEngine.Model3d = {}));
2522
2558
  })(exports.EntityRenderEngine || (exports.EntityRenderEngine = {}));
2523
2559
 
2560
+ function GetValue$1(viewer, obj) {
2561
+ if (obj === null || obj === void 0 ? void 0 : obj.getValue) {
2562
+ return obj.getValue(viewer.scene.lastRenderTime);
2563
+ }
2564
+ return obj;
2565
+ }
2566
+ var Point = /** @class */ (function () {
2567
+ function Point(lon, lat, id) {
2568
+ this.lon = lon;
2569
+ this.lat = lat;
2570
+ this.id = id;
2571
+ }
2572
+ return Point;
2573
+ }());
2574
+ var Circle = /** @class */ (function () {
2575
+ function Circle(x, y, radius) {
2576
+ this.x = x;
2577
+ this.y = y;
2578
+ this.radius = radius;
2579
+ this.width = 2 * radius;
2580
+ this.height = 2 * radius;
2581
+ }
2582
+ Circle.prototype.Intersects = function (range) {
2583
+ var xDist = Math.abs(range.x - this.x);
2584
+ var yDist = Math.abs(range.y - this.y);
2585
+ var r = this.radius;
2586
+ var w = range.width;
2587
+ var h = range.height;
2588
+ if (xDist > (w + r) || yDist > (h + r)) {
2589
+ return false;
2590
+ }
2591
+ if (xDist <= w || yDist <= h) {
2592
+ return true;
2593
+ }
2594
+ var dx = xDist - w;
2595
+ var dy = yDist - h;
2596
+ return (dx * dx + dy * dy <= this.radius * this.radius);
2597
+ };
2598
+ Circle.prototype.Contains = function (point) {
2599
+ var dx = this.x - point.lon;
2600
+ var dy = this.y - point.lat;
2601
+ var distance = Math.sqrt(dx * dx + dy * dy);
2602
+ return distance <= this.radius;
2603
+ };
2604
+ return Circle;
2605
+ }());
2606
+ var Rectangle = /** @class */ (function () {
2607
+ function Rectangle(x, y, w, h) {
2608
+ this.x = x;
2609
+ this.y = y;
2610
+ this.w = w;
2611
+ this.h = h;
2612
+ this.width = 2 * w;
2613
+ this.height = 2 * h;
2614
+ }
2615
+ Rectangle.prototype.Contains = function (point) {
2616
+ return (point.lon >= this.x - this.w &&
2617
+ point.lon < this.x + this.w &&
2618
+ point.lat >= this.y - this.h &&
2619
+ point.lat < this.y + this.h);
2620
+ };
2621
+ Rectangle.prototype.Intersects = function (range) {
2622
+ return !(range.x - range.w > this.x + this.w ||
2623
+ range.x + range.w < this.x - this.w ||
2624
+ range.y - range.h > this.y + this.h ||
2625
+ range.y + range.h < this.y - this.h);
2626
+ };
2627
+ return Rectangle;
2628
+ }());
2629
+ var Quad = /** @class */ (function () {
2630
+ function Quad(boundary, capacity) {
2631
+ this.boundary = boundary;
2632
+ this.capacity = capacity;
2633
+ this.points = [];
2634
+ this.divided = false;
2635
+ }
2636
+ Quad.prototype.Subdivide = function () {
2637
+ var x = this.boundary.x;
2638
+ var y = this.boundary.y;
2639
+ var w = this.boundary.w / 2;
2640
+ var h = this.boundary.h / 2;
2641
+ var ne = new Rectangle(x + w / 2, y - h / 2, w, h);
2642
+ this.northeast = new Quad(ne, this.capacity);
2643
+ var nw = new Rectangle(x - w / 2, y - h / 2, w, h);
2644
+ this.northwest = new Quad(nw, this.capacity);
2645
+ var se = new Rectangle(x + w / 2, y + h / 2, w, h);
2646
+ this.southeast = new Quad(se, this.capacity);
2647
+ var sw = new Rectangle(x - w / 2, y + h / 2, w, h);
2648
+ this.southwest = new Quad(sw, this.capacity);
2649
+ this.divided = true;
2650
+ };
2651
+ Quad.prototype.Insert = function (point) {
2652
+ if (!this.boundary.Contains(point)) {
2653
+ return false;
2654
+ }
2655
+ if (this.points.length < this.capacity) {
2656
+ this.points.push(point);
2657
+ return true;
2658
+ }
2659
+ if (!this.divided) {
2660
+ this.Subdivide();
2661
+ }
2662
+ return (this.northeast.Insert(point) || this.northwest.Insert(point) ||
2663
+ this.southeast.Insert(point) || this.southwest.Insert(point));
2664
+ };
2665
+ Quad.prototype.Query = function (range, found) {
2666
+ if (!found) {
2667
+ found = [];
2668
+ }
2669
+ if (!range.Intersects(this.boundary)) {
2670
+ return found;
2671
+ }
2672
+ for (var _i = 0, _a = this.points; _i < _a.length; _i++) {
2673
+ var p = _a[_i];
2674
+ if (range.Contains(p)) {
2675
+ found.push(p);
2676
+ }
2677
+ }
2678
+ if (this.divided) {
2679
+ this.northwest.Query(range, found);
2680
+ this.northeast.Query(range, found);
2681
+ this.southwest.Query(range, found);
2682
+ this.southeast.Query(range, found);
2683
+ }
2684
+ return found;
2685
+ };
2686
+ Quad.prototype.Find = function (id) {
2687
+ for (var _i = 0, _a = this.points; _i < _a.length; _i++) {
2688
+ var point = _a[_i];
2689
+ if (point.id === id) {
2690
+ return point;
2691
+ }
2692
+ }
2693
+ if (this.divided) {
2694
+ return this.northwest.Find(id) || this.northeast.Find(id) ||
2695
+ this.southwest.Find(id) || this.southeast.Find(id);
2696
+ }
2697
+ return null;
2698
+ };
2699
+ Quad.prototype.Remove = function (point) {
2700
+ var index = this.points.indexOf(point);
2701
+ if (index !== -1) {
2702
+ this.points.splice(index, 1);
2703
+ }
2704
+ if (this.divided) {
2705
+ this.northwest.Remove(point);
2706
+ this.northeast.Remove(point);
2707
+ this.southwest.Remove(point);
2708
+ this.southeast.Remove(point);
2709
+ }
2710
+ };
2711
+ return Quad;
2712
+ }());
2713
+ var PointClustering = /** @class */ (function () {
2714
+ function PointClustering(register, menuItemId) {
2715
+ var _this = this;
2716
+ this.disposed = false;
2717
+ this.registeredEntityIds = new Set();
2718
+ this.register = register;
2719
+ this.viewer = register.Viewer;
2720
+ this.menuItemId = menuItemId;
2721
+ this.updateClusterSpacing(0);
2722
+ var boundary = new Rectangle(0, 0, 360, 180);
2723
+ this.quadTree = new Quad(boundary, 4);
2724
+ this.prevClusteredEntities = new Set();
2725
+ this.currClusteredEntities = new Set();
2726
+ this.clusterEntities = new Map();
2727
+ this.updateQueue = new bruceModels.DelayQueue(function () {
2728
+ _this.doUpdate();
2729
+ }, 1000);
2730
+ this.listenCamera();
2731
+ }
2732
+ /**
2733
+ * Starts listening to camera changes.
2734
+ * This will trigger the clustering update whenever camera is moved.
2735
+ */
2736
+ PointClustering.prototype.listenCamera = function () {
2737
+ var _this = this;
2738
+ var viewer = this.viewer;
2739
+ var lastPos3d;
2740
+ var hasMoved = function () {
2741
+ var _a;
2742
+ var cameraPos = (_a = viewer === null || viewer === void 0 ? void 0 : viewer.camera) === null || _a === void 0 ? void 0 : _a.positionCartographic;
2743
+ if (!cameraPos) {
2744
+ return false;
2745
+ }
2746
+ var pos3d = Cesium.Cartographic.toCartesian(cameraPos);
2747
+ if (!lastPos3d) {
2748
+ lastPos3d = pos3d;
2749
+ return false;
2750
+ }
2751
+ var distance = Cesium.Cartesian3.distance(pos3d, lastPos3d);
2752
+ lastPos3d = pos3d;
2753
+ // 50 meter movement at least.
2754
+ return distance > 50;
2755
+ };
2756
+ this.listenCameraRemoval = viewer.scene.camera.changed.addEventListener(function () {
2757
+ if (hasMoved()) {
2758
+ _this.updateQueue.Call();
2759
+ }
2760
+ });
2761
+ };
2762
+ /**
2763
+ * Stops listening to camera changes.
2764
+ */
2765
+ PointClustering.prototype.unlistenCamera = function () {
2766
+ if (this.listenCameraRemoval) {
2767
+ this.listenCameraRemoval();
2768
+ this.listenCameraRemoval = null;
2769
+ }
2770
+ };
2771
+ /**
2772
+ * Removes all clusters and updates entities to no longer be suppressed.
2773
+ * @returns
2774
+ */
2775
+ PointClustering.prototype.Dispose = function () {
2776
+ if (this.disposed) {
2777
+ return;
2778
+ }
2779
+ for (var _i = 0, _a = Array.from(this.clusterEntities); _i < _a.length; _i++) {
2780
+ var _b = _a[_i], entity = _b[1];
2781
+ this.viewer.entities.remove(entity);
2782
+ }
2783
+ this.clusterEntities.clear();
2784
+ this.unlistenCamera();
2785
+ this.disposed = true;
2786
+ // Restore entities.
2787
+ var toUpdateIds = [];
2788
+ for (var _c = 0, _d = Array.from(this.registeredEntityIds); _c < _d.length; _c++) {
2789
+ var id = _d[_c];
2790
+ var rego = this.register.GetRego({
2791
+ entityId: id,
2792
+ menuItemId: this.menuItemId
2793
+ });
2794
+ if (rego && rego.suppressShow) {
2795
+ rego.suppressShow = false;
2796
+ toUpdateIds.push(id);
2797
+ }
2798
+ }
2799
+ if (toUpdateIds.length) {
2800
+ this.register.ForceUpdate({
2801
+ entityIds: toUpdateIds,
2802
+ });
2803
+ }
2804
+ };
2805
+ /**
2806
+ * Calculates center of given points.
2807
+ * @param points
2808
+ * @returns
2809
+ */
2810
+ PointClustering.prototype.calculateCentroid = function (points) {
2811
+ var lonSum = 0;
2812
+ var latSum = 0;
2813
+ for (var _i = 0, points_1 = points; _i < points_1.length; _i++) {
2814
+ var point = points_1[_i];
2815
+ lonSum += point.lon;
2816
+ latSum += point.lat;
2817
+ }
2818
+ var lonAvg = lonSum / points.length;
2819
+ var latAvg = latSum / points.length;
2820
+ return new Point(lonAvg, latAvg, "");
2821
+ };
2822
+ /**
2823
+ * Gathers current clusters and renders them.
2824
+ * @returns
2825
+ */
2826
+ PointClustering.prototype.doUpdate = function () {
2827
+ var _this = this;
2828
+ if (this.disposed) {
2829
+ return;
2830
+ }
2831
+ // 1: Update precision.
2832
+ // This defines how far apart these clusters can be.
2833
+ var cameraHeight = this.viewer.camera.positionCartographic.height;
2834
+ this.updateClusterSpacing(cameraHeight);
2835
+ // 2: Get clusters.
2836
+ var _a = this.getClusters(), clusters = _a.clusters, noLongerClustered = _a.noLongerClustered;
2837
+ // 3: Remove all cesium cluster entities that are no longer clustered.
2838
+ for (var _i = 0, _b = Array.from(noLongerClustered); _i < _b.length; _i++) {
2839
+ var id = _b[_i];
2840
+ this.removeClusterEntity(id);
2841
+ }
2842
+ // 4: Stop hiding entities that are no longer clustered.
2843
+ var previousEntityIds = new Set(this.registeredEntityIds);
2844
+ for (var _c = 0, _d = Array.from(previousEntityIds); _c < _d.length; _c++) {
2845
+ var id = _d[_c];
2846
+ if (!this.currClusteredEntities.has(id)) {
2847
+ var rego = this.register.GetRego({
2848
+ entityId: id,
2849
+ menuItemId: this.menuItemId
2850
+ });
2851
+ if (rego && rego.suppressShow) {
2852
+ rego.suppressShow = false;
2853
+ this.register.ForceUpdate({
2854
+ entityIds: [id],
2855
+ });
2856
+ }
2857
+ }
2858
+ }
2859
+ var getScale = function (count) {
2860
+ var baseSize = 20;
2861
+ var scalingFactor = 2;
2862
+ var scale = baseSize + scalingFactor * (count - 1);
2863
+ scale = Math.min(scale, 120);
2864
+ return scale;
2865
+ };
2866
+ var getLabel = function (count) {
2867
+ var color = _this.pointColorTxt ? _this.pointColorTxt : "white";
2868
+ var canvas = document.createElement("canvas");
2869
+ var ctx = canvas.getContext("2d");
2870
+ var size = getScale(count);
2871
+ canvas.width = size;
2872
+ canvas.height = size;
2873
+ ctx.font = "bold 20px Arial";
2874
+ ctx.fillStyle = color;
2875
+ ctx.textAlign = "center";
2876
+ ctx.textBaseline = "middle";
2877
+ ctx.fillText(String(count), size / 2, size / 2);
2878
+ return canvas;
2879
+ };
2880
+ // 5: iterate over clusters and add/update them as Cesium entities.
2881
+ for (var _e = 0, clusters_1 = clusters; _e < clusters_1.length; _e++) {
2882
+ var cluster = clusters_1[_e];
2883
+ var clusterId = cluster.center.id;
2884
+ var clusterEntity = this.clusterEntities.get(clusterId);
2885
+ var centroid = this.calculateCentroid(cluster.points);
2886
+ var count = cluster.points.length;
2887
+ if (clusterEntity) {
2888
+ clusterEntity.position = Cesium.Cartesian3.fromDegrees(centroid.lon, centroid.lat, 150);
2889
+ clusterEntity.point.pixelSize = getScale(cluster.points.length);
2890
+ clusterEntity.billboard.image = getLabel(cluster.points.length);
2891
+ clusterEntity.billboard.width = getScale(cluster.points.length);
2892
+ clusterEntity.billboard.height = getScale(cluster.points.length);
2893
+ }
2894
+ else {
2895
+ clusterEntity = this.viewer.entities.add({
2896
+ position: Cesium.Cartesian3.fromDegrees(centroid.lon, centroid.lat, 150),
2897
+ point: {
2898
+ heightReference: Cesium.HeightReference.NONE,
2899
+ pixelSize: getScale(count),
2900
+ color: this.pointColorBg ? this.pointColorBg : Cesium.Color.fromCssColorString("#4287f5")
2901
+ },
2902
+ billboard: {
2903
+ heightReference: Cesium.HeightReference.NONE,
2904
+ image: getLabel(count),
2905
+ width: getScale(count),
2906
+ height: getScale(count),
2907
+ verticalOrigin: Cesium.VerticalOrigin.CENTER,
2908
+ horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
2909
+ disableDepthTestDistance: Number.POSITIVE_INFINITY
2910
+ }
2911
+ });
2912
+ this.clusterEntities.set(clusterId, clusterEntity);
2913
+ }
2914
+ for (var i = 0; i < cluster.points.length; i++) {
2915
+ var entityId = cluster.points[i].id;
2916
+ var rego = this.register.GetRego({
2917
+ entityId: entityId,
2918
+ menuItemId: this.menuItemId
2919
+ });
2920
+ if (rego && !rego.suppressShow) {
2921
+ rego.suppressShow = true;
2922
+ this.register.ForceUpdate({
2923
+ entityIds: [entityId],
2924
+ });
2925
+ }
2926
+ }
2927
+ }
2928
+ var _loop_1 = function (clusterId, clusterEntity) {
2929
+ if (!clusters.find(function (x) { return x.center.id == clusterId; })) {
2930
+ this_1.viewer.entities.remove(clusterEntity);
2931
+ this_1.clusterEntities.delete(clusterId);
2932
+ }
2933
+ };
2934
+ var this_1 = this;
2935
+ // 6: Iterate over existing cluster entities and remove those that are no longer clustered.
2936
+ for (var _f = 0, _g = Array.from(this.clusterEntities); _f < _g.length; _f++) {
2937
+ var _h = _g[_f], clusterId = _h[0], clusterEntity = _h[1];
2938
+ _loop_1(clusterId, clusterEntity);
2939
+ }
2940
+ };
2941
+ /**
2942
+ * Updates how apart clusters can be based on camera distance.
2943
+ * @param cameraHeight
2944
+ */
2945
+ PointClustering.prototype.updateClusterSpacing = function (cameraHeight) {
2946
+ // Camera height thresholds in meters.
2947
+ var cameraHeightThresholds = [2000, 5000, 8000, 13000, 25000, 40000];
2948
+ // Distance increments in degrees.
2949
+ var distanceIncrements = [0.005, 0.01, 0.02, 0.04, 0.1, 0.5];
2950
+ // Find the appropriate spacing based on the camera height.
2951
+ var spacing = 0;
2952
+ for (var i = 0; i < cameraHeightThresholds.length; i++) {
2953
+ if (cameraHeight && cameraHeight < cameraHeightThresholds[i]) {
2954
+ spacing += distanceIncrements[i];
2955
+ break;
2956
+ }
2957
+ spacing += distanceIncrements[i];
2958
+ }
2959
+ this.distanceBetweenClusters = spacing;
2960
+ };
2961
+ /**
2962
+ * Gathers clusters.
2963
+ * @returns
2964
+ */
2965
+ PointClustering.prototype.getClusters = function () {
2966
+ var _this = this;
2967
+ this.currClusteredEntities.clear();
2968
+ var clusters = [];
2969
+ var processedPoints = new Set();
2970
+ var cameraPosition = this.viewer.camera.position;
2971
+ for (var _i = 0, _a = this.quadTree.points; _i < _a.length; _i++) {
2972
+ var point = _a[_i];
2973
+ // Skip points already processed in previous clusters.
2974
+ if (processedPoints.has(point.id)) {
2975
+ continue;
2976
+ }
2977
+ // Skip points closer than 2000 meters to the camera.
2978
+ var cartesian3 = Cesium.Cartesian3.fromDegrees(point.lon, point.lat);
2979
+ if (Cesium.Cartesian3.distance(cartesian3, cameraPosition) <= 2000) {
2980
+ continue;
2981
+ }
2982
+ var found = [];
2983
+ var nearbyPoints = this.quadTree.Query(new Circle(point.lon, point.lat, this.distanceBetweenClusters), found);
2984
+ if (nearbyPoints.length > 1) {
2985
+ var cluster = { center: point, points: [] };
2986
+ for (var _b = 0, nearbyPoints_1 = nearbyPoints; _b < nearbyPoints_1.length; _b++) {
2987
+ var nearby = nearbyPoints_1[_b];
2988
+ if (!cluster.points.includes(nearby)) {
2989
+ cluster.points.push(nearby);
2990
+ processedPoints.add(nearby.id);
2991
+ this.currClusteredEntities.add(nearby.id);
2992
+ }
2993
+ }
2994
+ clusters.push(cluster);
2995
+ }
2996
+ }
2997
+ // Filter out clusters with only one point.
2998
+ var validClusters = clusters.filter(function (cluster) { return cluster.points.length > 1; });
2999
+ // Merge adjacent clusters.
3000
+ var mergedClusters = this.mergeClusters(validClusters);
3001
+ // Get the entity IDs that are no longer clustered
3002
+ var noLongerClustered = new Set(Array.from(this.prevClusteredEntities).filter(function (id) { return !_this.currClusteredEntities.has(id); }));
3003
+ // Update the previous clustered entities ref.
3004
+ this.prevClusteredEntities = new Set(this.currClusteredEntities);
3005
+ return { clusters: mergedClusters, noLongerClustered: noLongerClustered };
3006
+ };
3007
+ /**
3008
+ * Merges clusters that are nearby based on the distanceBetweenClusters value.
3009
+ * @param clusters
3010
+ * @returns
3011
+ */
3012
+ PointClustering.prototype.mergeClusters = function (clusters) {
3013
+ var _a;
3014
+ var mergedClusters = [].concat(clusters);
3015
+ // Keep looping while merges keep happening.
3016
+ var mergeOccurred = true;
3017
+ while (mergeOccurred) {
3018
+ mergeOccurred = false;
3019
+ for (var i = 0; i < mergedClusters.length - 1; i++) {
3020
+ for (var j = i + 1; j < mergedClusters.length; j++) {
3021
+ var cluster1 = mergedClusters[i];
3022
+ var cluster2 = mergedClusters[j];
3023
+ var centerDistance = this.calculateDistance(cluster1.center.lon, cluster1.center.lat, cluster2.center.lon, cluster2.center.lat);
3024
+ var distanceThreshold = this.distanceBetweenClusters;
3025
+ if (centerDistance <= distanceThreshold) {
3026
+ // Merge clusters.
3027
+ (_a = cluster1.points).push.apply(_a, cluster2.points);
3028
+ mergedClusters.splice(j, 1);
3029
+ this.removeClusterEntity(cluster2.center.id);
3030
+ mergeOccurred = true;
3031
+ break;
3032
+ }
3033
+ }
3034
+ if (mergeOccurred) {
3035
+ break;
3036
+ }
3037
+ }
3038
+ }
3039
+ return mergedClusters;
3040
+ };
3041
+ /**
3042
+ * Removes Cesium cluster entity.
3043
+ * @param clusterId
3044
+ */
3045
+ PointClustering.prototype.removeClusterEntity = function (clusterId) {
3046
+ var clusterEntity = this.clusterEntities.get(clusterId);
3047
+ if (clusterEntity) {
3048
+ this.viewer.entities.remove(clusterEntity);
3049
+ this.clusterEntities.delete(clusterId);
3050
+ }
3051
+ };
3052
+ PointClustering.prototype.convertCartesianToCartographic = function (cartesian3) {
3053
+ var carto = Cesium.Cartographic.fromCartesian(cartesian3);
3054
+ var lon = Cesium.Math.toDegrees(carto.longitude);
3055
+ var lat = Cesium.Math.toDegrees(carto.latitude);
3056
+ var id = lon + ',' + lat;
3057
+ return new Point(lon, lat, id);
3058
+ };
3059
+ /**
3060
+ * Adds new entity to the clustering logic.
3061
+ * @param id Bruce entity ID.
3062
+ * @param cartesian3 entity's position.
3063
+ */
3064
+ PointClustering.prototype.addPoint = function (id, cartesian3) {
3065
+ var point = this.convertCartesianToCartographic(cartesian3);
3066
+ point.id = id;
3067
+ this.quadTree.Insert(point);
3068
+ };
3069
+ /**
3070
+ * Calculates rough distance across earth between two points.
3071
+ * @param lon1
3072
+ * @param lat1
3073
+ * @param lon2
3074
+ * @param lat2
3075
+ * @returns
3076
+ */
3077
+ PointClustering.prototype.calculateDistance = function (lon1, lat1, lon2, lat2) {
3078
+ var lonDelta = Math.abs(lon1 - lon2);
3079
+ var latDelta = Math.abs(lat1 - lat2);
3080
+ // Approximate radius of the Earth in kilometers
3081
+ var earthRadius = 6371;
3082
+ var distance = 2 * Math.asin(Math.sqrt(Math.sin(latDelta / 2) * Math.sin(latDelta / 2) +
3083
+ Math.cos(lat1) * Math.cos(lat2) * Math.sin(lonDelta / 2) * Math.sin(lonDelta / 2))) * earthRadius;
3084
+ return distance;
3085
+ };
3086
+ /**
3087
+ * Adds entity to clustering logic.
3088
+ * Will return false if entity could not be clustered and therefor should not be hidden.
3089
+ * @param id
3090
+ * @param entity
3091
+ * @returns
3092
+ */
3093
+ PointClustering.prototype.AddEntity = function (id, entity) {
3094
+ if (this.disposed) {
3095
+ return false;
3096
+ }
3097
+ if (!entity) {
3098
+ return false;
3099
+ }
3100
+ if (entity.polygon || entity.polyline) {
3101
+ return false;
3102
+ }
3103
+ var pos3d = GetValue$1(this.viewer, entity.position);
3104
+ if (!(pos3d === null || pos3d === void 0 ? void 0 : pos3d.x)) {
3105
+ return false;
3106
+ }
3107
+ if (!this.pointColorBg && entity.point) {
3108
+ this.pointColorBg = GetValue$1(this.viewer, entity.point.color);
3109
+ if (this.pointColorBg) {
3110
+ var cColor = null;
3111
+ if (this.pointColorBg instanceof Object) {
3112
+ cColor = new Cesium.Color(this.pointColorBg.red, this.pointColorBg.green, this.pointColorBg.blue, this.pointColorBg.alpha);
3113
+ }
3114
+ else if (typeof this.pointColorBg === "string") {
3115
+ cColor = Cesium.Color.fromCssColorString(this.pointColorBg);
3116
+ }
3117
+ // Determine if text color should instead be black based on background.
3118
+ // cColor contains r,g,b,a values where r,g,b are in the range [0,1].
3119
+ if (cColor) {
3120
+ var brightness = (cColor.red * 299 + cColor.green * 587 + cColor.blue * 114) / 1000;
3121
+ if (brightness > 0.5) {
3122
+ this.pointColorTxt = "black";
3123
+ }
3124
+ else {
3125
+ this.pointColorTxt = "white";
3126
+ }
3127
+ }
3128
+ }
3129
+ }
3130
+ this.registeredEntityIds.add(id);
3131
+ this.addPoint(id, pos3d);
3132
+ this.updateQueue.Call();
3133
+ return true;
3134
+ };
3135
+ /**
3136
+ * Removes entity from clustering logic.
3137
+ * Warning: This will not reveal the entity, suppressShow will remain true.
3138
+ * This is made with the assumption that the entity is being removed from viewer.
3139
+ * @param id
3140
+ * @returns
3141
+ */
3142
+ PointClustering.prototype.RemoveEntity = function (id) {
3143
+ if (this.disposed) {
3144
+ return;
3145
+ }
3146
+ var pointToRemove = this.quadTree.Find(id);
3147
+ if (pointToRemove) {
3148
+ this.quadTree.Remove(pointToRemove);
3149
+ this.updateQueue.Call();
3150
+ }
3151
+ this.registeredEntityIds.delete(id);
3152
+ };
3153
+ return PointClustering;
3154
+ }());
3155
+
2524
3156
  var BATCH_SIZE = 500;
2525
3157
  var CHECK_BATCH_SIZE = 250;
2526
3158
  (function (EntitiesRenderManager) {
@@ -2544,6 +3176,9 @@
2544
3176
  this.apiGetter = apiGetter;
2545
3177
  this.item = item;
2546
3178
  this.visualsManager = visualsManager;
3179
+ if (item.enableClustering) {
3180
+ this.clustering = new PointClustering(this.visualsManager, this.item.id);
3181
+ }
2547
3182
  }
2548
3183
  Object.defineProperty(Manager.prototype, "Disposed", {
2549
3184
  get: function () {
@@ -2554,7 +3189,7 @@
2554
3189
  });
2555
3190
  Manager.prototype.Init = function (params) {
2556
3191
  var _this = this;
2557
- var _a, _b, _c, _d, _e;
3192
+ var _a, _b, _c, _d, _e, _f, _g;
2558
3193
  if (this.disposed) {
2559
3194
  throw (new Error("This item is disposed."));
2560
3195
  }
@@ -2586,17 +3221,22 @@
2586
3221
  menuItemId: this.item.id,
2587
3222
  retainTagIds: tagsToRender
2588
3223
  });
3224
+ (_e = this.clustering) === null || _e === void 0 ? void 0 : _e.Dispose();
2589
3225
  }
2590
3226
  else {
2591
3227
  this.visualsManager.RemoveRegos({
2592
3228
  menuItemId: this.item.id
2593
3229
  });
3230
+ (_f = this.clustering) === null || _f === void 0 ? void 0 : _f.Dispose();
2594
3231
  return;
2595
3232
  }
3233
+ if (this.item.enableClustering) {
3234
+ this.clustering = new PointClustering(this.visualsManager, this.item.id);
3235
+ }
2596
3236
  var api = this.apiGetter.getApi();
2597
3237
  this.getter = this.sharedGetters.GetOrCreateFilterGetter({
2598
3238
  api: api,
2599
- attrFilter: (_e = this.item.BruceEntity.Filter) !== null && _e !== void 0 ? _e : {},
3239
+ attrFilter: (_g = this.item.BruceEntity.Filter) !== null && _g !== void 0 ? _g : {},
2600
3240
  batchSize: BATCH_SIZE,
2601
3241
  typeId: this.item.BruceEntity["EntityType.ID"],
2602
3242
  monitor: this.monitor,
@@ -2628,7 +3268,13 @@
2628
3268
  _this.entityCheckQueue.Call();
2629
3269
  });
2630
3270
  this.entityCheckQueue = new bruceModels.DelayQueue(function () {
2631
- _this.doEntityCheck(Object.keys(_this.renderedEntities));
3271
+ var _a;
3272
+ // Don't bother checking for zoom control changes if we only have 1 item.
3273
+ // We'll let Cesium handle hide/show at max zoom range.
3274
+ var shouldCheck = ((_a = _this.item) === null || _a === void 0 ? void 0 : _a.CameraZoomSettings) && _this.item.CameraZoomSettings.length > 1;
3275
+ if (shouldCheck) {
3276
+ _this.doEntityCheck(Object.keys(_this.renderedEntities));
3277
+ }
2632
3278
  }, 3000);
2633
3279
  };
2634
3280
  Manager.prototype.Dispose = function () {
@@ -2638,7 +3284,7 @@
2638
3284
  this.doDispose();
2639
3285
  };
2640
3286
  Manager.prototype.doDispose = function () {
2641
- var _a, _b, _c;
3287
+ var _a, _b, _c, _d;
2642
3288
  (_a = this.getterSub) === null || _a === void 0 ? void 0 : _a.call(this);
2643
3289
  this.getterSub = null;
2644
3290
  if (this.getter) {
@@ -2652,6 +3298,7 @@
2652
3298
  (_c = this.viewMonitorRemoval) === null || _c === void 0 ? void 0 : _c.call(this);
2653
3299
  clearInterval(this.renderQueueInterval);
2654
3300
  this.renderQueue = [];
3301
+ (_d = this.clustering) === null || _d === void 0 ? void 0 : _d.Dispose();
2655
3302
  };
2656
3303
  Manager.prototype.ReRender = function (params) {
2657
3304
  return __awaiter(this, void 0, void 0, function () {
@@ -2785,14 +3432,14 @@
2785
3432
  }
2786
3433
  };
2787
3434
  Manager.prototype.renderEntities = function (entities, force) {
2788
- var _a, _b, _c;
3435
+ var _a, _b, _c, _d;
2789
3436
  if (force === void 0) { force = false; }
2790
3437
  return __awaiter(this, void 0, void 0, function () {
2791
- var typeId_1, cEntities, i, entity, id, cEntity, visual, tagIds, e_3;
2792
- return __generator(this, function (_d) {
2793
- switch (_d.label) {
3438
+ var typeId_1, cEntities, i, entity, id, cEntity, visual, wasClustered, tagIds, rego, e_3;
3439
+ return __generator(this, function (_e) {
3440
+ switch (_e.label) {
2794
3441
  case 0:
2795
- _d.trys.push([0, 2, , 3]);
3442
+ _e.trys.push([0, 2, , 3]);
2796
3443
  if (this.disposed || this.viewer.isDestroyed()) {
2797
3444
  return [2 /*return*/];
2798
3445
  }
@@ -2810,7 +3457,7 @@
2810
3457
  force: force
2811
3458
  })];
2812
3459
  case 1:
2813
- cEntities = _d.sent();
3460
+ cEntities = _e.sent();
2814
3461
  if (this.disposed) {
2815
3462
  this.doDispose();
2816
3463
  return [2 /*return*/];
@@ -2826,17 +3473,20 @@
2826
3473
  menuItemId: this.item.id
2827
3474
  })) === null || _b === void 0 ? void 0 : _b.visual;
2828
3475
  if (!visual || visual != cEntity) {
3476
+ wasClustered = this.clustering ? this.clustering.AddEntity(id, cEntity) : false;
2829
3477
  tagIds = (_c = entity.Bruce) === null || _c === void 0 ? void 0 : _c["Layer.ID"];
3478
+ rego = {
3479
+ entityId: id,
3480
+ menuItemId: this.item.id,
3481
+ visual: cEntity,
3482
+ priority: 0,
3483
+ entityTypeId: entity.Bruce["EntityType.ID"],
3484
+ accountId: this.apiGetter.accountId,
3485
+ tagIds: tagIds ? [].concat(tagIds) : [],
3486
+ suppressShow: wasClustered
3487
+ };
2830
3488
  this.visualsManager.AddRego({
2831
- rego: {
2832
- entityId: id,
2833
- menuItemId: this.item.id,
2834
- visual: cEntity,
2835
- priority: 0,
2836
- entityTypeId: entity.Bruce["EntityType.ID"],
2837
- accountId: this.apiGetter.accountId,
2838
- tagIds: tagIds ? [].concat(tagIds) : []
2839
- }
3489
+ rego: rego
2840
3490
  });
2841
3491
  }
2842
3492
  }
@@ -2845,12 +3495,13 @@
2845
3495
  entityId: id,
2846
3496
  menuItemId: this.item.id
2847
3497
  });
3498
+ (_d = this.clustering) === null || _d === void 0 ? void 0 : _d.RemoveEntity(id);
2848
3499
  }
2849
3500
  }
2850
3501
  this.viewer.scene.requestRender();
2851
3502
  return [3 /*break*/, 3];
2852
3503
  case 2:
2853
- e_3 = _d.sent();
3504
+ e_3 = _e.sent();
2854
3505
  console.error(e_3);
2855
3506
  return [3 /*break*/, 3];
2856
3507
  case 3: return [2 /*return*/];
@@ -3736,7 +4387,7 @@
3736
4387
  for (var i = 0; i < regos.length; i++) {
3737
4388
  var rego = regos[i];
3738
4389
  rego.best = rego === highestPriority;
3739
- updateEntityShow(viewer, rego.visual, getShowState(rego));
4390
+ updateEntityShow(viewer, rego.visual, rego.suppressShow ? false : getShowState(rego));
3740
4391
  }
3741
4392
  }
3742
4393
  function markEntity(register, rego, visual, ignoreParent) {
@@ -3928,6 +4579,17 @@
3928
4579
  enumerable: false,
3929
4580
  configurable: true
3930
4581
  });
4582
+ Register.prototype.ForceUpdate = function (params) {
4583
+ var entityIds = params.entityIds;
4584
+ for (var i = 0; i < entityIds.length; i++) {
4585
+ var entityId = entityIds[i];
4586
+ updateEntity(this.viewer, entityId, this);
4587
+ this.OnUpdate.Trigger({
4588
+ type: EVisualUpdateType.Update,
4589
+ entityId: entityId
4590
+ });
4591
+ }
4592
+ };
3931
4593
  Register.prototype.SetSelectionColor = function (color) {
3932
4594
  this.selectionColor = color;
3933
4595
  };
@@ -4445,6 +5107,9 @@
4445
5107
  this.monitor = monitor;
4446
5108
  this.item = item;
4447
5109
  this.visualsManager = visualsManager;
5110
+ if (this.item.enableClustering) {
5111
+ this.clustering = new PointClustering(visualsManager, this.item.id);
5112
+ }
4448
5113
  }
4449
5114
  Object.defineProperty(Manager.prototype, "Disposed", {
4450
5115
  get: function () {
@@ -4465,7 +5130,7 @@
4465
5130
  this.getter.Start();
4466
5131
  };
4467
5132
  Manager.prototype.Dispose = function () {
4468
- var _a;
5133
+ var _a, _b;
4469
5134
  if (this.disposed) {
4470
5135
  return;
4471
5136
  }
@@ -4475,6 +5140,7 @@
4475
5140
  this.visualsManager.RemoveRegos({
4476
5141
  menuItemId: this.item.id
4477
5142
  });
5143
+ (_b = this.clustering) === null || _b === void 0 ? void 0 : _b.Dispose();
4478
5144
  };
4479
5145
  Manager.prototype.ReRender = function (params) {
4480
5146
  return __awaiter(this, void 0, void 0, function () {
@@ -4543,19 +5209,19 @@
4543
5209
  });
4544
5210
  };
4545
5211
  Manager.prototype.renderEntities = function (entities, force) {
4546
- var _a;
5212
+ var _a, _b;
4547
5213
  if (force === void 0) { force = false; }
4548
5214
  return __awaiter(this, void 0, void 0, function () {
4549
- var cEntities, i, entity, id, cEntity, visual, e_2;
4550
- return __generator(this, function (_b) {
4551
- switch (_b.label) {
5215
+ var cEntities, i, entity, id, cEntity, visual, clustered, e_2;
5216
+ return __generator(this, function (_c) {
5217
+ switch (_c.label) {
4552
5218
  case 0:
4553
5219
  if (this.disposed || this.viewer.isDestroyed()) {
4554
5220
  return [2 /*return*/];
4555
5221
  }
4556
- _b.label = 1;
5222
+ _c.label = 1;
4557
5223
  case 1:
4558
- _b.trys.push([1, 3, , 4]);
5224
+ _c.trys.push([1, 3, , 4]);
4559
5225
  return [4 /*yield*/, exports.EntityRenderEngine.Render({
4560
5226
  viewer: this.viewer,
4561
5227
  apiGetter: this.apiGetter,
@@ -4566,7 +5232,7 @@
4566
5232
  force: force
4567
5233
  })];
4568
5234
  case 2:
4569
- cEntities = _b.sent();
5235
+ cEntities = _c.sent();
4570
5236
  for (i = 0; i < entities.length; i++) {
4571
5237
  entity = entities[i];
4572
5238
  id = entity.Bruce.ID;
@@ -4578,6 +5244,7 @@
4578
5244
  menuItemId: this.item.id
4579
5245
  })) === null || _a === void 0 ? void 0 : _a.visual;
4580
5246
  if (!visual || visual != cEntity) {
5247
+ clustered = this.clustering ? this.clustering.AddEntity(id, cEntity) : false;
4581
5248
  this.visualsManager.AddRego({
4582
5249
  rego: {
4583
5250
  entityId: id,
@@ -4585,7 +5252,8 @@
4585
5252
  visual: cEntity,
4586
5253
  priority: 0,
4587
5254
  entityTypeId: entity.Bruce["EntityType.ID"],
4588
- accountId: this.apiGetter.accountId
5255
+ accountId: this.apiGetter.accountId,
5256
+ suppressShow: clustered
4589
5257
  }
4590
5258
  });
4591
5259
  }
@@ -4595,12 +5263,13 @@
4595
5263
  entityId: id,
4596
5264
  menuItemId: this.item.id
4597
5265
  });
5266
+ (_b = this.clustering) === null || _b === void 0 ? void 0 : _b.RemoveEntity(id);
4598
5267
  }
4599
5268
  }
4600
5269
  this.viewer.scene.requestRender();
4601
5270
  return [3 /*break*/, 4];
4602
5271
  case 3:
4603
- e_2 = _b.sent();
5272
+ e_2 = _c.sent();
4604
5273
  console.error(e_2);
4605
5274
  return [3 /*break*/, 4];
4606
5275
  case 4: return [2 /*return*/];
@@ -9939,7 +10608,7 @@
9939
10608
  ViewRenderEngine.Render = Render;
9940
10609
  })(exports.ViewRenderEngine || (exports.ViewRenderEngine = {}));
9941
10610
 
9942
- var VERSION = "2.1.3";
10611
+ var VERSION = "2.1.8";
9943
10612
 
9944
10613
  exports.VERSION = VERSION;
9945
10614
  exports.CesiumViewMonitor = CesiumViewMonitor;