SkillAgentSearch skills...

Geotiff.js

geotiff.js is a small library to parse TIFF files for visualization or analysis. It is written in pure JavaScript, and is usable in both the browser and node.js applications.

Install / Use

/learn @geotiffjs/Geotiff.js
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

geotiff.js

Node.js CI npm version Gitter chat

Read (geospatial) metadata and raw array data from a wide variety of different (Geo)TIFF files types.

Features

Currently available functionality:

  • Parsing TIFFs from various sources:
    • remote (via fetch or XHR)
    • from a local ArrayBuffer
    • from the filesystem (on Browsers using the FileReader and on node using the filesystem functions)
  • Parsing the headers of all possible TIFF files
  • Rudimentary extraction of geospatial metadata
  • Reading raster data from:
    • stripped images
    • tiled images
    • band interleaved images
    • pixel interleaved images
  • Supported data-types:
    • (U)Int8/16/32
    • UInt1-31 (with some drawbacks)
    • Float16/32/64
  • Enabled compressions:
    • no compression
    • Packbits
    • LZW
    • Deflate (with floating point or horizontal predictor support)
    • JPEG
    • LERC (with additional Deflate compression support)
    • Zstandard
    • image formats supported via the browser (such as WebP)
  • Automatic selection of overview level to read from
  • Subsetting via an image window or bounding box and selected bands
  • Reading of samples into separate arrays or a single pixel-interleaved array
  • Configurable tile/strip cache
  • Configurable Pool of workers to increase decoding efficiency
  • Utility functions for geospatial parameters (Bounding Box, Origin, Resolution)
  • Limited bigTIFF support
  • Automated testing via PhantomJS

Further documentation can be found here.

Example Usage

Geotiff gives you access to all GeoTIFF metadata, but does not offer any one specific higher level API (such as GDAL) for things like transforms or data extraction. However, you can write your own higher level API using this library, given your specific dataset needs.

As an example, here is how you would resolve GPS coordinates to elevation in a GeoTIFF that encodes WGS-84 compliant geo data:

import { fromUrl, fromArrayBuffer, fromBlob  } from "geotiff";

const lerp = (a, b, t) => (1 - t) * a + t * b;

function transform(a, b, M, roundToInt = false) {
  const round = (v) => (roundToInt ? v | 0 : v);
  return [
    round(M[0] + M[1] * a + M[2] * b),
    round(M[3] + M[4] * a + M[5] * b),
  ];
}

// Load our data tile from url, arraybuffer, or blob, so we can work with it:
const tiff = await fromArrayBuffer(...);
const image = await tiff.getImage(); // by default, the first image is read.

// Construct the WGS-84 forward and inverse affine matrices:
const s = image.fileDirectory.getValue('ModelPixelScale');
const t = image.fileDirectory.getValue('ModelTiepoint');
let [sx, sy, sz] = s;
let [px, py, k, gx, gy, gz] = t;
sy = -sy; // WGS-84 tiles have a "flipped" y component

const pixelToGPS = [gx, sx, 0, gy, 0, sy];
console.log(`pixel to GPS transform matrix:`, pixelToGPS);

const gpsToPixel = [-gx / sx, 1 / sx, 0, -gy / sy, 0, 1 / sy];
console.log(`GPS to pixel transform matrix:`, gpsToPixel);

// Convert a GPS coordinate to a pixel coordinate in our tile:
const [gx1, gy1, gx2, gy2] = image.getBoundingBox();
const lat = lerp(gy1, gy2, Math.random());
const long = lerp(gx1, gx2, Math.random());
console.log(`Looking up GPS coordinate (${lat.toFixed(6)},${long.toFixed(6)})`);

const [x, y] = transform(long, lat, gpsToPixel, true);
console.log(`Corresponding tile pixel coordinate: [${x}][${y}]`);

// And as each pixel in the tile covers a geographic area, not a single
// GPS coordinate, get the area that this pixel covers:
const gpsBBox = [transform(x, y, pixelToGPS), transform(x + 1, y + 1, pixelToGPS)];
console.log(`Pixel covers the following GPS area:`, gpsBBox);

// Finally, retrieve the elevation associated with this pixel's geographic area:
const rasters = await image.readRasters();
const { width, [0]: raster } = rasters;
const elevation = raster[x + y * width];
console.log(`The elevation at (${lat.toFixed(6)},${long.toFixed(6)}) is ${elevation}m`);

Advanced Example Usage

For more advanced examples of geotiff in larger codebases, please have a look at the following projects:

3D slice view

contour

Migration Guide (v2 to v3)

Version 3.0 introduces significant performance improvements through deferred tag reading, but includes breaking changes to some APIs. This guide will help you migrate your code.

Summary of Changes

For most users: Minimal changes required. The high-level APIs (getImage(), readRasters(), etc.) remain compatible. You'll mainly need to:

  • Add await to getTiePoints() and getGDALMetadata()
  • Change image.geoKeys to image.getGeoKeys()

For advanced users: If you directly access fileDirectory properties, you'll need to migrate to the new ImageFileDirectory methods.

Breaking Changes

1. GeoKeys Access

Before (v2):

const image = await tiff.getImage();
const geoKeys = image.geoKeys;

After (v3):

const image = await tiff.getImage();
const geoKeys = image.getGeoKeys();

2. getTiePoints() and getGDALMetadata() are now async

Before (v2):

const tiePoints = image.getTiePoints();
const metadata = image.getGDALMetadata();

After (v3):

const tiePoints = await image.getTiePoints();
const metadata = await image.getGDALMetadata();

3. Accessing fileDirectory properties

The fileDirectory object has been replaced with an ImageFileDirectory class that supports deferred loading.

Before (v2):

const image = await tiff.getImage();
const { ModelPixelScale: s, ModelTiepoint: t } = image.fileDirectory;
const width = image.fileDirectory.ImageWidth;
const compression = image.fileDirectory.Compression;

After (v3) - Use getValue() for synchronous access:

const image = await tiff.getImage();
const s = image.fileDirectory.getValue('ModelPixelScale');
const t = image.fileDirectory.getValue('ModelTiepoint');
const width = image.fileDirectory.getValue('ImageWidth');
const compression = image.fileDirectory.getValue('Compression');

Note: getValue() throws an error if the tag is deferred. For tags that might be deferred (like large arrays), use loadValue():

const colorMap = await image.fileDirectory.loadValue('ColorMap');

4. Checking if tags exist

Before (v2):

if (image.fileDirectory.ModelTiepoint) {
  // ...
}

After (v3):

if (image.fileDirectory.hasTag('ModelTiepoint')) {
  // ...
}

5. Accessing array elements

For large arrays (like TileOffsets, StripOffsets), individual elements can now be loaded on-demand:

Before (v2):

const offset = image.fileDirectory.TileOffsets[5];

After (v3):

const offset = await image.fileDirectory.loadValueIndexed('TileOffsets', 5);

6. Pool API changes

If you're using the Pool class directly (most users don't):

Before (v2):

const pool = new GeoTIFF.Pool();
const decoded = await pool.decode(fileDirectory, buffer);

After (v3):

const pool = new GeoTIFF.Pool();
const compression = fileDirectory.getValue('Compression');
const params = await getDecoderParameters(compression, fileDirectory);
const boundPool = pool.bindParameters(compression, params);
const decoded = await boundPool.decode(buffer);

Note: When using readRasters({ pool }), this is handled automatically.

7. Custom Decoders

If you've implemented custom decoders:

Before (v2):

class MyDecoder extends BaseDecoder {
  async decode(fileDirectory, buffer) {
    // decode using fileDirectory properties
  }
}

After (v3):

class MyDecoder extends BaseDecoder {
  constructor(parameters) {
    super(parameters);
    // parameters extracted once during construction
  }

  async decode(buffer) {
    // decode using this.parameters
  }
}

// Register with parameter extraction function
addDecoder(
  12345, // compression ID
  () => import('./mydecoder.js').then(m => m.default),
  async (fileDirectory) => {
    return {
      ...await defaultDecoderParameterFn(fileDirectory),
      myCustomParam: await fileDirectory.loadValue('MyCustomTag')
    };
  }
);

Migration Examples

Example 1: Reading with ModelTiepoint/ModelPixelScale

Before (v2):

const tiff = await fromUrl(url);
const image = await tiff.getImage();
const { ModelPixelScale: s, ModelTiepoint: t } = image.fileDirectory;
const [sx, sy, sz] = s;
const [px, py, k, gx, gy, gz] = t;

After (v3):

const tiff = await fromUrl(url);
const image = await tiff.getImage();
const s = image.fileDirectory.getValue('ModelPixelScale');
const t = image.fileDirectory.getValue('ModelTiepoint');
const [sx, sy, sz] = s;
const [px, py, k, gx, gy, gz] = t;

Example 2: Working with ColorMap (potentially deferred)

Before (v2):

const image = await tiff.getImage();
const colorMap = image.fileDirectory.ColorMap;

After (v3):

const image = await tiff.getImage();
const colorMap = await image.fileDirectory.loadValue('Co
View on GitHub
GitHub Stars1.0k
CategoryDevelopment
Updated21h ago
Forks211

Languages

JavaScript

Security Score

100/100

Audited on Mar 31, 2026

No findings