/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2006-2011, Open Source Geospatial Foundation (OSGeo)
* (C) 2010, HydroloGIS S.r.l.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.gce.grassraster;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.ComponentSampleModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.awt.image.renderable.ParameterBlock;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import javax.media.jai.Interpolation;
import javax.media.jai.JAI;
import javax.media.jai.RasterFactory;
import javax.media.jai.iterator.RandomIterFactory;
import javax.media.jai.iterator.RectIter;
import javax.media.jai.iterator.RectIterFactory;
import javax.media.jai.iterator.WritableRandomIter;
import org.geotools.coverage.CoverageFactoryFinder;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridCoverageFactory;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.InvalidGridGeometryException;
import org.geotools.geometry.Envelope2D;
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.CRS;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
/**
* <p>
* A facade of often used methods by the JGrass engine
* </p>
*
* @author Andrea Antonello - www.hydrologis.com
* @since 1.1.0
*
* @source $URL: http://svn.osgeo.org/geotools/trunk/modules/plugin/grassraster/src/main/java/org/geotools/gce/grassraster/JGrassUtilities.java $
*/
public class JGrassUtilities {
public static final String NORTH = "NORTH"; //$NON-NLS-1$
public static final String SOUTH = "SOUTH"; //$NON-NLS-1$
public static final String WEST = "WEST"; //$NON-NLS-1$
public static final String EAST = "EAST"; //$NON-NLS-1$
public static final String XRES = "XRES"; //$NON-NLS-1$
public static final String YRES = "YRES"; //$NON-NLS-1$
public static final String ROWS = "ROWS"; //$NON-NLS-1$
public static final String COLS = "COLS"; //$NON-NLS-1$
public static Interpolation interpolation = Interpolation.getInstance(Interpolation.INTERP_NEAREST);
/**
* Returns the list of files involved in the raster map issues. If for example a map has to be
* deleted, then all these files have to.
*
* @param mapsetPath - the path of the mapset
* @param mapname -the name of the map
* @return the array of strings containing the full path to the involved files
*/
public static boolean checkRasterMapConsistence( String mapsetPath, String mapname ) {
File file = null;
File file2 = null;
file = new File(mapsetPath + File.separator + JGrassConstants.FCELL + File.separator + mapname);
file2 = new File(mapsetPath + File.separator + JGrassConstants.CELL + File.separator + mapname);
// the map is in one of the two
if (!file.exists() && !file2.exists())
return false;
/*
* helper files
*/
file = new File(mapsetPath + File.separator + JGrassConstants.CELLHD + File.separator + mapname);
if (!file.exists())
return false;
// it is important that the folder cell_misc/mapname comes before the
// files in it
file = new File(mapsetPath + File.separator + JGrassConstants.CELL_MISC + File.separator + mapname);
if (!file.exists())
return false;
return true;
}
/**
* create a buffered image from a set of color triplets
*
* @param data
* @param width
* @param height
* @return
*/
public static BufferedImage ByteBufferImage( byte[] data, int width, int height ) {
int[] bandoffsets = {0, 1, 2, 3};
DataBufferByte dbb = new DataBufferByte(data, data.length);
WritableRaster wr = Raster.createInterleavedRaster(dbb, width, height, width * 4, 4, bandoffsets, null);
int[] bitfield = {8, 8, 8, 8};
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorModel cm = new ComponentColorModel(cs, bitfield, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
return new BufferedImage(cm, wr, false, null);
}
public static Envelope reprojectEnvelopeByEpsg( int srcEpsg, int destEpsg, Envelope srcEnvelope ) throws FactoryException,
TransformException {
CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:" + srcEpsg); //$NON-NLS-1$
CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:" + destEpsg); //$NON-NLS-1$
MathTransform tr = CRS.findMathTransform(sourceCRS, targetCRS);
// From that point, I'm not sure which kind of object is returned by
// getLatLonBoundingBox(). But there is some convenience methods if CRS
// like:
return JTS.transform(srcEnvelope, tr);
}
/**
* return the rectangle of the cell of the active region, that surrounds the given coordinates
*
* @param activeRegion
* @param x the given easting coordinate
* @param y given northing coordinate
* @return the rectangle localizing the cell inside which the x and y stay
*/
public static JGrassRegion getRectangleAroundPoint( JGrassRegion activeRegion, double x, double y ) {
double minx = activeRegion.getRectangle().getBounds2D().getMinX();
double ewres = activeRegion.getWEResolution();
double snapx = minx + (Math.round((x - minx) / ewres) * ewres);
double miny = activeRegion.getRectangle().getBounds2D().getMinY();
double nsres = activeRegion.getNSResolution();
double snapy = miny + (Math.round((y - miny) / nsres) * nsres);
double xmin = 0.0;
double xmax = 0.0;
double ymin = 0.0;
double ymax = 0.0;
if (x >= snapx) {
xmin = snapx;
xmax = xmin + ewres;
} else {
xmax = snapx;
xmin = xmax - ewres;
}
if (y <= snapy) {
ymax = snapy;
ymin = ymax - nsres;
} else {
ymin = snapy;
ymax = ymin + nsres;
}
// why do I have to put ymin, Rectangle requires the upper left
// corner?????!!!! Is it a BUG
// in the Rectangle2D class? or docu?
// Rectangle2D rect = new Rectangle2D.Double(xmin, ymin, ewres, nsres);
return new JGrassRegion(xmin, xmax, ymin, ymax, 1, 1);
}
// /**
// * This method gives the possibility to supply a point through coordinates
// and gain the value
// of
// * a certain supplied map in that point (a kind of d.what.rast)
// *
// * @param mapName - the map of which we want to know the value ("@mapset")
// * @param copt
// * @param rasterFormat - the raster format from where to read from
// * @param coordinates - the point in which we want to know the value
// (x=easting, y=northing)
// * @return
// */
// public static String getWhatValueFromMap( String mapName, Point2D
// coordinates ) {
//
// Rectangle2D pixelBound =
// dataMask.snapRectangleToClickedCell(coordinates.getX(),
// coordinates.getY());
//
// Window active = new Window(pixelBound.getMinX(), pixelBound.getMaxX(),
// pixelBound.getMinY(), pixelBound.getMaxY(), 1, 1);
//
// // define the map
// MapReader reader = MapIOFactory.CreateRasterMapReader(rasterFormat);
// RasterMap theMap = (RasterMap) copt.g.getLocation().getMaps(reader,
// mapName).elementAt(0); //
// get
// // the
// // rastermap
// theMap.setReader(rasterFormat); // the type of reader has to be set
// theMap.setNovalue(new java.lang.Double(FluidConstants.fluidnovalue)); //
// the
// // novalue
// // has to be
// // set
// theMap.setDataWindow(active); // the calculation region is set
// theMap.setOutputMatrixType(0); // set the matrix type
// theMap.setOutputDataObject(new double[0][0]); // data type
//
// // read elevation
// double[][] elevation = null;
// if (!theMap.openMap()) {
// JOptionPane.showMessageDialog(null, "Error! Problems opening map...");
// return "NaN";
// }
// while( theMap.hasMoreData() ) {
// elevation = (double[][]) theMap.getNextData();
// }
//
// if (elevation[0][0] == FluidConstants.fluidnovalue)
// return "null";
// return String.valueOf(elevation[0][0]);
// }
//
// /**
// * When we click on the map the chosen point will be somewhere inside the
// hit cell. It is a
// * matter of rounding the northing and easting of the point to get the
// right row and column
// that
// * contains the hit. Therefore this method returns the row and col in
// which the point falls.
// *
// * @param loc - the location
// * @param coordinates - the coordinates of the hit point
// * @return the reviewed point as row, col
// */
// public static Point putClickToCenterOfCell( Window active, Point2D
// coordinates ) {
// double eastingClick = coordinates.getX();
// double northingClick = coordinates.getY();
//
// double startnorth = active.getNorth();
// double startwest = active.getWest();
// double northdelta = active.getNSResolution();
// double westdelta = active.getWEResolution();
// int clickrow = 0;
// int clickcol = 0;
//
// for( int i = 0; i < active.getRows(); i++ ) {
// startnorth = startnorth - northdelta;
// if (northingClick > startnorth) {
// clickrow = i;
// break;
// }
// }
// for( int i = 0; i < active.getCols(); i++ ) {
// startwest = startwest + westdelta;
// if (eastingClick < startwest) {
// clickcol = i;
// break;
// }
// }
//
// return new Point(clickrow, clickcol);
// }
//
/**
* <p>
* Transforms row and column index of the active region into the regarding northing and easting
* coordinates. The center of the cell is taken.
* </p>
* <p>
* NOTE: basically the inverse of
* {@link JGrassUtilities#coordinateToNearestRowCol(JGrassRegion, Coordinate)}
* </p>
*
* @param active - the active region (can be null)
* @param row - row number of the point to transform
* @param col - column number of the point to transform
* @return the point in N/E coordinates of the supplied row and column
*/
public static Coordinate rowColToCenterCoordinates( JGrassRegion active, int row, int col ) {
double north = active.getNorth();
double west = active.getWest();
double nsres = active.getNSResolution();
double ewres = active.getWEResolution();
double northing = north - row * nsres - nsres / 2.0;
double easting = west + col * ewres + ewres / 2.0;
return new Coordinate(easting, northing);
}
/**
* <p>
* Return the row and column of the active region matrix for a give coordinate *
* </p>
* <p>
* NOTE: basically the inverse of
* {@link JGrassUtilities#rowColToCenterCoordinates(JGrassRegion, int, int)}
* </p>
*
* @param active the active region
* @param coord
* @return and int array containing row and col
*/
public static int[] coordinateToNearestRowCol( JGrassRegion active, Coordinate coord ) {
double easting = coord.x;
double northing = coord.y;
int[] rowcol = new int[2];
if (easting > active.getEast() || easting < active.getWest() || northing > active.getNorth()
|| northing < active.getSouth()) {
return null;
}
double minx = active.getWest();
double ewres = active.getWEResolution();
for( int i = 0; i < active.getCols(); i++ ) {
minx = minx + ewres;
if (easting < minx) {
rowcol[1] = i;
break;
}
}
double maxy = active.getNorth();
double nsres = active.getNSResolution();
for( int i = 0; i < active.getRows(); i++ ) {
maxy = maxy - nsres;
if (northing > maxy) {
rowcol[0] = i;
break;
}
}
return rowcol;
}
// /**
// * Given the mapsetpath and the mapname, the map is removed with all its accessor files
// *
// * @param mapsetPath
// * @param mapName
// */
// public static boolean removeGrassRasterMap( String mapsetPath, String mapName ) {
//
// // list of files to remove
// String mappaths[] = filesOfRasterMap(mapsetPath, mapName);
//
// // first delete the list above, which are just files
// for( int j = 0; j < mappaths.length; j++ ) {
// File filetoremove = new File(mappaths[j]);
// if (filetoremove.exists()) {
// if (!FileUtilities.deleteFileOrDir(filetoremove)) {
// return false;
// }
// }
// }
// return true;
// }
/**
* Returns the list of files involved in the raster map issues. If for example a map has to be
* deleted, then all these files have to.
*
* @param mapsetPath - the path of the mapset
* @param mapname -the name of the map
* @return the array of strings containing the full path to the involved files
*/
public static String[] filesOfRasterMap( String mapsetPath, String mapname ) {
String filesOfRaster[] = new String[]{
mapsetPath + File.separator + JGrassConstants.FCELL + File.separator + mapname,
mapsetPath + File.separator + JGrassConstants.CELL + File.separator + mapname,
mapsetPath + File.separator + JGrassConstants.CATS + File.separator + mapname,
mapsetPath + File.separator + JGrassConstants.HIST + File.separator + mapname,
mapsetPath + File.separator + JGrassConstants.CELLHD + File.separator + mapname,
mapsetPath + File.separator + JGrassConstants.COLR + File.separator + mapname,
// it is very important that the folder cell_misc/mapname comes
// before the files in it
mapsetPath + File.separator + JGrassConstants.CELL_MISC + File.separator + mapname,
mapsetPath + File.separator + JGrassConstants.CELL_MISC + File.separator + mapname + File.separator
+ JGrassConstants.CELLMISC_FORMAT,
mapsetPath + File.separator + JGrassConstants.CELL_MISC + File.separator + mapname + File.separator
+ JGrassConstants.CELLMISC_QUANT,
mapsetPath + File.separator + JGrassConstants.CELL_MISC + File.separator + mapname + File.separator
+ JGrassConstants.CELLMISC_RANGE,
mapsetPath + File.separator + JGrassConstants.CELL_MISC + File.separator + mapname + File.separator
+ JGrassConstants.CELLMISC_NULL};
return filesOfRaster;
}
/**
* Transforms row and column index of the active region into an array of the coordinates of the
* edgaes, i.e. n, s, e, w
*
* @param active - the active region (can be null)
* @param row - row number of the point to transform
* @param col - column number of the point to transform
* @return the array of north, south, east, west
*/
public static double[] rowColToNodeboundCoordinates( JGrassRegion active, int row, int col ) {
double anorth = active.getNorth();
double awest = active.getWest();
double nsres = active.getNSResolution();
double ewres = active.getWEResolution();
double[] nsew = new double[4];
nsew[0] = anorth - row * nsres;
nsew[1] = anorth - row * nsres - nsres;
nsew[2] = awest + col * ewres + ewres;
nsew[3] = awest + col * ewres;
return nsew;
}
public static int factorial( int n ) {
int fact = 1;
for( int i = 1; i <= n; i++ ) {
fact *= i;
}
return fact;
}
public static void makeColorRulesPersistent( File colrFile, List<String> rules, double[] minMax, int alpha )
throws IOException {
if (!colrFile.getParentFile().exists()) {
colrFile.getParentFile().mkdir();
}
BufferedWriter bw = new BufferedWriter(new FileWriter(colrFile));
if (rules.size() == 0) {
throw new IllegalArgumentException("The list of colorrules can't be empty.");
}
String header = "% " + minMax[0] + " " + minMax[1] + " " + alpha;
bw.write(header + "\n");
for( String r : rules ) {
bw.write(r + "\n");
}
bw.close();
}
/**
* Calculates optimal tile size for the actual free memory.
*
* @param rows the rows of the complete image the tiles are calculated for.
* @param cols the cols of the complete image the tiles are calculated for.
* @return
*/
public static int[] getTilesBasedOnFreeMemory( int rows, int cols ) {
long freeMemory = Runtime.getRuntime().freeMemory();
int tileSizeY = 256;
int tileSizeX = 256;
if (freeMemory > 8L * cols) {
tileSizeX = cols;
tileSizeY = (int) (freeMemory / 8) / cols;
if (tileSizeY > rows) {
tileSizeY = rows;
}
}
return new int[]{tileSizeX, tileSizeY};
}
public static JGrassRegion getJGrassRegionFromGridCoverage( GridCoverage2D gridCoverage2D )
throws InvalidGridGeometryException, TransformException {
Envelope2D env = gridCoverage2D.getEnvelope2D();
GridEnvelope2D worldToGrid = gridCoverage2D.getGridGeometry().worldToGrid(env);
double xRes = env.getWidth() / worldToGrid.getWidth();
double yRes = env.getHeight() / worldToGrid.getHeight();
JGrassRegion region = new JGrassRegion(env.getMinX(), env.getMaxX(), env.getMinY(), env.getMaxY(), xRes, yRes);
return region;
}
public static RenderedImage scaleJAIImage( int requestedCols, int requestedRows, RenderedImage translatedImage,
Interpolation interpolation ) {
if (interpolation == null) {
interpolation = JGrassUtilities.interpolation;
}
ParameterBlock block = new ParameterBlock();
block.addSource(translatedImage);
block.add((float) requestedCols / (float) translatedImage.getWidth());
block.add((float) requestedRows / (float) translatedImage.getHeight());
// this is the translation, we have set to 0, an alternative is to put
// the value
// of the
// above operation but the result is different because this operation
// use a
// special
// formula.
block.add(0F);
block.add(0F);
block.add(interpolation);
return JAI.create("scale", block);
}
/**
* Creates a {@link GridCoverage2D coverage} from a double[][] matrix and the necessary geographic Information.
*
* @param name the name of the coverage.
* @param dataMatrix the matrix containing the data.
* @param n
* @param s
* @param w
* @param e
* @param crs the {@link CoordinateReferenceSystem}.
* @param matrixIsRowCol a flag to tell if the matrix has rowCol or colRow order.
* @return the {@link GridCoverage2D coverage}.
*/
public static GridCoverage2D buildCoverage( String name, double[][] dataMatrix, double n, double s, double w, double e,
CoordinateReferenceSystem crs, boolean matrixIsRowCol ) {
WritableRaster writableRaster = createWritableRasterFromMatrix(dataMatrix, matrixIsRowCol);
Envelope2D writeEnvelope = new Envelope2D(crs, w, s, e - w, n - s);
GridCoverageFactory factory = CoverageFactoryFinder.getGridCoverageFactory(null);
GridCoverage2D coverage2D = factory.create(name, writableRaster, writeEnvelope);
return coverage2D;
}
/**
* Create a {@link WritableRaster} from a double matrix.
*
* @param matrix the matrix to take the data from.
* @param matrixIsRowCol a flag to tell if the matrix has rowCol or colRow order.
* @return the produced raster.
*/
public static WritableRaster createWritableRasterFromMatrix( double[][] matrix, boolean matrixIsRowCol ) {
int height = matrix.length;
int width = matrix[0].length;
if (!matrixIsRowCol) {
int tmp = height;
height = width;
width = tmp;
}
WritableRaster writableRaster = createDoubleWritableRaster(width, height, null, null, null);
WritableRandomIter disckRandomIter = RandomIterFactory.createWritable(writableRaster, null);
for( int x = 0; x < width; x++ ) {
for( int y = 0; y < height; y++ ) {
if (matrixIsRowCol) {
disckRandomIter.setSample(x, y, 0, matrix[y][x]);
} else {
disckRandomIter.setSample(x, y, 0, matrix[x][y]);
}
}
}
disckRandomIter.done();
return writableRaster;
}
/**
* Creates a {@link WritableRaster writable raster}.
*
* @param width width of the raster to create.
* @param height height of the raster to create.
* @param dataClass data type for the raster. If <code>null</code>, defaults to double.
* @param sampleModel the samplemodel to use. If <code>null</code>, defaults to
* <code>new ComponentSampleModel(dataType, width, height, 1, width, new int[]{0});</code>.
* @param value value to which to set the raster to. If null, the default of the raster creation is
* used, which is 0.
* @return a {@link WritableRaster writable raster}.
*/
public static WritableRaster createDoubleWritableRaster( int width, int height, Class< ? > dataClass,
SampleModel sampleModel, Double value ) {
int dataType = DataBuffer.TYPE_DOUBLE;
if (dataClass != null) {
if (dataClass.isAssignableFrom(Integer.class)) {
dataType = DataBuffer.TYPE_INT;
} else if (dataClass.isAssignableFrom(Float.class)) {
dataType = DataBuffer.TYPE_FLOAT;
} else if (dataClass.isAssignableFrom(Byte.class)) {
dataType = DataBuffer.TYPE_BYTE;
}
}
if (sampleModel == null) {
sampleModel = new ComponentSampleModel(dataType, width, height, 1, width, new int[]{0});
}
WritableRaster raster = RasterFactory.createWritableRaster(sampleModel, null);
if (value != null) {
// autobox only once
double v = value;
for( int y = 0; y < height; y++ ) {
for( int x = 0; x < width; x++ ) {
raster.setSample(x, y, 0, v);
}
}
}
return raster;
}
public static void printImage( GridCoverage2D coverage2D ) {
RenderedImage renderedImage = coverage2D.getRenderedImage();
printImage(renderedImage);
}
public static void printImage( RenderedImage renderedImage ) {
RectIter rectIter = RectIterFactory.create(renderedImage, null);
int y = 0;
do {
int x = 0;
do {
double value = rectIter.getSampleDouble();
System.out.print(value + " ");
x++;
} while( !rectIter.nextPixelDone() );
rectIter.startPixels();
y++;
System.out.println();
} while( !rectIter.nextLineDone() );
}
}