diff --git a/examples/cog-math-multisource.js b/examples/cog-math-multisource.js
index 0ba74e9d80..1f1d6f3583 100644
--- a/examples/cog-math-multisource.js
+++ b/examples/cog-math-multisource.js
@@ -59,7 +59,7 @@ const map = new Map({
],
view: new View({
center: [1900000, 6100000],
- zoom: 15,
+ zoom: 13,
minZoom: 10,
}),
});
diff --git a/examples/cog-math.js b/examples/cog-math.js
index a02b7cd234..ca9dcdfc9f 100644
--- a/examples/cog-math.js
+++ b/examples/cog-math.js
@@ -80,13 +80,11 @@ const map = new Map({
{
// visible red, band 1 in the style expression above
url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/2020/S2A_36QWD_20200701_0_L2A/B04.tif',
- nodata: 0,
max: 10000,
},
{
// near infrared, band 2 in the style expression above
url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/2020/S2A_36QWD_20200701_0_L2A/B08.tif',
- nodata: 0,
max: 10000,
},
],
diff --git a/examples/cog.html b/examples/cog.html
index 2328e224ec..708b47bdde 100644
--- a/examples/cog.html
+++ b/examples/cog.html
@@ -4,8 +4,7 @@ title: Cloud Optimized GeoTIFF (COG)
shortdesc: Rendering a COG as a tiled layer.
docs: >
Tiled data from a Cloud Optimized GeoTIFF (COG) can be rendered as a layer. In this
- example, a single 3-band GeoTIFF is used to render RGB data. The `nodata` property is
- used to avoid rendering pixels where all three bands are 0.
+ example, a single 3-band GeoTIFF is used to render RGB data.
tags: "cog"
---
diff --git a/examples/cog.js b/examples/cog.js
index e264069327..9dc5bc4afb 100644
--- a/examples/cog.js
+++ b/examples/cog.js
@@ -26,7 +26,6 @@ const map = new Map({
sources: [
{
url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/2020/S2A_36QWD_20200701_0_L2A/TCI.tif',
- nodata: 0,
},
],
}),
diff --git a/src/ol/source/GeoTIFF.js b/src/ol/source/GeoTIFF.js
index b01f16411b..af20f311d2 100644
--- a/src/ol/source/GeoTIFF.js
+++ b/src/ol/source/GeoTIFF.js
@@ -191,6 +191,12 @@ class GeoTIFFSource extends DataTile {
*/
this.samplesPerPixel_;
+ /**
+ * @type {Array>}
+ * @private
+ */
+ this.nodataValues_;
+
/**
* @type {boolean}
* @private
@@ -249,6 +255,7 @@ class GeoTIFFSource extends DataTile {
let tileSizes;
let resolutions;
const samplesPerPixel = new Array(sources.length);
+ const nodataValues = new Array(sources.length);
let minZoom = 0;
const sourceCount = sources.length;
@@ -261,8 +268,14 @@ class GeoTIFFSource extends DataTile {
const sourceTileSizes = new Array(imageCount);
const sourceResolutions = new Array(imageCount);
+ nodataValues[sourceIndex] = new Array(imageCount);
+
for (let imageIndex = 0; imageIndex < imageCount; ++imageIndex) {
const image = images[imageIndex];
+ const nodataValue = image.getGDALNoData();
+ nodataValues[sourceIndex][imageIndex] =
+ nodataValue === null ? NaN : nodataValue;
+
const wantedSamples = this.sourceInfo_[sourceIndex].bands;
samplesPerPixel[sourceIndex] = wantedSamples
? wantedSamples.length
@@ -351,13 +364,39 @@ class GeoTIFFSource extends DataTile {
}
this.samplesPerPixel_ = samplesPerPixel;
- const sourceInfo = this.sourceInfo_;
- for (let sourceIndex = 0; sourceIndex < sourceCount; ++sourceIndex) {
- if (sourceInfo[sourceIndex].nodata !== undefined) {
+ this.nodataValues_ = nodataValues;
+
+ // decide if we need to add an alpha band to handle nodata
+ outer: for (let sourceIndex = 0; sourceIndex < sourceCount; ++sourceIndex) {
+ // option 1: source is configured with a nodata value
+ if (this.sourceInfo_[sourceIndex].nodata !== undefined) {
this.addAlpha_ = true;
break;
}
+
+ const values = nodataValues[sourceIndex];
+
+ // option 2: check image metadata for limited bands
+ const bands = this.sourceInfo_[sourceIndex].bands;
+ if (bands) {
+ for (let i = 0; i < bands.length; ++i) {
+ if (!isNaN(values[bands[i]])) {
+ this.addAlpha_ = true;
+ break outer;
+ }
+ }
+ continue;
+ }
+
+ // option 3: check image metadata for all bands
+ for (let imageIndex = 0; imageIndex < values.length; ++imageIndex) {
+ if (!isNaN(values[imageIndex])) {
+ this.addAlpha_ = true;
+ break outer;
+ }
+ }
}
+
const additionalBands = this.addAlpha_ ? 1 : 0;
this.bandCount =
samplesPerPixel.reduce((accumulator, value) => {
@@ -410,6 +449,7 @@ class GeoTIFFSource extends DataTile {
const pixelCount = size[0] * size[1];
const dataLength = pixelCount * bandCount;
+ const nodataValues = this.nodataValues_;
return Promise.all(requests).then(function (sourceSamples) {
const data = new Uint8ClampedArray(dataLength);
@@ -430,8 +470,6 @@ class GeoTIFFSource extends DataTile {
const gain = 255 / (max - min);
const bias = -min * gain;
- const nodata = source.nodata;
-
for (
let sampleIndex = 0;
sampleIndex < samplesPerPixel[sourceIndex];
@@ -444,6 +482,17 @@ class GeoTIFFSource extends DataTile {
if (!addAlpha) {
data[dataIndex] = value;
} else {
+ let nodata = source.nodata;
+ if (nodata === undefined) {
+ let bandIndex;
+ if (source.bands) {
+ bandIndex = source.bands[sampleIndex];
+ } else {
+ bandIndex = sampleIndex;
+ }
+ nodata = nodataValues[sourceIndex][bandIndex];
+ }
+
if (sourceValue !== nodata) {
transparent = false;
data[dataIndex] = value;
diff --git a/test/browser/spec/ol/source/geotiff.test.js b/test/browser/spec/ol/source/geotiff.test.js
index 4efb2199a1..de76b67e98 100644
--- a/test/browser/spec/ol/source/geotiff.test.js
+++ b/test/browser/spec/ol/source/geotiff.test.js
@@ -25,7 +25,9 @@ describe('ol.source.GeoTIFF', function () {
it('configures itself from source metadata', function (done) {
source.on('change', () => {
- expect(source.bandCount).to.be(3);
+ expect(source.addAlpha_).to.be(true);
+ expect(source.bandCount).to.be(4);
+ expect(source.nodataValues_).to.eql([[0]]);
expect(source.getTileGrid().getResolutions().length).to.be(1);
expect(source.projection.getCode()).to.be('EPSG:4326');
done();