geotiffjs / 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.
https://geotiffjs.github.io/
MIT License
859 stars 179 forks source link

Example of how to actually *use* this? #350

Closed Pomax closed 1 year ago

Pomax commented 1 year ago

I dug through https://geotiffjs.github.io/geotiff.js/ but I can't for the life of me figure how, after loading in a DSM tif, I can actually resolve GPS coordinates to elevation pixels, and vice versa. I can resolve the file directory and see my tile file uses WGS84, but I can't see anything that lets me use that to turn lat/long into 2d y/x indices. For instance, usinggdal-async or the like (which requires having gdal installed, and that's exactly what we're trying to avoid =) we can use something as simple as the following:

const ds = gdal.open(filename);
const transform = new gdal.CoordinateTransformation(WGS84, ds);
const band = ds.bands.get(1);

fucntion lookup(lat, lon) {
    const { x, y } = transform.transformPoint(lon, lat);
    return band.pixels.get(x | 0, y | 0);
}

I don't see how to do the same thing using geotiff. How does one actually use this to get elevation data?

potion-cellar commented 1 year ago

Here's what I generally do.

First off the goal is to get the actual pixel coordinates for the raster, for the desired coordinates.

So, GeoTIFFImage has a geoKeys property (https://geotiffjs.github.io/geotiff.js/module-geotiffimage-GeoTIFFImage.html).

This provides the projection information. There is potentially a lot of stuff there...You can skip the headache of dealing with complicated projections by using a couple libraries:

geotiff-geokeys-to-proj4 https://github.com/matafokka/geotiff-geokeys-to-proj4

And then, of course, feeding that into proj4js http://proj4js.org/


Using those libraries, set up the projection object...

import proj4 from 'proj4';
import * as geokeysToProj4 from "geotiff-geokeys-to-proj4";

...
const image = await <your geotiff object>.getImage();
const geoKeys = image.getGeoKeys();

const projObj = geokeysToProj4.toProj4( geokeys );
const projection = proj4( `WGS84`, projObj.proj4 );

Get the x and y pixel coordinates from the lat/lng...

const width = image.getWidth();
const height = image.getHeight();
const [ originX, originY ] = image.getOrigin();
const [ xSize, ySize ] = image.getResolution();
const uWidth = xSize * width;
const uHeight = ySize * height;

const { x, y } = projection.forward( {
    x: <your longitude>,
    y: <your latitude>
} );

const percentX = ( x - originX ) / uWidth;
const percentY = ( y - originY ) / uHeight;

const pixelX = Math.floor( width * percentX );
const pixelY = Math.floor( height * percentY );

Now you have two options. You can either read a window that is one pixel in size, or you can read the raw data array. If you're going to be querying a lot of data points a loop, the raw data array will be faster.

Window

const [ value ] = await <your geotiff object>.readRasters( {
    interleave: true,
    window: [ pixelX, pixelY, pixelX + 1, pixelY + 1],
    samples: [ 0 ]
} );

or...

Raw Data Array

Get your DEM data as a 1d array...

const data = await <your geotiff object>.readRasters( {
    interleave: true,
    samples: [ 0 ]
} );

Convert the x and y pixels into an array index...

const val = data[width * pixelY + pixelX];

Hope this helps or gives you some ideas. This was transcribed from a project that is a couple years old so apologies if it doesn't work out of the box but should get you on the right track.

Pomax commented 1 year ago

While useful "for me", the main reason I filed this issue is to get examples added to the README.md, or linked from the README.md - a library without examples is an incomplete library.

ashley-mort commented 1 year ago

https://github.com/geotiffjs/cog-explorer

Pomax commented 1 year ago

That's a separate project. Not "examples of how to use this library, and the functions it supports, in this library's own docs".

constantinius commented 1 year ago

@Pomax

Thanks for the feedback!

It was a deliberate choice to not include coordinate transformation as part of the library, so this is why it is not shipped with proj or an alternative. It is meant as a low level reader of TIFF raster data.

The problem is that in many cases you have a straightforward way to get from pixel/line to geo coordinates, but in many cases it is not. Think of SAR files with GCPs instead of a transformation matrix. geotiff.js is not GDAL for the web.

However I agree to the fact that the Readme could get a brushup on that exact topic. Since you already delved into the topic, would you be willing to contribute a small section to get raster values of a specific location (also maybe even using proj)?

Pomax commented 1 year ago

Ah, I see! That might actually be good to call out (more?) explicitly in the readme, since you can read tiff tags with any tif parser (it's the "t" in tiff, after all =D), so when I found "geotiff" I assumed it was more specifically geared towards not needing to write your own "value for geo coordinate" (even if there's the option to do all the low level stuff if you really need that) =)

I can give that section a shot, I'm currently using node-tiff to get elevation data out of the ALOS World3D dataset, which is probably one of the easier datasets to work with, but I'd probably end up writing an example that would work with any tiff parser, rather than geotiff specifically, so if that's fine I can write something up.

constantinius commented 1 year ago

Thanks @Pomax, this is very much appreciated

Pomax commented 1 year ago

I've filed a PR with some example code and guide text, feel free to massage that into whatever you feel makes the most sense for inclusion =)