@gisatcz/deckgl-geolib 2.4.0-dev.5 → 2.4.1-dev.1

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 CHANGED
@@ -4868,6 +4868,7 @@ async function fromArrayBuffer(arrayBuffer, signal) {
4868
4868
  const DefaultGeoImageOptions = {
4869
4869
  // --- Shared / Data ---
4870
4870
  type: 'image',
4871
+ blockSize: 65536,
4871
4872
  format: undefined,
4872
4873
  useChannel: null,
4873
4874
  useChannelIndex: null,
@@ -6257,36 +6258,57 @@ class CogTiles {
6257
6258
  bounds = [0, 0, 0, 0];
6258
6259
  geo = new GeoImage();
6259
6260
  options;
6261
+ // Cache GeoTIFFImage Promises by index to prevent redundant HTTP requests from geotiff 3.0.4+ eager loading
6262
+ // Stores Promises (not resolved values) so concurrent requests share the same getImage() call
6263
+ imageCache = new Map();
6264
+ // Store initialization promise to prevent concurrent duplicate initializations
6265
+ initializePromise;
6260
6266
  constructor(options) {
6261
6267
  this.options = { ...CogTilesGeoImageOptionsDefaults, ...options };
6262
6268
  }
6263
6269
  async initializeCog(url) {
6270
+ // Return existing initialization promise if already in progress (prevents concurrent duplicates)
6271
+ if (this.initializePromise)
6272
+ return this.initializePromise;
6264
6273
  if (this.cog)
6265
- return; // Prevent re-initialization on the same instance
6266
- try {
6267
- this.cog = await fromUrl(url);
6268
- const image = await this.cog.getImage();
6269
- const fileDirectory = image.fileDirectory;
6270
- this.cogOrigin = image.getOrigin();
6271
- this.options.noDataValue ??= await this.getNoDataValue(image);
6272
- this.options.format ??= await this.getDataTypeFromTags(fileDirectory);
6273
- this.options.numOfChannels = fileDirectory.getValue('SamplesPerPixel');
6274
- this.options.planarConfig = fileDirectory.getValue('PlanarConfiguration');
6275
- [this.cogZoomLookup, this.cogResolutionLookup] = await this.buildCogZoomResolutionLookup(this.cog);
6276
- this.tileSize = image.getTileWidth();
6277
- // 1. Validation: Ensure the image is tiled
6278
- if (!this.tileSize || !image.getTileHeight()) {
6279
- throw new Error('GeoTIFF Error: The provided image is not tiled. '
6280
- + 'Please use "rio cogeo create --web-optimized" to fix this.');
6281
- }
6282
- this.zoomRange = this.calculateZoomRange(this.tileSize, image.getResolution()[0], await this.cog.getImageCount());
6283
- this.bounds = this.calculateBoundsAsLatLon(image.getBoundingBox());
6284
- }
6285
- catch (error) {
6286
- /* eslint-disable no-console */
6287
- console.error(`[CogTiles] Failed to initialize COG from ${url}:`, error);
6288
- throw error;
6289
- }
6274
+ return;
6275
+ this.initializePromise = (async () => {
6276
+ try {
6277
+ // fromUrl's type declaration only exposes RemoteSourceOptions, but the implementation
6278
+ // also accepts BlockedSourceOptions (forwarded to makeFetchSource internally).
6279
+ // Explicitly enabling BlockedSource restores the block-level LRU cache that was
6280
+ // accidentally active in geotiff 3.0.3 (due to a null vs undefined bug there).
6281
+ // blockSize defaults to 65536 (64KB) and can be tuned via GeoImageOptions.
6282
+ const blockSize = this.options.blockSize ?? 65536;
6283
+ this.cog = await fromUrl(url, { blockSize });
6284
+ const imagePromise = this.cog.getImage();
6285
+ this.imageCache.set(0, imagePromise); // Cache base image (index 0) to avoid re-fetching during getTileFromImage
6286
+ const image = await imagePromise;
6287
+ const fileDirectory = image.fileDirectory;
6288
+ this.cogOrigin = image.getOrigin();
6289
+ this.options.noDataValue ??= await this.getNoDataValue(image);
6290
+ this.options.format ??= await this.getDataTypeFromTags(fileDirectory);
6291
+ this.options.numOfChannels = fileDirectory.getValue('SamplesPerPixel');
6292
+ this.options.planarConfig = fileDirectory.getValue('PlanarConfiguration');
6293
+ [this.cogZoomLookup, this.cogResolutionLookup] = await this.buildCogZoomResolutionLookup(this.cog);
6294
+ this.tileSize = image.getTileWidth();
6295
+ // 1. Validation: Ensure the image is tiled
6296
+ if (!this.tileSize || !image.getTileHeight()) {
6297
+ throw new Error('GeoTIFF Error: The provided image is not tiled. '
6298
+ + 'Please use "rio cogeo create --web-optimized" to fix this.');
6299
+ }
6300
+ this.zoomRange = this.calculateZoomRange(this.tileSize, image.getResolution()[0], await this.cog.getImageCount());
6301
+ this.bounds = this.calculateBoundsAsLatLon(image.getBoundingBox());
6302
+ }
6303
+ catch (error) {
6304
+ // Reset initialization promise on error so retry can be attempted
6305
+ this.initializePromise = undefined;
6306
+ /* eslint-disable no-console */
6307
+ console.error(`[CogTiles] Failed to initialize COG from ${url}:`, error);
6308
+ throw error;
6309
+ }
6310
+ })();
6311
+ return this.initializePromise;
6290
6312
  }
6291
6313
  getZoomRange() {
6292
6314
  return this.zoomRange;
@@ -6391,7 +6413,13 @@ class CogTiles {
6391
6413
  }
6392
6414
  async getTileFromImage(tileX, tileY, zoom, fetchSize) {
6393
6415
  const imageIndex = this.getImageIndexForZoomLevel(zoom);
6394
- const targetImage = await this.cog.getImage(imageIndex);
6416
+ // Cache Promises to share in-flight requests across concurrent tiles at the same overview
6417
+ let imagePromise = this.imageCache.get(imageIndex);
6418
+ if (!imagePromise) {
6419
+ imagePromise = this.cog.getImage(imageIndex);
6420
+ this.imageCache.set(imageIndex, imagePromise);
6421
+ }
6422
+ const targetImage = await imagePromise;
6395
6423
  // --- STEP 1: CALCULATE BOUNDS IN METERS ---
6396
6424
  // 2. Get COG Metadata (image = COG)
6397
6425
  const imageResolution = this.cogResolutionLookup[imageIndex];
@@ -6882,11 +6910,21 @@ class CogTerrainLayer extends core.CompositeLayer {
6882
6910
  terrainUrl = '';
6883
6911
  async initializeState(context) {
6884
6912
  super.initializeState(context);
6913
+ const terrainCogTiles = this.props.cogTiles || new CogTiles(this.props.terrainOptions);
6885
6914
  this.setState({
6886
- terrainCogTiles: this.props.cogTiles || new CogTiles(this.props.terrainOptions),
6915
+ terrainCogTiles,
6887
6916
  initialized: false,
6888
6917
  });
6889
- await this.init(this.terrainUrl);
6918
+ // Only initialize if not already done (e.g., provided cogTiles instance may be pre-initialized)
6919
+ if (!terrainCogTiles.cog) {
6920
+ await this.init();
6921
+ }
6922
+ else {
6923
+ // CogTiles already initialized; just extract zoom range and mark ready
6924
+ const zoomRange = terrainCogTiles.getZoomRange();
6925
+ const [minZoom, maxZoom] = zoomRange;
6926
+ this.setState({ initialized: true, minZoom, maxZoom });
6927
+ }
6890
6928
  }
6891
6929
  async init() {
6892
6930
  await this.state.terrainCogTiles.initializeCog(this.props.elevationData);