bruce-cesium 2.1.9 → 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,6 +2708,30 @@
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
  }());
2713
2737
  var _clusterImageCache = new bruceModels.LRUCache(500);
@@ -2730,14 +2754,15 @@
2730
2754
  canvas.width = size;
2731
2755
  canvas.height = size;
2732
2756
  var ctx = canvas.getContext("2d");
2757
+ var WHITESPACE_PADDING_PERCENT = 0.05;
2758
+ var radius = (size / 2) - (size * WHITESPACE_PADDING_PERCENT);
2733
2759
  var drawWithoutImage = function (img) {
2734
2760
  ctx.beginPath();
2735
- ctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI, false);
2761
+ ctx.arc(size / 2, size / 2, radius, 0, 2 * Math.PI, false);
2736
2762
  var fill = null;
2737
2763
  var txt = null;
2738
2764
  if (img) {
2739
2765
  var brightness = calculateImageBrightness(img);
2740
- // brightness < 128 = light background.
2741
2766
  fill = brightness < 128 ? "white" : "#114d78";
2742
2767
  txt = brightness < 128 ? "black" : "white";
2743
2768
  }
@@ -2747,9 +2772,9 @@
2747
2772
  }
2748
2773
  ctx.fillStyle = fill;
2749
2774
  ctx.fill();
2750
- var maxTextWidth = size * 0.8;
2751
- var maxTextHeight = size * 0.8;
2752
- var minTextSize = Math.floor(size / 10);
2775
+ var maxTextWidth = size * 0.7;
2776
+ var maxTextHeight = size * 0.7;
2777
+ var minTextSize = Math.floor(size / 12);
2753
2778
  var textSize = findOptimalFontSize(text, maxTextWidth, maxTextHeight, minTextSize);
2754
2779
  ctx.font = "bold ".concat(textSize, "px Arial");
2755
2780
  ctx.fillStyle = txt;
@@ -2759,14 +2784,14 @@
2759
2784
  };
2760
2785
  var drawWithImage = function (img) {
2761
2786
  var aspectRatio = img.width / img.height;
2762
- var imageSize = Math.min(size / 2, img.width, img.height);
2763
- if (imageSize / aspectRatio > size / 2) {
2764
- imageSize = (size / 2) * aspectRatio;
2787
+ var imageSize = Math.min(radius, img.width, img.height);
2788
+ if (imageSize / aspectRatio > radius) {
2789
+ imageSize = radius * aspectRatio;
2765
2790
  }
2766
2791
  var imageX = (size - imageSize) / 2;
2767
2792
  var imageY = (size - imageSize) / 2 - imageSize / 3;
2768
2793
  ctx.beginPath();
2769
- ctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI, false);
2794
+ ctx.arc(size / 2, size / 2, radius, 0, 2 * Math.PI, false);
2770
2795
  var brightness = calculateImageBrightness(img);
2771
2796
  var bgColor = brightness < 128 ? "white" : "#114d78";
2772
2797
  var txtColor = brightness < 128 ? "black" : "white";
@@ -2777,7 +2802,7 @@
2777
2802
  ctx.shadowOffsetY = 3;
2778
2803
  ctx.shadowBlur = 5;
2779
2804
  ctx.drawImage(img, imageX, imageY, imageSize, imageSize);
2780
- var padding = imageSize / 6;
2805
+ var padding = imageSize / 7;
2781
2806
  var maxTextWidth = imageSize;
2782
2807
  var maxTextHeight = imageSize - padding;
2783
2808
  var minTextSize = Math.floor(imageSize / 5);
@@ -2799,7 +2824,7 @@
2799
2824
  while (fontSize > minSize) {
2800
2825
  tempCtx.font = "bold ".concat(fontSize, "px Arial");
2801
2826
  var measuredWidth = tempCtx.measureText(text).width;
2802
- var measuredHeight = fontSize * 1.2; // Allowing some padding
2827
+ var measuredHeight = fontSize * 1.2;
2803
2828
  if (measuredWidth <= maxWidth && measuredHeight <= maxHeight) {
2804
2829
  break;
2805
2830
  }
@@ -2871,17 +2896,20 @@
2871
2896
  }
2872
2897
  return null;
2873
2898
  }
2899
+ var FORCE_UPDATE_BATCH_SIZE = 1000;
2900
+ var FORCE_UPDATE_BATCH_DELAY = 100;
2874
2901
  var PointClustering = /** @class */ (function () {
2875
2902
  function PointClustering(register, menuItemId) {
2876
2903
  var _this = this;
2877
2904
  this.disposed = false;
2878
2905
  this.registeredEntityIds = new Set();
2906
+ // Queue to force update entities.
2907
+ this.updateEntityQueue = [];
2879
2908
  this.register = register;
2880
2909
  this.viewer = register.Viewer;
2881
2910
  this.menuItemId = menuItemId;
2882
- this.updateClusterSpacing(0);
2883
2911
  var boundary = new Rectangle(0, 0, 360, 180);
2884
- this.quadTree = new Quad(boundary, 4);
2912
+ this.quadTree = new Quad(boundary, 30);
2885
2913
  this.prevClusteredEntities = new Set();
2886
2914
  this.currClusteredEntities = new Set();
2887
2915
  this.clusterEntities = new Map();
@@ -2890,6 +2918,39 @@
2890
2918
  }, 1000);
2891
2919
  this.listenCamera();
2892
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
+ };
2893
2954
  /**
2894
2955
  * Starts listening to camera changes.
2895
2956
  * This will trigger the clustering update whenever camera is moved.
@@ -2944,6 +3005,7 @@
2944
3005
  this.clusterEntities.clear();
2945
3006
  this.unlistenCamera();
2946
3007
  this.disposed = true;
3008
+ this.updateEntityQueue = [];
2947
3009
  // Restore entities.
2948
3010
  var toUpdateIds = [];
2949
3011
  for (var _c = 0, _d = Array.from(this.registeredEntityIds); _c < _d.length; _c++) {
@@ -2971,8 +3033,8 @@
2971
3033
  PointClustering.prototype.calculateCentroid = function (points) {
2972
3034
  var lonSum = 0;
2973
3035
  var latSum = 0;
2974
- for (var _i = 0, points_1 = points; _i < points_1.length; _i++) {
2975
- var point = points_1[_i];
3036
+ for (var _i = 0, points_2 = points; _i < points_2.length; _i++) {
3037
+ var point = points_2[_i];
2976
3038
  lonSum += point.lon;
2977
3039
  latSum += point.lat;
2978
3040
  }
@@ -2992,9 +3054,10 @@
2992
3054
  // 1: Update precision.
2993
3055
  // This defines how far apart these clusters can be.
2994
3056
  var cameraHeight = this.viewer.camera.positionCartographic.height;
2995
- this.updateClusterSpacing(cameraHeight);
3057
+ this.getClusterSpacing(cameraHeight);
2996
3058
  // 2: Get clusters.
2997
3059
  var _a = this.getClusters(), clusters = _a.clusters, noLongerClustered = _a.noLongerClustered;
3060
+ var entitiesToUpdate = [];
2998
3061
  // 3: Remove all cesium cluster entities that are no longer clustered.
2999
3062
  for (var _i = 0, _b = Array.from(noLongerClustered); _i < _b.length; _i++) {
3000
3063
  var id = _b[_i];
@@ -3011,18 +3074,30 @@
3011
3074
  });
3012
3075
  if (rego && rego.suppressShow) {
3013
3076
  rego.suppressShow = false;
3014
- this.register.ForceUpdate({
3015
- entityIds: [id],
3016
- });
3077
+ entitiesToUpdate.push(id);
3017
3078
  }
3018
3079
  }
3019
3080
  }
3020
3081
  var getScale = function (count) {
3021
- var baseSize = 20;
3022
- var scalingFactor = 2;
3023
- var scale = baseSize + scalingFactor * (count - 1);
3024
- scale = Math.min(scale, 120);
3025
- return scale;
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;
3026
3101
  };
3027
3102
  /**
3028
3103
  * Generate circle with label in center.
@@ -3064,7 +3139,8 @@
3064
3139
  height: getScale(count),
3065
3140
  verticalOrigin: Cesium.VerticalOrigin.CENTER,
3066
3141
  horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
3067
- disableDepthTestDistance: Number.POSITIVE_INFINITY
3142
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
3143
+ scaleByDistance: new Cesium.NearFarScalar(1.5e2, 0.5, 3.0e7, 0.1),
3068
3144
  }
3069
3145
  });
3070
3146
  this_1.clusterEntities.set(clusterId, clusterEntity);
@@ -3077,9 +3153,7 @@
3077
3153
  });
3078
3154
  if (rego && !rego.suppressShow) {
3079
3155
  rego.suppressShow = true;
3080
- this_1.register.ForceUpdate({
3081
- entityIds: [entityId],
3082
- });
3156
+ entitiesToUpdate.push(entityId);
3083
3157
  }
3084
3158
  }
3085
3159
  };
@@ -3089,6 +3163,7 @@
3089
3163
  var cluster = clusters_1[_e];
3090
3164
  _loop_1(cluster);
3091
3165
  }
3166
+ this.queueForceUpdate(entitiesToUpdate);
3092
3167
  var _loop_2 = function (clusterId, clusterEntity) {
3093
3168
  if (!clusters.find(function (x) { return x.center.id == clusterId; })) {
3094
3169
  this_2.viewer.entities.remove(clusterEntity);
@@ -3102,105 +3177,77 @@
3102
3177
  _loop_2(clusterId, clusterEntity);
3103
3178
  }
3104
3179
  };
3105
- /**
3106
- * Updates how apart clusters can be based on camera distance.
3107
- * @param cameraHeight
3108
- */
3109
- PointClustering.prototype.updateClusterSpacing = function (cameraHeight) {
3110
- // Camera height thresholds in meters.
3111
- var cameraHeightThresholds = [4000, 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];
3112
3183
  // Distance increments in degrees.
3113
- var distanceIncrements = [0.005, 0.01, 0.02, 0.04, 0.1, 0.5];
3114
- // Find the appropriate spacing based on the camera height.
3115
- var spacing = 0;
3116
- for (var i = 0; i < cameraHeightThresholds.length; i++) {
3117
- if (cameraHeight && cameraHeight < cameraHeightThresholds[i]) {
3118
- spacing += distanceIncrements[i];
3119
- 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
+ }
3120
3191
  }
3121
- spacing += distanceIncrements[i];
3122
3192
  }
3123
- this.distanceBetweenClusters = spacing;
3193
+ return distanceIncrements[index];
3124
3194
  };
3125
- /**
3126
- * Gathers clusters.
3127
- * @returns
3128
- */
3129
3195
  PointClustering.prototype.getClusters = function () {
3130
3196
  var _this = this;
3131
3197
  this.currClusteredEntities.clear();
3132
3198
  var clusters = [];
3133
3199
  var processedPoints = new Set();
3134
3200
  var cameraPosition = this.viewer.camera.position;
3135
- for (var _i = 0, _a = this.quadTree.points; _i < _a.length; _i++) {
3136
- var point = _a[_i];
3137
- // Skip points already processed in previous clusters.
3138
- if (processedPoints.has(point.id)) {
3139
- continue;
3140
- }
3141
- // Skip points closer than 4000 meters to the camera.
3142
- var cartesian3 = Cesium.Cartesian3.fromDegrees(point.lon, point.lat);
3143
- if (Cesium.Cartesian3.distance(cartesian3, cameraPosition) <= 4000) {
3144
- continue;
3145
- }
3146
- var found = [];
3147
- var nearbyPoints = this.quadTree.Query(new Circle(point.lon, point.lat, this.distanceBetweenClusters), found);
3148
- if (nearbyPoints.length > 1) {
3149
- var cluster = { center: point, points: [] };
3150
- for (var _b = 0, nearbyPoints_1 = nearbyPoints; _b < nearbyPoints_1.length; _b++) {
3151
- var nearby = nearbyPoints_1[_b];
3152
- if (!cluster.points.includes(nearby)) {
3153
- cluster.points.push(nearby);
3154
- processedPoints.add(nearby.id);
3155
- 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);
3156
3233
  }
3157
3234
  }
3158
- clusters.push(cluster);
3159
3235
  }
3160
- }
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);
3161
3244
  // Filter out clusters with only one point.
3162
3245
  var validClusters = clusters.filter(function (cluster) { return cluster.points.length > 1; });
3163
- // Merge adjacent clusters.
3164
- var mergedClusters = this.mergeClusters(validClusters);
3165
3246
  // Get the entity IDs that are no longer clustered
3166
3247
  var noLongerClustered = new Set(Array.from(this.prevClusteredEntities).filter(function (id) { return !_this.currClusteredEntities.has(id); }));
3167
3248
  // Update the previous clustered entities ref.
3168
3249
  this.prevClusteredEntities = new Set(this.currClusteredEntities);
3169
- return { clusters: mergedClusters, noLongerClustered: noLongerClustered };
3170
- };
3171
- /**
3172
- * Merges clusters that are nearby based on the distanceBetweenClusters value.
3173
- * @param clusters
3174
- * @returns
3175
- */
3176
- PointClustering.prototype.mergeClusters = function (clusters) {
3177
- var _a;
3178
- var mergedClusters = [].concat(clusters);
3179
- // Keep looping while merges keep happening.
3180
- var mergeOccurred = true;
3181
- while (mergeOccurred) {
3182
- mergeOccurred = false;
3183
- for (var i = 0; i < mergedClusters.length - 1; i++) {
3184
- for (var j = i + 1; j < mergedClusters.length; j++) {
3185
- var cluster1 = mergedClusters[i];
3186
- var cluster2 = mergedClusters[j];
3187
- var centerDistance = this.calculateDistance(cluster1.center.lon, cluster1.center.lat, cluster2.center.lon, cluster2.center.lat);
3188
- var distanceThreshold = this.distanceBetweenClusters;
3189
- if (centerDistance <= distanceThreshold) {
3190
- // Merge clusters.
3191
- (_a = cluster1.points).push.apply(_a, cluster2.points);
3192
- mergedClusters.splice(j, 1);
3193
- this.removeClusterEntity(cluster2.center.id);
3194
- mergeOccurred = true;
3195
- break;
3196
- }
3197
- }
3198
- if (mergeOccurred) {
3199
- break;
3200
- }
3201
- }
3202
- }
3203
- return mergedClusters;
3250
+ return { clusters: validClusters, noLongerClustered: noLongerClustered };
3204
3251
  };
3205
3252
  /**
3206
3253
  * Removes Cesium cluster entity.
@@ -3228,24 +3275,7 @@
3228
3275
  PointClustering.prototype.addPoint = function (id, cartesian3) {
3229
3276
  var point = this.convertCartesianToCartographic(cartesian3);
3230
3277
  point.id = id;
3231
- this.quadTree.Insert(point);
3232
- };
3233
- /**
3234
- * Calculates rough distance across earth between two points.
3235
- * @param lon1
3236
- * @param lat1
3237
- * @param lon2
3238
- * @param lat2
3239
- * @returns
3240
- */
3241
- PointClustering.prototype.calculateDistance = function (lon1, lat1, lon2, lat2) {
3242
- var lonDelta = Math.abs(lon1 - lon2);
3243
- var latDelta = Math.abs(lat1 - lat2);
3244
- // Approximate radius of the Earth in kilometers
3245
- var earthRadius = 6371;
3246
- var distance = 2 * Math.asin(Math.sqrt(Math.sin(latDelta / 2) * Math.sin(latDelta / 2) +
3247
- Math.cos(lat1) * Math.cos(lat2) * Math.sin(lonDelta / 2) * Math.sin(lonDelta / 2))) * earthRadius;
3248
- return distance;
3278
+ return this.quadTree.Insert(point);
3249
3279
  };
3250
3280
  /**
3251
3281
  * Adds entity to clustering logic.
@@ -3298,8 +3328,11 @@
3298
3328
  this.iconUrl = iconUrl;
3299
3329
  }
3300
3330
  }
3331
+ var added = this.addPoint(id, pos3d);
3332
+ if (!added) {
3333
+ return false;
3334
+ }
3301
3335
  this.registeredEntityIds.add(id);
3302
- this.addPoint(id, pos3d);
3303
3336
  this.updateQueue.Call();
3304
3337
  return true;
3305
3338
  };
@@ -10779,7 +10812,7 @@
10779
10812
  ViewRenderEngine.Render = Render;
10780
10813
  })(exports.ViewRenderEngine || (exports.ViewRenderEngine = {}));
10781
10814
 
10782
- var VERSION = "2.1.9";
10815
+ var VERSION = "2.2.0";
10783
10816
 
10784
10817
  exports.VERSION = VERSION;
10785
10818
  exports.CesiumViewMonitor = CesiumViewMonitor;