diff --git a/examples/cog-math-multisource.html b/examples/cog-math-multisource.html
index 08e09a0105..b5889c5bfe 100644
--- a/examples/cog-math-multisource.html
+++ b/examples/cog-math-multisource.html
@@ -6,7 +6,8 @@ docs: >
The GeoTIFF layer in this example calculates the Normalized Difference Vegetation Index (NDVI)
and Normalized Difference Water Index (NDWI) from two cloud-optimized Sentinel 2 GeoTIFFs: one
with 10 m resolution and red and a near infrared bands, and one with 60 m resolution and a short
- wave infrared channel. The NDVI is shown as green, the NDWI as blue.
+ wave infrared channel. The NDVI is shown as green, the NDWI as blue. The 4th band is the alpha
+ band, which gets added when a source has a `nodata` value configured.
tags: "cog, ndvi, ndwi, sentinel, geotiff"
---
diff --git a/examples/cog-math-multisource.js b/examples/cog-math-multisource.js
index e585b24305..0ba74e9d80 100644
--- a/examples/cog-math-multisource.js
+++ b/examples/cog-math-multisource.js
@@ -7,17 +7,16 @@ const source = new GeoTIFF({
sources: [
{
url: 'https://s2downloads.eox.at/demo/Sentinel-2/3857/R10m.tif',
- samples: [2],
- max: 65535,
- },
- {
- url: 'https://s2downloads.eox.at/demo/Sentinel-2/3857/R10m.tif',
- samples: [3],
+ bands: [2, 3],
+ min: 0,
+ nodata: 0,
max: 65535,
},
{
url: 'https://s2downloads.eox.at/demo/Sentinel-2/3857/R60m.tif',
- samples: [8],
+ bands: [8],
+ min: 0,
+ nodata: 0,
max: 65535,
},
],
@@ -51,6 +50,8 @@ const map = new Map({
['*', 255, ndvi],
// blue: NDWI
['*', 255, ndwi],
+ // alpha
+ ['band', 4],
],
},
source,
diff --git a/src/ol/source/GeoTIFF.js b/src/ol/source/GeoTIFF.js
index 6465b406e8..3cc6c45d9a 100644
--- a/src/ol/source/GeoTIFF.js
+++ b/src/ol/source/GeoTIFF.js
@@ -18,9 +18,10 @@ import {toSize} from '../size.js';
* the configured min and max.
* @property {number} [max] The maximum source data value. Rendered values are scaled from 0 to 1 based on
* the configured min and max.
- * @property {number} [nodata] Values to discard.
- * @property {Array} [samples] Indices of the samples to be read from. If not provided, all samples
- * will be read.
+ * @property {number} [nodata] Values to discard. When provided, an additional band (alpha) will be added
+ * to the data.
+ * @property {Array} [bands] Indices of the bands to be read from. If not provided, all bands will
+ * be read.
*/
let workerPool;
@@ -179,7 +180,7 @@ class GeoTIFFSource extends DataTile {
this.resolutionFactors_ = new Array(numSources);
/**
- * @type {number}
+ * @type {Array}
* @private
*/
this.samplesPerPixel_;
@@ -239,7 +240,7 @@ class GeoTIFFSource extends DataTile {
let origin;
let tileSizes;
let resolutions;
- let samplesPerPixel;
+ const samplesPerPixel = new Array(sources.length);
let minZoom = 0;
const sourceCount = sources.length;
@@ -254,16 +255,10 @@ class GeoTIFFSource extends DataTile {
for (let imageIndex = 0; imageIndex < imageCount; ++imageIndex) {
const image = images[imageIndex];
- const wantedSamples = this.sourceInfo_[sourceIndex].samples;
- const imageSamplesPerPixel = wantedSamples
+ const wantedSamples = this.sourceInfo_[sourceIndex].bands;
+ samplesPerPixel[sourceIndex] = wantedSamples
? wantedSamples.length
: image.getSamplesPerPixel();
- if (!samplesPerPixel) {
- samplesPerPixel = imageSamplesPerPixel;
- } else {
- const message = `Band count mismatch for source ${sourceIndex}, got ${imageSamplesPerPixel} but expected ${samplesPerPixel}`;
- assertEqual(samplesPerPixel, imageSamplesPerPixel, 0, message);
- }
const level = imageCount - (imageIndex + 1);
if (!sourceExtent) {
@@ -347,12 +342,6 @@ class GeoTIFFSource extends DataTile {
}
}
- if (sourceCount > 1 && samplesPerPixel !== 1) {
- throw new Error(
- 'Expected single band GeoTIFFs when using multiple sources'
- );
- }
-
this.samplesPerPixel_ = samplesPerPixel;
const sourceInfo = this.sourceInfo_;
for (let sourceIndex = 0; sourceIndex < sourceCount; ++sourceIndex) {
@@ -361,15 +350,12 @@ class GeoTIFFSource extends DataTile {
break;
}
}
- let additionalBands = 0;
- if (this.addAlpha_) {
- if (sourceCount === 2 && samplesPerPixel === 1) {
- additionalBands = 2;
- } else {
- additionalBands = 1;
- }
- }
- this.bandCount = samplesPerPixel * sourceCount + additionalBands;
+ const additionalBands = this.addAlpha_ ? 1 : 0;
+ this.bandCount =
+ samplesPerPixel.reduce((accumulator, value) => {
+ accumulator += value;
+ return accumulator;
+ }, 0) + additionalBands;
const tileGrid = new TileGrid({
extent: extent,
@@ -408,7 +394,8 @@ class GeoTIFFSource extends DataTile {
window: pixelBounds,
width: size[0],
height: size[1],
- samples: source.samples,
+ samples: source.bands,
+ fillValue: source.nodata,
pool: getWorkerPool(),
});
}
@@ -418,9 +405,9 @@ class GeoTIFFSource extends DataTile {
return Promise.all(requests).then(function (sourceSamples) {
const data = new Uint8ClampedArray(dataLength);
+ let dataIndex = 0;
for (let pixelIndex = 0; pixelIndex < pixelCount; ++pixelIndex) {
let transparent = addAlpha;
- const sourceOffset = pixelIndex * bandCount;
for (let sourceIndex = 0; sourceIndex < sourceCount; ++sourceIndex) {
const source = sourceInfo[sourceIndex];
let min = source.min;
@@ -437,10 +424,9 @@ class GeoTIFFSource extends DataTile {
const nodata = source.nodata;
- const sampleOffset = sourceOffset + sourceIndex * samplesPerPixel;
for (
let sampleIndex = 0;
- sampleIndex < samplesPerPixel;
+ sampleIndex < samplesPerPixel[sourceIndex];
++sampleIndex
) {
const sourceValue =
@@ -448,18 +434,21 @@ class GeoTIFFSource extends DataTile {
const value = gain * sourceValue + bias;
if (!addAlpha) {
- data[sampleOffset + sampleIndex] = value;
+ data[dataIndex] = value;
} else {
if (sourceValue !== nodata) {
transparent = false;
- data[sampleOffset + sampleIndex] = value;
+ data[dataIndex] = value;
}
}
+ dataIndex++;
}
-
- if (addAlpha && !transparent) {
- data[sampleOffset + samplesPerPixel] = 255;
+ }
+ if (addAlpha) {
+ if (!transparent) {
+ data[dataIndex] = 255;
}
+ dataIndex++;
}
}