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.
- package/dist/bruce-cesium.es5.js +365 -161
- package/dist/bruce-cesium.es5.js.map +1 -1
- package/dist/bruce-cesium.umd.js +364 -160
- package/dist/bruce-cesium.umd.js.map +1 -1
- package/dist/lib/bruce-cesium.js +1 -1
- package/dist/lib/rendering/render-managers/common/point-clustering.js +398 -158
- package/dist/lib/rendering/render-managers/common/point-clustering.js.map +1 -1
- package/dist/types/bruce-cesium.d.ts +1 -1
- package/dist/types/rendering/render-managers/common/point-clustering.d.ts +6 -25
- package/package.json +1 -1
|
@@ -1,4 +1,40 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
2
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
39
|
exports.PointClustering = void 0;
|
|
4
40
|
var bruce_models_1 = require("bruce-models");
|
|
@@ -154,19 +190,208 @@ var Quad = /** @class */ (function () {
|
|
|
154
190
|
this.southeast.Remove(point);
|
|
155
191
|
}
|
|
156
192
|
};
|
|
193
|
+
Quad.prototype.GetDistanceToQuad = function (pos3d) {
|
|
194
|
+
var minLat = this.boundary.y - this.boundary.h;
|
|
195
|
+
var maxLat = this.boundary.y + this.boundary.h;
|
|
196
|
+
var minLon = this.boundary.x - this.boundary.w;
|
|
197
|
+
var maxLon = this.boundary.x + this.boundary.w;
|
|
198
|
+
var points = [
|
|
199
|
+
// Corners.
|
|
200
|
+
new Cesium.Cartesian3(minLon, minLat, 0),
|
|
201
|
+
new Cesium.Cartesian3(minLon, maxLat, 0),
|
|
202
|
+
new Cesium.Cartesian3(maxLon, minLat, 0),
|
|
203
|
+
new Cesium.Cartesian3(maxLon, maxLat, 0),
|
|
204
|
+
// Center.
|
|
205
|
+
new Cesium.Cartesian3(this.boundary.x, this.boundary.y, 0)
|
|
206
|
+
];
|
|
207
|
+
var shortest = Number.MAX_VALUE;
|
|
208
|
+
for (var _i = 0, points_1 = points; _i < points_1.length; _i++) {
|
|
209
|
+
var point = points_1[_i];
|
|
210
|
+
var distance = Cesium.Cartesian3.distance(pos3d, point);
|
|
211
|
+
if (distance < shortest) {
|
|
212
|
+
shortest = distance;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return shortest;
|
|
216
|
+
};
|
|
157
217
|
return Quad;
|
|
158
218
|
}());
|
|
219
|
+
var _clusterImageCache = new bruce_models_1.LRUCache(500);
|
|
220
|
+
var _clusterImageLoadedCache = new bruce_models_1.LRUCache(500);
|
|
221
|
+
function _loadClusterImage(params) {
|
|
222
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
223
|
+
var size, txtColor, bgColor, text, iconUrl, key, cacheData, prom;
|
|
224
|
+
return __generator(this, function (_a) {
|
|
225
|
+
switch (_a.label) {
|
|
226
|
+
case 0:
|
|
227
|
+
size = params.size, txtColor = params.txtColor, bgColor = params.bgColor, text = params.text, iconUrl = params.iconUrl;
|
|
228
|
+
key = "".concat(size, "-").concat(txtColor, "-").concat(bgColor, "-").concat(text, "-").concat(iconUrl);
|
|
229
|
+
cacheData = _clusterImageCache.Get(key);
|
|
230
|
+
if (!cacheData) return [3 /*break*/, 2];
|
|
231
|
+
return [4 /*yield*/, cacheData];
|
|
232
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
233
|
+
case 2:
|
|
234
|
+
prom = new Promise(function (res, rej) {
|
|
235
|
+
var canvas = document.createElement("canvas");
|
|
236
|
+
canvas.width = size;
|
|
237
|
+
canvas.height = size;
|
|
238
|
+
var ctx = canvas.getContext("2d");
|
|
239
|
+
var WHITESPACE_PADDING_PERCENT = 0.05;
|
|
240
|
+
var radius = (size / 2) - (size * WHITESPACE_PADDING_PERCENT);
|
|
241
|
+
var drawWithoutImage = function (img) {
|
|
242
|
+
ctx.beginPath();
|
|
243
|
+
ctx.arc(size / 2, size / 2, radius, 0, 2 * Math.PI, false);
|
|
244
|
+
var fill = null;
|
|
245
|
+
var txt = null;
|
|
246
|
+
if (img) {
|
|
247
|
+
var brightness = calculateImageBrightness(img);
|
|
248
|
+
fill = brightness < 128 ? "white" : "#114d78";
|
|
249
|
+
txt = brightness < 128 ? "black" : "white";
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
fill = bgColor ? bgColor : "#114d78";
|
|
253
|
+
txt = txtColor ? txtColor : "white";
|
|
254
|
+
}
|
|
255
|
+
ctx.fillStyle = fill;
|
|
256
|
+
ctx.fill();
|
|
257
|
+
var maxTextWidth = size * 0.7;
|
|
258
|
+
var maxTextHeight = size * 0.7;
|
|
259
|
+
var minTextSize = Math.floor(size / 12);
|
|
260
|
+
var textSize = findOptimalFontSize(text, maxTextWidth, maxTextHeight, minTextSize);
|
|
261
|
+
ctx.font = "bold ".concat(textSize, "px Arial");
|
|
262
|
+
ctx.fillStyle = txt;
|
|
263
|
+
ctx.textAlign = "center";
|
|
264
|
+
ctx.textBaseline = "middle";
|
|
265
|
+
ctx.fillText(text, size / 2, size / 2);
|
|
266
|
+
};
|
|
267
|
+
var drawWithImage = function (img) {
|
|
268
|
+
var aspectRatio = img.width / img.height;
|
|
269
|
+
var imageSize = Math.min(radius, img.width, img.height);
|
|
270
|
+
if (imageSize / aspectRatio > radius) {
|
|
271
|
+
imageSize = radius * aspectRatio;
|
|
272
|
+
}
|
|
273
|
+
var imageX = (size - imageSize) / 2;
|
|
274
|
+
var imageY = (size - imageSize) / 2 - imageSize / 3;
|
|
275
|
+
ctx.beginPath();
|
|
276
|
+
ctx.arc(size / 2, size / 2, radius, 0, 2 * Math.PI, false);
|
|
277
|
+
var brightness = calculateImageBrightness(img);
|
|
278
|
+
var bgColor = brightness < 128 ? "white" : "#114d78";
|
|
279
|
+
var txtColor = brightness < 128 ? "black" : "white";
|
|
280
|
+
ctx.fillStyle = bgColor;
|
|
281
|
+
ctx.fill();
|
|
282
|
+
ctx.shadowColor = "rgba(0, 0, 0, 0.3)";
|
|
283
|
+
ctx.shadowOffsetX = 3;
|
|
284
|
+
ctx.shadowOffsetY = 3;
|
|
285
|
+
ctx.shadowBlur = 5;
|
|
286
|
+
ctx.drawImage(img, imageX, imageY, imageSize, imageSize);
|
|
287
|
+
var padding = imageSize / 7;
|
|
288
|
+
var maxTextWidth = imageSize;
|
|
289
|
+
var maxTextHeight = imageSize - padding;
|
|
290
|
+
var minTextSize = Math.floor(imageSize / 5);
|
|
291
|
+
var textSize = findOptimalFontSize(text, maxTextWidth, maxTextHeight, minTextSize);
|
|
292
|
+
ctx.font = "bold ".concat(textSize, "px Arial");
|
|
293
|
+
ctx.fillStyle = txtColor;
|
|
294
|
+
ctx.textAlign = "center";
|
|
295
|
+
ctx.textBaseline = "top";
|
|
296
|
+
ctx.shadowColor = "rgba(0, 0, 0, 0)";
|
|
297
|
+
ctx.shadowOffsetX = 0;
|
|
298
|
+
ctx.shadowOffsetY = 0;
|
|
299
|
+
ctx.shadowBlur = 0;
|
|
300
|
+
ctx.fillText(text, size / 2, imageY + imageSize + padding);
|
|
301
|
+
};
|
|
302
|
+
var findOptimalFontSize = function (text, maxWidth, maxHeight, minSize) {
|
|
303
|
+
var fontSize = maxHeight;
|
|
304
|
+
var tempCanvas = document.createElement("canvas");
|
|
305
|
+
var tempCtx = tempCanvas.getContext("2d");
|
|
306
|
+
while (fontSize > minSize) {
|
|
307
|
+
tempCtx.font = "bold ".concat(fontSize, "px Arial");
|
|
308
|
+
var measuredWidth = tempCtx.measureText(text).width;
|
|
309
|
+
var measuredHeight = fontSize * 1.2;
|
|
310
|
+
if (measuredWidth <= maxWidth && measuredHeight <= maxHeight) {
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
fontSize--;
|
|
314
|
+
}
|
|
315
|
+
return fontSize;
|
|
316
|
+
};
|
|
317
|
+
var calculateImageBrightness = function (img) {
|
|
318
|
+
var brightness = 0;
|
|
319
|
+
var tempCanvas = document.createElement("canvas");
|
|
320
|
+
tempCanvas.width = img.width;
|
|
321
|
+
tempCanvas.height = img.height;
|
|
322
|
+
var tempCtx = tempCanvas.getContext("2d");
|
|
323
|
+
tempCtx.drawImage(img, 0, 0);
|
|
324
|
+
var imageData = tempCtx.getImageData(0, 0, img.width, img.height).data;
|
|
325
|
+
for (var i = 0; i < imageData.length; i += 4) {
|
|
326
|
+
var r = imageData[i];
|
|
327
|
+
var g = imageData[i + 1];
|
|
328
|
+
var b = imageData[i + 2];
|
|
329
|
+
brightness += (r + g + b) / 3;
|
|
330
|
+
}
|
|
331
|
+
return Math.round(brightness / (img.width * img.height));
|
|
332
|
+
};
|
|
333
|
+
if (iconUrl) {
|
|
334
|
+
var img_1 = new Image();
|
|
335
|
+
img_1.crossOrigin = "anonymous";
|
|
336
|
+
img_1.src = iconUrl;
|
|
337
|
+
img_1.onload = function () {
|
|
338
|
+
if (size > 50) {
|
|
339
|
+
drawWithImage(img_1);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
drawWithoutImage(img_1);
|
|
343
|
+
}
|
|
344
|
+
res(canvas);
|
|
345
|
+
};
|
|
346
|
+
img_1.onerror = function () {
|
|
347
|
+
drawWithoutImage();
|
|
348
|
+
res(canvas);
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
drawWithoutImage();
|
|
353
|
+
res(canvas);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
_clusterImageCache.Set(key, prom);
|
|
357
|
+
prom.then(function (canvas) {
|
|
358
|
+
_clusterImageLoadedCache.Set(key, canvas);
|
|
359
|
+
_clusterImageCache.Set(key, null);
|
|
360
|
+
});
|
|
361
|
+
return [4 /*yield*/, prom];
|
|
362
|
+
case 3: return [2 /*return*/, _a.sent()];
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
function getClusterImage(params) {
|
|
368
|
+
var size = params.size, txtColor = params.txtColor, bgColor = params.bgColor, text = params.text, iconUrl = params.iconUrl;
|
|
369
|
+
var key = "".concat(size, "-").concat(txtColor, "-").concat(bgColor, "-").concat(text, "-").concat(iconUrl);
|
|
370
|
+
var cacheData = _clusterImageLoadedCache.Get(key);
|
|
371
|
+
// If available then return.
|
|
372
|
+
if (cacheData) {
|
|
373
|
+
return cacheData;
|
|
374
|
+
}
|
|
375
|
+
// If not available then queue for it to cook.
|
|
376
|
+
else {
|
|
377
|
+
_loadClusterImage(params);
|
|
378
|
+
}
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
var FORCE_UPDATE_BATCH_SIZE = 1000;
|
|
382
|
+
var FORCE_UPDATE_BATCH_DELAY = 100;
|
|
159
383
|
var PointClustering = /** @class */ (function () {
|
|
160
384
|
function PointClustering(register, menuItemId) {
|
|
161
385
|
var _this = this;
|
|
162
386
|
this.disposed = false;
|
|
163
387
|
this.registeredEntityIds = new Set();
|
|
388
|
+
// Queue to force update entities.
|
|
389
|
+
this.updateEntityQueue = [];
|
|
164
390
|
this.register = register;
|
|
165
391
|
this.viewer = register.Viewer;
|
|
166
392
|
this.menuItemId = menuItemId;
|
|
167
|
-
this.updateClusterSpacing(0);
|
|
168
393
|
var boundary = new Rectangle(0, 0, 360, 180);
|
|
169
|
-
this.quadTree = new Quad(boundary,
|
|
394
|
+
this.quadTree = new Quad(boundary, 30);
|
|
170
395
|
this.prevClusteredEntities = new Set();
|
|
171
396
|
this.currClusteredEntities = new Set();
|
|
172
397
|
this.clusterEntities = new Map();
|
|
@@ -175,6 +400,39 @@ var PointClustering = /** @class */ (function () {
|
|
|
175
400
|
}, 1000);
|
|
176
401
|
this.listenCamera();
|
|
177
402
|
}
|
|
403
|
+
PointClustering.prototype.queueForceUpdate = function (entityIds) {
|
|
404
|
+
for (var i = 0; i < entityIds.length; i++) {
|
|
405
|
+
if (this.updateEntityQueue.includes(entityIds[i])) {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
this.updateEntityQueue.push(entityIds[i]);
|
|
409
|
+
}
|
|
410
|
+
this.runForceUpdateQueue();
|
|
411
|
+
};
|
|
412
|
+
PointClustering.prototype.runForceUpdateQueue = function () {
|
|
413
|
+
var _this = this;
|
|
414
|
+
if (!this.updateEntityQueue.length) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (this.queueInterval) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
this.queueInterval = setInterval(function () {
|
|
421
|
+
if (_this.disposed) {
|
|
422
|
+
clearInterval(_this.queueInterval);
|
|
423
|
+
_this.queueInterval = null;
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
var ids = _this.updateEntityQueue.splice(0, FORCE_UPDATE_BATCH_SIZE);
|
|
427
|
+
_this.register.ForceUpdate({
|
|
428
|
+
entityIds: ids
|
|
429
|
+
});
|
|
430
|
+
if (!_this.updateEntityQueue.length) {
|
|
431
|
+
clearInterval(_this.queueInterval);
|
|
432
|
+
_this.queueInterval = null;
|
|
433
|
+
}
|
|
434
|
+
}, FORCE_UPDATE_BATCH_DELAY);
|
|
435
|
+
};
|
|
178
436
|
/**
|
|
179
437
|
* Starts listening to camera changes.
|
|
180
438
|
* This will trigger the clustering update whenever camera is moved.
|
|
@@ -229,6 +487,7 @@ var PointClustering = /** @class */ (function () {
|
|
|
229
487
|
this.clusterEntities.clear();
|
|
230
488
|
this.unlistenCamera();
|
|
231
489
|
this.disposed = true;
|
|
490
|
+
this.updateEntityQueue = [];
|
|
232
491
|
// Restore entities.
|
|
233
492
|
var toUpdateIds = [];
|
|
234
493
|
for (var _c = 0, _d = Array.from(this.registeredEntityIds); _c < _d.length; _c++) {
|
|
@@ -256,8 +515,8 @@ var PointClustering = /** @class */ (function () {
|
|
|
256
515
|
PointClustering.prototype.calculateCentroid = function (points) {
|
|
257
516
|
var lonSum = 0;
|
|
258
517
|
var latSum = 0;
|
|
259
|
-
for (var _i = 0,
|
|
260
|
-
var point =
|
|
518
|
+
for (var _i = 0, points_2 = points; _i < points_2.length; _i++) {
|
|
519
|
+
var point = points_2[_i];
|
|
261
520
|
lonSum += point.lon;
|
|
262
521
|
latSum += point.lat;
|
|
263
522
|
}
|
|
@@ -277,9 +536,10 @@ var PointClustering = /** @class */ (function () {
|
|
|
277
536
|
// 1: Update precision.
|
|
278
537
|
// This defines how far apart these clusters can be.
|
|
279
538
|
var cameraHeight = this.viewer.camera.positionCartographic.height;
|
|
280
|
-
this.
|
|
539
|
+
this.getClusterSpacing(cameraHeight);
|
|
281
540
|
// 2: Get clusters.
|
|
282
541
|
var _a = this.getClusters(), clusters = _a.clusters, noLongerClustered = _a.noLongerClustered;
|
|
542
|
+
var entitiesToUpdate = [];
|
|
283
543
|
// 3: Remove all cesium cluster entities that are no longer clustered.
|
|
284
544
|
for (var _i = 0, _b = Array.from(noLongerClustered); _i < _b.length; _i++) {
|
|
285
545
|
var id = _b[_i];
|
|
@@ -296,193 +556,180 @@ var PointClustering = /** @class */ (function () {
|
|
|
296
556
|
});
|
|
297
557
|
if (rego && rego.suppressShow) {
|
|
298
558
|
rego.suppressShow = false;
|
|
299
|
-
|
|
300
|
-
entityIds: [id],
|
|
301
|
-
});
|
|
559
|
+
entitiesToUpdate.push(id);
|
|
302
560
|
}
|
|
303
561
|
}
|
|
304
562
|
}
|
|
305
563
|
var getScale = function (count) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
scale =
|
|
310
|
-
|
|
564
|
+
// const MIN_SCALE = 15;
|
|
565
|
+
// const MAX_SCALE = 80;
|
|
566
|
+
// const SCALE_PER_POINT = 1.01;
|
|
567
|
+
// let scale = MIN_SCALE + (count * SCALE_PER_POINT);
|
|
568
|
+
// scale = Math.min(scale, MAX_SCALE);
|
|
569
|
+
// return scale;
|
|
570
|
+
if (count >= 10000) {
|
|
571
|
+
return 140;
|
|
572
|
+
}
|
|
573
|
+
if (count >= 1000) {
|
|
574
|
+
return 120;
|
|
575
|
+
}
|
|
576
|
+
if (count >= 100) {
|
|
577
|
+
return 90;
|
|
578
|
+
}
|
|
579
|
+
if (count >= 10) {
|
|
580
|
+
return 80;
|
|
581
|
+
}
|
|
582
|
+
return 70;
|
|
311
583
|
};
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
584
|
+
/**
|
|
585
|
+
* Generate circle with label in center.
|
|
586
|
+
* @param count
|
|
587
|
+
* @returns
|
|
588
|
+
*/
|
|
589
|
+
var getCanvas = function (count) {
|
|
590
|
+
var params = {
|
|
591
|
+
size: getScale(count),
|
|
592
|
+
txtColor: _this.pointColorTxt,
|
|
593
|
+
bgColor: _this.pointColorBg,
|
|
594
|
+
text: String(count),
|
|
595
|
+
iconUrl: _this.iconUrl
|
|
596
|
+
};
|
|
597
|
+
return getClusterImage(params);
|
|
325
598
|
};
|
|
326
|
-
|
|
327
|
-
for (var _e = 0, clusters_1 = clusters; _e < clusters_1.length; _e++) {
|
|
328
|
-
var cluster = clusters_1[_e];
|
|
599
|
+
var _loop_1 = function (cluster) {
|
|
329
600
|
var clusterId = cluster.center.id;
|
|
330
|
-
var clusterEntity =
|
|
331
|
-
var centroid =
|
|
601
|
+
var clusterEntity = this_1.clusterEntities.get(clusterId);
|
|
602
|
+
var centroid = this_1.calculateCentroid(cluster.points);
|
|
332
603
|
var count = cluster.points.length;
|
|
333
604
|
if (clusterEntity) {
|
|
334
605
|
clusterEntity.position = Cesium.Cartesian3.fromDegrees(centroid.lon, centroid.lat, 150);
|
|
335
|
-
clusterEntity.
|
|
336
|
-
|
|
337
|
-
|
|
606
|
+
clusterEntity.billboard.image = new Cesium.CallbackProperty(function () {
|
|
607
|
+
return getCanvas(count);
|
|
608
|
+
}, false),
|
|
609
|
+
clusterEntity.billboard.width = getScale(cluster.points.length);
|
|
338
610
|
clusterEntity.billboard.height = getScale(cluster.points.length);
|
|
339
611
|
}
|
|
340
612
|
else {
|
|
341
|
-
clusterEntity =
|
|
613
|
+
clusterEntity = this_1.viewer.entities.add({
|
|
342
614
|
position: Cesium.Cartesian3.fromDegrees(centroid.lon, centroid.lat, 150),
|
|
343
|
-
point: {
|
|
344
|
-
heightReference: Cesium.HeightReference.NONE,
|
|
345
|
-
pixelSize: getScale(count),
|
|
346
|
-
color: this.pointColorBg ? this.pointColorBg : Cesium.Color.fromCssColorString("#4287f5")
|
|
347
|
-
},
|
|
348
615
|
billboard: {
|
|
349
616
|
heightReference: Cesium.HeightReference.NONE,
|
|
350
|
-
image:
|
|
617
|
+
image: new Cesium.CallbackProperty(function () {
|
|
618
|
+
return getCanvas(count);
|
|
619
|
+
}, false),
|
|
351
620
|
width: getScale(count),
|
|
352
621
|
height: getScale(count),
|
|
353
622
|
verticalOrigin: Cesium.VerticalOrigin.CENTER,
|
|
354
623
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
|
355
|
-
disableDepthTestDistance: Number.POSITIVE_INFINITY
|
|
624
|
+
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
|
625
|
+
scaleByDistance: new Cesium.NearFarScalar(1.5e2, 0.5, 3.0e7, 0.1),
|
|
356
626
|
}
|
|
357
627
|
});
|
|
358
|
-
|
|
628
|
+
this_1.clusterEntities.set(clusterId, clusterEntity);
|
|
359
629
|
}
|
|
360
630
|
for (var i = 0; i < cluster.points.length; i++) {
|
|
361
631
|
var entityId = cluster.points[i].id;
|
|
362
|
-
var rego =
|
|
632
|
+
var rego = this_1.register.GetRego({
|
|
363
633
|
entityId: entityId,
|
|
364
|
-
menuItemId:
|
|
634
|
+
menuItemId: this_1.menuItemId
|
|
365
635
|
});
|
|
366
636
|
if (rego && !rego.suppressShow) {
|
|
367
637
|
rego.suppressShow = true;
|
|
368
|
-
|
|
369
|
-
entityIds: [entityId],
|
|
370
|
-
});
|
|
638
|
+
entitiesToUpdate.push(entityId);
|
|
371
639
|
}
|
|
372
640
|
}
|
|
641
|
+
};
|
|
642
|
+
var this_1 = this;
|
|
643
|
+
// 5: iterate over clusters and add/update them as Cesium entities.
|
|
644
|
+
for (var _e = 0, clusters_1 = clusters; _e < clusters_1.length; _e++) {
|
|
645
|
+
var cluster = clusters_1[_e];
|
|
646
|
+
_loop_1(cluster);
|
|
373
647
|
}
|
|
374
|
-
|
|
648
|
+
this.queueForceUpdate(entitiesToUpdate);
|
|
649
|
+
var _loop_2 = function (clusterId, clusterEntity) {
|
|
375
650
|
if (!clusters.find(function (x) { return x.center.id == clusterId; })) {
|
|
376
|
-
|
|
377
|
-
|
|
651
|
+
this_2.viewer.entities.remove(clusterEntity);
|
|
652
|
+
this_2.clusterEntities.delete(clusterId);
|
|
378
653
|
}
|
|
379
654
|
};
|
|
380
|
-
var
|
|
655
|
+
var this_2 = this;
|
|
381
656
|
// 6: Iterate over existing cluster entities and remove those that are no longer clustered.
|
|
382
657
|
for (var _f = 0, _g = Array.from(this.clusterEntities); _f < _g.length; _f++) {
|
|
383
658
|
var _h = _g[_f], clusterId = _h[0], clusterEntity = _h[1];
|
|
384
|
-
|
|
659
|
+
_loop_2(clusterId, clusterEntity);
|
|
385
660
|
}
|
|
386
661
|
};
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
*/
|
|
391
|
-
PointClustering.prototype.updateClusterSpacing = function (cameraHeight) {
|
|
392
|
-
// Camera height thresholds in meters.
|
|
393
|
-
var cameraHeightThresholds = [2000, 5000, 8000, 13000, 25000, 40000];
|
|
662
|
+
PointClustering.prototype.getClusterSpacing = function (distanceFromCluster) {
|
|
663
|
+
// Distance thresholds in meters from the cluster.
|
|
664
|
+
var distanceThresholds = [3000, 4000, 5300, 6000, 7000];
|
|
394
665
|
// Distance increments in degrees.
|
|
395
|
-
var distanceIncrements = [0.
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
666
|
+
var distanceIncrements = [0.001, 0.002, 0.01, 0.03, 0.09];
|
|
667
|
+
var index = 0;
|
|
668
|
+
if (distanceFromCluster) {
|
|
669
|
+
for (var i = 0; i < distanceThresholds.length; i++) {
|
|
670
|
+
if (distanceFromCluster > distanceThresholds[i]) {
|
|
671
|
+
index = i;
|
|
672
|
+
}
|
|
402
673
|
}
|
|
403
|
-
spacing += distanceIncrements[i];
|
|
404
674
|
}
|
|
405
|
-
|
|
675
|
+
return distanceIncrements[index];
|
|
406
676
|
};
|
|
407
|
-
/**
|
|
408
|
-
* Gathers clusters.
|
|
409
|
-
* @returns
|
|
410
|
-
*/
|
|
411
677
|
PointClustering.prototype.getClusters = function () {
|
|
412
678
|
var _this = this;
|
|
413
679
|
this.currClusteredEntities.clear();
|
|
414
680
|
var clusters = [];
|
|
415
681
|
var processedPoints = new Set();
|
|
416
682
|
var cameraPosition = this.viewer.camera.position;
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
//
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
683
|
+
var MIN_CAMERA_DISTANCE = 5000;
|
|
684
|
+
var processQuad = function (quad) {
|
|
685
|
+
// Distance to quad.
|
|
686
|
+
// TODO: Needs improvement.
|
|
687
|
+
var distanceToQuad = quad.GetDistanceToQuad(cameraPosition);
|
|
688
|
+
// Skip quads that are too close.
|
|
689
|
+
if (distanceToQuad >= MIN_CAMERA_DISTANCE) {
|
|
690
|
+
for (var _i = 0, _a = quad.points; _i < _a.length; _i++) {
|
|
691
|
+
var point = _a[_i];
|
|
692
|
+
// Skip points already processed in previous clusters.
|
|
693
|
+
if (processedPoints.has(point.id)) {
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
// Skip points closer than MIN_CAMERA_DISTANCE meters to the camera.
|
|
697
|
+
var cartesian3 = Cesium.Cartesian3.fromDegrees(point.lon, point.lat);
|
|
698
|
+
var distanceFromCluster = Cesium.Cartesian3.distance(cartesian3, cameraPosition);
|
|
699
|
+
if (distanceFromCluster <= MIN_CAMERA_DISTANCE) {
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
var found = [];
|
|
703
|
+
var nearbyPoints = quad.Query(new Circle(point.lon, point.lat, _this.getClusterSpacing(distanceFromCluster)), found);
|
|
704
|
+
if (nearbyPoints.length > 1) {
|
|
705
|
+
var cluster = { center: point, points: [] };
|
|
706
|
+
for (var _b = 0, nearbyPoints_1 = nearbyPoints; _b < nearbyPoints_1.length; _b++) {
|
|
707
|
+
var nearby = nearbyPoints_1[_b];
|
|
708
|
+
if (!cluster.points.includes(nearby)) {
|
|
709
|
+
cluster.points.push(nearby);
|
|
710
|
+
processedPoints.add(nearby.id);
|
|
711
|
+
_this.currClusteredEntities.add(nearby.id);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
clusters.push(cluster);
|
|
438
715
|
}
|
|
439
716
|
}
|
|
440
|
-
clusters.push(cluster);
|
|
441
717
|
}
|
|
442
|
-
|
|
718
|
+
if (quad.divided) {
|
|
719
|
+
processQuad(quad.northwest);
|
|
720
|
+
processQuad(quad.northeast);
|
|
721
|
+
processQuad(quad.southwest);
|
|
722
|
+
processQuad(quad.southeast);
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
processQuad(this.quadTree);
|
|
443
726
|
// Filter out clusters with only one point.
|
|
444
727
|
var validClusters = clusters.filter(function (cluster) { return cluster.points.length > 1; });
|
|
445
|
-
// Merge adjacent clusters.
|
|
446
|
-
var mergedClusters = this.mergeClusters(validClusters);
|
|
447
728
|
// Get the entity IDs that are no longer clustered
|
|
448
729
|
var noLongerClustered = new Set(Array.from(this.prevClusteredEntities).filter(function (id) { return !_this.currClusteredEntities.has(id); }));
|
|
449
730
|
// Update the previous clustered entities ref.
|
|
450
731
|
this.prevClusteredEntities = new Set(this.currClusteredEntities);
|
|
451
|
-
return { clusters:
|
|
452
|
-
};
|
|
453
|
-
/**
|
|
454
|
-
* Merges clusters that are nearby based on the distanceBetweenClusters value.
|
|
455
|
-
* @param clusters
|
|
456
|
-
* @returns
|
|
457
|
-
*/
|
|
458
|
-
PointClustering.prototype.mergeClusters = function (clusters) {
|
|
459
|
-
var _a;
|
|
460
|
-
var mergedClusters = [].concat(clusters);
|
|
461
|
-
// Keep looping while merges keep happening.
|
|
462
|
-
var mergeOccurred = true;
|
|
463
|
-
while (mergeOccurred) {
|
|
464
|
-
mergeOccurred = false;
|
|
465
|
-
for (var i = 0; i < mergedClusters.length - 1; i++) {
|
|
466
|
-
for (var j = i + 1; j < mergedClusters.length; j++) {
|
|
467
|
-
var cluster1 = mergedClusters[i];
|
|
468
|
-
var cluster2 = mergedClusters[j];
|
|
469
|
-
var centerDistance = this.calculateDistance(cluster1.center.lon, cluster1.center.lat, cluster2.center.lon, cluster2.center.lat);
|
|
470
|
-
var distanceThreshold = this.distanceBetweenClusters;
|
|
471
|
-
if (centerDistance <= distanceThreshold) {
|
|
472
|
-
// Merge clusters.
|
|
473
|
-
(_a = cluster1.points).push.apply(_a, cluster2.points);
|
|
474
|
-
mergedClusters.splice(j, 1);
|
|
475
|
-
this.removeClusterEntity(cluster2.center.id);
|
|
476
|
-
mergeOccurred = true;
|
|
477
|
-
break;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
if (mergeOccurred) {
|
|
481
|
-
break;
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
return mergedClusters;
|
|
732
|
+
return { clusters: validClusters, noLongerClustered: noLongerClustered };
|
|
486
733
|
};
|
|
487
734
|
/**
|
|
488
735
|
* Removes Cesium cluster entity.
|
|
@@ -510,24 +757,7 @@ var PointClustering = /** @class */ (function () {
|
|
|
510
757
|
PointClustering.prototype.addPoint = function (id, cartesian3) {
|
|
511
758
|
var point = this.convertCartesianToCartographic(cartesian3);
|
|
512
759
|
point.id = id;
|
|
513
|
-
this.quadTree.Insert(point);
|
|
514
|
-
};
|
|
515
|
-
/**
|
|
516
|
-
* Calculates rough distance across earth between two points.
|
|
517
|
-
* @param lon1
|
|
518
|
-
* @param lat1
|
|
519
|
-
* @param lon2
|
|
520
|
-
* @param lat2
|
|
521
|
-
* @returns
|
|
522
|
-
*/
|
|
523
|
-
PointClustering.prototype.calculateDistance = function (lon1, lat1, lon2, lat2) {
|
|
524
|
-
var lonDelta = Math.abs(lon1 - lon2);
|
|
525
|
-
var latDelta = Math.abs(lat1 - lat2);
|
|
526
|
-
// Approximate radius of the Earth in kilometers
|
|
527
|
-
var earthRadius = 6371;
|
|
528
|
-
var distance = 2 * Math.asin(Math.sqrt(Math.sin(latDelta / 2) * Math.sin(latDelta / 2) +
|
|
529
|
-
Math.cos(lat1) * Math.cos(lat2) * Math.sin(lonDelta / 2) * Math.sin(lonDelta / 2))) * earthRadius;
|
|
530
|
-
return distance;
|
|
760
|
+
return this.quadTree.Insert(point);
|
|
531
761
|
};
|
|
532
762
|
/**
|
|
533
763
|
* Adds entity to clustering logic.
|
|
@@ -551,14 +781,14 @@ var PointClustering = /** @class */ (function () {
|
|
|
551
781
|
return false;
|
|
552
782
|
}
|
|
553
783
|
if (!this.pointColorBg && entity.point) {
|
|
554
|
-
|
|
555
|
-
if (
|
|
784
|
+
var pointColorBg = GetValue(this.viewer, entity.point.color);
|
|
785
|
+
if (pointColorBg) {
|
|
556
786
|
var cColor = null;
|
|
557
|
-
if (
|
|
558
|
-
cColor = new Cesium.Color(
|
|
787
|
+
if (pointColorBg instanceof Object) {
|
|
788
|
+
cColor = new Cesium.Color(pointColorBg.red, pointColorBg.green, pointColorBg.blue, pointColorBg.alpha);
|
|
559
789
|
}
|
|
560
|
-
else if (typeof
|
|
561
|
-
cColor = Cesium.Color.fromCssColorString(
|
|
790
|
+
else if (typeof pointColorBg === "string") {
|
|
791
|
+
cColor = Cesium.Color.fromCssColorString(pointColorBg);
|
|
562
792
|
}
|
|
563
793
|
// Determine if text color should instead be black based on background.
|
|
564
794
|
// cColor contains r,g,b,a values where r,g,b are in the range [0,1].
|
|
@@ -570,11 +800,21 @@ var PointClustering = /** @class */ (function () {
|
|
|
570
800
|
else {
|
|
571
801
|
this.pointColorTxt = "white";
|
|
572
802
|
}
|
|
803
|
+
this.pointColorBg = cColor.toCssColorString();
|
|
573
804
|
}
|
|
574
805
|
}
|
|
575
806
|
}
|
|
807
|
+
if (!this.iconUrl && entity.billboard) {
|
|
808
|
+
var iconUrl = GetValue(this.viewer, entity.billboard.image);
|
|
809
|
+
if (typeof iconUrl == "string") {
|
|
810
|
+
this.iconUrl = iconUrl;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
var added = this.addPoint(id, pos3d);
|
|
814
|
+
if (!added) {
|
|
815
|
+
return false;
|
|
816
|
+
}
|
|
576
817
|
this.registeredEntityIds.add(id);
|
|
577
|
-
this.addPoint(id, pos3d);
|
|
578
818
|
this.updateQueue.Call();
|
|
579
819
|
return true;
|
|
580
820
|
};
|