@gisatcz/deckgl-geolib 2.4.0-dev.1 → 2.4.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 +160 -215
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/index.min.js +1 -1
- package/dist/cjs/index.min.js.map +1 -1
- package/dist/esm/index.js +160 -215
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.min.js +1 -1
- package/dist/esm/index.min.js.map +1 -1
- package/dist/esm/types/core/CogTiles.d.ts +2 -2
- package/dist/esm/types/core/lib/BitmapGenerator.d.ts +11 -6
- package/dist/esm/types/core/types.d.ts +8 -7
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -5241,88 +5241,62 @@ function scale(num, inMin, inMax, outMin, outMax) {
|
|
|
5241
5241
|
}
|
|
5242
5242
|
|
|
5243
5243
|
class BitmapGenerator {
|
|
5244
|
+
/**
|
|
5245
|
+
* Main entry point: Generates an ImageBitmap from raw raster data.
|
|
5246
|
+
*/
|
|
5244
5247
|
static async generate(input, options) {
|
|
5245
5248
|
const optionsLocal = { ...options };
|
|
5246
5249
|
const { rasters, width, height } = input;
|
|
5247
|
-
|
|
5250
|
+
// NOTE: As of 2026-03, only interleaved rasters (rasters.length === 1) are produced by the main COG tile path.
|
|
5251
|
+
// Planar (rasters.length > 1) is not currently supported in production, but this check is kept for future extension.
|
|
5252
|
+
const isInterleaved = rasters.length === 1;
|
|
5248
5253
|
const canvas = document.createElement('canvas');
|
|
5249
5254
|
canvas.width = width;
|
|
5250
5255
|
canvas.height = height;
|
|
5251
5256
|
const c = canvas.getContext('2d');
|
|
5252
5257
|
const imageData = c.createImageData(width, height);
|
|
5253
|
-
let r;
|
|
5254
|
-
let g;
|
|
5255
|
-
let b;
|
|
5256
|
-
let a;
|
|
5257
5258
|
const size = width * height * 4;
|
|
5258
5259
|
const alpha255 = Math.floor(optionsLocal.alpha * 2.55);
|
|
5260
|
+
// Normalize colors using chroma
|
|
5259
5261
|
optionsLocal.unidentifiedColor = this.getColorFromChromaType(optionsLocal.unidentifiedColor, alpha255);
|
|
5260
5262
|
optionsLocal.nullColor = this.getColorFromChromaType(optionsLocal.nullColor, alpha255);
|
|
5261
5263
|
optionsLocal.clippedColor = this.getColorFromChromaType(optionsLocal.clippedColor, alpha255);
|
|
5262
5264
|
optionsLocal.color = this.getColorFromChromaType(optionsLocal.color, alpha255);
|
|
5263
5265
|
optionsLocal.useChannelIndex ??= optionsLocal.useChannel == null ? null : optionsLocal.useChannel - 1;
|
|
5264
5266
|
// Derive channel count from data if not provided
|
|
5265
|
-
|
|
5267
|
+
// If planar support is added, this logic must be updated to handle both layouts correctly.
|
|
5268
|
+
const numAvailableChannels = optionsLocal.numOfChannels ??
|
|
5269
|
+
(rasters.length === 1 ? rasters[0].length / (width * height) : rasters.length);
|
|
5266
5270
|
if (optionsLocal.useChannelIndex == null) {
|
|
5267
|
-
if (
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
// AUTO RANGE
|
|
5271
|
+
if (isInterleaved) {
|
|
5272
|
+
const ratio = rasters[0].length / (width * height);
|
|
5273
|
+
if (ratio === 1) {
|
|
5271
5274
|
if (optionsLocal.useAutoRange) {
|
|
5272
|
-
optionsLocal.colorScaleValueRange = this.getMinMax(
|
|
5275
|
+
optionsLocal.colorScaleValueRange = this.getMinMax(rasters[0], optionsLocal);
|
|
5273
5276
|
}
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
[imageData.data[idx], imageData.data[idx + 1], imageData.data[idx + 2], imageData.data[idx + 3]] = rgbaColor;
|
|
5287
|
-
pixel += 3;
|
|
5277
|
+
imageData.data.set(this.getColorValue(rasters[0], optionsLocal, size));
|
|
5278
|
+
} // 3 or 4-band RGB(A) imagery: use per-pixel loop for direct color assignment
|
|
5279
|
+
else if (ratio === 3 || ratio === 4) {
|
|
5280
|
+
let sampleIndex = 0;
|
|
5281
|
+
for (let i = 0; i < size; i += 4) {
|
|
5282
|
+
const rgbColor = [rasters[0][sampleIndex], rasters[0][sampleIndex + 1], rasters[0][sampleIndex + 2]];
|
|
5283
|
+
const isNoData = this.hasPixelsNoData(rgbColor, optionsLocal.noDataValue);
|
|
5284
|
+
imageData.data[i] = isNoData ? optionsLocal.nullColor[0] : rgbColor[0];
|
|
5285
|
+
imageData.data[i + 1] = isNoData ? optionsLocal.nullColor[1] : rgbColor[1];
|
|
5286
|
+
imageData.data[i + 2] = isNoData ? optionsLocal.nullColor[2] : rgbColor[2];
|
|
5287
|
+
imageData.data[i + 3] = isNoData ? optionsLocal.nullColor[3] : (ratio === 4 ? rasters[0][sampleIndex + 3] : alpha255);
|
|
5288
|
+
sampleIndex += ratio;
|
|
5288
5289
|
}
|
|
5289
5290
|
}
|
|
5290
|
-
if (rasters[0].length / (width * height) === 4) {
|
|
5291
|
-
rasters[0].forEach((value, index) => {
|
|
5292
|
-
imageData.data[index] = value;
|
|
5293
|
-
});
|
|
5294
|
-
}
|
|
5295
|
-
}
|
|
5296
|
-
if (channels === 3) {
|
|
5297
|
-
// RGB
|
|
5298
|
-
let pixel = 0;
|
|
5299
|
-
const alphaConst = Math.floor(optionsLocal.alpha * 2.55);
|
|
5300
|
-
for (let i = 0; i < size; i += 4) {
|
|
5301
|
-
r = rasters[0][pixel];
|
|
5302
|
-
g = rasters[1][pixel];
|
|
5303
|
-
b = rasters[2][pixel];
|
|
5304
|
-
a = alphaConst;
|
|
5305
|
-
imageData.data[i] = r;
|
|
5306
|
-
imageData.data[i + 1] = g;
|
|
5307
|
-
imageData.data[i + 2] = b;
|
|
5308
|
-
imageData.data[i + 3] = a;
|
|
5309
|
-
pixel += 1;
|
|
5310
|
-
}
|
|
5311
5291
|
}
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
let pixel = 0;
|
|
5315
|
-
const alphaConst = Math.floor(optionsLocal.alpha * 2.55);
|
|
5292
|
+
else {
|
|
5293
|
+
let sampleIndex = 0;
|
|
5316
5294
|
for (let i = 0; i < size; i += 4) {
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
imageData.data[i + 1] = g;
|
|
5323
|
-
imageData.data[i + 2] = b;
|
|
5324
|
-
imageData.data[i + 3] = a;
|
|
5325
|
-
pixel += 1;
|
|
5295
|
+
imageData.data[i] = rasters[0][sampleIndex];
|
|
5296
|
+
imageData.data[i + 1] = rasters[1][sampleIndex];
|
|
5297
|
+
imageData.data[i + 2] = rasters[2][sampleIndex];
|
|
5298
|
+
imageData.data[i + 3] = rasters.length === 4 ? rasters[3][sampleIndex] : alpha255;
|
|
5299
|
+
sampleIndex++;
|
|
5326
5300
|
}
|
|
5327
5301
|
}
|
|
5328
5302
|
}
|
|
@@ -5330,12 +5304,10 @@ class BitmapGenerator {
|
|
|
5330
5304
|
const isInterleaved = rasters.length === 1 && numAvailableChannels > 1;
|
|
5331
5305
|
const channel = isInterleaved ? rasters[0] : (rasters[optionsLocal.useChannelIndex] ?? rasters[0]);
|
|
5332
5306
|
const samplesPerPixel = isInterleaved ? numAvailableChannels : 1;
|
|
5333
|
-
// AUTO RANGE
|
|
5334
5307
|
if (optionsLocal.useAutoRange) {
|
|
5335
5308
|
optionsLocal.colorScaleValueRange = this.getMinMax(channel, optionsLocal, samplesPerPixel);
|
|
5336
5309
|
}
|
|
5337
|
-
|
|
5338
|
-
imageData.data.set(colorData);
|
|
5310
|
+
imageData.data.set(this.getColorValue(channel, optionsLocal, size, samplesPerPixel));
|
|
5339
5311
|
}
|
|
5340
5312
|
else {
|
|
5341
5313
|
// if user defined channel does not exist
|
|
@@ -5351,173 +5323,130 @@ class BitmapGenerator {
|
|
|
5351
5323
|
// Note: createImageBitmap(imageData) is cleaner, but using the canvas ensures broad compatibility
|
|
5352
5324
|
c.putImageData(imageData, 0, 0);
|
|
5353
5325
|
const map = await createImageBitmap(canvas);
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
// Full multi-band raw picking support is tracked in https://github.com/Gisat/deck.gl-geotiff/issues/98
|
|
5359
|
-
raw: rasters[0],
|
|
5360
|
-
width,
|
|
5361
|
-
height
|
|
5362
|
-
};
|
|
5363
|
-
}
|
|
5364
|
-
static getMinMax(array, options, samplesPerPixel = 1) {
|
|
5365
|
-
let maxValue = -Infinity;
|
|
5366
|
-
let minValue = Infinity;
|
|
5367
|
-
let foundValid = false;
|
|
5368
|
-
let pixel = samplesPerPixel === 1 ? 0 : (options.useChannelIndex ?? 0);
|
|
5369
|
-
for (let idx = pixel; idx < array.length; idx += samplesPerPixel) {
|
|
5370
|
-
if (options.noDataValue === undefined || array[idx] !== options.noDataValue) {
|
|
5371
|
-
if (array[idx] > maxValue)
|
|
5372
|
-
maxValue = array[idx];
|
|
5373
|
-
if (array[idx] < minValue)
|
|
5374
|
-
minValue = array[idx];
|
|
5375
|
-
foundValid = true;
|
|
5376
|
-
}
|
|
5377
|
-
}
|
|
5378
|
-
if (!foundValid) {
|
|
5379
|
-
return options.colorScaleValueRange || [0, 255];
|
|
5380
|
-
}
|
|
5381
|
-
return [minValue, maxValue];
|
|
5326
|
+
// rasters[0] is the interleaved buffer on the CogTiles path (primary use case).
|
|
5327
|
+
// For planar multi-band GeoTIFFs via GeoImage.getBitmap(), only the first band is exposed here.
|
|
5328
|
+
// Full multi-band raw picking support is tracked in https://github.com/Gisat/deck.gl-geotiff/issues/98
|
|
5329
|
+
return { map, raw: rasters[0], width, height };
|
|
5382
5330
|
}
|
|
5383
5331
|
static getColorValue(dataArray, options, arrayLength, samplesPerPixel = 1) {
|
|
5384
|
-
|
|
5385
|
-
|
|
5332
|
+
// Normalize all colorScale entries for chroma.js compatibility
|
|
5333
|
+
const colorScale = chroma.scale(options.colorScale?.map(c => Array.isArray(c) ? chroma(c) : c)).domain(options.colorScaleValueRange);
|
|
5386
5334
|
const colorsArray = new Uint8ClampedArray(arrayLength);
|
|
5387
|
-
const cbvInput = options.colorsBasedOnValues ?? [];
|
|
5388
|
-
const classesInput = options.colorClasses ?? [];
|
|
5389
|
-
const optUseColorsBasedOnValues = options.useColorsBasedOnValues && cbvInput.length > 0;
|
|
5390
|
-
const optUseColorClasses = options.useColorClasses && classesInput.length > 0;
|
|
5391
|
-
const dataValues = optUseColorsBasedOnValues ? cbvInput.map(([first]) => first) : [];
|
|
5392
|
-
const colorValues = optUseColorsBasedOnValues ? cbvInput.map(([, second]) => [...chroma(second).rgb(), Math.floor(options.alpha * 2.55)]) : [];
|
|
5393
|
-
const colorClasses = optUseColorClasses ? classesInput.map(([color]) => [...chroma(color).rgb(), Math.floor(options.alpha * 2.55)]) : [];
|
|
5394
|
-
const dataIntervals = optUseColorClasses ? classesInput.map(([, interval]) => interval) : [];
|
|
5395
|
-
const dataIntervalBounds = optUseColorClasses ? classesInput.map(([, , bounds], index) => {
|
|
5396
|
-
if (bounds !== undefined)
|
|
5397
|
-
return bounds;
|
|
5398
|
-
if (index === classesInput.length - 1)
|
|
5399
|
-
return [true, true];
|
|
5400
|
-
return [true, false];
|
|
5401
|
-
}) : [];
|
|
5402
|
-
// Pre-calculate Loop Variables to avoid object lookup in loop
|
|
5403
|
-
const optNoData = options.noDataValue;
|
|
5404
|
-
const optClipLow = options.clipLow;
|
|
5405
|
-
const optClipHigh = options.clipHigh;
|
|
5406
|
-
const optClippedColor = options.clippedColor;
|
|
5407
|
-
const optUseHeatMap = options.useHeatMap;
|
|
5408
|
-
const optUseSingleColor = options.useSingleColor;
|
|
5409
|
-
const optUseDataForOpacity = options.useDataForOpacity;
|
|
5410
|
-
const optColor = options.color;
|
|
5411
|
-
const optUnidentifiedColor = options.unidentifiedColor;
|
|
5412
|
-
const optNullColor = options.nullColor;
|
|
5413
5335
|
const optAlpha = Math.floor(options.alpha * 2.55);
|
|
5414
5336
|
const rangeMin = options.colorScaleValueRange[0];
|
|
5415
5337
|
const rangeMax = options.colorScaleValueRange.slice(-1)[0];
|
|
5416
|
-
// LOOKUP TABLE OPTIMIZATION (for 8-bit data)
|
|
5417
|
-
// If the data is Uint8 (0-255), we can pre-calculate the result for every possible value.
|
|
5418
5338
|
const is8Bit = dataArray instanceof Uint8Array || dataArray instanceof Uint8ClampedArray;
|
|
5419
|
-
|
|
5420
|
-
//
|
|
5421
|
-
//
|
|
5422
|
-
|
|
5423
|
-
if (is8Bit && !optUseDataForOpacity) {
|
|
5424
|
-
// Create LUT: 256 values * 4 channels (RGBA)
|
|
5339
|
+
const isFloatOrWide = !is8Bit && (dataArray instanceof Float32Array || dataArray instanceof Uint16Array || dataArray instanceof Int16Array);
|
|
5340
|
+
// 1. 8-BIT COMPREHENSIVE LUT
|
|
5341
|
+
// Single-band 8-bit (grayscale or indexed): use LUT for fast mapping
|
|
5342
|
+
if (is8Bit && !options.useDataForOpacity) {
|
|
5425
5343
|
const lut = new Uint8ClampedArray(256 * 4);
|
|
5426
5344
|
for (let i = 0; i < 256; i++) {
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
if ((optClipLow != null && i <= optClipLow) || (optClipHigh != null && i >= optClipHigh)) {
|
|
5431
|
-
[r, g, b, a] = optClippedColor;
|
|
5432
|
-
}
|
|
5433
|
-
else {
|
|
5434
|
-
let c = [r, g, b, a];
|
|
5435
|
-
if (optUseHeatMap) {
|
|
5436
|
-
const rgb = colorScale(i).rgb();
|
|
5437
|
-
c = [rgb[0], rgb[1], rgb[2], optAlpha];
|
|
5438
|
-
}
|
|
5439
|
-
else if (optUseColorsBasedOnValues) {
|
|
5440
|
-
const index = dataValues.indexOf(i);
|
|
5441
|
-
c = (index > -1) ? colorValues[index] : optUnidentifiedColor;
|
|
5442
|
-
}
|
|
5443
|
-
else if (optUseColorClasses) {
|
|
5444
|
-
const index = this.findClassIndex(i, dataIntervals, dataIntervalBounds);
|
|
5445
|
-
c = (index > -1) ? colorClasses[index] : optUnidentifiedColor;
|
|
5446
|
-
}
|
|
5447
|
-
else if (optUseSingleColor) {
|
|
5448
|
-
c = optColor;
|
|
5449
|
-
}
|
|
5450
|
-
[r, g, b, a] = c;
|
|
5451
|
-
}
|
|
5345
|
+
if ((options.clipLow != null && i <= options.clipLow) ||
|
|
5346
|
+
(options.clipHigh != null && i >= options.clipHigh)) {
|
|
5347
|
+
lut.set(options.clippedColor, i * 4);
|
|
5452
5348
|
}
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
const lutIdx = Math.min(255, Math.max(0, val)) * 4;
|
|
5464
|
-
colorsArray[outIdx++] = lut[lutIdx];
|
|
5465
|
-
colorsArray[outIdx++] = lut[lutIdx + 1];
|
|
5466
|
-
colorsArray[outIdx++] = lut[lutIdx + 2];
|
|
5467
|
-
colorsArray[outIdx++] = lut[lutIdx + 3];
|
|
5468
|
-
pixel += samplesPerPixel;
|
|
5349
|
+
else {
|
|
5350
|
+
lut.set(this.calculateSingleColor(i, colorScale, options, optAlpha), i * 4);
|
|
5351
|
+
}
|
|
5352
|
+
}
|
|
5353
|
+
for (let i = 0, sampleIndex = (options.useChannelIndex ?? 0); i < arrayLength; i += 4, sampleIndex += samplesPerPixel) {
|
|
5354
|
+
const lutIdx = dataArray[sampleIndex] * 4;
|
|
5355
|
+
colorsArray[i] = lut[lutIdx];
|
|
5356
|
+
colorsArray[i + 1] = lut[lutIdx + 1];
|
|
5357
|
+
colorsArray[i + 2] = lut[lutIdx + 2];
|
|
5358
|
+
colorsArray[i + 3] = lut[lutIdx + 3];
|
|
5469
5359
|
}
|
|
5470
5360
|
return colorsArray;
|
|
5471
5361
|
}
|
|
5472
|
-
//
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
const
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5362
|
+
// 2. FLOAT / 16-BIT LUT (HEATMAP ONLY)
|
|
5363
|
+
if (isFloatOrWide && options.useHeatMap && !options.useDataForOpacity) {
|
|
5364
|
+
const LUT_SIZE = 1024;
|
|
5365
|
+
const lut = new Uint8ClampedArray(LUT_SIZE * 4);
|
|
5366
|
+
const rangeSpan = (rangeMax - rangeMin) || 1;
|
|
5367
|
+
for (let i = 0; i < LUT_SIZE; i++) {
|
|
5368
|
+
const domainVal = rangeMin + (i / (LUT_SIZE - 1)) * rangeSpan;
|
|
5369
|
+
if ((options.clipLow != null && domainVal <= options.clipLow) ||
|
|
5370
|
+
(options.clipHigh != null && domainVal >= options.clipHigh)) {
|
|
5371
|
+
lut.set(options.clippedColor, i * 4);
|
|
5479
5372
|
}
|
|
5480
5373
|
else {
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
if (optUseDataForOpacity) {
|
|
5501
|
-
a = scale(val, rangeMin, rangeMax, 0, 255);
|
|
5502
|
-
}
|
|
5374
|
+
const rgb = colorScale(domainVal).rgb();
|
|
5375
|
+
lut[i * 4] = rgb[0];
|
|
5376
|
+
lut[i * 4 + 1] = rgb[1];
|
|
5377
|
+
lut[i * 4 + 2] = rgb[2];
|
|
5378
|
+
lut[i * 4 + 3] = optAlpha;
|
|
5379
|
+
}
|
|
5380
|
+
}
|
|
5381
|
+
for (let i = 0, sampleIndex = (options.useChannelIndex ?? 0); i < arrayLength; i += 4, sampleIndex += samplesPerPixel) {
|
|
5382
|
+
const val = dataArray[sampleIndex];
|
|
5383
|
+
if (this.isInvalid(val, options)) {
|
|
5384
|
+
colorsArray.set(this.getInvalidColor(val, options), i);
|
|
5385
|
+
}
|
|
5386
|
+
else {
|
|
5387
|
+
const t = (val - rangeMin) / rangeSpan;
|
|
5388
|
+
const lutIdx = Math.min(LUT_SIZE - 1, Math.max(0, Math.floor(t * (LUT_SIZE - 1)))) * 4;
|
|
5389
|
+
colorsArray[i] = lut[lutIdx];
|
|
5390
|
+
colorsArray[i + 1] = lut[lutIdx + 1];
|
|
5391
|
+
colorsArray[i + 2] = lut[lutIdx + 2];
|
|
5392
|
+
colorsArray[i + 3] = lut[lutIdx + 3];
|
|
5503
5393
|
}
|
|
5504
5394
|
}
|
|
5505
|
-
colorsArray
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5395
|
+
return colorsArray;
|
|
5396
|
+
}
|
|
5397
|
+
// 3. FALLBACK LOOP (Categorical Float, Opacity, or Single Color)
|
|
5398
|
+
let sampleIndex = options.useChannelIndex ?? 0;
|
|
5399
|
+
for (let i = 0; i < arrayLength; i += 4) {
|
|
5400
|
+
const val = dataArray[sampleIndex];
|
|
5401
|
+
let color;
|
|
5402
|
+
if ((options.clipLow != null && val <= options.clipLow) || (options.clipHigh != null && val >= options.clipHigh)) {
|
|
5403
|
+
color = options.clippedColor;
|
|
5404
|
+
}
|
|
5405
|
+
else {
|
|
5406
|
+
color = this.calculateSingleColor(val, colorScale, options, optAlpha);
|
|
5407
|
+
}
|
|
5408
|
+
if (options.useDataForOpacity && !this.isInvalid(val, options)) {
|
|
5409
|
+
color[3] = scale(val, rangeMin, rangeMax, 0, 255);
|
|
5410
|
+
}
|
|
5411
|
+
colorsArray.set(color, i);
|
|
5412
|
+
sampleIndex += samplesPerPixel;
|
|
5510
5413
|
}
|
|
5511
5414
|
return colorsArray;
|
|
5512
5415
|
}
|
|
5513
|
-
static
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5416
|
+
static calculateSingleColor(val, colorScale, options, alpha) {
|
|
5417
|
+
if (this.isInvalid(val, options)) {
|
|
5418
|
+
return options.nullColor;
|
|
5419
|
+
}
|
|
5420
|
+
// Color mode priority (most specific wins):
|
|
5421
|
+
// 1. useSingleColor
|
|
5422
|
+
// 2. useColorClasses
|
|
5423
|
+
// 3. useColorsBasedOnValues
|
|
5424
|
+
// 4. useHeatMap
|
|
5425
|
+
// Only the first enabled mode is used.
|
|
5426
|
+
if (options.useSingleColor) {
|
|
5427
|
+
return options.color;
|
|
5428
|
+
}
|
|
5429
|
+
else if (options.useColorClasses) {
|
|
5430
|
+
const index = this.findClassIndex(val, options);
|
|
5431
|
+
return index > -1 ? [...chroma(Array.isArray(options.colorClasses[index][0]) ? chroma(options.colorClasses[index][0]) : options.colorClasses[index][0]).rgb(), alpha] : options.unidentifiedColor;
|
|
5432
|
+
}
|
|
5433
|
+
else if (options.useColorsBasedOnValues) {
|
|
5434
|
+
const match = options.colorsBasedOnValues?.find(([v]) => v === val);
|
|
5435
|
+
return match ? [...chroma(Array.isArray(match[1]) ? chroma(match[1]) : match[1]).rgb(), alpha] : options.unidentifiedColor;
|
|
5436
|
+
}
|
|
5437
|
+
else if (options.useHeatMap) {
|
|
5438
|
+
return [...colorScale(val).rgb(), alpha];
|
|
5439
|
+
}
|
|
5440
|
+
return options.unidentifiedColor;
|
|
5441
|
+
}
|
|
5442
|
+
static findClassIndex(val, options) {
|
|
5443
|
+
if (!options.colorClasses)
|
|
5444
|
+
return -1;
|
|
5445
|
+
for (let i = 0; i < options.colorClasses.length; i++) {
|
|
5446
|
+
const [, [min, max], bounds] = options.colorClasses[i];
|
|
5447
|
+
const [incMin, incMax] = bounds || (i === options.colorClasses.length - 1 ? [true, true] : [true, false]);
|
|
5448
|
+
if ((incMin ? val >= min : val > min) && (incMax ? val <= max : val < max))
|
|
5449
|
+
return i;
|
|
5521
5450
|
}
|
|
5522
5451
|
return -1;
|
|
5523
5452
|
}
|
|
@@ -5528,14 +5457,30 @@ class BitmapGenerator {
|
|
|
5528
5457
|
}
|
|
5529
5458
|
return colorsArray;
|
|
5530
5459
|
}
|
|
5531
|
-
static
|
|
5532
|
-
|
|
5533
|
-
|
|
5460
|
+
static isInvalid(val, options) {
|
|
5461
|
+
return Number.isNaN(val) || (options.noDataValue !== undefined && val === options.noDataValue);
|
|
5462
|
+
}
|
|
5463
|
+
static getInvalidColor(val, options) {
|
|
5464
|
+
return options.nullColor;
|
|
5465
|
+
}
|
|
5466
|
+
static getMinMax(array, options, samplesPerPixel = 1) {
|
|
5467
|
+
let max = -Infinity, min = Infinity;
|
|
5468
|
+
for (let i = (options.useChannelIndex ?? 0); i < array.length; i += samplesPerPixel) {
|
|
5469
|
+
const val = array[i];
|
|
5470
|
+
if (!this.isInvalid(val, options)) {
|
|
5471
|
+
if (val > max)
|
|
5472
|
+
max = val;
|
|
5473
|
+
if (val < min)
|
|
5474
|
+
min = val;
|
|
5475
|
+
}
|
|
5534
5476
|
}
|
|
5535
|
-
return
|
|
5477
|
+
return max === -Infinity ? (options.colorScaleValueRange || [0, 255]) : [min, max];
|
|
5478
|
+
}
|
|
5479
|
+
static getColorFromChromaType(color, alpha = 255) {
|
|
5480
|
+
return (!Array.isArray(color) || color.length !== 4) ? [...chroma(color).rgb(), alpha] : color;
|
|
5536
5481
|
}
|
|
5537
|
-
static hasPixelsNoData(pixels,
|
|
5538
|
-
return
|
|
5482
|
+
static hasPixelsNoData(pixels, noData) {
|
|
5483
|
+
return noData !== undefined && pixels.every(p => p === noData);
|
|
5539
5484
|
}
|
|
5540
5485
|
}
|
|
5541
5486
|
|