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

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/esm/index.js CHANGED
@@ -4876,7 +4876,7 @@ const DefaultGeoImageOptions = {
4876
4876
  planarConfig: undefined,
4877
4877
  // --- Mesh generation (terrain only) ---
4878
4878
  tesselator: 'martini',
4879
- terrainColor: [133, 133, 133, 255],
4879
+ terrainColor: [200, 200, 200, 255],
4880
4880
  terrainSkirtHeight: 100,
4881
4881
  // Default fallback for invalid/nodata elevations. Should be configured based on the dataset's actual range.
4882
4882
  terrainMinValue: 0,
@@ -5615,6 +5615,22 @@ class BitmapGenerator {
5615
5615
  * Key: colorScale config + range, Value: pre-computed RGBA LUT
5616
5616
  */
5617
5617
  static _swissColorLUTCache = new Map();
5618
+ /**
5619
+ * Cache for 8-bit (256-entry) color LUTs.
5620
+ * Shared process-wide across all tiles and datasets when options are fixed (i.e. !useAutoRange).
5621
+ * Key: serialised coloring options, Value: pre-computed 256×RGBA LUT
5622
+ */
5623
+ static _8bitLUTCache = new Map();
5624
+ /**
5625
+ * Cache for float/16-bit (1024-entry) heatmap LUTs.
5626
+ * Shared process-wide across all tiles and datasets when !useAutoRange.
5627
+ * Key: serialised coloring options + range, Value: pre-computed 1024×RGBA LUT
5628
+ */
5629
+ static _floatLUTCache = new Map();
5630
+ /** Build a cache key that captures all options affecting LUT colour output. */
5631
+ static getLUTCacheKey(options, rangeMin, rangeMax, optAlpha) {
5632
+ return `${rangeMin}_${rangeMax}_${optAlpha}_${JSON.stringify(options.colorScale)}_${options.useSingleColor}_${JSON.stringify(options.color)}_${options.useColorClasses}_${JSON.stringify(options.colorClasses)}_${options.useColorsBasedOnValues}_${JSON.stringify(options.colorsBasedOnValues)}_${options.useHeatMap}_${options.clipLow ?? ''}_${options.clipHigh ?? ''}_${JSON.stringify(options.clippedColor)}_${JSON.stringify(options.nullColor)}_${JSON.stringify(options.unidentifiedColor)}`;
5633
+ }
5618
5634
  /**
5619
5635
  * Main entry point: Generates an ImageBitmap from raw raster data.
5620
5636
  */
@@ -5795,17 +5811,25 @@ class BitmapGenerator {
5795
5811
  return colorsArray;
5796
5812
  }
5797
5813
  // 2. 8-BIT COMPREHENSIVE LUT
5798
- // Single-band 8-bit (grayscale or indexed): use LUT for fast mapping
5814
+ // Single-band 8-bit (grayscale or indexed): use LUT for fast mapping.
5815
+ // The LUT covers all 256 possible values and is fixed for a given set of coloring options,
5816
+ // so cache it across tiles (skip cache only when useAutoRange recomputes the range per tile).
5799
5817
  if (is8Bit && !options.useDataForOpacity) {
5800
- const lut = new Uint8ClampedArray(256 * 4);
5801
- for (let i = 0; i < 256; i++) {
5802
- if ((options.clipLow != null && i <= options.clipLow) ||
5803
- (options.clipHigh != null && i >= options.clipHigh)) {
5804
- lut.set(options.clippedColor, i * 4);
5805
- }
5806
- else {
5807
- lut.set(this.calculateSingleColor(i, colorScale, options, optAlpha), i * 4);
5818
+ const cacheKey = !options.useAutoRange ? this.getLUTCacheKey(options, rangeMin, rangeMax, optAlpha) : null;
5819
+ let lut = cacheKey ? (this._8bitLUTCache.get(cacheKey) ?? null) : null;
5820
+ if (!lut) {
5821
+ lut = new Uint8ClampedArray(256 * 4);
5822
+ for (let i = 0; i < 256; i++) {
5823
+ if ((options.clipLow != null && i <= options.clipLow) ||
5824
+ (options.clipHigh != null && i >= options.clipHigh)) {
5825
+ lut.set(options.clippedColor, i * 4);
5826
+ }
5827
+ else {
5828
+ lut.set(this.calculateSingleColor(i, colorScale, options, optAlpha), i * 4);
5829
+ }
5808
5830
  }
5831
+ if (cacheKey)
5832
+ this._8bitLUTCache.set(cacheKey, lut);
5809
5833
  }
5810
5834
  for (let i = 0, sampleIndex = (options.useChannelIndex ?? 0); i < arrayLength; i += 4, sampleIndex += samplesPerPixel) {
5811
5835
  const lutIdx = primaryBuffer[sampleIndex] * 4;
@@ -5817,23 +5841,32 @@ class BitmapGenerator {
5817
5841
  return colorsArray;
5818
5842
  }
5819
5843
  // 3. FLOAT / 16-BIT LUT (HEATMAP ONLY)
5820
- if (isFloatOrWide && options.useHeatMap && !options.useDataForOpacity) {
5844
+ // Guard: only activate when heatmap is the highest-priority active mode.
5845
+ // If a more specific mode (useSingleColor, useColorClasses, useColorsBasedOnValues) is set,
5846
+ // fall through to the general loop so calculateSingleColor can honour the priority chain.
5847
+ if (isFloatOrWide && options.useHeatMap && !options.useSingleColor && !options.useColorClasses && !options.useColorsBasedOnValues && !options.useDataForOpacity) {
5821
5848
  const LUT_SIZE = 1024;
5822
- const lut = new Uint8ClampedArray(LUT_SIZE * 4);
5823
5849
  const rangeSpan = (rangeMax - rangeMin) || 1;
5824
- for (let i = 0; i < LUT_SIZE; i++) {
5825
- const domainVal = rangeMin + (i / (LUT_SIZE - 1)) * rangeSpan;
5826
- if ((options.clipLow != null && domainVal <= options.clipLow) ||
5827
- (options.clipHigh != null && domainVal >= options.clipHigh)) {
5828
- lut.set(options.clippedColor, i * 4);
5829
- }
5830
- else {
5831
- const rgb = colorScale(domainVal).rgb();
5832
- lut[i * 4] = rgb[0];
5833
- lut[i * 4 + 1] = rgb[1];
5834
- lut[i * 4 + 2] = rgb[2];
5835
- lut[i * 4 + 3] = optAlpha;
5850
+ const cacheKey = !options.useAutoRange ? this.getLUTCacheKey(options, rangeMin, rangeMax, optAlpha) : null;
5851
+ let lut = cacheKey ? (this._floatLUTCache.get(cacheKey) ?? null) : null;
5852
+ if (!lut) {
5853
+ lut = new Uint8ClampedArray(LUT_SIZE * 4);
5854
+ for (let i = 0; i < LUT_SIZE; i++) {
5855
+ const domainVal = rangeMin + (i / (LUT_SIZE - 1)) * rangeSpan;
5856
+ if ((options.clipLow != null && domainVal <= options.clipLow) ||
5857
+ (options.clipHigh != null && domainVal >= options.clipHigh)) {
5858
+ lut.set(options.clippedColor, i * 4);
5859
+ }
5860
+ else {
5861
+ const rgb = colorScale(domainVal).rgb();
5862
+ lut[i * 4] = rgb[0];
5863
+ lut[i * 4 + 1] = rgb[1];
5864
+ lut[i * 4 + 2] = rgb[2];
5865
+ lut[i * 4 + 3] = optAlpha;
5866
+ }
5836
5867
  }
5868
+ if (cacheKey)
5869
+ this._floatLUTCache.set(cacheKey, lut);
5837
5870
  }
5838
5871
  for (let i = 0, sampleIndex = (options.useChannelIndex ?? 0); i < arrayLength; i += 4, sampleIndex += samplesPerPixel) {
5839
5872
  const val = primaryBuffer[sampleIndex];
@@ -6510,7 +6543,7 @@ class GeoImage {
6510
6543
  this.data = data;
6511
6544
  }
6512
6545
  async getMap(input, options, meshMaxError) {
6513
- const mergedOptions = { ...DefaultGeoImageOptions, ...options };
6546
+ const mergedOptions = GeoImage.resolveVisualizationMode({ ...DefaultGeoImageOptions, ...options }, options);
6514
6547
  switch (mergedOptions.type) {
6515
6548
  case 'image':
6516
6549
  return this.getBitmap(input, mergedOptions);
@@ -6520,6 +6553,56 @@ class GeoImage {
6520
6553
  return null;
6521
6554
  }
6522
6555
  }
6556
+ /**
6557
+ * Resolves the active visualization (coloring) mode after merging user options with defaults.
6558
+ *
6559
+ * Solves three key issues:
6560
+ *
6561
+ * 1. **Mutual exclusivity**: `DefaultGeoImageOptions` sets `useHeatMap: true`. If a user
6562
+ * explicitly enables a coloring mode without explicitly setting `useHeatMap: false`,
6563
+ * enforce that only the explicitly-enabled modes are active (all others forced to false).
6564
+ * This prevents the default `useHeatMap: true` from interfering with user-chosen modes.
6565
+ *
6566
+ * 2. **Bitmap default**: for `type === 'image'` with no user-specified coloring mode,
6567
+ * keep `useHeatMap: true` from defaults. This provides sensible data-driven visualization.
6568
+ *
6569
+ * 3. **Terrain default**: for `type === 'terrain'` with no user-specified coloring mode and
6570
+ * no kernel-texture mode (`useSwissRelief` / `useSlope` / `useHillshade`), enable
6571
+ * `useSingleColor` with `color = terrainColor`. This renders the mesh in the documented
6572
+ * default colour (grey) without a data-driven texture overlay. When a kernel mode IS
6573
+ * present but no coloring mode is specified, keep `useHeatMap: true` so the kernel output
6574
+ * is still colourised.
6575
+ */
6576
+ static resolveVisualizationMode(mergedOptions, userOptions) {
6577
+ const coloringModes = ['useSingleColor', 'useColorClasses', 'useColorsBasedOnValues', 'useHeatMap'];
6578
+ const userExplicitColoringModes = coloringModes.filter(m => userOptions[m] === true);
6579
+ const resolved = { ...mergedOptions };
6580
+ if (userExplicitColoringModes.length > 0) {
6581
+ // Enforce mutual exclusivity: disable all coloring modes, then enable only those
6582
+ // explicitly set by the user. This prevents the default useHeatMap from interfering.
6583
+ for (const mode of coloringModes) {
6584
+ resolved[mode] = false;
6585
+ }
6586
+ for (const mode of userExplicitColoringModes) {
6587
+ resolved[mode] = true;
6588
+ }
6589
+ }
6590
+ else if (mergedOptions.type === 'terrain') {
6591
+ // Terrain with no explicit coloring mode.
6592
+ const hasKernelMode = userOptions.useSwissRelief || userOptions.useSlope || userOptions.useHillshade;
6593
+ if (!hasKernelMode) {
6594
+ // No kernel mode: enable useSingleColor with terrainColor as the default.
6595
+ // This renders the mesh in the documented colour without a data-driven texture.
6596
+ resolved.useHeatMap = false;
6597
+ resolved.useSingleColor = true;
6598
+ resolved.color = mergedOptions.terrainColor;
6599
+ }
6600
+ // When a kernel mode is present without an explicit coloring mode, keep
6601
+ // useHeatMap: true from defaults so the kernel output is colourised.
6602
+ }
6603
+ // For 'image' with no explicit coloring mode: keep useHeatMap: true from DefaultGeoImageOptions.
6604
+ return resolved;
6605
+ }
6523
6606
  // GetHeightmap uses only "useChannel" and "multiplier" options
6524
6607
  async getHeightmap(input, options, meshMaxError) {
6525
6608
  let rasters = [];