bruce-cesium 2.1.8 → 2.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, Carto, Entity as Entity$1, Geometry, Tileset, MathUtils, LRUCache, ZoomControl, Style, EntityTag, Calculator, EntityLod, EntityType, ClientFile, ObjectUtils, DelayQueue, BatchedDataGetter, EntityRelationType, EntityCoords, EntityFilterGetter, EntitySource, MenuItem, EntityRelation, ENVIRONMENT, ProjectView, ProjectViewBookmark, ProjectViewTile, ProjectViewLegacyTile, ProgramKey, Camera } from 'bruce-models';
2
2
  import * as Cesium from 'cesium';
3
- import { Cartesian2, Cartographic, CallbackProperty, Cartesian3, Color, Rectangle, Math as Math$1, Entity, Primitive, Cesium3DTileFeature, SceneMode, HeightReference, DistanceDisplayCondition, NearFarScalar, HorizontalOrigin, VerticalOrigin, ClassificationType, ArcType, PolygonHierarchy, ShadowMode, PolylineGraphics, HeadingPitchRoll, Transforms, ColorBlendMode, Cesium3DTileColorBlendMode, HeadingPitchRange, createOsmBuildings, Cesium3DTileStyle, KmlDataSource, createWorldTerrain, EllipsoidTerrainProvider, CesiumTerrainProvider, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, UrlTemplateImageryProvider, TileMapServiceImageryProvider, IonImageryProvider, CesiumInspector, OrthographicFrustum, JulianDate, Cesium3DTileset, Matrix4, Matrix3, IonResource, EllipsoidGeodesic, sampleTerrainMostDetailed, ColorMaterialProperty, EasingFunction, GeometryInstance, PolygonPipeline } from 'cesium';
3
+ import { Cartesian2, Cartographic, CallbackProperty, Cartesian3, Color, Rectangle, Math as Math$1, HeightReference, DistanceDisplayCondition, NearFarScalar, Entity, HorizontalOrigin, VerticalOrigin, ClassificationType, ArcType, PolygonHierarchy, ShadowMode, PolylineGraphics, HeadingPitchRoll, Transforms, ColorBlendMode, SceneMode, Primitive, Cesium3DTileFeature, Cesium3DTileColorBlendMode, HeadingPitchRange, KmlDataSource, OrthographicFrustum, JulianDate, createOsmBuildings, Cesium3DTileStyle, createWorldTerrain, EllipsoidTerrainProvider, CesiumTerrainProvider, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, UrlTemplateImageryProvider, TileMapServiceImageryProvider, IonImageryProvider, Cesium3DTileset, Matrix4, Matrix3, IonResource, CesiumInspector, ColorMaterialProperty, EasingFunction, GeometryInstance, EllipsoidGeodesic, sampleTerrainMostDetailed, PolygonPipeline } from 'cesium';
4
4
 
5
5
  var TIME_LAG = 300;
6
6
  var POSITION_CHECK_TIMER = 950;
@@ -2711,19 +2711,208 @@ var Quad = /** @class */ (function () {
2711
2711
  this.southeast.Remove(point);
2712
2712
  }
2713
2713
  };
2714
+ Quad.prototype.GetDistanceToQuad = function (pos3d) {
2715
+ var minLat = this.boundary.y - this.boundary.h;
2716
+ var maxLat = this.boundary.y + this.boundary.h;
2717
+ var minLon = this.boundary.x - this.boundary.w;
2718
+ var maxLon = this.boundary.x + this.boundary.w;
2719
+ var points = [
2720
+ // Corners.
2721
+ new Cartesian3(minLon, minLat, 0),
2722
+ new Cartesian3(minLon, maxLat, 0),
2723
+ new Cartesian3(maxLon, minLat, 0),
2724
+ new Cartesian3(maxLon, maxLat, 0),
2725
+ // Center.
2726
+ new Cartesian3(this.boundary.x, this.boundary.y, 0)
2727
+ ];
2728
+ var shortest = Number.MAX_VALUE;
2729
+ for (var _i = 0, points_1 = points; _i < points_1.length; _i++) {
2730
+ var point = points_1[_i];
2731
+ var distance = Cartesian3.distance(pos3d, point);
2732
+ if (distance < shortest) {
2733
+ shortest = distance;
2734
+ }
2735
+ }
2736
+ return shortest;
2737
+ };
2714
2738
  return Quad;
2715
2739
  }());
2740
+ var _clusterImageCache = new LRUCache(500);
2741
+ var _clusterImageLoadedCache = new LRUCache(500);
2742
+ function _loadClusterImage(params) {
2743
+ return __awaiter(this, void 0, void 0, function () {
2744
+ var size, txtColor, bgColor, text, iconUrl, key, cacheData, prom;
2745
+ return __generator(this, function (_a) {
2746
+ switch (_a.label) {
2747
+ case 0:
2748
+ size = params.size, txtColor = params.txtColor, bgColor = params.bgColor, text = params.text, iconUrl = params.iconUrl;
2749
+ key = "".concat(size, "-").concat(txtColor, "-").concat(bgColor, "-").concat(text, "-").concat(iconUrl);
2750
+ cacheData = _clusterImageCache.Get(key);
2751
+ if (!cacheData) return [3 /*break*/, 2];
2752
+ return [4 /*yield*/, cacheData];
2753
+ case 1: return [2 /*return*/, _a.sent()];
2754
+ case 2:
2755
+ prom = new Promise(function (res, rej) {
2756
+ var canvas = document.createElement("canvas");
2757
+ canvas.width = size;
2758
+ canvas.height = size;
2759
+ var ctx = canvas.getContext("2d");
2760
+ var WHITESPACE_PADDING_PERCENT = 0.05;
2761
+ var radius = (size / 2) - (size * WHITESPACE_PADDING_PERCENT);
2762
+ var drawWithoutImage = function (img) {
2763
+ ctx.beginPath();
2764
+ ctx.arc(size / 2, size / 2, radius, 0, 2 * Math.PI, false);
2765
+ var fill = null;
2766
+ var txt = null;
2767
+ if (img) {
2768
+ var brightness = calculateImageBrightness(img);
2769
+ fill = brightness < 128 ? "white" : "#114d78";
2770
+ txt = brightness < 128 ? "black" : "white";
2771
+ }
2772
+ else {
2773
+ fill = bgColor ? bgColor : "#114d78";
2774
+ txt = txtColor ? txtColor : "white";
2775
+ }
2776
+ ctx.fillStyle = fill;
2777
+ ctx.fill();
2778
+ var maxTextWidth = size * 0.7;
2779
+ var maxTextHeight = size * 0.7;
2780
+ var minTextSize = Math.floor(size / 12);
2781
+ var textSize = findOptimalFontSize(text, maxTextWidth, maxTextHeight, minTextSize);
2782
+ ctx.font = "bold ".concat(textSize, "px Arial");
2783
+ ctx.fillStyle = txt;
2784
+ ctx.textAlign = "center";
2785
+ ctx.textBaseline = "middle";
2786
+ ctx.fillText(text, size / 2, size / 2);
2787
+ };
2788
+ var drawWithImage = function (img) {
2789
+ var aspectRatio = img.width / img.height;
2790
+ var imageSize = Math.min(radius, img.width, img.height);
2791
+ if (imageSize / aspectRatio > radius) {
2792
+ imageSize = radius * aspectRatio;
2793
+ }
2794
+ var imageX = (size - imageSize) / 2;
2795
+ var imageY = (size - imageSize) / 2 - imageSize / 3;
2796
+ ctx.beginPath();
2797
+ ctx.arc(size / 2, size / 2, radius, 0, 2 * Math.PI, false);
2798
+ var brightness = calculateImageBrightness(img);
2799
+ var bgColor = brightness < 128 ? "white" : "#114d78";
2800
+ var txtColor = brightness < 128 ? "black" : "white";
2801
+ ctx.fillStyle = bgColor;
2802
+ ctx.fill();
2803
+ ctx.shadowColor = "rgba(0, 0, 0, 0.3)";
2804
+ ctx.shadowOffsetX = 3;
2805
+ ctx.shadowOffsetY = 3;
2806
+ ctx.shadowBlur = 5;
2807
+ ctx.drawImage(img, imageX, imageY, imageSize, imageSize);
2808
+ var padding = imageSize / 7;
2809
+ var maxTextWidth = imageSize;
2810
+ var maxTextHeight = imageSize - padding;
2811
+ var minTextSize = Math.floor(imageSize / 5);
2812
+ var textSize = findOptimalFontSize(text, maxTextWidth, maxTextHeight, minTextSize);
2813
+ ctx.font = "bold ".concat(textSize, "px Arial");
2814
+ ctx.fillStyle = txtColor;
2815
+ ctx.textAlign = "center";
2816
+ ctx.textBaseline = "top";
2817
+ ctx.shadowColor = "rgba(0, 0, 0, 0)";
2818
+ ctx.shadowOffsetX = 0;
2819
+ ctx.shadowOffsetY = 0;
2820
+ ctx.shadowBlur = 0;
2821
+ ctx.fillText(text, size / 2, imageY + imageSize + padding);
2822
+ };
2823
+ var findOptimalFontSize = function (text, maxWidth, maxHeight, minSize) {
2824
+ var fontSize = maxHeight;
2825
+ var tempCanvas = document.createElement("canvas");
2826
+ var tempCtx = tempCanvas.getContext("2d");
2827
+ while (fontSize > minSize) {
2828
+ tempCtx.font = "bold ".concat(fontSize, "px Arial");
2829
+ var measuredWidth = tempCtx.measureText(text).width;
2830
+ var measuredHeight = fontSize * 1.2;
2831
+ if (measuredWidth <= maxWidth && measuredHeight <= maxHeight) {
2832
+ break;
2833
+ }
2834
+ fontSize--;
2835
+ }
2836
+ return fontSize;
2837
+ };
2838
+ var calculateImageBrightness = function (img) {
2839
+ var brightness = 0;
2840
+ var tempCanvas = document.createElement("canvas");
2841
+ tempCanvas.width = img.width;
2842
+ tempCanvas.height = img.height;
2843
+ var tempCtx = tempCanvas.getContext("2d");
2844
+ tempCtx.drawImage(img, 0, 0);
2845
+ var imageData = tempCtx.getImageData(0, 0, img.width, img.height).data;
2846
+ for (var i = 0; i < imageData.length; i += 4) {
2847
+ var r = imageData[i];
2848
+ var g = imageData[i + 1];
2849
+ var b = imageData[i + 2];
2850
+ brightness += (r + g + b) / 3;
2851
+ }
2852
+ return Math.round(brightness / (img.width * img.height));
2853
+ };
2854
+ if (iconUrl) {
2855
+ var img_1 = new Image();
2856
+ img_1.crossOrigin = "anonymous";
2857
+ img_1.src = iconUrl;
2858
+ img_1.onload = function () {
2859
+ if (size > 50) {
2860
+ drawWithImage(img_1);
2861
+ }
2862
+ else {
2863
+ drawWithoutImage(img_1);
2864
+ }
2865
+ res(canvas);
2866
+ };
2867
+ img_1.onerror = function () {
2868
+ drawWithoutImage();
2869
+ res(canvas);
2870
+ };
2871
+ }
2872
+ else {
2873
+ drawWithoutImage();
2874
+ res(canvas);
2875
+ }
2876
+ });
2877
+ _clusterImageCache.Set(key, prom);
2878
+ prom.then(function (canvas) {
2879
+ _clusterImageLoadedCache.Set(key, canvas);
2880
+ _clusterImageCache.Set(key, null);
2881
+ });
2882
+ return [4 /*yield*/, prom];
2883
+ case 3: return [2 /*return*/, _a.sent()];
2884
+ }
2885
+ });
2886
+ });
2887
+ }
2888
+ function getClusterImage(params) {
2889
+ var size = params.size, txtColor = params.txtColor, bgColor = params.bgColor, text = params.text, iconUrl = params.iconUrl;
2890
+ var key = "".concat(size, "-").concat(txtColor, "-").concat(bgColor, "-").concat(text, "-").concat(iconUrl);
2891
+ var cacheData = _clusterImageLoadedCache.Get(key);
2892
+ // If available then return.
2893
+ if (cacheData) {
2894
+ return cacheData;
2895
+ }
2896
+ // If not available then queue for it to cook.
2897
+ else {
2898
+ _loadClusterImage(params);
2899
+ }
2900
+ return null;
2901
+ }
2902
+ var FORCE_UPDATE_BATCH_SIZE = 1000;
2903
+ var FORCE_UPDATE_BATCH_DELAY = 100;
2716
2904
  var PointClustering = /** @class */ (function () {
2717
2905
  function PointClustering(register, menuItemId) {
2718
2906
  var _this = this;
2719
2907
  this.disposed = false;
2720
2908
  this.registeredEntityIds = new Set();
2909
+ // Queue to force update entities.
2910
+ this.updateEntityQueue = [];
2721
2911
  this.register = register;
2722
2912
  this.viewer = register.Viewer;
2723
2913
  this.menuItemId = menuItemId;
2724
- this.updateClusterSpacing(0);
2725
2914
  var boundary = new Rectangle$1(0, 0, 360, 180);
2726
- this.quadTree = new Quad(boundary, 4);
2915
+ this.quadTree = new Quad(boundary, 30);
2727
2916
  this.prevClusteredEntities = new Set();
2728
2917
  this.currClusteredEntities = new Set();
2729
2918
  this.clusterEntities = new Map();
@@ -2732,6 +2921,39 @@ var PointClustering = /** @class */ (function () {
2732
2921
  }, 1000);
2733
2922
  this.listenCamera();
2734
2923
  }
2924
+ PointClustering.prototype.queueForceUpdate = function (entityIds) {
2925
+ for (var i = 0; i < entityIds.length; i++) {
2926
+ if (this.updateEntityQueue.includes(entityIds[i])) {
2927
+ continue;
2928
+ }
2929
+ this.updateEntityQueue.push(entityIds[i]);
2930
+ }
2931
+ this.runForceUpdateQueue();
2932
+ };
2933
+ PointClustering.prototype.runForceUpdateQueue = function () {
2934
+ var _this = this;
2935
+ if (!this.updateEntityQueue.length) {
2936
+ return;
2937
+ }
2938
+ if (this.queueInterval) {
2939
+ return;
2940
+ }
2941
+ this.queueInterval = setInterval(function () {
2942
+ if (_this.disposed) {
2943
+ clearInterval(_this.queueInterval);
2944
+ _this.queueInterval = null;
2945
+ return;
2946
+ }
2947
+ var ids = _this.updateEntityQueue.splice(0, FORCE_UPDATE_BATCH_SIZE);
2948
+ _this.register.ForceUpdate({
2949
+ entityIds: ids
2950
+ });
2951
+ if (!_this.updateEntityQueue.length) {
2952
+ clearInterval(_this.queueInterval);
2953
+ _this.queueInterval = null;
2954
+ }
2955
+ }, FORCE_UPDATE_BATCH_DELAY);
2956
+ };
2735
2957
  /**
2736
2958
  * Starts listening to camera changes.
2737
2959
  * This will trigger the clustering update whenever camera is moved.
@@ -2786,6 +3008,7 @@ var PointClustering = /** @class */ (function () {
2786
3008
  this.clusterEntities.clear();
2787
3009
  this.unlistenCamera();
2788
3010
  this.disposed = true;
3011
+ this.updateEntityQueue = [];
2789
3012
  // Restore entities.
2790
3013
  var toUpdateIds = [];
2791
3014
  for (var _c = 0, _d = Array.from(this.registeredEntityIds); _c < _d.length; _c++) {
@@ -2813,8 +3036,8 @@ var PointClustering = /** @class */ (function () {
2813
3036
  PointClustering.prototype.calculateCentroid = function (points) {
2814
3037
  var lonSum = 0;
2815
3038
  var latSum = 0;
2816
- for (var _i = 0, points_1 = points; _i < points_1.length; _i++) {
2817
- var point = points_1[_i];
3039
+ for (var _i = 0, points_2 = points; _i < points_2.length; _i++) {
3040
+ var point = points_2[_i];
2818
3041
  lonSum += point.lon;
2819
3042
  latSum += point.lat;
2820
3043
  }
@@ -2834,9 +3057,10 @@ var PointClustering = /** @class */ (function () {
2834
3057
  // 1: Update precision.
2835
3058
  // This defines how far apart these clusters can be.
2836
3059
  var cameraHeight = this.viewer.camera.positionCartographic.height;
2837
- this.updateClusterSpacing(cameraHeight);
3060
+ this.getClusterSpacing(cameraHeight);
2838
3061
  // 2: Get clusters.
2839
3062
  var _a = this.getClusters(), clusters = _a.clusters, noLongerClustered = _a.noLongerClustered;
3063
+ var entitiesToUpdate = [];
2840
3064
  // 3: Remove all cesium cluster entities that are no longer clustered.
2841
3065
  for (var _i = 0, _b = Array.from(noLongerClustered); _i < _b.length; _i++) {
2842
3066
  var id = _b[_i];
@@ -2853,193 +3077,180 @@ var PointClustering = /** @class */ (function () {
2853
3077
  });
2854
3078
  if (rego && rego.suppressShow) {
2855
3079
  rego.suppressShow = false;
2856
- this.register.ForceUpdate({
2857
- entityIds: [id],
2858
- });
3080
+ entitiesToUpdate.push(id);
2859
3081
  }
2860
3082
  }
2861
3083
  }
2862
3084
  var getScale = function (count) {
2863
- var baseSize = 20;
2864
- var scalingFactor = 2;
2865
- var scale = baseSize + scalingFactor * (count - 1);
2866
- scale = Math.min(scale, 120);
2867
- return scale;
2868
- };
2869
- var getLabel = function (count) {
2870
- var color = _this.pointColorTxt ? _this.pointColorTxt : "white";
2871
- var canvas = document.createElement("canvas");
2872
- var ctx = canvas.getContext("2d");
2873
- var size = getScale(count);
2874
- canvas.width = size;
2875
- canvas.height = size;
2876
- ctx.font = "bold 20px Arial";
2877
- ctx.fillStyle = color;
2878
- ctx.textAlign = "center";
2879
- ctx.textBaseline = "middle";
2880
- ctx.fillText(String(count), size / 2, size / 2);
2881
- return canvas;
3085
+ // const MIN_SCALE = 15;
3086
+ // const MAX_SCALE = 80;
3087
+ // const SCALE_PER_POINT = 1.01;
3088
+ // let scale = MIN_SCALE + (count * SCALE_PER_POINT);
3089
+ // scale = Math.min(scale, MAX_SCALE);
3090
+ // return scale;
3091
+ if (count >= 10000) {
3092
+ return 140;
3093
+ }
3094
+ if (count >= 1000) {
3095
+ return 120;
3096
+ }
3097
+ if (count >= 100) {
3098
+ return 90;
3099
+ }
3100
+ if (count >= 10) {
3101
+ return 80;
3102
+ }
3103
+ return 70;
2882
3104
  };
2883
- // 5: iterate over clusters and add/update them as Cesium entities.
2884
- for (var _e = 0, clusters_1 = clusters; _e < clusters_1.length; _e++) {
2885
- var cluster = clusters_1[_e];
3105
+ /**
3106
+ * Generate circle with label in center.
3107
+ * @param count
3108
+ * @returns
3109
+ */
3110
+ var getCanvas = function (count) {
3111
+ var params = {
3112
+ size: getScale(count),
3113
+ txtColor: _this.pointColorTxt,
3114
+ bgColor: _this.pointColorBg,
3115
+ text: String(count),
3116
+ iconUrl: _this.iconUrl
3117
+ };
3118
+ return getClusterImage(params);
3119
+ };
3120
+ var _loop_1 = function (cluster) {
2886
3121
  var clusterId = cluster.center.id;
2887
- var clusterEntity = this.clusterEntities.get(clusterId);
2888
- var centroid = this.calculateCentroid(cluster.points);
3122
+ var clusterEntity = this_1.clusterEntities.get(clusterId);
3123
+ var centroid = this_1.calculateCentroid(cluster.points);
2889
3124
  var count = cluster.points.length;
2890
3125
  if (clusterEntity) {
2891
3126
  clusterEntity.position = Cartesian3.fromDegrees(centroid.lon, centroid.lat, 150);
2892
- clusterEntity.point.pixelSize = getScale(cluster.points.length);
2893
- clusterEntity.billboard.image = getLabel(cluster.points.length);
2894
- clusterEntity.billboard.width = getScale(cluster.points.length);
3127
+ clusterEntity.billboard.image = new CallbackProperty(function () {
3128
+ return getCanvas(count);
3129
+ }, false),
3130
+ clusterEntity.billboard.width = getScale(cluster.points.length);
2895
3131
  clusterEntity.billboard.height = getScale(cluster.points.length);
2896
3132
  }
2897
3133
  else {
2898
- clusterEntity = this.viewer.entities.add({
3134
+ clusterEntity = this_1.viewer.entities.add({
2899
3135
  position: Cartesian3.fromDegrees(centroid.lon, centroid.lat, 150),
2900
- point: {
2901
- heightReference: HeightReference.NONE,
2902
- pixelSize: getScale(count),
2903
- color: this.pointColorBg ? this.pointColorBg : Color.fromCssColorString("#4287f5")
2904
- },
2905
3136
  billboard: {
2906
3137
  heightReference: HeightReference.NONE,
2907
- image: getLabel(count),
3138
+ image: new CallbackProperty(function () {
3139
+ return getCanvas(count);
3140
+ }, false),
2908
3141
  width: getScale(count),
2909
3142
  height: getScale(count),
2910
3143
  verticalOrigin: VerticalOrigin.CENTER,
2911
3144
  horizontalOrigin: HorizontalOrigin.CENTER,
2912
- disableDepthTestDistance: Number.POSITIVE_INFINITY
3145
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
3146
+ scaleByDistance: new NearFarScalar(1.5e2, 0.5, 3.0e7, 0.1),
2913
3147
  }
2914
3148
  });
2915
- this.clusterEntities.set(clusterId, clusterEntity);
3149
+ this_1.clusterEntities.set(clusterId, clusterEntity);
2916
3150
  }
2917
3151
  for (var i = 0; i < cluster.points.length; i++) {
2918
3152
  var entityId = cluster.points[i].id;
2919
- var rego = this.register.GetRego({
3153
+ var rego = this_1.register.GetRego({
2920
3154
  entityId: entityId,
2921
- menuItemId: this.menuItemId
3155
+ menuItemId: this_1.menuItemId
2922
3156
  });
2923
3157
  if (rego && !rego.suppressShow) {
2924
3158
  rego.suppressShow = true;
2925
- this.register.ForceUpdate({
2926
- entityIds: [entityId],
2927
- });
3159
+ entitiesToUpdate.push(entityId);
2928
3160
  }
2929
3161
  }
3162
+ };
3163
+ var this_1 = this;
3164
+ // 5: iterate over clusters and add/update them as Cesium entities.
3165
+ for (var _e = 0, clusters_1 = clusters; _e < clusters_1.length; _e++) {
3166
+ var cluster = clusters_1[_e];
3167
+ _loop_1(cluster);
2930
3168
  }
2931
- var _loop_1 = function (clusterId, clusterEntity) {
3169
+ this.queueForceUpdate(entitiesToUpdate);
3170
+ var _loop_2 = function (clusterId, clusterEntity) {
2932
3171
  if (!clusters.find(function (x) { return x.center.id == clusterId; })) {
2933
- this_1.viewer.entities.remove(clusterEntity);
2934
- this_1.clusterEntities.delete(clusterId);
3172
+ this_2.viewer.entities.remove(clusterEntity);
3173
+ this_2.clusterEntities.delete(clusterId);
2935
3174
  }
2936
3175
  };
2937
- var this_1 = this;
3176
+ var this_2 = this;
2938
3177
  // 6: Iterate over existing cluster entities and remove those that are no longer clustered.
2939
3178
  for (var _f = 0, _g = Array.from(this.clusterEntities); _f < _g.length; _f++) {
2940
3179
  var _h = _g[_f], clusterId = _h[0], clusterEntity = _h[1];
2941
- _loop_1(clusterId, clusterEntity);
3180
+ _loop_2(clusterId, clusterEntity);
2942
3181
  }
2943
3182
  };
2944
- /**
2945
- * Updates how apart clusters can be based on camera distance.
2946
- * @param cameraHeight
2947
- */
2948
- PointClustering.prototype.updateClusterSpacing = function (cameraHeight) {
2949
- // Camera height thresholds in meters.
2950
- var cameraHeightThresholds = [2000, 5000, 8000, 13000, 25000, 40000];
3183
+ PointClustering.prototype.getClusterSpacing = function (distanceFromCluster) {
3184
+ // Distance thresholds in meters from the cluster.
3185
+ var distanceThresholds = [3000, 4000, 5300, 6000, 7000];
2951
3186
  // Distance increments in degrees.
2952
- var distanceIncrements = [0.005, 0.01, 0.02, 0.04, 0.1, 0.5];
2953
- // Find the appropriate spacing based on the camera height.
2954
- var spacing = 0;
2955
- for (var i = 0; i < cameraHeightThresholds.length; i++) {
2956
- if (cameraHeight && cameraHeight < cameraHeightThresholds[i]) {
2957
- spacing += distanceIncrements[i];
2958
- break;
3187
+ var distanceIncrements = [0.001, 0.002, 0.01, 0.03, 0.09];
3188
+ var index = 0;
3189
+ if (distanceFromCluster) {
3190
+ for (var i = 0; i < distanceThresholds.length; i++) {
3191
+ if (distanceFromCluster > distanceThresholds[i]) {
3192
+ index = i;
3193
+ }
2959
3194
  }
2960
- spacing += distanceIncrements[i];
2961
3195
  }
2962
- this.distanceBetweenClusters = spacing;
3196
+ return distanceIncrements[index];
2963
3197
  };
2964
- /**
2965
- * Gathers clusters.
2966
- * @returns
2967
- */
2968
3198
  PointClustering.prototype.getClusters = function () {
2969
3199
  var _this = this;
2970
3200
  this.currClusteredEntities.clear();
2971
3201
  var clusters = [];
2972
3202
  var processedPoints = new Set();
2973
3203
  var cameraPosition = this.viewer.camera.position;
2974
- for (var _i = 0, _a = this.quadTree.points; _i < _a.length; _i++) {
2975
- var point = _a[_i];
2976
- // Skip points already processed in previous clusters.
2977
- if (processedPoints.has(point.id)) {
2978
- continue;
2979
- }
2980
- // Skip points closer than 2000 meters to the camera.
2981
- var cartesian3 = Cartesian3.fromDegrees(point.lon, point.lat);
2982
- if (Cartesian3.distance(cartesian3, cameraPosition) <= 2000) {
2983
- continue;
2984
- }
2985
- var found = [];
2986
- var nearbyPoints = this.quadTree.Query(new Circle(point.lon, point.lat, this.distanceBetweenClusters), found);
2987
- if (nearbyPoints.length > 1) {
2988
- var cluster = { center: point, points: [] };
2989
- for (var _b = 0, nearbyPoints_1 = nearbyPoints; _b < nearbyPoints_1.length; _b++) {
2990
- var nearby = nearbyPoints_1[_b];
2991
- if (!cluster.points.includes(nearby)) {
2992
- cluster.points.push(nearby);
2993
- processedPoints.add(nearby.id);
2994
- this.currClusteredEntities.add(nearby.id);
3204
+ var MIN_CAMERA_DISTANCE = 5000;
3205
+ var processQuad = function (quad) {
3206
+ // Distance to quad.
3207
+ // TODO: Needs improvement.
3208
+ var distanceToQuad = quad.GetDistanceToQuad(cameraPosition);
3209
+ // Skip quads that are too close.
3210
+ if (distanceToQuad >= MIN_CAMERA_DISTANCE) {
3211
+ for (var _i = 0, _a = quad.points; _i < _a.length; _i++) {
3212
+ var point = _a[_i];
3213
+ // Skip points already processed in previous clusters.
3214
+ if (processedPoints.has(point.id)) {
3215
+ continue;
3216
+ }
3217
+ // Skip points closer than MIN_CAMERA_DISTANCE meters to the camera.
3218
+ var cartesian3 = Cartesian3.fromDegrees(point.lon, point.lat);
3219
+ var distanceFromCluster = Cartesian3.distance(cartesian3, cameraPosition);
3220
+ if (distanceFromCluster <= MIN_CAMERA_DISTANCE) {
3221
+ continue;
3222
+ }
3223
+ var found = [];
3224
+ var nearbyPoints = quad.Query(new Circle(point.lon, point.lat, _this.getClusterSpacing(distanceFromCluster)), found);
3225
+ if (nearbyPoints.length > 1) {
3226
+ var cluster = { center: point, points: [] };
3227
+ for (var _b = 0, nearbyPoints_1 = nearbyPoints; _b < nearbyPoints_1.length; _b++) {
3228
+ var nearby = nearbyPoints_1[_b];
3229
+ if (!cluster.points.includes(nearby)) {
3230
+ cluster.points.push(nearby);
3231
+ processedPoints.add(nearby.id);
3232
+ _this.currClusteredEntities.add(nearby.id);
3233
+ }
3234
+ }
3235
+ clusters.push(cluster);
2995
3236
  }
2996
3237
  }
2997
- clusters.push(cluster);
2998
3238
  }
2999
- }
3239
+ if (quad.divided) {
3240
+ processQuad(quad.northwest);
3241
+ processQuad(quad.northeast);
3242
+ processQuad(quad.southwest);
3243
+ processQuad(quad.southeast);
3244
+ }
3245
+ };
3246
+ processQuad(this.quadTree);
3000
3247
  // Filter out clusters with only one point.
3001
3248
  var validClusters = clusters.filter(function (cluster) { return cluster.points.length > 1; });
3002
- // Merge adjacent clusters.
3003
- var mergedClusters = this.mergeClusters(validClusters);
3004
3249
  // Get the entity IDs that are no longer clustered
3005
3250
  var noLongerClustered = new Set(Array.from(this.prevClusteredEntities).filter(function (id) { return !_this.currClusteredEntities.has(id); }));
3006
3251
  // Update the previous clustered entities ref.
3007
3252
  this.prevClusteredEntities = new Set(this.currClusteredEntities);
3008
- return { clusters: mergedClusters, noLongerClustered: noLongerClustered };
3009
- };
3010
- /**
3011
- * Merges clusters that are nearby based on the distanceBetweenClusters value.
3012
- * @param clusters
3013
- * @returns
3014
- */
3015
- PointClustering.prototype.mergeClusters = function (clusters) {
3016
- var _a;
3017
- var mergedClusters = [].concat(clusters);
3018
- // Keep looping while merges keep happening.
3019
- var mergeOccurred = true;
3020
- while (mergeOccurred) {
3021
- mergeOccurred = false;
3022
- for (var i = 0; i < mergedClusters.length - 1; i++) {
3023
- for (var j = i + 1; j < mergedClusters.length; j++) {
3024
- var cluster1 = mergedClusters[i];
3025
- var cluster2 = mergedClusters[j];
3026
- var centerDistance = this.calculateDistance(cluster1.center.lon, cluster1.center.lat, cluster2.center.lon, cluster2.center.lat);
3027
- var distanceThreshold = this.distanceBetweenClusters;
3028
- if (centerDistance <= distanceThreshold) {
3029
- // Merge clusters.
3030
- (_a = cluster1.points).push.apply(_a, cluster2.points);
3031
- mergedClusters.splice(j, 1);
3032
- this.removeClusterEntity(cluster2.center.id);
3033
- mergeOccurred = true;
3034
- break;
3035
- }
3036
- }
3037
- if (mergeOccurred) {
3038
- break;
3039
- }
3040
- }
3041
- }
3042
- return mergedClusters;
3253
+ return { clusters: validClusters, noLongerClustered: noLongerClustered };
3043
3254
  };
3044
3255
  /**
3045
3256
  * Removes Cesium cluster entity.
@@ -3067,24 +3278,7 @@ var PointClustering = /** @class */ (function () {
3067
3278
  PointClustering.prototype.addPoint = function (id, cartesian3) {
3068
3279
  var point = this.convertCartesianToCartographic(cartesian3);
3069
3280
  point.id = id;
3070
- this.quadTree.Insert(point);
3071
- };
3072
- /**
3073
- * Calculates rough distance across earth between two points.
3074
- * @param lon1
3075
- * @param lat1
3076
- * @param lon2
3077
- * @param lat2
3078
- * @returns
3079
- */
3080
- PointClustering.prototype.calculateDistance = function (lon1, lat1, lon2, lat2) {
3081
- var lonDelta = Math.abs(lon1 - lon2);
3082
- var latDelta = Math.abs(lat1 - lat2);
3083
- // Approximate radius of the Earth in kilometers
3084
- var earthRadius = 6371;
3085
- var distance = 2 * Math.asin(Math.sqrt(Math.sin(latDelta / 2) * Math.sin(latDelta / 2) +
3086
- Math.cos(lat1) * Math.cos(lat2) * Math.sin(lonDelta / 2) * Math.sin(lonDelta / 2))) * earthRadius;
3087
- return distance;
3281
+ return this.quadTree.Insert(point);
3088
3282
  };
3089
3283
  /**
3090
3284
  * Adds entity to clustering logic.
@@ -3108,14 +3302,14 @@ var PointClustering = /** @class */ (function () {
3108
3302
  return false;
3109
3303
  }
3110
3304
  if (!this.pointColorBg && entity.point) {
3111
- this.pointColorBg = GetValue$1(this.viewer, entity.point.color);
3112
- if (this.pointColorBg) {
3305
+ var pointColorBg = GetValue$1(this.viewer, entity.point.color);
3306
+ if (pointColorBg) {
3113
3307
  var cColor = null;
3114
- if (this.pointColorBg instanceof Object) {
3115
- cColor = new Color(this.pointColorBg.red, this.pointColorBg.green, this.pointColorBg.blue, this.pointColorBg.alpha);
3308
+ if (pointColorBg instanceof Object) {
3309
+ cColor = new Color(pointColorBg.red, pointColorBg.green, pointColorBg.blue, pointColorBg.alpha);
3116
3310
  }
3117
- else if (typeof this.pointColorBg === "string") {
3118
- cColor = Color.fromCssColorString(this.pointColorBg);
3311
+ else if (typeof pointColorBg === "string") {
3312
+ cColor = Color.fromCssColorString(pointColorBg);
3119
3313
  }
3120
3314
  // Determine if text color should instead be black based on background.
3121
3315
  // cColor contains r,g,b,a values where r,g,b are in the range [0,1].
@@ -3127,11 +3321,21 @@ var PointClustering = /** @class */ (function () {
3127
3321
  else {
3128
3322
  this.pointColorTxt = "white";
3129
3323
  }
3324
+ this.pointColorBg = cColor.toCssColorString();
3130
3325
  }
3131
3326
  }
3132
3327
  }
3328
+ if (!this.iconUrl && entity.billboard) {
3329
+ var iconUrl = GetValue$1(this.viewer, entity.billboard.image);
3330
+ if (typeof iconUrl == "string") {
3331
+ this.iconUrl = iconUrl;
3332
+ }
3333
+ }
3334
+ var added = this.addPoint(id, pos3d);
3335
+ if (!added) {
3336
+ return false;
3337
+ }
3133
3338
  this.registeredEntityIds.add(id);
3134
- this.addPoint(id, pos3d);
3135
3339
  this.updateQueue.Call();
3136
3340
  return true;
3137
3341
  };
@@ -10662,7 +10866,7 @@ var ViewRenderEngine;
10662
10866
  ViewRenderEngine.Render = Render;
10663
10867
  })(ViewRenderEngine || (ViewRenderEngine = {}));
10664
10868
 
10665
- var VERSION = "2.1.8";
10869
+ var VERSION = "2.2.0";
10666
10870
 
10667
10871
  export { VERSION, CesiumViewMonitor, ViewerUtils, MenuItemManager, EntityRenderEngine, MenuItemCreator, VisualsRegister, RenderManager, EntitiesIdsRenderManager, EntitiesLoadedRenderManager, EntitiesRenderManager, EntityRenderManager, TilesetCadRenderManager, TilesetArbRenderManager, TilesetEntitiesRenderManager, TilesetOsmRenderManager, TilesetPointcloudRenderManager, TilesetGooglePhotosRenderManager, DataSourceStaticKmlManager, RelationsRenderManager, SharedGetters, CesiumParabola, ViewRenderEngine, TileRenderEngine, TilesetRenderEngine, CESIUM_INSPECTOR_KEY, ViewUtils, DrawingUtils, MeasureUtils, EntityUtils };
10668
10872
  //# sourceMappingURL=bruce-cesium.es5.js.map