@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/cjs/index.js
CHANGED
|
@@ -6720,11 +6720,37 @@ class CogTiles {
|
|
|
6720
6720
|
bounds = [0, 0, 0, 0];
|
|
6721
6721
|
geo = new GeoImage();
|
|
6722
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
|
+
}
|
|
6723
6747
|
// Cache GeoTIFFImage Promises by index to prevent redundant HTTP requests from geotiff 3.0.4+ eager loading
|
|
6724
6748
|
// Stores Promises (not resolved values) so concurrent requests share the same getImage() call
|
|
6725
6749
|
imageCache = new Map();
|
|
6726
6750
|
// Store initialization promise to prevent concurrent duplicate initializations
|
|
6727
6751
|
initializePromise;
|
|
6752
|
+
// Track the last successfully initialized URL to detect URL changes
|
|
6753
|
+
lastInitializedUrl;
|
|
6728
6754
|
constructor(options) {
|
|
6729
6755
|
this.options = { ...CogTilesGeoImageOptionsDefaults, ...options };
|
|
6730
6756
|
}
|
|
@@ -6732,6 +6758,10 @@ class CogTiles {
|
|
|
6732
6758
|
// Return existing initialization promise if already in progress (prevents concurrent duplicates)
|
|
6733
6759
|
if (this.initializePromise)
|
|
6734
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
|
+
}
|
|
6735
6765
|
if (this.cog)
|
|
6736
6766
|
return;
|
|
6737
6767
|
this.initializePromise = (async () => {
|
|
@@ -6761,6 +6791,8 @@ class CogTiles {
|
|
|
6761
6791
|
}
|
|
6762
6792
|
this.zoomRange = this.calculateZoomRange(this.tileSize, image.getResolution()[0], await this.cog.getImageCount());
|
|
6763
6793
|
this.bounds = this.calculateBoundsAsLatLon(image.getBoundingBox());
|
|
6794
|
+
// Mark initialization complete for this URL (used to detect URL changes)
|
|
6795
|
+
this.lastInitializedUrl = url;
|
|
6764
6796
|
}
|
|
6765
6797
|
catch (error) {
|
|
6766
6798
|
// Reset initialization promise on error so retry can be attempted
|
|
@@ -6866,12 +6898,20 @@ class CogTiles {
|
|
|
6866
6898
|
return this.cogZoomLookup.length - 1;
|
|
6867
6899
|
// For zoom levels within the available range, find the exact or closest matching index.
|
|
6868
6900
|
const exactMatchIndex = this.cogZoomLookup.indexOf(zoom);
|
|
6869
|
-
if (exactMatchIndex
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
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
|
+
}
|
|
6873
6913
|
}
|
|
6874
|
-
return
|
|
6914
|
+
return closestIndex;
|
|
6875
6915
|
}
|
|
6876
6916
|
async getTileFromImage(tileX, tileY, zoom, fetchSize, signal) {
|
|
6877
6917
|
// Create a fresh local AbortController for this specific fetch.
|
|
@@ -6885,6 +6925,12 @@ class CogTiles {
|
|
|
6885
6925
|
signal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
6886
6926
|
}
|
|
6887
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
|
+
}
|
|
6888
6934
|
try {
|
|
6889
6935
|
const imageIndex = this.getImageIndexForZoomLevel(zoom);
|
|
6890
6936
|
// Cache Promises to share in-flight requests across concurrent tiles at the same overview
|
|
@@ -6975,8 +7021,7 @@ class CogTiles {
|
|
|
6975
7021
|
tileBuffer[destRowOffset + destCol] = validRasterData[band][srcRowOffset + col];
|
|
6976
7022
|
}
|
|
6977
7023
|
else {
|
|
6978
|
-
|
|
6979
|
-
console.log(`error in assigning data to tile buffer: destRow ${destRow}, destCol ${destCol}, FETCH_SIZE ${FETCH_SIZE}`);
|
|
7024
|
+
console.error(`[CogTiles] tile buffer bounds exceeded: destRow ${destRow}, destCol ${destCol}, FETCH_SIZE ${FETCH_SIZE}`);
|
|
6980
7025
|
}
|
|
6981
7026
|
}
|
|
6982
7027
|
}
|
|
@@ -6984,11 +7029,15 @@ class CogTiles {
|
|
|
6984
7029
|
validImageData[i * numChannels + band] = tileBuffer[i];
|
|
6985
7030
|
}
|
|
6986
7031
|
}
|
|
7032
|
+
// Mark raster as cached after successful fetch
|
|
7033
|
+
this.setCachedRaster(cacheKey, validImageData); // for partial overlap
|
|
6987
7034
|
return [validImageData];
|
|
6988
7035
|
}
|
|
6989
7036
|
// Case B: Perfect Match (Optimization)
|
|
6990
7037
|
// If the read window is exactly 256x256 and aligned, we can read directly interleaved.
|
|
6991
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
|
|
6992
7041
|
return [tileData];
|
|
6993
7042
|
}
|
|
6994
7043
|
catch (error) {
|
|
@@ -7049,6 +7098,10 @@ class CogTiles {
|
|
|
7049
7098
|
tileWidth = this.tileSize;
|
|
7050
7099
|
tileHeight = this.tileSize;
|
|
7051
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
|
+
}
|
|
7052
7105
|
return this.geo.getMap({
|
|
7053
7106
|
rasters,
|
|
7054
7107
|
width: tileWidth,
|