Parsing GeoTIFF Files in Java

Published

Lately I have been interested in associating elevation data to roads, especially in hilly cities like San Francisco. Fortunately the USGS publishes elevation data for the US as part of The National Map. Data is made available in GeoTIFF format via the download interface, or direct file download (folder names are latitude/longitude pairs, so n38w123 is the SF Bay Area).

Searching for GeoTIFF data on The National Map

I set out to parse the GeoTIFF files as part of an existing Java project using the GeoTools library, which was a good balance between feature-set and ease-of-use. The code and project is discussed below!

Source Code Posted to GitHub

The code sampled in this post is available on GitHub, and demonstrates how to parse GeoTIFF files in Java using the GeoTools library. Another project was created to experiment with the Java GDAL bindings, and is posted on GitHub as well.

GeoTIFF Compression

GeoTIFF files are often compressed to reduce file size. I found that the Java libraries that parse GeoTIFF files have varying support for compression algorithms. In my case, the GeoTIFF files from The National Map were compressed with LZW compression, and could not be read directly by GeoTools. I had to use the following GDAL command to decompress the GeoTIFF file first:

// https://gis.stackexchange.com/questions/92608/decompress-a-lzw-compressed-geotiff
gdal_translate pathtoinput.tif pathforoutput.tif -co COMPRESS=NONE

I found that the Java GDAL bindings had better support for reading compressed GeoTIFF files directly.

Loading GeoTIFF in Java

Reading the GeoTIFF files in Java was straightforward. The application appears to read all of the raster data into memory, and I had to increase my Java heap size when working with larger files.

// load tiff file to memory
File tiffFile = new File("/development/workspace/USGS_13_n38w123_uncomp.tif");
GeoTiffReader reader = new GeoTiffReader(tiffFile);
GridCoverage2D cov = reader.read(null);
Raster tiffRaster = cov.getRenderedImage().getData();

Sampling GeoTIFF data by GPS Coordinate

I was interested in looking up the elevation for a particular GPS latitude/longitude coordinate. This required converting the GPS coordinates to the appropriate x/y pixel in the GeoTIFF raster before reading the data.

GeoTIFF data is associated with a particular pixel or pixel group; image taken from the QGIS documentation.
// convert lat/lon gps coordinates to tiff x/y coordinates
double lat = 37.75497;
double lon = -122.44580;
CoordinateReferenceSystem wgs84 = DefaultGeographicCRS.WGS84;
GridGeometry2D gg = cov.getGridGeometry();
DirectPosition2D posWorld = new DirectPosition2D(wgs84, lon, lat); // longitude supplied first
GridCoordinates2D posGrid = gg.worldToGrid(posWorld);

// sample tiff data with at pixel coordinate
double[] rasterData = new double[1];
tiffRaster.getPixel(posGrid.x, posGrid.y, rasterData);
System.out.println(String.format("GeoTIFF data at %s, %s: %s", lat, lon, rasterData[0]));

Once the x/y pixel coordinates are available, the data is read from the GeoTIFF Raster object. An array of values may be returned (representing the different bands of data that a GeoTIFF file can have), however only the first value is taken in the example above.

Confirming the Results

It was exciting to finally read a value from the GeoTIFF, but I had to confirm the value was accurate. This was done by querying the same location in QGIS, and also consulting the Elevation Finder website.

While there was some variance in the reported values, I found the best results when using either:

  • An uncompressed GeoTIFF file with GeoTools
  • GDAL Java bindings

The alternative libraries listed below had some support for compression, but reported values were not as accurate as with GeoTools.

Alternative Libraries for Reading GeoTIFF files

I came across a few other alternatives for reading GeoTIFF files. While they did not work as well as GeoTools in my experience, I wanted to share them just incase. Once the Raster object is available, the same steps above can be followed to sample the data.

Java Advanced Imaging – no GeoTIFF LZW Compression support

// https://stackoverflow.com/questions/15429011/how-to-convert-tiff-to-jpeg-png-in-java
FileSeekableStream s = new FileSeekableStream(tiffFile);
TIFFDecodeParam param = null;
ImageDecoder dec = ImageCodec.createImageDecoder("tiff", s, param);
RenderedImage op = dec.decodeAsRenderedImage(0);
Raster tiffRaster = op.getData();

ImageIO – no GeoTIFF LZW Compression support

// https://stackoverflow.com/questions/64537498/get-tiff-tag-value-including-non-ascii-characters-from-tiff-images-in-java-11
try (ImageInputStream input = ImageIO.createImageInputStream(tiffFile)) {
  ImageReader reader = ImageIO.getImageReaders(input).next(); // TODO: Handle reader not found
  reader.setInput(input);
  // IIOMetadata metadata = reader.getImageMetadata(0); // 0 is the index of first image
  BufferedImage image = reader.read(0);
  Raster tiffRaster = image.getData();
}

Apache Commons Imaging – support for GeoTiff LZW compression but samples not as accurate

// https://gis.stackexchange.com/questions/119134/looking-for-an-open-source-java-based-geotiff-library
final TiffImagingParameters params = new TiffImagingParameters();
TiffImageParser readerT = new TiffImageParser();
BufferedImage image = readerT.getBufferedImage(tiffFile, params);
Raster tiffRaster = image.getData();

Subscribe by Email

Enter your email address below to be notified about updates and new posts.


Comments

Loading comments..

No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *