Cloud Optimized GeoTIFF compatibility

August 21, 2026 · View on GitHub

PureJsImage supports selective range-backed reads from tiled TIFF and BigTIFF files, but COG compatibility is a combination of container layout, compression, overview structure, pixel layout, and the API used for decoding. The matrix below is checked against executable tests instead of being inferred from compression constants.

“Fully tested” means a deterministic COG or existing selective-range fixture exercises the assigned compression. “Implemented but weakly COG-tested” means the codec has focused pixel tests, but this repository does not yet have a deterministic COG-layout fixture for that assignment. “Recognized but unsupported” and “not implemented” both fail with an explicit compression ID and name.

Compression audit

IDCompressionEvidence statusDecode surfaceBoundary
1UncompressedFully testedDisplay and native rasterStrips and tiles.
2CCITT Modified HuffmanImplemented but weakly COG-testedDisplay onlyBilevel display decoding.
3CCITT Group 3Implemented but weakly COG-testedDisplay onlyBilevel display decoding with supported T4 options.
4CCITT Group 4Implemented but weakly COG-testedDisplay onlyBilevel display decoding with supported T6 options.
5LZWFully testedDisplay and native rasterStandard and legacy code packing.
6Old-style JPEGImplemented but weakly COG-testedDisplay onlyComplete streams and supported table reconstruction.
7JPEGFully testedDisplay and native rasterComplete or JPEGTables-composed streams. Native raster keeps 3-band YCbCr as converted RGB and 4-band photometric RGB ExtraSamples=0 as preserved components.
8DeflateFully testedDisplay and native rasterTIFF Deflate assignment.
32773PackBitsFully testedDisplay and native rasterBounded PackBits strips and tiles.
32809ThunderScanRecognized but unsupportedUnsupportedNo decoder; rejected explicitly.
32946Adobe DeflateImplemented but weakly COG-testedDisplay and native rasterDecoded by the bounded Deflate path.
33003Aperio JPEG 2000 YCbCrImplemented but weakly COG-testedDisplay onlyAperio codestream tiles.
33005Aperio JPEG 2000 MCTImplemented but weakly COG-testedDisplay onlyAperio codestream tiles.
34676SGILogImplemented but weakly COG-testedDisplay onlyLogL and LogLuv layouts only.
34677SGILog24Implemented but weakly COG-testedDisplay onlyLogLuv layouts only.
34712JPEG 2000Not implementedUnsupportedThe general TIFF assignment is not implemented; only the tested Aperio assignments are.
34887LERCImplemented but weakly COG-testedDisplay and native rasterLERC2 and LERC plus Deflate; LERC plus Zstandard is unsupported.
50000ZstandardImplemented but weakly COG-testedDisplay and native rasterFirst-party bounded Zstandard decoder.
50001WebPImplemented but weakly COG-testedDisplay with explicit codec compositionRequires explicit TIFF/WebP codec composition.
50002JPEG XLNot implementedUnsupportedNo TIFF JPEG XL segment integration.

Native scientific TIFF reads require the “Display and native raster” surface. Old-style JPEG, CCITT, Aperio JPEG 2000, and SGILog remain display-decoder capabilities and are not silently converted into native scientific raster samples. JPEG compression 7 native raster is tested for three-band YCbCr-converted RGB and four-band photometric RGB ExtraSamples=0 layouts; four-band sources are not routed through the RGB display decoder. WebP requires explicit TIFF/WebP codec composition.

Deterministic COG corpus

FixtureContainerCompression IDsLevelsBytes
classic-deflate-rgb-nodata.tifTIFF816×161,220
bigtiff-lzw-rgba.tifBigTIFF58×8908
subifd-deflate-rotated.tifTIFF832×32 → 16×164,916
showcase-subifd-deflate-rotated.tifTIFF82048×1024 → 1024×512 → 512×256238,596
classic-packbits-gray.tifTIFF3277316×8490
classic-jpeg-rgb.tifTIFF716×81,610
classic-jpeg-rgb-nir.tifTIFF720×12 → 10×63,706

The corpus covers tiled Classic TIFF and BigTIFF, internal SubIFD overviews, Deflate, LZW, PackBits, JPEG-in-TIFF three-band YCbCr and four-band RGB+unspecified extra-sample layouts, scalar and component nodata, RGB and RGBA samples, north-up and rotated affines, and pixel-is-area/pixel-is-point semantics. Every audited compression fixture can be opened through the GeoTIFF geo reader. Reduced-resolution tile payloads precede full-resolution tile payloads in the pyramid fixture so a remote overview request can avoid the base imagery.

Regenerate the corpus reproducibly with:

npm run fixtures:cog:prepare

The generator is first-party TypeScript. Node's zlib and the existing development-only jpeg-js oracle encode fixture segments; neither is a published runtime dependency. SHA-256 values and byte lengths are recorded in tests/fixtures/cog/manifest.json.

Set PUREJSIMAGE_GDAL_ORACLE=1 when GDAL development tools are installed to compare the public geo reader's size, affine, and band count with gdalinfo -json. GDAL is an optional test oracle and is not a runtime dependency.

Structural inspection

inspectCog(document) reports TIFF versus BigTIFF, byte order, IFD/SubIFD paths and offsets, overview dimensions, tile geometry and offsets, compression status, sample layout, and likely COG issues such as strips, missing tile tables, non-reduced overviews, non-monotonic tile offsets, unsupported compression, or IFDs stored after image data.

import { inspectCog, openTiffDocument } from 'purejsimage/tiff'

const document = await openTiffDocument(source)
const report = await inspectCog(document)
console.log(report.container, report.directories, report.issues)

This is a structural diagnostic, not a standards certification service. A warning identifies a layout that is likely to cost extra remote reads; an error means the file misses a core tiled/readable boundary used by PureJsImage.

The public geo adapter adds object size, normalized geospatial evidence, range-read suitability, request and transferred-byte counts, unique bytes, and cache activity while keeping the same non-certification boundary:

import { geoTiffReader } from 'purejsimage/geo/readers/geotiff'

const document = await geoTiffReader.open(context)
const report = await document.inspectStructure()
console.log(report.formalCogCertification, report.io)

Viewport benchmark

npm run bench:cog:viewport opens the pyramid through a simulated HTTP Range server. It accepts pixel or model viewports, chooses an overview from requested output resolution, asserts the expected level, decodes the selected region twice, and reports request count, fetched bytes, cache hits, time to first decoded TIFF tile block, total decode time, and decoded pixels. The benchmark fails if it fetches the complete fixture, and CI uses no live remote server.

npm run bench:cog:viewport
npm run bench:cog:viewport -- --space model
npm run bench:cog:viewport -- --space pixel --viewport 0,0,32,32