/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2006-2010, Open Source Geospatial Foundation (OSGeo) * * 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.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import org.geotools.gce.grassraster.core.color.AttributeTable; import org.geotools.gce.grassraster.core.color.JGrassColorTable; import org.geotools.gce.grassraster.core.color.JlsTokenizer; import org.geotools.gce.grassraster.core.color.AttributeTable.CellAttribute; import org.geotools.gce.grassraster.metadata.GrassBinaryImageMetadata; import org.geotools.gce.grassraster.spi.GrassBinaryImageReaderSpi; import org.geotools.referencing.CRS; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.datum.PixelInCell; /** * Represents the structure around a map inside a grass database. * <p> * Given a map file path, all the related files to that map in the * GRASS Location/Mapset are defined. * </p> * * @author Andrea Antonello (www.hydrologis.com) * @since 3.0 * @see {@link JGrassConstants} * @see {@link JGrassRegion} * * @source $URL: http://svn.osgeo.org/geotools/branches/2.7.x/build/maven/javadoc/../../../modules/unsupported/grassraster/src/main/java/org/geotools/gce/grassraster/JGrassMapEnvironment.java $ */ public class JGrassMapEnvironment { /** * A minimum value to be used when no range is available for a map. */ public static double defaultMapMin = 0.0; /** * A maximum value to be used when no range is available for a map. */ public static double defaultMapMax = 5000.0; /** * The default, always existing Mapset inside a Location. */ private File PERMANENT_MAPSET = null; /** * The Mapset file of the map, through which the * {@linkplain JGrassMapEnvironment} was built. */ private File MAPSET = null; /** * The Location file of the map, through which the * {@linkplain JGrassMapEnvironment} was built. */ private File LOCATION = null; /** * The DEFAULT_WIND file, that keeps the default region informations. */ private File DEFAULT_WIND = null; /** * The PROJ_INFO file, that keeps the projection informations as defined * by GRASS through the g.proj command. */ private File PROJ_INFO = null; /** * The PROJ_WKT file, that keeps the projection informations as defined by * JGRASS to be compatible with geotools. */ private File PROJ_WKT = null; /** * The PROJ_WKT file, that keeps the projection units informations as * defined by GRASS. */ private File PROJ_UNITS = null; /** * The WIND file, that keeps the current active region information, * i.e. the region on which calculations are executed. */ private File WIND = null; /** * The FCELL file, i.e. the file that holds the raw data for floating * value maps. */ private File FCELL = null; /** * The CELL file, i.e. the file that holds the raw data for integer * value maps. This file also always exist since it is placeholder also * for floating point maps. */ private File CELL = null; /** * The CATS file, holding the categories for the map. */ private File CATS = null; /** * The HIST file, holding the history of the map. */ private File HIST = null; /** * The CELLHD file, holding the native file region information of the file. */ private File CELLHD = null; /** * The COLR file, holding the colortable for the map. */ private File COLR = null; /** * The CELLMISC_FORMAT file, holding the format and compression information * for the map. */ private File CELLMISC_FORMAT = null; /** * The CELLMISC_QUANT file, holding quantization rules for passing from * floating points to integers. */ private File CELLMISC_QUANT = null; /** * The CELLMISC_RANGE file, holding the range values for the map. */ private File CELLMISC_RANGE = null; /** * The CELLMISC_NULL file, holding bitmap for novalues for the map. */ private File CELLMISC_NULL = null; /** * The {@linkplain JGrassMapEnvironment} that is created in the case a raster * map was reclassed. */ private JGrassMapEnvironment RECLASSEDENVIRONMENT = null; /** * The name of the map to which the environment refers to. */ private String mapName; private GrassBinaryImageReader coverageReader; private HashMap<String, String> coverageMetadataMap; /** * Constructs an instance of {@linkplain JGrassMapEnvironment}. * * @param cellFile the absolute path to a grass raster map, through which * the environment is defined. */ public JGrassMapEnvironment( File cellFile ) { CELL = cellFile; mapName = CELL.getName(); MAPSET = CELL.getParentFile().getParentFile(); LOCATION = MAPSET.getParentFile(); String permanentFolderPath = LOCATION.getAbsolutePath() + File.separator + JGrassConstants.PERMANENT_MAPSET; PERMANENT_MAPSET = new File(permanentFolderPath); DEFAULT_WIND = new File(permanentFolderPath + File.separator + JGrassConstants.DEFAULT_WIND); PROJ_INFO = new File(permanentFolderPath + File.separator + JGrassConstants.PROJ_INFO); PROJ_WKT = new File(permanentFolderPath + File.separator + JGrassConstants.PROJ_WKT); PROJ_UNITS = new File(permanentFolderPath + File.separator + JGrassConstants.PROJ_UNITS); String mapsetPath = MAPSET.getAbsolutePath(); WIND = new File(mapsetPath + File.separator + JGrassConstants.WIND); FCELL = new File(mapsetPath + File.separator + JGrassConstants.FCELL + File.separator + mapName); CELLHD = new File(mapsetPath + File.separator + JGrassConstants.CELLHD + File.separator + mapName); CATS = new File(mapsetPath + File.separator + JGrassConstants.CATS + File.separator + mapName); COLR = new File(mapsetPath + File.separator + JGrassConstants.COLR + File.separator + mapName); HIST = new File(mapsetPath + File.separator + JGrassConstants.HIST + File.separator + mapName); String cellMiscPath = mapsetPath + File.separator + JGrassConstants.CELL_MISC + File.separator + mapName + File.separator; CELLMISC_NULL = new File(cellMiscPath + JGrassConstants.CELLMISC_NULL); CELLMISC_FORMAT = new File(cellMiscPath + JGrassConstants.CELLMISC_FORMAT); CELLMISC_QUANT = new File(cellMiscPath + JGrassConstants.CELLMISC_QUANT); CELLMISC_RANGE = new File(cellMiscPath + JGrassConstants.CELLMISC_RANGE); } /** * Constructs an instance of {@linkplain JGrassMapEnvironment}. * <p> * This constructor is a facility method in the case the mapset file and the * name of the GRASS raster map are passed. * </p> * * @param mapsetFile file of the mapset path * @param mapName name of the GRASS raster map */ public JGrassMapEnvironment( File mapsetFile, String mapName ) { this(new File(mapsetFile.getAbsolutePath() + File.separator + JGrassConstants.CELL + File.separator + mapName)); } /** * Reclasses the environment. * * <p> * In the case a reclassed map was found, this method has to be called, in * order to set the current environment to the new reclassed one. * </p> * * @param reclassedMapset the name of the mapset holding the reclassed map * @param reclassedMap the name of the reclassed map */ public void setReclassed( String reclassedMapset, String reclassedMap ) { String reclassedCell = LOCATION.getAbsolutePath() + File.separator + reclassedMapset + File.separator + JGrassConstants.CELL + File.separator + reclassedMap; CELL = new File(reclassedCell); mapName = CELL.getName(); MAPSET = CELL.getParentFile().getParentFile(); LOCATION = MAPSET.getParentFile(); String permanentFolderPath = LOCATION.getAbsolutePath() + File.separator + JGrassConstants.PERMANENT_MAPSET; PERMANENT_MAPSET = new File(permanentFolderPath); DEFAULT_WIND = new File(permanentFolderPath + File.separator + JGrassConstants.DEFAULT_WIND); PROJ_INFO = new File(permanentFolderPath + File.separator + JGrassConstants.PROJ_INFO); PROJ_WKT = new File(permanentFolderPath + File.separator + JGrassConstants.PROJ_WKT); PROJ_UNITS = new File(permanentFolderPath + File.separator + JGrassConstants.PROJ_UNITS); String mapsetPath = MAPSET.getAbsolutePath(); WIND = new File(mapsetPath + File.separator + JGrassConstants.WIND); FCELL = new File(mapsetPath + File.separator + JGrassConstants.FCELL + File.separator + mapName); CELLHD = new File(mapsetPath + File.separator + JGrassConstants.CELLHD + File.separator + mapName); CATS = new File(mapsetPath + File.separator + JGrassConstants.CATS + File.separator + mapName); COLR = new File(mapsetPath + File.separator + JGrassConstants.COLR + File.separator + mapName); HIST = new File(mapsetPath + File.separator + JGrassConstants.HIST + File.separator + mapName); String cellMiscPath = mapsetPath + File.separator + JGrassConstants.CELL_MISC + File.separator + mapName + File.separator; CELLMISC_NULL = new File(cellMiscPath + JGrassConstants.CELLMISC_NULL); CELLMISC_FORMAT = new File(cellMiscPath + JGrassConstants.CELLMISC_FORMAT); CELLMISC_QUANT = new File(cellMiscPath + JGrassConstants.CELLMISC_QUANT); CELLMISC_RANGE = new File(cellMiscPath + JGrassConstants.CELLMISC_RANGE); RECLASSEDENVIRONMENT = this; } /** * Getter for mapName. * * @return the map name. */ public String getMapName() { return mapName; } /** * Getter for PERMANENT MAPSET folder. * * @return the PERMANENT MAPSET folder file. */ public File getPERMANENT_MAPSET() { return PERMANENT_MAPSET; } /** * Getter for MAPSET folder. * * @return the MAPSET folder file. */ public File getMAPSET() { return MAPSET; } /** * Getter for LOCATION folder. * * @return the LOCATION file folder. */ public File getLOCATION() { return LOCATION; } /** * Getter for DEFAULT_WIND file. * * @return the DEFAULT_WIND file. */ public File getDEFAULT_WIND() { return DEFAULT_WIND; } /** * Getter for PROJ_INFO file. * * @return the PROJ_INFO file. */ public File getPROJ_INFO() { return PROJ_INFO; } /** * Getter for PROJ_WKT file. * * @return the PROJ_WKT file. */ public File getPROJ_WKT() { return PROJ_WKT; } /** * Getter for PROJ_UNITS file. * * @return the PROJ_UNITS file. */ public File getPROJ_UNITS() { return PROJ_UNITS; } /** * Getter for WIND file. * * @return the WIND file. */ public File getWIND() { return WIND; } /** * Getter for FCELL file. * * @return the FCELL file. */ public File getFCELL() { return FCELL; } /** * Getter for FCELL folder. * * @return the FCELL folder file. */ public File getFcellFolder() { return FCELL.getParentFile(); } /** * Getter for CELL file, i.e. the main map file. * * @return the CELL file. */ public File getCELL() { return CELL; } /** * Getter for the main map file. * * @return the main map file. */ public File getMapFile() { return CELL; } /** * Getter for CELL folder. * * @return the CELL folder file. */ public File getCellFolder() { return CELL.getParentFile(); } /** * Getter for CATS file. * * @return the CATS file. */ public File getCATS() { return CATS; } /** * Getter for CATS folder. * * @return the CATS folder file. */ public File getCatsFolder() { return CATS.getParentFile(); } /** * Getter for the history file. * * @return the HIST file. */ public File getHIST() { return HIST; } /** * Getter for CELLHD file. * * @return the CELLHD file. */ public File getCELLHD() { return CELLHD; } /** * Getter for COLR file. * * @return the COLR file. */ public File getCOLR() { return COLR; } /** * Getter for COLR folder. * * @return the COLR folder file. */ public File getColrFolder() { return COLR.getParentFile(); } /** * Getter for CELLMISC_FORMAT file. * * @return the CELLMISC_FORMAT file. */ public File getCELLMISC_FORMAT() { return CELLMISC_FORMAT; } /** * Getter for CELLMISC_QUANT file. * * @return the CELLMISC_QUANT file. */ public File getCELLMISC_QUANT() { return CELLMISC_QUANT; } /** * Getter for CELLMISC_RANGE file. * * @return the CELLMISC_RANGE file. */ public File getCELLMISC_RANGE() { return CELLMISC_RANGE; } /** * Getter for CELLMISC_NULL file. * * @return the CELLMISC_NULL file. */ public File getCELLMISC_NULL() { return CELLMISC_NULL; } /** * Getter for RECLASSEDENVIRONMENT. * * @return the RECLASSEDENVIRONMENT. */ public JGrassMapEnvironment getRECLASSEDENVIRONMENT() { return RECLASSEDENVIRONMENT; } /** * Read the colorrules for the map wrapped by this {@link JGrassMapEnvironment}. * * @param range the range to use for the default colortable, in the case of * missing color file. Can be null. * @return a {@link List} of color rules in string format. * @throws IOException */ public List<String> getColorRules( double[] range ) throws IOException { if (range == null) { range = new double[]{defaultMapMin, defaultMapMax}; } JGrassColorTable colorTable = new JGrassColorTable(this, range); return colorTable.getColorRules(); } /** * Reads the categories for the map wrapped by this {@link JGrassMapEnvironment}. * * <p> * The categories are returned in a {@link List} of strings that may * be of two types:<br> * <ul> * <li>value:categorytext</li> * <li>value1-value2:categorytext</li> * </ul> * </p> * * @return the list of categories in text format. * @throws IOException */ public List<String> getCategories() throws IOException { List<String> categoriesList = new ArrayList<String>(); /* * File is a standard file where the categories values are stored in * the cats directory. */ BufferedReader rdr = new BufferedReader(new FileReader(getCATS())); try { /* Instantiate attribute table */ AttributeTable attTable = new AttributeTable(); /* Ignore first 4 lines. */ rdr.readLine(); rdr.readLine(); rdr.readLine(); rdr.readLine(); /* Read next n lines */ String line; while( (line = rdr.readLine()) != null ) { /* All lines other than '0:no data' are processed */ // if (line.indexOf("0:no data") == -1) { //$NON-NLS-1$ JlsTokenizer tk = new JlsTokenizer(line, ":"); //$NON-NLS-1$ if (tk.countTokens() == 2) { float f = Float.parseFloat(tk.nextToken()); String att = tk.nextToken().trim(); attTable.addAttribute(f, att); } else if (tk.countTokens() == 3) { float f0 = Float.parseFloat(tk.nextToken()); float f1 = Float.parseFloat(tk.nextToken()); String att = tk.nextToken().trim(); attTable.addAttribute(f0, f1, att); } // } } Enumeration<CellAttribute> categories = attTable.getCategories(); while( categories.hasMoreElements() ) { AttributeTable.CellAttribute object = categories.nextElement(); categoriesList.add(object.toString()); } } finally { rdr.close(); } return categoriesList; } /** * Read the {@link JGrassRegion} from the active region file. * * @return the active grass region. * @throws IOException */ public JGrassRegion getActiveRegion() throws IOException { JGrassRegion jGrassRegion = new JGrassRegion(getWIND().getAbsolutePath()); return jGrassRegion; } /** * Reads the data range from a color table file, if existing. * * @return the data range or null if no range could be read. * @throws IOException */ public double[] getRangeFromColorTable() throws IOException { double[] dataRange = new double[2]; JGrassColorTable colorTable = new JGrassColorTable(this, null); List<String> rules = colorTable.getColorRules(); if (rules.size() == 0) { return null; } for( int i = 0; i < rules.size(); i++ ) { String rule = rules.get(i); double[] values = new double[2]; JGrassColorTable.parseColorRule(rule, values, null); if (i == 0) { dataRange[0] = values[0]; } if (i == rules.size() - 1) { dataRange[1] = values[1]; } } if (dataRange == null || Double.isNaN(dataRange[0]) || Double.isInfinite(dataRange[0]) || Double.isNaN(dataRange[1]) || Double.isInfinite(dataRange[1])) { return null; } return dataRange; } /** * Reads the data range from the GRASS range file. * * @return the data range or null if the content is infinite or NaN. * @throws IOException */ public double[] getRangeFromRangeFile() throws IOException { double[] dataRange = null; /* * first check if there is a range file available */ File rangeFile = getCELLMISC_RANGE(); // if the file exists, read the range. if (rangeFile.exists()) { dataRange = new double[2]; InputStream is = new FileInputStream(rangeFile); byte[] numbers = new byte[16]; int testread = is.read(numbers); is.close(); if (testread == 16) { ByteBuffer rangeBuffer = ByteBuffer.wrap(numbers); dataRange[0] = rangeBuffer.getDouble(); dataRange[1] = rangeBuffer.getDouble(); } } if (dataRange == null || Double.isNaN(dataRange[0]) || Double.isInfinite(dataRange[0]) || Double.isNaN(dataRange[1]) || Double.isInfinite(dataRange[1])) { return null; } return dataRange; } /** * Reads the data range by reading the map. * * @return the data range. * @throws IOException */ public double[] getRangeFromMapScan() throws IOException { /* * if the range file doesn't exist, the only way is to scan the whole map. */ GrassCoverageReader coverageReader = new GrassCoverageReader(getCELL()); coverageReader.setParams(PixelInCell.CELL_CENTER, null, false, false, null); coverageReader.read(null); double[] dataRange = coverageReader.getRange(); // write the range to disk OutputStream cell_miscRangeStream = new FileOutputStream(getCELLMISC_RANGE()); cell_miscRangeStream.write(double2bytearray(dataRange[0])); cell_miscRangeStream.write(double2bytearray(dataRange[1])); cell_miscRangeStream.close(); return dataRange; } /** * Getter for the legend string. * * @return the legendstring. */ public String getLegendString() throws IOException { checkReader(); String legendString = coverageMetadataMap .get(GrassBinaryImageMetadata.COLOR_RULES_DESCRIPTOR); return legendString; } /** * Getter for the categories string. * * @return the categories string. */ public String getCategoriesString() throws IOException { checkReader(); String categoriesString = coverageMetadataMap .get(GrassBinaryImageMetadata.CATEGORIES_DESCRIPTOR); return categoriesString; } /** * Read the {@link CoordinateReferenceSystem crs} from the location. * * @return the crs of the location containing the map. * @throws Exception */ public CoordinateReferenceSystem getCoordinateReferenceSystem() throws Exception { File projWtkFile = getPROJ_WKT(); if (projWtkFile.exists()) { BufferedReader crsReader = new BufferedReader(new FileReader(projWtkFile)); StringBuffer wtkString = new StringBuffer(); try { String line = null; while( (line = crsReader.readLine()) != null ) { wtkString.append(line.trim()); } } finally { crsReader.close(); } CoordinateReferenceSystem readCrs = null; try { readCrs = CRS.parseWKT(wtkString.toString()); } catch (FactoryException e) { throw new IOException(e.getLocalizedMessage()); } return readCrs; } else { return null; } } /** * Read the file region of the map. * * @return the {@link JGrassRegion} of the file. * @throws IOException */ public JGrassRegion getFileRegion() throws IOException { // checkReader(); // double fileNorth = Double.parseDouble(coverageMetadataMap // .get(GrassBinaryImageMetadata.NORTH)); // double fileSouth = Double.parseDouble(coverageMetadataMap // .get(GrassBinaryImageMetadata.SOUTH)); // double fileEast = Double // .parseDouble(coverageMetadataMap.get(GrassBinaryImageMetadata.EAST)); // double fileWest = Double // .parseDouble(coverageMetadataMap.get(GrassBinaryImageMetadata.WEST)); // int fileRows = Integer.parseInt(coverageMetadataMap.get(GrassBinaryImageMetadata.NROWS)); // int fileCols = Integer.parseInt(coverageMetadataMap.get(GrassBinaryImageMetadata.NCOLS)); // JGrassRegion fileRegion = new JGrassRegion(fileWest, fileEast, fileSouth, fileNorth, // fileRows, fileCols); File cellhdFile = getCELLHD(); JGrassRegion fileRegion = new JGrassRegion(cellhdFile.getAbsolutePath()); return fileRegion; } private void checkReader() throws IOException { if (coverageReader == null) { coverageReader = new GrassBinaryImageReader(new GrassBinaryImageReaderSpi()); coverageReader.setInput(getCELL()); coverageMetadataMap = ((GrassBinaryImageMetadata) coverageReader.getImageMetadata(0)) .toHashMap(); } } private static byte[] double2bytearray( double doubleValue ) { long l = Double.doubleToLongBits(doubleValue); byte[] b = new byte[8]; int shift = 64 - 8; for( int k = 0; k < 8; k++, shift -= 8 ) { b[k] = (byte) (l >>> shift); } return b; } }