bruce-cesium 7.3.8 โ 7.4.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 +2911 -626
- package/dist/bruce-cesium.es5.js.map +1 -1
- package/dist/bruce-cesium.umd.js +2910 -623
- package/dist/bruce-cesium.umd.js.map +1 -1
- package/dist/lib/bruce-cesium.js +8 -2
- package/dist/lib/bruce-cesium.js.map +1 -1
- package/dist/lib/rendering/entity-gatherer.js +249 -16
- package/dist/lib/rendering/entity-gatherer.js.map +1 -1
- package/dist/lib/rendering/render-managers/common/entity-label-group.js +482 -0
- package/dist/lib/rendering/render-managers/common/entity-label-group.js.map +1 -0
- package/dist/lib/rendering/render-managers/common/entity-label.js +1213 -155
- package/dist/lib/rendering/render-managers/common/entity-label.js.map +1 -1
- package/dist/lib/rendering/render-managers/common/historic-utils.js +64 -7
- package/dist/lib/rendering/render-managers/common/historic-utils.js.map +1 -1
- package/dist/lib/rendering/render-managers/common/point-clustering-core.js +85 -0
- package/dist/lib/rendering/render-managers/common/point-clustering-core.js.map +1 -0
- package/dist/lib/rendering/render-managers/common/point-clustering.js +566 -419
- package/dist/lib/rendering/render-managers/common/point-clustering.js.map +1 -1
- package/dist/lib/rendering/render-managers/entities/entities-ids-render-manager.js +1 -1
- package/dist/lib/rendering/render-managers/entities/entities-ids-render-manager.js.map +1 -1
- package/dist/lib/rendering/render-managers/entities/entities-render-manager.js +2 -2
- package/dist/lib/rendering/render-managers/entities/entities-render-manager.js.map +1 -1
- package/dist/lib/rendering/visuals-register.js +273 -24
- package/dist/lib/rendering/visuals-register.js.map +1 -1
- package/dist/types/bruce-cesium.d.ts +4 -1
- package/dist/types/rendering/entity-gatherer.d.ts +27 -4
- package/dist/types/rendering/render-managers/common/entity-label-group.d.ts +104 -0
- package/dist/types/rendering/render-managers/common/entity-label.d.ts +138 -1
- package/dist/types/rendering/render-managers/common/historic-utils.d.ts +12 -4
- package/dist/types/rendering/render-managers/common/point-clustering-core.d.ts +17 -0
- package/dist/types/rendering/render-managers/common/point-clustering.d.ts +37 -72
- package/dist/types/rendering/visuals-register.d.ts +43 -2
- package/package.json +1 -1
package/dist/bruce-cesium.es5.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Cesium from 'cesium';
|
|
2
|
-
import { Cartographic, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature,
|
|
3
|
-
import { Cartes, LRUCache, ProjectViewTile, ENVIRONMENT, Calculator,
|
|
2
|
+
import { Cartographic, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, Math as Math$1, Cartesian3, JulianDate, Quaternion, Transforms, HeadingPitchRoll, HeightReference, Matrix4, ShaderSource, Ellipsoid, Geometry, GeometryAttribute, ComponentDatatype, PrimitiveType, BoundingSphere, GeometryPipeline, Cartesian2, Cartesian4, sampleTerrain, Texture, PixelFormat, Sampler, TextureWrap, TextureMinificationFilter, TextureMagnificationFilter, VertexArray, BufferUsage, ShaderProgram, RenderState, BlendingState, DrawCommand, Pass, DistanceDisplayCondition, ColorBlendMode, ShadowMode, ClassificationType, Model, HorizontalOrigin, VerticalOrigin, ConstantPositionProperty, ArcType, CornerType, ImageMaterialProperty, SceneTransforms, PolygonHierarchy, PolylineGraphics, Rectangle, FeatureDetection, NearFarScalar, Matrix3, KmlDataSource, GeoJsonDataSource, SceneMode, Cesium3DTileStyle, HeadingPitchRange, Ion, IonResource, Cesium3DTileColorBlendMode, Cesium3DTileset, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, OrthographicFrustum, EasingFunction, Intersect, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, EllipsoidGeodesic, sampleTerrainMostDetailed, defined, PolygonPipeline, CustomDataSource, GeometryInstance, CesiumInspector, ClockRange, DynamicEnvironmentMapManager, ScreenSpaceEventHandler, ScreenSpaceEventType, Fullscreen, CzmlDataSource } from 'cesium';
|
|
3
|
+
import { Cartes, LRUCache, ProjectViewTile, ENVIRONMENT, Calculator, ClientFile, Entity as Entity$1, EntityLod, EntityTag, EntityType, ObjectUtils, Style, Api, Carto, Geometry as Geometry$1, ClientFileValueMap, Color as Color$1, Bounds, MenuItem, ProjectView, ProjectViewBookmark, Tileset, ZoomControl, BruceEvent, EntityCoords, DataLab, DelayQueue, AccountConcept, BruceApi, RecordChangeFeed, EntityRelation, EntitySource, ProgramKey, ProjectViewLegacyTile, Camera, Account, AccountLimits, Session, UrlUtils, EntityAttachment, EntityAttachmentType, EntityAttribute, MathUtils, ProjectViewBookmarkGroup, EntityRelationType, EntityHistoricData } from 'bruce-models';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Ensures a number is returned from a given value.
|
|
@@ -5295,6 +5295,86 @@ function InstallShowGate(target) {
|
|
|
5295
5295
|
return gate;
|
|
5296
5296
|
}
|
|
5297
5297
|
|
|
5298
|
+
/*
|
|
5299
|
+
* Status semantics are shared by label and scene-entity clusters; opaque image pixels are not inferred.
|
|
5300
|
+
* A bit jank but helps keep current 'important' labels higher than others.
|
|
5301
|
+
*/
|
|
5302
|
+
function GetClusterIconPriority(options) {
|
|
5303
|
+
if (Number.isFinite(options === null || options === void 0 ? void 0 : options.iconPriority)) {
|
|
5304
|
+
return options.iconPriority;
|
|
5305
|
+
}
|
|
5306
|
+
const statusPriority = (value) => {
|
|
5307
|
+
const tokens = value.toLowerCase().split(/[^a-z0-9]+/);
|
|
5308
|
+
if (tokens.some(token => ["critical", "fatal", "error", "fault", "failed", "alarm", "danger", "red"].includes(token))) {
|
|
5309
|
+
return 3;
|
|
5310
|
+
}
|
|
5311
|
+
if (tokens.some(token => ["warning", "warn", "degraded", "amber", "orange", "yellow"].includes(token))) {
|
|
5312
|
+
return 2;
|
|
5313
|
+
}
|
|
5314
|
+
if (tokens.some(token => ["unknown", "offline"].includes(token))) {
|
|
5315
|
+
return 1;
|
|
5316
|
+
}
|
|
5317
|
+
return 0;
|
|
5318
|
+
};
|
|
5319
|
+
if ((options === null || options === void 0 ? void 0 : options.iconStatus) != null) {
|
|
5320
|
+
return statusPriority(options.iconStatus);
|
|
5321
|
+
}
|
|
5322
|
+
const glyph = (options === null || options === void 0 ? void 0 : options.icon) || "";
|
|
5323
|
+
if (/[๐ด๐ฅโ๐จ]/u.test(glyph)) {
|
|
5324
|
+
return 3;
|
|
5325
|
+
}
|
|
5326
|
+
if (/[๐ ๐กโ ]/u.test(glyph)) {
|
|
5327
|
+
return 2;
|
|
5328
|
+
}
|
|
5329
|
+
let filename = "";
|
|
5330
|
+
if ((options === null || options === void 0 ? void 0 : options.iconUrl) && !/^(data|blob):/i.test(options.iconUrl)) {
|
|
5331
|
+
try {
|
|
5332
|
+
filename = decodeURIComponent(new URL(options.iconUrl, "https://label.invalid").pathname.split("/").pop());
|
|
5333
|
+
}
|
|
5334
|
+
catch {
|
|
5335
|
+
// Ignore malformed URLs when inferring status.
|
|
5336
|
+
}
|
|
5337
|
+
}
|
|
5338
|
+
return Math.max(statusPriority(glyph), statusPriority(filename));
|
|
5339
|
+
}
|
|
5340
|
+
/*
|
|
5341
|
+
* Groups eligible neighbours around stable representatives without coupling membership to its renderer.
|
|
5342
|
+
*/
|
|
5343
|
+
function ClusterItems(params) {
|
|
5344
|
+
const eligible = new Map();
|
|
5345
|
+
for (const item of params.items) {
|
|
5346
|
+
if (!params.eligible || params.eligible(item)) {
|
|
5347
|
+
eligible.set(params.id(item), item);
|
|
5348
|
+
}
|
|
5349
|
+
}
|
|
5350
|
+
const assigned = new Set();
|
|
5351
|
+
const result = [];
|
|
5352
|
+
const minimum = Math.max(2, params.minSize || 2);
|
|
5353
|
+
for (const [id, item] of eligible) {
|
|
5354
|
+
if (assigned.has(id)) {
|
|
5355
|
+
continue;
|
|
5356
|
+
}
|
|
5357
|
+
const members = new Map([[id, item]]);
|
|
5358
|
+
for (const neighbour of params.neighbours(item)) {
|
|
5359
|
+
const key = params.id(neighbour);
|
|
5360
|
+
if (eligible.has(key) && !assigned.has(key)) {
|
|
5361
|
+
members.set(key, eligible.get(key));
|
|
5362
|
+
}
|
|
5363
|
+
}
|
|
5364
|
+
if (members.size < minimum) {
|
|
5365
|
+
continue;
|
|
5366
|
+
}
|
|
5367
|
+
for (const key of members.keys()) {
|
|
5368
|
+
assigned.add(key);
|
|
5369
|
+
}
|
|
5370
|
+
result.push({
|
|
5371
|
+
representative: item,
|
|
5372
|
+
members: Array.from(members.values())
|
|
5373
|
+
});
|
|
5374
|
+
}
|
|
5375
|
+
return result;
|
|
5376
|
+
}
|
|
5377
|
+
|
|
5298
5378
|
/**
|
|
5299
5379
|
* Returns cesium property's value.
|
|
5300
5380
|
* This will check if it's one that changes over time, or just a fixed value.
|
|
@@ -5317,10 +5397,564 @@ function getValue(viewer, obj) {
|
|
|
5317
5397
|
*/
|
|
5318
5398
|
var EntityLabel;
|
|
5319
5399
|
(function (EntityLabel) {
|
|
5400
|
+
const ICON_BACKGROUND_BLEND = 0.8;
|
|
5401
|
+
const ICON_BACKGROUND_ALPHA = 0.45;
|
|
5402
|
+
const TEXT_MAX_WIDTH = 200;
|
|
5320
5403
|
// Key where the label instance is stored within the cesium entity.
|
|
5321
5404
|
// Eg: rego[ATTACH_KEY] = new EntityLabel(...);
|
|
5322
5405
|
EntityLabel.ATTACH_KEY = "nextspace-navigator-entity-label";
|
|
5406
|
+
/*
|
|
5407
|
+
* Indexes screen rectangles so sparse scenes do not compare every label with every other label.
|
|
5408
|
+
*/
|
|
5409
|
+
class ScreenIndex {
|
|
5410
|
+
constructor() {
|
|
5411
|
+
this.cells = new Map();
|
|
5412
|
+
this.large = [];
|
|
5413
|
+
this.all = [];
|
|
5414
|
+
}
|
|
5415
|
+
keys(frame, padding = 0) {
|
|
5416
|
+
const x0 = Math.floor((frame.x - padding) / 64);
|
|
5417
|
+
const y0 = Math.floor((frame.y - padding) / 64);
|
|
5418
|
+
const x1 = Math.floor((frame.x + frame.width + padding) / 64);
|
|
5419
|
+
const y1 = Math.floor((frame.y + frame.height + padding) / 64);
|
|
5420
|
+
if ((x1 - x0 + 1) * (y1 - y0 + 1) > 4096) {
|
|
5421
|
+
return null;
|
|
5422
|
+
}
|
|
5423
|
+
const keys = [];
|
|
5424
|
+
for (let x = x0; x <= x1; x++) {
|
|
5425
|
+
for (let y = y0; y <= y1; y++) {
|
|
5426
|
+
keys.push(`${x},${y}`);
|
|
5427
|
+
}
|
|
5428
|
+
}
|
|
5429
|
+
return keys;
|
|
5430
|
+
}
|
|
5431
|
+
Add(frame) {
|
|
5432
|
+
this.all.push(frame);
|
|
5433
|
+
const keys = this.keys(frame);
|
|
5434
|
+
if (!keys) {
|
|
5435
|
+
this.large.push(frame);
|
|
5436
|
+
return;
|
|
5437
|
+
}
|
|
5438
|
+
for (const key of keys) {
|
|
5439
|
+
if (!this.cells.has(key)) {
|
|
5440
|
+
this.cells.set(key, []);
|
|
5441
|
+
}
|
|
5442
|
+
this.cells.get(key).push(frame);
|
|
5443
|
+
}
|
|
5444
|
+
}
|
|
5445
|
+
Query(frame, padding = 0) {
|
|
5446
|
+
const keys = this.keys(frame, padding);
|
|
5447
|
+
const found = new Set(keys ? this.large : this.all);
|
|
5448
|
+
for (const key of keys || []) {
|
|
5449
|
+
for (const item of this.cells.get(key) || []) {
|
|
5450
|
+
found.add(item);
|
|
5451
|
+
}
|
|
5452
|
+
}
|
|
5453
|
+
return (Array.from(found).filter(other => frame.x <= other.x + other.width + padding && frame.x + frame.width + padding >= other.x &&
|
|
5454
|
+
frame.y <= other.y + other.height + padding && frame.y + frame.height + padding >= other.y));
|
|
5455
|
+
}
|
|
5456
|
+
HasOverlap(frame) {
|
|
5457
|
+
var _a;
|
|
5458
|
+
const intersects = (other) => (frame.x <= other.x + other.width && frame.x + frame.width >= other.x &&
|
|
5459
|
+
frame.y <= other.y + other.height && frame.y + frame.height >= other.y);
|
|
5460
|
+
const keys = this.keys(frame);
|
|
5461
|
+
if ((keys ? this.large : this.all).some(intersects)) {
|
|
5462
|
+
return true;
|
|
5463
|
+
}
|
|
5464
|
+
for (const key of keys || []) {
|
|
5465
|
+
if ((_a = this.cells.get(key)) === null || _a === void 0 ? void 0 : _a.some(intersects)) {
|
|
5466
|
+
return true;
|
|
5467
|
+
}
|
|
5468
|
+
}
|
|
5469
|
+
return false;
|
|
5470
|
+
}
|
|
5471
|
+
}
|
|
5472
|
+
/*
|
|
5473
|
+
* Includes the stem in collision checks without treating two crossing stems as text occlusion.
|
|
5474
|
+
*/
|
|
5475
|
+
function leaderBounds(frame) {
|
|
5476
|
+
if (!frame.hasLeader || !Number.isFinite(frame.anchorX) || !Number.isFinite(frame.anchorY)) {
|
|
5477
|
+
return undefined;
|
|
5478
|
+
}
|
|
5479
|
+
const endX = Math.max(frame.x, Math.min(frame.anchorX, frame.x + frame.width));
|
|
5480
|
+
const endY = Math.max(frame.y, Math.min(frame.anchorY, frame.y + frame.height));
|
|
5481
|
+
if (Math.hypot(frame.anchorX - endX, frame.anchorY - endY) < .01) {
|
|
5482
|
+
return undefined;
|
|
5483
|
+
}
|
|
5484
|
+
return {
|
|
5485
|
+
...frame,
|
|
5486
|
+
x: Math.min(frame.anchorX, endX) - 1,
|
|
5487
|
+
y: Math.min(frame.anchorY, endY) + .01,
|
|
5488
|
+
width: Math.abs(frame.anchorX - endX) + 2,
|
|
5489
|
+
height: Math.max(0, Math.abs(frame.anchorY - endY) - .02)
|
|
5490
|
+
};
|
|
5491
|
+
}
|
|
5492
|
+
/*
|
|
5493
|
+
* Remembers sides, never rendered positions: its own output cannot trigger another layout pass.
|
|
5494
|
+
*/
|
|
5495
|
+
class PlacementMemory {
|
|
5496
|
+
constructor() {
|
|
5497
|
+
this.previous = new Map();
|
|
5498
|
+
this.nextOrder = 0;
|
|
5499
|
+
}
|
|
5500
|
+
Resolve(frames) {
|
|
5501
|
+
var _a, _b;
|
|
5502
|
+
const live = new Set(frames.map(frame => frame.id));
|
|
5503
|
+
const moved = (frame) => {
|
|
5504
|
+
var _a;
|
|
5505
|
+
const old = (_a = this.previous.get(frame.id)) === null || _a === void 0 ? void 0 : _a.frame;
|
|
5506
|
+
return (!old ||
|
|
5507
|
+
old.transient !== frame.transient || old.canFlip !== frame.canFlip ||
|
|
5508
|
+
Math.hypot(frame.x - old.x, frame.y - old.y) >= 24 ||
|
|
5509
|
+
Math.abs(frame.width - old.width) >= 8 || Math.abs(frame.height - old.height) >= 8);
|
|
5510
|
+
};
|
|
5511
|
+
const persistent = frames.filter(frame => !frame.transient);
|
|
5512
|
+
const changes = (persistent.filter(moved).length +
|
|
5513
|
+
Array.from(this.previous.values()).filter(old => !old.frame.transient && !live.has(old.frame.id)).length);
|
|
5514
|
+
// A hover or one arrival cannot rearrange incumbents; a substantial batch/camera change can.
|
|
5515
|
+
const restructure = this.previous.size > 0 && changes >= Math.max(2, Math.ceil(persistent.length * 0.3));
|
|
5516
|
+
const fixed = (frame) => !restructure && !frame.transient && this.previous.has(frame.id) && !moved(frame);
|
|
5517
|
+
const ordered = ([...frames].sort((a, b) => {
|
|
5518
|
+
var _a, _b, _c, _d;
|
|
5519
|
+
return Number(fixed(b)) - Number(fixed(a)) ||
|
|
5520
|
+
Number(Boolean(a.transient)) - Number(Boolean(b.transient)) ||
|
|
5521
|
+
((_b = (_a = this.previous.get(a.id)) === null || _a === void 0 ? void 0 : _a.order) !== null && _b !== void 0 ? _b : Infinity) - ((_d = (_c = this.previous.get(b.id)) === null || _c === void 0 ? void 0 : _c.order) !== null && _d !== void 0 ? _d : Infinity) ||
|
|
5522
|
+
a.depth - b.depth || a.id.localeCompare(b.id);
|
|
5523
|
+
}));
|
|
5524
|
+
const text = new ScreenIndex(), leaders = new ScreenIndex();
|
|
5525
|
+
const placed = new Map();
|
|
5526
|
+
const next = new Map();
|
|
5527
|
+
const candidate = (frame, side) => {
|
|
5528
|
+
var _a;
|
|
5529
|
+
return (side === "left" && frame.canFlip ?
|
|
5530
|
+
{ ...frame, x: Math.max(0, Math.min(frame.anchorX - frame.width, ((_a = frame.viewportWidth) !== null && _a !== void 0 ? _a : Infinity) - frame.width)) } :
|
|
5531
|
+
frame);
|
|
5532
|
+
};
|
|
5533
|
+
const score = (frame, padding = 0) => {
|
|
5534
|
+
const box = {
|
|
5535
|
+
...frame,
|
|
5536
|
+
x: frame.x - padding + .01,
|
|
5537
|
+
y: frame.y - padding + .01,
|
|
5538
|
+
width: Math.max(0, frame.width + padding * 2 - .02),
|
|
5539
|
+
height: Math.max(0, frame.height + padding * 2 - .02)
|
|
5540
|
+
};
|
|
5541
|
+
const stem = leaderBounds(frame);
|
|
5542
|
+
return (Number(text.HasOverlap(box)) * 4 + Number(leaders.HasOverlap(box)) +
|
|
5543
|
+
Number(Boolean(stem && text.HasOverlap(stem))));
|
|
5544
|
+
};
|
|
5545
|
+
for (const frame of ordered) {
|
|
5546
|
+
const old = this.previous.get(frame.id);
|
|
5547
|
+
let side = frame.canFlip ? (_a = old === null || old === void 0 ? void 0 : old.side) !== null && _a !== void 0 ? _a : "right" : "right";
|
|
5548
|
+
let chosen = candidate(frame, side);
|
|
5549
|
+
if (!fixed(frame) && frame.canFlip) {
|
|
5550
|
+
const otherSide = side === "left" ? "right" : "left";
|
|
5551
|
+
const other = candidate(frame, otherSide);
|
|
5552
|
+
// An established side only changes when the alternative has a little breathing room.
|
|
5553
|
+
if (score(other, old ? 4 : 0) < score(chosen)) {
|
|
5554
|
+
side = otherSide;
|
|
5555
|
+
chosen = other;
|
|
5556
|
+
}
|
|
5557
|
+
}
|
|
5558
|
+
placed.set(frame.id, { frame: chosen, side });
|
|
5559
|
+
next.set(frame.id, { frame: fixed(frame) ? old.frame : { ...frame }, side, order: (_b = old === null || old === void 0 ? void 0 : old.order) !== null && _b !== void 0 ? _b : this.nextOrder++ });
|
|
5560
|
+
text.Add(chosen);
|
|
5561
|
+
const stem = leaderBounds(chosen);
|
|
5562
|
+
if (stem) {
|
|
5563
|
+
leaders.Add(stem);
|
|
5564
|
+
}
|
|
5565
|
+
}
|
|
5566
|
+
this.previous = next;
|
|
5567
|
+
return placed;
|
|
5568
|
+
}
|
|
5569
|
+
}
|
|
5570
|
+
EntityLabel.PlacementMemory = PlacementMemory;
|
|
5571
|
+
/*
|
|
5572
|
+
* Prefer declared semantics; only infers from recognisable glyphs and filename tokens, never image pixels.
|
|
5573
|
+
*/
|
|
5574
|
+
EntityLabel.IconPriority = GetClusterIconPriority;
|
|
5575
|
+
/*
|
|
5576
|
+
* Resolves clustering and stable text placement before depth fading; highlighted labels receive priority.
|
|
5577
|
+
*/
|
|
5578
|
+
function Layout(input, memory = new PlacementMemory()) {
|
|
5579
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
5580
|
+
const frames = input.filter(frame => [frame.x, frame.y, frame.width, frame.height, frame.depth].every(Number.isFinite))
|
|
5581
|
+
.sort((a, b) => (b.priority || 0) - (a.priority || 0) || a.depth - b.depth || a.id.localeCompare(b.id));
|
|
5582
|
+
const result = new Map();
|
|
5583
|
+
const entityKeys = new Set();
|
|
5584
|
+
const groups = new Map();
|
|
5585
|
+
for (const frame of frames) {
|
|
5586
|
+
const duplicate = frame.entityKey && entityKeys.has(frame.entityKey);
|
|
5587
|
+
result.set(frame.id, { frame, visible: !duplicate, opacity: frame.opacity, zIndex: 0, members: [], entityIds: frame.entityId ? [frame.entityId] : [] });
|
|
5588
|
+
if (duplicate) {
|
|
5589
|
+
continue;
|
|
5590
|
+
}
|
|
5591
|
+
if (frame.entityKey)
|
|
5592
|
+
entityKeys.add(frame.entityKey);
|
|
5593
|
+
if (!((_b = (_a = frame.options) === null || _a === void 0 ? void 0 : _a.cluster) === null || _b === void 0 ? void 0 : _b.enabled) || frame.priority > 0) {
|
|
5594
|
+
continue;
|
|
5595
|
+
}
|
|
5596
|
+
const key = JSON.stringify([frame.group || "", frame.options.presentation || "text"]);
|
|
5597
|
+
if (!groups.has(key)) {
|
|
5598
|
+
groups.set(key, []);
|
|
5599
|
+
}
|
|
5600
|
+
groups.get(key).push(frame);
|
|
5601
|
+
}
|
|
5602
|
+
for (const group of groups.values()) {
|
|
5603
|
+
// Summary and badge dimensions are output, never the next frame's membership input.
|
|
5604
|
+
const candidates = group.map(frame => frame.extendedGeometry && frame.canFlip &&
|
|
5605
|
+
frame.options.cluster.pixelRange == null ? { ...frame, x: frame.anchorX, y: frame.anchorY, width: 1, height: 1 } :
|
|
5606
|
+
frame.clusterBounds ? { ...frame, ...frame.clusterBounds } : frame);
|
|
5607
|
+
const index = new ScreenIndex();
|
|
5608
|
+
for (const frame of candidates) {
|
|
5609
|
+
index.Add(frame);
|
|
5610
|
+
}
|
|
5611
|
+
const clusters = ClusterItems({
|
|
5612
|
+
items: candidates,
|
|
5613
|
+
id: frame => frame.id,
|
|
5614
|
+
minSize: group[0].options.cluster.minSize,
|
|
5615
|
+
neighbours: frame => {
|
|
5616
|
+
var _a;
|
|
5617
|
+
return (index.Query(frame, (_a = frame.options.cluster.pixelRange) !== null && _a !== void 0 ? _a : (frame.options.presentation === "icon" ? 24 : 8)));
|
|
5618
|
+
}
|
|
5619
|
+
});
|
|
5620
|
+
for (const cluster of clusters) {
|
|
5621
|
+
let chosen = cluster.representative;
|
|
5622
|
+
if ((chosen.options.presentation || "text") !== "text" && chosen.options.cluster.representative !== "nearest") {
|
|
5623
|
+
let priority = EntityLabel.IconPriority(chosen.options);
|
|
5624
|
+
for (const member of cluster.members) {
|
|
5625
|
+
const candidate = EntityLabel.IconPriority(member.options);
|
|
5626
|
+
if (candidate > priority) {
|
|
5627
|
+
chosen = member;
|
|
5628
|
+
priority = candidate;
|
|
5629
|
+
}
|
|
5630
|
+
}
|
|
5631
|
+
}
|
|
5632
|
+
const representative = result.get(chosen.id);
|
|
5633
|
+
representative.members = cluster.members.map(frame => frame.id);
|
|
5634
|
+
representative.entityIds = Array.from(new Set(cluster.members.map(frame => frame.entityId).filter(Boolean)));
|
|
5635
|
+
if (chosen.options.presentation !== "icon") {
|
|
5636
|
+
const values = new Set();
|
|
5637
|
+
const stableMembers = [...cluster.members].sort((a, b) => (a.entityKey || a.entityId || a.id).localeCompare(b.entityKey || b.entityId || b.id));
|
|
5638
|
+
for (const member of stableMembers) {
|
|
5639
|
+
const value = ((_e = (_c = member.text) !== null && _c !== void 0 ? _c : (_d = member.options) === null || _d === void 0 ? void 0 : _d.text) !== null && _e !== void 0 ? _e : "").replace(/\s+/g, " ").trim();
|
|
5640
|
+
if (value) {
|
|
5641
|
+
values.add(value);
|
|
5642
|
+
}
|
|
5643
|
+
}
|
|
5644
|
+
if (values.size > 1) {
|
|
5645
|
+
const description = stableMembers.find(member => { var _a, _b; return member.textWrapped && ((_b = (member.text || ((_a = member.options) === null || _a === void 0 ? void 0 : _a.text))) === null || _b === void 0 ? void 0 : _b.trim()); });
|
|
5646
|
+
if (description) {
|
|
5647
|
+
representative.text = ((_f = description.text) !== null && _f !== void 0 ? _f : description.options.text).trim();
|
|
5648
|
+
representative.summaryMore = true;
|
|
5649
|
+
}
|
|
5650
|
+
else {
|
|
5651
|
+
const rows = Array.from(values).slice(0, 2);
|
|
5652
|
+
if (values.size > 2)
|
|
5653
|
+
rows.push(`+${values.size - 2} moreโฆ`);
|
|
5654
|
+
representative.text = rows.join("\n");
|
|
5655
|
+
}
|
|
5656
|
+
}
|
|
5657
|
+
}
|
|
5658
|
+
for (const frame of cluster.members) {
|
|
5659
|
+
if (frame !== chosen)
|
|
5660
|
+
result.get(frame.id).visible = false;
|
|
5661
|
+
}
|
|
5662
|
+
}
|
|
5663
|
+
}
|
|
5664
|
+
const placed = memory.Resolve(frames.filter(frame => result.get(frame.id).visible));
|
|
5665
|
+
const foreground = new ScreenIndex(), foregroundLeaders = new ScreenIndex();
|
|
5666
|
+
let zIndex = frames.length + 1;
|
|
5667
|
+
for (const original of frames) {
|
|
5668
|
+
const placement = result.get(original.id);
|
|
5669
|
+
if (!placement.visible) {
|
|
5670
|
+
continue;
|
|
5671
|
+
}
|
|
5672
|
+
const chosen = placed.get(original.id);
|
|
5673
|
+
const frame = placement.frame = chosen.frame;
|
|
5674
|
+
placement.textSide = chosen.side;
|
|
5675
|
+
const stem = leaderBounds(frame);
|
|
5676
|
+
const box = { ...frame, x: frame.x + .01, y: frame.y + .01, width: Math.max(0, frame.width - .02), height: Math.max(0, frame.height - .02) };
|
|
5677
|
+
const overlap = foreground.HasOverlap(box) || foregroundLeaders.HasOverlap(box) || Boolean(stem && foreground.HasOverlap(stem));
|
|
5678
|
+
if (overlap) {
|
|
5679
|
+
placement.opacity *= Math.max(0, Math.min(1, (_h = (_g = frame.options) === null || _g === void 0 ? void 0 : _g.overlapOpacity) !== null && _h !== void 0 ? _h : 0.25));
|
|
5680
|
+
}
|
|
5681
|
+
placement.zIndex = zIndex--;
|
|
5682
|
+
foreground.Add(frame);
|
|
5683
|
+
if (stem) {
|
|
5684
|
+
foregroundLeaders.Add(stem);
|
|
5685
|
+
}
|
|
5686
|
+
}
|
|
5687
|
+
return Array.from(result.values());
|
|
5688
|
+
}
|
|
5689
|
+
EntityLabel.Layout = Layout;
|
|
5690
|
+
const layers = new WeakMap();
|
|
5691
|
+
const containers = new WeakMap();
|
|
5692
|
+
/*
|
|
5693
|
+
* Moves the shared overlay without recreating labels or resolving their sources.
|
|
5694
|
+
*/
|
|
5695
|
+
function SetContainer(viewer, container) {
|
|
5696
|
+
const layer = layers.get(viewer);
|
|
5697
|
+
if (container && (container === (layer === null || layer === void 0 ? void 0 : layer.element) || (layer === null || layer === void 0 ? void 0 : layer.element.contains(container)))) {
|
|
5698
|
+
throw new Error("Label container cannot be inside the label overlay");
|
|
5699
|
+
}
|
|
5700
|
+
if (container) {
|
|
5701
|
+
containers.set(viewer, container);
|
|
5702
|
+
}
|
|
5703
|
+
else
|
|
5704
|
+
containers.delete(viewer);
|
|
5705
|
+
layer === null || layer === void 0 ? void 0 : layer.Move();
|
|
5706
|
+
}
|
|
5707
|
+
EntityLabel.SetContainer = SetContainer;
|
|
5708
|
+
/*
|
|
5709
|
+
* Owns one scene callback per viewer and separates projection/measurement from DOM writes.
|
|
5710
|
+
*/
|
|
5711
|
+
class Layer {
|
|
5712
|
+
constructor(viewer) {
|
|
5713
|
+
this.viewer = viewer;
|
|
5714
|
+
this.element = document.createElement("div");
|
|
5715
|
+
this.coordinates = { x: 0, y: 0, scaleX: 1, scaleY: 1 };
|
|
5716
|
+
this.participants = new Set();
|
|
5717
|
+
this.placementMemory = new PlacementMemory();
|
|
5718
|
+
this.forwardingWheel = false;
|
|
5719
|
+
this.pendingDrag = null;
|
|
5720
|
+
this.TrackPointer = (event) => {
|
|
5721
|
+
var _a, _b;
|
|
5722
|
+
if (event.pointerType && event.pointerType !== "mouse" && event.pointerType !== "pen")
|
|
5723
|
+
return;
|
|
5724
|
+
const rect = (_b = (_a = this.viewer.canvas).getBoundingClientRect) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
5725
|
+
if (!rect || event.clientX < rect.left || event.clientX >= rect.right || event.clientY < rect.top || event.clientY >= rect.bottom) {
|
|
5726
|
+
if (this.pointerClient)
|
|
5727
|
+
this.ClearPointer();
|
|
5728
|
+
return;
|
|
5729
|
+
}
|
|
5730
|
+
this.pointerClient = { x: event.clientX, y: event.clientY };
|
|
5731
|
+
this.viewer.scene.requestRender();
|
|
5732
|
+
};
|
|
5733
|
+
this.PointerOut = (event) => { if (!event.relatedTarget)
|
|
5734
|
+
this.ClearPointer(); };
|
|
5735
|
+
this.ClearPointer = () => {
|
|
5736
|
+
this.pointerClient = undefined;
|
|
5737
|
+
this.pointer = undefined;
|
|
5738
|
+
if (!this.viewer.isDestroyed()) {
|
|
5739
|
+
this.viewer.scene.requestRender();
|
|
5740
|
+
}
|
|
5741
|
+
};
|
|
5742
|
+
this.PreventNativeDrag = (event) => {
|
|
5743
|
+
event.preventDefault();
|
|
5744
|
+
};
|
|
5745
|
+
this.BeginDrag = (event) => {
|
|
5746
|
+
var _a, _b, _c;
|
|
5747
|
+
this.EndPendingDrag();
|
|
5748
|
+
const detection = FeatureDetection;
|
|
5749
|
+
const target = (_b = (_a = event.target) === null || _a === void 0 ? void 0 : _a.closest) === null || _b === void 0 ? void 0 : _b.call(_a, '.entity-label [role="button"]');
|
|
5750
|
+
const canvas = this.viewer.canvas;
|
|
5751
|
+
if (event.defaultPrevented || event.button !== 0 || event.pointerType !== "mouse" ||
|
|
5752
|
+
((_c = detection.supportsPointerEvents) === null || _c === void 0 ? void 0 : _c.call(detection)) === false || !canvas.setPointerCapture || this.viewer.isDestroyed() ||
|
|
5753
|
+
!target || !this.element.contains(target) ||
|
|
5754
|
+
this.element.ownerDocument.defaultView.getComputedStyle(target).visibility !== "visible") {
|
|
5755
|
+
return;
|
|
5756
|
+
}
|
|
5757
|
+
const bounds = canvas.getBoundingClientRect();
|
|
5758
|
+
if (event.clientX < bounds.left || event.clientX >= bounds.right || event.clientY < bounds.top || event.clientY >= bounds.bottom) {
|
|
5759
|
+
return;
|
|
5760
|
+
}
|
|
5761
|
+
this.pendingDrag = event;
|
|
5762
|
+
const doc = this.element.ownerDocument;
|
|
5763
|
+
doc.addEventListener("pointermove", this.ContinueDrag, true);
|
|
5764
|
+
doc.addEventListener("pointerup", this.FinishPendingDrag, true);
|
|
5765
|
+
doc.addEventListener("pointercancel", this.FinishPendingDrag, true);
|
|
5766
|
+
doc.defaultView.addEventListener("blur", this.EndPendingDrag);
|
|
5767
|
+
};
|
|
5768
|
+
this.FinishPendingDrag = (event) => {
|
|
5769
|
+
var _a;
|
|
5770
|
+
if (event.pointerId === ((_a = this.pendingDrag) === null || _a === void 0 ? void 0 : _a.pointerId)) {
|
|
5771
|
+
this.EndPendingDrag();
|
|
5772
|
+
}
|
|
5773
|
+
};
|
|
5774
|
+
this.EndPendingDrag = () => {
|
|
5775
|
+
this.pendingDrag = null;
|
|
5776
|
+
const doc = this.element.ownerDocument;
|
|
5777
|
+
doc.removeEventListener("pointermove", this.ContinueDrag, true);
|
|
5778
|
+
doc.removeEventListener("pointerup", this.FinishPendingDrag, true);
|
|
5779
|
+
doc.removeEventListener("pointercancel", this.FinishPendingDrag, true);
|
|
5780
|
+
doc.defaultView.removeEventListener("blur", this.EndPendingDrag);
|
|
5781
|
+
};
|
|
5782
|
+
this.ContinueDrag = (event) => {
|
|
5783
|
+
const start = this.pendingDrag;
|
|
5784
|
+
if (!start || event.pointerId !== start.pointerId) {
|
|
5785
|
+
return;
|
|
5786
|
+
}
|
|
5787
|
+
if (!(event.buttons & 1) || this.viewer.isDestroyed() || !this.element.contains(start.target)) {
|
|
5788
|
+
this.EndPendingDrag();
|
|
5789
|
+
return;
|
|
5790
|
+
}
|
|
5791
|
+
// Keep ordinary clicks local; only a deliberate drag starts Cesium's camera gesture.
|
|
5792
|
+
if (Math.hypot(event.clientX - start.clientX, event.clientY - start.clientY) < 6) {
|
|
5793
|
+
return;
|
|
5794
|
+
}
|
|
5795
|
+
this.EndPendingDrag();
|
|
5796
|
+
const canvas = this.viewer.canvas;
|
|
5797
|
+
try {
|
|
5798
|
+
canvas.setPointerCapture(start.pointerId);
|
|
5799
|
+
}
|
|
5800
|
+
catch {
|
|
5801
|
+
// A cancelled pointer or unsupported capture still cannot start native image dragging.
|
|
5802
|
+
return;
|
|
5803
|
+
}
|
|
5804
|
+
const win = this.element.ownerDocument.defaultView;
|
|
5805
|
+
const copy = (source) => ({
|
|
5806
|
+
bubbles: true, cancelable: true, pointerId: source.pointerId, pointerType: source.pointerType,
|
|
5807
|
+
isPrimary: source.isPrimary, button: source.button, buttons: source.buttons,
|
|
5808
|
+
clientX: source.clientX, clientY: source.clientY, screenX: source.screenX, screenY: source.screenY,
|
|
5809
|
+
ctrlKey: source.ctrlKey, shiftKey: source.shiftKey, altKey: source.altKey, metaKey: source.metaKey
|
|
5810
|
+
});
|
|
5811
|
+
// Capture routes subsequent real move/up events to Cesium, including releases outside the label.
|
|
5812
|
+
canvas.dispatchEvent(new win.PointerEvent("pointerdown", copy(start)));
|
|
5813
|
+
canvas.dispatchEvent(new win.PointerEvent("pointermove", copy(event)));
|
|
5814
|
+
event.preventDefault();
|
|
5815
|
+
event.stopPropagation();
|
|
5816
|
+
this.viewer.scene.requestRender();
|
|
5817
|
+
};
|
|
5818
|
+
this.ForwardWheel = (event) => {
|
|
5819
|
+
var _a, _b, _c;
|
|
5820
|
+
if (this.forwardingWheel || event.defaultPrevented || this.viewer.isDestroyed()) {
|
|
5821
|
+
return;
|
|
5822
|
+
}
|
|
5823
|
+
const target = (_b = (_a = event.target) === null || _a === void 0 ? void 0 : _a.closest) === null || _b === void 0 ? void 0 : _b.call(_a, '.entity-label [role="button"]');
|
|
5824
|
+
const canvas = this.viewer.canvas;
|
|
5825
|
+
const win = this.element.ownerDocument.defaultView;
|
|
5826
|
+
if (!target || !this.element.contains(target) || win.getComputedStyle(target).visibility !== "visible") {
|
|
5827
|
+
return;
|
|
5828
|
+
}
|
|
5829
|
+
const bounds = canvas.getBoundingClientRect();
|
|
5830
|
+
if (event.clientX < bounds.left || event.clientX >= bounds.right || event.clientY < bounds.top || event.clientY >= bounds.bottom) {
|
|
5831
|
+
return;
|
|
5832
|
+
}
|
|
5833
|
+
const position = {
|
|
5834
|
+
bubbles: true, cancelable: true, clientX: event.clientX, clientY: event.clientY,
|
|
5835
|
+
screenX: event.screenX, screenY: event.screenY, buttons: event.buttons,
|
|
5836
|
+
ctrlKey: event.ctrlKey, shiftKey: event.shiftKey, altKey: event.altKey, metaKey: event.metaKey,
|
|
5837
|
+
pointerId: 1, pointerType: "mouse", isPrimary: true
|
|
5838
|
+
};
|
|
5839
|
+
this.forwardingWheel = true;
|
|
5840
|
+
try {
|
|
5841
|
+
// Cesium's wheel zoom uses its last mouse position, not the wheel event's coordinates.
|
|
5842
|
+
const detection = FeatureDetection;
|
|
5843
|
+
// Older Cesium versions opt Firefox out even when the browser exposes PointerEvent.
|
|
5844
|
+
canvas.dispatchEvent(((_c = detection.supportsPointerEvents) === null || _c === void 0 ? void 0 : _c.call(detection)) !== false && typeof win.PointerEvent === "function" ?
|
|
5845
|
+
new win.PointerEvent("pointermove", position) : new win.MouseEvent("mousemove", position));
|
|
5846
|
+
canvas.dispatchEvent(new win.WheelEvent("wheel", {
|
|
5847
|
+
...position, deltaX: event.deltaX, deltaY: event.deltaY, deltaZ: event.deltaZ, deltaMode: event.deltaMode
|
|
5848
|
+
}));
|
|
5849
|
+
this.viewer.scene.requestRender();
|
|
5850
|
+
event.preventDefault();
|
|
5851
|
+
event.stopPropagation();
|
|
5852
|
+
}
|
|
5853
|
+
finally {
|
|
5854
|
+
this.forwardingWheel = false;
|
|
5855
|
+
}
|
|
5856
|
+
};
|
|
5857
|
+
this.element.className = "entity-label-layer";
|
|
5858
|
+
// Depth ordering belongs inside the map overlay, never in the application's UI stack.
|
|
5859
|
+
this.element.style.cssText = "position:absolute;inset:0;z-index:0;isolation:isolate;pointer-events:none;";
|
|
5860
|
+
this.Move();
|
|
5861
|
+
this.element.addEventListener("wheel", this.ForwardWheel, { passive: false });
|
|
5862
|
+
this.element.addEventListener("pointerdown", this.BeginDrag, true);
|
|
5863
|
+
this.element.addEventListener("dragstart", this.PreventNativeDrag);
|
|
5864
|
+
this.element.ownerDocument.addEventListener("pointermove", this.TrackPointer, true);
|
|
5865
|
+
this.element.ownerDocument.addEventListener("pointerout", this.PointerOut, true);
|
|
5866
|
+
this.element.ownerDocument.defaultView.addEventListener("blur", this.ClearPointer);
|
|
5867
|
+
this.remove = viewer.scene.postUpdate.addEventListener(() => this.Update());
|
|
5868
|
+
}
|
|
5869
|
+
Move() {
|
|
5870
|
+
(containers.get(this.viewer) || this.viewer.container).appendChild(this.element);
|
|
5871
|
+
this.Update();
|
|
5872
|
+
this.viewer.scene.requestRender();
|
|
5873
|
+
}
|
|
5874
|
+
MeasureCoordinates() {
|
|
5875
|
+
var _a;
|
|
5876
|
+
const canvas = this.viewer.canvas;
|
|
5877
|
+
const canvasRect = (_a = canvas.getBoundingClientRect) === null || _a === void 0 ? void 0 : _a.call(canvas);
|
|
5878
|
+
const rect = this.element.getBoundingClientRect();
|
|
5879
|
+
const scaleX = rect.width / this.element.offsetWidth || 1;
|
|
5880
|
+
const scaleY = rect.height / this.element.offsetHeight || 1;
|
|
5881
|
+
return {
|
|
5882
|
+
x: (((canvasRect === null || canvasRect === void 0 ? void 0 : canvasRect.left) || 0) - rect.left) / scaleX,
|
|
5883
|
+
y: (((canvasRect === null || canvasRect === void 0 ? void 0 : canvasRect.top) || 0) - rect.top) / scaleY,
|
|
5884
|
+
scaleX: ((canvasRect === null || canvasRect === void 0 ? void 0 : canvasRect.width) / canvas.clientWidth || 1) / scaleX,
|
|
5885
|
+
scaleY: ((canvasRect === null || canvasRect === void 0 ? void 0 : canvasRect.height) / canvas.clientHeight || 1) / scaleY
|
|
5886
|
+
};
|
|
5887
|
+
}
|
|
5888
|
+
Add(participant) {
|
|
5889
|
+
this.participants.add(participant);
|
|
5890
|
+
this.viewer.scene.requestRender();
|
|
5891
|
+
return () => {
|
|
5892
|
+
this.participants.delete(participant);
|
|
5893
|
+
if (!this.participants.size) {
|
|
5894
|
+
this.remove();
|
|
5895
|
+
this.element.removeEventListener("wheel", this.ForwardWheel);
|
|
5896
|
+
this.element.removeEventListener("pointerdown", this.BeginDrag, true);
|
|
5897
|
+
this.element.removeEventListener("dragstart", this.PreventNativeDrag);
|
|
5898
|
+
this.element.ownerDocument.removeEventListener("pointermove", this.TrackPointer, true);
|
|
5899
|
+
this.element.ownerDocument.removeEventListener("pointerout", this.PointerOut, true);
|
|
5900
|
+
this.element.ownerDocument.defaultView.removeEventListener("blur", this.ClearPointer);
|
|
5901
|
+
this.EndPendingDrag();
|
|
5902
|
+
this.element.remove();
|
|
5903
|
+
layers.delete(this.viewer);
|
|
5904
|
+
}
|
|
5905
|
+
};
|
|
5906
|
+
}
|
|
5907
|
+
Update() {
|
|
5908
|
+
var _a, _b;
|
|
5909
|
+
this.coordinates = this.MeasureCoordinates();
|
|
5910
|
+
const rect = (_b = (_a = this.viewer.canvas).getBoundingClientRect) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
5911
|
+
const pointer = this.pointerClient;
|
|
5912
|
+
this.pointer = pointer && (rect === null || rect === void 0 ? void 0 : rect.width) && (rect === null || rect === void 0 ? void 0 : rect.height) && pointer.x >= rect.left && pointer.x < rect.right && pointer.y >= rect.top && pointer.y < rect.bottom ?
|
|
5913
|
+
{
|
|
5914
|
+
x: (pointer.x - rect.left) * this.viewer.canvas.clientWidth / rect.width,
|
|
5915
|
+
y: (pointer.y - rect.top) * this.viewer.canvas.clientHeight / rect.height
|
|
5916
|
+
} : undefined;
|
|
5917
|
+
const projected = new Map();
|
|
5918
|
+
for (const participant of this.participants) {
|
|
5919
|
+
projected.set(participant, participant.project());
|
|
5920
|
+
}
|
|
5921
|
+
const placements = new Map(Layout(Array.from(projected.values()).filter(Boolean), this.placementMemory).map(item => [item.frame.id, item]));
|
|
5922
|
+
for (const [participant, frame] of projected) {
|
|
5923
|
+
participant.apply(frame ? placements.get(frame.id) : undefined);
|
|
5924
|
+
}
|
|
5925
|
+
}
|
|
5926
|
+
}
|
|
5927
|
+
let nextLabelId = 0;
|
|
5323
5928
|
class Label {
|
|
5929
|
+
get HasText() { return this.options.presentation !== "icon"; }
|
|
5930
|
+
SetHoverVisible(visible) {
|
|
5931
|
+
var _a, _b, _c;
|
|
5932
|
+
if (!this.Transient || this.Persistent) {
|
|
5933
|
+
return;
|
|
5934
|
+
}
|
|
5935
|
+
if (!visible) {
|
|
5936
|
+
this.retainedHoverPosition = (_a = this.visualRegister) === null || _a === void 0 ? void 0 : _a.GetLabelHoverPosition((_b = this.rego) === null || _b === void 0 ? void 0 : _b.entityId, (_c = this.rego) === null || _c === void 0 ? void 0 : _c.menuItemId);
|
|
5937
|
+
// Retiring labels must not change the ring now owned by the next hover target.
|
|
5938
|
+
if (this.ringAnimation) {
|
|
5939
|
+
this.ringAnimation = { ...this.ringAnimation };
|
|
5940
|
+
}
|
|
5941
|
+
}
|
|
5942
|
+
if (this.hoverFade.to === Number(visible)) {
|
|
5943
|
+
return;
|
|
5944
|
+
}
|
|
5945
|
+
this.hoverFade = { from: this.HoverOpacity(), to: Number(visible), start: Date.now() };
|
|
5946
|
+
this.viewer.scene.requestRender();
|
|
5947
|
+
}
|
|
5948
|
+
HoverOpacity() {
|
|
5949
|
+
if (!this.Transient || this.Persistent) {
|
|
5950
|
+
return 1;
|
|
5951
|
+
}
|
|
5952
|
+
const progress = Math.min(1, Math.max(0, (Date.now() - this.hoverFade.start) / 120));
|
|
5953
|
+
if (progress < 1) {
|
|
5954
|
+
this.viewer.scene.requestRender();
|
|
5955
|
+
}
|
|
5956
|
+
return this.hoverFade.from + (this.hoverFade.to - this.hoverFade.from) * progress * progress * (3 - 2 * progress);
|
|
5957
|
+
}
|
|
5324
5958
|
get Rego() {
|
|
5325
5959
|
return this.rego;
|
|
5326
5960
|
}
|
|
@@ -5333,10 +5967,18 @@ var EntityLabel;
|
|
|
5333
5967
|
set pos3d(val) {
|
|
5334
5968
|
this._pos3d = val;
|
|
5335
5969
|
this.prePositioned = !isNaN(val === null || val === void 0 ? void 0 : val.x);
|
|
5336
|
-
this.
|
|
5970
|
+
if (this.label && this.prePositioned)
|
|
5971
|
+
this.viewer.scene.requestRender();
|
|
5972
|
+
else
|
|
5973
|
+
this.createLabel(val);
|
|
5337
5974
|
}
|
|
5338
5975
|
constructor(params) {
|
|
5339
|
-
var _a;
|
|
5976
|
+
var _a, _b, _c, _d;
|
|
5977
|
+
this.id = `label-${++nextLabelId}`;
|
|
5978
|
+
this.Persistent = false;
|
|
5979
|
+
this.Transient = false;
|
|
5980
|
+
this.hoverFade = { from: 0, to: 1, start: Date.now() };
|
|
5981
|
+
this.cursorMotion = { amount: 0, from: 0, target: 0, start: 0, position: undefined };
|
|
5340
5982
|
// Counter to help differentiate between createLabel calls.
|
|
5341
5983
|
this.counter = 0;
|
|
5342
5984
|
// The label element itself.
|
|
@@ -5356,15 +5998,24 @@ var EntityLabel;
|
|
|
5356
5998
|
this.loadTimeout = null;
|
|
5357
5999
|
this.loadedName = false;
|
|
5358
6000
|
this.line = null;
|
|
6001
|
+
this.anchor = null;
|
|
5359
6002
|
// Null = not attempted to load.
|
|
5360
6003
|
this.additionalTexts = null;
|
|
5361
6004
|
this.rego = params.rego;
|
|
5362
6005
|
this.viewer = params.viewer;
|
|
5363
6006
|
this.api = params.api;
|
|
5364
6007
|
this.visualRegister = params.visualRegister;
|
|
5365
|
-
this.
|
|
6008
|
+
this.options = params.options || {};
|
|
6009
|
+
this.groupId = params.groupId;
|
|
6010
|
+
this.Persistent = (_a = params.persistent) !== null && _a !== void 0 ? _a : Boolean(params.groupId);
|
|
6011
|
+
this.Transient = (_b = params.transient) !== null && _b !== void 0 ? _b : false;
|
|
6012
|
+
this.hoverRingState = params.hoverRingState;
|
|
6013
|
+
this.visible = params.visible;
|
|
6014
|
+
if (((_c = params.options) === null || _c === void 0 ? void 0 : _c.text) != null)
|
|
6015
|
+
this.additionalTexts = [];
|
|
6016
|
+
this.prePositioned = !isNaN((_d = params.pos3d) === null || _d === void 0 ? void 0 : _d.x);
|
|
5366
6017
|
this.createLabel(params.pos3d);
|
|
5367
|
-
if (!params.api && !ENVIRONMENT.IS_SELF_MANAGED) {
|
|
6018
|
+
if (!params.api && !ENVIRONMENT.IS_SELF_MANAGED && this.options.text == null) {
|
|
5368
6019
|
this.api = ENVIRONMENT.Api().GetBruceApi();
|
|
5369
6020
|
}
|
|
5370
6021
|
}
|
|
@@ -5414,12 +6065,24 @@ var EntityLabel;
|
|
|
5414
6065
|
clearTimeout(this.loadTimeout);
|
|
5415
6066
|
this.loadTimeout = null;
|
|
5416
6067
|
}
|
|
6068
|
+
/*
|
|
6069
|
+
* Rebuilds only when configuration changes; state refreshes must not recreate DOM every frame.
|
|
6070
|
+
*/
|
|
6071
|
+
Configure(options, groupId) {
|
|
6072
|
+
if (this.groupId === groupId && JSON.stringify(this.options) === JSON.stringify(options || {})) {
|
|
6073
|
+
return;
|
|
6074
|
+
}
|
|
6075
|
+
this.options = options || {};
|
|
6076
|
+
this.groupId = groupId;
|
|
6077
|
+
this.additionalTexts = this.options.text != null ? [] : null;
|
|
6078
|
+
this.createLabel();
|
|
6079
|
+
}
|
|
5417
6080
|
/**
|
|
5418
6081
|
* Creates cesium entity for the label if one does not exist.
|
|
5419
6082
|
* @param starterPos3d optional position to create label at. Ignored for billboard labels.
|
|
5420
6083
|
*/
|
|
5421
6084
|
async createLabel(starterPos3d) {
|
|
5422
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
6085
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
5423
6086
|
this.removeLabel();
|
|
5424
6087
|
let pos3d = starterPos3d ? starterPos3d.clone() : null;
|
|
5425
6088
|
const counter = ++this.counter;
|
|
@@ -5628,7 +6291,7 @@ var EntityLabel;
|
|
|
5628
6291
|
}
|
|
5629
6292
|
clearTimeout(this.loadTimeout);
|
|
5630
6293
|
this.loadTimeout = setTimeout(async () => {
|
|
5631
|
-
var _a, _b, _c, _d;
|
|
6294
|
+
var _a, _b, _c, _d, _e;
|
|
5632
6295
|
if (this.disposed || this.counter != counter) {
|
|
5633
6296
|
return;
|
|
5634
6297
|
}
|
|
@@ -5636,10 +6299,10 @@ var EntityLabel;
|
|
|
5636
6299
|
if (this.additionalTexts == null) {
|
|
5637
6300
|
const additionalTexts = [];
|
|
5638
6301
|
// Rego might not have a known or default Entity Type. Eg: OSM building.
|
|
5639
|
-
if (this.rego.entityTypeId) {
|
|
6302
|
+
if (this.rego.entityTypeId || this.options.attributes) {
|
|
5640
6303
|
let entityType = null;
|
|
5641
6304
|
try {
|
|
5642
|
-
const data = await EntityType.Get({
|
|
6305
|
+
const data = this.options.attributes ? null : await EntityType.Get({
|
|
5643
6306
|
api: this.api,
|
|
5644
6307
|
entityTypeId: this.rego.entityTypeId
|
|
5645
6308
|
});
|
|
@@ -5654,14 +6317,14 @@ var EntityLabel;
|
|
|
5654
6317
|
}
|
|
5655
6318
|
// Type could be deleted, however we'll just catch that to show a label instead of nothing.
|
|
5656
6319
|
// This is not unusual for Tilesets where the Type that is baked might be outdated.
|
|
5657
|
-
if (entityType) {
|
|
5658
|
-
const attributes = (
|
|
6320
|
+
if (entityType || this.options.attributes) {
|
|
6321
|
+
const attributes = (_a = this.options.attributes) !== null && _a !== void 0 ? _a : (_c = (_b = entityType.VisualSettings) === null || _b === void 0 ? void 0 : _b.label) === null || _c === void 0 ? void 0 : _c.attributes;
|
|
5659
6322
|
if (attributes) {
|
|
5660
6323
|
try {
|
|
5661
6324
|
const { entity } = await Entity$1.Get({
|
|
5662
6325
|
api: this.api,
|
|
5663
6326
|
entityId: this.rego.entityId,
|
|
5664
|
-
scenario: ((
|
|
6327
|
+
scenario: ((_d = this.rego.outline) === null || _d === void 0 ? void 0 : _d.length) ? (_e = this.rego.outline.find(x => !!x.Scenario)) === null || _e === void 0 ? void 0 : _e.Scenario : null,
|
|
5665
6328
|
schemaId: this.rego.schema,
|
|
5666
6329
|
migrated: true
|
|
5667
6330
|
});
|
|
@@ -5670,7 +6333,7 @@ var EntityLabel;
|
|
|
5670
6333
|
entity: entity,
|
|
5671
6334
|
path: attributes[i]
|
|
5672
6335
|
});
|
|
5673
|
-
if (value) {
|
|
6336
|
+
if (value != null) {
|
|
5674
6337
|
additionalTexts.push(value);
|
|
5675
6338
|
}
|
|
5676
6339
|
}
|
|
@@ -5699,12 +6362,166 @@ var EntityLabel;
|
|
|
5699
6362
|
if (this.disposed || this.counter != counter) {
|
|
5700
6363
|
return;
|
|
5701
6364
|
}
|
|
5702
|
-
const
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
6365
|
+
const presentation = this.options.presentation || "text";
|
|
6366
|
+
let clickEntityIds = [];
|
|
6367
|
+
const selectOnClick = (_j = this.options.selectOnClick) !== null && _j !== void 0 ? _j : Boolean(this.groupId);
|
|
6368
|
+
const select = (event) => {
|
|
6369
|
+
event.preventDefault();
|
|
6370
|
+
event.stopPropagation();
|
|
6371
|
+
if (!selectOnClick || this.disposed || this.counter !== counter || !clickEntityIds.length || !this.visualRegister) {
|
|
6372
|
+
return;
|
|
6373
|
+
}
|
|
6374
|
+
this.visualRegister.ClearSelected({ requestRender: false });
|
|
6375
|
+
this.visualRegister.SetSelected({ entityIds: clickEntityIds, selected: true });
|
|
6376
|
+
};
|
|
6377
|
+
const makeClickable = (target) => {
|
|
6378
|
+
if (!selectOnClick || !this.visualRegister) {
|
|
6379
|
+
return;
|
|
6380
|
+
}
|
|
6381
|
+
target.style.pointerEvents = "auto";
|
|
6382
|
+
target.style.cursor = "pointer";
|
|
6383
|
+
target.style.userSelect = "none";
|
|
6384
|
+
target.setAttribute("role", "button");
|
|
6385
|
+
target.tabIndex = 0;
|
|
6386
|
+
target.addEventListener("click", select);
|
|
6387
|
+
target.addEventListener("keydown", event => {
|
|
6388
|
+
if (event.key === "Enter" || event.key === " ")
|
|
6389
|
+
select(event);
|
|
6390
|
+
});
|
|
6391
|
+
// Prevent viewer picking and camera gestures from interpreting label interactions.
|
|
6392
|
+
for (const type of ["pointerdown", "pointerup", "mousedown", "mouseup", "dblclick"]) {
|
|
6393
|
+
target.addEventListener(type, event => event.stopPropagation());
|
|
6394
|
+
}
|
|
6395
|
+
};
|
|
6396
|
+
const normalLeader = (_k = this.options.leader) !== null && _k !== void 0 ? _k : presentation === "text";
|
|
6397
|
+
let leader = normalLeader;
|
|
6398
|
+
const content = document.createElement("span");
|
|
6399
|
+
const originalText = (_l = this.options.text) !== null && _l !== void 0 ? _l : [text, ...additionalTexts].join("\n");
|
|
6400
|
+
let textElement;
|
|
6401
|
+
let sourceText;
|
|
6402
|
+
let sourceNaturalText;
|
|
6403
|
+
let iconElement;
|
|
6404
|
+
let iconContainer;
|
|
6405
|
+
content.className = "entity-label-content";
|
|
6406
|
+
content.style.cssText = "display:flex;align-items:center;gap:6px;";
|
|
6407
|
+
if (presentation === "icon-text-above") {
|
|
6408
|
+
content.style.flexDirection = "column-reverse";
|
|
6409
|
+
}
|
|
6410
|
+
if (presentation !== "text") {
|
|
6411
|
+
const icon = document.createElement("span");
|
|
6412
|
+
iconElement = icon;
|
|
6413
|
+
icon.className = "entity-label-icon";
|
|
6414
|
+
icon.style.cssText = `font-size:${(this.options.iconSize || 24) * ((_m = this.options.iconScale) !== null && _m !== void 0 ? _m : 1)}px;line-height:1;`;
|
|
6415
|
+
icon.style.textShadow = "0 1px 2px rgba(0, 0, 0, 0.25)";
|
|
6416
|
+
const iconColor = typeof this.options.iconColor === "string" ? this.options.iconColor : undefined;
|
|
6417
|
+
const iconBackground = this.options.iconBackground && this.options.iconScale !== 0;
|
|
6418
|
+
if (iconColor) {
|
|
6419
|
+
icon.style.color = iconColor;
|
|
6420
|
+
}
|
|
6421
|
+
if (this.options.iconUrl) {
|
|
6422
|
+
const image = document.createElement("img");
|
|
6423
|
+
image.draggable = false;
|
|
6424
|
+
image.alt = this.options.icon || "";
|
|
6425
|
+
const scale = (_o = this.options.iconScale) !== null && _o !== void 0 ? _o : 1;
|
|
6426
|
+
const size = this.options.iconSize;
|
|
6427
|
+
const mask = iconColor && iconBackground ? document.createElement("span") : icon;
|
|
6428
|
+
if (mask !== icon) {
|
|
6429
|
+
icon.appendChild(mask);
|
|
6430
|
+
}
|
|
6431
|
+
const target = iconColor ? mask : image;
|
|
6432
|
+
if (iconColor) {
|
|
6433
|
+
mask.style.display = "inline-block";
|
|
6434
|
+
mask.style.backgroundColor = iconColor;
|
|
6435
|
+
for (const prefix of ["", "-webkit-"]) {
|
|
6436
|
+
mask.style.setProperty(`${prefix}mask-image`, `url(${JSON.stringify(this.options.iconUrl)})`);
|
|
6437
|
+
mask.style.setProperty(`${prefix}mask-size`, "contain");
|
|
6438
|
+
mask.style.setProperty(`${prefix}mask-repeat`, "no-repeat");
|
|
6439
|
+
mask.style.setProperty(`${prefix}mask-position`, "center");
|
|
6440
|
+
}
|
|
6441
|
+
}
|
|
6442
|
+
const resize = () => {
|
|
6443
|
+
if (this.disposed || this.counter !== counter) {
|
|
6444
|
+
return;
|
|
6445
|
+
}
|
|
6446
|
+
const intrinsic = this.options.iconScale != null && size == null;
|
|
6447
|
+
target.style.width = `${(intrinsic ? image.naturalWidth || 24 : size || 24) * scale}px`;
|
|
6448
|
+
target.style.height = `${(intrinsic ? image.naturalHeight || 24 : size || 24) * scale}px`;
|
|
6449
|
+
this.viewer.scene.requestRender();
|
|
6450
|
+
};
|
|
6451
|
+
image.onload = resize;
|
|
6452
|
+
resize();
|
|
6453
|
+
image.src = this.options.iconUrl;
|
|
6454
|
+
if (!iconColor) {
|
|
6455
|
+
icon.appendChild(image);
|
|
6456
|
+
}
|
|
6457
|
+
}
|
|
6458
|
+
else if (typeof this.options.iconClass === "string" && this.options.iconClass) {
|
|
6459
|
+
icon.className = `entity-label-icon ${this.options.iconClass}`;
|
|
6460
|
+
}
|
|
6461
|
+
else {
|
|
6462
|
+
icon.textContent = this.options.icon || "โ";
|
|
6463
|
+
}
|
|
6464
|
+
if (iconBackground) {
|
|
6465
|
+
const color = Color.fromCssColorString(iconColor || "white") || Color.WHITE;
|
|
6466
|
+
const luminance = 0.2126 * color.red + 0.7152 * color.green + 0.0722 * color.blue;
|
|
6467
|
+
const background = (luminance > 0.5 ? color.darken(ICON_BACKGROUND_BLEND, new Color()) :
|
|
6468
|
+
color.brighten(ICON_BACKGROUND_BLEND, new Color()));
|
|
6469
|
+
const size = (this.options.iconSize || 24) * ((_p = this.options.iconScale) !== null && _p !== void 0 ? _p : 1);
|
|
6470
|
+
icon.style.backgroundColor = background.withAlpha(ICON_BACKGROUND_ALPHA).toCssColorString();
|
|
6471
|
+
icon.style.borderRadius = "50%";
|
|
6472
|
+
icon.style.padding = `${size / 3}px`;
|
|
6473
|
+
icon.style.display = "inline-flex";
|
|
6474
|
+
icon.style.alignItems = "center";
|
|
6475
|
+
icon.style.justifyContent = "center";
|
|
6476
|
+
icon.style.minWidth = `${size}px`;
|
|
6477
|
+
icon.style.minHeight = `${size}px`;
|
|
6478
|
+
icon.style.boxSizing = "content-box";
|
|
6479
|
+
}
|
|
6480
|
+
if (this.options.iconScale === 0) {
|
|
6481
|
+
icon.style.display = "none";
|
|
6482
|
+
}
|
|
6483
|
+
makeClickable(icon);
|
|
6484
|
+
// Keep badges outside the glyph so font classes and image masks cannot affect them.
|
|
6485
|
+
iconContainer = document.createElement("span");
|
|
6486
|
+
iconContainer.style.cssText = "position:relative;display:inline-flex;flex:none;";
|
|
6487
|
+
iconContainer.appendChild(icon);
|
|
6488
|
+
content.appendChild(iconContainer);
|
|
6489
|
+
}
|
|
6490
|
+
if (presentation !== "icon") {
|
|
6491
|
+
textElement = document.createElement("span");
|
|
6492
|
+
textElement.className = "entity-label-text";
|
|
6493
|
+
const textStyle = `white-space:pre-wrap;overflow-wrap:anywhere;max-width:${TEXT_MAX_WIDTH}px;width:max-content;flex-shrink:0;`;
|
|
6494
|
+
textElement.style.cssText = textStyle;
|
|
6495
|
+
textElement.textContent = originalText;
|
|
6496
|
+
if (presentation === "text")
|
|
6497
|
+
makeClickable(textElement);
|
|
6498
|
+
content.appendChild(textElement);
|
|
6499
|
+
if ((_q = this.options.cluster) === null || _q === void 0 ? void 0 : _q.enabled) {
|
|
6500
|
+
sourceText = document.createElement("span");
|
|
6501
|
+
sourceText.className = "entity-label-source-text";
|
|
6502
|
+
sourceText.setAttribute("aria-hidden", "true");
|
|
6503
|
+
sourceText.style.cssText = textStyle + "position:absolute;visibility:hidden;pointer-events:none;";
|
|
6504
|
+
sourceText.textContent = originalText;
|
|
6505
|
+
content.appendChild(sourceText);
|
|
6506
|
+
// Measure the original unwrapped text, never a rendered cluster summary.
|
|
6507
|
+
sourceNaturalText = document.createElement("span");
|
|
6508
|
+
sourceNaturalText.setAttribute("aria-hidden", "true");
|
|
6509
|
+
sourceNaturalText.style.cssText = "position:absolute;visibility:hidden;pointer-events:none;white-space:pre;width:max-content;";
|
|
6510
|
+
sourceNaturalText.textContent = originalText;
|
|
6511
|
+
content.appendChild(sourceNaturalText);
|
|
6512
|
+
}
|
|
6513
|
+
}
|
|
6514
|
+
const countElement = document.createElement("span");
|
|
6515
|
+
countElement.className = "entity-label-count";
|
|
6516
|
+
countElement.style.cssText = "flex:none;align-self:center;white-space:nowrap;background:#fff;color:#000;text-shadow:none;border-radius:8px;padding:0 3px;display:none;";
|
|
6517
|
+
if (iconContainer) {
|
|
6518
|
+
countElement.style.cssText += "position:absolute;left:100%;top:0;transform:translate(-50%,-50%);";
|
|
6519
|
+
iconContainer.appendChild(countElement);
|
|
6520
|
+
}
|
|
6521
|
+
if (!layers.has(this.viewer)) {
|
|
6522
|
+
layers.set(this.viewer, new Layer(this.viewer));
|
|
6523
|
+
}
|
|
6524
|
+
const layer = layers.get(this.viewer);
|
|
5708
6525
|
let ele = null;
|
|
5709
6526
|
let lineEle = null;
|
|
5710
6527
|
if (this.label) {
|
|
@@ -5712,11 +6529,22 @@ var EntityLabel;
|
|
|
5712
6529
|
}
|
|
5713
6530
|
else {
|
|
5714
6531
|
ele = document.createElement("div");
|
|
5715
|
-
ele.
|
|
6532
|
+
ele.appendChild(content);
|
|
6533
|
+
if (!iconContainer) {
|
|
6534
|
+
ele.appendChild(countElement);
|
|
6535
|
+
}
|
|
6536
|
+
ele.className = "entity-label";
|
|
6537
|
+
ele.dataset.entityId = ((_r = this.rego) === null || _r === void 0 ? void 0 : _r.entityId) || "";
|
|
6538
|
+
ele.dataset.presentation = presentation;
|
|
6539
|
+
if (this.groupId) {
|
|
6540
|
+
ele.dataset.groupId = this.groupId;
|
|
6541
|
+
}
|
|
5716
6542
|
ele.setAttribute("style", `
|
|
5717
6543
|
position: absolute;
|
|
5718
6544
|
z-index: 0;
|
|
5719
6545
|
display: none;
|
|
6546
|
+
align-items: center;
|
|
6547
|
+
gap: 6px;
|
|
5720
6548
|
pointer-events: none;
|
|
5721
6549
|
padding: 6px 8px;
|
|
5722
6550
|
border-radius: 6px;
|
|
@@ -5725,9 +6553,10 @@ var EntityLabel;
|
|
|
5725
6553
|
border-radius: 9px;
|
|
5726
6554
|
color: #ffffff;
|
|
5727
6555
|
text-shadow: 0 0 5px rgba(0, 0, 0, 0.8);
|
|
5728
|
-
|
|
6556
|
+
`);
|
|
5729
6557
|
this.label = ele;
|
|
5730
|
-
|
|
6558
|
+
ele.style.width = "max-content";
|
|
6559
|
+
layer.element.appendChild(ele);
|
|
5731
6560
|
}
|
|
5732
6561
|
if (this.line) {
|
|
5733
6562
|
lineEle = this.line;
|
|
@@ -5744,7 +6573,10 @@ var EntityLabel;
|
|
|
5744
6573
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
|
|
5745
6574
|
`);
|
|
5746
6575
|
this.line = lineEle;
|
|
5747
|
-
|
|
6576
|
+
lineEle.className = "entity-label-leader";
|
|
6577
|
+
if (this.options.leaderColor)
|
|
6578
|
+
lineEle.style.background = this.options.leaderColor;
|
|
6579
|
+
// A separate anchor keeps the stem out of the hollow centre when proximity opens the ring.
|
|
5748
6580
|
const lineEleNode = document.createElement("div");
|
|
5749
6581
|
lineEleNode.setAttribute("style", `
|
|
5750
6582
|
position: absolute;
|
|
@@ -5757,35 +6589,20 @@ var EntityLabel;
|
|
|
5757
6589
|
top: -2px;
|
|
5758
6590
|
left: -2px;
|
|
5759
6591
|
`);
|
|
5760
|
-
|
|
5761
|
-
|
|
6592
|
+
lineEleNode.className = "entity-label-anchor";
|
|
6593
|
+
// SVG preserves fractional strokes in scaled containers where CSS borders round to pixels.
|
|
6594
|
+
const ringSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
6595
|
+
ringSvg.style.cssText = "position:absolute;inset:0;width:100%;height:100%;overflow:visible;";
|
|
6596
|
+
ringSvg.setAttribute("aria-hidden", "true");
|
|
6597
|
+
ringSvg.setAttribute("preserveAspectRatio", "none");
|
|
6598
|
+
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
6599
|
+
circle.setAttribute("fill", "none");
|
|
6600
|
+
ringSvg.appendChild(circle);
|
|
6601
|
+
lineEleNode.appendChild(ringSvg);
|
|
6602
|
+
this.anchor = lineEleNode;
|
|
6603
|
+
layer.element.appendChild(lineEleNode);
|
|
6604
|
+
layer.element.appendChild(lineEle);
|
|
5762
6605
|
}
|
|
5763
|
-
let _lastDistance = null;
|
|
5764
|
-
let _lastCameraPos = null;
|
|
5765
|
-
const getDistance = () => {
|
|
5766
|
-
var _a;
|
|
5767
|
-
if (this.disposed) {
|
|
5768
|
-
return null;
|
|
5769
|
-
}
|
|
5770
|
-
const cameraPos = (_a = this.viewer.camera) === null || _a === void 0 ? void 0 : _a.position;
|
|
5771
|
-
if (isNaN(cameraPos === null || cameraPos === void 0 ? void 0 : cameraPos.x)) {
|
|
5772
|
-
return null;
|
|
5773
|
-
}
|
|
5774
|
-
// If camera hasn't moved much then we can use the last distance.
|
|
5775
|
-
const TOLERANCE = 0.1;
|
|
5776
|
-
if (_lastCameraPos) {
|
|
5777
|
-
const totalMovement = Math.abs(_lastCameraPos.x - cameraPos.x) +
|
|
5778
|
-
Math.abs(_lastCameraPos.y - cameraPos.y) +
|
|
5779
|
-
Math.abs(_lastCameraPos.z - cameraPos.z);
|
|
5780
|
-
if (totalMovement < TOLERANCE) {
|
|
5781
|
-
return _lastDistance;
|
|
5782
|
-
}
|
|
5783
|
-
}
|
|
5784
|
-
const distance = Cartesian3.distance(cameraPos, this._pos3d);
|
|
5785
|
-
_lastDistance = distance;
|
|
5786
|
-
_lastCameraPos = cameraPos.clone();
|
|
5787
|
-
return distance;
|
|
5788
|
-
};
|
|
5789
6606
|
let MAX_DISTANCE = Math.max(15000, sizeInM * 35);
|
|
5790
6607
|
if (visual && visual instanceof Entity) {
|
|
5791
6608
|
// Consistent sized things.
|
|
@@ -5793,123 +6610,399 @@ var EntityLabel;
|
|
|
5793
6610
|
MAX_DISTANCE = Infinity;
|
|
5794
6611
|
}
|
|
5795
6612
|
}
|
|
5796
|
-
const
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
6613
|
+
const fitLeader = (anchorX, anchorY, desired, width, height, radius = 0) => {
|
|
6614
|
+
const above = Math.max(0, anchorY - height);
|
|
6615
|
+
const below = Math.max(0, this.viewer.canvas.clientHeight - anchorY - height);
|
|
6616
|
+
// Keep a short visible stem before switching sides; never aim it at the cursor or a box corner.
|
|
6617
|
+
const upwards = above >= Math.max(radius ? radius + 4 : 0, Math.min(8, desired)) || above >= below;
|
|
6618
|
+
const length = Math.min(desired, upwards ? above : below);
|
|
6619
|
+
const endY = anchorY + (upwards ? -length : length);
|
|
6620
|
+
return {
|
|
6621
|
+
x: Math.max(0, Math.min(anchorX, this.viewer.canvas.clientWidth - width)),
|
|
6622
|
+
y: upwards ? endY - height : endY, endY
|
|
6623
|
+
};
|
|
6624
|
+
};
|
|
6625
|
+
let cursorMotion = this.cursorMotion;
|
|
6626
|
+
const project = () => {
|
|
6627
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
6628
|
+
if (this.disposed || !this.viewer || this.viewer.isDestroyed() || ((_a = this.visible) === null || _a === void 0 ? void 0 : _a.call(this)) === false ||
|
|
6629
|
+
(presentation === "icon" && this.options.iconScale === 0)) {
|
|
6630
|
+
return null;
|
|
5800
6631
|
}
|
|
5801
|
-
if (!this.
|
|
5802
|
-
this.
|
|
5803
|
-
return;
|
|
6632
|
+
if (this.Transient && !this.Persistent && this.hoverFade.to === 0 &&
|
|
6633
|
+
(this.hoverFade.from === 0 || Date.now() - this.hoverFade.start >= 120)) {
|
|
6634
|
+
return null;
|
|
5804
6635
|
}
|
|
5805
|
-
|
|
5806
|
-
if (
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
6636
|
+
// Moving point/model positions must be sampled at the current clock time.
|
|
6637
|
+
if (!this.prePositioned && visual instanceof Entity && visual.position) {
|
|
6638
|
+
const position = getValue(this.viewer, visual.position);
|
|
6639
|
+
if (position)
|
|
6640
|
+
this._pos3d = DrawingUtils.EnsurePosHeight({
|
|
6641
|
+
desiredHeightRef: HeightReference.NONE, heightRef, pos3d: position, viewer: this.viewer
|
|
6642
|
+
});
|
|
5810
6643
|
}
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
6644
|
+
if (!Number.isFinite((_b = this._pos3d) === null || _b === void 0 ? void 0 : _b.x)) {
|
|
6645
|
+
return null;
|
|
6646
|
+
}
|
|
6647
|
+
const camera = this.viewer.camera;
|
|
6648
|
+
const cameraPosition = camera.positionWC || camera.position;
|
|
6649
|
+
const relative = Cartesian3.subtract(this._pos3d, cameraPosition, new Cartesian3());
|
|
6650
|
+
const depth = Cartesian3.dot(relative, camera.directionWC || camera.direction);
|
|
6651
|
+
const distance = Cartesian3.magnitude(relative);
|
|
6652
|
+
if (!Number.isFinite(distance) || depth <= 0 || distance < ((_c = this.options.near) !== null && _c !== void 0 ? _c : 0) ||
|
|
6653
|
+
distance > ((_d = this.options.far) !== null && _d !== void 0 ? _d : Infinity) || (!this.prePositioned && distance >= MAX_DISTANCE)) {
|
|
6654
|
+
return null;
|
|
5820
6655
|
}
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
this.
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
6656
|
+
const transforms = SceneTransforms;
|
|
6657
|
+
const projectPosition = transforms.wgs84ToWindowCoordinates || transforms.worldToWindowCoordinates;
|
|
6658
|
+
const anchor = projectPosition(this.viewer.scene, this._pos3d);
|
|
6659
|
+
if (!Number.isFinite(anchor === null || anchor === void 0 ? void 0 : anchor.x) || !Number.isFinite(anchor === null || anchor === void 0 ? void 0 : anchor.y)) {
|
|
6660
|
+
return null;
|
|
6661
|
+
}
|
|
6662
|
+
if (leader && (anchor.x < 0 || anchor.y < 0 || anchor.x >= this.viewer.canvas.clientWidth || anchor.y >= this.viewer.canvas.clientHeight))
|
|
6663
|
+
return null;
|
|
6664
|
+
const transientPivot = (this.Transient && !this.Persistent ?
|
|
6665
|
+
(this.hoverFade.to === 0 ? this.retainedHoverPosition : (_e = this.visualRegister) === null || _e === void 0 ? void 0 : _e.GetLabelHoverPosition((_f = this.rego) === null || _f === void 0 ? void 0 : _f.entityId, (_g = this.rego) === null || _g === void 0 ? void 0 : _g.menuItemId)) : undefined);
|
|
6666
|
+
const hover = (presentation !== "icon" ?
|
|
6667
|
+
(this.Transient && !this.Persistent && this.hoverFade.to === 0 ? this.retainedHoverPosition :
|
|
6668
|
+
(_h = this.visualRegister) === null || _h === void 0 ? void 0 : _h.GetLabelHoverPosition((_j = this.rego) === null || _j === void 0 ? void 0 : _j.entityId, (_k = this.rego) === null || _k === void 0 ? void 0 : _k.menuItemId)) : undefined);
|
|
6669
|
+
const reused = !this.Transient || this.Persistent;
|
|
6670
|
+
const now = Date.now();
|
|
6671
|
+
const animateCursor = () => {
|
|
6672
|
+
const progress = Math.min(1, Math.max(0, (now - cursorMotion.start) / 200));
|
|
6673
|
+
const eased = progress * progress * (3 - 2 * progress);
|
|
6674
|
+
cursorMotion.amount = cursorMotion.from + (cursorMotion.target - cursorMotion.from) * eased;
|
|
6675
|
+
return progress;
|
|
6676
|
+
};
|
|
6677
|
+
animateCursor();
|
|
6678
|
+
const cursorTarget = reused && hover ? 1 : 0;
|
|
6679
|
+
if (cursorMotion.target !== cursorTarget) {
|
|
6680
|
+
cursorMotion = { ...cursorMotion, from: cursorMotion.amount, target: cursorTarget, start: now };
|
|
6681
|
+
}
|
|
6682
|
+
if (hover) {
|
|
6683
|
+
cursorMotion.position = { x: hover.x, y: hover.y };
|
|
6684
|
+
}
|
|
6685
|
+
if (animateCursor() < 1 && cursorMotion.from !== cursorMotion.target) {
|
|
6686
|
+
this.viewer.scene.requestRender();
|
|
6687
|
+
}
|
|
6688
|
+
this.cursorMotion = cursorMotion;
|
|
6689
|
+
const cursorAmount = cursorMotion.amount;
|
|
6690
|
+
leader = normalLeader || cursorAmount > 0;
|
|
6691
|
+
let x = ((_l = transientPivot === null || transientPivot === void 0 ? void 0 : transientPivot.x) !== null && _l !== void 0 ? _l : anchor.x) + pixelOffset.x;
|
|
6692
|
+
let y = ((_m = transientPivot === null || transientPivot === void 0 ? void 0 : transientPivot.y) !== null && _m !== void 0 ? _m : anchor.y) + pixelOffset.y;
|
|
6693
|
+
if (cursorAmount > 0) {
|
|
6694
|
+
// A leader-less label starts centred on its anchor; preserve that box as it lifts away.
|
|
6695
|
+
if (!normalLeader) {
|
|
6696
|
+
x -= ele.offsetWidth / layer.coordinates.scaleX / 2;
|
|
6697
|
+
y += ele.offsetHeight / layer.coordinates.scaleY / 2;
|
|
6698
|
+
}
|
|
6699
|
+
x += (cursorMotion.position.x + pixelOffset.x - x) * cursorAmount;
|
|
6700
|
+
y += (cursorMotion.position.y + pixelOffset.y - y) * cursorAmount;
|
|
6701
|
+
}
|
|
6702
|
+
const anchorX = x;
|
|
6703
|
+
const anchorY = y;
|
|
6704
|
+
let leaderLength;
|
|
6705
|
+
if (leader) {
|
|
6706
|
+
if (!normalLeader) {
|
|
6707
|
+
leaderLength = 0;
|
|
6708
|
+
}
|
|
6709
|
+
else if (this.options.leaderLength != null) {
|
|
6710
|
+
leaderLength = Math.max(0, this.options.leaderLength);
|
|
6711
|
+
}
|
|
6712
|
+
else if (transientPivot) {
|
|
6713
|
+
// Cursor callouts must not jump when the next hover target has a different extent.
|
|
6714
|
+
leaderLength = 96;
|
|
5840
6715
|
}
|
|
5841
6716
|
else {
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
//
|
|
5848
|
-
|
|
5849
|
-
//
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
const
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
6717
|
+
const point = Cartographic.fromCartesian(this._pos3d);
|
|
6718
|
+
const height = (this.prePositioned ? distance / 24 : sizeInM > 0 ?
|
|
6719
|
+
Math.min(distance / 5, sizeInM * 1.2) : Math.min(distance, camera.positionCartographic.height) / 7);
|
|
6720
|
+
point.height += Math.max(0.001, height);
|
|
6721
|
+
const end = projectPosition(this.viewer.scene, Cartographic.toCartesian(point));
|
|
6722
|
+
// A tall world-space leader can cross the camera plane while its anchor remains visible.
|
|
6723
|
+
leaderLength = Number.isFinite(end === null || end === void 0 ? void 0 : end.x) && Number.isFinite(end === null || end === void 0 ? void 0 : end.y) ? Math.hypot(end.x - anchor.x, end.y - anchor.y) : 24;
|
|
6724
|
+
// Scale after projection so large areas and routes get shorter leaders at every zoom.
|
|
6725
|
+
if (visual instanceof Entity && (visual.polygon || visual.polyline))
|
|
6726
|
+
leaderLength /= 3;
|
|
6727
|
+
}
|
|
6728
|
+
if (cursorAmount > 0) {
|
|
6729
|
+
leaderLength += (96 - leaderLength) * cursorAmount;
|
|
6730
|
+
}
|
|
6731
|
+
y = anchorY - leaderLength;
|
|
6732
|
+
}
|
|
6733
|
+
else if (hover && !reused) {
|
|
6734
|
+
x = hover.x + pixelOffset.x;
|
|
6735
|
+
y = hover.y + pixelOffset.y - 24;
|
|
6736
|
+
}
|
|
6737
|
+
// Hidden participants remain measurable; visibility never removes their layout boxes.
|
|
6738
|
+
const coordinates = layer.coordinates;
|
|
6739
|
+
const width = ele.offsetWidth / coordinates.scaleX;
|
|
6740
|
+
const height = ele.offsetHeight / coordinates.scaleY;
|
|
6741
|
+
let clusterBounds;
|
|
6742
|
+
if ((_o = this.options.cluster) === null || _o === void 0 ? void 0 : _o.enabled) {
|
|
6743
|
+
const textWidth = (sourceText === null || sourceText === void 0 ? void 0 : sourceText.offsetWidth) || 0;
|
|
6744
|
+
const textHeight = (sourceText === null || sourceText === void 0 ? void 0 : sourceText.offsetHeight) || 0;
|
|
6745
|
+
const iconWidth = (iconElement === null || iconElement === void 0 ? void 0 : iconElement.offsetWidth) || 0;
|
|
6746
|
+
const iconHeight = (iconElement === null || iconElement === void 0 ? void 0 : iconElement.offsetHeight) || 0;
|
|
6747
|
+
const gap = sourceText && iconWidth ? 6 : 0;
|
|
6748
|
+
const above = presentation === "icon-text-above";
|
|
6749
|
+
// Match the fixed content gap and label padding, excluding the count badge.
|
|
6750
|
+
const baseWidth = ((above ? Math.max(textWidth, iconWidth) : textWidth + iconWidth + gap) + 16) / coordinates.scaleX;
|
|
6751
|
+
const baseHeight = ((above ? textHeight + iconHeight + gap : Math.max(textHeight, iconHeight)) + 12) / coordinates.scaleY;
|
|
6752
|
+
clusterBounds = {
|
|
6753
|
+
x: leader ? x : x - baseWidth / 2,
|
|
6754
|
+
y: leader ? y - baseHeight : y - baseHeight / 2, width: baseWidth, height: baseHeight
|
|
6755
|
+
};
|
|
6756
|
+
}
|
|
6757
|
+
if (!leader) {
|
|
6758
|
+
x -= width / 2;
|
|
6759
|
+
y -= height / 2;
|
|
6760
|
+
}
|
|
6761
|
+
// Membership uses the unclamped source footprint, never the summary's changing dimensions.
|
|
6762
|
+
if (leader)
|
|
6763
|
+
({ x, y } = fitLeader(anchorX, anchorY, leaderLength, width, height));
|
|
6764
|
+
const bounds = clusterBounds || { x, y, width, height };
|
|
6765
|
+
if (!leader && (bounds.x + bounds.width < 0 || bounds.y + bounds.height < 0 || bounds.x > this.viewer.canvas.clientWidth || bounds.y > this.viewer.canvas.clientHeight)) {
|
|
6766
|
+
return null;
|
|
6767
|
+
}
|
|
6768
|
+
const state = (_p = this.visualRegister) === null || _p === void 0 ? void 0 : _p.GetState({ entityId: (_q = this.rego) === null || _q === void 0 ? void 0 : _q.entityId, menuItemId: (_r = this.rego) === null || _r === void 0 ? void 0 : _r.menuItemId });
|
|
6769
|
+
const opacity = this.prePositioned || MAX_DISTANCE === Infinity ? 1 : 1 - distance / MAX_DISTANCE;
|
|
6770
|
+
return {
|
|
6771
|
+
canFlip: leader && presentation !== "icon", hasLeader: leader,
|
|
6772
|
+
textWrapped: ((sourceNaturalText === null || sourceNaturalText === void 0 ? void 0 : sourceNaturalText.offsetWidth) || 0) > TEXT_MAX_WIDTH,
|
|
6773
|
+
transient: this.Transient || Boolean(hover) || cursorAmount > 0,
|
|
6774
|
+
extendedGeometry: visual instanceof Entity && Boolean(visual.polygon || visual.polyline || visual.corridor),
|
|
6775
|
+
viewportWidth: this.viewer.canvas.clientWidth,
|
|
6776
|
+
id: this.id, entityKey: ((_s = this.rego) === null || _s === void 0 ? void 0 : _s.entityId) ? JSON.stringify([this.rego.entityId, this.rego.menuItemId || "", this.Transient && !this.Persistent ? "hover" : ""]) : undefined,
|
|
6777
|
+
entityId: (_t = this.rego) === null || _t === void 0 ? void 0 : _t.entityId, group: this.groupId, x, y, width, height, anchorX, anchorY, depth, leaderLength, distance,
|
|
6778
|
+
// Priority shows the hovered member individually without dragging its cluster along.
|
|
6779
|
+
opacity, priority: hover || cursorAmount > 0 || (state === null || state === void 0 ? void 0 : state.highlighted) ? 1 : 0, options: this.options, text: originalText, clusterBounds
|
|
6780
|
+
};
|
|
6781
|
+
};
|
|
6782
|
+
let displacement = { x: 0, y: 0 };
|
|
6783
|
+
let displayedSummaryMore = false;
|
|
6784
|
+
let nudge = { x: 0, y: 0, fromX: 0, fromY: 0, targetX: 0, targetY: 0, start: 0 };
|
|
6785
|
+
const apply = (placement) => {
|
|
6786
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
6787
|
+
const show = (placement === null || placement === void 0 ? void 0 : placement.visible) && placement.opacity > 0;
|
|
6788
|
+
clickEntityIds = show ? placement.entityIds : [];
|
|
6789
|
+
ele.style.visibility = show ? "visible" : "hidden";
|
|
6790
|
+
lineEle.style.display = show && leader ? "block" : "none";
|
|
6791
|
+
this.anchor.style.display = show && leader ? "block" : "none";
|
|
6792
|
+
if (!show) {
|
|
6793
|
+
displacement = { x: 0, y: 0 };
|
|
6794
|
+
nudge = { x: 0, y: 0, fromX: 0, fromY: 0, targetX: 0, targetY: 0, start: 0 };
|
|
6795
|
+
if (!this.Transient) {
|
|
6796
|
+
this.ringAnimation = undefined;
|
|
5876
6797
|
}
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
6798
|
+
return;
|
|
6799
|
+
}
|
|
6800
|
+
const displayText = (_a = placement.text) !== null && _a !== void 0 ? _a : originalText;
|
|
6801
|
+
const summaryMore = Boolean(placement.summaryMore);
|
|
6802
|
+
if (textElement && (textElement.textContent !== displayText + (summaryMore ? "\nโฆ" : "") || displayedSummaryMore !== summaryMore)) {
|
|
6803
|
+
textElement.textContent = displayText;
|
|
6804
|
+
if (summaryMore) {
|
|
6805
|
+
const more = document.createElement("span");
|
|
6806
|
+
more.className = "entity-label-more";
|
|
6807
|
+
more.style.opacity = "0.65";
|
|
6808
|
+
more.textContent = "\nโฆ";
|
|
6809
|
+
textElement.appendChild(more);
|
|
6810
|
+
}
|
|
6811
|
+
displayedSummaryMore = summaryMore;
|
|
6812
|
+
// Measure the summary's footprint next frame without replacing each member's source text.
|
|
6813
|
+
this.viewer.scene.requestRender();
|
|
6814
|
+
}
|
|
6815
|
+
const frame = placement.frame;
|
|
6816
|
+
const coordinates = layer.coordinates;
|
|
6817
|
+
const opacity = placement.opacity * this.HoverOpacity();
|
|
6818
|
+
ele.style.opacity = String(opacity);
|
|
6819
|
+
ele.style.zIndex = String(placement.zIndex);
|
|
6820
|
+
ele.dataset.clusterCount = String(placement.members.length || 1);
|
|
6821
|
+
countElement.style.display = placement.members.length ? "block" : "none";
|
|
6822
|
+
countElement.style.order = placement.textSide === "left" ? "-1" : "0";
|
|
6823
|
+
const count = placement.members.length ? String(placement.members.length) : "";
|
|
6824
|
+
if (countElement.textContent !== count) {
|
|
6825
|
+
countElement.textContent = count;
|
|
6826
|
+
// The badge participates in layout, so its new width needs another measurement.
|
|
6827
|
+
this.viewer.scene.requestRender();
|
|
6828
|
+
}
|
|
6829
|
+
if (iconContainer) {
|
|
6830
|
+
const badgeWidth = countElement.offsetWidth;
|
|
6831
|
+
const iconWidth = iconElement.offsetWidth;
|
|
6832
|
+
const narrow = iconWidth < iconElement.offsetHeight * 0.6;
|
|
6833
|
+
const clearance = badgeWidth ? (narrow ? badgeWidth / 2 + 2 : Math.max(0, badgeWidth / 2 - iconWidth / 4)) : 0;
|
|
6834
|
+
countElement.style.left = `calc(100% + ${clearance}px)`;
|
|
6835
|
+
// Reserve the badge overhang so adjacent text and viewport edges remain clear.
|
|
6836
|
+
iconContainer.style.marginTop = `${countElement.offsetHeight / 2}px`;
|
|
6837
|
+
iconContainer.style.marginRight = `${badgeWidth / 2 + clearance}px`;
|
|
6838
|
+
}
|
|
6839
|
+
// Summary and badge changes must fit on their first visible frame, including after resize.
|
|
6840
|
+
const ring = this.options.anchorRing;
|
|
6841
|
+
const pointer = (layer.pointer || (cursorMotion.target ? cursorMotion.position : undefined) || (this.Transient && !this.Persistent ?
|
|
6842
|
+
(this.hoverFade.to === 0 ? this.retainedHoverPosition : (_b = this.visualRegister) === null || _b === void 0 ? void 0 : _b.GetLabelHoverPosition((_c = this.rego) === null || _c === void 0 ? void 0 : _c.entityId, (_d = this.rego) === null || _d === void 0 ? void 0 : _d.menuItemId)) : undefined));
|
|
6843
|
+
const hovered = pointer && Math.hypot(pointer.x - frame.anchorX, pointer.y - frame.anchorY) <= ((_e = ring === null || ring === void 0 ? void 0 : ring.hoverDistance) !== null && _e !== void 0 ? _e : 24);
|
|
6844
|
+
const normalRadius = (!leader || (ring === null || ring === void 0 ? void 0 : ring.enabled) === false ? 0 : Math.max(frame.distance <= ((_f = ring === null || ring === void 0 ? void 0 : ring.nearDistance) !== null && _f !== void 0 ? _f : 100) ? ((_g = ring === null || ring === void 0 ? void 0 : ring.nearRadius) !== null && _g !== void 0 ? _g : 10) : 0, hovered ? ((_h = ring === null || ring === void 0 ? void 0 : ring.hoverRadius) !== null && _h !== void 0 ? _h : (this.Transient && !this.Persistent ? 15.4 : 22)) : 0));
|
|
6845
|
+
const targetRadius = normalRadius + (15.4 - normalRadius) * cursorMotion.amount;
|
|
6846
|
+
const now = Date.now();
|
|
6847
|
+
const duration = (_j = ring === null || ring === void 0 ? void 0 : ring.transitionDuration) !== null && _j !== void 0 ? _j : 200;
|
|
6848
|
+
const sharedRing = this.Transient && !this.Persistent && this.hoverFade.to === 1 ? this.hoverRingState : undefined;
|
|
6849
|
+
let ringAnimation = sharedRing ? sharedRing.animation : this.ringAnimation;
|
|
6850
|
+
if (!ringAnimation) {
|
|
6851
|
+
ringAnimation = { target: 0, start: now, outer: 2.5, inner: 0, fromOuter: 2.5, fromInner: 0 };
|
|
6852
|
+
}
|
|
6853
|
+
const animateRing = () => {
|
|
6854
|
+
const progress = duration > 0 ? Math.min(1, Math.max(0, (now - ringAnimation.start) / duration)) : 1;
|
|
6855
|
+
const eased = progress * progress * (3 - 2 * progress);
|
|
6856
|
+
ringAnimation.outer = ringAnimation.fromOuter + ((ringAnimation.target || 2.5) - ringAnimation.fromOuter) * eased;
|
|
6857
|
+
ringAnimation.inner = ringAnimation.fromInner + (Math.max(0, ringAnimation.target - 2) - ringAnimation.fromInner) * eased;
|
|
6858
|
+
return progress;
|
|
6859
|
+
};
|
|
6860
|
+
animateRing();
|
|
6861
|
+
if (ringAnimation.target !== targetRadius) {
|
|
6862
|
+
ringAnimation = {
|
|
6863
|
+
...ringAnimation,
|
|
6864
|
+
target: targetRadius,
|
|
6865
|
+
start: now,
|
|
6866
|
+
fromOuter: ringAnimation.outer,
|
|
6867
|
+
fromInner: ringAnimation.inner
|
|
6868
|
+
};
|
|
6869
|
+
}
|
|
6870
|
+
// Animate the hole and stem together; no CSS transition may lag behind a moving world anchor.
|
|
6871
|
+
if (animateRing() < 1 && (ringAnimation.fromOuter !== (targetRadius || 2.5) || ringAnimation.fromInner !== Math.max(0, targetRadius - 2))) {
|
|
6872
|
+
this.viewer.scene.requestRender();
|
|
6873
|
+
}
|
|
6874
|
+
this.ringAnimation = ringAnimation;
|
|
6875
|
+
if (sharedRing) {
|
|
6876
|
+
sharedRing.animation = ringAnimation;
|
|
6877
|
+
}
|
|
6878
|
+
const radius = ringAnimation.inner > 0 ? ringAnimation.outer : 0;
|
|
6879
|
+
const stem = (leader ? fitLeader(frame.anchorX, frame.anchorY, Math.max(frame.leaderLength, radius ? radius + 4 : 0), ele.offsetWidth / coordinates.scaleX, ele.offsetHeight / coordinates.scaleY, radius) : undefined);
|
|
6880
|
+
const position = { x: (stem || frame).x, y: (stem || frame).y };
|
|
6881
|
+
const width = ele.offsetWidth / coordinates.scaleX;
|
|
6882
|
+
const height = ele.offsetHeight / coordinates.scaleY;
|
|
6883
|
+
if (leader && placement.textSide === "left") {
|
|
6884
|
+
position.x = Math.max(0, Math.min(frame.anchorX - width, this.viewer.canvas.clientWidth - width));
|
|
6885
|
+
}
|
|
6886
|
+
const gap = (_l = (_k = this.options.pointerAvoidance) === null || _k === void 0 ? void 0 : _k.gap) !== null && _l !== void 0 ? _l : 12;
|
|
6887
|
+
const containsPointer = (x, y, margin) => {
|
|
6888
|
+
return pointer && pointer.x >= x - margin && pointer.x <= x + width + margin && pointer.y >= y - margin && pointer.y <= y + height + margin;
|
|
6889
|
+
};
|
|
6890
|
+
const passive = !selectOnClick || !this.visualRegister;
|
|
6891
|
+
if (passive && ((_m = this.options.pointerAvoidance) === null || _m === void 0 ? void 0 : _m.enabled) !== false && pointer &&
|
|
6892
|
+
(containsPointer(position.x, position.y, gap) || containsPointer(position.x + displacement.x, position.y + displacement.y, gap))) {
|
|
6893
|
+
if (containsPointer(position.x + displacement.x, position.y + displacement.y, 0)) {
|
|
6894
|
+
// Choose against the original box and retain the offset until the pointer leaves both boxes.
|
|
6895
|
+
const candidates = [
|
|
6896
|
+
{ x: 0, y: pointer.y - gap - height - position.y },
|
|
6897
|
+
{ x: 0, y: pointer.y + gap - position.y },
|
|
6898
|
+
{ x: pointer.x - gap - width - position.x, y: 0 },
|
|
6899
|
+
{ x: pointer.x + gap - position.x, y: 0 }
|
|
6900
|
+
].filter(offset => position.x + offset.x >= 0 && position.y + offset.y >= 0 &&
|
|
6901
|
+
position.x + offset.x + width <= this.viewer.canvas.clientWidth && position.y + offset.y + height <= this.viewer.canvas.clientHeight)
|
|
6902
|
+
.sort((a, b) => Math.hypot(a.x, a.y) - Math.hypot(b.x, b.y));
|
|
6903
|
+
displacement = candidates[0] || { x: 0, y: 0 };
|
|
5890
6904
|
}
|
|
5891
|
-
// Position the label above the line end point.
|
|
5892
|
-
this.label.style.left = `${lineEndPos2d.x}px`;
|
|
5893
|
-
this.label.style.top = `${lineEndPos2d.y}px`;
|
|
5894
|
-
// Update line position and size.
|
|
5895
|
-
const dx = lineEndPos2d.x - lineStartPos2d.x;
|
|
5896
|
-
const dy = lineEndPos2d.y - lineStartPos2d.y;
|
|
5897
|
-
const length = Math.sqrt(dx * dx + dy * dy);
|
|
5898
|
-
const angle = Math.atan2(dy, dx) - Math.PI / 2;
|
|
5899
|
-
this.line.style.left = `${lineStartPos2d.x}px`;
|
|
5900
|
-
this.line.style.top = `${lineStartPos2d.y}px`;
|
|
5901
|
-
this.line.style.height = `${length}px`;
|
|
5902
|
-
this.line.style.transform = `rotate(${angle}rad)`;
|
|
5903
|
-
this.line.style.transformOrigin = 'top center';
|
|
5904
6905
|
}
|
|
5905
6906
|
else {
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
6907
|
+
displacement = { x: 0, y: 0 };
|
|
6908
|
+
}
|
|
6909
|
+
const nudgeDuration = (_p = (_o = this.options.pointerAvoidance) === null || _o === void 0 ? void 0 : _o.transitionDuration) !== null && _p !== void 0 ? _p : 200;
|
|
6910
|
+
const animateNudge = () => {
|
|
6911
|
+
const progress = now < nudge.start ? 0 : nudgeDuration > 0 ? Math.min(1, (now - nudge.start) / nudgeDuration) : 1;
|
|
6912
|
+
const eased = progress * progress * (3 - 2 * progress);
|
|
6913
|
+
nudge.x = nudge.fromX + (nudge.targetX - nudge.fromX) * eased;
|
|
6914
|
+
nudge.y = nudge.fromY + (nudge.targetY - nudge.fromY) * eased;
|
|
6915
|
+
return progress;
|
|
6916
|
+
};
|
|
6917
|
+
animateNudge();
|
|
6918
|
+
if (nudge.targetX !== displacement.x || nudge.targetY !== displacement.y) {
|
|
6919
|
+
nudge = {
|
|
6920
|
+
...nudge,
|
|
6921
|
+
fromX: nudge.x,
|
|
6922
|
+
fromY: nudge.y,
|
|
6923
|
+
targetX: displacement.x,
|
|
6924
|
+
targetY: displacement.y,
|
|
6925
|
+
start: now + ((_r = (_q = this.options.pointerAvoidance) === null || _q === void 0 ? void 0 : _q.delay) !== null && _r !== void 0 ? _r : 180)
|
|
6926
|
+
};
|
|
6927
|
+
}
|
|
6928
|
+
if (animateNudge() < 1 && (nudge.fromX !== nudge.targetX || nudge.fromY !== nudge.targetY)) {
|
|
6929
|
+
this.viewer.scene.requestRender();
|
|
6930
|
+
}
|
|
6931
|
+
position.x += nudge.x;
|
|
6932
|
+
position.y += nudge.y;
|
|
6933
|
+
ele.style.left = `${coordinates.x + position.x * coordinates.scaleX}px`;
|
|
6934
|
+
ele.style.top = `${coordinates.y + position.y * coordinates.scaleY}px`;
|
|
6935
|
+
const clickTarget = content.querySelector('[role="button"]');
|
|
6936
|
+
if (clickTarget) {
|
|
6937
|
+
clickTarget.setAttribute("aria-label", placement.members.length ? `Select ${placement.entityIds.length} entities` : `Select ${(_t = (_s = this.options.text) !== null && _s !== void 0 ? _s : text) !== null && _t !== void 0 ? _t : (_u = this.rego) === null || _u === void 0 ? void 0 : _u.entityId}`);
|
|
6938
|
+
}
|
|
6939
|
+
if (leader) {
|
|
6940
|
+
const nodeRadius = ringAnimation.outer;
|
|
6941
|
+
const anchor = this.anchor;
|
|
6942
|
+
anchor.style.left = `${coordinates.x + (frame.anchorX - nodeRadius) * coordinates.scaleX}px`;
|
|
6943
|
+
anchor.style.top = `${coordinates.y + (frame.anchorY - nodeRadius) * coordinates.scaleY}px`;
|
|
6944
|
+
anchor.style.width = `${nodeRadius * 2 * coordinates.scaleX}px`;
|
|
6945
|
+
anchor.style.height = `${nodeRadius * 2 * coordinates.scaleY}px`;
|
|
6946
|
+
anchor.style.boxSizing = "border-box";
|
|
6947
|
+
anchor.style.background = "transparent";
|
|
6948
|
+
const svg = anchor.firstElementChild;
|
|
6949
|
+
svg.setAttribute("viewBox", `0 0 ${nodeRadius * 2} ${nodeRadius * 2}`);
|
|
6950
|
+
const circle = svg.firstElementChild;
|
|
6951
|
+
circle.setAttribute("cx", String(nodeRadius));
|
|
6952
|
+
circle.setAttribute("cy", String(nodeRadius));
|
|
6953
|
+
circle.setAttribute("r", String((nodeRadius + ringAnimation.inner) / 2));
|
|
6954
|
+
circle.setAttribute("stroke", this.options.leaderColor || "rgba(255, 255, 255, 0.8)");
|
|
6955
|
+
circle.setAttribute("stroke-width", String(nodeRadius - ringAnimation.inner));
|
|
6956
|
+
anchor.style.boxShadow = radius ? "none" : "0 0 5px rgba(0, 0, 0, 0.3)";
|
|
6957
|
+
anchor.style.opacity = String(opacity * (normalLeader ? 1 : cursorMotion.amount));
|
|
6958
|
+
anchor.style.zIndex = String(placement.zIndex);
|
|
6959
|
+
// The stem starts at the rim, never across the transparent centre, even after passive displacement.
|
|
6960
|
+
const endX = Math.max(position.x, Math.min(frame.anchorX, position.x + width));
|
|
6961
|
+
const endY = frame.anchorY < position.y ? position.y : frame.anchorY > position.y + height ? position.y + height : frame.anchorY;
|
|
6962
|
+
const dx = endX - frame.anchorX, dy = endY - frame.anchorY;
|
|
6963
|
+
const length = Math.hypot(dx, dy);
|
|
6964
|
+
const leaderAngle = Math.atan2(dy, dx);
|
|
6965
|
+
const pointerDx = pointer ? pointer.x - frame.anchorX : 0;
|
|
6966
|
+
const pointerDy = pointer ? pointer.y - frame.anchorY : 0;
|
|
6967
|
+
const desiredAngle = Math.hypot(pointerDx, pointerDy) > 0.5 ? Math.atan2(pointerDy, pointerDx) + Math.PI - leaderAngle : 0;
|
|
6968
|
+
const wrappedAngle = Math.atan2(Math.sin(desiredAngle), Math.cos(desiredAngle));
|
|
6969
|
+
// Keep the stem inside the 126-degree arc, away from antialiased endpoints.
|
|
6970
|
+
const arcFraction = 0.35;
|
|
6971
|
+
const rotationLimit = Math.PI * arcFraction - Math.PI / 30;
|
|
6972
|
+
const targetOffset = hovered ? Math.max(-rotationLimit, Math.min(rotationLimit, wrappedAngle)) : ((_v = ringAnimation.arcOffset) !== null && _v !== void 0 ? _v : 0);
|
|
6973
|
+
const targetOpen = cursorMotion.target || (hovered && (ring === null || ring === void 0 ? void 0 : ring.enabled) !== false && targetRadius > 0) ? 1 : 0;
|
|
6974
|
+
const elapsed = Math.min(32, Math.max(0, now - ((_w = ringAnimation.arcTime) !== null && _w !== void 0 ? _w : now)));
|
|
6975
|
+
const blend = duration > 0 ? 1 - Math.exp(-elapsed / (duration / 3)) : 1;
|
|
6976
|
+
const approach = (value, target) => Math.abs(value - target) < 0.001 ? target : value + (target - value) * blend;
|
|
6977
|
+
ringAnimation.arcOffset = approach((_x = ringAnimation.arcOffset) !== null && _x !== void 0 ? _x : 0, targetOffset);
|
|
6978
|
+
ringAnimation.arcOpen = approach((_y = ringAnimation.arcOpen) !== null && _y !== void 0 ? _y : 0, targetOpen);
|
|
6979
|
+
ringAnimation.arcTime = now;
|
|
6980
|
+
if (ringAnimation.arcOffset !== targetOffset || ringAnimation.arcOpen !== targetOpen) {
|
|
6981
|
+
this.viewer.scene.requestRender();
|
|
6982
|
+
}
|
|
6983
|
+
const circumference = Math.PI * (nodeRadius + ringAnimation.inner);
|
|
6984
|
+
const filledFraction = 1 - ringAnimation.arcOpen * (1 - arcFraction);
|
|
6985
|
+
circle.setAttribute("stroke-dasharray", `${circumference * filledFraction} ${circumference * (1 - filledFraction)}`);
|
|
6986
|
+
circle.setAttribute("transform", `rotate(${(leaderAngle + ringAnimation.arcOffset - Math.PI * filledFraction) * 180 / Math.PI} ${nodeRadius} ${nodeRadius})`);
|
|
6987
|
+
const cut = Math.min(radius, length);
|
|
6988
|
+
const startX = frame.anchorX + (length ? dx / length * cut : 0);
|
|
6989
|
+
const startY = frame.anchorY + (length ? dy / length * cut : 0);
|
|
6990
|
+
const screenDx = (endX - startX) * coordinates.scaleX;
|
|
6991
|
+
const screenDy = (endY - startY) * coordinates.scaleY;
|
|
6992
|
+
lineEle.style.width = `${2 * coordinates.scaleX}px`;
|
|
6993
|
+
lineEle.style.left = `${coordinates.x + (startX - 1) * coordinates.scaleX}px`;
|
|
6994
|
+
lineEle.style.top = `${coordinates.y + startY * coordinates.scaleY}px`;
|
|
6995
|
+
lineEle.style.height = `${Math.hypot(screenDx, screenDy)}px`;
|
|
6996
|
+
lineEle.style.transform = `rotate(${Math.atan2(screenDy, screenDx) - Math.PI / 2}rad)`;
|
|
6997
|
+
lineEle.style.boxShadow = radius ? "none" : "0 0 5px rgba(0, 0, 0, 0.3)";
|
|
6998
|
+
lineEle.style.transformOrigin = "top center";
|
|
6999
|
+
lineEle.style.opacity = String(opacity * (normalLeader ? 1 : cursorMotion.amount));
|
|
7000
|
+
lineEle.style.zIndex = String(placement.zIndex);
|
|
5909
7001
|
}
|
|
5910
7002
|
};
|
|
5911
|
-
|
|
5912
|
-
|
|
7003
|
+
ele.style.display = "flex";
|
|
7004
|
+
ele.style.visibility = "hidden";
|
|
7005
|
+
this.renderRemoval = layer.Add({ project, apply });
|
|
5913
7006
|
// If terrain changes then we'll re-call createLabel as the position may have changed.
|
|
5914
7007
|
let terrTimeout = null;
|
|
5915
7008
|
this.terrChangedRemoval = this.viewer.scene.terrainProviderChanged.addEventListener(() => {
|
|
@@ -5929,6 +7022,9 @@ var EntityLabel;
|
|
|
5929
7022
|
* Removes label from the viewer and unsubscribes from events.
|
|
5930
7023
|
*/
|
|
5931
7024
|
removeLabel() {
|
|
7025
|
+
var _a;
|
|
7026
|
+
(_a = this.anchor) === null || _a === void 0 ? void 0 : _a.remove();
|
|
7027
|
+
this.anchor = null;
|
|
5932
7028
|
if (this.label &&
|
|
5933
7029
|
this.label.parentElement) {
|
|
5934
7030
|
this.label.parentElement.removeChild(this.label);
|
|
@@ -5954,16 +7050,44 @@ var EntityLabel;
|
|
|
5954
7050
|
* If one exists then the action is ignored.
|
|
5955
7051
|
*/
|
|
5956
7052
|
function Attach(params) {
|
|
5957
|
-
|
|
5958
|
-
|
|
7053
|
+
var _a, _b, _c;
|
|
7054
|
+
const { rego, viewer, pos3d, visualRegister, api, options, groupId } = params;
|
|
7055
|
+
if (!rego || !viewer) {
|
|
5959
7056
|
return;
|
|
5960
7057
|
}
|
|
7058
|
+
if (params.persistent == null && visualRegister) {
|
|
7059
|
+
const group = visualRegister.GetLabelGroupFor(rego.entityId, rego.menuItemId);
|
|
7060
|
+
if (group || visualRegister.GetIsLabelled({ entityId: rego.entityId, menuItemId: rego.menuItemId })) {
|
|
7061
|
+
return Attach({
|
|
7062
|
+
...params,
|
|
7063
|
+
pos3d: undefined,
|
|
7064
|
+
options: (_a = group === null || group === void 0 ? void 0 : group.options) !== null && _a !== void 0 ? _a : options,
|
|
7065
|
+
groupId: (_b = group === null || group === void 0 ? void 0 : group.groupId) !== null && _b !== void 0 ? _b : groupId,
|
|
7066
|
+
persistent: true
|
|
7067
|
+
});
|
|
7068
|
+
}
|
|
7069
|
+
}
|
|
7070
|
+
if (rego[EntityLabel.ATTACH_KEY]) {
|
|
7071
|
+
// Legacy hover callers omit ownership/options and must not reconfigure a register-owned label.
|
|
7072
|
+
if (rego[EntityLabel.ATTACH_KEY].Persistent && params.persistent !== true && !groupId) {
|
|
7073
|
+
return rego[EntityLabel.ATTACH_KEY];
|
|
7074
|
+
}
|
|
7075
|
+
rego[EntityLabel.ATTACH_KEY].Persistent = (_c = params.persistent) !== null && _c !== void 0 ? _c : Boolean(groupId);
|
|
7076
|
+
rego[EntityLabel.ATTACH_KEY].Configure(options, groupId);
|
|
7077
|
+
return rego[EntityLabel.ATTACH_KEY];
|
|
7078
|
+
}
|
|
5961
7079
|
rego[EntityLabel.ATTACH_KEY] = new Label({
|
|
5962
7080
|
rego,
|
|
5963
7081
|
viewer,
|
|
5964
7082
|
pos3d,
|
|
5965
7083
|
visualRegister,
|
|
5966
|
-
api
|
|
7084
|
+
api,
|
|
7085
|
+
options,
|
|
7086
|
+
groupId,
|
|
7087
|
+
persistent: params.persistent,
|
|
7088
|
+
transient: params.transient,
|
|
7089
|
+
hoverRingState: params.hoverRingState,
|
|
7090
|
+
visible: params.visible
|
|
5967
7091
|
});
|
|
5968
7092
|
return rego[EntityLabel.ATTACH_KEY];
|
|
5969
7093
|
}
|
|
@@ -5978,12 +7102,19 @@ var EntityLabel;
|
|
|
5978
7102
|
if (!rego && label) {
|
|
5979
7103
|
rego = label.Rego;
|
|
5980
7104
|
}
|
|
7105
|
+
if (label && label !== (rego === null || rego === void 0 ? void 0 : rego[EntityLabel.ATTACH_KEY])) {
|
|
7106
|
+
if (!label.Persistent || params.force)
|
|
7107
|
+
label.Dispose();
|
|
7108
|
+
return;
|
|
7109
|
+
}
|
|
5981
7110
|
if (!rego || !rego[EntityLabel.ATTACH_KEY]) {
|
|
5982
7111
|
if (label) {
|
|
5983
7112
|
(_a = label.Dispose) === null || _a === void 0 ? void 0 : _a.call(label);
|
|
5984
7113
|
}
|
|
5985
7114
|
return;
|
|
5986
7115
|
}
|
|
7116
|
+
if (rego[EntityLabel.ATTACH_KEY].Persistent && !params.force)
|
|
7117
|
+
return;
|
|
5987
7118
|
(_c = (_b = rego[EntityLabel.ATTACH_KEY]).Dispose) === null || _c === void 0 ? void 0 : _c.call(_b);
|
|
5988
7119
|
rego[EntityLabel.ATTACH_KEY] = null;
|
|
5989
7120
|
}
|
|
@@ -6003,6 +7134,482 @@ var EntityLabel;
|
|
|
6003
7134
|
EntityLabel.GetLabel = GetLabel;
|
|
6004
7135
|
})(EntityLabel || (EntityLabel = {}));
|
|
6005
7136
|
|
|
7137
|
+
var EntityLabelGroup;
|
|
7138
|
+
(function (EntityLabelGroup) {
|
|
7139
|
+
/*
|
|
7140
|
+
* Keeps the query response useful even when the Entity has no loaded visual yet.
|
|
7141
|
+
*/
|
|
7142
|
+
function FromEntity(entity) {
|
|
7143
|
+
var _a, _b, _c, _d;
|
|
7144
|
+
const location = (_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a.Location;
|
|
7145
|
+
const pivot = (_b = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _b === void 0 ? void 0 : _b.GeometryWorldPivot;
|
|
7146
|
+
let cartographic;
|
|
7147
|
+
if ((pivot === null || pivot === void 0 ? void 0 : pivot.length) >= 3 && pivot.every(Number.isFinite)) {
|
|
7148
|
+
cartographic = Cartographic.fromCartesian(Cartesian3.fromArray(pivot));
|
|
7149
|
+
}
|
|
7150
|
+
return {
|
|
7151
|
+
entityId: (_c = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _c === void 0 ? void 0 : _c.ID,
|
|
7152
|
+
text: (_d = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _d === void 0 ? void 0 : _d.Name,
|
|
7153
|
+
longitude: cartographic ? Math$1.toDegrees(cartographic.longitude) : location === null || location === void 0 ? void 0 : location.longitude,
|
|
7154
|
+
latitude: cartographic ? Math$1.toDegrees(cartographic.latitude) : location === null || location === void 0 ? void 0 : location.latitude,
|
|
7155
|
+
altitude: cartographic ? cartographic.height : location === null || location === void 0 ? void 0 : location.altitude,
|
|
7156
|
+
entity
|
|
7157
|
+
};
|
|
7158
|
+
}
|
|
7159
|
+
EntityLabelGroup.FromEntity = FromEntity;
|
|
7160
|
+
/*
|
|
7161
|
+
* Uses the existing DataLab grammar, discarding actions and projections that cannot return Entities.
|
|
7162
|
+
*/
|
|
7163
|
+
async function Loader(source, api, signal) {
|
|
7164
|
+
var _a;
|
|
7165
|
+
var _b;
|
|
7166
|
+
if (source.type === "provider") {
|
|
7167
|
+
return (offset, limit, signal) => source.loadPage({ offset, limit, signal });
|
|
7168
|
+
}
|
|
7169
|
+
if (source.type === "entityIds" || source.type === "json") {
|
|
7170
|
+
const items = (source.type === "entityIds" ? source.entityIds.map(entityId => ({ entityId })) :
|
|
7171
|
+
typeof source.items === "string" ? JSON.parse(source.items) : source.items);
|
|
7172
|
+
if (!Array.isArray(items)) {
|
|
7173
|
+
throw new Error("Label JSON must be an array of label items");
|
|
7174
|
+
}
|
|
7175
|
+
return async (offset, limit) => ({ items: items.slice(offset, offset + limit), hasMore: offset + limit < items.length });
|
|
7176
|
+
}
|
|
7177
|
+
if (!api) {
|
|
7178
|
+
throw new Error("DataLab label sources require API getters on the visual register");
|
|
7179
|
+
}
|
|
7180
|
+
let query;
|
|
7181
|
+
if (source.type === "queryId") {
|
|
7182
|
+
const saved = (await DataLab.Get({ api, queryId: source.queryId, req: { noCache: true, abortSignal: signal } })).query;
|
|
7183
|
+
const selection = (_a = saved === null || saved === void 0 ? void 0 : saved.PrimarySelectionCriteria) !== null && _a !== void 0 ? _a : saved === null || saved === void 0 ? void 0 : saved.PrimarySelection;
|
|
7184
|
+
if (!selection) {
|
|
7185
|
+
throw new Error("Saved label query has no selection criteria");
|
|
7186
|
+
}
|
|
7187
|
+
query = { PrimarySelection: selection };
|
|
7188
|
+
}
|
|
7189
|
+
else {
|
|
7190
|
+
query = typeof source.query === "string" ? JSON.parse(source.query) : source.query;
|
|
7191
|
+
}
|
|
7192
|
+
if (!(query === null || query === void 0 ? void 0 : query.PrimarySelection)) {
|
|
7193
|
+
throw new Error("Label query requires PrimarySelection");
|
|
7194
|
+
}
|
|
7195
|
+
query = JSON.parse(JSON.stringify(query));
|
|
7196
|
+
delete query.Action;
|
|
7197
|
+
query.PrimarySelection.Select = { Type: DataLab.ESelectType.Entity };
|
|
7198
|
+
(_b = query.PrimarySelection).OrderBy || (_b.OrderBy = "Bruce/ID");
|
|
7199
|
+
query.Expand = query.Expand ? `${query.Expand},location` : "location";
|
|
7200
|
+
return async (offset, limit, signal) => {
|
|
7201
|
+
const response = await DataLab.Run({ api, query, skip: offset, load: limit, migrated: true, req: { noCache: true, abortSignal: signal } });
|
|
7202
|
+
const entities = response.entities || response.items || [];
|
|
7203
|
+
return {
|
|
7204
|
+
items: entities.map(FromEntity),
|
|
7205
|
+
hasMore: response.totalCount != null ? offset + entities.length < response.totalCount : entities.length === limit
|
|
7206
|
+
};
|
|
7207
|
+
};
|
|
7208
|
+
}
|
|
7209
|
+
EntityLabelGroup.Loader = Loader;
|
|
7210
|
+
/*
|
|
7211
|
+
* Snapshot settings without dropping Calculator's optional runtime function values.
|
|
7212
|
+
*/
|
|
7213
|
+
function copyOptions(value) {
|
|
7214
|
+
if (Array.isArray(value)) {
|
|
7215
|
+
return value.map(copyOptions);
|
|
7216
|
+
}
|
|
7217
|
+
if (value && typeof value === "object") {
|
|
7218
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, copyOptions(item)]));
|
|
7219
|
+
}
|
|
7220
|
+
return value;
|
|
7221
|
+
}
|
|
7222
|
+
/*
|
|
7223
|
+
* Evaluate source attributes during register refresh, never in the camera-frame layout loop.
|
|
7224
|
+
*/
|
|
7225
|
+
function resolveIcon(value, entity) {
|
|
7226
|
+
if (!Array.isArray(value))
|
|
7227
|
+
return value;
|
|
7228
|
+
try {
|
|
7229
|
+
return Calculator.GetString(value, entity || {});
|
|
7230
|
+
}
|
|
7231
|
+
catch {
|
|
7232
|
+
// An invalid expression on one item must not prevent the remaining labels from rendering.
|
|
7233
|
+
return null;
|
|
7234
|
+
}
|
|
7235
|
+
}
|
|
7236
|
+
/*
|
|
7237
|
+
* Owns membership independently of manual labelled states, with atomic replacement and stale-load cancellation.
|
|
7238
|
+
*/
|
|
7239
|
+
class Manager {
|
|
7240
|
+
constructor(register, changed = () => { }) {
|
|
7241
|
+
this.register = register;
|
|
7242
|
+
this.changed = changed;
|
|
7243
|
+
this.active = new Map();
|
|
7244
|
+
this.pending = new Map();
|
|
7245
|
+
this.standalone = new Map();
|
|
7246
|
+
this.standaloneItems = new Map();
|
|
7247
|
+
this.members = new Map();
|
|
7248
|
+
this.definitions = new Map();
|
|
7249
|
+
this.visibility = new Map();
|
|
7250
|
+
this.errors = new Map();
|
|
7251
|
+
}
|
|
7252
|
+
notify() {
|
|
7253
|
+
try {
|
|
7254
|
+
this.changed();
|
|
7255
|
+
}
|
|
7256
|
+
catch (error) {
|
|
7257
|
+
console.error("Label group observer failed", error);
|
|
7258
|
+
}
|
|
7259
|
+
}
|
|
7260
|
+
Refresh(id) {
|
|
7261
|
+
const definition = this.definitions.get(id);
|
|
7262
|
+
if (!definition)
|
|
7263
|
+
return Promise.reject(new Error(`Unknown label group: ${id}`));
|
|
7264
|
+
return this.Set({ ...definition, visible: this.visibility.get(id) });
|
|
7265
|
+
}
|
|
7266
|
+
SetVisible(id, visible) {
|
|
7267
|
+
var _a;
|
|
7268
|
+
if (!this.definitions.has(id))
|
|
7269
|
+
return false;
|
|
7270
|
+
if (this.visibility.get(id) === visible)
|
|
7271
|
+
return true;
|
|
7272
|
+
this.visibility.set(id, visible);
|
|
7273
|
+
this.refresh(new Set(((_a = this.active.get(id)) === null || _a === void 0 ? void 0 : _a.items.keys()) || []));
|
|
7274
|
+
this.notify();
|
|
7275
|
+
return true;
|
|
7276
|
+
}
|
|
7277
|
+
async Set(definition) {
|
|
7278
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
7279
|
+
if (!(definition === null || definition === void 0 ? void 0 : definition.id) || !definition.source) {
|
|
7280
|
+
throw new Error("Label group requires id and source");
|
|
7281
|
+
}
|
|
7282
|
+
definition = { ...definition, options: copyOptions(definition.options) };
|
|
7283
|
+
const pageSize = (_a = definition.pageSize) !== null && _a !== void 0 ? _a : 500;
|
|
7284
|
+
const maximum = (_b = definition.maxItems) !== null && _b !== void 0 ? _b : 50000;
|
|
7285
|
+
if (!Number.isInteger(pageSize) || pageSize < 1 || !Number.isInteger(maximum) || maximum < 1) {
|
|
7286
|
+
throw new Error("Label pageSize and maxItems must be positive integers");
|
|
7287
|
+
}
|
|
7288
|
+
ValidateOptions(definition.options);
|
|
7289
|
+
(_c = this.pending.get(definition.id)) === null || _c === void 0 ? void 0 : _c.abort();
|
|
7290
|
+
const controller = new AbortController();
|
|
7291
|
+
this.pending.set(definition.id, controller);
|
|
7292
|
+
this.definitions.set(definition.id, definition);
|
|
7293
|
+
const wasVisible = this.visibility.get(definition.id);
|
|
7294
|
+
this.visibility.set(definition.id, (_e = (_d = definition.visible) !== null && _d !== void 0 ? _d : this.visibility.get(definition.id)) !== null && _e !== void 0 ? _e : true);
|
|
7295
|
+
if (wasVisible != null && wasVisible !== this.visibility.get(definition.id)) {
|
|
7296
|
+
this.refresh(new Set(((_f = this.active.get(definition.id)) === null || _f === void 0 ? void 0 : _f.items.keys()) || []));
|
|
7297
|
+
}
|
|
7298
|
+
this.errors.delete(definition.id);
|
|
7299
|
+
this.notify();
|
|
7300
|
+
const cancelled = () => ({ id: definition.id, entityIds: [], truncated: false, cancelled: true });
|
|
7301
|
+
const items = new Map();
|
|
7302
|
+
let truncated = false;
|
|
7303
|
+
try {
|
|
7304
|
+
const load = await Loader(definition.source, (_g = this.register.ApiGetters) === null || _g === void 0 ? void 0 : _g.GetBruceApi(), controller.signal);
|
|
7305
|
+
let offset = 0;
|
|
7306
|
+
for (let pageNumber = 0;; pageNumber++) {
|
|
7307
|
+
if (controller.signal.aborted || this.register.Disposed) {
|
|
7308
|
+
return cancelled();
|
|
7309
|
+
}
|
|
7310
|
+
if (pageNumber >= 10000) {
|
|
7311
|
+
throw new Error("Label source exceeded 10000 pages");
|
|
7312
|
+
}
|
|
7313
|
+
const page = await load(offset, Math.min(pageSize, maximum - items.size), controller.signal);
|
|
7314
|
+
if (controller.signal.aborted || this.register.Disposed)
|
|
7315
|
+
return cancelled();
|
|
7316
|
+
if (!Array.isArray(page === null || page === void 0 ? void 0 : page.items)) {
|
|
7317
|
+
throw new Error("Label source returned an invalid page");
|
|
7318
|
+
}
|
|
7319
|
+
for (const item of page.items) {
|
|
7320
|
+
if (!item || typeof item.entityId !== "string" || !item.entityId) {
|
|
7321
|
+
throw new Error("Label item requires entityId");
|
|
7322
|
+
}
|
|
7323
|
+
if (!items.has(item.entityId) && items.size >= maximum) {
|
|
7324
|
+
truncated = true;
|
|
7325
|
+
break;
|
|
7326
|
+
}
|
|
7327
|
+
items.set(item.entityId, item);
|
|
7328
|
+
}
|
|
7329
|
+
if (!page.hasMore) {
|
|
7330
|
+
break;
|
|
7331
|
+
}
|
|
7332
|
+
if (items.size >= maximum) {
|
|
7333
|
+
truncated = true;
|
|
7334
|
+
break;
|
|
7335
|
+
}
|
|
7336
|
+
const next = (_h = page.nextOffset) !== null && _h !== void 0 ? _h : offset + page.items.length;
|
|
7337
|
+
if (!Number.isSafeInteger(next) || next <= offset) {
|
|
7338
|
+
throw new Error("Label source pagination did not advance");
|
|
7339
|
+
}
|
|
7340
|
+
offset = next;
|
|
7341
|
+
}
|
|
7342
|
+
const previous = this.active.get(definition.id);
|
|
7343
|
+
const affected = new Set([...((previous === null || previous === void 0 ? void 0 : previous.items.keys()) || []), ...items.keys()]);
|
|
7344
|
+
this.unindex(definition.id);
|
|
7345
|
+
// Reinsertion makes the most recently committed matching group the presentation owner.
|
|
7346
|
+
this.active.delete(definition.id);
|
|
7347
|
+
this.active.set(definition.id, { definition, items, truncated });
|
|
7348
|
+
for (const id of items.keys()) {
|
|
7349
|
+
if (!this.members.has(id)) {
|
|
7350
|
+
this.members.set(id, new Set());
|
|
7351
|
+
}
|
|
7352
|
+
this.members.get(id).add(definition.id);
|
|
7353
|
+
}
|
|
7354
|
+
this.refresh(affected);
|
|
7355
|
+
return { id: definition.id, entityIds: Array.from(items.keys()), truncated, cancelled: false };
|
|
7356
|
+
}
|
|
7357
|
+
catch (error) {
|
|
7358
|
+
if (controller.signal.aborted || this.register.Disposed) {
|
|
7359
|
+
return cancelled();
|
|
7360
|
+
}
|
|
7361
|
+
this.errors.set(definition.id, error instanceof Error ? error.message : String(error));
|
|
7362
|
+
throw error;
|
|
7363
|
+
}
|
|
7364
|
+
finally {
|
|
7365
|
+
if (this.pending.get(definition.id) === controller) {
|
|
7366
|
+
this.pending.delete(definition.id);
|
|
7367
|
+
this.notify();
|
|
7368
|
+
}
|
|
7369
|
+
}
|
|
7370
|
+
}
|
|
7371
|
+
Get() {
|
|
7372
|
+
return Array.from(this.definitions.keys(), id => {
|
|
7373
|
+
var _a;
|
|
7374
|
+
const group = this.active.get(id);
|
|
7375
|
+
return {
|
|
7376
|
+
id,
|
|
7377
|
+
entityIds: Array.from((group === null || group === void 0 ? void 0 : group.items.keys()) || []),
|
|
7378
|
+
truncated: (_a = group === null || group === void 0 ? void 0 : group.truncated) !== null && _a !== void 0 ? _a : false,
|
|
7379
|
+
cancelled: false,
|
|
7380
|
+
visible: this.visibility.get(id) !== false,
|
|
7381
|
+
loading: this.pending.has(id),
|
|
7382
|
+
error: this.errors.get(id)
|
|
7383
|
+
};
|
|
7384
|
+
});
|
|
7385
|
+
}
|
|
7386
|
+
Has(entityId, menuItemId) {
|
|
7387
|
+
for (const id of this.members.get(entityId) || []) {
|
|
7388
|
+
const group = this.active.get(id);
|
|
7389
|
+
if (group && this.visibility.get(id) !== false && (!group.definition.menuItemId || group.definition.menuItemId === menuItemId)) {
|
|
7390
|
+
return true;
|
|
7391
|
+
}
|
|
7392
|
+
}
|
|
7393
|
+
return false;
|
|
7394
|
+
}
|
|
7395
|
+
Resolve(entityId, menuItemId) {
|
|
7396
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
7397
|
+
const ids = Array.from(this.members.get(entityId) || []).reverse();
|
|
7398
|
+
for (const id of ids) {
|
|
7399
|
+
const group = this.active.get(id);
|
|
7400
|
+
if (this.visibility.get(id) === false) {
|
|
7401
|
+
continue;
|
|
7402
|
+
}
|
|
7403
|
+
if (group.definition.menuItemId && group.definition.menuItemId !== menuItemId) {
|
|
7404
|
+
continue;
|
|
7405
|
+
}
|
|
7406
|
+
const item = group.items.get(entityId);
|
|
7407
|
+
const entity = (_a = item.entity) !== null && _a !== void 0 ? _a : { Bruce: { ID: entityId, Name: item.text } };
|
|
7408
|
+
let iconPriority = (_b = group.definition.options) === null || _b === void 0 ? void 0 : _b.iconPriority;
|
|
7409
|
+
if (Array.isArray(iconPriority)) {
|
|
7410
|
+
try {
|
|
7411
|
+
iconPriority = Calculator.GetNumber(iconPriority, entity);
|
|
7412
|
+
}
|
|
7413
|
+
catch {
|
|
7414
|
+
iconPriority = null;
|
|
7415
|
+
}
|
|
7416
|
+
}
|
|
7417
|
+
let iconScale = (_c = group.definition.options) === null || _c === void 0 ? void 0 : _c.iconScale;
|
|
7418
|
+
if (Array.isArray(iconScale)) {
|
|
7419
|
+
try {
|
|
7420
|
+
iconScale = Calculator.GetNumber(iconScale, entity);
|
|
7421
|
+
}
|
|
7422
|
+
catch {
|
|
7423
|
+
iconScale = null;
|
|
7424
|
+
}
|
|
7425
|
+
if (!Number.isFinite(iconScale)) {
|
|
7426
|
+
iconScale = 1;
|
|
7427
|
+
}
|
|
7428
|
+
}
|
|
7429
|
+
if (iconScale != null) {
|
|
7430
|
+
iconScale = Math.max(0, iconScale);
|
|
7431
|
+
}
|
|
7432
|
+
const textSetting = (_d = group.definition.options) === null || _d === void 0 ? void 0 : _d.text;
|
|
7433
|
+
let resolvedText = resolveIcon(textSetting, entity);
|
|
7434
|
+
const expression = Array.isArray(textSetting);
|
|
7435
|
+
if (expression && !(resolvedText === null || resolvedText === void 0 ? void 0 : resolvedText.trim())) {
|
|
7436
|
+
resolvedText = undefined;
|
|
7437
|
+
}
|
|
7438
|
+
const fallbackText = [item.text, (_e = entity.Bruce) === null || _e === void 0 ? void 0 : _e.Name, (_f = this.register.GetRego({ entityId, menuItemId })) === null || _f === void 0 ? void 0 : _f.name, entityId]
|
|
7439
|
+
.find(text => typeof text === "string" && text.trim().length > 0);
|
|
7440
|
+
const options = {
|
|
7441
|
+
...group.definition.options,
|
|
7442
|
+
text: resolvedText !== null && resolvedText !== void 0 ? resolvedText : fallbackText,
|
|
7443
|
+
iconScale: iconScale,
|
|
7444
|
+
icon: resolveIcon((_g = group.definition.options) === null || _g === void 0 ? void 0 : _g.icon, entity),
|
|
7445
|
+
iconUrl: resolveIcon((_h = group.definition.options) === null || _h === void 0 ? void 0 : _h.iconUrl, entity),
|
|
7446
|
+
iconClass: resolveIcon((_j = group.definition.options) === null || _j === void 0 ? void 0 : _j.iconClass, entity),
|
|
7447
|
+
iconColor: resolveIcon((_k = group.definition.options) === null || _k === void 0 ? void 0 : _k.iconColor, entity),
|
|
7448
|
+
iconBackground: (_l = group.definition.options) === null || _l === void 0 ? void 0 : _l.iconBackground,
|
|
7449
|
+
iconPriority: Number.isFinite(iconPriority) ? iconPriority : item.iconPriority,
|
|
7450
|
+
iconStatus: (_o = resolveIcon((_m = group.definition.options) === null || _m === void 0 ? void 0 : _m.iconStatus, entity)) !== null && _o !== void 0 ? _o : item.iconStatus
|
|
7451
|
+
};
|
|
7452
|
+
if (options.attributes && item.entity && !(expression && resolvedText != null)) {
|
|
7453
|
+
options.text = [
|
|
7454
|
+
(_r = (_p = options.text) !== null && _p !== void 0 ? _p : (_q = item.entity.Bruce) === null || _q === void 0 ? void 0 : _q.Name) !== null && _r !== void 0 ? _r : entityId,
|
|
7455
|
+
...options.attributes.map(path => Entity$1.GetValue({ entity: item.entity, path })).filter(value => value != null).map(String)
|
|
7456
|
+
].join("\n");
|
|
7457
|
+
}
|
|
7458
|
+
return { groupId: id, options, item };
|
|
7459
|
+
}
|
|
7460
|
+
return null;
|
|
7461
|
+
}
|
|
7462
|
+
RefreshEntity(entityId) {
|
|
7463
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
7464
|
+
const match = this.Resolve(entityId);
|
|
7465
|
+
const registered = this.register.GetRego({ entityId });
|
|
7466
|
+
if (!registered && match && this.standalone.has(entityId) && this.standaloneItems.get(entityId) === match.item) {
|
|
7467
|
+
this.standalone.get(entityId).Configure({ ...match.options, text: (_b = (_a = match.options.text) !== null && _a !== void 0 ? _a : match.item.text) !== null && _b !== void 0 ? _b : entityId }, match.groupId);
|
|
7468
|
+
return;
|
|
7469
|
+
}
|
|
7470
|
+
(_c = this.standalone.get(entityId)) === null || _c === void 0 ? void 0 : _c.Dispose();
|
|
7471
|
+
this.standalone.delete(entityId);
|
|
7472
|
+
this.standaloneItems.delete(entityId);
|
|
7473
|
+
if (this.register.Disposed || registered) {
|
|
7474
|
+
return;
|
|
7475
|
+
}
|
|
7476
|
+
if (!match) {
|
|
7477
|
+
return;
|
|
7478
|
+
}
|
|
7479
|
+
const item = match.item;
|
|
7480
|
+
if (![item.longitude, item.latitude, (_d = item.altitude) !== null && _d !== void 0 ? _d : 0].every(Number.isFinite) || Math.abs(item.latitude) > 90) {
|
|
7481
|
+
return;
|
|
7482
|
+
}
|
|
7483
|
+
const visual = new Entity({
|
|
7484
|
+
position: Cartesian3.fromDegrees(item.longitude, item.latitude, item.altitude || 0),
|
|
7485
|
+
point: { heightReference: HeightReference.NONE, pixelSize: 0 }
|
|
7486
|
+
});
|
|
7487
|
+
this.standalone.set(entityId, new EntityLabel.Label({
|
|
7488
|
+
viewer: this.register.Viewer,
|
|
7489
|
+
visualRegister: this.register,
|
|
7490
|
+
api: (_e = this.register.ApiGetters) === null || _e === void 0 ? void 0 : _e.GetBruceApi(),
|
|
7491
|
+
rego: { entityId, visual, name: item.text || entityId, priority: 0 },
|
|
7492
|
+
options: { ...match.options, text: (_g = (_f = match.options.text) !== null && _f !== void 0 ? _f : item.text) !== null && _g !== void 0 ? _g : entityId },
|
|
7493
|
+
groupId: match.groupId,
|
|
7494
|
+
visible: () => {
|
|
7495
|
+
const state = this.register.GetState({ entityId });
|
|
7496
|
+
return !state.hidden && (!this.register.GetIsIsolatedAny() || state.isolated || this.register.GetIsolationOpacity() != null);
|
|
7497
|
+
}
|
|
7498
|
+
}));
|
|
7499
|
+
this.standaloneItems.set(entityId, item);
|
|
7500
|
+
}
|
|
7501
|
+
Remove(id) {
|
|
7502
|
+
var _a;
|
|
7503
|
+
const known = this.definitions.delete(id);
|
|
7504
|
+
this.visibility.delete(id);
|
|
7505
|
+
this.errors.delete(id);
|
|
7506
|
+
(_a = this.pending.get(id)) === null || _a === void 0 ? void 0 : _a.abort();
|
|
7507
|
+
this.pending.delete(id);
|
|
7508
|
+
const group = this.active.get(id);
|
|
7509
|
+
if (!group) {
|
|
7510
|
+
if (known) {
|
|
7511
|
+
this.notify();
|
|
7512
|
+
return;
|
|
7513
|
+
}
|
|
7514
|
+
}
|
|
7515
|
+
this.unindex(id);
|
|
7516
|
+
this.active.delete(id);
|
|
7517
|
+
this.refresh(new Set(group.items.keys()));
|
|
7518
|
+
this.notify();
|
|
7519
|
+
}
|
|
7520
|
+
Clear() {
|
|
7521
|
+
for (const controller of this.pending.values()) {
|
|
7522
|
+
controller.abort();
|
|
7523
|
+
}
|
|
7524
|
+
this.pending.clear();
|
|
7525
|
+
const affected = new Set(this.members.keys());
|
|
7526
|
+
this.active.clear();
|
|
7527
|
+
this.members.clear();
|
|
7528
|
+
this.definitions.clear();
|
|
7529
|
+
this.visibility.clear();
|
|
7530
|
+
this.errors.clear();
|
|
7531
|
+
this.refresh(affected);
|
|
7532
|
+
this.notify();
|
|
7533
|
+
}
|
|
7534
|
+
Dispose() {
|
|
7535
|
+
for (const controller of this.pending.values()) {
|
|
7536
|
+
controller.abort();
|
|
7537
|
+
}
|
|
7538
|
+
for (const label of this.standalone.values()) {
|
|
7539
|
+
label.Dispose();
|
|
7540
|
+
}
|
|
7541
|
+
this.pending.clear();
|
|
7542
|
+
this.standalone.clear();
|
|
7543
|
+
this.standaloneItems.clear();
|
|
7544
|
+
this.active.clear();
|
|
7545
|
+
this.members.clear();
|
|
7546
|
+
this.definitions.clear();
|
|
7547
|
+
this.visibility.clear();
|
|
7548
|
+
this.errors.clear();
|
|
7549
|
+
}
|
|
7550
|
+
unindex(groupId) {
|
|
7551
|
+
var _a;
|
|
7552
|
+
for (const id of ((_a = this.active.get(groupId)) === null || _a === void 0 ? void 0 : _a.items.keys()) || []) {
|
|
7553
|
+
const groups = this.members.get(id);
|
|
7554
|
+
groups === null || groups === void 0 ? void 0 : groups.delete(groupId);
|
|
7555
|
+
if (!(groups === null || groups === void 0 ? void 0 : groups.size)) {
|
|
7556
|
+
this.members.delete(id);
|
|
7557
|
+
}
|
|
7558
|
+
}
|
|
7559
|
+
}
|
|
7560
|
+
refresh(ids) {
|
|
7561
|
+
var _a;
|
|
7562
|
+
// Creation drains through the register's bounded update queue, including source-only labels.
|
|
7563
|
+
for (const id of ids) {
|
|
7564
|
+
(_a = this.standalone.get(id)) === null || _a === void 0 ? void 0 : _a.Dispose();
|
|
7565
|
+
this.standalone.delete(id);
|
|
7566
|
+
this.standaloneItems.delete(id);
|
|
7567
|
+
}
|
|
7568
|
+
this.register.ForceUpdate({ entityIds: Array.from(ids) });
|
|
7569
|
+
this.register.Viewer.scene.requestRender();
|
|
7570
|
+
}
|
|
7571
|
+
}
|
|
7572
|
+
EntityLabelGroup.Manager = Manager;
|
|
7573
|
+
/*
|
|
7574
|
+
* Rejects invalid geometry settings before they can create unbounded spatial-index work.
|
|
7575
|
+
*/
|
|
7576
|
+
function ValidateOptions(options) {
|
|
7577
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
7578
|
+
if (!options) {
|
|
7579
|
+
return;
|
|
7580
|
+
}
|
|
7581
|
+
if (options.iconBackground != null && typeof options.iconBackground !== "boolean") {
|
|
7582
|
+
throw new Error("Label iconBackground must be a boolean");
|
|
7583
|
+
}
|
|
7584
|
+
for (const value of [options.icon, options.iconUrl, options.iconClass, options.iconColor, options.text]) {
|
|
7585
|
+
if (value != null && typeof value !== "string" && !Array.isArray(value)) {
|
|
7586
|
+
throw new Error("Label icons must be strings or Calculator field arrays");
|
|
7587
|
+
}
|
|
7588
|
+
}
|
|
7589
|
+
if (options.iconScale != null && !Array.isArray(options.iconScale) && !Number.isFinite(options.iconScale)) {
|
|
7590
|
+
throw new Error("Label iconScale must be finite or a Calculator field array");
|
|
7591
|
+
}
|
|
7592
|
+
if (options.iconPriority != null && !Array.isArray(options.iconPriority) && !Number.isFinite(options.iconPriority)) {
|
|
7593
|
+
throw new Error("Label iconPriority must be finite or a Calculator field array");
|
|
7594
|
+
}
|
|
7595
|
+
for (const value of [options.iconSize, options.leaderLength, options.near, options.far, (_a = options.cluster) === null || _a === void 0 ? void 0 : _a.pixelRange, (_b = options.pointerAvoidance) === null || _b === void 0 ? void 0 : _b.gap, (_c = options.pointerAvoidance) === null || _c === void 0 ? void 0 : _c.delay, (_d = options.pointerAvoidance) === null || _d === void 0 ? void 0 : _d.transitionDuration, (_e = options.anchorRing) === null || _e === void 0 ? void 0 : _e.nearDistance, (_f = options.anchorRing) === null || _f === void 0 ? void 0 : _f.nearRadius, (_g = options.anchorRing) === null || _g === void 0 ? void 0 : _g.hoverDistance, (_h = options.anchorRing) === null || _h === void 0 ? void 0 : _h.hoverRadius, (_j = options.anchorRing) === null || _j === void 0 ? void 0 : _j.transitionDuration]) {
|
|
7596
|
+
if (value != null && (!Number.isFinite(value) || value < 0)) {
|
|
7597
|
+
throw new Error("Label dimensions and distances must be finite and nonnegative");
|
|
7598
|
+
}
|
|
7599
|
+
}
|
|
7600
|
+
if (((_k = options.near) !== null && _k !== void 0 ? _k : 0) > ((_l = options.far) !== null && _l !== void 0 ? _l : Infinity)) {
|
|
7601
|
+
throw new Error("Label near must not exceed far");
|
|
7602
|
+
}
|
|
7603
|
+
if (options.overlapOpacity != null && (!Number.isFinite(options.overlapOpacity) || options.overlapOpacity < 0 || options.overlapOpacity > 1)) {
|
|
7604
|
+
throw new Error("Label overlapOpacity must be between zero and one");
|
|
7605
|
+
}
|
|
7606
|
+
if (((_m = options.cluster) === null || _m === void 0 ? void 0 : _m.minSize) != null && (!Number.isInteger(options.cluster.minSize) || options.cluster.minSize < 2)) {
|
|
7607
|
+
throw new Error("Label cluster minSize must be an integer of at least two");
|
|
7608
|
+
}
|
|
7609
|
+
}
|
|
7610
|
+
EntityLabelGroup.ValidateOptions = ValidateOptions;
|
|
7611
|
+
})(EntityLabelGroup || (EntityLabelGroup = {}));
|
|
7612
|
+
|
|
6006
7613
|
/**
|
|
6007
7614
|
* Utility for rendering parabolas between two points on the map.
|
|
6008
7615
|
*/
|
|
@@ -7497,7 +9104,7 @@ function updateEntityShow(viewer, rego, show) {
|
|
|
7497
9104
|
}
|
|
7498
9105
|
}
|
|
7499
9106
|
function updateEntity(viewer, entityId, register, refresh, cache = {}) {
|
|
7500
|
-
var _a;
|
|
9107
|
+
var _a, _b, _c;
|
|
7501
9108
|
if (!entityId) {
|
|
7502
9109
|
// What are we doing?
|
|
7503
9110
|
return;
|
|
@@ -7610,20 +9217,25 @@ function updateEntity(viewer, entityId, register, refresh, cache = {}) {
|
|
|
7610
9217
|
}
|
|
7611
9218
|
}
|
|
7612
9219
|
}
|
|
7613
|
-
|
|
9220
|
+
const labelGroup = register.GetLabelGroupFor(rego.entityId, rego.menuItemId);
|
|
9221
|
+
if ((state.labelled || labelGroup) && visible) {
|
|
7614
9222
|
EntityLabel.Attach({
|
|
7615
9223
|
rego,
|
|
7616
9224
|
viewer,
|
|
7617
|
-
api: register.ApiGetters.GetBruceApi(),
|
|
7618
|
-
visualRegister: register
|
|
9225
|
+
api: (_b = register.ApiGetters) === null || _b === void 0 ? void 0 : _b.GetBruceApi(),
|
|
9226
|
+
visualRegister: register,
|
|
9227
|
+
options: labelGroup === null || labelGroup === void 0 ? void 0 : labelGroup.options,
|
|
9228
|
+
groupId: labelGroup === null || labelGroup === void 0 ? void 0 : labelGroup.groupId,
|
|
9229
|
+
persistent: true
|
|
7619
9230
|
});
|
|
7620
9231
|
}
|
|
7621
|
-
else {
|
|
9232
|
+
else if (!visible || ((_c = EntityLabel.GetLabel({ rego })) === null || _c === void 0 ? void 0 : _c.Persistent)) {
|
|
7622
9233
|
EntityLabel.Detatch({
|
|
7623
|
-
rego
|
|
9234
|
+
rego, force: true
|
|
7624
9235
|
});
|
|
7625
9236
|
}
|
|
7626
9237
|
}
|
|
9238
|
+
register.RefreshLabelGroupEntity(entityId);
|
|
7627
9239
|
}
|
|
7628
9240
|
function markEntity(register, rego, visual, ignoreParent) {
|
|
7629
9241
|
const vis = visual;
|
|
@@ -7738,6 +9350,7 @@ var VisualsRegister;
|
|
|
7738
9350
|
}
|
|
7739
9351
|
return this.onUpdate;
|
|
7740
9352
|
}
|
|
9353
|
+
get OnLabelGroupsChanged() { return this.labelGroupsChanged; }
|
|
7741
9354
|
constructor(params) {
|
|
7742
9355
|
this.id = ObjectUtils.UId();
|
|
7743
9356
|
this.disposed = false;
|
|
@@ -7776,10 +9389,35 @@ var VisualsRegister;
|
|
|
7776
9389
|
this.menuItemSources = new Map();
|
|
7777
9390
|
// Set when the data source order no longer matches the zIndexes seen, so it is re-applied once.
|
|
7778
9391
|
this.sourceOrderDirty = false;
|
|
9392
|
+
this.labelGroupsChanged = new BruceEvent();
|
|
9393
|
+
this.hoverRingState = {};
|
|
9394
|
+
this.retiringHoverLabels = new Map();
|
|
7779
9395
|
/*
|
|
7780
9396
|
* Tilesets in the scene, keyed by the tileset itself.
|
|
7781
9397
|
*/
|
|
7782
9398
|
this.tilesets = new Map();
|
|
9399
|
+
this.ClearLabelHover = () => {
|
|
9400
|
+
if (!this.labelHover && !this.hoverLabel) {
|
|
9401
|
+
return;
|
|
9402
|
+
}
|
|
9403
|
+
this.ReleaseHoverLabel();
|
|
9404
|
+
this.labelHover = undefined;
|
|
9405
|
+
clearTimeout(this.hoverRingReset);
|
|
9406
|
+
this.hoverRingReset = setTimeout(() => { this.hoverRingState = {}; }, 400);
|
|
9407
|
+
const doc = this.viewer.container.ownerDocument;
|
|
9408
|
+
doc.removeEventListener("pointermove", this.CheckLabelHoverBounds, true);
|
|
9409
|
+
this.viewer.container.removeEventListener("pointerleave", this.CheckLabelHoverBounds);
|
|
9410
|
+
doc.defaultView.removeEventListener("blur", this.ClearLabelHover);
|
|
9411
|
+
if (!this.viewer.isDestroyed()) {
|
|
9412
|
+
this.viewer.scene.requestRender();
|
|
9413
|
+
}
|
|
9414
|
+
};
|
|
9415
|
+
this.CheckLabelHoverBounds = (event) => {
|
|
9416
|
+
const bounds = this.viewer.canvas.getBoundingClientRect();
|
|
9417
|
+
if (event.clientX < bounds.left || event.clientX >= bounds.right || event.clientY < bounds.top || event.clientY >= bounds.bottom) {
|
|
9418
|
+
this.ClearLabelHover();
|
|
9419
|
+
}
|
|
9420
|
+
};
|
|
7783
9421
|
this.viewer = params.viewer;
|
|
7784
9422
|
this.apiGetters = params.apiGetters;
|
|
7785
9423
|
this.cameraCullerDispose = VisualRegisterCuller.Monitor({
|
|
@@ -7975,6 +9613,7 @@ var VisualsRegister;
|
|
|
7975
9613
|
const BATCH_SIZE = 300;
|
|
7976
9614
|
if (this.updateQueue.length) {
|
|
7977
9615
|
this.updateQueueInterval = setInterval(() => {
|
|
9616
|
+
var _a;
|
|
7978
9617
|
if (this.Disposed || (!this.viewer || !this.viewer.scene || this.viewer.isDestroyed())) {
|
|
7979
9618
|
clearInterval(this.updateQueueInterval);
|
|
7980
9619
|
this.updateQueueInterval = null;
|
|
@@ -7987,6 +9626,8 @@ var VisualsRegister;
|
|
|
7987
9626
|
this.updateQueueIds.delete(entityId);
|
|
7988
9627
|
// Check if still registered.
|
|
7989
9628
|
if (entityId && (!this.rego[entityId] || !this.rego[entityId].length)) {
|
|
9629
|
+
(_a = this.labelGroups) === null || _a === void 0 ? void 0 : _a.RefreshEntity(entityId);
|
|
9630
|
+
delete this.updateQueueSettings[entityId];
|
|
7990
9631
|
continue;
|
|
7991
9632
|
}
|
|
7992
9633
|
updateEntity(this.viewer, entityId, this, this.updateQueueSettings[entityId] == null ? false : this.updateQueueSettings[entityId], cache);
|
|
@@ -8644,8 +10285,23 @@ var VisualsRegister;
|
|
|
8644
10285
|
markEntity(this, rego, rego.visual, false);
|
|
8645
10286
|
}
|
|
8646
10287
|
Dispose() {
|
|
8647
|
-
var _a, _b, _c, _d;
|
|
8648
|
-
|
|
10288
|
+
var _a, _b, _c, _d, _e;
|
|
10289
|
+
this.ClearLabelHover();
|
|
10290
|
+
clearTimeout(this.hoverRingReset);
|
|
10291
|
+
this.hoverRingState = {};
|
|
10292
|
+
for (const { label, timer } of this.retiringHoverLabels.values()) {
|
|
10293
|
+
clearTimeout(timer);
|
|
10294
|
+
EntityLabel.Detatch({ label });
|
|
10295
|
+
}
|
|
10296
|
+
this.retiringHoverLabels.clear();
|
|
10297
|
+
(_a = this.labelGroups) === null || _a === void 0 ? void 0 : _a.Dispose();
|
|
10298
|
+
this.labelGroupsChanged.Clear();
|
|
10299
|
+
for (const regos of Object.values(this.rego)) {
|
|
10300
|
+
for (const rego of regos)
|
|
10301
|
+
EntityLabel.Detatch({ rego, force: true });
|
|
10302
|
+
}
|
|
10303
|
+
EntityLabel.SetContainer(this.viewer);
|
|
10304
|
+
(_b = this.cameraCullerDispose) === null || _b === void 0 ? void 0 : _b.call(this, {
|
|
8649
10305
|
uncull: false
|
|
8650
10306
|
});
|
|
8651
10307
|
this.cameraCullerDispose = null;
|
|
@@ -8661,7 +10317,7 @@ var VisualsRegister;
|
|
|
8661
10317
|
this.disposed = true;
|
|
8662
10318
|
for (const record of Array.from(this.menuItemSources.values())) {
|
|
8663
10319
|
try {
|
|
8664
|
-
(
|
|
10320
|
+
(_d = (_c = this.viewer) === null || _c === void 0 ? void 0 : _c.dataSources) === null || _d === void 0 ? void 0 : _d.remove(record.source, true);
|
|
8665
10321
|
}
|
|
8666
10322
|
catch (e) {
|
|
8667
10323
|
console.error(e);
|
|
@@ -8670,7 +10326,7 @@ var VisualsRegister;
|
|
|
8670
10326
|
this.menuItemSources.clear();
|
|
8671
10327
|
// Hand every tileset's show flag back to its owner, holding whatever the owner last asked for.
|
|
8672
10328
|
for (const rego of Array.from(this.tilesets.values())) {
|
|
8673
|
-
(
|
|
10329
|
+
(_e = rego.gate) === null || _e === void 0 ? void 0 : _e.Release();
|
|
8674
10330
|
}
|
|
8675
10331
|
this.tilesets.clear();
|
|
8676
10332
|
}
|
|
@@ -9622,7 +11278,7 @@ var VisualsRegister;
|
|
|
9622
11278
|
* @returns
|
|
9623
11279
|
*/
|
|
9624
11280
|
RemoveRegos(params) {
|
|
9625
|
-
var _a, _b, _c;
|
|
11281
|
+
var _a, _b, _c, _d, _e;
|
|
9626
11282
|
let { entityId, menuItemId, doUpdate, relation, retainTagIds, requestRender, doRemove, source, lazy, onComplete } = params;
|
|
9627
11283
|
if (doUpdate == null || doUpdate === undefined) {
|
|
9628
11284
|
doUpdate = true;
|
|
@@ -9643,7 +11299,7 @@ var VisualsRegister;
|
|
|
9643
11299
|
}
|
|
9644
11300
|
}
|
|
9645
11301
|
const removeOne = (entityId) => {
|
|
9646
|
-
var _a, _b;
|
|
11302
|
+
var _a, _b, _c;
|
|
9647
11303
|
const entityRegos = this.rego[entityId];
|
|
9648
11304
|
if (!entityRegos) {
|
|
9649
11305
|
return;
|
|
@@ -9658,7 +11314,7 @@ var VisualsRegister;
|
|
|
9658
11314
|
return;
|
|
9659
11315
|
}
|
|
9660
11316
|
EntityLabel.Detatch({
|
|
9661
|
-
rego
|
|
11317
|
+
rego, force: true
|
|
9662
11318
|
});
|
|
9663
11319
|
// Never tear down the graphic the caller is registering: the renderers hand back
|
|
9664
11320
|
// the same object when they reuse it, so removing it here destroys what replaces it.
|
|
@@ -9683,15 +11339,14 @@ var VisualsRegister;
|
|
|
9683
11339
|
(_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.Trigger(update);
|
|
9684
11340
|
}
|
|
9685
11341
|
// Only worth refreshing while the Entity is still drawn by something else, since
|
|
9686
|
-
// losing this rego can change which of the survivors wins.
|
|
9687
|
-
// nothing to restyle, and queueing one per removed Entity is what turns disabling a
|
|
9688
|
-
// large menu item into minutes of scheduled work.
|
|
11342
|
+
// losing this rego can change which of the survivors wins.
|
|
9689
11343
|
if (doesInclude && ((_b = this.rego[entityId]) === null || _b === void 0 ? void 0 : _b.length)) {
|
|
9690
11344
|
this.queueUpdate({
|
|
9691
11345
|
entityId: entityId,
|
|
9692
11346
|
refresh: false
|
|
9693
11347
|
});
|
|
9694
11348
|
}
|
|
11349
|
+
(_c = this.labelGroups) === null || _c === void 0 ? void 0 : _c.RefreshEntity(entityId);
|
|
9695
11350
|
};
|
|
9696
11351
|
const finish = () => {
|
|
9697
11352
|
this.releaseMenuItemCollection(menuItemId);
|
|
@@ -9722,7 +11377,7 @@ var VisualsRegister;
|
|
|
9722
11377
|
return;
|
|
9723
11378
|
}
|
|
9724
11379
|
EntityLabel.Detatch({
|
|
9725
|
-
rego
|
|
11380
|
+
rego, force: true
|
|
9726
11381
|
});
|
|
9727
11382
|
// Never tear down the graphic the caller is registering: the renderers hand back
|
|
9728
11383
|
// the same object when they reuse it, so removing it here destroys what replaces it.
|
|
@@ -9741,7 +11396,7 @@ var VisualsRegister;
|
|
|
9741
11396
|
if (source !== EUpdateSource.TREE_CASCADE) {
|
|
9742
11397
|
(_b = this.onUpdate) === null || _b === void 0 ? void 0 : _b.Trigger(update);
|
|
9743
11398
|
}
|
|
9744
|
-
this.rego[entityId] =
|
|
11399
|
+
this.rego[entityId] = this.rego[entityId].filter(r => r.menuItemId !== menuItemId);
|
|
9745
11400
|
this.unindexMenuItemEntityIfAbsent(menuItemId, entityId);
|
|
9746
11401
|
if (doUpdate && menuItemId) {
|
|
9747
11402
|
this.queueUpdate({
|
|
@@ -9750,6 +11405,7 @@ var VisualsRegister;
|
|
|
9750
11405
|
});
|
|
9751
11406
|
}
|
|
9752
11407
|
}
|
|
11408
|
+
(_c = this.labelGroups) === null || _c === void 0 ? void 0 : _c.RefreshEntity(entityId);
|
|
9753
11409
|
}
|
|
9754
11410
|
else if (relation) {
|
|
9755
11411
|
const relationKey = RelationRenderEngine.GetRenderGroupId(relation);
|
|
@@ -9761,7 +11417,7 @@ var VisualsRegister;
|
|
|
9761
11417
|
continue;
|
|
9762
11418
|
}
|
|
9763
11419
|
EntityLabel.Detatch({
|
|
9764
|
-
rego
|
|
11420
|
+
rego, force: true
|
|
9765
11421
|
});
|
|
9766
11422
|
removeEntity(this.viewer, rego.visual);
|
|
9767
11423
|
rego.visual = null;
|
|
@@ -9770,6 +11426,7 @@ var VisualsRegister;
|
|
|
9770
11426
|
delete this.rego[entityId];
|
|
9771
11427
|
}
|
|
9772
11428
|
this.unindexMenuItemEntityIfAbsent(rego.menuItemId, entityId);
|
|
11429
|
+
(_d = this.labelGroups) === null || _d === void 0 ? void 0 : _d.RefreshEntity(entityId);
|
|
9773
11430
|
const update = {
|
|
9774
11431
|
type: EVisualUpdateType.Remove,
|
|
9775
11432
|
entityId: rego.entityId,
|
|
@@ -9779,7 +11436,7 @@ var VisualsRegister;
|
|
|
9779
11436
|
update.source = source;
|
|
9780
11437
|
}
|
|
9781
11438
|
if (source !== EUpdateSource.TREE_CASCADE) {
|
|
9782
|
-
(
|
|
11439
|
+
(_e = this.onUpdate) === null || _e === void 0 ? void 0 : _e.Trigger(update);
|
|
9783
11440
|
}
|
|
9784
11441
|
if (doUpdate) {
|
|
9785
11442
|
this.queueUpdate({
|
|
@@ -9798,7 +11455,7 @@ var VisualsRegister;
|
|
|
9798
11455
|
* @param params
|
|
9799
11456
|
*/
|
|
9800
11457
|
RemoveRegosByVisuals(params) {
|
|
9801
|
-
var _a, _b;
|
|
11458
|
+
var _a, _b, _c;
|
|
9802
11459
|
const { doRemove = true, requestRender = true, source, doUpdate, menuItemId } = params;
|
|
9803
11460
|
const removedEntityIds = new Set();
|
|
9804
11461
|
for (const visual of params.visuals) {
|
|
@@ -9813,9 +11470,8 @@ var VisualsRegister;
|
|
|
9813
11470
|
continue;
|
|
9814
11471
|
}
|
|
9815
11472
|
if (rego.visual === visual) {
|
|
9816
|
-
// The primary graphic for this rego - remove the rego entirely.
|
|
9817
11473
|
EntityLabel.Detatch({
|
|
9818
|
-
rego
|
|
11474
|
+
rego, force: true
|
|
9819
11475
|
});
|
|
9820
11476
|
if (doRemove != false) {
|
|
9821
11477
|
removeEntity(this.viewer, rego.visual);
|
|
@@ -9844,9 +11500,10 @@ var VisualsRegister;
|
|
|
9844
11500
|
});
|
|
9845
11501
|
}
|
|
9846
11502
|
removedEntityIds.add(entityId);
|
|
11503
|
+
(_b = this.labelGroups) === null || _b === void 0 ? void 0 : _b.RefreshEntity(entityId);
|
|
9847
11504
|
}
|
|
9848
11505
|
else {
|
|
9849
|
-
const siblings = (
|
|
11506
|
+
const siblings = (_c = rego.visual) === null || _c === void 0 ? void 0 : _c["_siblingGraphics"];
|
|
9850
11507
|
if (siblings === null || siblings === void 0 ? void 0 : siblings.length) {
|
|
9851
11508
|
if (doRemove != false && siblings.includes(visual)) {
|
|
9852
11509
|
removeEntity(this.viewer, visual);
|
|
@@ -10113,6 +11770,204 @@ var VisualsRegister;
|
|
|
10113
11770
|
}
|
|
10114
11771
|
return map;
|
|
10115
11772
|
}
|
|
11773
|
+
ReleaseHoverLabel(immediate = false) {
|
|
11774
|
+
const label = this.hoverLabel;
|
|
11775
|
+
this.hoverLabel = undefined;
|
|
11776
|
+
if (!label || label.Disposed || label.Persistent) {
|
|
11777
|
+
return;
|
|
11778
|
+
}
|
|
11779
|
+
if (immediate) {
|
|
11780
|
+
EntityLabel.Detatch({ label });
|
|
11781
|
+
return;
|
|
11782
|
+
}
|
|
11783
|
+
label.SetHoverVisible(false);
|
|
11784
|
+
const key = JSON.stringify([label.Rego.entityId, label.Rego.menuItemId || ""]);
|
|
11785
|
+
const previous = this.retiringHoverLabels.get(key);
|
|
11786
|
+
if (previous) {
|
|
11787
|
+
clearTimeout(previous.timer);
|
|
11788
|
+
EntityLabel.Detatch({ label: previous.label });
|
|
11789
|
+
}
|
|
11790
|
+
// Retain briefly beyond the fade to bridge debounced same-target hover updates.
|
|
11791
|
+
const timer = setTimeout(() => {
|
|
11792
|
+
this.retiringHoverLabels.delete(key);
|
|
11793
|
+
EntityLabel.Detatch({ label });
|
|
11794
|
+
}, 240);
|
|
11795
|
+
this.retiringHoverLabels.set(key, { label, timer });
|
|
11796
|
+
}
|
|
11797
|
+
/*
|
|
11798
|
+
* Owns temporary positioning separately from persistent label membership and world anchors.
|
|
11799
|
+
*/
|
|
11800
|
+
SetLabelHover(params) {
|
|
11801
|
+
var _a, _b;
|
|
11802
|
+
if (this.disposed) {
|
|
11803
|
+
return;
|
|
11804
|
+
}
|
|
11805
|
+
if (!(params === null || params === void 0 ? void 0 : params.entityId)) {
|
|
11806
|
+
this.ClearLabelHover();
|
|
11807
|
+
return;
|
|
11808
|
+
}
|
|
11809
|
+
if (params.position && (!Number.isFinite(params.position.x) || !Number.isFinite(params.position.y) ||
|
|
11810
|
+
params.position.x < 0 || params.position.y < 0 ||
|
|
11811
|
+
params.position.x >= this.viewer.canvas.clientWidth || params.position.y >= this.viewer.canvas.clientHeight)) {
|
|
11812
|
+
this.ClearLabelHover();
|
|
11813
|
+
return;
|
|
11814
|
+
}
|
|
11815
|
+
const changed = ((_a = this.labelHover) === null || _a === void 0 ? void 0 : _a.entityId) !== params.entityId || ((_b = this.labelHover) === null || _b === void 0 ? void 0 : _b.menuItemId) !== params.menuItemId;
|
|
11816
|
+
if (changed) {
|
|
11817
|
+
this.ClearLabelHover();
|
|
11818
|
+
}
|
|
11819
|
+
clearTimeout(this.hoverRingReset);
|
|
11820
|
+
this.labelHover = {
|
|
11821
|
+
entityId: params.entityId,
|
|
11822
|
+
menuItemId: params.menuItemId,
|
|
11823
|
+
position: params.position ? { ...params.position } : undefined,
|
|
11824
|
+
createIfMissing: params.createIfMissing !== false
|
|
11825
|
+
};
|
|
11826
|
+
const doc = this.viewer.container.ownerDocument;
|
|
11827
|
+
doc.addEventListener("pointermove", this.CheckLabelHoverBounds, true);
|
|
11828
|
+
this.viewer.container.addEventListener("pointerleave", this.CheckLabelHoverBounds);
|
|
11829
|
+
doc.defaultView.addEventListener("blur", this.ClearLabelHover);
|
|
11830
|
+
this.SyncLabelHover();
|
|
11831
|
+
this.viewer.scene.requestRender();
|
|
11832
|
+
}
|
|
11833
|
+
GetLabelHoverPosition(entityId, menuItemId) {
|
|
11834
|
+
const hover = this.labelHover;
|
|
11835
|
+
return (hover === null || hover === void 0 ? void 0 : hover.entityId) === entityId && (hover.menuItemId == null || hover.menuItemId === menuItemId) ? hover.position : undefined;
|
|
11836
|
+
}
|
|
11837
|
+
SyncLabelHover() {
|
|
11838
|
+
var _a, _b, _c, _d;
|
|
11839
|
+
const hover = this.labelHover;
|
|
11840
|
+
if (!hover || this.disposed) {
|
|
11841
|
+
return;
|
|
11842
|
+
}
|
|
11843
|
+
if ((_a = this.hoverLabel) === null || _a === void 0 ? void 0 : _a.Disposed) {
|
|
11844
|
+
this.hoverLabel = undefined;
|
|
11845
|
+
}
|
|
11846
|
+
if (!hover.createIfMissing) {
|
|
11847
|
+
this.ReleaseHoverLabel(true);
|
|
11848
|
+
return;
|
|
11849
|
+
}
|
|
11850
|
+
const group = this.GetLabelGroupFor(hover.entityId, hover.menuItemId);
|
|
11851
|
+
let rego = this.GetRego(hover);
|
|
11852
|
+
const key = JSON.stringify([hover.entityId, hover.menuItemId || ""]);
|
|
11853
|
+
const retiring = this.retiringHoverLabels.get(key);
|
|
11854
|
+
if (retiring) {
|
|
11855
|
+
clearTimeout(retiring.timer);
|
|
11856
|
+
this.retiringHoverLabels.delete(key);
|
|
11857
|
+
if (!retiring.label.Disposed) {
|
|
11858
|
+
this.hoverLabel = retiring.label;
|
|
11859
|
+
}
|
|
11860
|
+
}
|
|
11861
|
+
const existing = rego && EntityLabel.GetLabel({ rego });
|
|
11862
|
+
const state = this.GetState(hover);
|
|
11863
|
+
if ((!rego && !group) || state.hidden || ((_b = rego === null || rego === void 0 ? void 0 : rego.visual) === null || _b === void 0 ? void 0 : _b.show) === false ||
|
|
11864
|
+
(this.GetIsIsolatedAny() && !state.isolated && this.GetIsolationOpacity() == null) ||
|
|
11865
|
+
(existing && existing !== this.hoverLabel && existing.HasText) ||
|
|
11866
|
+
(group && group.options.presentation !== "icon")) {
|
|
11867
|
+
this.ReleaseHoverLabel(true);
|
|
11868
|
+
return;
|
|
11869
|
+
}
|
|
11870
|
+
if (this.hoverLabel && !this.hoverLabel.Disposed) {
|
|
11871
|
+
this.hoverLabel.SetHoverVisible(true);
|
|
11872
|
+
return;
|
|
11873
|
+
}
|
|
11874
|
+
// Query-only icon groups have coordinates but no registered scene visual to attach to.
|
|
11875
|
+
if (!rego && group && [group.item.longitude, group.item.latitude, (_c = group.item.altitude) !== null && _c !== void 0 ? _c : 0].every(Number.isFinite)) {
|
|
11876
|
+
rego = {
|
|
11877
|
+
entityId: hover.entityId,
|
|
11878
|
+
menuItemId: hover.menuItemId,
|
|
11879
|
+
name: group.options.text,
|
|
11880
|
+
priority: 0,
|
|
11881
|
+
visual: new Entity({
|
|
11882
|
+
position: Cartesian3.fromDegrees(group.item.longitude, group.item.latitude, group.item.altitude || 0),
|
|
11883
|
+
point: {
|
|
11884
|
+
pixelSize: 0,
|
|
11885
|
+
heightReference: HeightReference.NONE
|
|
11886
|
+
}
|
|
11887
|
+
})
|
|
11888
|
+
};
|
|
11889
|
+
}
|
|
11890
|
+
if (!rego) {
|
|
11891
|
+
return;
|
|
11892
|
+
}
|
|
11893
|
+
const params = {
|
|
11894
|
+
rego,
|
|
11895
|
+
viewer: this.viewer,
|
|
11896
|
+
visualRegister: this,
|
|
11897
|
+
api: (_d = this.ApiGetters) === null || _d === void 0 ? void 0 : _d.GetBruceApi(),
|
|
11898
|
+
persistent: false,
|
|
11899
|
+
transient: true,
|
|
11900
|
+
hoverRingState: this.hoverRingState,
|
|
11901
|
+
options: {
|
|
11902
|
+
presentation: "text",
|
|
11903
|
+
text: group === null || group === void 0 ? void 0 : group.options.text,
|
|
11904
|
+
selectOnClick: false
|
|
11905
|
+
},
|
|
11906
|
+
visible: () => {
|
|
11907
|
+
var _a, _b;
|
|
11908
|
+
const current = this.GetState(hover);
|
|
11909
|
+
return (!this.disposed && !current.hidden && ((_a = rego.visual) === null || _a === void 0 ? void 0 : _a.show) !== false &&
|
|
11910
|
+
Boolean(this.GetRego(hover) || ((_b = this.labelGroups) === null || _b === void 0 ? void 0 : _b.Has(hover.entityId, hover.menuItemId))) &&
|
|
11911
|
+
(!this.GetIsIsolatedAny() || current.isolated || this.GetIsolationOpacity() != null));
|
|
11912
|
+
}
|
|
11913
|
+
};
|
|
11914
|
+
this.hoverLabel = existing || group ? new EntityLabel.Label(params) : EntityLabel.Attach(params);
|
|
11915
|
+
}
|
|
11916
|
+
SetLabelContainer(container) {
|
|
11917
|
+
if (this.disposed) {
|
|
11918
|
+
return;
|
|
11919
|
+
}
|
|
11920
|
+
EntityLabel.SetContainer(this.viewer, container);
|
|
11921
|
+
}
|
|
11922
|
+
ClearLabelContainer() {
|
|
11923
|
+
this.SetLabelContainer();
|
|
11924
|
+
}
|
|
11925
|
+
/*
|
|
11926
|
+
* Adds source-driven membership without changing manual labelled state or legacy bookmark snapshots.
|
|
11927
|
+
*/
|
|
11928
|
+
SetLabelGroup(group) {
|
|
11929
|
+
if (this.disposed) {
|
|
11930
|
+
return Promise.reject(new Error("Visual register is disposed"));
|
|
11931
|
+
}
|
|
11932
|
+
if (!this.labelGroups) {
|
|
11933
|
+
this.labelGroups = new EntityLabelGroup.Manager(this, () => this.labelGroupsChanged.Trigger(undefined));
|
|
11934
|
+
}
|
|
11935
|
+
return this.labelGroups.Set(group);
|
|
11936
|
+
}
|
|
11937
|
+
RemoveLabelGroup(id) {
|
|
11938
|
+
var _a;
|
|
11939
|
+
(_a = this.labelGroups) === null || _a === void 0 ? void 0 : _a.Remove(id);
|
|
11940
|
+
}
|
|
11941
|
+
RefreshLabelGroup(id) {
|
|
11942
|
+
var _a, _b;
|
|
11943
|
+
if (this.disposed) {
|
|
11944
|
+
return Promise.reject(new Error("Visual register is disposed"));
|
|
11945
|
+
}
|
|
11946
|
+
return (_b = (_a = this.labelGroups) === null || _a === void 0 ? void 0 : _a.Refresh(id)) !== null && _b !== void 0 ? _b : Promise.reject(new Error(`Unknown label group: ${id}`));
|
|
11947
|
+
}
|
|
11948
|
+
SetLabelGroupVisible(id, visible) {
|
|
11949
|
+
var _a, _b;
|
|
11950
|
+
return (_b = (_a = this.labelGroups) === null || _a === void 0 ? void 0 : _a.SetVisible(id, visible)) !== null && _b !== void 0 ? _b : false;
|
|
11951
|
+
}
|
|
11952
|
+
ClearLabelGroups() {
|
|
11953
|
+
var _a;
|
|
11954
|
+
(_a = this.labelGroups) === null || _a === void 0 ? void 0 : _a.Clear();
|
|
11955
|
+
}
|
|
11956
|
+
GetLabelGroups() {
|
|
11957
|
+
var _a;
|
|
11958
|
+
return ((_a = this.labelGroups) === null || _a === void 0 ? void 0 : _a.Get()) || [];
|
|
11959
|
+
}
|
|
11960
|
+
GetLabelGroupFor(entityId, menuItemId) {
|
|
11961
|
+
var _a;
|
|
11962
|
+
return (_a = this.labelGroups) === null || _a === void 0 ? void 0 : _a.Resolve(entityId, menuItemId);
|
|
11963
|
+
}
|
|
11964
|
+
RefreshLabelGroupEntity(entityId) {
|
|
11965
|
+
var _a, _b;
|
|
11966
|
+
(_a = this.labelGroups) === null || _a === void 0 ? void 0 : _a.RefreshEntity(entityId);
|
|
11967
|
+
if (((_b = this.labelHover) === null || _b === void 0 ? void 0 : _b.entityId) === entityId) {
|
|
11968
|
+
this.SyncLabelHover();
|
|
11969
|
+
}
|
|
11970
|
+
}
|
|
10116
11971
|
}
|
|
10117
11972
|
VisualsRegister.Register = Register;
|
|
10118
11973
|
})(VisualsRegister || (VisualsRegister = {}));
|
|
@@ -10239,6 +12094,8 @@ function GetValue(viewer, obj) {
|
|
|
10239
12094
|
}
|
|
10240
12095
|
class Point {
|
|
10241
12096
|
constructor(lon, lat, id) {
|
|
12097
|
+
this.radius = 0;
|
|
12098
|
+
this.minDistance = 0;
|
|
10242
12099
|
this.lon = lon;
|
|
10243
12100
|
this.lat = lat;
|
|
10244
12101
|
this.id = id;
|
|
@@ -10256,8 +12113,8 @@ class Circle {
|
|
|
10256
12113
|
let xDist = Math.abs(range.x - this.x);
|
|
10257
12114
|
let yDist = Math.abs(range.y - this.y);
|
|
10258
12115
|
let r = this.radius;
|
|
10259
|
-
let w = range.width;
|
|
10260
|
-
let h = range.height;
|
|
12116
|
+
let w = range.width / 2;
|
|
12117
|
+
let h = range.height / 2;
|
|
10261
12118
|
if (xDist > (w + r) || yDist > (h + r)) {
|
|
10262
12119
|
return false;
|
|
10263
12120
|
}
|
|
@@ -10271,8 +12128,7 @@ class Circle {
|
|
|
10271
12128
|
Contains(point) {
|
|
10272
12129
|
const dx = this.x - point.lon;
|
|
10273
12130
|
const dy = this.y - point.lat;
|
|
10274
|
-
|
|
10275
|
-
return distance <= this.radius;
|
|
12131
|
+
return dx * dx + dy * dy <= this.radius * this.radius;
|
|
10276
12132
|
}
|
|
10277
12133
|
}
|
|
10278
12134
|
class Rectangle$1 {
|
|
@@ -10309,13 +12165,13 @@ class Quad {
|
|
|
10309
12165
|
let y = this.boundary.y;
|
|
10310
12166
|
let w = this.boundary.w / 2;
|
|
10311
12167
|
let h = this.boundary.h / 2;
|
|
10312
|
-
let ne = new Rectangle$1(x + w
|
|
12168
|
+
let ne = new Rectangle$1(x + w, y - h, w, h);
|
|
10313
12169
|
this.northeast = new Quad(ne, this.capacity);
|
|
10314
|
-
let nw = new Rectangle$1(x - w
|
|
12170
|
+
let nw = new Rectangle$1(x - w, y - h, w, h);
|
|
10315
12171
|
this.northwest = new Quad(nw, this.capacity);
|
|
10316
|
-
let se = new Rectangle$1(x + w
|
|
12172
|
+
let se = new Rectangle$1(x + w, y + h, w, h);
|
|
10317
12173
|
this.southeast = new Quad(se, this.capacity);
|
|
10318
|
-
let sw = new Rectangle$1(x - w
|
|
12174
|
+
let sw = new Rectangle$1(x - w, y + h, w, h);
|
|
10319
12175
|
this.southwest = new Quad(sw, this.capacity);
|
|
10320
12176
|
this.divided = true;
|
|
10321
12177
|
}
|
|
@@ -10323,7 +12179,8 @@ class Quad {
|
|
|
10323
12179
|
if (!this.boundary.Contains(point)) {
|
|
10324
12180
|
return false;
|
|
10325
12181
|
}
|
|
10326
|
-
|
|
12182
|
+
// Coincident points must terminate in a bucket rather than subdividing indefinitely.
|
|
12183
|
+
if (this.points.length < this.capacity || this.boundary.w < 1e-10) {
|
|
10327
12184
|
this.points.push(point);
|
|
10328
12185
|
return true;
|
|
10329
12186
|
}
|
|
@@ -10553,505 +12410,640 @@ function getClusterImage(params) {
|
|
|
10553
12410
|
}
|
|
10554
12411
|
return null;
|
|
10555
12412
|
}
|
|
10556
|
-
const
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
|
|
10560
|
-
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
// Queue to force update entities.
|
|
10565
|
-
this.updateEntityQueue = [];
|
|
10566
|
-
this.register = register;
|
|
10567
|
-
this.viewer = register.Viewer;
|
|
10568
|
-
this.menuItemId = menuItemId;
|
|
10569
|
-
let boundary = new Rectangle$1(0, 0, 360, 180);
|
|
10570
|
-
this.quadTree = new Quad(boundary, 30);
|
|
10571
|
-
this.prevClusteredEntities = new Set();
|
|
10572
|
-
this.currClusteredEntities = new Set();
|
|
10573
|
-
this.clusterEntities = new Map();
|
|
10574
|
-
this.updateQueue = new DelayQueue(() => {
|
|
10575
|
-
this.doUpdate();
|
|
10576
|
-
}, 1000);
|
|
10577
|
-
this.listenCamera();
|
|
10578
|
-
this.listenSelection();
|
|
10579
|
-
if (settings) {
|
|
10580
|
-
if (!isNaN(+settings.minDistance)) {
|
|
10581
|
-
this.minDistance = +settings.minDistance;
|
|
10582
|
-
}
|
|
10583
|
-
if (!isNaN(+settings.minTotalCount)) {
|
|
10584
|
-
this.minTotalCount = +settings.minTotalCount;
|
|
10585
|
-
}
|
|
10586
|
-
}
|
|
10587
|
-
}
|
|
10588
|
-
Update() {
|
|
10589
|
-
this.updateQueue.Call();
|
|
12413
|
+
const clusterPickers = new WeakMap();
|
|
12414
|
+
/*
|
|
12415
|
+
* One capture listener set per viewer prevents host canvas picking from undoing cluster selection.
|
|
12416
|
+
*/
|
|
12417
|
+
function getClusterPicker(viewer) {
|
|
12418
|
+
let picker = clusterPickers.get(viewer);
|
|
12419
|
+
if (picker) {
|
|
12420
|
+
return picker;
|
|
10590
12421
|
}
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
|
|
10594
|
-
|
|
12422
|
+
const canvas = viewer.canvas;
|
|
12423
|
+
picker = {
|
|
12424
|
+
owners: new Set(),
|
|
12425
|
+
targets: new Map(),
|
|
12426
|
+
remove: () => { }
|
|
12427
|
+
};
|
|
12428
|
+
let gesture;
|
|
12429
|
+
let released;
|
|
12430
|
+
const handler = (event) => {
|
|
12431
|
+
if (viewer.isDestroyed())
|
|
12432
|
+
return;
|
|
12433
|
+
const stop = () => { event.preventDefault(); event.stopImmediatePropagation(); };
|
|
12434
|
+
if (event.type === "pointermove") {
|
|
12435
|
+
if (gesture && Math.hypot(event.clientX - gesture.x, event.clientY - gesture.y) > 6) {
|
|
12436
|
+
gesture.moved = true;
|
|
10595
12437
|
}
|
|
10596
|
-
|
|
12438
|
+
return;
|
|
10597
12439
|
}
|
|
10598
|
-
|
|
10599
|
-
|
|
10600
|
-
|
|
10601
|
-
|
|
12440
|
+
if (event.type === "pointercancel") {
|
|
12441
|
+
if (gesture) {
|
|
12442
|
+
released = { ...gesture, moved: true };
|
|
12443
|
+
gesture = null;
|
|
12444
|
+
if (released.target) {
|
|
12445
|
+
stop();
|
|
12446
|
+
}
|
|
12447
|
+
}
|
|
10602
12448
|
return;
|
|
10603
12449
|
}
|
|
10604
|
-
if (
|
|
12450
|
+
if (event.button !== 0) {
|
|
10605
12451
|
return;
|
|
10606
12452
|
}
|
|
10607
|
-
|
|
10608
|
-
if
|
|
10609
|
-
|
|
10610
|
-
this.queueInterval = null;
|
|
12453
|
+
if (event.type === "pointerup" || event.type === "mouseup") {
|
|
12454
|
+
// Only consume releases whose press we owned, even if the cursor left the billboard.
|
|
12455
|
+
if (!gesture) {
|
|
10611
12456
|
return;
|
|
10612
12457
|
}
|
|
10613
|
-
|
|
10614
|
-
|
|
10615
|
-
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
clearInterval(this.queueInterval);
|
|
10619
|
-
this.queueInterval = null;
|
|
12458
|
+
gesture.moved || (gesture.moved = Math.hypot(event.clientX - gesture.x, event.clientY - gesture.y) > 6);
|
|
12459
|
+
released = gesture;
|
|
12460
|
+
gesture = null;
|
|
12461
|
+
if (released.target) {
|
|
12462
|
+
stop();
|
|
10620
12463
|
}
|
|
10621
|
-
|
|
10622
|
-
|
|
10623
|
-
|
|
10624
|
-
|
|
10625
|
-
|
|
10626
|
-
|
|
10627
|
-
|
|
10628
|
-
|
|
10629
|
-
|
|
10630
|
-
|
|
10631
|
-
var _a;
|
|
10632
|
-
const cameraPos = (_a = viewer === null || viewer === void 0 ? void 0 : viewer.camera) === null || _a === void 0 ? void 0 : _a.positionCartographic;
|
|
10633
|
-
if (!cameraPos) {
|
|
10634
|
-
return false;
|
|
12464
|
+
return;
|
|
12465
|
+
}
|
|
12466
|
+
const rect = canvas.getBoundingClientRect();
|
|
12467
|
+
const picked = viewer.scene.pick(new Cartesian2(event.clientX - rect.left, event.clientY - rect.top));
|
|
12468
|
+
const select = picker.targets.get(picked === null || picked === void 0 ? void 0 : picked.id);
|
|
12469
|
+
if (event.type === "pointerdown" || event.type === "mousedown") {
|
|
12470
|
+
released = null;
|
|
12471
|
+
gesture = { target: select ? picked.id : null, x: event.clientX, y: event.clientY, moved: false };
|
|
12472
|
+
if (gesture.target) {
|
|
12473
|
+
stop();
|
|
10635
12474
|
}
|
|
10636
|
-
|
|
10637
|
-
|
|
10638
|
-
|
|
10639
|
-
|
|
12475
|
+
return;
|
|
12476
|
+
}
|
|
12477
|
+
if (released) {
|
|
12478
|
+
const owned = !!released.target;
|
|
12479
|
+
const invalid = released.moved || released.target !== (picked === null || picked === void 0 ? void 0 : picked.id);
|
|
12480
|
+
released = null;
|
|
12481
|
+
if (!owned) {
|
|
12482
|
+
return;
|
|
10640
12483
|
}
|
|
10641
|
-
|
|
10642
|
-
|
|
10643
|
-
|
|
10644
|
-
return distance > 50;
|
|
10645
|
-
};
|
|
10646
|
-
this.listenCameraRemoval = viewer.scene.camera.changed.addEventListener(() => {
|
|
10647
|
-
if (hasMoved()) {
|
|
10648
|
-
this.updateQueue.Call();
|
|
12484
|
+
if (invalid) {
|
|
12485
|
+
stop();
|
|
12486
|
+
return;
|
|
10649
12487
|
}
|
|
10650
|
-
});
|
|
10651
|
-
}
|
|
10652
|
-
/**
|
|
10653
|
-
* Stops listening to camera changes.
|
|
10654
|
-
*/
|
|
10655
|
-
unlistenCamera() {
|
|
10656
|
-
if (this.listenCameraRemoval) {
|
|
10657
|
-
this.listenCameraRemoval();
|
|
10658
|
-
this.listenCameraRemoval = null;
|
|
10659
12488
|
}
|
|
12489
|
+
if (!select) {
|
|
12490
|
+
return;
|
|
12491
|
+
}
|
|
12492
|
+
stop();
|
|
12493
|
+
if (event.type === "click") {
|
|
12494
|
+
select();
|
|
12495
|
+
}
|
|
12496
|
+
};
|
|
12497
|
+
const types = ["pointerdown", "pointermove", "pointerup", "pointercancel", "mousedown", "mouseup", "click", "dblclick"];
|
|
12498
|
+
if (canvas === null || canvas === void 0 ? void 0 : canvas.addEventListener) {
|
|
12499
|
+
for (const type of types) {
|
|
12500
|
+
canvas.addEventListener(type, handler, true);
|
|
12501
|
+
}
|
|
12502
|
+
picker.remove = () => {
|
|
12503
|
+
for (const type of types) {
|
|
12504
|
+
canvas.removeEventListener(type, handler, true);
|
|
12505
|
+
}
|
|
12506
|
+
};
|
|
10660
12507
|
}
|
|
10661
|
-
|
|
10662
|
-
|
|
10663
|
-
|
|
10664
|
-
|
|
10665
|
-
|
|
10666
|
-
|
|
10667
|
-
|
|
10668
|
-
|
|
12508
|
+
clusterPickers.set(viewer, picker);
|
|
12509
|
+
return picker;
|
|
12510
|
+
}
|
|
12511
|
+
/*
|
|
12512
|
+
* Count tiers are bounded so large clusters do not cover the scene with enormous billboards.
|
|
12513
|
+
*/
|
|
12514
|
+
function PointClusterSize(count) {
|
|
12515
|
+
return count >= 10000 ? 140 : count >= 1000 ? 120 : count >= 100 ? 90 : count >= 10 ? 80 : 70;
|
|
12516
|
+
}
|
|
12517
|
+
class PointClustering {
|
|
12518
|
+
constructor(register, menuItemId, settings = {}) {
|
|
12519
|
+
this.register = register;
|
|
12520
|
+
this.menuItemId = menuItemId;
|
|
12521
|
+
this.settings = settings;
|
|
12522
|
+
this.minDistance = 5000;
|
|
12523
|
+
this.minTotalCount = 0;
|
|
12524
|
+
this.currClusteredEntities = new Set();
|
|
12525
|
+
this.clusterEntities = new Map();
|
|
12526
|
+
this.clusterViews = new Map();
|
|
12527
|
+
this.registeredEntityIds = new Set();
|
|
12528
|
+
this.records = new Map();
|
|
12529
|
+
this.disposed = false;
|
|
12530
|
+
this.removals = [];
|
|
12531
|
+
this.selectionQueued = false;
|
|
12532
|
+
this.viewer = register.Viewer;
|
|
12533
|
+
this.settings = settings = settings || {};
|
|
12534
|
+
if (Number.isFinite(+settings.minDistance)) {
|
|
12535
|
+
this.minDistance = Math.max(0, +settings.minDistance);
|
|
12536
|
+
}
|
|
12537
|
+
if (Number.isFinite(+settings.minTotalCount)) {
|
|
12538
|
+
this.minTotalCount = Math.max(0, +settings.minTotalCount);
|
|
12539
|
+
}
|
|
12540
|
+
this.quadTree = new Quad(new Rectangle$1(0, 0, 360, 180), 30);
|
|
12541
|
+
this.updateQueue = new DelayQueue(() => this.doUpdate(), 1000);
|
|
12542
|
+
this.picker = getClusterPicker(this.viewer);
|
|
12543
|
+
this.picker.owners.add(this);
|
|
12544
|
+
this.removals.push(this.viewer.scene.camera.changed.addEventListener(() => this.Update()));
|
|
12545
|
+
this.removals.push(this.viewer.scene.terrainProviderChanged.addEventListener(() => this.Update()));
|
|
12546
|
+
this.removals.push(register.OnUpdate.Subscribe(data => {
|
|
12547
|
+
if (data.type === VisualsRegister.EVisualUpdateType.DeselectAll ||
|
|
12548
|
+
(data.entityId && this.registeredEntityIds.has(data.entityId) && data.selected != null)) {
|
|
12549
|
+
if (!this.selectionQueued) {
|
|
12550
|
+
this.selectionQueued = true;
|
|
12551
|
+
Promise.resolve().then(() => {
|
|
12552
|
+
this.selectionQueued = false;
|
|
12553
|
+
if (!this.disposed)
|
|
12554
|
+
this.refreshSelection();
|
|
12555
|
+
});
|
|
12556
|
+
}
|
|
10669
12557
|
}
|
|
10670
|
-
|
|
10671
|
-
|
|
10672
|
-
data.type == VisualsRegister.EVisualUpdateType.Update &&
|
|
10673
|
-
this.registeredEntityIds.has(data.entityId)) {
|
|
10674
|
-
this.updateQueue.Call();
|
|
12558
|
+
if ("isolated" in data || (data.entityId && this.registeredEntityIds.has(data.entityId) && "hidden" in data)) {
|
|
12559
|
+
this.Update();
|
|
10675
12560
|
}
|
|
10676
|
-
});
|
|
12561
|
+
}));
|
|
10677
12562
|
}
|
|
10678
|
-
|
|
10679
|
-
if (this.
|
|
10680
|
-
this.
|
|
10681
|
-
this.listenUpdateRemoval = null;
|
|
12563
|
+
Update() {
|
|
12564
|
+
if (!this.disposed) {
|
|
12565
|
+
this.updateQueue.Call();
|
|
10682
12566
|
}
|
|
10683
12567
|
}
|
|
10684
|
-
|
|
10685
|
-
|
|
10686
|
-
|
|
10687
|
-
*/
|
|
12568
|
+
GetClusters() {
|
|
12569
|
+
return Array.from(this.clusterViews.values(), view => ({ ...view, entityIds: [...view.entityIds] }));
|
|
12570
|
+
}
|
|
10688
12571
|
Dispose() {
|
|
10689
12572
|
if (this.disposed) {
|
|
10690
12573
|
return;
|
|
10691
12574
|
}
|
|
10692
|
-
for (let [, entity] of Array.from(this.clusterEntities)) {
|
|
10693
|
-
this.viewer.entities.remove(entity);
|
|
10694
|
-
}
|
|
10695
|
-
this.clusterEntities.clear();
|
|
10696
|
-
this.unlistenCamera();
|
|
10697
|
-
this.unlistenSelection();
|
|
10698
12575
|
this.disposed = true;
|
|
10699
|
-
this.
|
|
10700
|
-
|
|
10701
|
-
|
|
10702
|
-
|
|
10703
|
-
|
|
10704
|
-
|
|
10705
|
-
|
|
10706
|
-
|
|
10707
|
-
|
|
12576
|
+
this.updateQueue.Dispose();
|
|
12577
|
+
for (const remove of this.removals) {
|
|
12578
|
+
remove();
|
|
12579
|
+
}
|
|
12580
|
+
for (const id of this.clusterEntities.keys()) {
|
|
12581
|
+
this.removeClusterEntity(id);
|
|
12582
|
+
}
|
|
12583
|
+
this.picker.owners.delete(this);
|
|
12584
|
+
if (!this.picker.owners.size) {
|
|
12585
|
+
this.picker.remove();
|
|
12586
|
+
clusterPickers.delete(this.viewer);
|
|
12587
|
+
}
|
|
12588
|
+
const restore = [];
|
|
12589
|
+
for (const id of this.registeredEntityIds) {
|
|
12590
|
+
const rego = this.register.GetRego({ entityId: id, menuItemId: this.menuItemId });
|
|
12591
|
+
if ((rego === null || rego === void 0 ? void 0 : rego.overrideShow) === false) {
|
|
10708
12592
|
rego.overrideShow = null;
|
|
10709
|
-
|
|
12593
|
+
restore.push(id);
|
|
10710
12594
|
}
|
|
10711
12595
|
}
|
|
10712
|
-
|
|
10713
|
-
|
|
10714
|
-
|
|
10715
|
-
|
|
12596
|
+
this.records.clear();
|
|
12597
|
+
this.registeredEntityIds.clear();
|
|
12598
|
+
this.currClusteredEntities.clear();
|
|
12599
|
+
if (restore.length) {
|
|
12600
|
+
this.register.ForceUpdate({ entityIds: restore });
|
|
10716
12601
|
}
|
|
10717
12602
|
}
|
|
10718
|
-
|
|
10719
|
-
|
|
10720
|
-
|
|
10721
|
-
|
|
10722
|
-
|
|
10723
|
-
|
|
10724
|
-
|
|
10725
|
-
|
|
10726
|
-
|
|
10727
|
-
|
|
10728
|
-
latSum += point.lat;
|
|
12603
|
+
refreshSelection() {
|
|
12604
|
+
for (const view of this.clusterViews.values()) {
|
|
12605
|
+
const selected = view.entityIds.length > 0 && view.entityIds.every(entityId => this.register.GetIsSelected({ entityId, menuItemId: this.menuItemId }));
|
|
12606
|
+
if (selected === view.selected)
|
|
12607
|
+
continue;
|
|
12608
|
+
view.selected = selected;
|
|
12609
|
+
if (selected)
|
|
12610
|
+
CesiumEntityStyler.Select({ viewer: this.viewer, entity: view.entity, requestRender: false });
|
|
12611
|
+
else
|
|
12612
|
+
CesiumEntityStyler.Deselect({ viewer: this.viewer, entity: view.entity, requestRender: false });
|
|
10729
12613
|
}
|
|
10730
|
-
|
|
10731
|
-
const latAvg = latSum / points.length;
|
|
10732
|
-
return new Point(lonAvg, latAvg, "");
|
|
12614
|
+
this.viewer.scene.requestRender();
|
|
10733
12615
|
}
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
|
|
10737
|
-
*/
|
|
10738
|
-
doUpdate() {
|
|
10739
|
-
if (this.disposed) {
|
|
12616
|
+
selectCluster(clusterId) {
|
|
12617
|
+
const view = this.clusterViews.get(clusterId);
|
|
12618
|
+
if (this.disposed || !view)
|
|
10740
12619
|
return;
|
|
12620
|
+
this.register.ClearSelected({ requestRender: false });
|
|
12621
|
+
this.register.SetSelected({ entityIds: view.entityIds, selected: true });
|
|
12622
|
+
this.refreshSelection();
|
|
12623
|
+
}
|
|
12624
|
+
resolveGeometry(visual, seen = new Set()) {
|
|
12625
|
+
var _a, _b, _c;
|
|
12626
|
+
if (!visual || seen.has(visual)) {
|
|
12627
|
+
return null;
|
|
10741
12628
|
}
|
|
10742
|
-
|
|
10743
|
-
|
|
10744
|
-
const
|
|
10745
|
-
|
|
10746
|
-
|
|
10747
|
-
|
|
10748
|
-
|
|
10749
|
-
|
|
10750
|
-
|
|
10751
|
-
|
|
12629
|
+
seen.add(visual);
|
|
12630
|
+
const value = (property) => GetValue(this.viewer, property);
|
|
12631
|
+
const valid = (p) => p && [p.x, p.y, p.z].every(Number.isFinite) && Cartesian3.magnitudeSquared(p) > 0;
|
|
12632
|
+
const kind = visual.model || visual.box || visual.cylinder || visual.ellipsoid ? "model" :
|
|
12633
|
+
visual.polygon || visual.rectangle || visual.ellipse ? "polygon" :
|
|
12634
|
+
visual.polyline || visual.corridor || visual.wall || visual.polylineVolume ? "polyline" :
|
|
12635
|
+
visual.point || visual.billboard || visual.label ? "point" : "other";
|
|
12636
|
+
const configured = (_a = this.settings.minDistanceByType) === null || _a === void 0 ? void 0 : _a[kind];
|
|
12637
|
+
let minDistance = Number.isFinite(configured) ? Math.max(0, configured) : this.minDistance * (kind === "point" || kind === "other" ? 1 : 2);
|
|
12638
|
+
const graphic = (visual.model || visual.polygon || visual.rectangle || visual.ellipse || visual.polyline ||
|
|
12639
|
+
visual.corridor || visual.wall || visual.polylineVolume || visual.billboard || visual.point || visual.label || visual.box || visual.cylinder || visual.ellipsoid);
|
|
12640
|
+
let heightRef = (_b = value(graphic === null || graphic === void 0 ? void 0 : graphic.heightReference)) !== null && _b !== void 0 ? _b : HeightReference.NONE;
|
|
12641
|
+
if (visual.polyline) {
|
|
12642
|
+
heightRef = value(visual.polyline.clampToGround) ? HeightReference.CLAMP_TO_GROUND : HeightReference.NONE;
|
|
12643
|
+
}
|
|
12644
|
+
let positions;
|
|
12645
|
+
let height;
|
|
12646
|
+
if (visual.polygon) {
|
|
12647
|
+
const hierarchy = value(visual.polygon.hierarchy);
|
|
12648
|
+
positions = hierarchy === null || hierarchy === void 0 ? void 0 : hierarchy.positions;
|
|
12649
|
+
if (!value(visual.polygon.perPositionHeight)) {
|
|
12650
|
+
height = value(visual.polygon.height);
|
|
12651
|
+
if (height == null && value(visual.polygon.extrudedHeight) == null) {
|
|
12652
|
+
heightRef = HeightReference.CLAMP_TO_GROUND;
|
|
12653
|
+
}
|
|
12654
|
+
height !== null && height !== void 0 ? height : (height = 0);
|
|
12655
|
+
}
|
|
12656
|
+
}
|
|
12657
|
+
else if (visual.rectangle) {
|
|
12658
|
+
const rectangle = value(visual.rectangle.coordinates);
|
|
12659
|
+
if (rectangle) {
|
|
12660
|
+
positions = [Rectangle.northwest(rectangle), Rectangle.northeast(rectangle),
|
|
12661
|
+
Rectangle.southwest(rectangle), Rectangle.southeast(rectangle)].map(c => Cartographic.toCartesian(c));
|
|
12662
|
+
}
|
|
12663
|
+
height = value(visual.rectangle.height);
|
|
12664
|
+
if (height == null && value(visual.rectangle.extrudedHeight) == null) {
|
|
12665
|
+
heightRef = HeightReference.CLAMP_TO_GROUND;
|
|
12666
|
+
}
|
|
12667
|
+
height !== null && height !== void 0 ? height : (height = 0);
|
|
12668
|
+
}
|
|
12669
|
+
else if (visual.polyline || visual.corridor || visual.wall || visual.polylineVolume) {
|
|
12670
|
+
positions = value(graphic.positions);
|
|
12671
|
+
if (visual.wall && positions) {
|
|
12672
|
+
const minimum = value(visual.wall.minimumHeights);
|
|
12673
|
+
const maximum = value(visual.wall.maximumHeights);
|
|
12674
|
+
positions = positions.flatMap((position, index) => {
|
|
12675
|
+
var _a, _b;
|
|
12676
|
+
if (!valid(position)) {
|
|
12677
|
+
return [];
|
|
12678
|
+
}
|
|
12679
|
+
const carto = Cartographic.fromCartesian(position);
|
|
12680
|
+
if (!carto) {
|
|
12681
|
+
return [];
|
|
12682
|
+
}
|
|
12683
|
+
return ([(_a = minimum === null || minimum === void 0 ? void 0 : minimum[index]) !== null && _a !== void 0 ? _a : 0, (_b = maximum === null || maximum === void 0 ? void 0 : maximum[index]) !== null && _b !== void 0 ? _b : carto.height]
|
|
12684
|
+
.filter(Number.isFinite).map(height => Cartesian3.fromRadians(carto.longitude, carto.latitude, height)));
|
|
12685
|
+
});
|
|
12686
|
+
}
|
|
10752
12687
|
}
|
|
10753
|
-
|
|
10754
|
-
|
|
10755
|
-
|
|
10756
|
-
|
|
10757
|
-
|
|
10758
|
-
|
|
10759
|
-
|
|
12688
|
+
if (visual.corridor || visual.ellipse) {
|
|
12689
|
+
height = value(graphic.height);
|
|
12690
|
+
if (height == null && value(graphic.extrudedHeight) == null)
|
|
12691
|
+
heightRef = HeightReference.CLAMP_TO_GROUND;
|
|
12692
|
+
height !== null && height !== void 0 ? height : (height = 0);
|
|
12693
|
+
}
|
|
12694
|
+
if (!(positions === null || positions === void 0 ? void 0 : positions.length)) {
|
|
12695
|
+
const position = value(visual.position);
|
|
12696
|
+
if (valid(position)) {
|
|
12697
|
+
positions = [position];
|
|
12698
|
+
}
|
|
12699
|
+
}
|
|
12700
|
+
const children = visual._siblingGraphics || [];
|
|
12701
|
+
// Container entities often copy their first child's position; don't count that anchor twice.
|
|
12702
|
+
if (!graphic && children.length) {
|
|
12703
|
+
positions = [];
|
|
12704
|
+
minDistance = 0;
|
|
12705
|
+
}
|
|
12706
|
+
const resolved = [];
|
|
12707
|
+
for (const position of positions || []) {
|
|
12708
|
+
if (!valid(position))
|
|
12709
|
+
continue;
|
|
12710
|
+
let source = position;
|
|
12711
|
+
if (height != null) {
|
|
12712
|
+
const carto = Cartographic.fromCartesian(position);
|
|
12713
|
+
if (!carto)
|
|
12714
|
+
continue;
|
|
12715
|
+
source = Cartesian3.fromRadians(carto.longitude, carto.latitude, height);
|
|
12716
|
+
}
|
|
12717
|
+
const actual = DrawingUtils.EnsurePosHeight({
|
|
12718
|
+
viewer: this.viewer,
|
|
12719
|
+
pos3d: source,
|
|
12720
|
+
desiredHeightRef: HeightReference.NONE,
|
|
12721
|
+
heightRef
|
|
12722
|
+
});
|
|
12723
|
+
if (valid(actual)) {
|
|
12724
|
+
resolved.push(actual);
|
|
12725
|
+
}
|
|
12726
|
+
}
|
|
12727
|
+
const extrudedHeight = value(graphic === null || graphic === void 0 ? void 0 : graphic.extrudedHeight);
|
|
12728
|
+
if (Number.isFinite(extrudedHeight)) {
|
|
12729
|
+
const reference = (_c = value(graphic.extrudedHeightReference)) !== null && _c !== void 0 ? _c : HeightReference.NONE;
|
|
12730
|
+
for (const position of positions || []) {
|
|
12731
|
+
if (!valid(position)) {
|
|
12732
|
+
continue;
|
|
12733
|
+
}
|
|
12734
|
+
const carto = Cartographic.fromCartesian(position);
|
|
12735
|
+
if (!carto) {
|
|
12736
|
+
continue;
|
|
12737
|
+
}
|
|
12738
|
+
const actual = DrawingUtils.EnsurePosHeight({
|
|
12739
|
+
viewer: this.viewer,
|
|
12740
|
+
pos3d: Cartesian3.fromRadians(carto.longitude, carto.latitude, extrudedHeight),
|
|
12741
|
+
desiredHeightRef: HeightReference.NONE, heightRef: reference
|
|
10760
12742
|
});
|
|
10761
|
-
if (
|
|
10762
|
-
|
|
10763
|
-
entitiesToUpdate.push(id);
|
|
12743
|
+
if (valid(actual)) {
|
|
12744
|
+
resolved.push(actual);
|
|
10764
12745
|
}
|
|
10765
12746
|
}
|
|
10766
12747
|
}
|
|
10767
|
-
const
|
|
10768
|
-
|
|
10769
|
-
|
|
10770
|
-
|
|
10771
|
-
//
|
|
10772
|
-
|
|
10773
|
-
|
|
10774
|
-
|
|
10775
|
-
|
|
12748
|
+
const spheres = [];
|
|
12749
|
+
const anchors = [];
|
|
12750
|
+
if (resolved.length) {
|
|
12751
|
+
const sphere = BoundingSphere.fromPoints(resolved);
|
|
12752
|
+
// A chord midpoint can lie underground. Preserve the average rendered vertex altitude.
|
|
12753
|
+
const center = Cartographic.fromCartesian(sphere.center);
|
|
12754
|
+
if (center) {
|
|
12755
|
+
const altitude = resolved.reduce((sum, p) => sum + Cartographic.fromCartesian(p).height, 0) / resolved.length;
|
|
12756
|
+
const position = Cartesian3.fromRadians(center.longitude, center.latitude, altitude);
|
|
12757
|
+
sphere.radius += Cartesian3.distance(position, sphere.center);
|
|
12758
|
+
sphere.center = position;
|
|
12759
|
+
}
|
|
12760
|
+
if (visual.ellipse) {
|
|
12761
|
+
sphere.radius = Math.max(sphere.radius, value(visual.ellipse.semiMajorAxis) || 0, value(visual.ellipse.semiMinorAxis) || 0);
|
|
12762
|
+
}
|
|
12763
|
+
if (visual.box) {
|
|
12764
|
+
sphere.radius = Math.max(sphere.radius, Cartesian3.magnitude(value(visual.box.dimensions) || Cartesian3.ZERO) / 2);
|
|
10776
12765
|
}
|
|
10777
|
-
if (
|
|
10778
|
-
|
|
12766
|
+
if (visual.ellipsoid) {
|
|
12767
|
+
sphere.radius = Math.max(sphere.radius, Cartesian3.maximumComponent(value(visual.ellipsoid.radii) || Cartesian3.ZERO));
|
|
10779
12768
|
}
|
|
10780
|
-
if (
|
|
10781
|
-
|
|
12769
|
+
if (visual.cylinder) {
|
|
12770
|
+
sphere.radius = Math.max(sphere.radius, Math.hypot((value(visual.cylinder.length) || 0) / 2, Math.max(value(visual.cylinder.topRadius) || 0, value(visual.cylinder.bottomRadius) || 0)));
|
|
10782
12771
|
}
|
|
10783
|
-
if (
|
|
10784
|
-
|
|
12772
|
+
if (visual.corridor) {
|
|
12773
|
+
sphere.radius += (value(visual.corridor.width) || 0) / 2;
|
|
10785
12774
|
}
|
|
10786
|
-
|
|
12775
|
+
if (visual.polylineVolume) {
|
|
12776
|
+
const shape = value(visual.polylineVolume.shape) || [];
|
|
12777
|
+
sphere.radius += shape.reduce((radius, p) => Math.max(radius, Math.hypot(p.x, p.y)), 0);
|
|
12778
|
+
}
|
|
12779
|
+
spheres.push(sphere);
|
|
12780
|
+
anchors.push(sphere.center);
|
|
12781
|
+
}
|
|
12782
|
+
for (const child of children) {
|
|
12783
|
+
const result = this.resolveGeometry(child, seen);
|
|
12784
|
+
if (!result) {
|
|
12785
|
+
continue;
|
|
12786
|
+
}
|
|
12787
|
+
spheres.push(new BoundingSphere(result.position, result.radius));
|
|
12788
|
+
anchors.push(result.position);
|
|
12789
|
+
minDistance = Math.max(minDistance, result.minDistance);
|
|
12790
|
+
}
|
|
12791
|
+
if (!spheres.length) {
|
|
12792
|
+
return null;
|
|
12793
|
+
}
|
|
12794
|
+
const sphere = spheres.length === 1 ? spheres[0] : BoundingSphere.fromBoundingSpheres(spheres);
|
|
12795
|
+
if (!valid(sphere.center) || !Number.isFinite(sphere.radius)) {
|
|
12796
|
+
return null;
|
|
12797
|
+
}
|
|
12798
|
+
if (spheres.length > 1) {
|
|
12799
|
+
const carto = Cartographic.fromCartesian(sphere.center);
|
|
12800
|
+
if (!carto) {
|
|
12801
|
+
return null;
|
|
12802
|
+
}
|
|
12803
|
+
const height = anchors.reduce((sum, p) => sum + Cartographic.fromCartesian(p).height, 0) / anchors.length;
|
|
12804
|
+
const position = Cartesian3.fromRadians(carto.longitude, carto.latitude, height);
|
|
12805
|
+
sphere.radius += Cartesian3.distance(position, sphere.center);
|
|
12806
|
+
sphere.center = position;
|
|
12807
|
+
}
|
|
12808
|
+
return { position: sphere.center, radius: sphere.radius, minDistance };
|
|
12809
|
+
}
|
|
12810
|
+
appearance(id) {
|
|
12811
|
+
var _a, _b, _c, _d, _e, _f;
|
|
12812
|
+
const { visual, metadata } = this.records.get(id);
|
|
12813
|
+
const data = metadata.entity || { Bruce: { ID: id } };
|
|
12814
|
+
let priority;
|
|
12815
|
+
let status;
|
|
12816
|
+
try {
|
|
12817
|
+
priority = Array.isArray(this.settings.iconPriority) ? Calculator.GetNumber(this.settings.iconPriority, data) : this.settings.iconPriority;
|
|
12818
|
+
status = Array.isArray(this.settings.iconStatus) ? Calculator.GetString(this.settings.iconStatus, data) : this.settings.iconStatus;
|
|
12819
|
+
}
|
|
12820
|
+
catch {
|
|
12821
|
+
priority = undefined;
|
|
12822
|
+
status = undefined;
|
|
12823
|
+
}
|
|
12824
|
+
const rawImage = GetValue(this.viewer, (_a = visual.billboard) === null || _a === void 0 ? void 0 : _a.image);
|
|
12825
|
+
const imageKey = (_b = visual.billboard) === null || _b === void 0 ? void 0 : _b._billboardImgKey;
|
|
12826
|
+
const iconUrl = (_c = metadata.iconUrl) !== null && _c !== void 0 ? _c : (typeof rawImage === "string" ? rawImage : undefined);
|
|
12827
|
+
// Original URLs retain status tokens after the point renderer converts an image into a data URI.
|
|
12828
|
+
const inferenceUrl = (_e = (_d = metadata.iconUrl) !== null && _d !== void 0 ? _d : imageKey) !== null && _e !== void 0 ? _e : iconUrl;
|
|
12829
|
+
const colorGraphic = visual.point || visual.polygon || visual.polyline || visual.corridor || visual.ellipse || visual.model;
|
|
12830
|
+
const color = colorGraphic ? CesiumEntityStyler.GetDefaultColor(this.viewer, colorGraphic) : null;
|
|
12831
|
+
let colorStatus;
|
|
12832
|
+
if (color && color.alpha > 0 && color.red > 0.5 && color.blue < 0.4) {
|
|
12833
|
+
colorStatus = color.green > 0.4 ? "warning" : "red";
|
|
12834
|
+
}
|
|
12835
|
+
const bgColor = color === null || color === void 0 ? void 0 : color.toCssColorString();
|
|
12836
|
+
return {
|
|
12837
|
+
priority: GetClusterIconPriority({
|
|
12838
|
+
iconPriority: Number.isFinite(priority) ? priority : metadata.iconPriority,
|
|
12839
|
+
iconStatus: (_f = status !== null && status !== void 0 ? status : metadata.iconStatus) !== null && _f !== void 0 ? _f : colorStatus, icon: metadata.icon, iconUrl: inferenceUrl
|
|
12840
|
+
}),
|
|
12841
|
+
iconUrl, bgColor,
|
|
12842
|
+
txtColor: color && color.red * 0.299 + color.green * 0.587 + color.blue * 0.114 > 0.5 ? "black" : "white"
|
|
10787
12843
|
};
|
|
10788
|
-
|
|
10789
|
-
|
|
10790
|
-
|
|
10791
|
-
|
|
10792
|
-
|
|
10793
|
-
|
|
12844
|
+
}
|
|
12845
|
+
doUpdate() {
|
|
12846
|
+
var _a;
|
|
12847
|
+
if (this.disposed) {
|
|
12848
|
+
return;
|
|
12849
|
+
}
|
|
12850
|
+
const previous = new Set(this.currClusteredEntities);
|
|
12851
|
+
const { clusters } = this.getClusters();
|
|
12852
|
+
const live = new Set(clusters.map(cluster => cluster.center.id));
|
|
12853
|
+
for (const id of this.clusterEntities.keys()) {
|
|
12854
|
+
if (!live.has(id)) {
|
|
12855
|
+
this.removeClusterEntity(id);
|
|
12856
|
+
}
|
|
12857
|
+
}
|
|
12858
|
+
const changed = new Set();
|
|
12859
|
+
for (const id of new Set([...previous, ...this.registeredEntityIds])) {
|
|
12860
|
+
if (this.currClusteredEntities.has(id)) {
|
|
12861
|
+
continue;
|
|
12862
|
+
}
|
|
12863
|
+
const rego = this.register.GetRego({ entityId: id, menuItemId: this.menuItemId });
|
|
12864
|
+
if ((rego === null || rego === void 0 ? void 0 : rego.overrideShow) === false) {
|
|
12865
|
+
rego.overrideShow = null;
|
|
12866
|
+
changed.add(id);
|
|
12867
|
+
}
|
|
12868
|
+
}
|
|
12869
|
+
for (const cluster of clusters) {
|
|
12870
|
+
const clusterId = cluster.center.id;
|
|
12871
|
+
let representative = cluster.center;
|
|
12872
|
+
let appearance = this.appearance(representative.id);
|
|
12873
|
+
if (this.settings.representative !== "nearest") {
|
|
12874
|
+
for (const point of cluster.points) {
|
|
12875
|
+
const candidate = this.appearance(point.id);
|
|
12876
|
+
const distance = Cartesian3.distanceSquared(point.position, this.viewer.camera.positionWC || this.viewer.camera.position);
|
|
12877
|
+
const representativeDistance = Cartesian3.distanceSquared(representative.position, this.viewer.camera.positionWC || this.viewer.camera.position);
|
|
12878
|
+
if (candidate.priority > appearance.priority || (candidate.priority === appearance.priority &&
|
|
12879
|
+
(distance < representativeDistance || (distance === representativeDistance &&
|
|
12880
|
+
point.id.localeCompare(representative.id) < 0)))) {
|
|
12881
|
+
representative = point;
|
|
12882
|
+
appearance = candidate;
|
|
12883
|
+
}
|
|
12884
|
+
}
|
|
12885
|
+
}
|
|
12886
|
+
const position = new Cartesian3();
|
|
12887
|
+
for (const point of cluster.points) {
|
|
12888
|
+
Cartesian3.add(position, point.position, position);
|
|
12889
|
+
}
|
|
12890
|
+
Cartesian3.divideByScalar(position, cluster.points.length, position);
|
|
12891
|
+
const count = cluster.points.length;
|
|
10794
12892
|
const params = {
|
|
10795
|
-
size:
|
|
10796
|
-
txtColor: this.pointColorTxt,
|
|
10797
|
-
bgColor: this.pointColorBg,
|
|
12893
|
+
size: PointClusterSize(count),
|
|
10798
12894
|
text: String(count),
|
|
10799
|
-
iconUrl:
|
|
12895
|
+
iconUrl: appearance.iconUrl,
|
|
12896
|
+
bgColor: appearance.bgColor,
|
|
12897
|
+
txtColor: appearance.txtColor
|
|
10800
12898
|
};
|
|
10801
|
-
|
|
10802
|
-
|
|
10803
|
-
|
|
10804
|
-
|
|
10805
|
-
|
|
10806
|
-
let clusterEntity = this.clusterEntities.get(clusterId);
|
|
10807
|
-
const centroid = this.calculateCentroid(cluster.points);
|
|
10808
|
-
const count = cluster.points.length;
|
|
10809
|
-
if (clusterEntity) {
|
|
10810
|
-
clusterEntity.position = new ConstantPositionProperty(Cartesian3.fromDegrees(centroid.lon, centroid.lat, 150));
|
|
10811
|
-
clusterEntity.billboard.image = new CallbackProperty(() => {
|
|
10812
|
-
return getCanvas(count);
|
|
10813
|
-
}, false),
|
|
10814
|
-
clusterEntity.billboard.width = new ConstantProperty(getScale(cluster.points.length));
|
|
10815
|
-
clusterEntity.billboard.height = new ConstantProperty(getScale(cluster.points.length));
|
|
10816
|
-
}
|
|
10817
|
-
else {
|
|
10818
|
-
clusterEntity = this.viewer.entities.add({
|
|
10819
|
-
position: Cartesian3.fromDegrees(centroid.lon, centroid.lat, 150),
|
|
12899
|
+
const image = new CallbackProperty(() => getClusterImage(params), false);
|
|
12900
|
+
let entity = this.clusterEntities.get(clusterId);
|
|
12901
|
+
if (!entity) {
|
|
12902
|
+
entity = this.viewer.entities.add({
|
|
12903
|
+
position,
|
|
10820
12904
|
billboard: {
|
|
12905
|
+
image,
|
|
12906
|
+
color: Color.WHITE,
|
|
12907
|
+
width: params.size,
|
|
12908
|
+
height: params.size,
|
|
10821
12909
|
heightReference: HeightReference.NONE,
|
|
10822
|
-
image: new CallbackProperty(() => {
|
|
10823
|
-
return getCanvas(count);
|
|
10824
|
-
}, false),
|
|
10825
|
-
width: getScale(count),
|
|
10826
|
-
height: getScale(count),
|
|
10827
12910
|
verticalOrigin: VerticalOrigin.CENTER,
|
|
10828
12911
|
horizontalOrigin: HorizontalOrigin.CENTER,
|
|
10829
|
-
disableDepthTestDistance:
|
|
10830
|
-
scaleByDistance: new NearFarScalar(1.5e2, 0.5, 3.0e7, 0.1)
|
|
12912
|
+
disableDepthTestDistance: Infinity,
|
|
12913
|
+
scaleByDistance: new NearFarScalar(1.5e2, 0.5, 3.0e7, 0.1)
|
|
10831
12914
|
}
|
|
10832
12915
|
});
|
|
10833
|
-
this.clusterEntities.set(clusterId,
|
|
12916
|
+
this.clusterEntities.set(clusterId, entity);
|
|
12917
|
+
if (this.settings.selectOnClick !== false)
|
|
12918
|
+
this.picker.targets.set(entity, () => this.selectCluster(clusterId));
|
|
10834
12919
|
}
|
|
10835
|
-
|
|
10836
|
-
|
|
12920
|
+
else {
|
|
12921
|
+
entity.position = new ConstantPositionProperty(position);
|
|
12922
|
+
entity.billboard.image = image;
|
|
12923
|
+
entity.billboard.width = new ConstantProperty(params.size);
|
|
12924
|
+
entity.billboard.height = new ConstantProperty(params.size);
|
|
12925
|
+
}
|
|
12926
|
+
const old = this.clusterViews.get(clusterId);
|
|
12927
|
+
this.clusterViews.set(clusterId, {
|
|
12928
|
+
entity,
|
|
12929
|
+
entityIds: cluster.points.map(point => point.id),
|
|
12930
|
+
representativeId: representative.id,
|
|
12931
|
+
selected: (_a = old === null || old === void 0 ? void 0 : old.selected) !== null && _a !== void 0 ? _a : false
|
|
12932
|
+
});
|
|
12933
|
+
_loadClusterImage(params).then(() => {
|
|
12934
|
+
if (!this.disposed && !this.viewer.isDestroyed()) {
|
|
12935
|
+
this.viewer.scene.requestRender();
|
|
12936
|
+
}
|
|
12937
|
+
});
|
|
12938
|
+
for (const point of cluster.points) {
|
|
10837
12939
|
const rego = this.register.GetRego({
|
|
10838
|
-
entityId:
|
|
12940
|
+
entityId: point.id,
|
|
10839
12941
|
menuItemId: this.menuItemId
|
|
10840
12942
|
});
|
|
10841
|
-
if (rego && rego.overrideShow
|
|
12943
|
+
if (rego && rego.overrideShow !== false) {
|
|
10842
12944
|
rego.overrideShow = false;
|
|
10843
|
-
|
|
12945
|
+
changed.add(point.id);
|
|
10844
12946
|
}
|
|
10845
12947
|
}
|
|
10846
12948
|
}
|
|
10847
|
-
|
|
10848
|
-
|
|
10849
|
-
for (let [clusterId, clusterEntity] of Array.from(this.clusterEntities)) {
|
|
10850
|
-
if (!clusters.find(x => x.center.id == clusterId)) {
|
|
10851
|
-
this.viewer.entities.remove(clusterEntity);
|
|
10852
|
-
this.clusterEntities.delete(clusterId);
|
|
10853
|
-
}
|
|
12949
|
+
if (changed.size) {
|
|
12950
|
+
this.register.ForceUpdate({ entityIds: Array.from(changed) });
|
|
10854
12951
|
}
|
|
10855
|
-
this.
|
|
12952
|
+
this.refreshSelection();
|
|
10856
12953
|
}
|
|
10857
|
-
getClusterSpacing(
|
|
10858
|
-
|
|
10859
|
-
const distanceThresholds = [3000, 4000, 5300, 6000, 7000];
|
|
10860
|
-
// Distance increments in degrees.
|
|
10861
|
-
const distanceIncrements = [0.001, 0.002, 0.01, 0.03, 0.09];
|
|
10862
|
-
let index = 0;
|
|
10863
|
-
if (distanceFromCluster) {
|
|
10864
|
-
for (let i = 0; i < distanceThresholds.length; i++) {
|
|
10865
|
-
if (distanceFromCluster > distanceThresholds[i]) {
|
|
10866
|
-
index = i;
|
|
10867
|
-
}
|
|
10868
|
-
}
|
|
10869
|
-
}
|
|
10870
|
-
return distanceIncrements[index];
|
|
12954
|
+
getClusterSpacing(distance) {
|
|
12955
|
+
return distance > 7000 ? 0.09 : distance > 6000 ? 0.03 : distance > 5300 ? 0.01 : distance > 4000 ? 0.002 : 0.001;
|
|
10871
12956
|
}
|
|
10872
12957
|
getClusters() {
|
|
12958
|
+
const previous = new Set(this.currClusteredEntities);
|
|
10873
12959
|
this.currClusteredEntities.clear();
|
|
10874
|
-
|
|
10875
|
-
|
|
10876
|
-
const
|
|
10877
|
-
|
|
10878
|
-
const
|
|
10879
|
-
|
|
10880
|
-
|
|
10881
|
-
|
|
10882
|
-
|
|
10883
|
-
|
|
10884
|
-
|
|
10885
|
-
|
|
10886
|
-
|
|
10887
|
-
|
|
10888
|
-
|
|
10889
|
-
|
|
10890
|
-
|
|
10891
|
-
|
|
10892
|
-
|
|
10893
|
-
|
|
10894
|
-
|
|
10895
|
-
|
|
10896
|
-
|
|
10897
|
-
|
|
10898
|
-
|
|
10899
|
-
|
|
10900
|
-
|
|
10901
|
-
|
|
10902
|
-
nearbyPoints = nearbyPoints.filter(x => !processedPoints.has(x.id) && !selectedIds.includes(x.id));
|
|
10903
|
-
if (nearbyPoints.length > 1) {
|
|
10904
|
-
let cluster = { center: point, points: [] };
|
|
10905
|
-
for (let nearby of nearbyPoints) {
|
|
10906
|
-
if (!cluster.points.includes(nearby)) {
|
|
10907
|
-
cluster.points.push(nearby);
|
|
10908
|
-
processedPoints.add(nearby.id);
|
|
10909
|
-
this.currClusteredEntities.add(nearby.id);
|
|
10910
|
-
}
|
|
10911
|
-
}
|
|
10912
|
-
clusters.push(cluster);
|
|
10913
|
-
}
|
|
10914
|
-
}
|
|
10915
|
-
}
|
|
10916
|
-
if (quad.divided) {
|
|
10917
|
-
processQuad(quad.northwest);
|
|
10918
|
-
processQuad(quad.northeast);
|
|
10919
|
-
processQuad(quad.southwest);
|
|
10920
|
-
processQuad(quad.southeast);
|
|
10921
|
-
}
|
|
10922
|
-
};
|
|
10923
|
-
processQuad(this.quadTree);
|
|
10924
|
-
// Filter out clusters with only one point.
|
|
10925
|
-
validClusters = clusters.filter((cluster) => cluster.points.length > 1);
|
|
12960
|
+
this.quadTree = new Quad(new Rectangle$1(0, 0, 360, 180), 30);
|
|
12961
|
+
const points = [];
|
|
12962
|
+
const distances = new Map();
|
|
12963
|
+
const camera = this.viewer.camera.positionWC || this.viewer.camera.position;
|
|
12964
|
+
const isolated = this.register.GetIsIsolatedAny();
|
|
12965
|
+
for (const [id, record] of this.records) {
|
|
12966
|
+
const state = this.register.GetState({ entityId: id, menuItemId: this.menuItemId });
|
|
12967
|
+
if (state.hidden || (isolated && !state.isolated)) {
|
|
12968
|
+
continue;
|
|
12969
|
+
}
|
|
12970
|
+
const geometry = this.resolveGeometry(record.visual);
|
|
12971
|
+
if (!geometry) {
|
|
12972
|
+
continue;
|
|
12973
|
+
}
|
|
12974
|
+
const { position, radius, minDistance } = geometry;
|
|
12975
|
+
const carto = Cartographic.fromCartesian(position);
|
|
12976
|
+
if (!carto) {
|
|
12977
|
+
continue;
|
|
12978
|
+
}
|
|
12979
|
+
const point = new Point(Math$1.toDegrees(carto.longitude), Math$1.toDegrees(carto.latitude), id);
|
|
12980
|
+
point.position = position;
|
|
12981
|
+
point.radius = radius;
|
|
12982
|
+
point.minDistance = minDistance;
|
|
12983
|
+
if (!this.quadTree.Insert(point)) {
|
|
12984
|
+
continue;
|
|
12985
|
+
}
|
|
12986
|
+
points.push(point);
|
|
12987
|
+
distances.set(id, Cartesian3.distance(position, camera));
|
|
10926
12988
|
}
|
|
10927
|
-
|
|
10928
|
-
|
|
10929
|
-
|
|
10930
|
-
|
|
10931
|
-
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
|
|
10935
|
-
|
|
10936
|
-
|
|
10937
|
-
|
|
10938
|
-
const clusterEntity = this.clusterEntities.get(clusterId);
|
|
10939
|
-
if (clusterEntity) {
|
|
10940
|
-
this.viewer.entities.remove(clusterEntity);
|
|
10941
|
-
this.clusterEntities.delete(clusterId);
|
|
12989
|
+
const clusters = points.length < this.minTotalCount ? [] :
|
|
12990
|
+
ClusterItems({
|
|
12991
|
+
items: points.sort((a, b) => distances.get(a.id) - distances.get(b.id) || a.id.localeCompare(b.id)),
|
|
12992
|
+
id: point => point.id,
|
|
12993
|
+
eligible: point => Math.max(0, distances.get(point.id) - point.radius) > point.minDistance,
|
|
12994
|
+
neighbours: point => this.quadTree.Query(new Circle(point.lon, point.lat, this.getClusterSpacing(distances.get(point.id))), [])
|
|
12995
|
+
}).map(cluster => ({ center: cluster.representative, points: cluster.members }));
|
|
12996
|
+
for (const cluster of clusters) {
|
|
12997
|
+
for (const point of cluster.points) {
|
|
12998
|
+
this.currClusteredEntities.add(point.id);
|
|
12999
|
+
}
|
|
10942
13000
|
}
|
|
13001
|
+
return {
|
|
13002
|
+
clusters,
|
|
13003
|
+
noLongerClustered: new Set(Array.from(previous).filter(id => !this.currClusteredEntities.has(id)))
|
|
13004
|
+
};
|
|
10943
13005
|
}
|
|
10944
|
-
|
|
10945
|
-
const
|
|
10946
|
-
|
|
10947
|
-
|
|
10948
|
-
|
|
10949
|
-
|
|
10950
|
-
|
|
10951
|
-
|
|
10952
|
-
* Adds new entity to the clustering logic.
|
|
10953
|
-
* @param id Bruce entity ID.
|
|
10954
|
-
* @param cartesian3 entity's position.
|
|
10955
|
-
*/
|
|
10956
|
-
addPoint(id, cartesian3) {
|
|
10957
|
-
let point = this.convertCartesianToCartographic(cartesian3);
|
|
10958
|
-
point.id = id;
|
|
10959
|
-
return this.quadTree.Insert(point);
|
|
13006
|
+
removeClusterEntity(id) {
|
|
13007
|
+
const entity = this.clusterEntities.get(id);
|
|
13008
|
+
if (!entity)
|
|
13009
|
+
return;
|
|
13010
|
+
this.picker.targets.delete(entity);
|
|
13011
|
+
this.viewer.entities.remove(entity);
|
|
13012
|
+
this.clusterEntities.delete(id);
|
|
13013
|
+
this.clusterViews.delete(id);
|
|
10960
13014
|
}
|
|
10961
|
-
|
|
10962
|
-
*
|
|
10963
|
-
* Will return false if entity could not be clustered and therefor should not be hidden.
|
|
10964
|
-
* @param id
|
|
10965
|
-
* @param entity
|
|
10966
|
-
* @param queueUpdate
|
|
10967
|
-
* @returns
|
|
13015
|
+
/*
|
|
13016
|
+
* Metadata is additive; existing three-argument calls keep their original signature.
|
|
10968
13017
|
*/
|
|
10969
|
-
AddEntity(id,
|
|
10970
|
-
if (this.disposed) {
|
|
10971
|
-
return false;
|
|
10972
|
-
}
|
|
10973
|
-
if (!entity) {
|
|
10974
|
-
return false;
|
|
10975
|
-
}
|
|
10976
|
-
if (entity.polygon || entity.polyline) {
|
|
13018
|
+
AddEntity(id, visual, queueUpdate = true, metadata = {}) {
|
|
13019
|
+
if (this.disposed || !visual) {
|
|
10977
13020
|
return false;
|
|
10978
13021
|
}
|
|
10979
|
-
|
|
10980
|
-
|
|
10981
|
-
return false;
|
|
10982
|
-
}
|
|
10983
|
-
if (!this.pointColorBg && entity.point) {
|
|
10984
|
-
const pointColorBg = GetValue(this.viewer, entity.point.color);
|
|
10985
|
-
if (pointColorBg) {
|
|
10986
|
-
let cColor = null;
|
|
10987
|
-
if (pointColorBg instanceof Object) {
|
|
10988
|
-
cColor = new Color(pointColorBg.red, pointColorBg.green, pointColorBg.blue, pointColorBg.alpha);
|
|
10989
|
-
}
|
|
10990
|
-
else if (typeof pointColorBg === "string") {
|
|
10991
|
-
cColor = Color.fromCssColorString(pointColorBg);
|
|
10992
|
-
}
|
|
10993
|
-
// Determine if text color should instead be black based on background.
|
|
10994
|
-
// cColor contains r,g,b,a values where r,g,b are in the range [0,1].
|
|
10995
|
-
if (cColor) {
|
|
10996
|
-
const brightness = (cColor.red * 299 + cColor.green * 587 + cColor.blue * 114) / 1000;
|
|
10997
|
-
if (brightness > 0.5) {
|
|
10998
|
-
this.pointColorTxt = "black";
|
|
10999
|
-
}
|
|
11000
|
-
else {
|
|
11001
|
-
this.pointColorTxt = "white";
|
|
11002
|
-
}
|
|
11003
|
-
this.pointColorBg = cColor.toCssColorString();
|
|
11004
|
-
}
|
|
11005
|
-
}
|
|
11006
|
-
}
|
|
11007
|
-
if (!this.iconUrl && entity.billboard) {
|
|
11008
|
-
const iconUrl = GetValue(this.viewer, entity.billboard.image);
|
|
11009
|
-
if (typeof iconUrl == "string") {
|
|
11010
|
-
this.iconUrl = iconUrl;
|
|
11011
|
-
}
|
|
11012
|
-
}
|
|
11013
|
-
if (this.registeredEntityIds.has(id)) {
|
|
11014
|
-
this.RemoveEntity(id, false);
|
|
11015
|
-
}
|
|
11016
|
-
const added = this.addPoint(id, pos3d);
|
|
11017
|
-
if (!added) {
|
|
13022
|
+
if (!this.resolveGeometry(visual)) {
|
|
13023
|
+
this.RemoveEntity(id, queueUpdate);
|
|
11018
13024
|
return false;
|
|
11019
13025
|
}
|
|
13026
|
+
this.records.set(id, { visual, metadata });
|
|
11020
13027
|
this.registeredEntityIds.add(id);
|
|
11021
13028
|
if (queueUpdate) {
|
|
11022
|
-
this.
|
|
13029
|
+
this.Update();
|
|
11023
13030
|
}
|
|
11024
13031
|
return true;
|
|
11025
13032
|
}
|
|
11026
|
-
/**
|
|
11027
|
-
* Removes entity from clustering logic.
|
|
11028
|
-
* Warning: This will not reveal the entity, suppressShow will remain true.
|
|
11029
|
-
* This is made with the assumption that the entity is being removed from viewer.
|
|
11030
|
-
* @param id
|
|
11031
|
-
* @param queueUpdate
|
|
11032
|
-
* @returns
|
|
11033
|
-
*/
|
|
11034
13033
|
RemoveEntity(id, queueUpdate = false) {
|
|
11035
13034
|
if (this.disposed) {
|
|
11036
13035
|
return;
|
|
11037
13036
|
}
|
|
11038
|
-
|
|
11039
|
-
if (pointToRemove) {
|
|
11040
|
-
this.quadTree.Remove(pointToRemove);
|
|
11041
|
-
if (queueUpdate) {
|
|
11042
|
-
this.updateQueue.Call();
|
|
11043
|
-
}
|
|
11044
|
-
}
|
|
13037
|
+
this.records.delete(id);
|
|
11045
13038
|
this.registeredEntityIds.delete(id);
|
|
13039
|
+
if (queueUpdate) {
|
|
13040
|
+
this.Update();
|
|
13041
|
+
}
|
|
11046
13042
|
}
|
|
11047
13043
|
}
|
|
11048
13044
|
|
|
11049
13045
|
/**
|
|
11050
13046
|
* How often a historic refresh is worth running, and which Entities it should ask about first.
|
|
11051
|
-
*
|
|
11052
|
-
* A handful of tracked vehicles can afford to re-read on almost every clock tick. Thousands of
|
|
11053
|
-
* parcels cannot, and refreshing them that often was never buying anything since the answer barely
|
|
11054
|
-
* changes between ticks.
|
|
11055
13047
|
*/
|
|
11056
13048
|
var HistoricFetchOrder;
|
|
11057
13049
|
(function (HistoricFetchOrder) {
|
|
@@ -11096,6 +13088,13 @@ var HistoricFetchOrder;
|
|
|
11096
13088
|
HistoricFetchOrder.IntervalForClock = IntervalForClock;
|
|
11097
13089
|
// Upper bounds in metres for the distance bands, nearest first.
|
|
11098
13090
|
HistoricFetchOrder.DEFAULT_BANDS = [500, 2000, 10000, 50000];
|
|
13091
|
+
// How far outside the visible rectangle something still counts as worth re-reading, share of the view itself.
|
|
13092
|
+
HistoricFetchOrder.REFRESH_MARGIN_FRACTION = 0.5;
|
|
13093
|
+
// A close view still gets a usable buffer, and a distant one does not reach half the account.
|
|
13094
|
+
HistoricFetchOrder.MIN_REFRESH_MARGIN_METRES = 250;
|
|
13095
|
+
HistoricFetchOrder.MAX_REFRESH_MARGIN_METRES = 2000;
|
|
13096
|
+
const METRES_PER_DEGREE_LAT = 111320;
|
|
13097
|
+
const MIN_LAT_COSINE = 0.01;
|
|
11099
13098
|
/**
|
|
11100
13099
|
* Resolves what to order by from the view monitor, falling back to the camera itself.
|
|
11101
13100
|
*/
|
|
@@ -11176,6 +13175,55 @@ var HistoricFetchOrder;
|
|
|
11176
13175
|
return grouped.filter(group => group.length > 0);
|
|
11177
13176
|
}
|
|
11178
13177
|
HistoricFetchOrder.ByCameraDistanceOf = ByCameraDistanceOf;
|
|
13178
|
+
/**
|
|
13179
|
+
* Returns how far past a visible rectangle a refresh should still reach, in metres.
|
|
13180
|
+
*/
|
|
13181
|
+
function RefreshMarginFor(bounds) {
|
|
13182
|
+
if (!bounds) {
|
|
13183
|
+
return HistoricFetchOrder.MIN_REFRESH_MARGIN_METRES;
|
|
13184
|
+
}
|
|
13185
|
+
const latSpan = Math.abs(bounds.north - bounds.south) * METRES_PER_DEGREE_LAT;
|
|
13186
|
+
const cosine = Math.max(MIN_LAT_COSINE, Math.cos(Math$1.toRadians((bounds.north + bounds.south) / 2)));
|
|
13187
|
+
const lonDegrees = bounds.west > bounds.east
|
|
13188
|
+
? (bounds.east + 360) - bounds.west
|
|
13189
|
+
: bounds.east - bounds.west;
|
|
13190
|
+
const lonSpan = Math.abs(lonDegrees) * METRES_PER_DEGREE_LAT * cosine;
|
|
13191
|
+
const span = Math.min(latSpan, lonSpan);
|
|
13192
|
+
if (!(span > 0)) {
|
|
13193
|
+
return HistoricFetchOrder.MIN_REFRESH_MARGIN_METRES;
|
|
13194
|
+
}
|
|
13195
|
+
return Math.min(HistoricFetchOrder.MAX_REFRESH_MARGIN_METRES, Math.max(HistoricFetchOrder.MIN_REFRESH_MARGIN_METRES, span * HistoricFetchOrder.REFRESH_MARGIN_FRACTION));
|
|
13196
|
+
}
|
|
13197
|
+
HistoricFetchOrder.RefreshMarginFor = RefreshMarginFor;
|
|
13198
|
+
/**
|
|
13199
|
+
* Returns the rectangle grown outwards by a margin in metres.
|
|
13200
|
+
*/
|
|
13201
|
+
function GrowBounds(bounds, metres) {
|
|
13202
|
+
if (!bounds || !(metres > 0)) {
|
|
13203
|
+
return bounds;
|
|
13204
|
+
}
|
|
13205
|
+
const dLat = metres / METRES_PER_DEGREE_LAT;
|
|
13206
|
+
const north = Math.min(90, bounds.north + dLat);
|
|
13207
|
+
const south = Math.max(-90, bounds.south - dLat);
|
|
13208
|
+
const cosine = Math.cos(Math$1.toRadians((bounds.north + bounds.south) / 2));
|
|
13209
|
+
if (cosine < MIN_LAT_COSINE) {
|
|
13210
|
+
return { north, south, west: -180, east: 180 };
|
|
13211
|
+
}
|
|
13212
|
+
const dLon = metres / (METRES_PER_DEGREE_LAT * cosine);
|
|
13213
|
+
const straddles = bounds.west > bounds.east;
|
|
13214
|
+
const gap = straddles ? bounds.west - bounds.east : 360 - (bounds.east - bounds.west);
|
|
13215
|
+
if (gap <= dLon * 2) {
|
|
13216
|
+
return { north, south, west: -180, east: 180 };
|
|
13217
|
+
}
|
|
13218
|
+
return { north, south, west: wrapLongitude(bounds.west - dLon), east: wrapLongitude(bounds.east + dLon) };
|
|
13219
|
+
}
|
|
13220
|
+
HistoricFetchOrder.GrowBounds = GrowBounds;
|
|
13221
|
+
/*
|
|
13222
|
+
* Returns a longitude folded back into the range the bounds are expressed in.
|
|
13223
|
+
*/
|
|
13224
|
+
function wrapLongitude(lon) {
|
|
13225
|
+
return (((lon + 180) % 360) + 360) % 360 - 180;
|
|
13226
|
+
}
|
|
11179
13227
|
/*
|
|
11180
13228
|
* Returns whether a world position sits inside a ground rectangle given in degrees.
|
|
11181
13229
|
*/
|
|
@@ -11184,17 +13232,22 @@ var HistoricFetchOrder;
|
|
|
11184
13232
|
if (!degrees) {
|
|
11185
13233
|
return false;
|
|
11186
13234
|
}
|
|
11187
|
-
|
|
13235
|
+
return InDegreeBounds(degrees.lat, degrees.lon, bounds);
|
|
13236
|
+
}
|
|
13237
|
+
HistoricFetchOrder.InBounds = InBounds;
|
|
13238
|
+
/*
|
|
13239
|
+
* The same test for a caller that already holds the degrees and should not pay to resolve them from a world position again.
|
|
13240
|
+
*/
|
|
13241
|
+
function InDegreeBounds(lat, lon, bounds) {
|
|
11188
13242
|
if (lat < bounds.south || lat > bounds.north) {
|
|
11189
13243
|
return false;
|
|
11190
13244
|
}
|
|
11191
|
-
// A view straddling the antimeridian reports west east of east, so the span is the outside.
|
|
11192
13245
|
if (bounds.west > bounds.east) {
|
|
11193
13246
|
return lon >= bounds.west || lon <= bounds.east;
|
|
11194
13247
|
}
|
|
11195
13248
|
return lon >= bounds.west && lon <= bounds.east;
|
|
11196
13249
|
}
|
|
11197
|
-
HistoricFetchOrder.
|
|
13250
|
+
HistoricFetchOrder.InDegreeBounds = InDegreeBounds;
|
|
11198
13251
|
/*
|
|
11199
13252
|
* Returns an Entity's world position, or null when it has none to place it by.
|
|
11200
13253
|
*/
|
|
@@ -11690,7 +13743,7 @@ var EntitiesRenderManager;
|
|
|
11690
13743
|
});
|
|
11691
13744
|
this.useGeojson = item.renderAsGeojson == true;
|
|
11692
13745
|
if (item.enableClustering) {
|
|
11693
|
-
this.clustering = new PointClustering(this.visualsManager, this.item.id);
|
|
13746
|
+
this.clustering = new PointClustering(this.visualsManager, this.item.id, this.item.clustering);
|
|
11694
13747
|
}
|
|
11695
13748
|
}
|
|
11696
13749
|
Init(params) {
|
|
@@ -12726,8 +14779,8 @@ var EntitiesRenderManager;
|
|
|
12726
14779
|
// The baseline data source must be editable.
|
|
12727
14780
|
const canEdit = !((_b = entity.Bruce.Outline) === null || _b === void 0 ? void 0 : _b.find(x => x.Baseline && !x.Editable));
|
|
12728
14781
|
const visual = rego === null || rego === void 0 ? void 0 : rego.visual;
|
|
14782
|
+
const wasClustered = this.clustering ? this.clustering.AddEntity(id, cEntity, false, { entity }) : false;
|
|
12729
14783
|
if (!visual || visual != cEntity) {
|
|
12730
|
-
const wasClustered = this.clustering ? this.clustering.AddEntity(id, cEntity, false) : false;
|
|
12731
14784
|
const tagIds = entity.Bruce["Layer.ID"];
|
|
12732
14785
|
const rego = {
|
|
12733
14786
|
canEdit: canEdit,
|
|
@@ -14173,8 +16226,8 @@ var EntitiesIdsRenderManager;
|
|
|
14173
16226
|
menuItemId: this.item.id
|
|
14174
16227
|
});
|
|
14175
16228
|
const visual = rego === null || rego === void 0 ? void 0 : rego.visual;
|
|
16229
|
+
const clustered = this.clustering ? this.clustering.AddEntity(id, cEntity, false, { entity }) : false;
|
|
14176
16230
|
if (!visual || visual != cEntity) {
|
|
14177
|
-
const clustered = this.clustering ? this.clustering.AddEntity(id, cEntity, false) : false;
|
|
14178
16231
|
this.visualsManager.AddRego({
|
|
14179
16232
|
rego: {
|
|
14180
16233
|
canEdit: true,
|
|
@@ -14600,6 +16653,19 @@ class EntityGatherer {
|
|
|
14600
16653
|
// All IDs that have ever been queued.
|
|
14601
16654
|
// When nothing is in queue, we'll re-request the existing IDs based on timeline changes.
|
|
14602
16655
|
this.allEIds = new Set();
|
|
16656
|
+
// IDs a historic read has actually answered with a record, rather than falling back to a stub.
|
|
16657
|
+
// A refresh serves these before the ones that have never produced anything.
|
|
16658
|
+
this.everFound = new Set();
|
|
16659
|
+
// Every known Entity bucketed by the ground cell its position falls in, so a refresh can ask
|
|
16660
|
+
// what the view reaches instead of walking the whole set. Built as IDs arrive.
|
|
16661
|
+
this.cells = new Map();
|
|
16662
|
+
// IDs the position source could not place yet. They are always refreshed.
|
|
16663
|
+
this.unplaced = new Set();
|
|
16664
|
+
// The IDs the last decision found inside the view, so a camera move can queue the difference
|
|
16665
|
+
// rather than re-requesting everything it already asked about.
|
|
16666
|
+
this.lastInView = new Set();
|
|
16667
|
+
// Unsubscribes the view monitor listener that queues what the camera has moved onto.
|
|
16668
|
+
this.monitorRemoval = null;
|
|
14603
16669
|
// Indicates a tick is active.
|
|
14604
16670
|
// When true, we won't bother calling onTick again.
|
|
14605
16671
|
this.runningTick = false;
|
|
@@ -14650,6 +16716,16 @@ class EntityGatherer {
|
|
|
14650
16716
|
}
|
|
14651
16717
|
this.historic = historic;
|
|
14652
16718
|
this.expandSources = expandSources;
|
|
16719
|
+
if (this.historic) {
|
|
16720
|
+
// Anything already known was taken in before the index was being built, so it is filed
|
|
16721
|
+
// a bounded number at a time from here rather than in one pass now.
|
|
16722
|
+
for (const entityId of this.allEIds) {
|
|
16723
|
+
this.unplaced.add(entityId);
|
|
16724
|
+
}
|
|
16725
|
+
}
|
|
16726
|
+
else {
|
|
16727
|
+
this.clearIndex();
|
|
16728
|
+
}
|
|
14653
16729
|
if (this.historic) {
|
|
14654
16730
|
// 4 ticks per second.
|
|
14655
16731
|
// The fastest data we are working with is 5 times per second.
|
|
@@ -14701,6 +16777,8 @@ class EntityGatherer {
|
|
|
14701
16777
|
this.lastOrderFocus = null;
|
|
14702
16778
|
this.allEIds.clear();
|
|
14703
16779
|
this.lastFoundDateTimes.clear();
|
|
16780
|
+
this.everFound.clear();
|
|
16781
|
+
this.clearIndex();
|
|
14704
16782
|
this.lastDateTime = null;
|
|
14705
16783
|
}
|
|
14706
16784
|
ReQueueAll() {
|
|
@@ -14711,22 +16789,193 @@ class EntityGatherer {
|
|
|
14711
16789
|
this.eIdQueued = new Set(this.eIdQueue);
|
|
14712
16790
|
this.priorityCount = 0;
|
|
14713
16791
|
this.lastOrderFocus = null;
|
|
16792
|
+
this.clearIndex();
|
|
14714
16793
|
}
|
|
14715
16794
|
/**
|
|
14716
|
-
* Supplies a lookup from Entity ID to world position, letting the queue resolve what the camera
|
|
14717
|
-
*
|
|
16795
|
+
* Supplies a lookup from Entity ID to world position, letting the queue resolve what the camera is nearest first.
|
|
16796
|
+
* Sources that cannot answer should not call this, and are served in order.
|
|
14718
16797
|
*/
|
|
14719
16798
|
SetPositionSource(positionOf) {
|
|
14720
16799
|
this.positionOf = positionOf;
|
|
14721
16800
|
this.lastOrderFocus = null;
|
|
16801
|
+
this.cells.clear();
|
|
16802
|
+
this.lastInView.clear();
|
|
16803
|
+
for (const entityId of this.allEIds) {
|
|
16804
|
+
this.unplaced.add(entityId);
|
|
16805
|
+
}
|
|
14722
16806
|
}
|
|
14723
16807
|
/**
|
|
14724
|
-
* Supplies the view monitor, so the order follows the ground the camera is aimed at and prefers
|
|
14725
|
-
*
|
|
16808
|
+
* Supplies the view monitor, so the order follows the ground the camera is aimed at and prefers what is on screen.
|
|
16809
|
+
* Without one the ordering falls back to distance from the camera itself.
|
|
14726
16810
|
*/
|
|
14727
16811
|
SetViewMonitor(monitor) {
|
|
16812
|
+
var _a;
|
|
16813
|
+
(_a = this.monitorRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
16814
|
+
this.monitorRemoval = null;
|
|
14728
16815
|
this.monitor = monitor;
|
|
14729
16816
|
this.lastOrderFocus = null;
|
|
16817
|
+
if (!monitor) {
|
|
16818
|
+
return;
|
|
16819
|
+
}
|
|
16820
|
+
this.monitorRemoval = monitor.Updated().Subscribe(() => {
|
|
16821
|
+
this.admitNewlyInView();
|
|
16822
|
+
});
|
|
16823
|
+
}
|
|
16824
|
+
/*
|
|
16825
|
+
* Queues whatever the camera has moved onto since the last decision.
|
|
16826
|
+
*/
|
|
16827
|
+
admitNewlyInView() {
|
|
16828
|
+
if (this.disposed || !this.historic) {
|
|
16829
|
+
return;
|
|
16830
|
+
}
|
|
16831
|
+
const inView = this.decideInView();
|
|
16832
|
+
if (!inView) {
|
|
16833
|
+
return;
|
|
16834
|
+
}
|
|
16835
|
+
const admit = [];
|
|
16836
|
+
for (const entityId of inView) {
|
|
16837
|
+
if (!this.lastInView.has(entityId)) {
|
|
16838
|
+
admit.push(entityId);
|
|
16839
|
+
}
|
|
16840
|
+
}
|
|
16841
|
+
this.lastInView = new Set(inView);
|
|
16842
|
+
if (admit.length) {
|
|
16843
|
+
this.Queue(admit);
|
|
16844
|
+
}
|
|
16845
|
+
}
|
|
16846
|
+
/*
|
|
16847
|
+
* Returns the IDs a refresh should ask about, or null when the view cannot say and the answer has to be everything.
|
|
16848
|
+
*/
|
|
16849
|
+
decideInView() {
|
|
16850
|
+
if (!this.historic || !this.positionOf || !this.monitor) {
|
|
16851
|
+
return null;
|
|
16852
|
+
}
|
|
16853
|
+
const area = this.refreshArea();
|
|
16854
|
+
if (!area) {
|
|
16855
|
+
return null;
|
|
16856
|
+
}
|
|
16857
|
+
const inView = [];
|
|
16858
|
+
for (const key of this.candidateCellKeys(area)) {
|
|
16859
|
+
const cell = this.cells.get(key);
|
|
16860
|
+
if (!cell) {
|
|
16861
|
+
continue;
|
|
16862
|
+
}
|
|
16863
|
+
for (let i = 0; i < cell.ids.length; i++) {
|
|
16864
|
+
if (HistoricFetchOrder.InDegreeBounds(cell.lats[i], cell.lons[i], area)) {
|
|
16865
|
+
inView.push(cell.ids[i]);
|
|
16866
|
+
}
|
|
16867
|
+
}
|
|
16868
|
+
}
|
|
16869
|
+
// Anything the source cannot place yet is asked about rather than quietly dropped.
|
|
16870
|
+
for (const entityId of this.unplaced) {
|
|
16871
|
+
inView.push(entityId);
|
|
16872
|
+
}
|
|
16873
|
+
return inView;
|
|
16874
|
+
}
|
|
16875
|
+
/*
|
|
16876
|
+
* Returns the cell keys a rectangle can reach.
|
|
16877
|
+
*/
|
|
16878
|
+
candidateCellKeys(area) {
|
|
16879
|
+
const size = EntityGatherer.CELL_DEGREES;
|
|
16880
|
+
const southCell = Math.floor(area.south / size);
|
|
16881
|
+
const northCell = Math.floor(area.north / size);
|
|
16882
|
+
const spans = (area.west > area.east ? [
|
|
16883
|
+
{
|
|
16884
|
+
from: Math.floor(area.west / size),
|
|
16885
|
+
to: Math.floor(180 / size)
|
|
16886
|
+
},
|
|
16887
|
+
{
|
|
16888
|
+
from: Math.floor(-180 / size),
|
|
16889
|
+
to: Math.floor(area.east / size)
|
|
16890
|
+
}
|
|
16891
|
+
] :
|
|
16892
|
+
[{
|
|
16893
|
+
from: Math.floor(area.west / size),
|
|
16894
|
+
to: Math.floor(area.east / size)
|
|
16895
|
+
}]);
|
|
16896
|
+
let width = 0;
|
|
16897
|
+
for (const span of spans) {
|
|
16898
|
+
width += (span.to - span.from) + 1;
|
|
16899
|
+
}
|
|
16900
|
+
const wanted = ((northCell - southCell) + 1) * width;
|
|
16901
|
+
if (!(wanted > 0) || wanted > this.cells.size) {
|
|
16902
|
+
return Array.from(this.cells.keys());
|
|
16903
|
+
}
|
|
16904
|
+
const keys = [];
|
|
16905
|
+
for (let lat = southCell; lat <= northCell; lat++) {
|
|
16906
|
+
for (const span of spans) {
|
|
16907
|
+
for (let lon = span.from; lon <= span.to; lon++) {
|
|
16908
|
+
keys.push(lat + ":" + lon);
|
|
16909
|
+
}
|
|
16910
|
+
}
|
|
16911
|
+
}
|
|
16912
|
+
return keys;
|
|
16913
|
+
}
|
|
16914
|
+
/*
|
|
16915
|
+
* Files an Entity in the cell its position falls in.
|
|
16916
|
+
*/
|
|
16917
|
+
place(entityId) {
|
|
16918
|
+
const degrees = this.degreesOf(entityId);
|
|
16919
|
+
if (!degrees) {
|
|
16920
|
+
this.unplaced.add(entityId);
|
|
16921
|
+
return;
|
|
16922
|
+
}
|
|
16923
|
+
const size = EntityGatherer.CELL_DEGREES;
|
|
16924
|
+
const key = Math.floor(degrees.lat / size) + ":" + Math.floor(degrees.lon / size);
|
|
16925
|
+
let cell = this.cells.get(key);
|
|
16926
|
+
if (!cell) {
|
|
16927
|
+
cell = { ids: [], lats: [], lons: [] };
|
|
16928
|
+
this.cells.set(key, cell);
|
|
16929
|
+
}
|
|
16930
|
+
cell.ids.push(entityId);
|
|
16931
|
+
cell.lats.push(degrees.lat);
|
|
16932
|
+
cell.lons.push(degrees.lon);
|
|
16933
|
+
this.unplaced.delete(entityId);
|
|
16934
|
+
}
|
|
16935
|
+
/*
|
|
16936
|
+
* Retries a bounded number of the Entities the source could not place before, so a late baked
|
|
16937
|
+
* position joins the index without any one tick paying for all of them.
|
|
16938
|
+
*/
|
|
16939
|
+
placeStragglers() {
|
|
16940
|
+
if (!this.unplaced.size || !this.positionOf) {
|
|
16941
|
+
return;
|
|
16942
|
+
}
|
|
16943
|
+
let budget = EntityGatherer.PLACE_BATCH;
|
|
16944
|
+
for (const entityId of this.unplaced) {
|
|
16945
|
+
if (budget-- <= 0) {
|
|
16946
|
+
return;
|
|
16947
|
+
}
|
|
16948
|
+
this.place(entityId);
|
|
16949
|
+
}
|
|
16950
|
+
}
|
|
16951
|
+
clearIndex() {
|
|
16952
|
+
this.cells.clear();
|
|
16953
|
+
this.unplaced.clear();
|
|
16954
|
+
this.lastInView.clear();
|
|
16955
|
+
}
|
|
16956
|
+
/*
|
|
16957
|
+
* Returns where an Entity sits in degrees, or null when the source cannot place it.
|
|
16958
|
+
*/
|
|
16959
|
+
degreesOf(entityId) {
|
|
16960
|
+
let pos = null;
|
|
16961
|
+
try {
|
|
16962
|
+
pos = this.positionOf ? this.positionOf(entityId) : null;
|
|
16963
|
+
}
|
|
16964
|
+
catch {
|
|
16965
|
+
pos = null;
|
|
16966
|
+
}
|
|
16967
|
+
return pos ? CartesianToDegrees(pos) : null;
|
|
16968
|
+
}
|
|
16969
|
+
/*
|
|
16970
|
+
* Returns the visible rectangle plus the margin a refresh still reaches into,
|
|
16971
|
+
* or null when the view cannot say, which is what leaves every Entity in.
|
|
16972
|
+
*/
|
|
16973
|
+
refreshArea() {
|
|
16974
|
+
const focus = HistoricFetchOrder.FocusFrom({ viewer: this.viewer, monitor: this.monitor });
|
|
16975
|
+
if (!(focus === null || focus === void 0 ? void 0 : focus.bounds)) {
|
|
16976
|
+
return null;
|
|
16977
|
+
}
|
|
16978
|
+
return HistoricFetchOrder.GrowBounds(focus.bounds, HistoricFetchOrder.RefreshMarginFor(focus.bounds));
|
|
14730
16979
|
}
|
|
14731
16980
|
/**
|
|
14732
16981
|
* Re-bands the pending queue so the nearest Entities are requested first.
|
|
@@ -14739,8 +16988,6 @@ class EntityGatherer {
|
|
|
14739
16988
|
if (!focus) {
|
|
14740
16989
|
return;
|
|
14741
16990
|
}
|
|
14742
|
-
// Re-banding on every batch would cost a full pass per request for no change in the answer.
|
|
14743
|
-
// Measured on the focus, so panning across the ground counts even when the eye barely moves.
|
|
14744
16991
|
if (this.lastOrderFocus && !EntityGatherer.boundsChanged(this.lastOrderFocus, focus) &&
|
|
14745
16992
|
Cartesian3.distance(this.lastOrderFocus.origin, focus.origin) < EntityGatherer.REORDER_MOVE_METRES) {
|
|
14746
16993
|
return;
|
|
@@ -14787,26 +17034,47 @@ class EntityGatherer {
|
|
|
14787
17034
|
});
|
|
14788
17035
|
const ordered = [];
|
|
14789
17036
|
for (const band of banded) {
|
|
17037
|
+
// Within a band, the ones a read has answered for go first.
|
|
17038
|
+
// A building that has never produced a record is the one least likely to start now, so it waits.
|
|
17039
|
+
const withData = [];
|
|
17040
|
+
const withoutData = [];
|
|
14790
17041
|
for (const entityId of band) {
|
|
17042
|
+
(this.everFound.has(entityId) ? withData : withoutData).push(entityId);
|
|
17043
|
+
}
|
|
17044
|
+
for (const entityId of withData) {
|
|
17045
|
+
ordered.push(entityId);
|
|
17046
|
+
}
|
|
17047
|
+
for (const entityId of withoutData) {
|
|
14791
17048
|
ordered.push(entityId);
|
|
14792
17049
|
}
|
|
14793
17050
|
}
|
|
14794
17051
|
return ordered;
|
|
14795
17052
|
}
|
|
17053
|
+
/*
|
|
17054
|
+
* Takes an ID into the known set, filing it in the index the first time it is seen.
|
|
17055
|
+
*/
|
|
17056
|
+
remember(entityId) {
|
|
17057
|
+
if (this.allEIds.has(entityId)) {
|
|
17058
|
+
return;
|
|
17059
|
+
}
|
|
17060
|
+
this.allEIds.add(entityId);
|
|
17061
|
+
// Only a historic refresh reads the index, so nothing else pays to build it.
|
|
17062
|
+
if (this.historic) {
|
|
17063
|
+
this.place(entityId);
|
|
17064
|
+
}
|
|
17065
|
+
}
|
|
14796
17066
|
Queue(entityIds, topOfQueue = false) {
|
|
14797
17067
|
if (this.disposed || !(entityIds === null || entityIds === void 0 ? void 0 : entityIds.length)) {
|
|
14798
17068
|
return;
|
|
14799
17069
|
}
|
|
14800
17070
|
let changes = 0;
|
|
14801
17071
|
if (topOfQueue) {
|
|
14802
|
-
// Walked back to front so a repeated ID keeps the position of its last occurrence, then
|
|
14803
|
-
// laid down in that order, which is where inserting one at a time leaves them.
|
|
14804
17072
|
const toFront = [];
|
|
14805
17073
|
const promoting = new Set();
|
|
14806
17074
|
let anyAlreadyQueued = false;
|
|
14807
17075
|
for (let i = entityIds.length - 1; i >= 0; i--) {
|
|
14808
17076
|
const eId = entityIds[i];
|
|
14809
|
-
this.
|
|
17077
|
+
this.remember(eId);
|
|
14810
17078
|
if (promoting.has(eId)) {
|
|
14811
17079
|
continue;
|
|
14812
17080
|
}
|
|
@@ -14842,7 +17110,7 @@ class EntityGatherer {
|
|
|
14842
17110
|
else {
|
|
14843
17111
|
for (let i = 0; i < entityIds.length; i++) {
|
|
14844
17112
|
const eId = entityIds[i];
|
|
14845
|
-
this.
|
|
17113
|
+
this.remember(eId);
|
|
14846
17114
|
if (this.eIdQueued.has(eId)) {
|
|
14847
17115
|
continue;
|
|
14848
17116
|
}
|
|
@@ -14857,19 +17125,22 @@ class EntityGatherer {
|
|
|
14857
17125
|
this.onTick(false);
|
|
14858
17126
|
}
|
|
14859
17127
|
Dispose() {
|
|
17128
|
+
var _a;
|
|
14860
17129
|
if (this.disposed) {
|
|
14861
17130
|
return;
|
|
14862
17131
|
}
|
|
14863
17132
|
this.disposed = true;
|
|
17133
|
+
(_a = this.monitorRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
17134
|
+
this.monitorRemoval = null;
|
|
14864
17135
|
this.hDisposals.forEach((d) => d());
|
|
14865
17136
|
this.hDisposals = [];
|
|
14866
|
-
// A tick still awaiting a response resumes after this, and anything left here would have it
|
|
14867
|
-
// ask for the whole set again on the way out.
|
|
14868
17137
|
this.eIdQueue = [];
|
|
14869
17138
|
this.eIdQueued.clear();
|
|
14870
17139
|
this.priorityCount = 0;
|
|
14871
17140
|
this.allEIds.clear();
|
|
14872
17141
|
this.lastFoundDateTimes.clear();
|
|
17142
|
+
this.everFound.clear();
|
|
17143
|
+
this.clearIndex();
|
|
14873
17144
|
this.entityDataOverrides.clear();
|
|
14874
17145
|
}
|
|
14875
17146
|
checkRange(force) {
|
|
@@ -14893,6 +17164,7 @@ class EntityGatherer {
|
|
|
14893
17164
|
this.lastDateTime = rTime;
|
|
14894
17165
|
(async () => {
|
|
14895
17166
|
this.runningTick = true;
|
|
17167
|
+
this.placeStragglers();
|
|
14896
17168
|
let rerun = false;
|
|
14897
17169
|
try {
|
|
14898
17170
|
const handleResponse = async (requestedBatch, entities) => {
|
|
@@ -14905,6 +17177,13 @@ class EntityGatherer {
|
|
|
14905
17177
|
foundById.set(entityId, entity);
|
|
14906
17178
|
}
|
|
14907
17179
|
}
|
|
17180
|
+
if (this.historic) {
|
|
17181
|
+
for (const entityId of requestedBatch || []) {
|
|
17182
|
+
if (foundById.has(entityId)) {
|
|
17183
|
+
this.everFound.add(entityId);
|
|
17184
|
+
}
|
|
17185
|
+
}
|
|
17186
|
+
}
|
|
14908
17187
|
for (const entityId of requestedBatch || []) {
|
|
14909
17188
|
const override = this.entityDataOverrides.get(entityId);
|
|
14910
17189
|
if (override) {
|
|
@@ -15033,15 +17312,16 @@ class EntityGatherer {
|
|
|
15033
17312
|
this._onQueueProgress.Trigger(100);
|
|
15034
17313
|
}
|
|
15035
17314
|
}
|
|
15036
|
-
// Now run through
|
|
17315
|
+
// Now run through the IDs we've seen that the view still reaches, and request them.
|
|
15037
17316
|
// We'll skip those that we just requested.
|
|
15038
|
-
|
|
17317
|
+
const inView = this.decideInView();
|
|
17318
|
+
this.lastInView = inView ? new Set(inView) : new Set();
|
|
17319
|
+
let allEIds = inView || Array.from(this.allEIds);
|
|
15039
17320
|
if (requestedIds.size) {
|
|
15040
17321
|
allEIds = allEIds.filter((eId) => !requestedIds.has(eId));
|
|
15041
17322
|
}
|
|
15042
17323
|
if (allEIds.length) {
|
|
15043
|
-
//
|
|
15044
|
-
// first here even more than in the queue above.
|
|
17324
|
+
// The near ones are resolved first out of whatever the view still reaches.
|
|
15045
17325
|
allEIds = this.bandIds(allEIds);
|
|
15046
17326
|
while (allEIds.length && !this.disposed && !this.eIdQueue.length) {
|
|
15047
17327
|
if (this.historic && this.lastDateTime !== rTime) {
|
|
@@ -15080,6 +17360,11 @@ class EntityGatherer {
|
|
|
15080
17360
|
}
|
|
15081
17361
|
// Below this the whole queue clears in one or two requests, so ordering it buys nothing.
|
|
15082
17362
|
EntityGatherer.MIN_ORDER_QUEUE = 500;
|
|
17363
|
+
// Side of the ground cells Entities are bucketed into, in degrees.
|
|
17364
|
+
// About a kilometre, so a view covers a handful of them rather than hundreds.
|
|
17365
|
+
EntityGatherer.CELL_DEGREES = 0.01;
|
|
17366
|
+
// How many Entities one tick will try to place in the index.
|
|
17367
|
+
EntityGatherer.PLACE_BATCH = 20000;
|
|
15083
17368
|
// How far the point being looked at has to move before the queue is worth re-banding, in metres.
|
|
15084
17369
|
// Under the nearest band's width, so a move that could change what is nearest still triggers one.
|
|
15085
17370
|
EntityGatherer.REORDER_MOVE_METRES = 250;
|
|
@@ -47923,7 +50208,7 @@ var StyleUtils;
|
|
|
47923
50208
|
StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
|
|
47924
50209
|
})(StyleUtils || (StyleUtils = {}));
|
|
47925
50210
|
|
|
47926
|
-
const VERSION = "7.
|
|
50211
|
+
const VERSION = "7.4.0";
|
|
47927
50212
|
/**
|
|
47928
50213
|
* Updates the environment instance used by bruce-cesium to one specified.
|
|
47929
50214
|
* This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.
|
|
@@ -47940,5 +50225,5 @@ const getENVIRONMENT = () => {
|
|
|
47940
50225
|
return ENVIRONMENT;
|
|
47941
50226
|
};
|
|
47942
50227
|
|
|
47943
|
-
export { VERSION, setENVIRONMENT, getENVIRONMENT, CesiumAnimatedInOut, DisplacedSurfacePrimitive, CesiumAnimatedProperty, isStyleChanged, isOutlineChanged$1 as isOutlineChanged, StyleEffective, EntityRenderEngine, EntityRenderEngineModel3d, EntityRenderEnginePoint, EntityRenderEnginePolygon, EntityRenderEnginePolyline, TextureFrameSeriesAnimator, TextureValueLabels, MenuItemCreator, MenuItemManager, CesiumParabola, EntityLabel, LiveCursor, SharedGetters, DataSourceStaticKmlManager, DataLabRenderManager, EntitiesIdsRenderManager, EntitiesLoadedRenderManager, EntitiesRenderManager, EntityRenderManager, AssemblyRenderManager, GoogleSearchRenderManager, RelationsRenderManager, RenderManager, TilesetArbRenderManager, TilesetCadRenderManager, TilesetEntitiesRenderManager, TilesetGooglePhotosRenderManager, TilesetOsmRenderManager, TilesetPointcloudRenderManager, TileRenderEngine, createTileset, TilesetRenderEngine, ViewRenderEngine, VisualRegisterCuller, VisualsRegister, XGridsRenderEngine, CesiumEntityStyler, DrawingUtils, EntityUtils, MeasureUtils, StyleUtils, CESIUM_INSPECTOR_KEY, CESIUM_TIMELINE_KEY, CESIUM_TIMELINE_LIVE_KEY, CESIUM_TIMELINE_LIVE_PADDING_KEY, CESIUM_TIMELINE_INTERVAL_KEY, CESIUM_MODEL_SPACE_KEY, DEFAULT_LIVE_PADDING_SECONDS, DEFAULT_BRIGHTNESS_SHIFT, DEFAULT_LIGHT_INTENSITY, LIGHTING_SETTINGS_VERSION, LIGHT_INTENSITY_COMPENSATION, MAX_COMPENSATED_LIGHT_INTENSITY, ViewUtils, CesiumViewMonitor, ViewGroundArea, ViewerEventTracker, ViewerUtils, Draw3dPolygon, Draw3dPolyline, MeasureCreator, Walkthrough, WidgetControlViewBar, WidgetControlViewBarSearch, WidgetLeftPanelTab, WidgetLeftPanelTabBookmarks, WidgetLeftPanelTabDashboard, EMenuItemState, WidgetLeftPanelTabMenuItems, Widget, VIEWER_BOOKMARKS_WIDGET_KEY, WidgetBookmarks, WidgetBranding, ECursor, WidgetCursorBar, WidgetEmbeddedInfoView, WidgetInfoView, VIEWER_LEFT_PANEL_WIDGET_KEY, VIEWER_LEFT_PANEL_CSS_VAR_LEFT, WidgetLeftPanel, WidgetNavCompass$$1 as WidgetNavCompass, VIEWER_VIEW_BAR_WIDGET_KEY, WidgetViewBar };
|
|
50228
|
+
export { VERSION, setENVIRONMENT, getENVIRONMENT, ClusterItems, PointClustering, PointClusterSize, CesiumAnimatedInOut, DisplacedSurfacePrimitive, CesiumAnimatedProperty, isStyleChanged, isOutlineChanged$1 as isOutlineChanged, StyleEffective, EntityRenderEngine, EntityRenderEngineModel3d, EntityRenderEnginePoint, EntityRenderEnginePolygon, EntityRenderEnginePolyline, TextureFrameSeriesAnimator, TextureValueLabels, MenuItemCreator, MenuItemManager, CesiumParabola, EntityLabel, EntityLabelGroup, LiveCursor, SharedGetters, DataSourceStaticKmlManager, DataLabRenderManager, EntitiesIdsRenderManager, EntitiesLoadedRenderManager, EntitiesRenderManager, EntityRenderManager, AssemblyRenderManager, GoogleSearchRenderManager, RelationsRenderManager, RenderManager, TilesetArbRenderManager, TilesetCadRenderManager, TilesetEntitiesRenderManager, TilesetGooglePhotosRenderManager, TilesetOsmRenderManager, TilesetPointcloudRenderManager, TileRenderEngine, createTileset, TilesetRenderEngine, ViewRenderEngine, VisualRegisterCuller, VisualsRegister, XGridsRenderEngine, CesiumEntityStyler, DrawingUtils, EntityUtils, MeasureUtils, StyleUtils, CESIUM_INSPECTOR_KEY, CESIUM_TIMELINE_KEY, CESIUM_TIMELINE_LIVE_KEY, CESIUM_TIMELINE_LIVE_PADDING_KEY, CESIUM_TIMELINE_INTERVAL_KEY, CESIUM_MODEL_SPACE_KEY, DEFAULT_LIVE_PADDING_SECONDS, DEFAULT_BRIGHTNESS_SHIFT, DEFAULT_LIGHT_INTENSITY, LIGHTING_SETTINGS_VERSION, LIGHT_INTENSITY_COMPENSATION, MAX_COMPENSATED_LIGHT_INTENSITY, ViewUtils, CesiumViewMonitor, ViewGroundArea, ViewerEventTracker, ViewerUtils, Draw3dPolygon, Draw3dPolyline, MeasureCreator, Walkthrough, WidgetControlViewBar, WidgetControlViewBarSearch, WidgetLeftPanelTab, WidgetLeftPanelTabBookmarks, WidgetLeftPanelTabDashboard, EMenuItemState, WidgetLeftPanelTabMenuItems, Widget, VIEWER_BOOKMARKS_WIDGET_KEY, WidgetBookmarks, WidgetBranding, ECursor, WidgetCursorBar, WidgetEmbeddedInfoView, WidgetInfoView, VIEWER_LEFT_PANEL_WIDGET_KEY, VIEWER_LEFT_PANEL_CSS_VAR_LEFT, WidgetLeftPanel, WidgetNavCompass$$1 as WidgetNavCompass, VIEWER_VIEW_BAR_WIDGET_KEY, WidgetViewBar };
|
|
47944
50229
|
//# sourceMappingURL=bruce-cesium.es5.js.map
|