@gisatcz/deckgl-geolib 2.5.0-dev.3 → 2.5.0-dev.5
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/cjs/index.js +264 -155
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/index.min.js +2 -2
- package/dist/cjs/index.min.js.map +1 -1
- package/dist/esm/index.js +264 -156
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.min.js +2 -2
- package/dist/esm/index.min.js.map +1 -1
- package/dist/esm/types/core/CogTiles.d.ts +8 -3
- package/dist/esm/types/index.d.ts +1 -0
- package/dist/esm/types/utils/suppressAbortErrors.d.ts +23 -0
- package/package.json +1 -1
package/dist/cjs/index.js
CHANGED
|
@@ -9,6 +9,45 @@ var schema = require('@loaders.gl/schema');
|
|
|
9
9
|
var loaderUtils = require('@loaders.gl/loader-utils');
|
|
10
10
|
var meshLayers = require('@deck.gl/mesh-layers');
|
|
11
11
|
|
|
12
|
+
let isListenerAttached = false;
|
|
13
|
+
/**
|
|
14
|
+
* Suppresses unhandled AbortErrors from deck.gl tile cancellation.
|
|
15
|
+
*
|
|
16
|
+
* NOTE: The library's main entry point installs this handler automatically
|
|
17
|
+
* when the package is imported via its primary build (for example
|
|
18
|
+
* `import '@gisatcz/deckgl-geolib'`). This default prevents console spam during
|
|
19
|
+
* normal tile cancellation (pan/zoom) for the vast majority of consumers.
|
|
20
|
+
*
|
|
21
|
+
* If you need to control installation manually (for example when importing
|
|
22
|
+
* internals or for custom lifecycle control), import and call the exported
|
|
23
|
+
* function yourself:
|
|
24
|
+
*
|
|
25
|
+
* import { suppressGlobalAbortErrors } from '@gisatcz/deckgl-geolib';
|
|
26
|
+
* suppressGlobalAbortErrors();
|
|
27
|
+
*
|
|
28
|
+
* Warning: This suppresses ALL unhandled AbortErrors (including from your own
|
|
29
|
+
* code). If you need finer control, implement a custom `unhandledrejection`
|
|
30
|
+
* handler instead.
|
|
31
|
+
*
|
|
32
|
+
* The listener is attached only once and only in browser environments,
|
|
33
|
+
* making this function idempotent and safe to call multiple times.
|
|
34
|
+
*/
|
|
35
|
+
function suppressGlobalAbortErrors() {
|
|
36
|
+
// Ensure we are in a browser environment and haven't already attached the listener
|
|
37
|
+
if (typeof window !== 'undefined' && !isListenerAttached) {
|
|
38
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
39
|
+
// Suppress standard AbortErrors from tile cancellation and fetch aborts.
|
|
40
|
+
// These are expected during viewport changes and represent normal control flow,
|
|
41
|
+
// not application errors.
|
|
42
|
+
if (event.reason && event.reason.name === 'AbortError') {
|
|
43
|
+
// Prevent the browser from logging it to the console
|
|
44
|
+
event.preventDefault();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
isListenerAttached = true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
12
51
|
/* eslint-disable no-restricted-globals, no-restricted-syntax */
|
|
13
52
|
/* global SharedArrayBuffer */
|
|
14
53
|
|
|
@@ -2959,7 +2998,7 @@ class CustomAggregateError extends Error {
|
|
|
2959
2998
|
this.name = 'AggregateError';
|
|
2960
2999
|
}
|
|
2961
3000
|
}
|
|
2962
|
-
const AggregateError = CustomAggregateError;
|
|
3001
|
+
const AggregateError$1 = CustomAggregateError;
|
|
2963
3002
|
|
|
2964
3003
|
class Block {
|
|
2965
3004
|
/**
|
|
@@ -3090,7 +3129,7 @@ class BlockedSource extends BaseSource {
|
|
|
3090
3129
|
const blocks = allBlockIds.map((id) => this.blockCache.get(id) || this.evictedBlocks.get(id));
|
|
3091
3130
|
const failedBlocks = blocks.filter((i) => !i);
|
|
3092
3131
|
if (failedBlocks.length) {
|
|
3093
|
-
throw new AggregateError(failedBlocks, 'Request failed');
|
|
3132
|
+
throw new AggregateError$1(failedBlocks, 'Request failed');
|
|
3094
3133
|
}
|
|
3095
3134
|
// create a final Map, with all required blocks for this request to satisfy
|
|
3096
3135
|
const requiredBlocks = new Map(zip(allBlockIds, blocks));
|
|
@@ -5549,14 +5588,14 @@ function addSkirt(attributes, triangles, skirtHeight, outsideIndices) {
|
|
|
5549
5588
|
* @returns {number[][]} - outside edges data
|
|
5550
5589
|
*/
|
|
5551
5590
|
function getOutsideEdgesFromTriangles(triangles) {
|
|
5552
|
-
// Use
|
|
5553
|
-
//
|
|
5591
|
+
// Use BigInt keys to avoid collisions for large meshes.
|
|
5592
|
+
// Pack min and max into a single BigInt key: (min << 32) | max
|
|
5554
5593
|
const edgeMap = new Map();
|
|
5555
5594
|
const processEdge = (a, b) => {
|
|
5556
5595
|
const min = Math.min(a, b);
|
|
5557
5596
|
const max = Math.max(a, b);
|
|
5558
|
-
//
|
|
5559
|
-
const key = min
|
|
5597
|
+
// Pack indices into a single BigInt key to avoid string allocation and collisions
|
|
5598
|
+
const key = (BigInt(min) << 32n) | BigInt(max);
|
|
5560
5599
|
if (edgeMap.has(key)) {
|
|
5561
5600
|
edgeMap.delete(key); // Interior edge, remove
|
|
5562
5601
|
}
|
|
@@ -6681,11 +6720,37 @@ class CogTiles {
|
|
|
6681
6720
|
bounds = [0, 0, 0, 0];
|
|
6682
6721
|
geo = new GeoImage();
|
|
6683
6722
|
options;
|
|
6723
|
+
// Cache fetched rasters in an LRU-style Map keyed by `${z}/${x}/${y}/${fetchSize}`,
|
|
6724
|
+
// with each value holding the cached raster for that tile request.
|
|
6725
|
+
rasterCache = new Map();
|
|
6726
|
+
maxCacheSize = 256;
|
|
6727
|
+
// LRU cache helpers
|
|
6728
|
+
getCachedRaster(key) {
|
|
6729
|
+
const value = this.rasterCache.get(key);
|
|
6730
|
+
if (value !== undefined) {
|
|
6731
|
+
// Refresh order: delete and re-set
|
|
6732
|
+
this.rasterCache.delete(key);
|
|
6733
|
+
this.rasterCache.set(key, value);
|
|
6734
|
+
}
|
|
6735
|
+
return value;
|
|
6736
|
+
}
|
|
6737
|
+
setCachedRaster(key, value) {
|
|
6738
|
+
this.rasterCache.set(key, value);
|
|
6739
|
+
if (this.rasterCache.size > this.maxCacheSize) {
|
|
6740
|
+
// Evict oldest
|
|
6741
|
+
const oldestKey = this.rasterCache.keys().next().value;
|
|
6742
|
+
if (typeof oldestKey === 'string') {
|
|
6743
|
+
this.rasterCache.delete(oldestKey);
|
|
6744
|
+
}
|
|
6745
|
+
}
|
|
6746
|
+
}
|
|
6684
6747
|
// Cache GeoTIFFImage Promises by index to prevent redundant HTTP requests from geotiff 3.0.4+ eager loading
|
|
6685
6748
|
// Stores Promises (not resolved values) so concurrent requests share the same getImage() call
|
|
6686
6749
|
imageCache = new Map();
|
|
6687
6750
|
// Store initialization promise to prevent concurrent duplicate initializations
|
|
6688
6751
|
initializePromise;
|
|
6752
|
+
// Track the last successfully initialized URL to detect URL changes
|
|
6753
|
+
lastInitializedUrl;
|
|
6689
6754
|
constructor(options) {
|
|
6690
6755
|
this.options = { ...CogTilesGeoImageOptionsDefaults, ...options };
|
|
6691
6756
|
}
|
|
@@ -6693,6 +6758,10 @@ class CogTiles {
|
|
|
6693
6758
|
// Return existing initialization promise if already in progress (prevents concurrent duplicates)
|
|
6694
6759
|
if (this.initializePromise)
|
|
6695
6760
|
return this.initializePromise;
|
|
6761
|
+
// Clear cache only if URL changed (preserves cache on idempotent re-init)
|
|
6762
|
+
if (this.lastInitializedUrl !== url) {
|
|
6763
|
+
this.rasterCache.clear();
|
|
6764
|
+
}
|
|
6696
6765
|
if (this.cog)
|
|
6697
6766
|
return;
|
|
6698
6767
|
this.initializePromise = (async () => {
|
|
@@ -6722,6 +6791,8 @@ class CogTiles {
|
|
|
6722
6791
|
}
|
|
6723
6792
|
this.zoomRange = this.calculateZoomRange(this.tileSize, image.getResolution()[0], await this.cog.getImageCount());
|
|
6724
6793
|
this.bounds = this.calculateBoundsAsLatLon(image.getBoundingBox());
|
|
6794
|
+
// Mark initialization complete for this URL (used to detect URL changes)
|
|
6795
|
+
this.lastInitializedUrl = url;
|
|
6725
6796
|
}
|
|
6726
6797
|
catch (error) {
|
|
6727
6798
|
// Reset initialization promise on error so retry can be attempted
|
|
@@ -6827,120 +6898,161 @@ class CogTiles {
|
|
|
6827
6898
|
return this.cogZoomLookup.length - 1;
|
|
6828
6899
|
// For zoom levels within the available range, find the exact or closest matching index.
|
|
6829
6900
|
const exactMatchIndex = this.cogZoomLookup.indexOf(zoom);
|
|
6830
|
-
if (exactMatchIndex
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
//
|
|
6847
|
-
//
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
const
|
|
6857
|
-
//
|
|
6858
|
-
|
|
6859
|
-
const
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
const
|
|
6901
|
-
//
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
const
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6901
|
+
if (exactMatchIndex !== -1) {
|
|
6902
|
+
return exactMatchIndex;
|
|
6903
|
+
}
|
|
6904
|
+
// No exact match: find the closest zoom level
|
|
6905
|
+
let closestIndex = 0;
|
|
6906
|
+
let minDistance = Math.abs(this.cogZoomLookup[0] - zoom);
|
|
6907
|
+
for (let i = 1; i < this.cogZoomLookup.length; i += 1) {
|
|
6908
|
+
const distance = Math.abs(this.cogZoomLookup[i] - zoom);
|
|
6909
|
+
if (distance < minDistance) {
|
|
6910
|
+
minDistance = distance;
|
|
6911
|
+
closestIndex = i;
|
|
6912
|
+
}
|
|
6913
|
+
}
|
|
6914
|
+
return closestIndex;
|
|
6915
|
+
}
|
|
6916
|
+
async getTileFromImage(tileX, tileY, zoom, fetchSize, signal) {
|
|
6917
|
+
// Create a fresh local AbortController for this specific fetch.
|
|
6918
|
+
// We do NOT pass `signal` directly to readRasters because deck.gl may reuse tile
|
|
6919
|
+
// objects whose signal is already aborted (same tile re-requested after viewport change).
|
|
6920
|
+
// An already-aborted signal passed to geotiff.js immediately cancels the fetch,
|
|
6921
|
+
// leaving the tile permanently empty. Instead, we only forward cancellation when
|
|
6922
|
+
// the signal fires WHILE the request is actually in flight.
|
|
6923
|
+
const controller = new AbortController();
|
|
6924
|
+
if (signal && !signal.aborted) {
|
|
6925
|
+
signal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
6926
|
+
}
|
|
6927
|
+
const localSignal = controller.signal;
|
|
6928
|
+
// Check if raster is already cached
|
|
6929
|
+
const cacheKey = `${zoom}/${tileX}/${tileY}/${fetchSize ?? this.tileSize}`;
|
|
6930
|
+
const cachedRaster = this.getCachedRaster(cacheKey);
|
|
6931
|
+
if (cachedRaster) {
|
|
6932
|
+
return [cachedRaster];
|
|
6933
|
+
}
|
|
6934
|
+
try {
|
|
6935
|
+
const imageIndex = this.getImageIndexForZoomLevel(zoom);
|
|
6936
|
+
// Cache Promises to share in-flight requests across concurrent tiles at the same overview
|
|
6937
|
+
let imagePromise = this.imageCache.get(imageIndex);
|
|
6938
|
+
if (!imagePromise) {
|
|
6939
|
+
imagePromise = this.cog.getImage(imageIndex);
|
|
6940
|
+
this.imageCache.set(imageIndex, imagePromise);
|
|
6941
|
+
}
|
|
6942
|
+
const targetImage = await imagePromise;
|
|
6943
|
+
// --- STEP 1: CALCULATE BOUNDS IN METERS ---
|
|
6944
|
+
// 2. Get COG Metadata (image = COG)
|
|
6945
|
+
const imageResolution = this.cogResolutionLookup[imageIndex];
|
|
6946
|
+
const imageHeight = targetImage.getHeight();
|
|
6947
|
+
const imageWidth = targetImage.getWidth();
|
|
6948
|
+
const [imgOriginX, imgOriginY] = this.cogOrigin;
|
|
6949
|
+
// 3. Define Web Mercator Constants
|
|
6950
|
+
// We use the class property tileSize (usually 256) as the ground truth for grid calculations
|
|
6951
|
+
const TILE_SIZE = this.tileSize;
|
|
6952
|
+
const ORIGIN_X = webMercatorOrigin[0];
|
|
6953
|
+
const ORIGIN_Y = webMercatorOrigin[1];
|
|
6954
|
+
// 4. Calculate Tile BBox in World Meters
|
|
6955
|
+
// This defines where the map expects the tile to be physically located
|
|
6956
|
+
const tileGridResolution = (EARTH_CIRCUMFERENCE / TILE_SIZE) / (2 ** zoom);
|
|
6957
|
+
const tileMinXMeters = ORIGIN_X + (tileX * TILE_SIZE * tileGridResolution);
|
|
6958
|
+
const tileMaxYMeters = ORIGIN_Y - (tileY * TILE_SIZE * tileGridResolution);
|
|
6959
|
+
// Note: We don't strictly need MaxX/MinY meters for the start calculation,
|
|
6960
|
+
// but they are useful if debugging the full meter footprint.
|
|
6961
|
+
// --- STEP 2: CONVERT TO PIXEL COORDINATES ---
|
|
6962
|
+
// 5. Calculate precise floating-point start position relative to the image
|
|
6963
|
+
const windowMinX = (tileMinXMeters - imgOriginX) / imageResolution;
|
|
6964
|
+
const windowMinY = (imgOriginY - tileMaxYMeters) / imageResolution;
|
|
6965
|
+
// 6. Snap to Integer Grid (The "Force 256" Fix)
|
|
6966
|
+
// We round the start position to align with the nearest pixel.
|
|
6967
|
+
const FETCH_SIZE = fetchSize || TILE_SIZE; // Default to 256 if not provided
|
|
6968
|
+
const startX = Math.round(windowMinX);
|
|
6969
|
+
const startY = Math.round(windowMinY);
|
|
6970
|
+
const endX = startX + FETCH_SIZE;
|
|
6971
|
+
const endY = startY + FETCH_SIZE;
|
|
6972
|
+
// --- STEP 3: CALCULATE INTERSECTION ---
|
|
6973
|
+
// 7. Clamp the read window to the actual image dimensions
|
|
6974
|
+
// This defines the "Safe" area we can actually read from the file.
|
|
6975
|
+
const validReadX = Math.max(0, startX);
|
|
6976
|
+
const validReadY = Math.max(0, startY);
|
|
6977
|
+
const validReadMaxX = Math.min(imageWidth, endX);
|
|
6978
|
+
const validReadMaxY = Math.min(imageHeight, endY);
|
|
6979
|
+
const readWidth = validReadMaxX - validReadX;
|
|
6980
|
+
const readHeight = validReadMaxY - validReadY;
|
|
6981
|
+
// CHECK: If no overlap, return empty
|
|
6982
|
+
if (readWidth <= 0 || readHeight <= 0) {
|
|
6983
|
+
return [this.createEmptyTile(FETCH_SIZE)];
|
|
6984
|
+
}
|
|
6985
|
+
// 8. Calculate Offsets (Padding)
|
|
6986
|
+
// "missingLeft" is how many blank pixels we need to insert before the image data starts.
|
|
6987
|
+
// Logic: If we wanted to read from -50 (startX), but clamped to 0 (validReadX),
|
|
6988
|
+
// we are missing the first 50 pixels.
|
|
6989
|
+
const missingLeft = validReadX - startX;
|
|
6990
|
+
const missingTop = validReadY - startY;
|
|
6991
|
+
const window = [validReadX, validReadY, validReadMaxX, validReadMaxY];
|
|
6992
|
+
// --- STEP 4: READ AND COMPOSITE ---
|
|
6993
|
+
// Case A: Partial Overlap (Padding or Cropping required)
|
|
6994
|
+
// If the tile is hanging off the edge, we need to manually reconstruct it.
|
|
6995
|
+
// We strictly compare against FETCH_SIZE because that is our target buffer dimension.
|
|
6996
|
+
if (missingLeft > 0 || missingTop > 0 || readWidth < FETCH_SIZE || readHeight < FETCH_SIZE) {
|
|
6997
|
+
const numChannels = this.options.numOfChannels || 1;
|
|
6998
|
+
// Initialize with a TypedArray of the full target size and correct data type
|
|
6999
|
+
const validImageData = this.createTileBuffer(this.options.format || 'Float32', FETCH_SIZE, numChannels);
|
|
6912
7000
|
if (this.options.noDataValue !== undefined) {
|
|
6913
|
-
|
|
7001
|
+
validImageData.fill(this.options.noDataValue);
|
|
6914
7002
|
}
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
for
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
7003
|
+
// if the valid window is smaller than the tile size, it gets the image size width and height, thus validRasterData.width must be used as below
|
|
7004
|
+
const validRasterData = await targetImage.readRasters({ window, signal: localSignal });
|
|
7005
|
+
// Place the valid pixel data into the tile buffer.
|
|
7006
|
+
for (let band = 0; band < validRasterData.length; band += 1) {
|
|
7007
|
+
// We must reset the buffer for each band, otherwise data from previous band persists in padding areas
|
|
7008
|
+
const tileBuffer = this.createTileBuffer(this.options.format || 'Float32', FETCH_SIZE);
|
|
7009
|
+
if (this.options.noDataValue !== undefined) {
|
|
7010
|
+
tileBuffer.fill(this.options.noDataValue);
|
|
7011
|
+
}
|
|
7012
|
+
for (let row = 0; row < readHeight; row += 1) {
|
|
7013
|
+
const destRow = missingTop + row;
|
|
7014
|
+
const destRowOffset = destRow * FETCH_SIZE;
|
|
7015
|
+
const srcRowOffset = row * validRasterData.width;
|
|
7016
|
+
for (let col = 0; col < readWidth; col += 1) {
|
|
7017
|
+
// Compute the destination position in the tile buffer.
|
|
7018
|
+
const destCol = missingLeft + col;
|
|
7019
|
+
// Bounds Check: Ensure we don't write outside the allocated buffer
|
|
7020
|
+
if (destRow < FETCH_SIZE && destCol < FETCH_SIZE) {
|
|
7021
|
+
tileBuffer[destRowOffset + destCol] = validRasterData[band][srcRowOffset + col];
|
|
7022
|
+
}
|
|
7023
|
+
else {
|
|
7024
|
+
console.error(`[CogTiles] tile buffer bounds exceeded: destRow ${destRow}, destCol ${destCol}, FETCH_SIZE ${FETCH_SIZE}`);
|
|
7025
|
+
}
|
|
6929
7026
|
}
|
|
6930
7027
|
}
|
|
7028
|
+
for (let i = 0; i < tileBuffer.length; i += 1) {
|
|
7029
|
+
validImageData[i * numChannels + band] = tileBuffer[i];
|
|
7030
|
+
}
|
|
6931
7031
|
}
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
7032
|
+
// Mark raster as cached after successful fetch
|
|
7033
|
+
this.setCachedRaster(cacheKey, validImageData); // for partial overlap
|
|
7034
|
+
return [validImageData];
|
|
6935
7035
|
}
|
|
6936
|
-
|
|
7036
|
+
// Case B: Perfect Match (Optimization)
|
|
7037
|
+
// If the read window is exactly 256x256 and aligned, we can read directly interleaved.
|
|
7038
|
+
const tileData = await targetImage.readRasters({ window, interleave: true, signal: localSignal });
|
|
7039
|
+
// Mark raster as cached after successful fetch
|
|
7040
|
+
this.setCachedRaster(cacheKey, tileData); // for perfect match
|
|
7041
|
+
return [tileData];
|
|
7042
|
+
}
|
|
7043
|
+
catch (error) {
|
|
7044
|
+
// If the signal was aborted (or geotiff.js threw AggregateError wrapping an abort),
|
|
7045
|
+
// re-throw as a standard AbortError so deck.gl handles tile cancellation gracefully
|
|
7046
|
+
// and suppressGlobalAbortErrors() can suppress the unhandled rejection noise.
|
|
7047
|
+
const isAbortRelated = localSignal.aborted
|
|
7048
|
+
|| (error instanceof AggregateError && error.errors?.some((e) => e?.name === 'AbortError' || e?.message?.includes('aborted') || e?.message?.includes('abort')))
|
|
7049
|
+
|| (error instanceof DOMException && error.name === 'AbortError')
|
|
7050
|
+
|| (error instanceof Error && error.message === 'Request was aborted');
|
|
7051
|
+
if (isAbortRelated) {
|
|
7052
|
+
throw new DOMException('Tile request aborted', 'AbortError');
|
|
7053
|
+
}
|
|
7054
|
+
throw error;
|
|
6937
7055
|
}
|
|
6938
|
-
// Case B: Perfect Match (Optimization)
|
|
6939
|
-
// If the read window is exactly 256x256 and aligned, we can read directly interleaved.
|
|
6940
|
-
// console.log("Perfect aligned read");
|
|
6941
|
-
const tileData = await targetImage.readRasters({ window, interleave: true });
|
|
6942
|
-
// console.log(`data that starts at the left top corner of the tile ${tileX}, ${tileY}`);
|
|
6943
|
-
return [tileData];
|
|
6944
7056
|
}
|
|
6945
7057
|
/**
|
|
6946
7058
|
* Creates a blank tile buffer filled with the "No Data" value.
|
|
@@ -6956,7 +7068,7 @@ class CogTiles {
|
|
|
6956
7068
|
}
|
|
6957
7069
|
return tileData;
|
|
6958
7070
|
}
|
|
6959
|
-
async getTile(x, y, z, bounds, meshMaxError) {
|
|
7071
|
+
async getTile(x, y, z, bounds, meshMaxError, signal) {
|
|
6960
7072
|
let requiredSize = this.tileSize; // Default 256 for image/bitmap
|
|
6961
7073
|
if (this.options.type === 'terrain') {
|
|
6962
7074
|
const isKernel = this.options.useSlope || this.options.useHillshade || this.options.useSwissRelief;
|
|
@@ -6966,7 +7078,7 @@ class CogTiles {
|
|
|
6966
7078
|
// Bitmap layer with relief glaze mode needs kernel padding for slope/hillshade computation
|
|
6967
7079
|
requiredSize = this.tileSize + 2; // 258 for kernel
|
|
6968
7080
|
}
|
|
6969
|
-
const tileData = await this.getTileFromImage(x, y, z, requiredSize);
|
|
7081
|
+
const tileData = await this.getTileFromImage(x, y, z, requiredSize, signal);
|
|
6970
7082
|
// Compute true ground cell size in meters from tile indices.
|
|
6971
7083
|
// Tile y in slippy-map convention → center latitude → Web Mercator distortion correction.
|
|
6972
7084
|
const latRad = Math.atan(Math.sinh(Math.PI * (1 - 2 * (y + 0.5) / Math.pow(2, z))));
|
|
@@ -6986,13 +7098,17 @@ class CogTiles {
|
|
|
6986
7098
|
tileWidth = this.tileSize;
|
|
6987
7099
|
tileHeight = this.tileSize;
|
|
6988
7100
|
}
|
|
7101
|
+
// Guard against abort race condition: if signal aborted after cache hit but before expensive geo.getMap()
|
|
7102
|
+
if (signal?.aborted) {
|
|
7103
|
+
return null;
|
|
7104
|
+
}
|
|
6989
7105
|
return this.geo.getMap({
|
|
6990
7106
|
rasters,
|
|
6991
7107
|
width: tileWidth,
|
|
6992
7108
|
height: tileHeight,
|
|
6993
|
-
bounds,
|
|
7109
|
+
bounds: bounds ?? [0, 0, 0, 0],
|
|
6994
7110
|
cellSizeMeters,
|
|
6995
|
-
}, this.options, meshMaxError);
|
|
7111
|
+
}, this.options, meshMaxError ?? 4.0);
|
|
6996
7112
|
}
|
|
6997
7113
|
/**
|
|
6998
7114
|
* Determines the data type (e.g., "Int32", "Float64") of a GeoTIFF image
|
|
@@ -7202,20 +7318,11 @@ class CogBitmapLayer extends core.CompositeLayer {
|
|
|
7202
7318
|
}
|
|
7203
7319
|
}
|
|
7204
7320
|
async getTiledBitmapData(tile) {
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
const tileData = await this.state.bitmapCogTiles.getTile(tile.index.x, tile.index.y, tile.index.z);
|
|
7209
|
-
if (tileData && !this.props.pickable) {
|
|
7210
|
-
tileData.raw = null;
|
|
7211
|
-
}
|
|
7212
|
-
return tileData;
|
|
7213
|
-
}
|
|
7214
|
-
catch (error) {
|
|
7215
|
-
// Log the error and rethrow so TileLayer can surface the failure via onTileError
|
|
7216
|
-
core.log.warn(`Failed to load bitmap tile at ${tile.index.z}/${tile.index.x}/${tile.index.y}:`, error)();
|
|
7217
|
-
throw error;
|
|
7321
|
+
const resolvedTileData = await this.state.bitmapCogTiles.getTile(tile.index.x, tile.index.y, tile.index.z, undefined, undefined, tile.signal);
|
|
7322
|
+
if (resolvedTileData && !this.props.pickable) {
|
|
7323
|
+
resolvedTileData.raw = null;
|
|
7218
7324
|
}
|
|
7325
|
+
return resolvedTileData;
|
|
7219
7326
|
}
|
|
7220
7327
|
renderSubLayers(props) {
|
|
7221
7328
|
const SubLayerClass = this.getSubLayerClass('image', layers.BitmapLayer);
|
|
@@ -7349,7 +7456,6 @@ function urlTemplateToUpdateTrigger(template) {
|
|
|
7349
7456
|
}
|
|
7350
7457
|
// TODO remove elevationDecoder
|
|
7351
7458
|
// TODO use meshMaxError
|
|
7352
|
-
// TODO - pass signal to getTile
|
|
7353
7459
|
/** Render mesh surfaces from height map images. */
|
|
7354
7460
|
class CogTerrainLayer extends core.CompositeLayer {
|
|
7355
7461
|
static defaultProps = defaultProps;
|
|
@@ -7448,13 +7554,11 @@ class CogTerrainLayer extends core.CompositeLayer {
|
|
|
7448
7554
|
topRight = [bbox.right, bbox.top];
|
|
7449
7555
|
}
|
|
7450
7556
|
const bounds = [bottomLeft[0], bottomLeft[1], topRight[0], topRight[1]];
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
if (terrain && !this.props.pickable) {
|
|
7455
|
-
terrain.raw = null;
|
|
7557
|
+
const resolvedTerrain = await this.state.terrainCogTiles.getTile(tile.index.x, tile.index.y, tile.index.z, bounds, this.props.meshMaxError, tile.signal);
|
|
7558
|
+
if (resolvedTerrain && !this.props.pickable) {
|
|
7559
|
+
resolvedTerrain.raw = null;
|
|
7456
7560
|
}
|
|
7457
|
-
return Promise.all([
|
|
7561
|
+
return Promise.all([resolvedTerrain, null]);
|
|
7458
7562
|
}
|
|
7459
7563
|
renderSubLayers(props) {
|
|
7460
7564
|
const SubLayerClass = this.getSubLayerClass('mesh', meshLayers.SimpleMeshLayer);
|
|
@@ -7563,6 +7667,10 @@ class CogTerrainLayer extends core.CompositeLayer {
|
|
|
7563
7667
|
}
|
|
7564
7668
|
}
|
|
7565
7669
|
|
|
7670
|
+
// src/index.ts
|
|
7671
|
+
// Initialize global error suppression for deck.gl AbortErrors
|
|
7672
|
+
suppressGlobalAbortErrors();
|
|
7673
|
+
|
|
7566
7674
|
class RawDecoder extends BaseDecoder {
|
|
7567
7675
|
/** @param {ArrayBuffer} buffer */
|
|
7568
7676
|
decodeBlock(buffer) {
|
|
@@ -7571,8 +7679,8 @@ class RawDecoder extends BaseDecoder {
|
|
|
7571
7679
|
}
|
|
7572
7680
|
|
|
7573
7681
|
var raw = /*#__PURE__*/Object.freeze({
|
|
7574
|
-
|
|
7575
|
-
|
|
7682
|
+
__proto__: null,
|
|
7683
|
+
default: RawDecoder
|
|
7576
7684
|
});
|
|
7577
7685
|
|
|
7578
7686
|
const MIN_BITS = 9;
|
|
@@ -7730,8 +7838,8 @@ class LZWDecoder extends BaseDecoder {
|
|
|
7730
7838
|
}
|
|
7731
7839
|
|
|
7732
7840
|
var lzw = /*#__PURE__*/Object.freeze({
|
|
7733
|
-
|
|
7734
|
-
|
|
7841
|
+
__proto__: null,
|
|
7842
|
+
default: LZWDecoder
|
|
7735
7843
|
});
|
|
7736
7844
|
|
|
7737
7845
|
/* -*- tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
|
@@ -8794,8 +8902,8 @@ class JpegDecoder extends BaseDecoder {
|
|
|
8794
8902
|
}
|
|
8795
8903
|
|
|
8796
8904
|
var jpeg = /*#__PURE__*/Object.freeze({
|
|
8797
|
-
|
|
8798
|
-
|
|
8905
|
+
__proto__: null,
|
|
8906
|
+
default: JpegDecoder
|
|
8799
8907
|
});
|
|
8800
8908
|
|
|
8801
8909
|
/*============================================================================*/
|
|
@@ -12041,8 +12149,8 @@ class DeflateDecoder extends BaseDecoder {
|
|
|
12041
12149
|
}
|
|
12042
12150
|
|
|
12043
12151
|
var deflate = /*#__PURE__*/Object.freeze({
|
|
12044
|
-
|
|
12045
|
-
|
|
12152
|
+
__proto__: null,
|
|
12153
|
+
default: DeflateDecoder
|
|
12046
12154
|
});
|
|
12047
12155
|
|
|
12048
12156
|
class PackbitsDecoder extends BaseDecoder {
|
|
@@ -12072,8 +12180,8 @@ class PackbitsDecoder extends BaseDecoder {
|
|
|
12072
12180
|
}
|
|
12073
12181
|
|
|
12074
12182
|
var packbits = /*#__PURE__*/Object.freeze({
|
|
12075
|
-
|
|
12076
|
-
|
|
12183
|
+
__proto__: null,
|
|
12184
|
+
default: PackbitsDecoder
|
|
12077
12185
|
});
|
|
12078
12186
|
|
|
12079
12187
|
function getDefaultExportFromCjs (x) {
|
|
@@ -14527,9 +14635,9 @@ class LercDecoder extends BaseDecoder {
|
|
|
14527
14635
|
}
|
|
14528
14636
|
|
|
14529
14637
|
var lerc = /*#__PURE__*/Object.freeze({
|
|
14530
|
-
|
|
14531
|
-
|
|
14532
|
-
|
|
14638
|
+
__proto__: null,
|
|
14639
|
+
default: LercDecoder,
|
|
14640
|
+
zstd: zstd$2
|
|
14533
14641
|
});
|
|
14534
14642
|
|
|
14535
14643
|
/**
|
|
@@ -14703,9 +14811,9 @@ class ZstdDecoder extends BaseDecoder {
|
|
|
14703
14811
|
}
|
|
14704
14812
|
|
|
14705
14813
|
var zstd$1 = /*#__PURE__*/Object.freeze({
|
|
14706
|
-
|
|
14707
|
-
|
|
14708
|
-
|
|
14814
|
+
__proto__: null,
|
|
14815
|
+
default: ZstdDecoder,
|
|
14816
|
+
zstd: zstd
|
|
14709
14817
|
});
|
|
14710
14818
|
|
|
14711
14819
|
/**
|
|
@@ -14768,12 +14876,13 @@ class WebImageDecoder extends BaseDecoder {
|
|
|
14768
14876
|
}
|
|
14769
14877
|
|
|
14770
14878
|
var webimage = /*#__PURE__*/Object.freeze({
|
|
14771
|
-
|
|
14772
|
-
|
|
14879
|
+
__proto__: null,
|
|
14880
|
+
default: WebImageDecoder
|
|
14773
14881
|
});
|
|
14774
14882
|
|
|
14775
14883
|
exports.CogBitmapLayer = CogBitmapLayer;
|
|
14776
14884
|
exports.CogTerrainLayer = CogTerrainLayer;
|
|
14777
14885
|
exports.CogTiles = CogTiles;
|
|
14778
14886
|
exports.GeoImage = GeoImage;
|
|
14887
|
+
exports.suppressGlobalAbortErrors = suppressGlobalAbortErrors;
|
|
14779
14888
|
//# sourceMappingURL=index.js.map
|