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