maplibre-gl-lidar 0.6.2 → 0.7.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/{LidarLayerAdapter-Sw_plY5a.cjs → LidarLayerAdapter-eh59KEMT.cjs} +1347 -152
- package/dist/LidarLayerAdapter-eh59KEMT.cjs.map +1 -0
- package/dist/{LidarLayerAdapter-mP-0IyJm.js → LidarLayerAdapter-kyyfQw05.js} +1356 -161
- package/dist/LidarLayerAdapter-kyyfQw05.js.map +1 -0
- package/dist/index.cjs +5 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +14 -10
- package/dist/maplibre-gl-lidar.css +119 -0
- package/dist/react.cjs +8 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.mjs +10 -3
- package/dist/react.mjs.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/colorizers/ColorScheme.d.ts +75 -6
- package/dist/types/lib/colorizers/ColorScheme.d.ts.map +1 -1
- package/dist/types/lib/colorizers/Colormaps.d.ts +25 -0
- package/dist/types/lib/colorizers/Colormaps.d.ts.map +1 -0
- package/dist/types/lib/core/LidarControl.d.ts +43 -1
- package/dist/types/lib/core/LidarControl.d.ts.map +1 -1
- package/dist/types/lib/core/types.d.ts +46 -0
- package/dist/types/lib/core/types.d.ts.map +1 -1
- package/dist/types/lib/gui/Colorbar.d.ts +86 -0
- package/dist/types/lib/gui/Colorbar.d.ts.map +1 -0
- package/dist/types/lib/gui/DualRangeSlider.d.ts +9 -0
- package/dist/types/lib/gui/DualRangeSlider.d.ts.map +1 -1
- package/dist/types/lib/gui/PanelBuilder.d.ts +31 -5
- package/dist/types/lib/gui/PanelBuilder.d.ts.map +1 -1
- package/dist/types/lib/gui/PercentileRangeControl.d.ts +113 -0
- package/dist/types/lib/gui/PercentileRangeControl.d.ts.map +1 -0
- package/dist/types/lib/gui/index.d.ts +4 -0
- package/dist/types/lib/gui/index.d.ts.map +1 -1
- package/dist/types/lib/hooks/useLidarState.d.ts.map +1 -1
- package/dist/types/lib/layers/PointCloudManager.d.ts +22 -1
- package/dist/types/lib/layers/PointCloudManager.d.ts.map +1 -1
- package/dist/types/lib/layers/types.d.ts +11 -1
- package/dist/types/lib/layers/types.d.ts.map +1 -1
- package/dist/types/lib/loaders/CopcStreamingLoader.d.ts.map +1 -1
- package/dist/types/lib/loaders/EptStreamingLoader.d.ts +44 -0
- package/dist/types/lib/loaders/EptStreamingLoader.d.ts.map +1 -1
- package/dist/types/lib/loaders/streaming-types.d.ts +6 -0
- package/dist/types/lib/loaders/streaming-types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/LidarLayerAdapter-Sw_plY5a.cjs.map +0 -1
- package/dist/LidarLayerAdapter-mP-0IyJm.js.map +0 -1
|
@@ -34361,10 +34361,12 @@ function clampLatLng$1(lng, lat, context = "") {
|
|
|
34361
34361
|
}
|
|
34362
34362
|
const DEFAULT_OPTIONS$2 = {
|
|
34363
34363
|
pointBudget: 5e6,
|
|
34364
|
-
maxConcurrentRequests:
|
|
34365
|
-
viewportDebounceMs:
|
|
34364
|
+
maxConcurrentRequests: 8,
|
|
34365
|
+
viewportDebounceMs: 100,
|
|
34366
34366
|
minDetailZoom: 10,
|
|
34367
|
-
maxOctreeDepth: 20
|
|
34367
|
+
maxOctreeDepth: 20,
|
|
34368
|
+
maxSubtreesPerViewport: 60
|
|
34369
|
+
// Not used by COPC, but required by interface
|
|
34368
34370
|
};
|
|
34369
34371
|
class CopcStreamingLoader {
|
|
34370
34372
|
/**
|
|
@@ -35104,10 +35106,11 @@ function getVerticalUnitConversionFactor(wkt2) {
|
|
|
35104
35106
|
}
|
|
35105
35107
|
const DEFAULT_OPTIONS$1 = {
|
|
35106
35108
|
pointBudget: 5e6,
|
|
35107
|
-
maxConcurrentRequests:
|
|
35108
|
-
viewportDebounceMs:
|
|
35109
|
+
maxConcurrentRequests: 8,
|
|
35110
|
+
viewportDebounceMs: 100,
|
|
35109
35111
|
minDetailZoom: 10,
|
|
35110
|
-
maxOctreeDepth: 20
|
|
35112
|
+
maxOctreeDepth: 20,
|
|
35113
|
+
maxSubtreesPerViewport: 60
|
|
35111
35114
|
};
|
|
35112
35115
|
class EptStreamingLoader {
|
|
35113
35116
|
/**
|
|
@@ -35122,6 +35125,8 @@ class EptStreamingLoader {
|
|
|
35122
35125
|
__publicField(this, "_metadata", null);
|
|
35123
35126
|
// Hierarchy cache
|
|
35124
35127
|
__publicField(this, "_hierarchyCache", /* @__PURE__ */ new Map());
|
|
35128
|
+
__publicField(this, "_hierarchyLoading", /* @__PURE__ */ new Set());
|
|
35129
|
+
__publicField(this, "_hierarchyFailures", /* @__PURE__ */ new Map());
|
|
35125
35130
|
__publicField(this, "_subtreeRoots", /* @__PURE__ */ new Set());
|
|
35126
35131
|
__publicField(this, "_rootHierarchyLoaded", false);
|
|
35127
35132
|
// Node cache
|
|
@@ -35159,6 +35164,7 @@ class EptStreamingLoader {
|
|
|
35159
35164
|
__publicField(this, "_pendingLayerUpdate", false);
|
|
35160
35165
|
__publicField(this, "_updateBatchTimeout", null);
|
|
35161
35166
|
__publicField(this, "_onPointsLoaded");
|
|
35167
|
+
__publicField(this, "_isResetting", false);
|
|
35162
35168
|
this._baseUrl = eptUrl.endsWith("/ept.json") ? eptUrl.slice(0, -9) : eptUrl.replace(/\/$/, "");
|
|
35163
35169
|
this._options = { ...DEFAULT_OPTIONS$1, ...options };
|
|
35164
35170
|
}
|
|
@@ -35418,22 +35424,26 @@ class EptStreamingLoader {
|
|
|
35418
35424
|
* @param key - Hierarchy key (e.g., "0-0-0-0")
|
|
35419
35425
|
*/
|
|
35420
35426
|
async _loadHierarchy(key) {
|
|
35421
|
-
if (this._hierarchyCache.has(key)) return;
|
|
35427
|
+
if (this._hierarchyCache.has(key) || this._hierarchyLoading.has(key)) return;
|
|
35422
35428
|
const url = `${this._baseUrl}/ept-hierarchy/${key}.json`;
|
|
35429
|
+
this._hierarchyLoading.add(key);
|
|
35423
35430
|
try {
|
|
35424
35431
|
const response = await fetch(url);
|
|
35425
35432
|
if (!response.ok) {
|
|
35433
|
+
this._hierarchyFailures.set(key, Date.now());
|
|
35426
35434
|
console.warn(`Failed to load hierarchy ${key}: ${response.status}`);
|
|
35427
35435
|
return;
|
|
35428
35436
|
}
|
|
35429
35437
|
const hierarchy2 = await response.json();
|
|
35430
35438
|
this._hierarchyCache.set(key, hierarchy2);
|
|
35439
|
+
this._hierarchyFailures.delete(key);
|
|
35431
35440
|
for (const [nodeKey, value] of Object.entries(hierarchy2)) {
|
|
35432
35441
|
const keyArray = this._parseNodeKey(nodeKey);
|
|
35433
35442
|
const { bounds: bounds2, boundsWgs84 } = this._calculateNodeBounds(keyArray);
|
|
35443
|
+
const existingNode = this._nodeCache.get(nodeKey);
|
|
35434
35444
|
if (value === -1) {
|
|
35435
35445
|
this._subtreeRoots.add(nodeKey);
|
|
35436
|
-
if (!
|
|
35446
|
+
if (!existingNode) {
|
|
35437
35447
|
this._nodeCache.set(nodeKey, {
|
|
35438
35448
|
key: nodeKey,
|
|
35439
35449
|
keyArray,
|
|
@@ -35444,19 +35454,29 @@ class EptStreamingLoader {
|
|
|
35444
35454
|
boundsWgs84
|
|
35445
35455
|
});
|
|
35446
35456
|
}
|
|
35447
|
-
} else if (value > 0
|
|
35448
|
-
|
|
35449
|
-
|
|
35450
|
-
|
|
35451
|
-
|
|
35452
|
-
|
|
35453
|
-
|
|
35454
|
-
|
|
35455
|
-
|
|
35457
|
+
} else if (value > 0) {
|
|
35458
|
+
if ((existingNode == null ? void 0 : existingNode.state) === "subtree") {
|
|
35459
|
+
existingNode.state = "pending";
|
|
35460
|
+
existingNode.pointCount = value;
|
|
35461
|
+
existingNode.bounds = bounds2;
|
|
35462
|
+
existingNode.boundsWgs84 = boundsWgs84;
|
|
35463
|
+
} else if (!existingNode) {
|
|
35464
|
+
this._nodeCache.set(nodeKey, {
|
|
35465
|
+
key: nodeKey,
|
|
35466
|
+
keyArray,
|
|
35467
|
+
state: "pending",
|
|
35468
|
+
pointCount: value,
|
|
35469
|
+
bounds: bounds2,
|
|
35470
|
+
boundsWgs84
|
|
35471
|
+
});
|
|
35472
|
+
}
|
|
35456
35473
|
}
|
|
35457
35474
|
}
|
|
35458
35475
|
} catch (error) {
|
|
35476
|
+
this._hierarchyFailures.set(key, Date.now());
|
|
35459
35477
|
console.warn(`Error loading hierarchy ${key}:`, error);
|
|
35478
|
+
} finally {
|
|
35479
|
+
this._hierarchyLoading.delete(key);
|
|
35460
35480
|
}
|
|
35461
35481
|
}
|
|
35462
35482
|
/**
|
|
@@ -35481,22 +35501,31 @@ class EptStreamingLoader {
|
|
|
35481
35501
|
}
|
|
35482
35502
|
await this._ensureHierarchyLoaded();
|
|
35483
35503
|
const targetDepth = viewport.targetDepth;
|
|
35484
|
-
const
|
|
35485
|
-
|
|
35486
|
-
|
|
35487
|
-
|
|
35488
|
-
|
|
35489
|
-
|
|
35490
|
-
|
|
35491
|
-
|
|
35492
|
-
|
|
35493
|
-
|
|
35494
|
-
|
|
35495
|
-
|
|
35496
|
-
|
|
35504
|
+
const maxSubtreesToLoad = Math.max(1, this._options.maxSubtreesPerViewport);
|
|
35505
|
+
const loadedSubtrees = /* @__PURE__ */ new Set();
|
|
35506
|
+
const maxPasses = 3;
|
|
35507
|
+
const now = Date.now();
|
|
35508
|
+
const hierarchyRetryCooldownMs = 5e3;
|
|
35509
|
+
for (let pass = 0; pass < maxPasses; pass++) {
|
|
35510
|
+
const subtreeCandidates = [];
|
|
35511
|
+
for (const [, node] of this._nodeCache) {
|
|
35512
|
+
const depth = node.keyArray[0];
|
|
35513
|
+
if (depth > targetDepth + 3) continue;
|
|
35514
|
+
const lastFailure = this._hierarchyFailures.get(node.key);
|
|
35515
|
+
if (node.state === "subtree" && !this._hierarchyCache.has(node.key) && !this._hierarchyLoading.has(node.key) && !loadedSubtrees.has(node.key) && (!lastFailure || now - lastFailure >= hierarchyRetryCooldownMs) && this._boundsIntersectsViewport(node.boundsWgs84, viewport)) {
|
|
35516
|
+
const priority = this._calculateNodePriority(node.boundsWgs84, viewport);
|
|
35517
|
+
subtreeCandidates.push({ key: node.key, priority });
|
|
35518
|
+
}
|
|
35519
|
+
}
|
|
35520
|
+
if (subtreeCandidates.length === 0) break;
|
|
35521
|
+
subtreeCandidates.sort((a, b) => a.priority - b.priority);
|
|
35522
|
+
const perPassLimit = Math.ceil(maxSubtreesToLoad / maxPasses);
|
|
35523
|
+
const subtreesToProcess = subtreeCandidates.slice(0, perPassLimit).map((s) => s.key);
|
|
35524
|
+
await Promise.all(subtreesToProcess.map((subtreeKey) => this._loadHierarchy(subtreeKey)));
|
|
35525
|
+
subtreesToProcess.forEach((key) => loadedSubtrees.add(key));
|
|
35526
|
+
if (loadedSubtrees.size >= maxSubtreesToLoad) break;
|
|
35497
35527
|
}
|
|
35498
35528
|
const nodesToLoad = [];
|
|
35499
|
-
const now = Date.now();
|
|
35500
35529
|
const retryCooldownMs = 5e3;
|
|
35501
35530
|
for (const [, node] of this._nodeCache) {
|
|
35502
35531
|
const depth = node.keyArray[0];
|
|
@@ -35505,7 +35534,7 @@ class EptStreamingLoader {
|
|
|
35505
35534
|
if (node.lastFailedAt && now - node.lastFailedAt < retryCooldownMs) {
|
|
35506
35535
|
continue;
|
|
35507
35536
|
}
|
|
35508
|
-
if (depth > targetDepth +
|
|
35537
|
+
if (depth > targetDepth + 2) continue;
|
|
35509
35538
|
if (!this._boundsIntersectsViewport(node.boundsWgs84, viewport)) {
|
|
35510
35539
|
continue;
|
|
35511
35540
|
}
|
|
@@ -35826,6 +35855,27 @@ class EptStreamingLoader {
|
|
|
35826
35855
|
wkt: (_c = (_b = this._metadata) == null ? void 0 : _b.srs) == null ? void 0 : _c.wkt
|
|
35827
35856
|
};
|
|
35828
35857
|
}
|
|
35858
|
+
/**
|
|
35859
|
+
* Checks whether there are subtree hierarchies still pending for the viewport.
|
|
35860
|
+
*
|
|
35861
|
+
* @param viewport - Current viewport information
|
|
35862
|
+
* @returns True if more subtree hierarchies should be loaded
|
|
35863
|
+
*/
|
|
35864
|
+
hasPendingSubtrees(viewport) {
|
|
35865
|
+
if (!this._isInitialized) return false;
|
|
35866
|
+
const targetDepth = viewport.targetDepth;
|
|
35867
|
+
const now = Date.now();
|
|
35868
|
+
const hierarchyRetryCooldownMs = 5e3;
|
|
35869
|
+
for (const [, node] of this._nodeCache) {
|
|
35870
|
+
const depth = node.keyArray[0];
|
|
35871
|
+
if (depth > targetDepth + 3) continue;
|
|
35872
|
+
const lastFailure = this._hierarchyFailures.get(node.key);
|
|
35873
|
+
if (node.state === "subtree" && !this._hierarchyCache.has(node.key) && !this._hierarchyLoading.has(node.key) && (!lastFailure || now - lastFailure >= hierarchyRetryCooldownMs) && this._boundsIntersectsViewport(node.boundsWgs84, viewport)) {
|
|
35874
|
+
return true;
|
|
35875
|
+
}
|
|
35876
|
+
}
|
|
35877
|
+
return false;
|
|
35878
|
+
}
|
|
35829
35879
|
/**
|
|
35830
35880
|
* Gets the current streaming progress.
|
|
35831
35881
|
*/
|
|
@@ -35881,6 +35931,60 @@ class EptStreamingLoader {
|
|
|
35881
35931
|
getLoadedPointCount() {
|
|
35882
35932
|
return this._totalLoadedPoints;
|
|
35883
35933
|
}
|
|
35934
|
+
/**
|
|
35935
|
+
* Gets the current point budget.
|
|
35936
|
+
*/
|
|
35937
|
+
getPointBudget() {
|
|
35938
|
+
return this._options.pointBudget;
|
|
35939
|
+
}
|
|
35940
|
+
/**
|
|
35941
|
+
* Checks if any nodes intersecting the viewport have already been loaded.
|
|
35942
|
+
*
|
|
35943
|
+
* @param viewport - Current viewport information
|
|
35944
|
+
* @returns True if viewport has loaded coverage
|
|
35945
|
+
*/
|
|
35946
|
+
hasLoadedNodesInViewport(viewport, minDepth = 0) {
|
|
35947
|
+
for (const [, node] of this._nodeCache) {
|
|
35948
|
+
if (node.state !== "loaded") continue;
|
|
35949
|
+
if (node.keyArray[0] < minDepth) continue;
|
|
35950
|
+
if (this._boundsIntersectsViewport(node.boundsWgs84, viewport)) {
|
|
35951
|
+
return true;
|
|
35952
|
+
}
|
|
35953
|
+
}
|
|
35954
|
+
return false;
|
|
35955
|
+
}
|
|
35956
|
+
/**
|
|
35957
|
+
* Estimates viewport coverage ratio by loaded nodes.
|
|
35958
|
+
* Returns a value from 0 to 1 representing how much of the viewport
|
|
35959
|
+
* is covered by loaded tiles.
|
|
35960
|
+
*
|
|
35961
|
+
* @param viewport - Current viewport information
|
|
35962
|
+
* @param minDepth - Minimum octree depth to consider
|
|
35963
|
+
* @returns Coverage ratio (0-1)
|
|
35964
|
+
*/
|
|
35965
|
+
getViewportCoverageRatio(viewport, minDepth = 0) {
|
|
35966
|
+
const [west, south, east, north] = viewport.bounds;
|
|
35967
|
+
const viewportArea = (east - west) * (north - south);
|
|
35968
|
+
if (viewportArea <= 0) return 0;
|
|
35969
|
+
let coveredArea = 0;
|
|
35970
|
+
for (const [, node] of this._nodeCache) {
|
|
35971
|
+
if (node.state !== "loaded") continue;
|
|
35972
|
+
if (node.keyArray[0] < minDepth) continue;
|
|
35973
|
+
const nodeWest = node.boundsWgs84.minX;
|
|
35974
|
+
const nodeSouth = node.boundsWgs84.minY;
|
|
35975
|
+
const nodeEast = node.boundsWgs84.maxX;
|
|
35976
|
+
const nodeNorth = node.boundsWgs84.maxY;
|
|
35977
|
+
const intersectWest = Math.max(west, nodeWest);
|
|
35978
|
+
const intersectEast = Math.min(east, nodeEast);
|
|
35979
|
+
const intersectSouth = Math.max(south, nodeSouth);
|
|
35980
|
+
const intersectNorth = Math.min(north, nodeNorth);
|
|
35981
|
+
if (intersectWest < intersectEast && intersectSouth < intersectNorth) {
|
|
35982
|
+
const intersectArea = (intersectEast - intersectWest) * (intersectNorth - intersectSouth);
|
|
35983
|
+
coveredArea += intersectArea;
|
|
35984
|
+
}
|
|
35985
|
+
}
|
|
35986
|
+
return Math.min(1, coveredArea / viewportArea);
|
|
35987
|
+
}
|
|
35884
35988
|
/**
|
|
35885
35989
|
* Gets the total number of loaded nodes.
|
|
35886
35990
|
*/
|
|
@@ -35893,6 +35997,48 @@ class EptStreamingLoader {
|
|
|
35893
35997
|
isLoading() {
|
|
35894
35998
|
return this._activeRequests > 0 || this._loadingQueue.length > 0;
|
|
35895
35999
|
}
|
|
36000
|
+
/**
|
|
36001
|
+
* Removes queued nodes that are outside the current viewport and re-sorts priorities.
|
|
36002
|
+
*
|
|
36003
|
+
* @param viewport - Current viewport information
|
|
36004
|
+
*/
|
|
36005
|
+
pruneQueueForViewport(viewport) {
|
|
36006
|
+
if (this._loadingQueue.length === 0) return;
|
|
36007
|
+
this._loadingQueue = this._loadingQueue.filter(
|
|
36008
|
+
(node) => this._boundsIntersectsViewport(node.boundsWgs84, viewport)
|
|
36009
|
+
);
|
|
36010
|
+
for (const node of this._loadingQueue) {
|
|
36011
|
+
const distPriority = this._calculateNodePriority(node.boundsWgs84, viewport);
|
|
36012
|
+
const depth = node.keyArray[0];
|
|
36013
|
+
node.priority = distPriority - depth * 1e-4;
|
|
36014
|
+
}
|
|
36015
|
+
this._loadingQueue.sort((a, b) => (a.priority || Infinity) - (b.priority || Infinity));
|
|
36016
|
+
}
|
|
36017
|
+
/**
|
|
36018
|
+
* Resets loaded node data to allow loading a new area.
|
|
36019
|
+
* Keeps hierarchy cache intact but clears loaded points and node states.
|
|
36020
|
+
*
|
|
36021
|
+
* @returns True if reset occurred
|
|
36022
|
+
*/
|
|
36023
|
+
resetLoadedData() {
|
|
36024
|
+
if (this._activeRequests > 0 || this._isResetting) return false;
|
|
36025
|
+
this._isResetting = true;
|
|
36026
|
+
this._loadingQueue = [];
|
|
36027
|
+
this._totalLoadedPoints = 0;
|
|
36028
|
+
this._totalLoadedNodes = 0;
|
|
36029
|
+
for (const [, node] of this._nodeCache) {
|
|
36030
|
+
if (node.state === "loaded" || node.state === "loading" || node.state === "error") {
|
|
36031
|
+
node.state = "pending";
|
|
36032
|
+
node.bufferStartIndex = void 0;
|
|
36033
|
+
node.error = void 0;
|
|
36034
|
+
node.retryCount = void 0;
|
|
36035
|
+
node.lastFailedAt = void 0;
|
|
36036
|
+
}
|
|
36037
|
+
}
|
|
36038
|
+
this._scheduleLayerUpdate();
|
|
36039
|
+
this._isResetting = false;
|
|
36040
|
+
return true;
|
|
36041
|
+
}
|
|
35896
36042
|
/**
|
|
35897
36043
|
* Gets the EPT metadata.
|
|
35898
36044
|
*/
|
|
@@ -35909,6 +36055,8 @@ class EptStreamingLoader {
|
|
|
35909
36055
|
this._loadingQueue = [];
|
|
35910
36056
|
this._nodeCache.clear();
|
|
35911
36057
|
this._hierarchyCache.clear();
|
|
36058
|
+
this._hierarchyLoading.clear();
|
|
36059
|
+
this._hierarchyFailures.clear();
|
|
35912
36060
|
this._subtreeRoots.clear();
|
|
35913
36061
|
this._eventHandlers.clear();
|
|
35914
36062
|
this._positions = null;
|
|
@@ -36006,37 +36154,180 @@ function computePercentileBounds(arr, lowerPercentile = 2, upperPercentile = 98)
|
|
|
36006
36154
|
}
|
|
36007
36155
|
return { min, max };
|
|
36008
36156
|
}
|
|
36009
|
-
const
|
|
36157
|
+
const VIRIDIS = [
|
|
36010
36158
|
[68, 1, 84],
|
|
36011
|
-
// dark purple
|
|
36012
36159
|
[72, 40, 120],
|
|
36013
|
-
// purple
|
|
36014
36160
|
[62, 74, 137],
|
|
36015
|
-
// blue-purple
|
|
36016
36161
|
[49, 104, 142],
|
|
36017
|
-
// blue
|
|
36018
36162
|
[38, 130, 142],
|
|
36019
|
-
// teal-blue
|
|
36020
36163
|
[31, 158, 137],
|
|
36021
|
-
// teal
|
|
36022
36164
|
[53, 183, 121],
|
|
36023
|
-
// green-teal
|
|
36024
36165
|
[109, 205, 89],
|
|
36025
|
-
// green
|
|
36026
36166
|
[180, 222, 44],
|
|
36027
|
-
// yellow-green
|
|
36028
36167
|
[253, 231, 37]
|
|
36029
|
-
// yellow
|
|
36030
36168
|
];
|
|
36031
|
-
const
|
|
36169
|
+
const PLASMA = [
|
|
36170
|
+
[13, 8, 135],
|
|
36171
|
+
[75, 3, 161],
|
|
36172
|
+
[125, 3, 168],
|
|
36173
|
+
[168, 34, 150],
|
|
36174
|
+
[203, 70, 121],
|
|
36175
|
+
[229, 107, 93],
|
|
36176
|
+
[248, 148, 65],
|
|
36177
|
+
[253, 195, 40],
|
|
36178
|
+
[240, 249, 33],
|
|
36179
|
+
[240, 249, 33]
|
|
36180
|
+
];
|
|
36181
|
+
const INFERNO = [
|
|
36182
|
+
[0, 0, 4],
|
|
36183
|
+
[40, 11, 84],
|
|
36184
|
+
[89, 13, 115],
|
|
36185
|
+
[137, 31, 107],
|
|
36186
|
+
[179, 55, 79],
|
|
36187
|
+
[213, 87, 49],
|
|
36188
|
+
[240, 130, 24],
|
|
36189
|
+
[253, 184, 43],
|
|
36190
|
+
[249, 251, 146],
|
|
36191
|
+
[252, 255, 164]
|
|
36192
|
+
];
|
|
36193
|
+
const MAGMA = [
|
|
36194
|
+
[0, 0, 4],
|
|
36195
|
+
[28, 16, 68],
|
|
36196
|
+
[79, 18, 123],
|
|
36197
|
+
[129, 37, 129],
|
|
36198
|
+
[181, 54, 122],
|
|
36199
|
+
[229, 80, 100],
|
|
36200
|
+
[251, 135, 97],
|
|
36201
|
+
[254, 194, 135],
|
|
36202
|
+
[254, 247, 187],
|
|
36203
|
+
[252, 253, 191]
|
|
36204
|
+
];
|
|
36205
|
+
const CIVIDIS = [
|
|
36206
|
+
[0, 32, 77],
|
|
36207
|
+
[0, 58, 103],
|
|
36208
|
+
[52, 77, 105],
|
|
36209
|
+
[87, 95, 108],
|
|
36210
|
+
[115, 113, 112],
|
|
36211
|
+
[143, 132, 108],
|
|
36212
|
+
[171, 152, 97],
|
|
36213
|
+
[200, 173, 79],
|
|
36214
|
+
[231, 196, 55],
|
|
36215
|
+
[253, 231, 37]
|
|
36216
|
+
];
|
|
36217
|
+
const TURBO = [
|
|
36218
|
+
[48, 18, 59],
|
|
36219
|
+
[70, 107, 227],
|
|
36220
|
+
[40, 170, 225],
|
|
36221
|
+
[35, 221, 162],
|
|
36222
|
+
[122, 249, 85],
|
|
36223
|
+
[194, 241, 45],
|
|
36224
|
+
[241, 206, 51],
|
|
36225
|
+
[250, 144, 42],
|
|
36226
|
+
[229, 68, 25],
|
|
36227
|
+
[122, 4, 3]
|
|
36228
|
+
];
|
|
36229
|
+
const JET = [
|
|
36230
|
+
[0, 0, 127],
|
|
36231
|
+
[0, 0, 255],
|
|
36232
|
+
[0, 127, 255],
|
|
36233
|
+
[0, 255, 255],
|
|
36234
|
+
[127, 255, 127],
|
|
36235
|
+
[255, 255, 0],
|
|
36236
|
+
[255, 127, 0],
|
|
36237
|
+
[255, 0, 0],
|
|
36238
|
+
[127, 0, 0],
|
|
36239
|
+
[127, 0, 0]
|
|
36240
|
+
];
|
|
36241
|
+
const RAINBOW = [
|
|
36242
|
+
[150, 0, 90],
|
|
36243
|
+
[0, 0, 200],
|
|
36244
|
+
[0, 125, 255],
|
|
36245
|
+
[0, 200, 255],
|
|
36246
|
+
[0, 255, 125],
|
|
36247
|
+
[125, 255, 0],
|
|
36248
|
+
[255, 255, 0],
|
|
36249
|
+
[255, 125, 0],
|
|
36250
|
+
[255, 0, 0],
|
|
36251
|
+
[128, 0, 0]
|
|
36252
|
+
];
|
|
36253
|
+
const TERRAIN = [
|
|
36254
|
+
[51, 51, 153],
|
|
36255
|
+
[51, 102, 153],
|
|
36256
|
+
[51, 153, 153],
|
|
36257
|
+
[102, 178, 102],
|
|
36258
|
+
[153, 204, 102],
|
|
36259
|
+
[204, 229, 102],
|
|
36260
|
+
[204, 204, 153],
|
|
36261
|
+
[178, 153, 102],
|
|
36262
|
+
[153, 102, 51],
|
|
36263
|
+
[255, 255, 255]
|
|
36264
|
+
];
|
|
36265
|
+
const COOLWARM = [
|
|
36266
|
+
[59, 76, 192],
|
|
36267
|
+
[98, 130, 234],
|
|
36268
|
+
[141, 176, 254],
|
|
36269
|
+
[184, 208, 249],
|
|
36270
|
+
[221, 221, 221],
|
|
36271
|
+
[245, 196, 173],
|
|
36272
|
+
[244, 154, 123],
|
|
36273
|
+
[222, 96, 77],
|
|
36274
|
+
[180, 4, 38],
|
|
36275
|
+
[180, 4, 38]
|
|
36276
|
+
];
|
|
36277
|
+
const GRAY = [
|
|
36032
36278
|
[0, 0, 0],
|
|
36033
|
-
|
|
36034
|
-
[
|
|
36035
|
-
[
|
|
36036
|
-
[
|
|
36279
|
+
[28, 28, 28],
|
|
36280
|
+
[57, 57, 57],
|
|
36281
|
+
[85, 85, 85],
|
|
36282
|
+
[113, 113, 113],
|
|
36283
|
+
[142, 142, 142],
|
|
36284
|
+
[170, 170, 170],
|
|
36285
|
+
[198, 198, 198],
|
|
36286
|
+
[227, 227, 227],
|
|
36037
36287
|
[255, 255, 255]
|
|
36038
|
-
// white
|
|
36039
36288
|
];
|
|
36289
|
+
const COLORMAPS = {
|
|
36290
|
+
viridis: VIRIDIS,
|
|
36291
|
+
plasma: PLASMA,
|
|
36292
|
+
inferno: INFERNO,
|
|
36293
|
+
magma: MAGMA,
|
|
36294
|
+
cividis: CIVIDIS,
|
|
36295
|
+
turbo: TURBO,
|
|
36296
|
+
jet: JET,
|
|
36297
|
+
rainbow: RAINBOW,
|
|
36298
|
+
terrain: TERRAIN,
|
|
36299
|
+
coolwarm: COOLWARM,
|
|
36300
|
+
gray: GRAY
|
|
36301
|
+
};
|
|
36302
|
+
const COLORMAP_NAMES = [
|
|
36303
|
+
"viridis",
|
|
36304
|
+
"plasma",
|
|
36305
|
+
"inferno",
|
|
36306
|
+
"magma",
|
|
36307
|
+
"cividis",
|
|
36308
|
+
"turbo",
|
|
36309
|
+
"jet",
|
|
36310
|
+
"rainbow",
|
|
36311
|
+
"terrain",
|
|
36312
|
+
"coolwarm",
|
|
36313
|
+
"gray"
|
|
36314
|
+
];
|
|
36315
|
+
const COLORMAP_LABELS = {
|
|
36316
|
+
viridis: "Viridis",
|
|
36317
|
+
plasma: "Plasma",
|
|
36318
|
+
inferno: "Inferno",
|
|
36319
|
+
magma: "Magma",
|
|
36320
|
+
cividis: "Cividis",
|
|
36321
|
+
turbo: "Turbo",
|
|
36322
|
+
jet: "Jet",
|
|
36323
|
+
rainbow: "Rainbow",
|
|
36324
|
+
terrain: "Terrain",
|
|
36325
|
+
coolwarm: "Cool-Warm",
|
|
36326
|
+
gray: "Grayscale"
|
|
36327
|
+
};
|
|
36328
|
+
function getColormap(name) {
|
|
36329
|
+
return COLORMAPS[name] || COLORMAPS.viridis;
|
|
36330
|
+
}
|
|
36040
36331
|
const CLASSIFICATION_COLORS = {
|
|
36041
36332
|
0: [128, 128, 128],
|
|
36042
36333
|
// Created, never classified
|
|
@@ -36078,6 +36369,10 @@ const CLASSIFICATION_COLORS = {
|
|
|
36078
36369
|
// High Noise
|
|
36079
36370
|
};
|
|
36080
36371
|
class ColorSchemeProcessor {
|
|
36372
|
+
constructor() {
|
|
36373
|
+
/** Last computed color bounds (for colorbar display) */
|
|
36374
|
+
__publicField(this, "_lastComputedBounds");
|
|
36375
|
+
}
|
|
36081
36376
|
/**
|
|
36082
36377
|
* Generates a color array for the point cloud based on the color scheme.
|
|
36083
36378
|
*
|
|
@@ -36087,100 +36382,150 @@ class ColorSchemeProcessor {
|
|
|
36087
36382
|
* @returns Uint8Array of RGBA colors (length = pointCount * 4)
|
|
36088
36383
|
*/
|
|
36089
36384
|
getColors(data, scheme, options = {}) {
|
|
36385
|
+
const result = this.getColorsWithBounds(data, scheme, options);
|
|
36386
|
+
return result.colors;
|
|
36387
|
+
}
|
|
36388
|
+
/**
|
|
36389
|
+
* Generates a color array and returns the computed bounds.
|
|
36390
|
+
*
|
|
36391
|
+
* @param data - Point cloud data
|
|
36392
|
+
* @param scheme - Color scheme to apply
|
|
36393
|
+
* @param options - Optional color generation options
|
|
36394
|
+
* @returns ColorResult containing colors and computed bounds
|
|
36395
|
+
*/
|
|
36396
|
+
getColorsWithBounds(data, scheme, options = {}) {
|
|
36090
36397
|
const colors = new Uint8Array(data.pointCount * 4);
|
|
36398
|
+
const colormap = options.colormap ?? "viridis";
|
|
36399
|
+
const colorRange = options.colorRange;
|
|
36091
36400
|
const usePercentile = options.usePercentile ?? true;
|
|
36092
36401
|
if (typeof scheme === "string") {
|
|
36093
36402
|
switch (scheme) {
|
|
36094
36403
|
case "elevation":
|
|
36095
|
-
return this._colorByElevation(data, colors, usePercentile);
|
|
36404
|
+
return this._colorByElevation(data, colors, colormap, colorRange, usePercentile);
|
|
36096
36405
|
case "intensity":
|
|
36097
|
-
return this._colorByIntensity(data, colors, usePercentile);
|
|
36406
|
+
return this._colorByIntensity(data, colors, colormap, colorRange, usePercentile);
|
|
36098
36407
|
case "classification":
|
|
36099
|
-
return this._colorByClassification(data, colors, options.hiddenClassifications);
|
|
36408
|
+
return { colors: this._colorByClassification(data, colors, options.hiddenClassifications) };
|
|
36100
36409
|
case "rgb":
|
|
36101
|
-
return this._colorByRGB(data, colors);
|
|
36410
|
+
return { colors: this._colorByRGB(data, colors) };
|
|
36102
36411
|
default:
|
|
36103
|
-
return this._colorByElevation(data, colors, usePercentile);
|
|
36412
|
+
return this._colorByElevation(data, colors, colormap, colorRange, usePercentile);
|
|
36413
|
+
}
|
|
36414
|
+
} else {
|
|
36415
|
+
return { colors: this._colorByCustom(data, colors, scheme, colormap, colorRange, usePercentile) };
|
|
36416
|
+
}
|
|
36417
|
+
}
|
|
36418
|
+
/**
|
|
36419
|
+
* Gets the last computed color bounds (for colorbar display).
|
|
36420
|
+
*
|
|
36421
|
+
* @returns The last computed bounds or undefined
|
|
36422
|
+
*/
|
|
36423
|
+
getLastComputedBounds() {
|
|
36424
|
+
return this._lastComputedBounds;
|
|
36425
|
+
}
|
|
36426
|
+
/**
|
|
36427
|
+
* Computes the color bounds based on the configuration.
|
|
36428
|
+
*
|
|
36429
|
+
* @param values - Array of values to compute bounds for
|
|
36430
|
+
* @param dataBounds - Data bounds (min/max)
|
|
36431
|
+
* @param colorRange - Color range configuration
|
|
36432
|
+
* @param usePercentile - Legacy percentile flag
|
|
36433
|
+
* @returns Computed min and max bounds
|
|
36434
|
+
*/
|
|
36435
|
+
_computeBounds(values, dataBounds, colorRange, usePercentile) {
|
|
36436
|
+
if (colorRange) {
|
|
36437
|
+
if (colorRange.mode === "absolute") {
|
|
36438
|
+
return {
|
|
36439
|
+
min: colorRange.absoluteMin ?? dataBounds.min,
|
|
36440
|
+
max: colorRange.absoluteMax ?? dataBounds.max
|
|
36441
|
+
};
|
|
36442
|
+
} else {
|
|
36443
|
+
const pLow = colorRange.percentileLow ?? 2;
|
|
36444
|
+
const pHigh = colorRange.percentileHigh ?? 98;
|
|
36445
|
+
return computePercentileBounds(values, pLow, pHigh);
|
|
36104
36446
|
}
|
|
36447
|
+
} else if (usePercentile) {
|
|
36448
|
+
return computePercentileBounds(values, 2, 98);
|
|
36105
36449
|
} else {
|
|
36106
|
-
return
|
|
36450
|
+
return dataBounds;
|
|
36107
36451
|
}
|
|
36108
36452
|
}
|
|
36109
36453
|
/**
|
|
36110
|
-
* Colors points by elevation using
|
|
36454
|
+
* Colors points by elevation using the specified colormap.
|
|
36111
36455
|
*
|
|
36112
36456
|
* @param data - Point cloud data
|
|
36113
36457
|
* @param colors - Output color array
|
|
36114
|
-
* @param
|
|
36458
|
+
* @param colormap - Colormap name to use
|
|
36459
|
+
* @param colorRange - Color range configuration
|
|
36460
|
+
* @param usePercentile - Legacy percentile flag
|
|
36461
|
+
* @returns ColorResult with colors and computed bounds
|
|
36115
36462
|
*/
|
|
36116
|
-
_colorByElevation(data, colors, usePercentile) {
|
|
36463
|
+
_colorByElevation(data, colors, colormap, colorRange, usePercentile) {
|
|
36117
36464
|
var _a, _b;
|
|
36118
36465
|
if (!data.positions || data.positions.length === 0) {
|
|
36119
|
-
return colors;
|
|
36120
|
-
}
|
|
36121
|
-
let minZ;
|
|
36122
|
-
let maxZ;
|
|
36123
|
-
if (usePercentile) {
|
|
36124
|
-
const zValues = new Float32Array(data.pointCount);
|
|
36125
|
-
for (let i = 0; i < data.pointCount; i++) {
|
|
36126
|
-
zValues[i] = data.positions[i * 3 + 2] ?? 0;
|
|
36127
|
-
}
|
|
36128
|
-
const bounds2 = computePercentileBounds(zValues, 2, 98);
|
|
36129
|
-
minZ = bounds2.min;
|
|
36130
|
-
maxZ = bounds2.max;
|
|
36131
|
-
} else {
|
|
36132
|
-
minZ = ((_a = data.bounds) == null ? void 0 : _a.minZ) ?? 0;
|
|
36133
|
-
maxZ = ((_b = data.bounds) == null ? void 0 : _b.maxZ) ?? 1;
|
|
36466
|
+
return { colors };
|
|
36134
36467
|
}
|
|
36468
|
+
const zValues = new Float32Array(data.pointCount);
|
|
36469
|
+
for (let i = 0; i < data.pointCount; i++) {
|
|
36470
|
+
zValues[i] = data.positions[i * 3 + 2] ?? 0;
|
|
36471
|
+
}
|
|
36472
|
+
const dataBounds = {
|
|
36473
|
+
min: ((_a = data.bounds) == null ? void 0 : _a.minZ) ?? 0,
|
|
36474
|
+
max: ((_b = data.bounds) == null ? void 0 : _b.maxZ) ?? 1
|
|
36475
|
+
};
|
|
36476
|
+
const bounds2 = this._computeBounds(zValues, dataBounds, colorRange, usePercentile);
|
|
36477
|
+
this._lastComputedBounds = bounds2;
|
|
36478
|
+
const { min: minZ, max: maxZ } = bounds2;
|
|
36135
36479
|
const range = maxZ - minZ || 1;
|
|
36480
|
+
const ramp = COLORMAPS[colormap] || COLORMAPS.viridis;
|
|
36136
36481
|
for (let i = 0; i < data.pointCount; i++) {
|
|
36137
|
-
const z =
|
|
36482
|
+
const z = zValues[i];
|
|
36138
36483
|
const t = (z - minZ) / range;
|
|
36139
|
-
const color = this._interpolateRamp(
|
|
36484
|
+
const color = this._interpolateRamp(ramp, t);
|
|
36140
36485
|
colors[i * 4] = color[0];
|
|
36141
36486
|
colors[i * 4 + 1] = color[1];
|
|
36142
36487
|
colors[i * 4 + 2] = color[2];
|
|
36143
36488
|
colors[i * 4 + 3] = 255;
|
|
36144
36489
|
}
|
|
36145
|
-
return colors;
|
|
36490
|
+
return { colors, bounds: bounds2 };
|
|
36146
36491
|
}
|
|
36147
36492
|
/**
|
|
36148
|
-
* Colors points by intensity using
|
|
36493
|
+
* Colors points by intensity using the specified colormap.
|
|
36149
36494
|
*
|
|
36150
36495
|
* @param data - Point cloud data
|
|
36151
36496
|
* @param colors - Output color array
|
|
36152
|
-
* @param
|
|
36497
|
+
* @param colormap - Colormap name to use
|
|
36498
|
+
* @param colorRange - Color range configuration
|
|
36499
|
+
* @param usePercentile - Legacy percentile flag
|
|
36500
|
+
* @returns ColorResult with colors and computed bounds
|
|
36153
36501
|
*/
|
|
36154
|
-
_colorByIntensity(data, colors, usePercentile) {
|
|
36502
|
+
_colorByIntensity(data, colors, colormap, colorRange, usePercentile) {
|
|
36155
36503
|
if (!data.hasIntensity || !data.intensities) {
|
|
36156
|
-
return this._colorByElevation(data, colors, usePercentile);
|
|
36157
|
-
}
|
|
36158
|
-
let minI;
|
|
36159
|
-
let maxI;
|
|
36160
|
-
if (usePercentile) {
|
|
36161
|
-
const bounds2 = computePercentileBounds(data.intensities, 2, 98);
|
|
36162
|
-
minI = bounds2.min;
|
|
36163
|
-
maxI = bounds2.max;
|
|
36164
|
-
} else {
|
|
36165
|
-
minI = Infinity;
|
|
36166
|
-
maxI = -Infinity;
|
|
36167
|
-
for (let i = 0; i < data.pointCount; i++) {
|
|
36168
|
-
const intensity = data.intensities[i];
|
|
36169
|
-
if (intensity < minI) minI = intensity;
|
|
36170
|
-
if (intensity > maxI) maxI = intensity;
|
|
36171
|
-
}
|
|
36504
|
+
return this._colorByElevation(data, colors, colormap, colorRange, usePercentile);
|
|
36172
36505
|
}
|
|
36173
|
-
|
|
36506
|
+
let minI = Infinity;
|
|
36507
|
+
let maxI = -Infinity;
|
|
36174
36508
|
for (let i = 0; i < data.pointCount; i++) {
|
|
36175
36509
|
const intensity = data.intensities[i];
|
|
36176
|
-
|
|
36177
|
-
|
|
36510
|
+
if (intensity < minI) minI = intensity;
|
|
36511
|
+
if (intensity > maxI) maxI = intensity;
|
|
36512
|
+
}
|
|
36513
|
+
const dataBounds = { min: minI, max: maxI };
|
|
36514
|
+
const bounds2 = this._computeBounds(data.intensities, dataBounds, colorRange, usePercentile);
|
|
36515
|
+
this._lastComputedBounds = bounds2;
|
|
36516
|
+
const { min: minVal, max: maxVal } = bounds2;
|
|
36517
|
+
const range = maxVal - minVal || 1;
|
|
36518
|
+
const ramp = COLORMAPS[colormap] || COLORMAPS.gray;
|
|
36519
|
+
for (let i = 0; i < data.pointCount; i++) {
|
|
36520
|
+
const intensity = data.intensities[i];
|
|
36521
|
+
const t = (intensity - minVal) / range;
|
|
36522
|
+
const color = this._interpolateRamp(ramp, t);
|
|
36178
36523
|
colors[i * 4] = color[0];
|
|
36179
36524
|
colors[i * 4 + 1] = color[1];
|
|
36180
36525
|
colors[i * 4 + 2] = color[2];
|
|
36181
36526
|
colors[i * 4 + 3] = 255;
|
|
36182
36527
|
}
|
|
36183
|
-
return colors;
|
|
36528
|
+
return { colors, bounds: bounds2 };
|
|
36184
36529
|
}
|
|
36185
36530
|
/**
|
|
36186
36531
|
* Colors points by classification using ASPRS standard colors.
|
|
@@ -36188,10 +36533,12 @@ class ColorSchemeProcessor {
|
|
|
36188
36533
|
* @param data - Point cloud data
|
|
36189
36534
|
* @param colors - Output color array
|
|
36190
36535
|
* @param hiddenClassifications - Optional set of classification codes to hide (alpha=0)
|
|
36536
|
+
* @returns Color array
|
|
36191
36537
|
*/
|
|
36192
36538
|
_colorByClassification(data, colors, hiddenClassifications) {
|
|
36193
36539
|
if (!data.hasClassification || !data.classifications) {
|
|
36194
|
-
|
|
36540
|
+
const result = this._colorByElevation(data, colors, "viridis", void 0, true);
|
|
36541
|
+
return result.colors;
|
|
36195
36542
|
}
|
|
36196
36543
|
for (let i = 0; i < data.pointCount; i++) {
|
|
36197
36544
|
const cls = data.classifications[i];
|
|
@@ -36205,10 +36552,15 @@ class ColorSchemeProcessor {
|
|
|
36205
36552
|
}
|
|
36206
36553
|
/**
|
|
36207
36554
|
* Uses embedded RGB colors from the point cloud.
|
|
36555
|
+
*
|
|
36556
|
+
* @param data - Point cloud data
|
|
36557
|
+
* @param colors - Output color array
|
|
36558
|
+
* @returns Color array
|
|
36208
36559
|
*/
|
|
36209
36560
|
_colorByRGB(data, colors) {
|
|
36210
36561
|
if (!data.hasRGB || !data.colors) {
|
|
36211
|
-
|
|
36562
|
+
const result = this._colorByElevation(data, colors, "viridis", void 0, true);
|
|
36563
|
+
return result.colors;
|
|
36212
36564
|
}
|
|
36213
36565
|
for (let i = 0; i < data.pointCount; i++) {
|
|
36214
36566
|
colors[i * 4] = data.colors[i * 4];
|
|
@@ -36220,12 +36572,25 @@ class ColorSchemeProcessor {
|
|
|
36220
36572
|
}
|
|
36221
36573
|
/**
|
|
36222
36574
|
* Applies a custom color scheme configuration.
|
|
36575
|
+
*
|
|
36576
|
+
* @param data - Point cloud data
|
|
36577
|
+
* @param colors - Output color array
|
|
36578
|
+
* @param _config - Custom color scheme config
|
|
36579
|
+
* @param colormap - Colormap name to use
|
|
36580
|
+
* @param colorRange - Color range configuration
|
|
36581
|
+
* @param usePercentile - Legacy percentile flag
|
|
36582
|
+
* @returns Color array
|
|
36223
36583
|
*/
|
|
36224
|
-
_colorByCustom(data, colors, _config) {
|
|
36225
|
-
|
|
36584
|
+
_colorByCustom(data, colors, _config, colormap, colorRange, usePercentile) {
|
|
36585
|
+
const result = this._colorByElevation(data, colors, colormap, colorRange, usePercentile);
|
|
36586
|
+
return result.colors;
|
|
36226
36587
|
}
|
|
36227
36588
|
/**
|
|
36228
36589
|
* Interpolates a color from a color ramp.
|
|
36590
|
+
*
|
|
36591
|
+
* @param ramp - Color ramp array
|
|
36592
|
+
* @param t - Interpolation parameter (0-1)
|
|
36593
|
+
* @returns Interpolated RGB color
|
|
36229
36594
|
*/
|
|
36230
36595
|
_interpolateRamp(ramp, t) {
|
|
36231
36596
|
if (!Number.isFinite(t)) {
|
|
@@ -36283,6 +36648,7 @@ class PointCloudManager {
|
|
|
36283
36648
|
__publicField(this, "_pointClouds");
|
|
36284
36649
|
__publicField(this, "_options");
|
|
36285
36650
|
__publicField(this, "_colorProcessor");
|
|
36651
|
+
__publicField(this, "_lastComputedBounds");
|
|
36286
36652
|
this._deckOverlay = deckOverlay;
|
|
36287
36653
|
this._pointClouds = /* @__PURE__ */ new Map();
|
|
36288
36654
|
this._colorProcessor = new ColorSchemeProcessor();
|
|
@@ -36291,6 +36657,8 @@ class PointCloudManager {
|
|
|
36291
36657
|
opacity: options.opacity ?? 1,
|
|
36292
36658
|
colorScheme: options.colorScheme ?? "elevation",
|
|
36293
36659
|
usePercentile: options.usePercentile ?? true,
|
|
36660
|
+
colormap: options.colormap ?? "viridis",
|
|
36661
|
+
colorRange: options.colorRange,
|
|
36294
36662
|
elevationRange: options.elevationRange ?? null,
|
|
36295
36663
|
pickable: options.pickable ?? false,
|
|
36296
36664
|
zOffset: options.zOffset ?? 0,
|
|
@@ -36312,15 +36680,20 @@ class PointCloudManager {
|
|
|
36312
36680
|
* @param data - Point cloud data (positions are already offsets from coordinateOrigin)
|
|
36313
36681
|
*/
|
|
36314
36682
|
addPointCloud(id, data) {
|
|
36315
|
-
const
|
|
36683
|
+
const result = this._colorProcessor.getColorsWithBounds(data, this._options.colorScheme, {
|
|
36316
36684
|
usePercentile: this._options.usePercentile,
|
|
36685
|
+
colormap: this._options.colormap,
|
|
36686
|
+
colorRange: this._options.colorRange,
|
|
36317
36687
|
hiddenClassifications: this._options.hiddenClassifications
|
|
36318
36688
|
});
|
|
36689
|
+
if (result.bounds) {
|
|
36690
|
+
this._lastComputedBounds = result.bounds;
|
|
36691
|
+
}
|
|
36319
36692
|
const coordinateOrigin = data.coordinateOrigin;
|
|
36320
36693
|
this._pointClouds.set(id, {
|
|
36321
36694
|
id,
|
|
36322
36695
|
data,
|
|
36323
|
-
colors,
|
|
36696
|
+
colors: result.colors,
|
|
36324
36697
|
coordinateOrigin,
|
|
36325
36698
|
visible: true,
|
|
36326
36699
|
opacityOverride: null
|
|
@@ -36340,14 +36713,19 @@ class PointCloudManager {
|
|
|
36340
36713
|
}
|
|
36341
36714
|
const existing = this._pointClouds.get(id);
|
|
36342
36715
|
if (existing) {
|
|
36343
|
-
const
|
|
36716
|
+
const result = this._colorProcessor.getColorsWithBounds(data, this._options.colorScheme, {
|
|
36344
36717
|
usePercentile: this._options.usePercentile,
|
|
36718
|
+
colormap: this._options.colormap,
|
|
36719
|
+
colorRange: this._options.colorRange,
|
|
36345
36720
|
hiddenClassifications: this._options.hiddenClassifications
|
|
36346
36721
|
});
|
|
36722
|
+
if (result.bounds) {
|
|
36723
|
+
this._lastComputedBounds = result.bounds;
|
|
36724
|
+
}
|
|
36347
36725
|
this._pointClouds.set(id, {
|
|
36348
36726
|
id,
|
|
36349
36727
|
data,
|
|
36350
|
-
colors,
|
|
36728
|
+
colors: result.colors,
|
|
36351
36729
|
coordinateOrigin: data.coordinateOrigin,
|
|
36352
36730
|
visible: existing.visible,
|
|
36353
36731
|
opacityOverride: existing.opacityOverride
|
|
@@ -36418,17 +36796,24 @@ class PointCloudManager {
|
|
|
36418
36796
|
updateStyle(options) {
|
|
36419
36797
|
const colorSchemeChanged = options.colorScheme !== void 0 && options.colorScheme !== this._options.colorScheme;
|
|
36420
36798
|
const percentileChanged = options.usePercentile !== void 0 && options.usePercentile !== this._options.usePercentile;
|
|
36799
|
+
const colormapChanged = options.colormap !== void 0 && options.colormap !== this._options.colormap;
|
|
36800
|
+
const colorRangeChanged = options.colorRange !== void 0;
|
|
36421
36801
|
const hiddenClassificationsChanged = options.hiddenClassifications !== void 0;
|
|
36422
36802
|
this._options = { ...this._options, ...options };
|
|
36423
|
-
if (colorSchemeChanged || percentileChanged || hiddenClassificationsChanged) {
|
|
36803
|
+
if (colorSchemeChanged || percentileChanged || colormapChanged || colorRangeChanged || hiddenClassificationsChanged) {
|
|
36424
36804
|
for (const [id, pc] of this._pointClouds) {
|
|
36425
|
-
const
|
|
36805
|
+
const result = this._colorProcessor.getColorsWithBounds(pc.data, this._options.colorScheme, {
|
|
36426
36806
|
usePercentile: this._options.usePercentile,
|
|
36807
|
+
colormap: this._options.colormap,
|
|
36808
|
+
colorRange: this._options.colorRange,
|
|
36427
36809
|
hiddenClassifications: this._options.hiddenClassifications
|
|
36428
36810
|
});
|
|
36811
|
+
if (result.bounds) {
|
|
36812
|
+
this._lastComputedBounds = result.bounds;
|
|
36813
|
+
}
|
|
36429
36814
|
this._pointClouds.set(id, {
|
|
36430
36815
|
...pc,
|
|
36431
|
-
colors,
|
|
36816
|
+
colors: result.colors,
|
|
36432
36817
|
coordinateOrigin: pc.coordinateOrigin,
|
|
36433
36818
|
visible: pc.visible,
|
|
36434
36819
|
opacityOverride: pc.opacityOverride
|
|
@@ -36475,6 +36860,22 @@ class PointCloudManager {
|
|
|
36475
36860
|
setUsePercentile(usePercentile) {
|
|
36476
36861
|
this.updateStyle({ usePercentile });
|
|
36477
36862
|
}
|
|
36863
|
+
/**
|
|
36864
|
+
* Sets the colormap for elevation/intensity coloring.
|
|
36865
|
+
*
|
|
36866
|
+
* @param colormap - Colormap name
|
|
36867
|
+
*/
|
|
36868
|
+
setColormap(colormap) {
|
|
36869
|
+
this.updateStyle({ colormap });
|
|
36870
|
+
}
|
|
36871
|
+
/**
|
|
36872
|
+
* Sets the color range configuration.
|
|
36873
|
+
*
|
|
36874
|
+
* @param colorRange - Color range configuration
|
|
36875
|
+
*/
|
|
36876
|
+
setColorRange(colorRange) {
|
|
36877
|
+
this.updateStyle({ colorRange });
|
|
36878
|
+
}
|
|
36478
36879
|
/**
|
|
36479
36880
|
* Sets the elevation range filter.
|
|
36480
36881
|
*
|
|
@@ -36573,6 +36974,13 @@ class PointCloudManager {
|
|
|
36573
36974
|
getOptions() {
|
|
36574
36975
|
return { ...this._options };
|
|
36575
36976
|
}
|
|
36977
|
+
/**
|
|
36978
|
+
* Gets the last computed color bounds.
|
|
36979
|
+
* Used for displaying accurate colorbar min/max values.
|
|
36980
|
+
*/
|
|
36981
|
+
getLastComputedBounds() {
|
|
36982
|
+
return this._lastComputedBounds;
|
|
36983
|
+
}
|
|
36576
36984
|
/**
|
|
36577
36985
|
* Creates a deck.gl layer for a point cloud.
|
|
36578
36986
|
* Chunks large point clouds into multiple layers to avoid WebGL buffer limits.
|
|
@@ -37061,6 +37469,7 @@ class DualRangeSlider {
|
|
|
37061
37469
|
__publicField(this, "_sliderLow");
|
|
37062
37470
|
__publicField(this, "_sliderHigh");
|
|
37063
37471
|
__publicField(this, "_valueDisplay");
|
|
37472
|
+
__publicField(this, "_rangeHighlight");
|
|
37064
37473
|
this._options = options;
|
|
37065
37474
|
}
|
|
37066
37475
|
/**
|
|
@@ -37108,6 +37517,7 @@ class DualRangeSlider {
|
|
|
37108
37517
|
background: #159895;
|
|
37109
37518
|
border-radius: 2px;
|
|
37110
37519
|
`;
|
|
37520
|
+
this._rangeHighlight = range;
|
|
37111
37521
|
sliderContainer.appendChild(range);
|
|
37112
37522
|
const sliderLow = document.createElement("input");
|
|
37113
37523
|
sliderLow.type = "range";
|
|
@@ -37225,6 +37635,7 @@ class DualRangeSlider {
|
|
|
37225
37635
|
if (this._valueDisplay) {
|
|
37226
37636
|
this._valueDisplay.textContent = this._formatRange(low, high);
|
|
37227
37637
|
}
|
|
37638
|
+
this._updateRangeHighlight();
|
|
37228
37639
|
}
|
|
37229
37640
|
/**
|
|
37230
37641
|
* Updates the min/max bounds of the slider.
|
|
@@ -37240,6 +37651,33 @@ class DualRangeSlider {
|
|
|
37240
37651
|
this._sliderHigh.min = String(min);
|
|
37241
37652
|
this._sliderHigh.max = String(max);
|
|
37242
37653
|
}
|
|
37654
|
+
this._updateRangeHighlight();
|
|
37655
|
+
}
|
|
37656
|
+
/**
|
|
37657
|
+
* Updates the step value of the slider.
|
|
37658
|
+
*/
|
|
37659
|
+
setStep(step2) {
|
|
37660
|
+
this._options.step = step2;
|
|
37661
|
+
if (this._sliderLow) {
|
|
37662
|
+
this._sliderLow.step = String(step2);
|
|
37663
|
+
}
|
|
37664
|
+
if (this._sliderHigh) {
|
|
37665
|
+
this._sliderHigh.step = String(step2);
|
|
37666
|
+
}
|
|
37667
|
+
}
|
|
37668
|
+
/**
|
|
37669
|
+
* Updates the visual range highlight bar.
|
|
37670
|
+
*/
|
|
37671
|
+
_updateRangeHighlight() {
|
|
37672
|
+
if (!this._rangeHighlight || !this._sliderLow || !this._sliderHigh) return;
|
|
37673
|
+
const low = parseFloat(this._sliderLow.value);
|
|
37674
|
+
const high = parseFloat(this._sliderHigh.value);
|
|
37675
|
+
const min = this._options.min;
|
|
37676
|
+
const max = this._options.max;
|
|
37677
|
+
const percentLow = (low - min) / (max - min) * 100;
|
|
37678
|
+
const percentHigh = (high - min) / (max - min) * 100;
|
|
37679
|
+
this._rangeHighlight.style.left = `${percentLow}%`;
|
|
37680
|
+
this._rangeHighlight.style.width = `${percentHigh - percentLow}%`;
|
|
37243
37681
|
}
|
|
37244
37682
|
/**
|
|
37245
37683
|
* Gets the current range values.
|
|
@@ -37392,6 +37830,457 @@ class ClassificationLegend {
|
|
|
37392
37830
|
return this._container;
|
|
37393
37831
|
}
|
|
37394
37832
|
}
|
|
37833
|
+
class Colorbar {
|
|
37834
|
+
/**
|
|
37835
|
+
* Creates a new Colorbar instance.
|
|
37836
|
+
*
|
|
37837
|
+
* @param options - Colorbar configuration options
|
|
37838
|
+
*/
|
|
37839
|
+
constructor(options) {
|
|
37840
|
+
__publicField(this, "_options");
|
|
37841
|
+
__publicField(this, "_canvas");
|
|
37842
|
+
__publicField(this, "_minLabel");
|
|
37843
|
+
__publicField(this, "_maxLabel");
|
|
37844
|
+
this._options = { ...options };
|
|
37845
|
+
}
|
|
37846
|
+
/**
|
|
37847
|
+
* Renders the colorbar component.
|
|
37848
|
+
*
|
|
37849
|
+
* @returns The colorbar container element
|
|
37850
|
+
*/
|
|
37851
|
+
render() {
|
|
37852
|
+
const container = document.createElement("div");
|
|
37853
|
+
container.className = "lidar-colorbar";
|
|
37854
|
+
if (this._options.label) {
|
|
37855
|
+
const label = document.createElement("div");
|
|
37856
|
+
label.className = "lidar-colorbar-label";
|
|
37857
|
+
label.textContent = this._options.label;
|
|
37858
|
+
container.appendChild(label);
|
|
37859
|
+
}
|
|
37860
|
+
const canvas = document.createElement("canvas");
|
|
37861
|
+
canvas.className = "lidar-colorbar-gradient";
|
|
37862
|
+
canvas.width = 200;
|
|
37863
|
+
canvas.height = 14;
|
|
37864
|
+
this._canvas = canvas;
|
|
37865
|
+
container.appendChild(canvas);
|
|
37866
|
+
const labelsContainer = document.createElement("div");
|
|
37867
|
+
labelsContainer.className = "lidar-colorbar-labels";
|
|
37868
|
+
const minLabel = document.createElement("span");
|
|
37869
|
+
minLabel.className = "lidar-colorbar-min";
|
|
37870
|
+
this._minLabel = minLabel;
|
|
37871
|
+
const maxLabel = document.createElement("span");
|
|
37872
|
+
maxLabel.className = "lidar-colorbar-max";
|
|
37873
|
+
this._maxLabel = maxLabel;
|
|
37874
|
+
labelsContainer.appendChild(minLabel);
|
|
37875
|
+
labelsContainer.appendChild(maxLabel);
|
|
37876
|
+
container.appendChild(labelsContainer);
|
|
37877
|
+
this._drawGradient();
|
|
37878
|
+
this._updateLabels();
|
|
37879
|
+
return container;
|
|
37880
|
+
}
|
|
37881
|
+
/**
|
|
37882
|
+
* Updates the colorbar with new options.
|
|
37883
|
+
*
|
|
37884
|
+
* @param options - Partial options to update
|
|
37885
|
+
*/
|
|
37886
|
+
update(options) {
|
|
37887
|
+
if (options.colormap !== void 0) {
|
|
37888
|
+
this._options.colormap = options.colormap;
|
|
37889
|
+
}
|
|
37890
|
+
if (options.minValue !== void 0) {
|
|
37891
|
+
this._options.minValue = options.minValue;
|
|
37892
|
+
}
|
|
37893
|
+
if (options.maxValue !== void 0) {
|
|
37894
|
+
this._options.maxValue = options.maxValue;
|
|
37895
|
+
}
|
|
37896
|
+
if (options.label !== void 0) {
|
|
37897
|
+
this._options.label = options.label;
|
|
37898
|
+
}
|
|
37899
|
+
this._drawGradient();
|
|
37900
|
+
this._updateLabels();
|
|
37901
|
+
}
|
|
37902
|
+
/**
|
|
37903
|
+
* Sets the colormap.
|
|
37904
|
+
*
|
|
37905
|
+
* @param colormap - The colormap name
|
|
37906
|
+
*/
|
|
37907
|
+
setColormap(colormap) {
|
|
37908
|
+
this._options.colormap = colormap;
|
|
37909
|
+
this._drawGradient();
|
|
37910
|
+
}
|
|
37911
|
+
/**
|
|
37912
|
+
* Sets the value range.
|
|
37913
|
+
*
|
|
37914
|
+
* @param min - Minimum value
|
|
37915
|
+
* @param max - Maximum value
|
|
37916
|
+
*/
|
|
37917
|
+
setRange(min, max) {
|
|
37918
|
+
this._options.minValue = min;
|
|
37919
|
+
this._options.maxValue = max;
|
|
37920
|
+
this._updateLabels();
|
|
37921
|
+
}
|
|
37922
|
+
/**
|
|
37923
|
+
* Gets the current colormap.
|
|
37924
|
+
*
|
|
37925
|
+
* @returns The current colormap name
|
|
37926
|
+
*/
|
|
37927
|
+
getColormap() {
|
|
37928
|
+
return this._options.colormap;
|
|
37929
|
+
}
|
|
37930
|
+
/**
|
|
37931
|
+
* Gets the current value range.
|
|
37932
|
+
*
|
|
37933
|
+
* @returns Object with min and max values
|
|
37934
|
+
*/
|
|
37935
|
+
getRange() {
|
|
37936
|
+
return {
|
|
37937
|
+
min: this._options.minValue,
|
|
37938
|
+
max: this._options.maxValue
|
|
37939
|
+
};
|
|
37940
|
+
}
|
|
37941
|
+
/**
|
|
37942
|
+
* Draws the color gradient on the canvas.
|
|
37943
|
+
*/
|
|
37944
|
+
_drawGradient() {
|
|
37945
|
+
if (!this._canvas) return;
|
|
37946
|
+
const ctx = this._canvas.getContext("2d");
|
|
37947
|
+
if (!ctx) return;
|
|
37948
|
+
const width = this._canvas.width;
|
|
37949
|
+
const height = this._canvas.height;
|
|
37950
|
+
const ramp = COLORMAPS[this._options.colormap] || COLORMAPS.viridis;
|
|
37951
|
+
const gradient = ctx.createLinearGradient(0, 0, width, 0);
|
|
37952
|
+
for (let i = 0; i < ramp.length; i++) {
|
|
37953
|
+
const t = i / (ramp.length - 1);
|
|
37954
|
+
const [r, g, b] = ramp[i];
|
|
37955
|
+
gradient.addColorStop(t, `rgb(${r}, ${g}, ${b})`);
|
|
37956
|
+
}
|
|
37957
|
+
ctx.fillStyle = gradient;
|
|
37958
|
+
ctx.fillRect(0, 0, width, height);
|
|
37959
|
+
}
|
|
37960
|
+
/**
|
|
37961
|
+
* Updates the min/max value labels.
|
|
37962
|
+
*/
|
|
37963
|
+
_updateLabels() {
|
|
37964
|
+
if (this._minLabel) {
|
|
37965
|
+
this._minLabel.textContent = this._formatValue(this._options.minValue);
|
|
37966
|
+
}
|
|
37967
|
+
if (this._maxLabel) {
|
|
37968
|
+
this._maxLabel.textContent = this._formatValue(this._options.maxValue);
|
|
37969
|
+
}
|
|
37970
|
+
}
|
|
37971
|
+
/**
|
|
37972
|
+
* Formats a value for display.
|
|
37973
|
+
*
|
|
37974
|
+
* @param value - The value to format
|
|
37975
|
+
* @returns Formatted string
|
|
37976
|
+
*/
|
|
37977
|
+
_formatValue(value) {
|
|
37978
|
+
if (!Number.isFinite(value)) {
|
|
37979
|
+
return "—";
|
|
37980
|
+
}
|
|
37981
|
+
const range = Math.abs(this._options.maxValue - this._options.minValue);
|
|
37982
|
+
if (range < 1) {
|
|
37983
|
+
return value.toFixed(3);
|
|
37984
|
+
} else if (range < 10) {
|
|
37985
|
+
return value.toFixed(2);
|
|
37986
|
+
} else if (range < 100) {
|
|
37987
|
+
return value.toFixed(1);
|
|
37988
|
+
} else {
|
|
37989
|
+
return value.toFixed(0);
|
|
37990
|
+
}
|
|
37991
|
+
}
|
|
37992
|
+
}
|
|
37993
|
+
const DEFAULT_PERCENTILE_LOW = 2;
|
|
37994
|
+
const DEFAULT_PERCENTILE_HIGH = 98;
|
|
37995
|
+
class PercentileRangeControl {
|
|
37996
|
+
/**
|
|
37997
|
+
* Creates a new PercentileRangeControl instance.
|
|
37998
|
+
*
|
|
37999
|
+
* @param options - Control configuration options
|
|
38000
|
+
*/
|
|
38001
|
+
constructor(options) {
|
|
38002
|
+
__publicField(this, "_options");
|
|
38003
|
+
__publicField(this, "_percentileRadio");
|
|
38004
|
+
__publicField(this, "_absoluteRadio");
|
|
38005
|
+
__publicField(this, "_percentileSliderContainer");
|
|
38006
|
+
__publicField(this, "_absoluteSliderContainer");
|
|
38007
|
+
__publicField(this, "_percentileSlider");
|
|
38008
|
+
__publicField(this, "_absoluteSlider");
|
|
38009
|
+
__publicField(this, "_computedBounds");
|
|
38010
|
+
this._options = { ...options };
|
|
38011
|
+
this._computedBounds = options.computedBounds;
|
|
38012
|
+
}
|
|
38013
|
+
/**
|
|
38014
|
+
* Renders the control component.
|
|
38015
|
+
*
|
|
38016
|
+
* @returns The control container element
|
|
38017
|
+
*/
|
|
38018
|
+
render() {
|
|
38019
|
+
const container = document.createElement("div");
|
|
38020
|
+
container.className = "lidar-color-range";
|
|
38021
|
+
const labelRow = document.createElement("div");
|
|
38022
|
+
labelRow.className = "lidar-color-range-header";
|
|
38023
|
+
const label = document.createElement("div");
|
|
38024
|
+
label.className = "lidar-control-label";
|
|
38025
|
+
label.textContent = "Color Range";
|
|
38026
|
+
labelRow.appendChild(label);
|
|
38027
|
+
const resetButton = document.createElement("button");
|
|
38028
|
+
resetButton.className = "lidar-range-reset-btn";
|
|
38029
|
+
resetButton.textContent = "Reset";
|
|
38030
|
+
resetButton.title = "Reset to default (2-98% percentile)";
|
|
38031
|
+
resetButton.addEventListener("click", () => this._onReset());
|
|
38032
|
+
labelRow.appendChild(resetButton);
|
|
38033
|
+
container.appendChild(labelRow);
|
|
38034
|
+
const modeContainer = document.createElement("div");
|
|
38035
|
+
modeContainer.className = "lidar-range-mode";
|
|
38036
|
+
const percentileLabel = document.createElement("label");
|
|
38037
|
+
const percentileRadio = document.createElement("input");
|
|
38038
|
+
percentileRadio.type = "radio";
|
|
38039
|
+
percentileRadio.name = "lidar-range-mode";
|
|
38040
|
+
percentileRadio.value = "percentile";
|
|
38041
|
+
percentileRadio.checked = this._options.config.mode === "percentile";
|
|
38042
|
+
this._percentileRadio = percentileRadio;
|
|
38043
|
+
percentileLabel.appendChild(percentileRadio);
|
|
38044
|
+
percentileLabel.appendChild(document.createTextNode(" Percentile"));
|
|
38045
|
+
modeContainer.appendChild(percentileLabel);
|
|
38046
|
+
const absoluteLabel = document.createElement("label");
|
|
38047
|
+
const absoluteRadio = document.createElement("input");
|
|
38048
|
+
absoluteRadio.type = "radio";
|
|
38049
|
+
absoluteRadio.name = "lidar-range-mode";
|
|
38050
|
+
absoluteRadio.value = "absolute";
|
|
38051
|
+
absoluteRadio.checked = this._options.config.mode === "absolute";
|
|
38052
|
+
this._absoluteRadio = absoluteRadio;
|
|
38053
|
+
absoluteLabel.appendChild(absoluteRadio);
|
|
38054
|
+
absoluteLabel.appendChild(document.createTextNode(" Absolute"));
|
|
38055
|
+
modeContainer.appendChild(absoluteLabel);
|
|
38056
|
+
container.appendChild(modeContainer);
|
|
38057
|
+
const percentileSliderContainer = document.createElement("div");
|
|
38058
|
+
percentileSliderContainer.style.display = this._options.config.mode === "percentile" ? "block" : "none";
|
|
38059
|
+
this._percentileSliderContainer = percentileSliderContainer;
|
|
38060
|
+
this._percentileSlider = new DualRangeSlider({
|
|
38061
|
+
label: "",
|
|
38062
|
+
min: 0,
|
|
38063
|
+
max: 100,
|
|
38064
|
+
step: 1,
|
|
38065
|
+
valueLow: this._options.config.percentileLow ?? DEFAULT_PERCENTILE_LOW,
|
|
38066
|
+
valueHigh: this._options.config.percentileHigh ?? DEFAULT_PERCENTILE_HIGH,
|
|
38067
|
+
onChange: (low, high) => this._onPercentileChange(low, high),
|
|
38068
|
+
formatValue: (v) => `${v.toFixed(0)}%`
|
|
38069
|
+
});
|
|
38070
|
+
percentileSliderContainer.appendChild(this._percentileSlider.render());
|
|
38071
|
+
container.appendChild(percentileSliderContainer);
|
|
38072
|
+
const absoluteSliderContainer = document.createElement("div");
|
|
38073
|
+
absoluteSliderContainer.style.display = this._options.config.mode === "absolute" ? "block" : "none";
|
|
38074
|
+
this._absoluteSliderContainer = absoluteSliderContainer;
|
|
38075
|
+
const dataBounds = this._options.dataBounds || { min: 0, max: 100 };
|
|
38076
|
+
const absMin = this._options.config.absoluteMin ?? dataBounds.min;
|
|
38077
|
+
const absMax = this._options.config.absoluteMax ?? dataBounds.max;
|
|
38078
|
+
this._absoluteSlider = new DualRangeSlider({
|
|
38079
|
+
label: "",
|
|
38080
|
+
min: dataBounds.min,
|
|
38081
|
+
max: dataBounds.max,
|
|
38082
|
+
step: this._getAbsoluteStep(dataBounds),
|
|
38083
|
+
valueLow: absMin,
|
|
38084
|
+
valueHigh: absMax,
|
|
38085
|
+
onChange: (low, high) => this._onAbsoluteChange(low, high),
|
|
38086
|
+
formatValue: (v) => this._formatAbsoluteValue(v)
|
|
38087
|
+
});
|
|
38088
|
+
absoluteSliderContainer.appendChild(this._absoluteSlider.render());
|
|
38089
|
+
container.appendChild(absoluteSliderContainer);
|
|
38090
|
+
percentileRadio.addEventListener("change", () => this._onModeChange());
|
|
38091
|
+
absoluteRadio.addEventListener("change", () => this._onModeChange());
|
|
38092
|
+
return container;
|
|
38093
|
+
}
|
|
38094
|
+
/**
|
|
38095
|
+
* Updates the control with new configuration.
|
|
38096
|
+
*
|
|
38097
|
+
* @param config - New color range configuration
|
|
38098
|
+
*/
|
|
38099
|
+
setConfig(config) {
|
|
38100
|
+
this._options.config = { ...config };
|
|
38101
|
+
if (this._percentileRadio && this._absoluteRadio) {
|
|
38102
|
+
this._percentileRadio.checked = config.mode === "percentile";
|
|
38103
|
+
this._absoluteRadio.checked = config.mode === "absolute";
|
|
38104
|
+
}
|
|
38105
|
+
if (this._percentileSlider) {
|
|
38106
|
+
this._percentileSlider.setRange(
|
|
38107
|
+
config.percentileLow ?? DEFAULT_PERCENTILE_LOW,
|
|
38108
|
+
config.percentileHigh ?? DEFAULT_PERCENTILE_HIGH
|
|
38109
|
+
);
|
|
38110
|
+
}
|
|
38111
|
+
if (this._absoluteSlider) {
|
|
38112
|
+
const dataBounds = this._options.dataBounds || { min: 0, max: 100 };
|
|
38113
|
+
this._absoluteSlider.setRange(
|
|
38114
|
+
config.absoluteMin ?? dataBounds.min,
|
|
38115
|
+
config.absoluteMax ?? dataBounds.max
|
|
38116
|
+
);
|
|
38117
|
+
}
|
|
38118
|
+
this._updateSlidersVisibility();
|
|
38119
|
+
}
|
|
38120
|
+
/**
|
|
38121
|
+
* Updates the data bounds (for absolute mode reference).
|
|
38122
|
+
*
|
|
38123
|
+
* @param bounds - Data bounds
|
|
38124
|
+
*/
|
|
38125
|
+
setDataBounds(bounds2) {
|
|
38126
|
+
this._options.dataBounds = bounds2;
|
|
38127
|
+
if (this._absoluteSlider) {
|
|
38128
|
+
this._absoluteSlider.setBounds(bounds2.min, bounds2.max);
|
|
38129
|
+
this._absoluteSlider.setStep(this._getAbsoluteStep(bounds2));
|
|
38130
|
+
if (this._options.config.absoluteMin === void 0) {
|
|
38131
|
+
this._options.config.absoluteMin = bounds2.min;
|
|
38132
|
+
}
|
|
38133
|
+
if (this._options.config.absoluteMax === void 0) {
|
|
38134
|
+
this._options.config.absoluteMax = bounds2.max;
|
|
38135
|
+
}
|
|
38136
|
+
const currentMin = this._options.config.absoluteMin ?? bounds2.min;
|
|
38137
|
+
const currentMax = this._options.config.absoluteMax ?? bounds2.max;
|
|
38138
|
+
const clampedMin = Math.max(bounds2.min, Math.min(bounds2.max, currentMin));
|
|
38139
|
+
const clampedMax = Math.max(bounds2.min, Math.min(bounds2.max, currentMax));
|
|
38140
|
+
this._absoluteSlider.setRange(clampedMin, clampedMax);
|
|
38141
|
+
}
|
|
38142
|
+
}
|
|
38143
|
+
/**
|
|
38144
|
+
* Sets the actual computed bounds from percentile calculation.
|
|
38145
|
+
* These bounds are used when switching from percentile to absolute mode.
|
|
38146
|
+
*
|
|
38147
|
+
* @param bounds - The actual computed bounds
|
|
38148
|
+
*/
|
|
38149
|
+
setComputedBounds(bounds2) {
|
|
38150
|
+
this._computedBounds = bounds2;
|
|
38151
|
+
}
|
|
38152
|
+
/**
|
|
38153
|
+
* Gets the current configuration.
|
|
38154
|
+
*
|
|
38155
|
+
* @returns The current color range configuration
|
|
38156
|
+
*/
|
|
38157
|
+
getConfig() {
|
|
38158
|
+
return { ...this._options.config };
|
|
38159
|
+
}
|
|
38160
|
+
/**
|
|
38161
|
+
* Handles percentile slider change.
|
|
38162
|
+
*/
|
|
38163
|
+
_onPercentileChange(low, high) {
|
|
38164
|
+
this._options.config.percentileLow = low;
|
|
38165
|
+
this._options.config.percentileHigh = high;
|
|
38166
|
+
this._emitChange();
|
|
38167
|
+
}
|
|
38168
|
+
/**
|
|
38169
|
+
* Handles absolute slider change.
|
|
38170
|
+
*/
|
|
38171
|
+
_onAbsoluteChange(low, high) {
|
|
38172
|
+
this._options.config.absoluteMin = low;
|
|
38173
|
+
this._options.config.absoluteMax = high;
|
|
38174
|
+
this._emitChange();
|
|
38175
|
+
}
|
|
38176
|
+
/**
|
|
38177
|
+
* Handles mode change (percentile/absolute toggle).
|
|
38178
|
+
* Syncs values when switching between modes using actual computed bounds.
|
|
38179
|
+
*/
|
|
38180
|
+
_onModeChange() {
|
|
38181
|
+
var _a;
|
|
38182
|
+
const newMode = ((_a = this._percentileRadio) == null ? void 0 : _a.checked) ? "percentile" : "absolute";
|
|
38183
|
+
const oldMode = this._options.config.mode;
|
|
38184
|
+
if (newMode !== oldMode) {
|
|
38185
|
+
if (newMode === "absolute") {
|
|
38186
|
+
if (this._computedBounds) {
|
|
38187
|
+
this._options.config.absoluteMin = this._computedBounds.min;
|
|
38188
|
+
this._options.config.absoluteMax = this._computedBounds.max;
|
|
38189
|
+
} else if (this._options.dataBounds) {
|
|
38190
|
+
const { min: dataMin, max: dataMax } = this._options.dataBounds;
|
|
38191
|
+
const range = dataMax - dataMin;
|
|
38192
|
+
const pLow = this._options.config.percentileLow ?? DEFAULT_PERCENTILE_LOW;
|
|
38193
|
+
const pHigh = this._options.config.percentileHigh ?? DEFAULT_PERCENTILE_HIGH;
|
|
38194
|
+
this._options.config.absoluteMin = parseFloat((dataMin + range * (pLow / 100)).toFixed(2));
|
|
38195
|
+
this._options.config.absoluteMax = parseFloat((dataMin + range * (pHigh / 100)).toFixed(2));
|
|
38196
|
+
}
|
|
38197
|
+
if (this._absoluteSlider && this._options.config.absoluteMin !== void 0 && this._options.config.absoluteMax !== void 0) {
|
|
38198
|
+
this._absoluteSlider.setRange(this._options.config.absoluteMin, this._options.config.absoluteMax);
|
|
38199
|
+
}
|
|
38200
|
+
}
|
|
38201
|
+
}
|
|
38202
|
+
this._options.config.mode = newMode;
|
|
38203
|
+
this._updateSlidersVisibility();
|
|
38204
|
+
this._emitChange();
|
|
38205
|
+
}
|
|
38206
|
+
/**
|
|
38207
|
+
* Handles reset button click.
|
|
38208
|
+
* Resets to default percentile mode with 2-98% range.
|
|
38209
|
+
*/
|
|
38210
|
+
_onReset() {
|
|
38211
|
+
this._options.config.mode = "percentile";
|
|
38212
|
+
this._options.config.percentileLow = DEFAULT_PERCENTILE_LOW;
|
|
38213
|
+
this._options.config.percentileHigh = DEFAULT_PERCENTILE_HIGH;
|
|
38214
|
+
if (this._options.dataBounds) {
|
|
38215
|
+
const { min: dataMin, max: dataMax } = this._options.dataBounds;
|
|
38216
|
+
const range = dataMax - dataMin;
|
|
38217
|
+
this._options.config.absoluteMin = parseFloat((dataMin + range * (DEFAULT_PERCENTILE_LOW / 100)).toFixed(2));
|
|
38218
|
+
this._options.config.absoluteMax = parseFloat((dataMin + range * (DEFAULT_PERCENTILE_HIGH / 100)).toFixed(2));
|
|
38219
|
+
}
|
|
38220
|
+
if (this._percentileRadio) {
|
|
38221
|
+
this._percentileRadio.checked = true;
|
|
38222
|
+
}
|
|
38223
|
+
if (this._absoluteRadio) {
|
|
38224
|
+
this._absoluteRadio.checked = false;
|
|
38225
|
+
}
|
|
38226
|
+
if (this._percentileSlider) {
|
|
38227
|
+
this._percentileSlider.setRange(DEFAULT_PERCENTILE_LOW, DEFAULT_PERCENTILE_HIGH);
|
|
38228
|
+
}
|
|
38229
|
+
if (this._absoluteSlider && this._options.config.absoluteMin !== void 0 && this._options.config.absoluteMax !== void 0) {
|
|
38230
|
+
this._absoluteSlider.setRange(this._options.config.absoluteMin, this._options.config.absoluteMax);
|
|
38231
|
+
}
|
|
38232
|
+
this._updateSlidersVisibility();
|
|
38233
|
+
this._emitChange();
|
|
38234
|
+
}
|
|
38235
|
+
/**
|
|
38236
|
+
* Updates the visibility of slider containers based on mode.
|
|
38237
|
+
*/
|
|
38238
|
+
_updateSlidersVisibility() {
|
|
38239
|
+
if (this._percentileSliderContainer) {
|
|
38240
|
+
this._percentileSliderContainer.style.display = this._options.config.mode === "percentile" ? "block" : "none";
|
|
38241
|
+
}
|
|
38242
|
+
if (this._absoluteSliderContainer) {
|
|
38243
|
+
this._absoluteSliderContainer.style.display = this._options.config.mode === "absolute" ? "block" : "none";
|
|
38244
|
+
}
|
|
38245
|
+
}
|
|
38246
|
+
/**
|
|
38247
|
+
* Gets the appropriate step value for absolute slider based on data range.
|
|
38248
|
+
*/
|
|
38249
|
+
_getAbsoluteStep(bounds2) {
|
|
38250
|
+
const range = bounds2.max - bounds2.min;
|
|
38251
|
+
if (range <= 1) {
|
|
38252
|
+
return 0.01;
|
|
38253
|
+
} else if (range <= 10) {
|
|
38254
|
+
return 0.1;
|
|
38255
|
+
} else if (range <= 100) {
|
|
38256
|
+
return 1;
|
|
38257
|
+
} else if (range <= 1e3) {
|
|
38258
|
+
return 1;
|
|
38259
|
+
} else {
|
|
38260
|
+
return Math.round(range / 100);
|
|
38261
|
+
}
|
|
38262
|
+
}
|
|
38263
|
+
/**
|
|
38264
|
+
* Formats absolute value for display based on the data range.
|
|
38265
|
+
*/
|
|
38266
|
+
_formatAbsoluteValue(value) {
|
|
38267
|
+
const bounds2 = this._options.dataBounds || { min: 0, max: 100 };
|
|
38268
|
+
const range = bounds2.max - bounds2.min;
|
|
38269
|
+
if (range <= 1) {
|
|
38270
|
+
return value.toFixed(2);
|
|
38271
|
+
} else if (range <= 10) {
|
|
38272
|
+
return value.toFixed(1);
|
|
38273
|
+
} else {
|
|
38274
|
+
return value.toFixed(0);
|
|
38275
|
+
}
|
|
38276
|
+
}
|
|
38277
|
+
/**
|
|
38278
|
+
* Emits a change event with the current configuration.
|
|
38279
|
+
*/
|
|
38280
|
+
_emitChange() {
|
|
38281
|
+
this._options.onChange({ ...this._options.config });
|
|
38282
|
+
}
|
|
38283
|
+
}
|
|
37395
38284
|
class PanelBuilder {
|
|
37396
38285
|
constructor(callbacks, initialState) {
|
|
37397
38286
|
__publicField(this, "_callbacks");
|
|
@@ -37402,6 +38291,12 @@ class PanelBuilder {
|
|
|
37402
38291
|
__publicField(this, "_urlInput");
|
|
37403
38292
|
__publicField(this, "_loadButton");
|
|
37404
38293
|
__publicField(this, "_colorSelect");
|
|
38294
|
+
__publicField(this, "_colormapSelect");
|
|
38295
|
+
__publicField(this, "_colormapGroup");
|
|
38296
|
+
__publicField(this, "_colorbar");
|
|
38297
|
+
__publicField(this, "_colorbarContainer");
|
|
38298
|
+
__publicField(this, "_colorRangeControl");
|
|
38299
|
+
__publicField(this, "_colorRangeContainer");
|
|
37405
38300
|
__publicField(this, "_percentileCheckbox");
|
|
37406
38301
|
__publicField(this, "_percentileGroup");
|
|
37407
38302
|
__publicField(this, "_pointSizeSlider");
|
|
@@ -37477,6 +38372,25 @@ class PanelBuilder {
|
|
|
37477
38372
|
this._colorSelect.value = state.colorScheme;
|
|
37478
38373
|
this._updatePercentileVisibility(state.colorScheme);
|
|
37479
38374
|
}
|
|
38375
|
+
if (this._colormapSelect && state.colormap) {
|
|
38376
|
+
this._colormapSelect.value = state.colormap;
|
|
38377
|
+
}
|
|
38378
|
+
if (this._colorbar) {
|
|
38379
|
+
if (state.colormap) {
|
|
38380
|
+
this._colorbar.setColormap(state.colormap);
|
|
38381
|
+
}
|
|
38382
|
+
if (state.computedColorBounds) {
|
|
38383
|
+
this._colorbar.setRange(state.computedColorBounds.min, state.computedColorBounds.max);
|
|
38384
|
+
}
|
|
38385
|
+
}
|
|
38386
|
+
if (this._colorRangeControl && state.colorRange) {
|
|
38387
|
+
this._colorRangeControl.setConfig(state.colorRange);
|
|
38388
|
+
const bounds2 = this._getDataBoundsForCurrentScheme();
|
|
38389
|
+
this._colorRangeControl.setDataBounds(bounds2);
|
|
38390
|
+
if (state.computedColorBounds) {
|
|
38391
|
+
this._colorRangeControl.setComputedBounds(state.computedColorBounds);
|
|
38392
|
+
}
|
|
38393
|
+
}
|
|
37480
38394
|
if (this._percentileCheckbox) {
|
|
37481
38395
|
this._percentileCheckbox.checked = state.usePercentile ?? true;
|
|
37482
38396
|
}
|
|
@@ -37617,8 +38531,10 @@ class PanelBuilder {
|
|
|
37617
38531
|
this._colorSelect = colorSelect;
|
|
37618
38532
|
colorGroup.appendChild(colorSelect);
|
|
37619
38533
|
section.appendChild(colorGroup);
|
|
38534
|
+
section.appendChild(this._buildColormapSelector());
|
|
38535
|
+
section.appendChild(this._buildColorbar());
|
|
37620
38536
|
section.appendChild(this._buildClassificationLegend());
|
|
37621
|
-
section.appendChild(this.
|
|
38537
|
+
section.appendChild(this._buildColorRangeControl());
|
|
37622
38538
|
this._pointSizeSlider = new RangeSlider({
|
|
37623
38539
|
label: "Point Size",
|
|
37624
38540
|
min: 1,
|
|
@@ -37803,46 +38719,120 @@ class PanelBuilder {
|
|
|
37803
38719
|
return { min: minZ, max: maxZ };
|
|
37804
38720
|
}
|
|
37805
38721
|
/**
|
|
37806
|
-
*
|
|
38722
|
+
* Gets the intensity bounds.
|
|
38723
|
+
* Intensity values are normalized to 0-1 range during loading.
|
|
38724
|
+
*/
|
|
38725
|
+
_getIntensityBounds() {
|
|
38726
|
+
return { min: 0, max: 1 };
|
|
38727
|
+
}
|
|
38728
|
+
/**
|
|
38729
|
+
* Gets the appropriate data bounds based on the current color scheme.
|
|
38730
|
+
*/
|
|
38731
|
+
_getDataBoundsForCurrentScheme() {
|
|
38732
|
+
const colorScheme = typeof this._state.colorScheme === "string" ? this._state.colorScheme : "elevation";
|
|
38733
|
+
if (colorScheme === "intensity") {
|
|
38734
|
+
return this._getIntensityBounds();
|
|
38735
|
+
}
|
|
38736
|
+
return this._getElevationBounds();
|
|
38737
|
+
}
|
|
38738
|
+
/**
|
|
38739
|
+
* Builds the colormap selector dropdown.
|
|
37807
38740
|
*/
|
|
37808
|
-
|
|
38741
|
+
_buildColormapSelector() {
|
|
37809
38742
|
const group = document.createElement("div");
|
|
37810
|
-
group.className = "lidar-
|
|
37811
|
-
this.
|
|
37812
|
-
const
|
|
37813
|
-
|
|
37814
|
-
|
|
37815
|
-
const checkbox = document.createElement("input");
|
|
37816
|
-
checkbox.type = "checkbox";
|
|
37817
|
-
checkbox.id = "lidar-percentile-checkbox";
|
|
37818
|
-
checkbox.checked = this._state.usePercentile ?? true;
|
|
37819
|
-
checkbox.style.marginRight = "6px";
|
|
37820
|
-
this._percentileCheckbox = checkbox;
|
|
38743
|
+
group.className = "lidar-colormap-group";
|
|
38744
|
+
this._colormapGroup = group;
|
|
38745
|
+
const currentScheme = typeof this._state.colorScheme === "string" ? this._state.colorScheme : "elevation";
|
|
38746
|
+
const showColormap = currentScheme === "elevation" || currentScheme === "intensity";
|
|
38747
|
+
group.style.display = showColormap ? "block" : "none";
|
|
37821
38748
|
const label = document.createElement("label");
|
|
37822
38749
|
label.className = "lidar-control-label";
|
|
37823
|
-
label.
|
|
37824
|
-
label
|
|
37825
|
-
|
|
37826
|
-
|
|
37827
|
-
|
|
37828
|
-
|
|
37829
|
-
|
|
38750
|
+
label.textContent = "Colormap";
|
|
38751
|
+
group.appendChild(label);
|
|
38752
|
+
const select = document.createElement("select");
|
|
38753
|
+
select.className = "lidar-colormap-select";
|
|
38754
|
+
for (const name of COLORMAP_NAMES) {
|
|
38755
|
+
const option = document.createElement("option");
|
|
38756
|
+
option.value = name;
|
|
38757
|
+
option.textContent = COLORMAP_LABELS[name];
|
|
38758
|
+
select.appendChild(option);
|
|
38759
|
+
}
|
|
38760
|
+
select.value = this._state.colormap || "viridis";
|
|
38761
|
+
this._colormapSelect = select;
|
|
38762
|
+
select.addEventListener("change", () => {
|
|
38763
|
+
const colormap = select.value;
|
|
38764
|
+
this._callbacks.onColormapChange(colormap);
|
|
37830
38765
|
});
|
|
37831
|
-
|
|
37832
|
-
labelRow.appendChild(label);
|
|
37833
|
-
group.appendChild(labelRow);
|
|
37834
|
-
const currentScheme = typeof this._state.colorScheme === "string" ? this._state.colorScheme : "elevation";
|
|
37835
|
-
this._updatePercentileVisibility(currentScheme);
|
|
38766
|
+
group.appendChild(select);
|
|
37836
38767
|
return group;
|
|
37837
38768
|
}
|
|
37838
38769
|
/**
|
|
37839
|
-
*
|
|
37840
|
-
|
|
38770
|
+
* Builds the colorbar component.
|
|
38771
|
+
*/
|
|
38772
|
+
_buildColorbar() {
|
|
38773
|
+
var _a, _b;
|
|
38774
|
+
const container = document.createElement("div");
|
|
38775
|
+
container.className = "lidar-control-group";
|
|
38776
|
+
this._colorbarContainer = container;
|
|
38777
|
+
const currentScheme = typeof this._state.colorScheme === "string" ? this._state.colorScheme : "elevation";
|
|
38778
|
+
const showColorbar = (currentScheme === "elevation" || currentScheme === "intensity") && this._state.showColorbar;
|
|
38779
|
+
container.style.display = showColorbar ? "block" : "none";
|
|
38780
|
+
this._colorbar = new Colorbar({
|
|
38781
|
+
colormap: this._state.colormap || "viridis",
|
|
38782
|
+
minValue: ((_a = this._state.computedColorBounds) == null ? void 0 : _a.min) ?? 0,
|
|
38783
|
+
maxValue: ((_b = this._state.computedColorBounds) == null ? void 0 : _b.max) ?? 100
|
|
38784
|
+
});
|
|
38785
|
+
container.appendChild(this._colorbar.render());
|
|
38786
|
+
return container;
|
|
38787
|
+
}
|
|
38788
|
+
/**
|
|
38789
|
+
* Builds the color range control (replaces percentile checkbox).
|
|
38790
|
+
*/
|
|
38791
|
+
_buildColorRangeControl() {
|
|
38792
|
+
const container = document.createElement("div");
|
|
38793
|
+
container.className = "lidar-control-group";
|
|
38794
|
+
this._colorRangeContainer = container;
|
|
38795
|
+
const currentScheme = typeof this._state.colorScheme === "string" ? this._state.colorScheme : "elevation";
|
|
38796
|
+
const showControl = currentScheme === "elevation" || currentScheme === "intensity";
|
|
38797
|
+
container.style.display = showControl ? "block" : "none";
|
|
38798
|
+
const dataBounds = this._getDataBoundsForCurrentScheme();
|
|
38799
|
+
this._colorRangeControl = new PercentileRangeControl({
|
|
38800
|
+
config: this._state.colorRange || {
|
|
38801
|
+
mode: "percentile",
|
|
38802
|
+
percentileLow: 2,
|
|
38803
|
+
percentileHigh: 98
|
|
38804
|
+
},
|
|
38805
|
+
dataBounds,
|
|
38806
|
+
computedBounds: this._state.computedColorBounds,
|
|
38807
|
+
onChange: (config) => {
|
|
38808
|
+
this._callbacks.onColorRangeChange(config);
|
|
38809
|
+
}
|
|
38810
|
+
});
|
|
38811
|
+
container.appendChild(this._colorRangeControl.render());
|
|
38812
|
+
return container;
|
|
38813
|
+
}
|
|
38814
|
+
/**
|
|
38815
|
+
* Updates the visibility of color-related controls based on color scheme.
|
|
38816
|
+
* Shows colormap/colorbar/range for elevation and intensity.
|
|
38817
|
+
* Shows classification legend for classification.
|
|
37841
38818
|
*/
|
|
37842
38819
|
_updatePercentileVisibility(colorScheme) {
|
|
38820
|
+
const showColorControls = colorScheme === "elevation" || colorScheme === "intensity";
|
|
38821
|
+
if (this._colormapGroup) {
|
|
38822
|
+
this._colormapGroup.style.display = showColorControls ? "block" : "none";
|
|
38823
|
+
}
|
|
38824
|
+
if (this._colorbarContainer) {
|
|
38825
|
+
this._colorbarContainer.style.display = showColorControls && this._state.showColorbar ? "block" : "none";
|
|
38826
|
+
}
|
|
38827
|
+
if (this._colorRangeContainer) {
|
|
38828
|
+
this._colorRangeContainer.style.display = showColorControls ? "block" : "none";
|
|
38829
|
+
}
|
|
38830
|
+
if (this._colorRangeControl && showColorControls) {
|
|
38831
|
+
const bounds2 = this._getDataBoundsForCurrentScheme();
|
|
38832
|
+
this._colorRangeControl.setDataBounds(bounds2);
|
|
38833
|
+
}
|
|
37843
38834
|
if (this._percentileGroup) {
|
|
37844
|
-
|
|
37845
|
-
this._percentileGroup.style.display = showPercentile ? "block" : "none";
|
|
38835
|
+
this._percentileGroup.style.display = "none";
|
|
37846
38836
|
}
|
|
37847
38837
|
if (this._classificationLegendContainer) {
|
|
37848
38838
|
this._classificationLegendContainer.style.display = colorScheme === "classification" ? "block" : "none";
|
|
@@ -38009,6 +38999,9 @@ const DEFAULT_OPTIONS = {
|
|
|
38009
38999
|
opacity: 1,
|
|
38010
39000
|
colorScheme: "elevation",
|
|
38011
39001
|
usePercentile: true,
|
|
39002
|
+
colormap: "viridis",
|
|
39003
|
+
colorRange: { mode: "percentile", percentileLow: 2, percentileHigh: 98 },
|
|
39004
|
+
showColorbar: true,
|
|
38012
39005
|
pointBudget: 1e6,
|
|
38013
39006
|
elevationRange: null,
|
|
38014
39007
|
pickable: false,
|
|
@@ -38055,7 +39048,14 @@ class LidarControl {
|
|
|
38055
39048
|
__publicField(this, "_streamingLoaders", /* @__PURE__ */ new Map());
|
|
38056
39049
|
__publicField(this, "_eptStreamingLoaders", /* @__PURE__ */ new Map());
|
|
38057
39050
|
__publicField(this, "_viewportManagers", /* @__PURE__ */ new Map());
|
|
39051
|
+
__publicField(this, "_eptViewportRequestIds", /* @__PURE__ */ new Map());
|
|
39052
|
+
__publicField(this, "_eptLastViewport", /* @__PURE__ */ new Map());
|
|
38058
39053
|
this._options = { ...DEFAULT_OPTIONS, ...options };
|
|
39054
|
+
const defaultColorRange = this._options.colorRange ?? {
|
|
39055
|
+
mode: "percentile",
|
|
39056
|
+
percentileLow: 2,
|
|
39057
|
+
percentileHigh: 98
|
|
39058
|
+
};
|
|
38059
39059
|
this._state = {
|
|
38060
39060
|
collapsed: this._options.collapsed,
|
|
38061
39061
|
panelWidth: this._options.panelWidth,
|
|
@@ -38065,6 +39065,9 @@ class LidarControl {
|
|
|
38065
39065
|
pointSize: this._options.pointSize,
|
|
38066
39066
|
opacity: this._options.opacity,
|
|
38067
39067
|
colorScheme: this._options.colorScheme,
|
|
39068
|
+
colormap: this._options.colormap ?? "viridis",
|
|
39069
|
+
colorRange: defaultColorRange,
|
|
39070
|
+
showColorbar: this._options.showColorbar ?? true,
|
|
38068
39071
|
usePercentile: this._options.usePercentile,
|
|
38069
39072
|
elevationRange: this._options.elevationRange,
|
|
38070
39073
|
pointBudget: this._options.pointBudget,
|
|
@@ -38260,7 +39263,7 @@ class LidarControl {
|
|
|
38260
39263
|
* @returns Promise resolving to the point cloud info
|
|
38261
39264
|
*/
|
|
38262
39265
|
async loadPointCloud(source, options) {
|
|
38263
|
-
var _a, _b, _c;
|
|
39266
|
+
var _a, _b, _c, _d;
|
|
38264
39267
|
const isEptUrl = typeof source === "string" && (source.endsWith("/ept.json") || source.includes("/ept.json?"));
|
|
38265
39268
|
if (isEptUrl) {
|
|
38266
39269
|
return this.loadPointCloudEptStreaming(source);
|
|
@@ -38335,6 +39338,8 @@ class LidarControl {
|
|
|
38335
39338
|
zOffset: zOffset ?? this._state.zOffset,
|
|
38336
39339
|
zOffsetEnabled
|
|
38337
39340
|
});
|
|
39341
|
+
this._updateComputedColorBounds();
|
|
39342
|
+
(_c = this._panelBuilder) == null ? void 0 : _c.updateState(this._state);
|
|
38338
39343
|
this._emitWithData("load", { pointCloud: info2 });
|
|
38339
39344
|
if (this._options.autoZoom) {
|
|
38340
39345
|
this.flyToPointCloud(id);
|
|
@@ -38348,7 +39353,7 @@ class LidarControl {
|
|
|
38348
39353
|
console.warn(
|
|
38349
39354
|
`CORS error detected for ${source}. Falling back to download mode...`
|
|
38350
39355
|
);
|
|
38351
|
-
(
|
|
39356
|
+
(_d = this._panelBuilder) == null ? void 0 : _d.updateLoadingProgress(5, "CORS blocked - downloading file...");
|
|
38352
39357
|
return this._loadPointCloudFullDownload(source);
|
|
38353
39358
|
}
|
|
38354
39359
|
this.setState({
|
|
@@ -38406,7 +39411,7 @@ class LidarControl {
|
|
|
38406
39411
|
* @returns Promise resolving to initial point cloud info
|
|
38407
39412
|
*/
|
|
38408
39413
|
async loadPointCloudStreaming(source, options) {
|
|
38409
|
-
var _a, _b, _c, _d;
|
|
39414
|
+
var _a, _b, _c, _d, _e;
|
|
38410
39415
|
const id = generateId("pc-stream");
|
|
38411
39416
|
let name;
|
|
38412
39417
|
if (typeof source === "string") {
|
|
@@ -38511,13 +39516,15 @@ class LidarControl {
|
|
|
38511
39516
|
pointClouds,
|
|
38512
39517
|
activePointCloudId: id
|
|
38513
39518
|
});
|
|
39519
|
+
this._updateComputedColorBounds();
|
|
39520
|
+
(_c = this._panelBuilder) == null ? void 0 : _c.updateState(this._state);
|
|
38514
39521
|
viewportManager.start();
|
|
38515
39522
|
if (this._options.autoZoom) {
|
|
38516
39523
|
const clampedMinY = Math.max(-90, Math.min(90, bounds2.minY));
|
|
38517
39524
|
const clampedMaxY = Math.max(-90, Math.min(90, bounds2.maxY));
|
|
38518
39525
|
const clampedMinX = Math.max(-180, Math.min(180, bounds2.minX));
|
|
38519
39526
|
const clampedMaxX = Math.max(-180, Math.min(180, bounds2.maxX));
|
|
38520
|
-
(
|
|
39527
|
+
(_d = this._map) == null ? void 0 : _d.fitBounds(
|
|
38521
39528
|
[
|
|
38522
39529
|
[clampedMinX, clampedMinY],
|
|
38523
39530
|
[clampedMaxX, clampedMaxY]
|
|
@@ -38556,7 +39563,7 @@ class LidarControl {
|
|
|
38556
39563
|
streamingActive: hasActiveStreaming,
|
|
38557
39564
|
error: null
|
|
38558
39565
|
});
|
|
38559
|
-
(
|
|
39566
|
+
(_e = this._panelBuilder) == null ? void 0 : _e.updateLoadingProgress(5, "CORS blocked - downloading file...");
|
|
38560
39567
|
return this._loadPointCloudFullDownload(source);
|
|
38561
39568
|
}
|
|
38562
39569
|
this.setState({
|
|
@@ -38577,7 +39584,7 @@ class LidarControl {
|
|
|
38577
39584
|
* @returns Promise resolving to initial point cloud info
|
|
38578
39585
|
*/
|
|
38579
39586
|
async loadPointCloudEptStreaming(eptUrl, options) {
|
|
38580
|
-
var _a, _b, _c, _d;
|
|
39587
|
+
var _a, _b, _c, _d, _e;
|
|
38581
39588
|
const id = generateId("ept-stream");
|
|
38582
39589
|
const name = getFilename(eptUrl.replace("/ept.json", ""));
|
|
38583
39590
|
this.setState({ loading: true, error: null, streamingActive: true });
|
|
@@ -38675,13 +39682,15 @@ class LidarControl {
|
|
|
38675
39682
|
pointClouds,
|
|
38676
39683
|
activePointCloudId: id
|
|
38677
39684
|
});
|
|
39685
|
+
this._updateComputedColorBounds();
|
|
39686
|
+
(_d = this._panelBuilder) == null ? void 0 : _d.updateState(this._state);
|
|
38678
39687
|
viewportManager.start();
|
|
38679
39688
|
if (this._options.autoZoom) {
|
|
38680
39689
|
const clampedMinY = Math.max(-90, Math.min(90, bounds2.minY));
|
|
38681
39690
|
const clampedMaxY = Math.max(-90, Math.min(90, bounds2.maxY));
|
|
38682
39691
|
const clampedMinX = Math.max(-180, Math.min(180, bounds2.minX));
|
|
38683
39692
|
const clampedMaxX = Math.max(-180, Math.min(180, bounds2.maxX));
|
|
38684
|
-
(
|
|
39693
|
+
(_e = this._map) == null ? void 0 : _e.fitBounds(
|
|
38685
39694
|
[
|
|
38686
39695
|
[clampedMinX, clampedMinY],
|
|
38687
39696
|
[clampedMaxX, clampedMaxY]
|
|
@@ -38704,6 +39713,8 @@ class LidarControl {
|
|
|
38704
39713
|
eptLoader.destroy();
|
|
38705
39714
|
this._eptStreamingLoaders.delete(id);
|
|
38706
39715
|
}
|
|
39716
|
+
this._eptViewportRequestIds.delete(id);
|
|
39717
|
+
this._eptLastViewport.delete(id);
|
|
38707
39718
|
const viewportManager = this._viewportManagers.get(id);
|
|
38708
39719
|
if (viewportManager) {
|
|
38709
39720
|
viewportManager.destroy();
|
|
@@ -38725,15 +39736,77 @@ class LidarControl {
|
|
|
38725
39736
|
* @param viewport - Current viewport information
|
|
38726
39737
|
* @param datasetId - ID of the EPT dataset
|
|
38727
39738
|
*/
|
|
38728
|
-
|
|
39739
|
+
_shouldResetEptForViewportChange(previous, current) {
|
|
39740
|
+
if (!previous) return false;
|
|
39741
|
+
const [prevWest, prevSouth, prevEast, prevNorth] = previous.bounds;
|
|
39742
|
+
const [curWest, curSouth, curEast, curNorth] = current.bounds;
|
|
39743
|
+
const intersects = !(curEast < prevWest || curWest > prevEast || curNorth < prevSouth || curSouth > prevNorth);
|
|
39744
|
+
if (!intersects) return true;
|
|
39745
|
+
const prevWidth = prevEast - prevWest;
|
|
39746
|
+
const prevHeight = prevNorth - prevSouth;
|
|
39747
|
+
const dx = current.center[0] - previous.center[0];
|
|
39748
|
+
const dy = current.center[1] - previous.center[1];
|
|
39749
|
+
const centerDistance = Math.sqrt(dx * dx + dy * dy);
|
|
39750
|
+
const threshold = Math.max(prevWidth, prevHeight) * 0.3;
|
|
39751
|
+
return centerDistance > threshold;
|
|
39752
|
+
}
|
|
39753
|
+
async _handleViewportChangeForEptStreaming(viewport, datasetId, requestId) {
|
|
38729
39754
|
const eptLoader = this._eptStreamingLoaders.get(datasetId);
|
|
38730
39755
|
if (!eptLoader) return;
|
|
38731
39756
|
try {
|
|
38732
|
-
const
|
|
39757
|
+
const currentRequestId = requestId ?? (this._eptViewportRequestIds.get(datasetId) ?? 0) + 1;
|
|
39758
|
+
if (requestId === void 0) {
|
|
39759
|
+
this._eptViewportRequestIds.set(datasetId, currentRequestId);
|
|
39760
|
+
}
|
|
39761
|
+
if (this._eptViewportRequestIds.get(datasetId) !== currentRequestId) return;
|
|
39762
|
+
const previousViewport = this._eptLastViewport.get(datasetId);
|
|
39763
|
+
const shouldResetForMove = this._shouldResetEptForViewportChange(previousViewport, viewport);
|
|
39764
|
+
this._eptLastViewport.set(datasetId, viewport);
|
|
39765
|
+
eptLoader.pruneQueueForViewport(viewport);
|
|
39766
|
+
if (shouldResetForMove) {
|
|
39767
|
+
const resetSucceeded2 = eptLoader.resetLoadedData();
|
|
39768
|
+
if (!resetSucceeded2) {
|
|
39769
|
+
setTimeout(() => {
|
|
39770
|
+
this._handleViewportChangeForEptStreaming(viewport, datasetId, currentRequestId);
|
|
39771
|
+
}, 200);
|
|
39772
|
+
return;
|
|
39773
|
+
}
|
|
39774
|
+
}
|
|
39775
|
+
let nodesToLoad = await eptLoader.selectNodesForViewport(viewport);
|
|
39776
|
+
let resetSucceeded = false;
|
|
39777
|
+
const loadedPoints = eptLoader.getLoadedPointCount();
|
|
39778
|
+
const pointBudget = eptLoader.getPointBudget();
|
|
39779
|
+
const budgetReached = loadedPoints >= pointBudget * 0.8;
|
|
39780
|
+
const minDepthForCoverage = Math.max(0, viewport.targetDepth - 2);
|
|
39781
|
+
const coverageRatio = eptLoader.getViewportCoverageRatio(viewport, minDepthForCoverage);
|
|
39782
|
+
const needsCoverage = coverageRatio < 0.5;
|
|
39783
|
+
const hasPendingSubtrees = eptLoader.hasPendingSubtrees(viewport);
|
|
39784
|
+
const hasPendingWork = nodesToLoad.length > 0 || hasPendingSubtrees;
|
|
39785
|
+
if (budgetReached && needsCoverage && hasPendingWork) {
|
|
39786
|
+
resetSucceeded = eptLoader.resetLoadedData();
|
|
39787
|
+
if (resetSucceeded) {
|
|
39788
|
+
nodesToLoad = await eptLoader.selectNodesForViewport(viewport);
|
|
39789
|
+
}
|
|
39790
|
+
}
|
|
39791
|
+
if (!budgetReached && coverageRatio < 0.1 && nodesToLoad.length === 0) {
|
|
39792
|
+
nodesToLoad = await eptLoader.selectNodesForViewport(viewport);
|
|
39793
|
+
}
|
|
38733
39794
|
for (const node of nodesToLoad) {
|
|
38734
39795
|
eptLoader.queueNode(node);
|
|
38735
39796
|
}
|
|
38736
39797
|
await eptLoader.loadQueuedNodes();
|
|
39798
|
+
if (this._eptViewportRequestIds.get(datasetId) !== currentRequestId) return;
|
|
39799
|
+
if (budgetReached && needsCoverage && nodesToLoad.length > 0 && !resetSucceeded) {
|
|
39800
|
+
setTimeout(() => {
|
|
39801
|
+
this._handleViewportChangeForEptStreaming(viewport, datasetId, currentRequestId);
|
|
39802
|
+
}, 200);
|
|
39803
|
+
return;
|
|
39804
|
+
}
|
|
39805
|
+
if (hasPendingSubtrees) {
|
|
39806
|
+
setTimeout(() => {
|
|
39807
|
+
this._handleViewportChangeForEptStreaming(viewport, datasetId, currentRequestId);
|
|
39808
|
+
}, 100);
|
|
39809
|
+
}
|
|
38737
39810
|
} catch (err) {
|
|
38738
39811
|
console.warn("Failed to load EPT nodes for viewport:", err);
|
|
38739
39812
|
}
|
|
@@ -38743,7 +39816,7 @@ class LidarControl {
|
|
|
38743
39816
|
* Used as fallback when streaming fails due to CORS.
|
|
38744
39817
|
*/
|
|
38745
39818
|
async _loadPointCloudFullDownload(url) {
|
|
38746
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
39819
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
38747
39820
|
const id = generateId("pc");
|
|
38748
39821
|
const name = getFilename(url);
|
|
38749
39822
|
try {
|
|
@@ -38830,6 +39903,8 @@ class LidarControl {
|
|
|
38830
39903
|
zOffset: zOffset ?? this._state.zOffset,
|
|
38831
39904
|
zOffsetEnabled
|
|
38832
39905
|
});
|
|
39906
|
+
this._updateComputedColorBounds();
|
|
39907
|
+
(_i = this._panelBuilder) == null ? void 0 : _i.updateState(this._state);
|
|
38833
39908
|
this._emitWithData("load", { pointCloud: info2 });
|
|
38834
39909
|
if (this._options.autoZoom) {
|
|
38835
39910
|
this.flyToPointCloud(id);
|
|
@@ -38926,6 +40001,8 @@ class LidarControl {
|
|
|
38926
40001
|
eptLoader.destroy();
|
|
38927
40002
|
}
|
|
38928
40003
|
this._eptStreamingLoaders.clear();
|
|
40004
|
+
this._eptViewportRequestIds.clear();
|
|
40005
|
+
this._eptLastViewport.clear();
|
|
38929
40006
|
for (const streamingId of streamingIds) {
|
|
38930
40007
|
(_c = this._pointCloudManager) == null ? void 0 : _c.removePointCloud(streamingId);
|
|
38931
40008
|
}
|
|
@@ -38994,16 +40071,84 @@ class LidarControl {
|
|
|
38994
40071
|
}
|
|
38995
40072
|
/**
|
|
38996
40073
|
* Sets the color scheme.
|
|
40074
|
+
* Automatically switches colormap and resets color range when changing between elevation and intensity.
|
|
38997
40075
|
*
|
|
38998
40076
|
* @param scheme - Color scheme to apply
|
|
38999
40077
|
*/
|
|
39000
40078
|
setColorScheme(scheme) {
|
|
39001
|
-
var _a;
|
|
40079
|
+
var _a, _b, _c, _d, _e;
|
|
40080
|
+
const previousScheme = this._state.colorScheme;
|
|
39002
40081
|
this._state.colorScheme = scheme;
|
|
39003
40082
|
(_a = this._pointCloudManager) == null ? void 0 : _a.setColorScheme(scheme);
|
|
40083
|
+
if (typeof scheme === "string" && typeof previousScheme === "string") {
|
|
40084
|
+
const isNewElevationOrIntensity = scheme === "elevation" || scheme === "intensity";
|
|
40085
|
+
const wasElevationOrIntensity = previousScheme === "elevation" || previousScheme === "intensity";
|
|
40086
|
+
const switchedBetweenElevationAndIntensity = isNewElevationOrIntensity && wasElevationOrIntensity && scheme !== previousScheme;
|
|
40087
|
+
if (switchedBetweenElevationAndIntensity) {
|
|
40088
|
+
this._state.colorRange = {
|
|
40089
|
+
mode: "percentile",
|
|
40090
|
+
percentileLow: 2,
|
|
40091
|
+
percentileHigh: 98
|
|
40092
|
+
};
|
|
40093
|
+
(_b = this._pointCloudManager) == null ? void 0 : _b.setColorRange(this._state.colorRange);
|
|
40094
|
+
}
|
|
40095
|
+
if (scheme === "intensity" && previousScheme !== "intensity") {
|
|
40096
|
+
this._state.colormap = "gray";
|
|
40097
|
+
(_c = this._pointCloudManager) == null ? void 0 : _c.setColormap("gray");
|
|
40098
|
+
} else if (scheme === "elevation" && previousScheme === "intensity") {
|
|
40099
|
+
this._state.colormap = "viridis";
|
|
40100
|
+
(_d = this._pointCloudManager) == null ? void 0 : _d.setColormap("viridis");
|
|
40101
|
+
}
|
|
40102
|
+
}
|
|
40103
|
+
this._updateComputedColorBounds();
|
|
40104
|
+
(_e = this._panelBuilder) == null ? void 0 : _e.updateState(this._state);
|
|
40105
|
+
this._emit("stylechange");
|
|
40106
|
+
this._emit("statechange");
|
|
40107
|
+
}
|
|
40108
|
+
/**
|
|
40109
|
+
* Sets the colormap for elevation/intensity coloring.
|
|
40110
|
+
*
|
|
40111
|
+
* @param colormap - The colormap name (e.g., 'viridis', 'plasma', 'turbo')
|
|
40112
|
+
*/
|
|
40113
|
+
setColormap(colormap) {
|
|
40114
|
+
var _a, _b;
|
|
40115
|
+
this._state.colormap = colormap;
|
|
40116
|
+
(_a = this._pointCloudManager) == null ? void 0 : _a.setColormap(colormap);
|
|
40117
|
+
this._updateComputedColorBounds();
|
|
40118
|
+
(_b = this._panelBuilder) == null ? void 0 : _b.updateState(this._state);
|
|
40119
|
+
this._emit("stylechange");
|
|
40120
|
+
this._emit("statechange");
|
|
40121
|
+
}
|
|
40122
|
+
/**
|
|
40123
|
+
* Gets the current colormap.
|
|
40124
|
+
*
|
|
40125
|
+
* @returns The current colormap name
|
|
40126
|
+
*/
|
|
40127
|
+
getColormap() {
|
|
40128
|
+
return this._state.colormap;
|
|
40129
|
+
}
|
|
40130
|
+
/**
|
|
40131
|
+
* Sets the color range configuration.
|
|
40132
|
+
*
|
|
40133
|
+
* @param config - The color range configuration
|
|
40134
|
+
*/
|
|
40135
|
+
setColorRange(config) {
|
|
40136
|
+
var _a, _b;
|
|
40137
|
+
this._state.colorRange = config;
|
|
40138
|
+
(_a = this._pointCloudManager) == null ? void 0 : _a.setColorRange(config);
|
|
40139
|
+
this._updateComputedColorBounds();
|
|
40140
|
+
(_b = this._panelBuilder) == null ? void 0 : _b.updateState(this._state);
|
|
39004
40141
|
this._emit("stylechange");
|
|
39005
40142
|
this._emit("statechange");
|
|
39006
40143
|
}
|
|
40144
|
+
/**
|
|
40145
|
+
* Gets the current color range configuration.
|
|
40146
|
+
*
|
|
40147
|
+
* @returns The current color range configuration
|
|
40148
|
+
*/
|
|
40149
|
+
getColorRange() {
|
|
40150
|
+
return this._state.colorRange;
|
|
40151
|
+
}
|
|
39007
40152
|
/**
|
|
39008
40153
|
* Sets whether to use percentile range for elevation/intensity coloring.
|
|
39009
40154
|
*
|
|
@@ -39213,6 +40358,50 @@ class LidarControl {
|
|
|
39213
40358
|
handlers.forEach((handler) => handler(eventData));
|
|
39214
40359
|
}
|
|
39215
40360
|
}
|
|
40361
|
+
/**
|
|
40362
|
+
* Updates the computed color bounds based on the current color scheme and range settings.
|
|
40363
|
+
* This is used to display accurate min/max values in the colorbar.
|
|
40364
|
+
*/
|
|
40365
|
+
_updateComputedColorBounds() {
|
|
40366
|
+
var _a;
|
|
40367
|
+
if (this._state.pointClouds.length === 0) {
|
|
40368
|
+
this._state.computedColorBounds = void 0;
|
|
40369
|
+
return;
|
|
40370
|
+
}
|
|
40371
|
+
const actualBounds = (_a = this._pointCloudManager) == null ? void 0 : _a.getLastComputedBounds();
|
|
40372
|
+
if (actualBounds) {
|
|
40373
|
+
this._state.computedColorBounds = actualBounds;
|
|
40374
|
+
} else {
|
|
40375
|
+
const colorScheme = typeof this._state.colorScheme === "string" ? this._state.colorScheme : "elevation";
|
|
40376
|
+
if (colorScheme === "intensity") {
|
|
40377
|
+
this._state.computedColorBounds = this._getIntensityBounds();
|
|
40378
|
+
} else {
|
|
40379
|
+
this._state.computedColorBounds = this._getElevationBounds();
|
|
40380
|
+
}
|
|
40381
|
+
}
|
|
40382
|
+
}
|
|
40383
|
+
/**
|
|
40384
|
+
* Gets the elevation bounds from loaded point clouds.
|
|
40385
|
+
*/
|
|
40386
|
+
_getElevationBounds() {
|
|
40387
|
+
if (this._state.pointClouds.length === 0) {
|
|
40388
|
+
return { min: 0, max: 100 };
|
|
40389
|
+
}
|
|
40390
|
+
let minZ = Infinity;
|
|
40391
|
+
let maxZ = -Infinity;
|
|
40392
|
+
for (const pc of this._state.pointClouds) {
|
|
40393
|
+
minZ = Math.min(minZ, pc.bounds.minZ);
|
|
40394
|
+
maxZ = Math.max(maxZ, pc.bounds.maxZ);
|
|
40395
|
+
}
|
|
40396
|
+
return { min: minZ, max: maxZ };
|
|
40397
|
+
}
|
|
40398
|
+
/**
|
|
40399
|
+
* Gets the intensity bounds from loaded point clouds.
|
|
40400
|
+
* Intensity values are typically normalized to 0-1 range.
|
|
40401
|
+
*/
|
|
40402
|
+
_getIntensityBounds() {
|
|
40403
|
+
return { min: 0, max: 1 };
|
|
40404
|
+
}
|
|
39216
40405
|
/**
|
|
39217
40406
|
* Creates the main container element for the control.
|
|
39218
40407
|
*
|
|
@@ -39273,6 +40462,8 @@ class LidarControl {
|
|
|
39273
40462
|
onPointSizeChange: (size) => this.setPointSize(size),
|
|
39274
40463
|
onOpacityChange: (opacity) => this.setOpacity(opacity),
|
|
39275
40464
|
onColorSchemeChange: (scheme) => this.setColorScheme(scheme),
|
|
40465
|
+
onColormapChange: (colormap) => this.setColormap(colormap),
|
|
40466
|
+
onColorRangeChange: (config) => this.setColorRange(config),
|
|
39276
40467
|
onUsePercentileChange: (usePercentile) => this.setUsePercentile(usePercentile),
|
|
39277
40468
|
onElevationRangeChange: (range) => {
|
|
39278
40469
|
if (range) {
|
|
@@ -39775,6 +40966,9 @@ class LidarLayerAdapter {
|
|
|
39775
40966
|
this._changeCallbacks = [];
|
|
39776
40967
|
}
|
|
39777
40968
|
}
|
|
40969
|
+
exports.COLORMAPS = COLORMAPS;
|
|
40970
|
+
exports.COLORMAP_LABELS = COLORMAP_LABELS;
|
|
40971
|
+
exports.COLORMAP_NAMES = COLORMAP_NAMES;
|
|
39778
40972
|
exports.ColorSchemeProcessor = ColorSchemeProcessor;
|
|
39779
40973
|
exports.CopcStreamingLoader = CopcStreamingLoader;
|
|
39780
40974
|
exports.DeckOverlay = DeckOverlay;
|
|
@@ -39792,6 +40986,7 @@ exports.formatNumber = formatNumber;
|
|
|
39792
40986
|
exports.formatNumericValue = formatNumericValue;
|
|
39793
40987
|
exports.generateId = generateId;
|
|
39794
40988
|
exports.getClassificationName = getClassificationName;
|
|
40989
|
+
exports.getColormap = getColormap;
|
|
39795
40990
|
exports.getFilename = getFilename;
|
|
39796
40991
|
exports.throttle = throttle;
|
|
39797
|
-
//# sourceMappingURL=LidarLayerAdapter-
|
|
40992
|
+
//# sourceMappingURL=LidarLayerAdapter-eh59KEMT.cjs.map
|