@gisatcz/deckgl-geolib 2.5.0-dev.4 → 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 +60 -7
- 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 +60 -7
- 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 +7 -2
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -6718,11 +6718,37 @@ class CogTiles {
|
|
|
6718
6718
|
bounds = [0, 0, 0, 0];
|
|
6719
6719
|
geo = new GeoImage();
|
|
6720
6720
|
options;
|
|
6721
|
+
// Cache fetched rasters in an LRU-style Map keyed by `${z}/${x}/${y}/${fetchSize}`,
|
|
6722
|
+
// with each value holding the cached raster for that tile request.
|
|
6723
|
+
rasterCache = new Map();
|
|
6724
|
+
maxCacheSize = 256;
|
|
6725
|
+
// LRU cache helpers
|
|
6726
|
+
getCachedRaster(key) {
|
|
6727
|
+
const value = this.rasterCache.get(key);
|
|
6728
|
+
if (value !== undefined) {
|
|
6729
|
+
// Refresh order: delete and re-set
|
|
6730
|
+
this.rasterCache.delete(key);
|
|
6731
|
+
this.rasterCache.set(key, value);
|
|
6732
|
+
}
|
|
6733
|
+
return value;
|
|
6734
|
+
}
|
|
6735
|
+
setCachedRaster(key, value) {
|
|
6736
|
+
this.rasterCache.set(key, value);
|
|
6737
|
+
if (this.rasterCache.size > this.maxCacheSize) {
|
|
6738
|
+
// Evict oldest
|
|
6739
|
+
const oldestKey = this.rasterCache.keys().next().value;
|
|
6740
|
+
if (typeof oldestKey === 'string') {
|
|
6741
|
+
this.rasterCache.delete(oldestKey);
|
|
6742
|
+
}
|
|
6743
|
+
}
|
|
6744
|
+
}
|
|
6721
6745
|
// Cache GeoTIFFImage Promises by index to prevent redundant HTTP requests from geotiff 3.0.4+ eager loading
|
|
6722
6746
|
// Stores Promises (not resolved values) so concurrent requests share the same getImage() call
|
|
6723
6747
|
imageCache = new Map();
|
|
6724
6748
|
// Store initialization promise to prevent concurrent duplicate initializations
|
|
6725
6749
|
initializePromise;
|
|
6750
|
+
// Track the last successfully initialized URL to detect URL changes
|
|
6751
|
+
lastInitializedUrl;
|
|
6726
6752
|
constructor(options) {
|
|
6727
6753
|
this.options = { ...CogTilesGeoImageOptionsDefaults, ...options };
|
|
6728
6754
|
}
|
|
@@ -6730,6 +6756,10 @@ class CogTiles {
|
|
|
6730
6756
|
// Return existing initialization promise if already in progress (prevents concurrent duplicates)
|
|
6731
6757
|
if (this.initializePromise)
|
|
6732
6758
|
return this.initializePromise;
|
|
6759
|
+
// Clear cache only if URL changed (preserves cache on idempotent re-init)
|
|
6760
|
+
if (this.lastInitializedUrl !== url) {
|
|
6761
|
+
this.rasterCache.clear();
|
|
6762
|
+
}
|
|
6733
6763
|
if (this.cog)
|
|
6734
6764
|
return;
|
|
6735
6765
|
this.initializePromise = (async () => {
|
|
@@ -6759,6 +6789,8 @@ class CogTiles {
|
|
|
6759
6789
|
}
|
|
6760
6790
|
this.zoomRange = this.calculateZoomRange(this.tileSize, image.getResolution()[0], await this.cog.getImageCount());
|
|
6761
6791
|
this.bounds = this.calculateBoundsAsLatLon(image.getBoundingBox());
|
|
6792
|
+
// Mark initialization complete for this URL (used to detect URL changes)
|
|
6793
|
+
this.lastInitializedUrl = url;
|
|
6762
6794
|
}
|
|
6763
6795
|
catch (error) {
|
|
6764
6796
|
// Reset initialization promise on error so retry can be attempted
|
|
@@ -6864,12 +6896,20 @@ class CogTiles {
|
|
|
6864
6896
|
return this.cogZoomLookup.length - 1;
|
|
6865
6897
|
// For zoom levels within the available range, find the exact or closest matching index.
|
|
6866
6898
|
const exactMatchIndex = this.cogZoomLookup.indexOf(zoom);
|
|
6867
|
-
if (exactMatchIndex
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6899
|
+
if (exactMatchIndex !== -1) {
|
|
6900
|
+
return exactMatchIndex;
|
|
6901
|
+
}
|
|
6902
|
+
// No exact match: find the closest zoom level
|
|
6903
|
+
let closestIndex = 0;
|
|
6904
|
+
let minDistance = Math.abs(this.cogZoomLookup[0] - zoom);
|
|
6905
|
+
for (let i = 1; i < this.cogZoomLookup.length; i += 1) {
|
|
6906
|
+
const distance = Math.abs(this.cogZoomLookup[i] - zoom);
|
|
6907
|
+
if (distance < minDistance) {
|
|
6908
|
+
minDistance = distance;
|
|
6909
|
+
closestIndex = i;
|
|
6910
|
+
}
|
|
6871
6911
|
}
|
|
6872
|
-
return
|
|
6912
|
+
return closestIndex;
|
|
6873
6913
|
}
|
|
6874
6914
|
async getTileFromImage(tileX, tileY, zoom, fetchSize, signal) {
|
|
6875
6915
|
// Create a fresh local AbortController for this specific fetch.
|
|
@@ -6883,6 +6923,12 @@ class CogTiles {
|
|
|
6883
6923
|
signal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
6884
6924
|
}
|
|
6885
6925
|
const localSignal = controller.signal;
|
|
6926
|
+
// Check if raster is already cached
|
|
6927
|
+
const cacheKey = `${zoom}/${tileX}/${tileY}/${fetchSize ?? this.tileSize}`;
|
|
6928
|
+
const cachedRaster = this.getCachedRaster(cacheKey);
|
|
6929
|
+
if (cachedRaster) {
|
|
6930
|
+
return [cachedRaster];
|
|
6931
|
+
}
|
|
6886
6932
|
try {
|
|
6887
6933
|
const imageIndex = this.getImageIndexForZoomLevel(zoom);
|
|
6888
6934
|
// Cache Promises to share in-flight requests across concurrent tiles at the same overview
|
|
@@ -6973,8 +7019,7 @@ class CogTiles {
|
|
|
6973
7019
|
tileBuffer[destRowOffset + destCol] = validRasterData[band][srcRowOffset + col];
|
|
6974
7020
|
}
|
|
6975
7021
|
else {
|
|
6976
|
-
|
|
6977
|
-
console.log(`error in assigning data to tile buffer: destRow ${destRow}, destCol ${destCol}, FETCH_SIZE ${FETCH_SIZE}`);
|
|
7022
|
+
console.error(`[CogTiles] tile buffer bounds exceeded: destRow ${destRow}, destCol ${destCol}, FETCH_SIZE ${FETCH_SIZE}`);
|
|
6978
7023
|
}
|
|
6979
7024
|
}
|
|
6980
7025
|
}
|
|
@@ -6982,11 +7027,15 @@ class CogTiles {
|
|
|
6982
7027
|
validImageData[i * numChannels + band] = tileBuffer[i];
|
|
6983
7028
|
}
|
|
6984
7029
|
}
|
|
7030
|
+
// Mark raster as cached after successful fetch
|
|
7031
|
+
this.setCachedRaster(cacheKey, validImageData); // for partial overlap
|
|
6985
7032
|
return [validImageData];
|
|
6986
7033
|
}
|
|
6987
7034
|
// Case B: Perfect Match (Optimization)
|
|
6988
7035
|
// If the read window is exactly 256x256 and aligned, we can read directly interleaved.
|
|
6989
7036
|
const tileData = await targetImage.readRasters({ window, interleave: true, signal: localSignal });
|
|
7037
|
+
// Mark raster as cached after successful fetch
|
|
7038
|
+
this.setCachedRaster(cacheKey, tileData); // for perfect match
|
|
6990
7039
|
return [tileData];
|
|
6991
7040
|
}
|
|
6992
7041
|
catch (error) {
|
|
@@ -7047,6 +7096,10 @@ class CogTiles {
|
|
|
7047
7096
|
tileWidth = this.tileSize;
|
|
7048
7097
|
tileHeight = this.tileSize;
|
|
7049
7098
|
}
|
|
7099
|
+
// Guard against abort race condition: if signal aborted after cache hit but before expensive geo.getMap()
|
|
7100
|
+
if (signal?.aborted) {
|
|
7101
|
+
return null;
|
|
7102
|
+
}
|
|
7050
7103
|
return this.geo.getMap({
|
|
7051
7104
|
rasters,
|
|
7052
7105
|
width: tileWidth,
|