@gisatcz/deckgl-geolib 1.12.0-dev.3 → 1.12.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.
@@ -65,7 +65,7 @@ export default class CogBitmapLayer<ExtraPropsT extends {} = {}> extends Composi
65
65
  getTileData: any;
66
66
  renderSubLayers: any;
67
67
  updateTriggers: {
68
- getTileData: (boolean | GeoImageOptions | ClampToTerrainOptions)[];
68
+ getTileData: (number | boolean | ClampToTerrainOptions)[];
69
69
  };
70
70
  extent: any;
71
71
  tileSize: any;
@@ -83,10 +83,10 @@ declare class CogTiles {
83
83
  getDataTypeFromTags(image: any): string;
84
84
  /**
85
85
  * Extracts the noData value from a GeoTIFF.js image.
86
- * Returns the noData value as a number if available, otherwise undefined.
86
+ * Returns the noData value as a number (including NaN) if available, otherwise undefined.
87
87
  *
88
88
  * @param {GeoTIFFImage} image - The GeoTIFF.js image.
89
- * @returns {number|undefined} The noData value as a number, or undefined if not available.
89
+ * @returns {number|undefined} The noData value, possibly NaN, or undefined if not set or invalid.
90
90
  */
91
91
  getNoDataValue(image: any): number;
92
92
  /**
@@ -96,6 +96,25 @@ declare class CogTiles {
96
96
  * @returns {number} The number of channels in the image.
97
97
  */
98
98
  getNumberOfChannels(image: any): any;
99
+ /**
100
+ * Calculates the intersection between a tile bounding box and a COG bounding box,
101
+ * returning the intersection window in image pixel space (relative to COG offsets),
102
+ * along with how much blank space (nodata) appears on the left and top of the tile.
103
+ *
104
+ * @param {number[]} tileBbox - Tile bounding box: [minX, minY, maxX, maxY]
105
+ * @param {number[]} cogBbox - COG bounding box: [minX, minY, maxX, maxY]
106
+ * @param {number} offsetXPixel - X offset of the COG origin in pixel space
107
+ * @param {number} offsetYPixel - Y offset of the COG origin in pixel space
108
+ * @param {number} tileSize - Size of the tile in pixels (default: 256)
109
+ * @returns {[number, number, number[] | null, number, number]}
110
+ * An array containing:
111
+ * - width of the intersection
112
+ * - height of the intersection
113
+ * - pixel-space window: [startX, startY, endX, endY] or null if no overlap
114
+ * - missingLeft: padding pixels on the left
115
+ * - missingTop: padding pixels on the top
116
+ */
117
+ getIntersectionBBox(tileBbox: any, cogBbox: any, offsetXPixel?: number, offsetYPixel?: number, tileSize?: number): any[];
99
118
  /**
100
119
  * Retrieves the PlanarConfiguration value from a GeoTIFF image.
101
120
  *
package/dist/esm/index.js CHANGED
@@ -5290,6 +5290,8 @@ class GeoImage {
5290
5290
  return [minValue, maxValue];
5291
5291
  }
5292
5292
  getColorValue(dataArray, options, arrayLength, numOfChannels = 1) {
5293
+ // const rgb = chroma.random().rgb(); // [R, G, B]
5294
+ // const randomColor = [...rgb, 120];
5293
5295
  const colorScale = chroma.scale(options.colorScale).domain(options.colorScaleValueRange);
5294
5296
  // channel index is equal to channel number - 1
5295
5297
  let pixel = options.useChannelIndex === null ? 0 : options.useChannelIndex;
@@ -5309,6 +5311,7 @@ class GeoImage {
5309
5311
  }) : undefined;
5310
5312
  for (let i = 0; i < arrayLength; i += 4) {
5311
5313
  let pixelColor = options.nullColor;
5314
+ // let pixelColor = randomColor;
5312
5315
  // FIXME
5313
5316
  // eslint-disable-next-line max-len
5314
5317
  if ((!Number.isNaN(dataArray[pixel])) && (options.noDataValue === undefined || dataArray[pixel] !== options.noDataValue)) {
@@ -5606,36 +5609,30 @@ class CogTiles {
5606
5609
  // Convert map offsets into pixel offsets.
5607
5610
  const offsetXPixel = Math.floor(offsetXMap / tileResolution);
5608
5611
  const offsetYPixel = Math.floor(offsetYMap / tileResolution);
5609
- // Calculate the pixel boundaries for the tile.
5610
- const window = [
5611
- tileX * tileWidth - offsetXPixel,
5612
- tileY * tileHeight - offsetYPixel,
5613
- (tileX + 1) * tileWidth - offsetXPixel,
5614
- (tileY + 1) * tileHeight - offsetYPixel, // endY (exclusive)
5615
- ];
5616
- const [windowStartX, windowStartY, windowEndX, windowEndY] = window;
5617
5612
  const imageHeight = targetImage.getHeight();
5618
5613
  const imageWidth = targetImage.getWidth();
5619
- // Determine the effective (valid) window inside the image:
5620
- const effectiveStartX = Math.max(0, windowStartX);
5621
- const effectiveStartY = Math.max(0, windowStartY);
5622
- const effectiveEndX = windowEndX;
5623
- const effectiveEndY = windowEndY;
5624
- // Calculate how many pixels are missing from the left and top due to negative windowStart.
5625
- const missingLeft = Math.max(0, 0 - windowStartX);
5626
- const missingTop = Math.max(0, 0 - windowStartY);
5627
- // Read only the valid window from the image.
5628
- const validWindow = [effectiveStartX, effectiveStartY, effectiveEndX, effectiveEndY];
5614
+ // approach by comparing bboxes of tile and cog image
5615
+ const tilePixelBbox = [
5616
+ tileX * tileWidth,
5617
+ tileY * tileHeight,
5618
+ (tileX + 1) * tileWidth,
5619
+ (tileY + 1) * tileHeight,
5620
+ ];
5621
+ const cogPixelBBox = [
5622
+ offsetXPixel,
5623
+ offsetYPixel,
5624
+ offsetXPixel + imageWidth,
5625
+ offsetYPixel + imageHeight,
5626
+ ];
5627
+ const intersecion = this.getIntersectionBBox(tilePixelBbox, cogPixelBBox, offsetXPixel, offsetYPixel, tileWidth);
5628
+ const [validWidth, validHeight, window, missingLeft, missingTop] = intersecion;
5629
5629
  // Read the raster data for the tile window with shifted origin.
5630
- if (missingLeft > 0 || missingTop > 0) {
5630
+ if (missingLeft > 0 || missingTop > 0 || validWidth < tileWidth || validHeight < tileHeight) {
5631
5631
  // Prepare the final tile buffer and fill it with noDataValue.
5632
5632
  const tileBuffer = this.createTileBuffer(this.options.format, tileWidth);
5633
5633
  tileBuffer.fill(this.options.noDataValue);
5634
- // Calculate the width of the valid window.
5635
- const validWidth = Math.min(imageWidth, effectiveEndX - effectiveStartX);
5636
- const validHeight = Math.min(imageHeight, effectiveEndY - effectiveStartY);
5637
5634
  // if the valid window is smaller than tile size, it gets the image size width and height, thus validRasterData.width must be used as below
5638
- const validRasterData = yield targetImage.readRasters({ window: validWindow });
5635
+ const validRasterData = yield targetImage.readRasters({ window });
5639
5636
  // FOR MULTI-BAND - the result is one array with sequentially typed bands, firstly all data for the band 0, then for band 1
5640
5637
  // I think this is less practical then the commented solution above, but I do it so it works with the code in geoimage.ts in deck.gl-geoimage in function getColorValue.
5641
5638
  const validImageData = Array(validRasterData.length * validRasterData[0].length);
@@ -5723,24 +5720,33 @@ class CogTiles {
5723
5720
  }
5724
5721
  /**
5725
5722
  * Extracts the noData value from a GeoTIFF.js image.
5726
- * Returns the noData value as a number if available, otherwise undefined.
5723
+ * Returns the noData value as a number (including NaN) if available, otherwise undefined.
5727
5724
  *
5728
5725
  * @param {GeoTIFFImage} image - The GeoTIFF.js image.
5729
- * @returns {number|undefined} The noData value as a number, or undefined if not available.
5726
+ * @returns {number|undefined} The noData value, possibly NaN, or undefined if not set or invalid.
5730
5727
  */
5731
5728
  getNoDataValue(image) {
5732
- // Attempt to retrieve the noData value via the GDAL method.
5733
5729
  const noDataRaw = image.getGDALNoData();
5734
5730
  if (noDataRaw === undefined || noDataRaw === null) {
5735
- console.log('noDataValue is undefined or null,raster might be displayed incorrectly.');
5736
- // No noData value is defined
5731
+ console.warn('No noData value defined raster might be rendered incorrectly.');
5732
+ return undefined;
5733
+ }
5734
+ const cleaned = String(noDataRaw).replace(/\0/g, '').trim();
5735
+ if (cleaned === '') {
5736
+ console.warn('noData value is an empty string after cleanup.');
5737
+ return undefined;
5738
+ }
5739
+ const parsed = Number(cleaned);
5740
+ // Allow NaN if explicitly declared
5741
+ if (cleaned.toLowerCase() === 'nan') {
5742
+ return NaN;
5743
+ }
5744
+ // If not declared as "nan" and still parsed to NaN, it's an error
5745
+ if (Number.isNaN(parsed)) {
5746
+ console.warn(`Failed to parse numeric noData value: '${cleaned}'`);
5737
5747
  return undefined;
5738
5748
  }
5739
- // In geotiff.js, the noData value is typically returned as a string.
5740
- // Clean up the string by removing any null characters or extra whitespace.
5741
- const cleanedValue = String(noDataRaw).replace(/\0/g, '').trim();
5742
- const parsedValue = Number(cleanedValue);
5743
- return Number.isNaN(parsedValue) ? undefined : parsedValue;
5749
+ return parsed;
5744
5750
  }
5745
5751
  /**
5746
5752
  * Retrieves the number of channels (samples per pixel) in a GeoTIFF image.
@@ -5751,6 +5757,53 @@ class CogTiles {
5751
5757
  getNumberOfChannels(image) {
5752
5758
  return image.getSamplesPerPixel();
5753
5759
  }
5760
+ /**
5761
+ * Calculates the intersection between a tile bounding box and a COG bounding box,
5762
+ * returning the intersection window in image pixel space (relative to COG offsets),
5763
+ * along with how much blank space (nodata) appears on the left and top of the tile.
5764
+ *
5765
+ * @param {number[]} tileBbox - Tile bounding box: [minX, minY, maxX, maxY]
5766
+ * @param {number[]} cogBbox - COG bounding box: [minX, minY, maxX, maxY]
5767
+ * @param {number} offsetXPixel - X offset of the COG origin in pixel space
5768
+ * @param {number} offsetYPixel - Y offset of the COG origin in pixel space
5769
+ * @param {number} tileSize - Size of the tile in pixels (default: 256)
5770
+ * @returns {[number, number, number[] | null, number, number]}
5771
+ * An array containing:
5772
+ * - width of the intersection
5773
+ * - height of the intersection
5774
+ * - pixel-space window: [startX, startY, endX, endY] or null if no overlap
5775
+ * - missingLeft: padding pixels on the left
5776
+ * - missingTop: padding pixels on the top
5777
+ */
5778
+ getIntersectionBBox(tileBbox, cogBbox, offsetXPixel = 0, offsetYPixel = 0, tileSize = 256) {
5779
+ const interLeft = Math.max(tileBbox[0], cogBbox[0]);
5780
+ const interTop = Math.max(tileBbox[1], cogBbox[1]);
5781
+ const interRight = Math.min(tileBbox[2], cogBbox[2]);
5782
+ const interBottom = Math.min(tileBbox[3], cogBbox[3]);
5783
+ const width = Math.max(0, interRight - interLeft);
5784
+ const height = Math.max(0, interBottom - interTop);
5785
+ let window = null;
5786
+ let missingLeft = 0;
5787
+ let missingTop = 0;
5788
+ if (width > 0 && height > 0) {
5789
+ window = [
5790
+ interLeft - offsetXPixel,
5791
+ interTop - offsetYPixel,
5792
+ interRight - offsetXPixel,
5793
+ interBottom - offsetYPixel,
5794
+ ];
5795
+ // Padding from the tile origin to valid data start
5796
+ missingLeft = interLeft - tileBbox[0];
5797
+ missingTop = interTop - tileBbox[1];
5798
+ }
5799
+ return [
5800
+ width,
5801
+ height,
5802
+ window,
5803
+ missingLeft,
5804
+ missingTop,
5805
+ ];
5806
+ }
5754
5807
  /**
5755
5808
  * Retrieves the PlanarConfiguration value from a GeoTIFF image.
5756
5809
  *
@@ -5954,7 +6007,7 @@ class CogBitmapLayer extends CompositeLayer {
5954
6007
  // opacity,
5955
6008
  // cogBitmapOptions,
5956
6009
  clampToTerrain,
5957
- cogBitmapOptions,
6010
+ cogBitmapOptions.useChannel,
5958
6011
  ],
5959
6012
  // renderSubLayers: [cogBitmapOptions],
5960
6013
  },