@gisatcz/deckgl-geolib 2.5.0-dev.2 → 2.5.0-dev.4

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
@@ -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));
@@ -4874,6 +4913,7 @@ const DefaultGeoImageOptions = {
4874
4913
  useChannelIndex: null,
4875
4914
  noDataValue: undefined,
4876
4915
  multiplier: 1.0,
4916
+ verticalExaggeration: 1.0,
4877
4917
  numOfChannels: undefined,
4878
4918
  planarConfig: undefined,
4879
4919
  // --- Mesh generation (terrain only) ---
@@ -5548,25 +5588,31 @@ function addSkirt(attributes, triangles, skirtHeight, outsideIndices) {
5548
5588
  * @returns {number[][]} - outside edges data
5549
5589
  */
5550
5590
  function getOutsideEdgesFromTriangles(triangles) {
5551
- const edges = [];
5552
- for (let i = 0; i < triangles.length; i += 3) {
5553
- edges.push([triangles[i], triangles[i + 1]]);
5554
- edges.push([triangles[i + 1], triangles[i + 2]]);
5555
- edges.push([triangles[i + 2], triangles[i]]);
5556
- }
5557
- edges.sort((a, b) => Math.min(...a) - Math.min(...b) || Math.max(...a) - Math.max(...b));
5558
- const outsideEdges = [];
5559
- let index = 0;
5560
- while (index < edges.length) {
5561
- if (edges[index][0] === edges[index + 1]?.[1] && edges[index][1] === edges[index + 1]?.[0]) {
5562
- index += 2;
5591
+ // Use BigInt keys to avoid collisions for large meshes.
5592
+ // Pack min and max into a single BigInt key: (min << 32) | max
5593
+ const edgeMap = new Map();
5594
+ const processEdge = (a, b) => {
5595
+ const min = Math.min(a, b);
5596
+ const max = Math.max(a, b);
5597
+ // Pack indices into a single BigInt key to avoid string allocation and collisions
5598
+ const key = (BigInt(min) << 32n) | BigInt(max);
5599
+ if (edgeMap.has(key)) {
5600
+ edgeMap.delete(key); // Interior edge, remove
5563
5601
  }
5564
5602
  else {
5565
- outsideEdges.push(edges[index]);
5566
- index++;
5603
+ edgeMap.set(key, [a, b]);
5567
5604
  }
5605
+ };
5606
+ for (let i = 0; i < triangles.length; i += 3) {
5607
+ const v0 = triangles[i];
5608
+ const v1 = triangles[i + 1];
5609
+ const v2 = triangles[i + 2];
5610
+ // Process each edge inline — no temporary array allocation per triangle
5611
+ processEdge(v0, v1);
5612
+ processEdge(v1, v2);
5613
+ processEdge(v2, v0);
5568
5614
  }
5569
- return outsideEdges;
5615
+ return Array.from(edgeMap.values());
5570
5616
  }
5571
5617
  /**
5572
5618
  * Get geometry edges that located on a border of the mesh
@@ -6284,7 +6330,7 @@ class TerrainGenerator {
6284
6330
  const meshWidth = isKernel ? 257 : width;
6285
6331
  const meshHeight = isKernel ? 257 : height;
6286
6332
  // 2. Tesselate (Generate Mesh)
6287
- const { terrainSkirtHeight } = options;
6333
+ const { terrainSkirtHeight, verticalExaggeration = 1.0 } = options;
6288
6334
  let mesh;
6289
6335
  switch (options.tesselator) {
6290
6336
  case 'martini':
@@ -6300,13 +6346,17 @@ class TerrainGenerator {
6300
6346
  }
6301
6347
  const { vertices } = mesh;
6302
6348
  let { triangles } = mesh;
6303
- let attributes = this.getMeshAttributes(vertices, meshTerrain, meshWidth, meshHeight, input.bounds);
6349
+ let attributes = this.getMeshAttributes(vertices, meshTerrain, meshWidth, meshHeight, input.bounds, verticalExaggeration);
6304
6350
  // Compute bounding box before adding skirt so that z values are not skewed
6305
6351
  const boundingBox = schema.getMeshBoundingBox(attributes);
6306
6352
  if (terrainSkirtHeight) {
6307
- const { attributes: newAttributes, triangles: newTriangles } = addSkirt(attributes, triangles, terrainSkirtHeight);
6308
- attributes = newAttributes;
6309
- triangles = newTriangles;
6353
+ const scaledSkirtHeight = terrainSkirtHeight * verticalExaggeration;
6354
+ // Skip skirt generation if scaled height is zero (e.g., verticalExaggeration = 0)
6355
+ if (scaledSkirtHeight > 0) {
6356
+ const { attributes: newAttributes, triangles: newTriangles } = addSkirt(attributes, triangles, scaledSkirtHeight);
6357
+ attributes = newAttributes;
6358
+ triangles = newTriangles;
6359
+ }
6310
6360
  }
6311
6361
  const map = {
6312
6362
  // Data return by this loader implementation
@@ -6502,10 +6552,10 @@ class TerrainGenerator {
6502
6552
  const vertices = coords;
6503
6553
  return { vertices, triangles };
6504
6554
  }
6505
- static getMeshAttributes(vertices, terrain, width, height, bounds) {
6555
+ static getMeshAttributes(vertices, terrain, width, height, bounds, verticalExaggeration = 1.0) {
6506
6556
  const gridSize = width === 257 ? 257 : width + 1;
6507
6557
  const numOfVerticies = vertices.length / 2;
6508
- // vec3. x, y in pixels, z in meters
6558
+ // vec3. x, y in pixels, z in meters (scaled by verticalExaggeration)
6509
6559
  const positions = new Float32Array(numOfVerticies * 3);
6510
6560
  // vec2. 1 to 1 relationship with position. represents the uv on the texture image. 0,0 to 1,1.
6511
6561
  const texCoords = new Float32Array(numOfVerticies * 2);
@@ -6522,7 +6572,7 @@ class TerrainGenerator {
6522
6572
  const pixelIdx = y * gridSize + x;
6523
6573
  positions[3 * i] = x * xScale + minX;
6524
6574
  positions[3 * i + 1] = -y * yScale + maxY;
6525
- positions[3 * i + 2] = terrain[pixelIdx];
6575
+ positions[3 * i + 2] = terrain[pixelIdx] * verticalExaggeration;
6526
6576
  texCoords[2 * i] = x / effectiveWidth;
6527
6577
  texCoords[2 * i + 1] = y / effectiveHeight;
6528
6578
  }
@@ -6823,113 +6873,137 @@ class CogTiles {
6823
6873
  }
6824
6874
  return exactMatchIndex;
6825
6875
  }
6826
- async getTileFromImage(tileX, tileY, zoom, fetchSize) {
6827
- const imageIndex = this.getImageIndexForZoomLevel(zoom);
6828
- // Cache Promises to share in-flight requests across concurrent tiles at the same overview
6829
- let imagePromise = this.imageCache.get(imageIndex);
6830
- if (!imagePromise) {
6831
- imagePromise = this.cog.getImage(imageIndex);
6832
- this.imageCache.set(imageIndex, imagePromise);
6833
- }
6834
- const targetImage = await imagePromise;
6835
- // --- STEP 1: CALCULATE BOUNDS IN METERS ---
6836
- // 2. Get COG Metadata (image = COG)
6837
- const imageResolution = this.cogResolutionLookup[imageIndex];
6838
- const imageHeight = targetImage.getHeight();
6839
- const imageWidth = targetImage.getWidth();
6840
- const [imgOriginX, imgOriginY] = this.cogOrigin;
6841
- // 3. Define Web Mercator Constants
6842
- // We use the class property tileSize (usually 256) as the ground truth for grid calculations
6843
- const TILE_SIZE = this.tileSize;
6844
- const ORIGIN_X = webMercatorOrigin[0];
6845
- const ORIGIN_Y = webMercatorOrigin[1];
6846
- // 4. Calculate Tile BBox in World Meters
6847
- // This defines where the map expects the tile to be physically located
6848
- const tileGridResolution = (EARTH_CIRCUMFERENCE / TILE_SIZE) / (2 ** zoom);
6849
- const tileMinXMeters = ORIGIN_X + (tileX * TILE_SIZE * tileGridResolution);
6850
- const tileMaxYMeters = ORIGIN_Y - (tileY * TILE_SIZE * tileGridResolution);
6851
- // Note: We don't strictly need MaxX/MinY meters for the start calculation,
6852
- // but they are useful if debugging the full meter footprint.
6853
- // --- STEP 2: CONVERT TO PIXEL COORDINATES ---
6854
- // 5. Calculate precise floating-point start position relative to the image
6855
- const windowMinX = (tileMinXMeters - imgOriginX) / imageResolution;
6856
- const windowMinY = (imgOriginY - tileMaxYMeters) / imageResolution;
6857
- // 6. Snap to Integer Grid (The "Force 256" Fix)
6858
- // We round the start position to align with the nearest pixel.
6859
- const FETCH_SIZE = fetchSize || TILE_SIZE; // Default to 256 if not provided
6860
- const startX = Math.round(windowMinX);
6861
- const startY = Math.round(windowMinY);
6862
- const endX = startX + FETCH_SIZE;
6863
- const endY = startY + FETCH_SIZE;
6864
- // --- STEP 3: CALCULATE INTERSECTION ---
6865
- // 7. Clamp the read window to the actual image dimensions
6866
- // This defines the "Safe" area we can actually read from the file.
6867
- const validReadX = Math.max(0, startX);
6868
- const validReadY = Math.max(0, startY);
6869
- const validReadMaxX = Math.min(imageWidth, endX);
6870
- const validReadMaxY = Math.min(imageHeight, endY);
6871
- const readWidth = validReadMaxX - validReadX;
6872
- const readHeight = validReadMaxY - validReadY;
6873
- // CHECK: If no overlap, return empty
6874
- if (readWidth <= 0 || readHeight <= 0) {
6875
- return [this.createEmptyTile(FETCH_SIZE)];
6876
- }
6877
- // 8. Calculate Offsets (Padding)
6878
- // "missingLeft" is how many blank pixels we need to insert before the image data starts.
6879
- // Logic: If we wanted to read from -50 (startX), but clamped to 0 (validReadX),
6880
- // we are missing the first 50 pixels.
6881
- const missingLeft = validReadX - startX;
6882
- const missingTop = validReadY - startY;
6883
- const window = [validReadX, validReadY, validReadMaxX, validReadMaxY];
6884
- // --- STEP 4: READ AND COMPOSITE ---
6885
- // Case A: Partial Overlap (Padding or Cropping required)
6886
- // If the tile is hanging off the edge, we need to manually reconstruct it.
6887
- // We strictly compare against FETCH_SIZE because that is our target buffer dimension.
6888
- if (missingLeft > 0 || missingTop > 0 || readWidth < FETCH_SIZE || readHeight < FETCH_SIZE) {
6889
- const numChannels = this.options.numOfChannels || 1;
6890
- // Initialize with a TypedArray of the full target size and correct data type
6891
- const validImageData = this.createTileBuffer(this.options.format || 'Float32', FETCH_SIZE, numChannels);
6892
- if (this.options.noDataValue !== undefined) {
6893
- validImageData.fill(this.options.noDataValue);
6894
- }
6895
- // 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
6896
- const validRasterData = await targetImage.readRasters({ window });
6897
- // Place the valid pixel data into the tile buffer.
6898
- for (let band = 0; band < validRasterData.length; band += 1) {
6899
- // We must reset the buffer for each band, otherwise data from previous band persists in padding areas
6900
- const tileBuffer = this.createTileBuffer(this.options.format || 'Float32', FETCH_SIZE);
6876
+ async getTileFromImage(tileX, tileY, zoom, fetchSize, signal) {
6877
+ // Create a fresh local AbortController for this specific fetch.
6878
+ // We do NOT pass `signal` directly to readRasters because deck.gl may reuse tile
6879
+ // objects whose signal is already aborted (same tile re-requested after viewport change).
6880
+ // An already-aborted signal passed to geotiff.js immediately cancels the fetch,
6881
+ // leaving the tile permanently empty. Instead, we only forward cancellation when
6882
+ // the signal fires WHILE the request is actually in flight.
6883
+ const controller = new AbortController();
6884
+ if (signal && !signal.aborted) {
6885
+ signal.addEventListener('abort', () => controller.abort(), { once: true });
6886
+ }
6887
+ const localSignal = controller.signal;
6888
+ try {
6889
+ const imageIndex = this.getImageIndexForZoomLevel(zoom);
6890
+ // Cache Promises to share in-flight requests across concurrent tiles at the same overview
6891
+ let imagePromise = this.imageCache.get(imageIndex);
6892
+ if (!imagePromise) {
6893
+ imagePromise = this.cog.getImage(imageIndex);
6894
+ this.imageCache.set(imageIndex, imagePromise);
6895
+ }
6896
+ const targetImage = await imagePromise;
6897
+ // --- STEP 1: CALCULATE BOUNDS IN METERS ---
6898
+ // 2. Get COG Metadata (image = COG)
6899
+ const imageResolution = this.cogResolutionLookup[imageIndex];
6900
+ const imageHeight = targetImage.getHeight();
6901
+ const imageWidth = targetImage.getWidth();
6902
+ const [imgOriginX, imgOriginY] = this.cogOrigin;
6903
+ // 3. Define Web Mercator Constants
6904
+ // We use the class property tileSize (usually 256) as the ground truth for grid calculations
6905
+ const TILE_SIZE = this.tileSize;
6906
+ const ORIGIN_X = webMercatorOrigin[0];
6907
+ const ORIGIN_Y = webMercatorOrigin[1];
6908
+ // 4. Calculate Tile BBox in World Meters
6909
+ // This defines where the map expects the tile to be physically located
6910
+ const tileGridResolution = (EARTH_CIRCUMFERENCE / TILE_SIZE) / (2 ** zoom);
6911
+ const tileMinXMeters = ORIGIN_X + (tileX * TILE_SIZE * tileGridResolution);
6912
+ const tileMaxYMeters = ORIGIN_Y - (tileY * TILE_SIZE * tileGridResolution);
6913
+ // Note: We don't strictly need MaxX/MinY meters for the start calculation,
6914
+ // but they are useful if debugging the full meter footprint.
6915
+ // --- STEP 2: CONVERT TO PIXEL COORDINATES ---
6916
+ // 5. Calculate precise floating-point start position relative to the image
6917
+ const windowMinX = (tileMinXMeters - imgOriginX) / imageResolution;
6918
+ const windowMinY = (imgOriginY - tileMaxYMeters) / imageResolution;
6919
+ // 6. Snap to Integer Grid (The "Force 256" Fix)
6920
+ // We round the start position to align with the nearest pixel.
6921
+ const FETCH_SIZE = fetchSize || TILE_SIZE; // Default to 256 if not provided
6922
+ const startX = Math.round(windowMinX);
6923
+ const startY = Math.round(windowMinY);
6924
+ const endX = startX + FETCH_SIZE;
6925
+ const endY = startY + FETCH_SIZE;
6926
+ // --- STEP 3: CALCULATE INTERSECTION ---
6927
+ // 7. Clamp the read window to the actual image dimensions
6928
+ // This defines the "Safe" area we can actually read from the file.
6929
+ const validReadX = Math.max(0, startX);
6930
+ const validReadY = Math.max(0, startY);
6931
+ const validReadMaxX = Math.min(imageWidth, endX);
6932
+ const validReadMaxY = Math.min(imageHeight, endY);
6933
+ const readWidth = validReadMaxX - validReadX;
6934
+ const readHeight = validReadMaxY - validReadY;
6935
+ // CHECK: If no overlap, return empty
6936
+ if (readWidth <= 0 || readHeight <= 0) {
6937
+ return [this.createEmptyTile(FETCH_SIZE)];
6938
+ }
6939
+ // 8. Calculate Offsets (Padding)
6940
+ // "missingLeft" is how many blank pixels we need to insert before the image data starts.
6941
+ // Logic: If we wanted to read from -50 (startX), but clamped to 0 (validReadX),
6942
+ // we are missing the first 50 pixels.
6943
+ const missingLeft = validReadX - startX;
6944
+ const missingTop = validReadY - startY;
6945
+ const window = [validReadX, validReadY, validReadMaxX, validReadMaxY];
6946
+ // --- STEP 4: READ AND COMPOSITE ---
6947
+ // Case A: Partial Overlap (Padding or Cropping required)
6948
+ // If the tile is hanging off the edge, we need to manually reconstruct it.
6949
+ // We strictly compare against FETCH_SIZE because that is our target buffer dimension.
6950
+ if (missingLeft > 0 || missingTop > 0 || readWidth < FETCH_SIZE || readHeight < FETCH_SIZE) {
6951
+ const numChannels = this.options.numOfChannels || 1;
6952
+ // Initialize with a TypedArray of the full target size and correct data type
6953
+ const validImageData = this.createTileBuffer(this.options.format || 'Float32', FETCH_SIZE, numChannels);
6901
6954
  if (this.options.noDataValue !== undefined) {
6902
- tileBuffer.fill(this.options.noDataValue);
6955
+ validImageData.fill(this.options.noDataValue);
6903
6956
  }
6904
- for (let row = 0; row < readHeight; row += 1) {
6905
- const destRow = missingTop + row;
6906
- const destRowOffset = destRow * FETCH_SIZE;
6907
- const srcRowOffset = row * validRasterData.width;
6908
- for (let col = 0; col < readWidth; col += 1) {
6909
- // Compute the destination position in the tile buffer.
6910
- const destCol = missingLeft + col;
6911
- // Bounds Check: Ensure we don't write outside the allocated buffer
6912
- if (destRow < FETCH_SIZE && destCol < FETCH_SIZE) {
6913
- tileBuffer[destRowOffset + destCol] = validRasterData[band][srcRowOffset + col];
6914
- }
6915
- else {
6916
- /* eslint-disable no-console */
6917
- console.log(`error in assigning data to tile buffer: destRow ${destRow}, destCol ${destCol}, FETCH_SIZE ${FETCH_SIZE}`);
6957
+ // 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
6958
+ const validRasterData = await targetImage.readRasters({ window, signal: localSignal });
6959
+ // Place the valid pixel data into the tile buffer.
6960
+ for (let band = 0; band < validRasterData.length; band += 1) {
6961
+ // We must reset the buffer for each band, otherwise data from previous band persists in padding areas
6962
+ const tileBuffer = this.createTileBuffer(this.options.format || 'Float32', FETCH_SIZE);
6963
+ if (this.options.noDataValue !== undefined) {
6964
+ tileBuffer.fill(this.options.noDataValue);
6965
+ }
6966
+ for (let row = 0; row < readHeight; row += 1) {
6967
+ const destRow = missingTop + row;
6968
+ const destRowOffset = destRow * FETCH_SIZE;
6969
+ const srcRowOffset = row * validRasterData.width;
6970
+ for (let col = 0; col < readWidth; col += 1) {
6971
+ // Compute the destination position in the tile buffer.
6972
+ const destCol = missingLeft + col;
6973
+ // Bounds Check: Ensure we don't write outside the allocated buffer
6974
+ if (destRow < FETCH_SIZE && destCol < FETCH_SIZE) {
6975
+ tileBuffer[destRowOffset + destCol] = validRasterData[band][srcRowOffset + col];
6976
+ }
6977
+ else {
6978
+ /* eslint-disable no-console */
6979
+ console.log(`error in assigning data to tile buffer: destRow ${destRow}, destCol ${destCol}, FETCH_SIZE ${FETCH_SIZE}`);
6980
+ }
6918
6981
  }
6919
6982
  }
6983
+ for (let i = 0; i < tileBuffer.length; i += 1) {
6984
+ validImageData[i * numChannels + band] = tileBuffer[i];
6985
+ }
6920
6986
  }
6921
- for (let i = 0; i < tileBuffer.length; i += 1) {
6922
- validImageData[i * numChannels + band] = tileBuffer[i];
6923
- }
6987
+ return [validImageData];
6988
+ }
6989
+ // Case B: Perfect Match (Optimization)
6990
+ // If the read window is exactly 256x256 and aligned, we can read directly interleaved.
6991
+ const tileData = await targetImage.readRasters({ window, interleave: true, signal: localSignal });
6992
+ return [tileData];
6993
+ }
6994
+ catch (error) {
6995
+ // If the signal was aborted (or geotiff.js threw AggregateError wrapping an abort),
6996
+ // re-throw as a standard AbortError so deck.gl handles tile cancellation gracefully
6997
+ // and suppressGlobalAbortErrors() can suppress the unhandled rejection noise.
6998
+ const isAbortRelated = localSignal.aborted
6999
+ || (error instanceof AggregateError && error.errors?.some((e) => e?.name === 'AbortError' || e?.message?.includes('aborted') || e?.message?.includes('abort')))
7000
+ || (error instanceof DOMException && error.name === 'AbortError')
7001
+ || (error instanceof Error && error.message === 'Request was aborted');
7002
+ if (isAbortRelated) {
7003
+ throw new DOMException('Tile request aborted', 'AbortError');
6924
7004
  }
6925
- return [validImageData];
7005
+ throw error;
6926
7006
  }
6927
- // Case B: Perfect Match (Optimization)
6928
- // If the read window is exactly 256x256 and aligned, we can read directly interleaved.
6929
- // console.log("Perfect aligned read");
6930
- const tileData = await targetImage.readRasters({ window, interleave: true });
6931
- // console.log(`data that starts at the left top corner of the tile ${tileX}, ${tileY}`);
6932
- return [tileData];
6933
7007
  }
6934
7008
  /**
6935
7009
  * Creates a blank tile buffer filled with the "No Data" value.
@@ -6945,7 +7019,7 @@ class CogTiles {
6945
7019
  }
6946
7020
  return tileData;
6947
7021
  }
6948
- async getTile(x, y, z, bounds, meshMaxError) {
7022
+ async getTile(x, y, z, bounds, meshMaxError, signal) {
6949
7023
  let requiredSize = this.tileSize; // Default 256 for image/bitmap
6950
7024
  if (this.options.type === 'terrain') {
6951
7025
  const isKernel = this.options.useSlope || this.options.useHillshade || this.options.useSwissRelief;
@@ -6955,7 +7029,7 @@ class CogTiles {
6955
7029
  // Bitmap layer with relief glaze mode needs kernel padding for slope/hillshade computation
6956
7030
  requiredSize = this.tileSize + 2; // 258 for kernel
6957
7031
  }
6958
- const tileData = await this.getTileFromImage(x, y, z, requiredSize);
7032
+ const tileData = await this.getTileFromImage(x, y, z, requiredSize, signal);
6959
7033
  // Compute true ground cell size in meters from tile indices.
6960
7034
  // Tile y in slippy-map convention → center latitude → Web Mercator distortion correction.
6961
7035
  const latRad = Math.atan(Math.sinh(Math.PI * (1 - 2 * (y + 0.5) / Math.pow(2, z))));
@@ -6979,9 +7053,9 @@ class CogTiles {
6979
7053
  rasters,
6980
7054
  width: tileWidth,
6981
7055
  height: tileHeight,
6982
- bounds,
7056
+ bounds: bounds ?? [0, 0, 0, 0],
6983
7057
  cellSizeMeters,
6984
- }, this.options, meshMaxError);
7058
+ }, this.options, meshMaxError ?? 4.0);
6985
7059
  }
6986
7060
  /**
6987
7061
  * Determines the data type (e.g., "Int32", "Float64") of a GeoTIFF image
@@ -7191,20 +7265,11 @@ class CogBitmapLayer extends core.CompositeLayer {
7191
7265
  }
7192
7266
  }
7193
7267
  async getTiledBitmapData(tile) {
7194
- try {
7195
- // TODO - pass signal to getTile
7196
- // abort request if signal is aborted
7197
- const tileData = await this.state.bitmapCogTiles.getTile(tile.index.x, tile.index.y, tile.index.z);
7198
- if (tileData && !this.props.pickable) {
7199
- tileData.raw = null;
7200
- }
7201
- return tileData;
7202
- }
7203
- catch (error) {
7204
- // Log the error and rethrow so TileLayer can surface the failure via onTileError
7205
- core.log.warn(`Failed to load bitmap tile at ${tile.index.z}/${tile.index.x}/${tile.index.y}:`, error)();
7206
- throw error;
7268
+ const resolvedTileData = await this.state.bitmapCogTiles.getTile(tile.index.x, tile.index.y, tile.index.z, undefined, undefined, tile.signal);
7269
+ if (resolvedTileData && !this.props.pickable) {
7270
+ resolvedTileData.raw = null;
7207
7271
  }
7272
+ return resolvedTileData;
7208
7273
  }
7209
7274
  renderSubLayers(props) {
7210
7275
  const SubLayerClass = this.getSubLayerClass('image', layers.BitmapLayer);
@@ -7338,7 +7403,6 @@ function urlTemplateToUpdateTrigger(template) {
7338
7403
  }
7339
7404
  // TODO remove elevationDecoder
7340
7405
  // TODO use meshMaxError
7341
- // TODO - pass signal to getTile
7342
7406
  /** Render mesh surfaces from height map images. */
7343
7407
  class CogTerrainLayer extends core.CompositeLayer {
7344
7408
  static defaultProps = defaultProps;
@@ -7437,13 +7501,11 @@ class CogTerrainLayer extends core.CompositeLayer {
7437
7501
  topRight = [bbox.right, bbox.top];
7438
7502
  }
7439
7503
  const bounds = [bottomLeft[0], bottomLeft[1], topRight[0], topRight[1]];
7440
- // TODO - pass signal to getTile
7441
- // abort request if signal is aborted
7442
- const terrain = await this.state.terrainCogTiles.getTile(tile.index.x, tile.index.y, tile.index.z, bounds, this.props.meshMaxError);
7443
- if (terrain && !this.props.pickable) {
7444
- terrain.raw = null;
7504
+ const resolvedTerrain = await this.state.terrainCogTiles.getTile(tile.index.x, tile.index.y, tile.index.z, bounds, this.props.meshMaxError, tile.signal);
7505
+ if (resolvedTerrain && !this.props.pickable) {
7506
+ resolvedTerrain.raw = null;
7445
7507
  }
7446
- return Promise.all([terrain, null]);
7508
+ return Promise.all([resolvedTerrain, null]);
7447
7509
  }
7448
7510
  renderSubLayers(props) {
7449
7511
  const SubLayerClass = this.getSubLayerClass('mesh', meshLayers.SimpleMeshLayer);
@@ -7552,6 +7614,10 @@ class CogTerrainLayer extends core.CompositeLayer {
7552
7614
  }
7553
7615
  }
7554
7616
 
7617
+ // src/index.ts
7618
+ // Initialize global error suppression for deck.gl AbortErrors
7619
+ suppressGlobalAbortErrors();
7620
+
7555
7621
  class RawDecoder extends BaseDecoder {
7556
7622
  /** @param {ArrayBuffer} buffer */
7557
7623
  decodeBlock(buffer) {
@@ -7560,8 +7626,8 @@ class RawDecoder extends BaseDecoder {
7560
7626
  }
7561
7627
 
7562
7628
  var raw = /*#__PURE__*/Object.freeze({
7563
- __proto__: null,
7564
- default: RawDecoder
7629
+ __proto__: null,
7630
+ default: RawDecoder
7565
7631
  });
7566
7632
 
7567
7633
  const MIN_BITS = 9;
@@ -7719,8 +7785,8 @@ class LZWDecoder extends BaseDecoder {
7719
7785
  }
7720
7786
 
7721
7787
  var lzw = /*#__PURE__*/Object.freeze({
7722
- __proto__: null,
7723
- default: LZWDecoder
7788
+ __proto__: null,
7789
+ default: LZWDecoder
7724
7790
  });
7725
7791
 
7726
7792
  /* -*- tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
@@ -8783,8 +8849,8 @@ class JpegDecoder extends BaseDecoder {
8783
8849
  }
8784
8850
 
8785
8851
  var jpeg = /*#__PURE__*/Object.freeze({
8786
- __proto__: null,
8787
- default: JpegDecoder
8852
+ __proto__: null,
8853
+ default: JpegDecoder
8788
8854
  });
8789
8855
 
8790
8856
  /*============================================================================*/
@@ -12030,8 +12096,8 @@ class DeflateDecoder extends BaseDecoder {
12030
12096
  }
12031
12097
 
12032
12098
  var deflate = /*#__PURE__*/Object.freeze({
12033
- __proto__: null,
12034
- default: DeflateDecoder
12099
+ __proto__: null,
12100
+ default: DeflateDecoder
12035
12101
  });
12036
12102
 
12037
12103
  class PackbitsDecoder extends BaseDecoder {
@@ -12061,8 +12127,8 @@ class PackbitsDecoder extends BaseDecoder {
12061
12127
  }
12062
12128
 
12063
12129
  var packbits = /*#__PURE__*/Object.freeze({
12064
- __proto__: null,
12065
- default: PackbitsDecoder
12130
+ __proto__: null,
12131
+ default: PackbitsDecoder
12066
12132
  });
12067
12133
 
12068
12134
  function getDefaultExportFromCjs (x) {
@@ -14516,9 +14582,9 @@ class LercDecoder extends BaseDecoder {
14516
14582
  }
14517
14583
 
14518
14584
  var lerc = /*#__PURE__*/Object.freeze({
14519
- __proto__: null,
14520
- default: LercDecoder,
14521
- zstd: zstd$2
14585
+ __proto__: null,
14586
+ default: LercDecoder,
14587
+ zstd: zstd$2
14522
14588
  });
14523
14589
 
14524
14590
  /**
@@ -14692,9 +14758,9 @@ class ZstdDecoder extends BaseDecoder {
14692
14758
  }
14693
14759
 
14694
14760
  var zstd$1 = /*#__PURE__*/Object.freeze({
14695
- __proto__: null,
14696
- default: ZstdDecoder,
14697
- zstd: zstd
14761
+ __proto__: null,
14762
+ default: ZstdDecoder,
14763
+ zstd: zstd
14698
14764
  });
14699
14765
 
14700
14766
  /**
@@ -14757,12 +14823,13 @@ class WebImageDecoder extends BaseDecoder {
14757
14823
  }
14758
14824
 
14759
14825
  var webimage = /*#__PURE__*/Object.freeze({
14760
- __proto__: null,
14761
- default: WebImageDecoder
14826
+ __proto__: null,
14827
+ default: WebImageDecoder
14762
14828
  });
14763
14829
 
14764
14830
  exports.CogBitmapLayer = CogBitmapLayer;
14765
14831
  exports.CogTerrainLayer = CogTerrainLayer;
14766
14832
  exports.CogTiles = CogTiles;
14767
14833
  exports.GeoImage = GeoImage;
14834
+ exports.suppressGlobalAbortErrors = suppressGlobalAbortErrors;
14768
14835
  //# sourceMappingURL=index.js.map