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.
@@ -2708,19 +2708,208 @@
2708
2708
  this.southeast.Remove(point);
2709
2709
  }
2710
2710
  };
2711
+ Quad.prototype.GetDistanceToQuad = function (pos3d) {
2712
+ var minLat = this.boundary.y - this.boundary.h;
2713
+ var maxLat = this.boundary.y + this.boundary.h;
2714
+ var minLon = this.boundary.x - this.boundary.w;
2715
+ var maxLon = this.boundary.x + this.boundary.w;
2716
+ var points = [
2717
+ // Corners.
2718
+ new Cesium.Cartesian3(minLon, minLat, 0),
2719
+ new Cesium.Cartesian3(minLon, maxLat, 0),
2720
+ new Cesium.Cartesian3(maxLon, minLat, 0),
2721
+ new Cesium.Cartesian3(maxLon, maxLat, 0),
2722
+ // Center.
2723
+ new Cesium.Cartesian3(this.boundary.x, this.boundary.y, 0)
2724
+ ];
2725
+ var shortest = Number.MAX_VALUE;
2726
+ for (var _i = 0, points_1 = points; _i < points_1.length; _i++) {
2727
+ var point = points_1[_i];
2728
+ var distance = Cesium.Cartesian3.distance(pos3d, point);
2729
+ if (distance < shortest) {
2730
+ shortest = distance;
2731
+ }
2732
+ }
2733
+ return shortest;
2734
+ };
2711
2735
  return Quad;
2712
2736
  }());
2737
+ var _clusterImageCache = new bruceModels.LRUCache(500);
2738
+ var _clusterImageLoadedCache = new bruceModels.LRUCache(500);
2739
+ function _loadClusterImage(params) {
2740
+ return __awaiter(this, void 0, void 0, function () {
2741
+ var size, txtColor, bgColor, text, iconUrl, key, cacheData, prom;
2742
+ return __generator(this, function (_a) {
2743
+ switch (_a.label) {
2744
+ case 0:
2745
+ size = params.size, txtColor = params.txtColor, bgColor = params.bgColor, text = params.text, iconUrl = params.iconUrl;
2746
+ key = "".concat(size, "-").concat(txtColor, "-").concat(bgColor, "-").concat(text, "-").concat(iconUrl);
2747
+ cacheData = _clusterImageCache.Get(key);
2748
+ if (!cacheData) return [3 /*break*/, 2];
2749
+ return [4 /*yield*/, cacheData];
2750
+ case 1: return [2 /*return*/, _a.sent()];
2751
+ case 2:
2752
+ prom = new Promise(function (res, rej) {
2753
+ var canvas = document.createElement("canvas");
2754
+ canvas.width = size;
2755
+ canvas.height = size;
2756
+ var ctx = canvas.getContext("2d");
2757
+ var WHITESPACE_PADDING_PERCENT = 0.05;
2758
+ var radius = (size / 2) - (size * WHITESPACE_PADDING_PERCENT);
2759
+ var drawWithoutImage = function (img) {
2760
+ ctx.beginPath();
2761
+ ctx.arc(size / 2, size / 2, radius, 0, 2 * Math.PI, false);
2762
+ var fill = null;
2763
+ var txt = null;
2764
+ if (img) {
2765
+ var brightness = calculateImageBrightness(img);
2766
+ fill = brightness < 128 ? "white" : "#114d78";
2767
+ txt = brightness < 128 ? "black" : "white";
2768
+ }
2769
+ else {
2770
+ fill = bgColor ? bgColor : "#114d78";
2771
+ txt = txtColor ? txtColor : "white";
2772
+ }
2773
+ ctx.fillStyle = fill;
2774
+ ctx.fill();
2775
+ var maxTextWidth = size * 0.7;
2776
+ var maxTextHeight = size * 0.7;
2777
+ var minTextSize = Math.floor(size / 12);
2778
+ var textSize = findOptimalFontSize(text, maxTextWidth, maxTextHeight, minTextSize);
2779
+ ctx.font = "bold ".concat(textSize, "px Arial");
2780
+ ctx.fillStyle = txt;
2781
+ ctx.textAlign = "center";
2782
+ ctx.textBaseline = "middle";
2783
+ ctx.fillText(text, size / 2, size / 2);
2784
+ };
2785
+ var drawWithImage = function (img) {
2786
+ var aspectRatio = img.width / img.height;
2787
+ var imageSize = Math.min(radius, img.width, img.height);
2788
+ if (imageSize / aspectRatio > radius) {
2789
+ imageSize = radius * aspectRatio;
2790
+ }
2791
+ var imageX = (size - imageSize) / 2;
2792
+ var imageY = (size - imageSize) / 2 - imageSize / 3;
2793
+ ctx.beginPath();
2794
+ ctx.arc(size / 2, size / 2, radius, 0, 2 * Math.PI, false);
2795
+ var brightness = calculateImageBrightness(img);
2796
+ var bgColor = brightness < 128 ? "white" : "#114d78";
2797
+ var txtColor = brightness < 128 ? "black" : "white";
2798
+ ctx.fillStyle = bgColor;
2799
+ ctx.fill();
2800
+ ctx.shadowColor = "rgba(0, 0, 0, 0.3)";
2801
+ ctx.shadowOffsetX = 3;
2802
+ ctx.shadowOffsetY = 3;
2803
+ ctx.shadowBlur = 5;
2804
+ ctx.drawImage(img, imageX, imageY, imageSize, imageSize);
2805
+ var padding = imageSize / 7;
2806
+ var maxTextWidth = imageSize;
2807
+ var maxTextHeight = imageSize - padding;
2808
+ var minTextSize = Math.floor(imageSize / 5);
2809
+ var textSize = findOptimalFontSize(text, maxTextWidth, maxTextHeight, minTextSize);
2810
+ ctx.font = "bold ".concat(textSize, "px Arial");
2811
+ ctx.fillStyle = txtColor;
2812
+ ctx.textAlign = "center";
2813
+ ctx.textBaseline = "top";
2814
+ ctx.shadowColor = "rgba(0, 0, 0, 0)";
2815
+ ctx.shadowOffsetX = 0;
2816
+ ctx.shadowOffsetY = 0;
2817
+ ctx.shadowBlur = 0;
2818
+ ctx.fillText(text, size / 2, imageY + imageSize + padding);
2819
+ };
2820
+ var findOptimalFontSize = function (text, maxWidth, maxHeight, minSize) {
2821
+ var fontSize = maxHeight;
2822
+ var tempCanvas = document.createElement("canvas");
2823
+ var tempCtx = tempCanvas.getContext("2d");
2824
+ while (fontSize > minSize) {
2825
+ tempCtx.font = "bold ".concat(fontSize, "px Arial");
2826
+ var measuredWidth = tempCtx.measureText(text).width;
2827
+ var measuredHeight = fontSize * 1.2;
2828
+ if (measuredWidth <= maxWidth && measuredHeight <= maxHeight) {
2829
+ break;
2830
+ }
2831
+ fontSize--;
2832
+ }
2833
+ return fontSize;
2834
+ };
2835
+ var calculateImageBrightness = function (img) {
2836
+ var brightness = 0;
2837
+ var tempCanvas = document.createElement("canvas");
2838
+ tempCanvas.width = img.width;
2839
+ tempCanvas.height = img.height;
2840
+ var tempCtx = tempCanvas.getContext("2d");
2841
+ tempCtx.drawImage(img, 0, 0);
2842
+ var imageData = tempCtx.getImageData(0, 0, img.width, img.height).data;
2843
+ for (var i = 0; i < imageData.length; i += 4) {
2844
+ var r = imageData[i];
2845
+ var g = imageData[i + 1];
2846
+ var b = imageData[i + 2];
2847
+ brightness += (r + g + b) / 3;
2848
+ }
2849
+ return Math.round(brightness / (img.width * img.height));
2850
+ };
2851
+ if (iconUrl) {
2852
+ var img_1 = new Image();
2853
+ img_1.crossOrigin = "anonymous";
2854
+ img_1.src = iconUrl;
2855
+ img_1.onload = function () {
2856
+ if (size > 50) {
2857
+ drawWithImage(img_1);
2858
+ }
2859
+ else {
2860
+ drawWithoutImage(img_1);
2861
+ }
2862
+ res(canvas);
2863
+ };
2864
+ img_1.onerror = function () {
2865
+ drawWithoutImage();
2866
+ res(canvas);
2867
+ };
2868
+ }
2869
+ else {
2870
+ drawWithoutImage();
2871
+ res(canvas);
2872
+ }
2873
+ });
2874
+ _clusterImageCache.Set(key, prom);
2875
+ prom.then(function (canvas) {
2876
+ _clusterImageLoadedCache.Set(key, canvas);
2877
+ _clusterImageCache.Set(key, null);
2878
+ });
2879
+ return [4 /*yield*/, prom];
2880
+ case 3: return [2 /*return*/, _a.sent()];
2881
+ }
2882
+ });
2883
+ });
2884
+ }
2885
+ function getClusterImage(params) {
2886
+ var size = params.size, txtColor = params.txtColor, bgColor = params.bgColor, text = params.text, iconUrl = params.iconUrl;
2887
+ var key = "".concat(size, "-").concat(txtColor, "-").concat(bgColor, "-").concat(text, "-").concat(iconUrl);
2888
+ var cacheData = _clusterImageLoadedCache.Get(key);
2889
+ // If available then return.
2890
+ if (cacheData) {
2891
+ return cacheData;
2892
+ }
2893
+ // If not available then queue for it to cook.
2894
+ else {
2895
+ _loadClusterImage(params);
2896
+ }
2897
+ return null;
2898
+ }
2899
+ var FORCE_UPDATE_BATCH_SIZE = 1000;
2900
+ var FORCE_UPDATE_BATCH_DELAY = 100;
2713
2901
  var PointClustering = /** @class */ (function () {
2714
2902
  function PointClustering(register, menuItemId) {
2715
2903
  var _this = this;
2716
2904
  this.disposed = false;
2717
2905
  this.registeredEntityIds = new Set();
2906
+ // Queue to force update entities.
2907
+ this.updateEntityQueue = [];
2718
2908
  this.register = register;
2719
2909
  this.viewer = register.Viewer;
2720
2910
  this.menuItemId = menuItemId;
2721
- this.updateClusterSpacing(0);
2722
2911
  var boundary = new Rectangle(0, 0, 360, 180);
2723
- this.quadTree = new Quad(boundary, 4);
2912
+ this.quadTree = new Quad(boundary, 30);
2724
2913
  this.prevClusteredEntities = new Set();
2725
2914
  this.currClusteredEntities = new Set();
2726
2915
  this.clusterEntities = new Map();
@@ -2729,6 +2918,39 @@
2729
2918
  }, 1000);
2730
2919
  this.listenCamera();
2731
2920
  }
2921
+ PointClustering.prototype.queueForceUpdate = function (entityIds) {
2922
+ for (var i = 0; i < entityIds.length; i++) {
2923
+ if (this.updateEntityQueue.includes(entityIds[i])) {
2924
+ continue;
2925
+ }
2926
+ this.updateEntityQueue.push(entityIds[i]);
2927
+ }
2928
+ this.runForceUpdateQueue();
2929
+ };
2930
+ PointClustering.prototype.runForceUpdateQueue = function () {
2931
+ var _this = this;
2932
+ if (!this.updateEntityQueue.length) {
2933
+ return;
2934
+ }
2935
+ if (this.queueInterval) {
2936
+ return;
2937
+ }
2938
+ this.queueInterval = setInterval(function () {
2939
+ if (_this.disposed) {
2940
+ clearInterval(_this.queueInterval);
2941
+ _this.queueInterval = null;
2942
+ return;
2943
+ }
2944
+ var ids = _this.updateEntityQueue.splice(0, FORCE_UPDATE_BATCH_SIZE);
2945
+ _this.register.ForceUpdate({
2946
+ entityIds: ids
2947
+ });
2948
+ if (!_this.updateEntityQueue.length) {
2949
+ clearInterval(_this.queueInterval);
2950
+ _this.queueInterval = null;
2951
+ }
2952
+ }, FORCE_UPDATE_BATCH_DELAY);
2953
+ };
2732
2954
  /**
2733
2955
  * Starts listening to camera changes.
2734
2956
  * This will trigger the clustering update whenever camera is moved.
@@ -2783,6 +3005,7 @@
2783
3005
  this.clusterEntities.clear();
2784
3006
  this.unlistenCamera();
2785
3007
  this.disposed = true;
3008
+ this.updateEntityQueue = [];
2786
3009
  // Restore entities.
2787
3010
  var toUpdateIds = [];
2788
3011
  for (var _c = 0, _d = Array.from(this.registeredEntityIds); _c < _d.length; _c++) {
@@ -2810,8 +3033,8 @@
2810
3033
  PointClustering.prototype.calculateCentroid = function (points) {
2811
3034
  var lonSum = 0;
2812
3035
  var latSum = 0;
2813
- for (var _i = 0, points_1 = points; _i < points_1.length; _i++) {
2814
- var point = points_1[_i];
3036
+ for (var _i = 0, points_2 = points; _i < points_2.length; _i++) {
3037
+ var point = points_2[_i];
2815
3038
  lonSum += point.lon;
2816
3039
  latSum += point.lat;
2817
3040
  }
@@ -2831,9 +3054,10 @@
2831
3054
  // 1: Update precision.
2832
3055
  // This defines how far apart these clusters can be.
2833
3056
  var cameraHeight = this.viewer.camera.positionCartographic.height;
2834
- this.updateClusterSpacing(cameraHeight);
3057
+ this.getClusterSpacing(cameraHeight);
2835
3058
  // 2: Get clusters.
2836
3059
  var _a = this.getClusters(), clusters = _a.clusters, noLongerClustered = _a.noLongerClustered;
3060
+ var entitiesToUpdate = [];
2837
3061
  // 3: Remove all cesium cluster entities that are no longer clustered.
2838
3062
  for (var _i = 0, _b = Array.from(noLongerClustered); _i < _b.length; _i++) {
2839
3063
  var id = _b[_i];
@@ -2850,193 +3074,180 @@
2850
3074
  });
2851
3075
  if (rego && rego.suppressShow) {
2852
3076
  rego.suppressShow = false;
2853
- this.register.ForceUpdate({
2854
- entityIds: [id],
2855
- });
3077
+ entitiesToUpdate.push(id);
2856
3078
  }
2857
3079
  }
2858
3080
  }
2859
3081
  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;
3082
+ // const MIN_SCALE = 15;
3083
+ // const MAX_SCALE = 80;
3084
+ // const SCALE_PER_POINT = 1.01;
3085
+ // let scale = MIN_SCALE + (count * SCALE_PER_POINT);
3086
+ // scale = Math.min(scale, MAX_SCALE);
3087
+ // return scale;
3088
+ if (count >= 10000) {
3089
+ return 140;
3090
+ }
3091
+ if (count >= 1000) {
3092
+ return 120;
3093
+ }
3094
+ if (count >= 100) {
3095
+ return 90;
3096
+ }
3097
+ if (count >= 10) {
3098
+ return 80;
3099
+ }
3100
+ return 70;
2879
3101
  };
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];
3102
+ /**
3103
+ * Generate circle with label in center.
3104
+ * @param count
3105
+ * @returns
3106
+ */
3107
+ var getCanvas = function (count) {
3108
+ var params = {
3109
+ size: getScale(count),
3110
+ txtColor: _this.pointColorTxt,
3111
+ bgColor: _this.pointColorBg,
3112
+ text: String(count),
3113
+ iconUrl: _this.iconUrl
3114
+ };
3115
+ return getClusterImage(params);
3116
+ };
3117
+ var _loop_1 = function (cluster) {
2883
3118
  var clusterId = cluster.center.id;
2884
- var clusterEntity = this.clusterEntities.get(clusterId);
2885
- var centroid = this.calculateCentroid(cluster.points);
3119
+ var clusterEntity = this_1.clusterEntities.get(clusterId);
3120
+ var centroid = this_1.calculateCentroid(cluster.points);
2886
3121
  var count = cluster.points.length;
2887
3122
  if (clusterEntity) {
2888
3123
  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);
3124
+ clusterEntity.billboard.image = new Cesium.CallbackProperty(function () {
3125
+ return getCanvas(count);
3126
+ }, false),
3127
+ clusterEntity.billboard.width = getScale(cluster.points.length);
2892
3128
  clusterEntity.billboard.height = getScale(cluster.points.length);
2893
3129
  }
2894
3130
  else {
2895
- clusterEntity = this.viewer.entities.add({
3131
+ clusterEntity = this_1.viewer.entities.add({
2896
3132
  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
3133
  billboard: {
2903
3134
  heightReference: Cesium.HeightReference.NONE,
2904
- image: getLabel(count),
3135
+ image: new Cesium.CallbackProperty(function () {
3136
+ return getCanvas(count);
3137
+ }, false),
2905
3138
  width: getScale(count),
2906
3139
  height: getScale(count),
2907
3140
  verticalOrigin: Cesium.VerticalOrigin.CENTER,
2908
3141
  horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
2909
- disableDepthTestDistance: Number.POSITIVE_INFINITY
3142
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
3143
+ scaleByDistance: new Cesium.NearFarScalar(1.5e2, 0.5, 3.0e7, 0.1),
2910
3144
  }
2911
3145
  });
2912
- this.clusterEntities.set(clusterId, clusterEntity);
3146
+ this_1.clusterEntities.set(clusterId, clusterEntity);
2913
3147
  }
2914
3148
  for (var i = 0; i < cluster.points.length; i++) {
2915
3149
  var entityId = cluster.points[i].id;
2916
- var rego = this.register.GetRego({
3150
+ var rego = this_1.register.GetRego({
2917
3151
  entityId: entityId,
2918
- menuItemId: this.menuItemId
3152
+ menuItemId: this_1.menuItemId
2919
3153
  });
2920
3154
  if (rego && !rego.suppressShow) {
2921
3155
  rego.suppressShow = true;
2922
- this.register.ForceUpdate({
2923
- entityIds: [entityId],
2924
- });
3156
+ entitiesToUpdate.push(entityId);
2925
3157
  }
2926
3158
  }
3159
+ };
3160
+ var this_1 = this;
3161
+ // 5: iterate over clusters and add/update them as Cesium entities.
3162
+ for (var _e = 0, clusters_1 = clusters; _e < clusters_1.length; _e++) {
3163
+ var cluster = clusters_1[_e];
3164
+ _loop_1(cluster);
2927
3165
  }
2928
- var _loop_1 = function (clusterId, clusterEntity) {
3166
+ this.queueForceUpdate(entitiesToUpdate);
3167
+ var _loop_2 = function (clusterId, clusterEntity) {
2929
3168
  if (!clusters.find(function (x) { return x.center.id == clusterId; })) {
2930
- this_1.viewer.entities.remove(clusterEntity);
2931
- this_1.clusterEntities.delete(clusterId);
3169
+ this_2.viewer.entities.remove(clusterEntity);
3170
+ this_2.clusterEntities.delete(clusterId);
2932
3171
  }
2933
3172
  };
2934
- var this_1 = this;
3173
+ var this_2 = this;
2935
3174
  // 6: Iterate over existing cluster entities and remove those that are no longer clustered.
2936
3175
  for (var _f = 0, _g = Array.from(this.clusterEntities); _f < _g.length; _f++) {
2937
3176
  var _h = _g[_f], clusterId = _h[0], clusterEntity = _h[1];
2938
- _loop_1(clusterId, clusterEntity);
3177
+ _loop_2(clusterId, clusterEntity);
2939
3178
  }
2940
3179
  };
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];
3180
+ PointClustering.prototype.getClusterSpacing = function (distanceFromCluster) {
3181
+ // Distance thresholds in meters from the cluster.
3182
+ var distanceThresholds = [3000, 4000, 5300, 6000, 7000];
2948
3183
  // 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;
3184
+ var distanceIncrements = [0.001, 0.002, 0.01, 0.03, 0.09];
3185
+ var index = 0;
3186
+ if (distanceFromCluster) {
3187
+ for (var i = 0; i < distanceThresholds.length; i++) {
3188
+ if (distanceFromCluster > distanceThresholds[i]) {
3189
+ index = i;
3190
+ }
2956
3191
  }
2957
- spacing += distanceIncrements[i];
2958
3192
  }
2959
- this.distanceBetweenClusters = spacing;
3193
+ return distanceIncrements[index];
2960
3194
  };
2961
- /**
2962
- * Gathers clusters.
2963
- * @returns
2964
- */
2965
3195
  PointClustering.prototype.getClusters = function () {
2966
3196
  var _this = this;
2967
3197
  this.currClusteredEntities.clear();
2968
3198
  var clusters = [];
2969
3199
  var processedPoints = new Set();
2970
3200
  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);
3201
+ var MIN_CAMERA_DISTANCE = 5000;
3202
+ var processQuad = function (quad) {
3203
+ // Distance to quad.
3204
+ // TODO: Needs improvement.
3205
+ var distanceToQuad = quad.GetDistanceToQuad(cameraPosition);
3206
+ // Skip quads that are too close.
3207
+ if (distanceToQuad >= MIN_CAMERA_DISTANCE) {
3208
+ for (var _i = 0, _a = quad.points; _i < _a.length; _i++) {
3209
+ var point = _a[_i];
3210
+ // Skip points already processed in previous clusters.
3211
+ if (processedPoints.has(point.id)) {
3212
+ continue;
3213
+ }
3214
+ // Skip points closer than MIN_CAMERA_DISTANCE meters to the camera.
3215
+ var cartesian3 = Cesium.Cartesian3.fromDegrees(point.lon, point.lat);
3216
+ var distanceFromCluster = Cesium.Cartesian3.distance(cartesian3, cameraPosition);
3217
+ if (distanceFromCluster <= MIN_CAMERA_DISTANCE) {
3218
+ continue;
3219
+ }
3220
+ var found = [];
3221
+ var nearbyPoints = quad.Query(new Circle(point.lon, point.lat, _this.getClusterSpacing(distanceFromCluster)), found);
3222
+ if (nearbyPoints.length > 1) {
3223
+ var cluster = { center: point, points: [] };
3224
+ for (var _b = 0, nearbyPoints_1 = nearbyPoints; _b < nearbyPoints_1.length; _b++) {
3225
+ var nearby = nearbyPoints_1[_b];
3226
+ if (!cluster.points.includes(nearby)) {
3227
+ cluster.points.push(nearby);
3228
+ processedPoints.add(nearby.id);
3229
+ _this.currClusteredEntities.add(nearby.id);
3230
+ }
3231
+ }
3232
+ clusters.push(cluster);
2992
3233
  }
2993
3234
  }
2994
- clusters.push(cluster);
2995
3235
  }
2996
- }
3236
+ if (quad.divided) {
3237
+ processQuad(quad.northwest);
3238
+ processQuad(quad.northeast);
3239
+ processQuad(quad.southwest);
3240
+ processQuad(quad.southeast);
3241
+ }
3242
+ };
3243
+ processQuad(this.quadTree);
2997
3244
  // Filter out clusters with only one point.
2998
3245
  var validClusters = clusters.filter(function (cluster) { return cluster.points.length > 1; });
2999
- // Merge adjacent clusters.
3000
- var mergedClusters = this.mergeClusters(validClusters);
3001
3246
  // Get the entity IDs that are no longer clustered
3002
3247
  var noLongerClustered = new Set(Array.from(this.prevClusteredEntities).filter(function (id) { return !_this.currClusteredEntities.has(id); }));
3003
3248
  // Update the previous clustered entities ref.
3004
3249
  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;
3250
+ return { clusters: validClusters, noLongerClustered: noLongerClustered };
3040
3251
  };
3041
3252
  /**
3042
3253
  * Removes Cesium cluster entity.
@@ -3064,24 +3275,7 @@
3064
3275
  PointClustering.prototype.addPoint = function (id, cartesian3) {
3065
3276
  var point = this.convertCartesianToCartographic(cartesian3);
3066
3277
  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;
3278
+ return this.quadTree.Insert(point);
3085
3279
  };
3086
3280
  /**
3087
3281
  * Adds entity to clustering logic.
@@ -3105,14 +3299,14 @@
3105
3299
  return false;
3106
3300
  }
3107
3301
  if (!this.pointColorBg && entity.point) {
3108
- this.pointColorBg = GetValue$1(this.viewer, entity.point.color);
3109
- if (this.pointColorBg) {
3302
+ var pointColorBg = GetValue$1(this.viewer, entity.point.color);
3303
+ if (pointColorBg) {
3110
3304
  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);
3305
+ if (pointColorBg instanceof Object) {
3306
+ cColor = new Cesium.Color(pointColorBg.red, pointColorBg.green, pointColorBg.blue, pointColorBg.alpha);
3113
3307
  }
3114
- else if (typeof this.pointColorBg === "string") {
3115
- cColor = Cesium.Color.fromCssColorString(this.pointColorBg);
3308
+ else if (typeof pointColorBg === "string") {
3309
+ cColor = Cesium.Color.fromCssColorString(pointColorBg);
3116
3310
  }
3117
3311
  // Determine if text color should instead be black based on background.
3118
3312
  // cColor contains r,g,b,a values where r,g,b are in the range [0,1].
@@ -3124,11 +3318,21 @@
3124
3318
  else {
3125
3319
  this.pointColorTxt = "white";
3126
3320
  }
3321
+ this.pointColorBg = cColor.toCssColorString();
3127
3322
  }
3128
3323
  }
3129
3324
  }
3325
+ if (!this.iconUrl && entity.billboard) {
3326
+ var iconUrl = GetValue$1(this.viewer, entity.billboard.image);
3327
+ if (typeof iconUrl == "string") {
3328
+ this.iconUrl = iconUrl;
3329
+ }
3330
+ }
3331
+ var added = this.addPoint(id, pos3d);
3332
+ if (!added) {
3333
+ return false;
3334
+ }
3130
3335
  this.registeredEntityIds.add(id);
3131
- this.addPoint(id, pos3d);
3132
3336
  this.updateQueue.Call();
3133
3337
  return true;
3134
3338
  };
@@ -10608,7 +10812,7 @@
10608
10812
  ViewRenderEngine.Render = Render;
10609
10813
  })(exports.ViewRenderEngine || (exports.ViewRenderEngine = {}));
10610
10814
 
10611
- var VERSION = "2.1.8";
10815
+ var VERSION = "2.2.0";
10612
10816
 
10613
10817
  exports.VERSION = VERSION;
10614
10818
  exports.CesiumViewMonitor = CesiumViewMonitor;