Extract nodata values from metadata when possible
This commit is contained in:
@@ -59,7 +59,7 @@ const map = new Map({
|
|||||||
],
|
],
|
||||||
view: new View({
|
view: new View({
|
||||||
center: [1900000, 6100000],
|
center: [1900000, 6100000],
|
||||||
zoom: 15,
|
zoom: 13,
|
||||||
minZoom: 10,
|
minZoom: 10,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -80,13 +80,11 @@ const map = new Map({
|
|||||||
{
|
{
|
||||||
// visible red, band 1 in the style expression above
|
// 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',
|
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,
|
max: 10000,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// near infrared, band 2 in the style expression above
|
// 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',
|
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,
|
max: 10000,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ title: Cloud Optimized GeoTIFF (COG)
|
|||||||
shortdesc: Rendering a COG as a tiled layer.
|
shortdesc: Rendering a COG as a tiled layer.
|
||||||
docs: >
|
docs: >
|
||||||
Tiled data from a Cloud Optimized GeoTIFF (COG) can be rendered as a layer. In this
|
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
|
example, a single 3-band GeoTIFF is used to render RGB data.
|
||||||
used to avoid rendering pixels where all three bands are 0.
|
|
||||||
tags: "cog"
|
tags: "cog"
|
||||||
---
|
---
|
||||||
<div id="map" class="map"></div>
|
<div id="map" class="map"></div>
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ const map = new Map({
|
|||||||
sources: [
|
sources: [
|
||||||
{
|
{
|
||||||
url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/2020/S2A_36QWD_20200701_0_L2A/TCI.tif',
|
url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/2020/S2A_36QWD_20200701_0_L2A/TCI.tif',
|
||||||
nodata: 0,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -191,6 +191,12 @@ class GeoTIFFSource extends DataTile {
|
|||||||
*/
|
*/
|
||||||
this.samplesPerPixel_;
|
this.samplesPerPixel_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Array<Array<number>>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.nodataValues_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
* @private
|
* @private
|
||||||
@@ -249,6 +255,7 @@ class GeoTIFFSource extends DataTile {
|
|||||||
let tileSizes;
|
let tileSizes;
|
||||||
let resolutions;
|
let resolutions;
|
||||||
const samplesPerPixel = new Array(sources.length);
|
const samplesPerPixel = new Array(sources.length);
|
||||||
|
const nodataValues = new Array(sources.length);
|
||||||
let minZoom = 0;
|
let minZoom = 0;
|
||||||
|
|
||||||
const sourceCount = sources.length;
|
const sourceCount = sources.length;
|
||||||
@@ -261,8 +268,14 @@ class GeoTIFFSource extends DataTile {
|
|||||||
const sourceTileSizes = new Array(imageCount);
|
const sourceTileSizes = new Array(imageCount);
|
||||||
const sourceResolutions = new Array(imageCount);
|
const sourceResolutions = new Array(imageCount);
|
||||||
|
|
||||||
|
nodataValues[sourceIndex] = new Array(imageCount);
|
||||||
|
|
||||||
for (let imageIndex = 0; imageIndex < imageCount; ++imageIndex) {
|
for (let imageIndex = 0; imageIndex < imageCount; ++imageIndex) {
|
||||||
const image = images[imageIndex];
|
const image = images[imageIndex];
|
||||||
|
const nodataValue = image.getGDALNoData();
|
||||||
|
nodataValues[sourceIndex][imageIndex] =
|
||||||
|
nodataValue === null ? NaN : nodataValue;
|
||||||
|
|
||||||
const wantedSamples = this.sourceInfo_[sourceIndex].bands;
|
const wantedSamples = this.sourceInfo_[sourceIndex].bands;
|
||||||
samplesPerPixel[sourceIndex] = wantedSamples
|
samplesPerPixel[sourceIndex] = wantedSamples
|
||||||
? wantedSamples.length
|
? wantedSamples.length
|
||||||
@@ -351,13 +364,39 @@ class GeoTIFFSource extends DataTile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.samplesPerPixel_ = samplesPerPixel;
|
this.samplesPerPixel_ = samplesPerPixel;
|
||||||
const sourceInfo = this.sourceInfo_;
|
this.nodataValues_ = nodataValues;
|
||||||
for (let sourceIndex = 0; sourceIndex < sourceCount; ++sourceIndex) {
|
|
||||||
if (sourceInfo[sourceIndex].nodata !== undefined) {
|
// 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;
|
this.addAlpha_ = true;
|
||||||
break;
|
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;
|
const additionalBands = this.addAlpha_ ? 1 : 0;
|
||||||
this.bandCount =
|
this.bandCount =
|
||||||
samplesPerPixel.reduce((accumulator, value) => {
|
samplesPerPixel.reduce((accumulator, value) => {
|
||||||
@@ -410,6 +449,7 @@ class GeoTIFFSource extends DataTile {
|
|||||||
|
|
||||||
const pixelCount = size[0] * size[1];
|
const pixelCount = size[0] * size[1];
|
||||||
const dataLength = pixelCount * bandCount;
|
const dataLength = pixelCount * bandCount;
|
||||||
|
const nodataValues = this.nodataValues_;
|
||||||
|
|
||||||
return Promise.all(requests).then(function (sourceSamples) {
|
return Promise.all(requests).then(function (sourceSamples) {
|
||||||
const data = new Uint8ClampedArray(dataLength);
|
const data = new Uint8ClampedArray(dataLength);
|
||||||
@@ -430,8 +470,6 @@ class GeoTIFFSource extends DataTile {
|
|||||||
const gain = 255 / (max - min);
|
const gain = 255 / (max - min);
|
||||||
const bias = -min * gain;
|
const bias = -min * gain;
|
||||||
|
|
||||||
const nodata = source.nodata;
|
|
||||||
|
|
||||||
for (
|
for (
|
||||||
let sampleIndex = 0;
|
let sampleIndex = 0;
|
||||||
sampleIndex < samplesPerPixel[sourceIndex];
|
sampleIndex < samplesPerPixel[sourceIndex];
|
||||||
@@ -444,6 +482,17 @@ class GeoTIFFSource extends DataTile {
|
|||||||
if (!addAlpha) {
|
if (!addAlpha) {
|
||||||
data[dataIndex] = value;
|
data[dataIndex] = value;
|
||||||
} else {
|
} 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) {
|
if (sourceValue !== nodata) {
|
||||||
transparent = false;
|
transparent = false;
|
||||||
data[dataIndex] = value;
|
data[dataIndex] = value;
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ describe('ol.source.GeoTIFF', function () {
|
|||||||
|
|
||||||
it('configures itself from source metadata', function (done) {
|
it('configures itself from source metadata', function (done) {
|
||||||
source.on('change', () => {
|
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.getTileGrid().getResolutions().length).to.be(1);
|
||||||
expect(source.projection.getCode()).to.be('EPSG:4326');
|
expect(source.projection.getCode()).to.be('EPSG:4326');
|
||||||
done();
|
done();
|
||||||
|
|||||||
Reference in New Issue
Block a user