Remove multi-source restrictions and fix alpha

This commit is contained in:
Andreas Hocevar
2021-08-06 22:05:15 +02:00
parent 7f3f4e6cdd
commit e8ead306ff
3 changed files with 36 additions and 45 deletions

View File

@@ -6,7 +6,8 @@ docs: >
The GeoTIFF layer in this example calculates the Normalized Difference Vegetation Index (NDVI) 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 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 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" tags: "cog, ndvi, ndwi, sentinel, geotiff"
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>

View File

@@ -7,17 +7,16 @@ const source = new GeoTIFF({
sources: [ sources: [
{ {
url: 'https://s2downloads.eox.at/demo/Sentinel-2/3857/R10m.tif', url: 'https://s2downloads.eox.at/demo/Sentinel-2/3857/R10m.tif',
samples: [2], bands: [2, 3],
max: 65535, min: 0,
}, nodata: 0,
{
url: 'https://s2downloads.eox.at/demo/Sentinel-2/3857/R10m.tif',
samples: [3],
max: 65535, max: 65535,
}, },
{ {
url: 'https://s2downloads.eox.at/demo/Sentinel-2/3857/R60m.tif', url: 'https://s2downloads.eox.at/demo/Sentinel-2/3857/R60m.tif',
samples: [8], bands: [8],
min: 0,
nodata: 0,
max: 65535, max: 65535,
}, },
], ],
@@ -51,6 +50,8 @@ const map = new Map({
['*', 255, ndvi], ['*', 255, ndvi],
// blue: NDWI // blue: NDWI
['*', 255, ndwi], ['*', 255, ndwi],
// alpha
['band', 4],
], ],
}, },
source, source,

View File

@@ -18,9 +18,10 @@ import {toSize} from '../size.js';
* the configured min and max. * the configured min and max.
* @property {number} [max] The maximum source data value. Rendered values are scaled from 0 to 1 based on * @property {number} [max] The maximum source data value. Rendered values are scaled from 0 to 1 based on
* the configured min and max. * the configured min and max.
* @property {number} [nodata] Values to discard. * @property {number} [nodata] Values to discard. When provided, an additional band (alpha) will be added
* @property {Array<number>} [samples] Indices of the samples to be read from. If not provided, all samples * to the data.
* will be read. * @property {Array<number>} [bands] Indices of the bands to be read from. If not provided, all bands will
* be read.
*/ */
let workerPool; let workerPool;
@@ -179,7 +180,7 @@ class GeoTIFFSource extends DataTile {
this.resolutionFactors_ = new Array(numSources); this.resolutionFactors_ = new Array(numSources);
/** /**
* @type {number} * @type {Array<number>}
* @private * @private
*/ */
this.samplesPerPixel_; this.samplesPerPixel_;
@@ -239,7 +240,7 @@ class GeoTIFFSource extends DataTile {
let origin; let origin;
let tileSizes; let tileSizes;
let resolutions; let resolutions;
let samplesPerPixel; const samplesPerPixel = new Array(sources.length);
let minZoom = 0; let minZoom = 0;
const sourceCount = sources.length; const sourceCount = sources.length;
@@ -254,16 +255,10 @@ class GeoTIFFSource extends DataTile {
for (let imageIndex = 0; imageIndex < imageCount; ++imageIndex) { for (let imageIndex = 0; imageIndex < imageCount; ++imageIndex) {
const image = images[imageIndex]; const image = images[imageIndex];
const wantedSamples = this.sourceInfo_[sourceIndex].samples; const wantedSamples = this.sourceInfo_[sourceIndex].bands;
const imageSamplesPerPixel = wantedSamples samplesPerPixel[sourceIndex] = wantedSamples
? wantedSamples.length ? wantedSamples.length
: image.getSamplesPerPixel(); : 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); const level = imageCount - (imageIndex + 1);
if (!sourceExtent) { 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; this.samplesPerPixel_ = samplesPerPixel;
const sourceInfo = this.sourceInfo_; const sourceInfo = this.sourceInfo_;
for (let sourceIndex = 0; sourceIndex < sourceCount; ++sourceIndex) { for (let sourceIndex = 0; sourceIndex < sourceCount; ++sourceIndex) {
@@ -361,15 +350,12 @@ class GeoTIFFSource extends DataTile {
break; break;
} }
} }
let additionalBands = 0; const additionalBands = this.addAlpha_ ? 1 : 0;
if (this.addAlpha_) { this.bandCount =
if (sourceCount === 2 && samplesPerPixel === 1) { samplesPerPixel.reduce((accumulator, value) => {
additionalBands = 2; accumulator += value;
} else { return accumulator;
additionalBands = 1; }, 0) + additionalBands;
}
}
this.bandCount = samplesPerPixel * sourceCount + additionalBands;
const tileGrid = new TileGrid({ const tileGrid = new TileGrid({
extent: extent, extent: extent,
@@ -408,7 +394,8 @@ class GeoTIFFSource extends DataTile {
window: pixelBounds, window: pixelBounds,
width: size[0], width: size[0],
height: size[1], height: size[1],
samples: source.samples, samples: source.bands,
fillValue: source.nodata,
pool: getWorkerPool(), pool: getWorkerPool(),
}); });
} }
@@ -418,9 +405,9 @@ class GeoTIFFSource extends DataTile {
return Promise.all(requests).then(function (sourceSamples) { return Promise.all(requests).then(function (sourceSamples) {
const data = new Uint8ClampedArray(dataLength); const data = new Uint8ClampedArray(dataLength);
let dataIndex = 0;
for (let pixelIndex = 0; pixelIndex < pixelCount; ++pixelIndex) { for (let pixelIndex = 0; pixelIndex < pixelCount; ++pixelIndex) {
let transparent = addAlpha; let transparent = addAlpha;
const sourceOffset = pixelIndex * bandCount;
for (let sourceIndex = 0; sourceIndex < sourceCount; ++sourceIndex) { for (let sourceIndex = 0; sourceIndex < sourceCount; ++sourceIndex) {
const source = sourceInfo[sourceIndex]; const source = sourceInfo[sourceIndex];
let min = source.min; let min = source.min;
@@ -437,10 +424,9 @@ class GeoTIFFSource extends DataTile {
const nodata = source.nodata; const nodata = source.nodata;
const sampleOffset = sourceOffset + sourceIndex * samplesPerPixel;
for ( for (
let sampleIndex = 0; let sampleIndex = 0;
sampleIndex < samplesPerPixel; sampleIndex < samplesPerPixel[sourceIndex];
++sampleIndex ++sampleIndex
) { ) {
const sourceValue = const sourceValue =
@@ -448,18 +434,21 @@ class GeoTIFFSource extends DataTile {
const value = gain * sourceValue + bias; const value = gain * sourceValue + bias;
if (!addAlpha) { if (!addAlpha) {
data[sampleOffset + sampleIndex] = value; data[dataIndex] = value;
} else { } else {
if (sourceValue !== nodata) { if (sourceValue !== nodata) {
transparent = false; 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++;
} }
} }