/* * 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() ); } }