Discover huge raster files in the Browser with geotiff.js
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Fabian Schindler
geotiff.js
Source code: https://github.com/geotiffjs/geotiff.js/
Documentation: https://geotiffjs.github.io/geotiff.js/
- Pure JavaScript implementation of (Geo)TIFF
- Support for browsers and node
- Focusing ease of use + performance
- Async approach
Since v0.4.1: rewrite
v1.0.0 is around the corner(-ish)
(Geo)TIFF
- Proven standard
- Well adopted
- Images and "More"
- (U)Int / Float raster data
- Multiple images per file
- Metadata
(Geo) TIFF Benefits
- All-in-one: Data + Metadata
- Multiple resolutions of the same image (Overviews)
- More than 8-bit RGB(A)
- Arbitrary number of Bands/Channels
- Arbitrary integer/float sizes
Cloud Optimized GeoTIFFs
(COG) - https://www.cogeo.org/
- Conventions for cloud native TIFF files
- Layout friendly for network consumption
Cloud Optimized GeoTIFFs
- Recommendations
- Tiled
- Compressed
- Overview images
- File structure
COG Benefit
Efficient access to subsets of large raster files
(... even via network)
TIFF layout - Header/IFD
Header
Endianness
bigtiff?
first IFD
IFD 1
size
tag 1
tag 2
...
next IFD
IFD 2
size
tag 1
tag 2
...
next IFD
IFD n
size
tag 1
tag 2
...
next IFD
TIFF layout - Tags
IFD
ID - 1 | type | ||
ID - 2 | type | ||
ID - 3 | type | ||
... | |||
Value
Value
Value
Reference
Value
TIFF layout - Raster data
IFD
... | |||
... | |||
ID | type | ||
... | |||
... |
Reference
Reference |
Reference |
Reference |
Reference |
Raster data
Raster data
Raster data
Raster data
Header
IFD
IFD Data
Raster Data
TIFF file layout
Header
IFD-1
IFD-1 Data
Raster Data n
COG-TIFF file layout
...
IFD-2
IFD-2 Data
IFD-n
IFD-n Data
...
Raster Data n-1
...
Raster Data 1
...
image size
image size
Challenges - TIFF related
- Very diverse format
- Stripped/Tiled
- Compression
- Planar configuration
- Color representation
- Data type
- Internal/external Overviews
- Many indirections
- Potentially huge files
Challenges - Network related
- Bandwidth
- Concurrent requests
- Memory
TIFF related
Handled by Reader/Decoder
HTTP Range requests
- Allows to get a subset of the file
- Can be detected with HEAD requests
GET /file.tiff HTTP/1.1
Host: example.com
Range: bytes=0-1023
Does it solve the problem?
- TIFF files have many indirections
- Header → first IFD
- IFD → next IFD
- Tag → Value
- Strip/Tile table → Raster data
- Many requests have to be sent
→ an additional abstraction layer needed
Handled by Source
Data Flow
Raw bytes
Tiles / rows
Data Arrays
Layers of abstraction
Source
Reader
Decoder
Reader
- Select appropriate IFD
- Fetch the required bytes from source
- Execute the decoding of the rows/tiles
- Perform transformation to RGB
low-level
- Read data from a file or specific image
- Subsetting/Scaling
Subsetting / Rescaling
- Window/BBox
- Optional resampling to desired size
RGB Transformation
- Grayscale
- Palette
- CMYK
- YCbCr
- CIE L*a*b*
Output → uniform RGB
Source
- Abstraction of (remote) files
- simple interface
const source = {
async fetch(offset, length) {
// ...
return data; // ArrayBuffer instance
}
};
Built-in sources
- ArrayBuffer
- remote (XHR/fetch/node HTTP API)
- FileAPI
Remote (HTTP) sources
- Use HTTP range requests
GET /file.tiff HTTP/1.1
Host: example.com
Range: bytes=0-1023
BlockedSource
- Wraps any type of source
- Best for remote files
- Chunks requests to blocks
- Serves as a low-level cache
- Configurable block and cache size
IFD
IFD Data
Raster Data
Blocked Source
Benefits
- Fewer requests
- Slices may already be cached
- Consecutive blocks → single request
- LRU-cache → only recently used parts are cached
Decoder
- Decodes compressed data
- Deflate, LZW, JPEG, Packbits
- Work splitting using thread pools
Examples
High level usage
import GeoTIFF from 'geotiff';
// ...
const tiff = await GeoTIFF.fromUrl(someUrl);
const image = await tiff.getImage();
const width = image.getWidth();
const height = image.getHeight();
const tileWidth = image.getTileWidth();
const tileHeight = image.getTileHeight();
const samplesPerPixel = image.getSamplesPerPixel();
const origin = image.getOrigin();
const resolution = image.getResolution();
const bbox = image.getBoundingBox();
Importing, loading and metadata
High level usage
const data = await image.readRasters();
const { width, height } = data;
const left = 50;
const top = 10;
const right = 150;
const bottom = 60;
const data = await image.readRasters({
window: [left, top, right, bottom],
width: 100,
height: 100,
resampleMethod: 'bilinear',
});
const data = await tiff.readRasters({
bbox: [10.34, 57.28, 13.34, 60.23],
resX: 0.1,
resY: 0.1
});
Read raster from TIFF
Read raster from specific image
Subsetting / Scaling
Opening a file
<input type="file" id="file">
<script>
const input = document.getElementById('file'):
input.onchange = async function() {
const tiff = await GeoTIFF.fromBlob(input.files[0]);
}
</script>
Opening with external overviews
const tiff = await GeoTIFF.fromUrls(
'LC08_L1TP_189027_20170403_20170414_01_T1_B3.TIF',
['LC08_L1TP_189027_20170403_20170414_01_T1_B3.TIF.ovr']
);
Custom source
const tiff = await GeoTIFF.GeoTIFF.fromSource({
async fetch(offset, length) {
// ...
return data;
}
});
Applications built with geotiff.js
TAMP
COG Explorer
COG Explorer
- Web App
- Proof of concept
- Visualization of TIFFs on a Map Widget
- Dynamic color correction
- RGB Composition from multiple sources
geotiff.io
georaster-layer-for-leaflet
Leaflet.TileLayer.GL + geotiff.js
Copernicus EMS Activations viewer
Any more?
Community
- Never planned as a community project
- Officially presented at foss4g 2016 (Bonn)
- 13 Contributors
- 36 closed / 30 open issues
Contributions are welcome!
Next steps
- Arbitrary width integers and 16/24 bit floats
- HTTP Multi-ranges (PR pending)
- Extended write support
- More compression algorithms
- Projection in WKT format
Reprojection?
- Maybe in a separate library
- Possible in WebGL?
tiledirectory.js
- Pyramids of layered TIFFs
- Used for development of EOxCloudless
- Worldwide mosaics
Thank you!
Discover huge raster files in the Browser with geotiff.js
By Fabian Schindler
Discover huge raster files in the Browser with geotiff.js
- 403