Reading each pixel of each band of multiband GeoTiff with GeoTools Java?
Mark's answer is great! It really helped me out.
Here's a slightly modified version of Mark's code. The major difference is that this code does not rely on the java.awt.image package to compute the image size, number of bands, or pixel values. Instead, it uses the GeoTools Coverage API.
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.gce.geotiff.GeoTiffReader;
import org.geotools.coverage.grid.*;
import org.opengis.coverage.grid.*;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterValue;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.OverviewPolicy;
public class Test {
public static void test(java.io.File file) throws Exception {
ParameterValue<OverviewPolicy> policy = AbstractGridFormat.OVERVIEW_POLICY.createValue();
policy.setValue(OverviewPolicy.IGNORE);
//this will basically read 4 tiles worth of data at once from the disk...
ParameterValue<String> gridsize = AbstractGridFormat.SUGGESTED_TILE_SIZE.createValue();
//Setting read type: use JAI ImageRead (true) or ImageReaders read methods (false)
ParameterValue<Boolean> useJaiRead = AbstractGridFormat.USE_JAI_IMAGEREAD.createValue();
useJaiRead.setValue(true);
GridCoverage2DReader reader = new GeoTiffReader(file);
GridEnvelope dimensions = reader.getOriginalGridRange();
GridCoordinates maxDimensions = dimensions.getHigh();
int w = maxDimensions.getCoordinateValue(0)+1;
int h = maxDimensions.getCoordinateValue(1)+1;
int numBands = reader.getGridCoverageCount();
GridCoverage2D coverage = reader.read(
new GeneralParameterValue[]{policy, gridsize, useJaiRead}
);
GridGeometry2D geometry = coverage.getGridGeometry();
for (int i=0; i<w; i++) {
for (int j=0; j<h; j++) {
org.geotools.geometry.Envelope2D pixelEnvelop =
geometry.gridToWorld(new GridEnvelope2D(i, j, 1, 1));
double lat = pixelEnvelop.getCenterY();
double lon = pixelEnvelop.getCenterX();
double[] vals = new double[numBands];
coverage.evaluate(new GridCoordinates2D(i, j), vals);
//Do something!
}
}
}
}
I finally figured it out... this code assumes that the geotif is in wgs84 (4326) proj, but it works well for getting the lat long for each pixel, and the band values for each pixel (formatted as a csv here). Hope this helps.
import com.spatial4j.core.io.GeohashUtils;
import java.awt.geom.Rectangle2D;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.OverviewPolicy;
import org.geotools.gce.geotiff.GeoTiffReader;
import org.geotools.geometry.Envelope2D;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterValue;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.cs.CoordinateSystem;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.Serializable;
public class PixelExtractor implements Serializable {
/**
* returns a pixel as a string with teh following format String outString =
* geoHash + "@" + name + "@" + date + "@" + originalBands
*
* @param f the geotif file
* @param name the name of the file
* @param date the date of information (when the image was captured)
* @param collector
* @throws Exception
*/
public void extract(File f, String name, String date) throws Exception {
ParameterValue<OverviewPolicy> policy = AbstractGridFormat.OVERVIEW_POLICY
.createValue();
policy.setValue(OverviewPolicy.IGNORE);
// this will basically read 4 tiles worth of data at once from the disk...
ParameterValue<String> gridsize = AbstractGridFormat.SUGGESTED_TILE_SIZE.createValue();
//gridsize.setValue(512 * 4 + "," + 512);
// Setting read type: use JAI ImageRead (true) or ImageReaders read methods (false)
ParameterValue<Boolean> useJaiRead = AbstractGridFormat.USE_JAI_IMAGEREAD.createValue();
useJaiRead.setValue(true);
//reader.read(new GeneralParameterValue[] { policy, gridsize, useJaiRead });
GridCoverage2D image
= new GeoTiffReader(f).read(new GeneralParameterValue[]{policy, gridsize, useJaiRead});
Rectangle2D bounds2D = image.getEnvelope2D().getBounds2D();
bounds2D.getCenterX();
// calculate zoom level for the image
GridGeometry2D geometry = image.getGridGeometry();
BufferedImage img = ImageIO.read(f);
// ColorModel colorModel = img.getColorModel(
WritableRaster raster = img.getRaster();
int numBands = raster.getNumBands();
int w = img.getWidth();
int h = img.getHeight();
outer:
for (int i = 0; i < w; i++) {//width...
for (int j = 0; j < h; j++) {
double[] latlon = geo(geometry, i, j);
double lat = latlon[0];
double lon = latlon[1];
Double s = 0d;
String originalBands = "";
for (int k = 0; k < numBands; k++) {
double d = raster.getSampleDouble(i, j, k);
originalBands += d + ",";
s += d;
}
originalBands = originalBands.substring(0, originalBands.length() - 1);
if (s.compareTo(0d) == 0) {
continue;
}
String geoHash = GeohashUtils.encodeLatLon(lat, lon);
//here do something with the bands, lat, long, geohash, etc....
}
}
}
private static double[] geo(GridGeometry2D geometry, int x, int y) throws Exception {
//int zoomlevel = 1;
Envelope2D pixelEnvelop = geometry.gridToWorld(new GridEnvelope2D(x, y, 1, 1));
// pixelEnvelop.getCoordinateReferenceSystem().getName().getCodeSpace();
return new double[]{pixelEnvelop.getCenterY(), pixelEnvelop.getCenterX()};
}
}