Skip to content

Commit 85ad082

Browse files
Support GDAL's SPARSE_OK=TRUE format
1 parent 763a42b commit 85ad082

File tree

3 files changed

+90
-0
lines changed

3 files changed

+90
-0
lines changed

src/geotiffimage.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,16 @@ class GeoTIFFImage {
382382
offset = this.fileDirectory.StripOffsets[index];
383383
byteCount = this.fileDirectory.StripByteCounts[index];
384384
}
385+
386+
if (byteCount === 0) {
387+
const nPixels = this.getBlockHeight(y) * this.getTileWidth();
388+
const bytesPerPixel = (this.planarConfiguration === 2) ? this.getSampleByteSize(sample) : this.getBytesPerPixel();
389+
const data = new ArrayBuffer(nPixels * bytesPerPixel);
390+
const view = this.getArrayForSample(sample, data);
391+
view.fill(this.getGDALNoData() || 0);
392+
return { x, y, sample, data };
393+
}
394+
385395
const slice = (await this.source.fetch([{ offset, length: byteCount }], signal))[0];
386396

387397
let request;

test/data/setup_data.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ convert rgb.tiff -colorspace Lab cielab.tif
5353
gdal_translate -of GTiff -co COMPRESS=JPEG rgb.tiff jpeg.tiff
5454
gdal_translate -of GTiff -co COMPRESS=JPEG -co PHOTOMETRIC=YCBCR rgb.tiff jpeg_ycbcr.tiff
5555

56+
# with empty tiles/strips
57+
gdal_translate -of GTiff -co COMPRESS=DEFLATE -co SPARSE_OK=TRUE -co TILED=YES -co BLOCKXSIZE=32 -co BLOCKYSIZE=32 rgb.tiff empty_tiles.tiff
58+
gdal_translate -of GTiff -a_nodata 0 -co COMPRESS=DEFLATE -co SPARSE_OK=TRUE -co TILED=YES -co BLOCKXSIZE=32 -co BLOCKYSIZE=32 rgb.tiff empty_tiles_nodata.tiff
59+
gdal_translate -of GTiff -ot UInt16 -co COMPRESS=DEFLATE -co SPARSE_OK=TRUE -co TILED=YES -co BLOCKXSIZE=32 -co BLOCKYSIZE=32 rgb.tiff empty_tiles_16.tiff
60+
gdalbuildvrt -srcnodata 0 -vrtnodata 256 empty_tiles_16_nodata256.vrt empty_tiles_16.tiff
61+
gdal_translate -of GTiff -a_nodata 256 -ot UInt16 -co COMPRESS=DEFLATE -co SPARSE_OK=TRUE -co TILED=YES -co BLOCKXSIZE=32 -co BLOCKYSIZE=32 empty_tiles_16_nodata256.vrt empty_tiles_16_nodata256.tiff
62+
rm empty_tiles_16_nodata256.vrt
63+
5664
# modeltransformation tag
5765
wget https://s3.amazonaws.com/wdt-external/no_pixelscale_or_tiepoints.tiff
5866

test/geotiff.spec.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,78 @@ describe('ifdRequestTests', () => {
436436
});
437437
});
438438

439+
describe('Empty tile tests', () => {
440+
it('should be able to read tiffs with empty tiles', async () => {
441+
const tiff = await GeoTIFF.fromSource(createSource('empty_tiles.tiff'));
442+
const image = await tiff.getImage();
443+
expect(image).to.be.ok;
444+
expect(image.getWidth()).to.equal(541);
445+
expect(image.getHeight()).to.equal(449);
446+
expect(image.getSamplesPerPixel()).to.equal(3);
447+
});
448+
449+
it('should be able to read tiffs with empty uint16 tiles', async () => {
450+
const tiff = await GeoTIFF.fromSource(createSource('empty_tiles_16.tiff'));
451+
const image = await tiff.getImage();
452+
expect(image).to.be.ok;
453+
expect(image.getWidth()).to.equal(541);
454+
expect(image.getHeight()).to.equal(449);
455+
expect(image.getSamplesPerPixel()).to.equal(3);
456+
});
457+
458+
const options = { width: 541, height: 449, interleave: true, samples: [0, 1, 2] };
459+
const readImage = async (fname) => {
460+
const tiff = await GeoTIFF.fromSource(createSource(fname));
461+
const image = await tiff.getImage();
462+
return image.readRasters(options);
463+
};
464+
465+
it('should interpret empty tiles', async () => {
466+
const comp = await readImage('rgb.tiff');
467+
const rgb = await readImage('empty_tiles.tiff');
468+
expect(rgb).to.have.lengthOf(comp.length);
469+
let maxDiff = 0;
470+
for (let i = 0; i < rgb.length; ++i) {
471+
maxDiff = Math.max(maxDiff, Math.abs(comp[i] - rgb[i]));
472+
}
473+
expect(maxDiff).to.equal(0);
474+
});
475+
476+
it('should interpret empty tiles with nodata', async () => {
477+
const comp = await readImage('rgb.tiff');
478+
const rgb = await readImage('empty_tiles_nodata.tiff');
479+
expect(rgb).to.have.lengthOf(comp.length);
480+
let maxDiff = 0;
481+
for (let i = 0; i < rgb.length; ++i) {
482+
maxDiff = Math.max(maxDiff, Math.abs(comp[i] - rgb[i]));
483+
}
484+
expect(maxDiff).to.equal(0);
485+
});
486+
487+
it('should interpret empty uint16 tiles', async () => {
488+
const comp = await readImage('rgb.tiff');
489+
const rgb = await readImage('empty_tiles_16.tiff');
490+
expect(rgb).to.have.lengthOf(comp.length);
491+
let maxDiff = 0;
492+
for (let i = 0; i < rgb.length; ++i) {
493+
maxDiff = Math.max(maxDiff, Math.abs(comp[i] - rgb[i]));
494+
}
495+
expect(maxDiff).to.equal(0);
496+
});
497+
498+
it('should interpret empty uint16 tiles and nodata==256', async () => {
499+
const comp = await readImage('rgb.tiff');
500+
const rgb = await readImage('empty_tiles_16_nodata256.tiff');
501+
expect(rgb).to.have.lengthOf(comp.length);
502+
let maxDiff = 0;
503+
for (let i = 0; i < rgb.length; ++i) {
504+
const compSample = comp[i] === 0 ? 256 : comp[i];
505+
maxDiff = Math.max(maxDiff, Math.abs(compSample - rgb[i]));
506+
}
507+
expect(maxDiff).to.equal(0);
508+
});
509+
});
510+
439511
describe('RGB-tests', () => {
440512
const options = { window: [250, 250, 300, 300], interleave: true };
441513
const comparisonRaster = (async () => {

0 commit comments

Comments
 (0)