/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2006-2008, 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.gtopo30; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.net.URL; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.geotools.data.DataSourceException; import org.geotools.data.DataUtilities; /** * Class used to parse a GTOPO30 header (.HDR) file * * @author Simone Giannecchini * @author aaime * @author mkraemer * @source $URL$ */ final class GT30Header { /** Mnemonic constant for line labels in the header file */ public static final String BYTEORDER = "BYTEORDER"; /** Mnemonic constant for line labels in the header file */ public static final String LAYOUT = "LAYOUT"; /** Mnemonic constant for line labels in the header file */ public static final String NROWS = "NROWS"; /** Mnemonic constant for line labels in the header file */ public static final String NCOLS = "NCOLS"; /** Mnemonic constant for line labels in the header file */ public static final String NBANDS = "NBANDS"; /** Mnemonic constant for line labels in the header file */ public static final String NBITS = "NBITS"; /** Mnemonic constant for line labels in the header file */ public static final String BANDROWBYTES = "BANDROWBYTES"; /** Mnemonic constant for line labels in the header file */ public static final String TOTALROWBYTES = "TOTALROWBYTES"; /** Mnemonic constant for line labels in the header file */ public static final String BANDGAPBYTES = "BANDGAPBYTES"; /** Mnemonic constant for line labels in the header file */ public static final String NODATA = "NODATA"; /** Mnemonic constant for line labels in the header file */ public static final String ULXMAP = "ULXMAP"; /** Mnemonic constant for line labels in the header file */ public static final String ULYMAP = "ULYMAP"; /** Mnemonic constant for line labels in the header file */ public static final String XDIM = "XDIM"; /** Mnemonic constant for line labels in the header file */ public static final String YDIM = "YDIM"; /** The standard cell size of GTOPO30 files */ private static final double STD_CELL_SIZE = 0.00833333333333; /** * A map for fast and convenient retrieval of the properties contained in * the header file * */ private Map<String, Object> propertyMap = new HashMap<String, Object>(); /** * Creates a new instance of GTOPO30Header * * @param headerURL * URL of a GTOPO30 header (.HDR) file * * @throws IOException * if some problem is encountered reading the file * @throws DataSourceException * for problems related to the file content */ public GT30Header(final URL headerURL) throws IOException { final File header = DataUtilities.urlToFile(headerURL); BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(header)); initMap(); parseHeaderFile(reader); if (!fullPropertySet(this.propertyMap)) { throw new DataSourceException( "Needed properties missing in GTOPO30 header file"); } } finally { if (reader != null) try { // freeing reader.close(); } catch (Exception e1) { //TODO log me } } } /** * Returns a property value * * @param property * use mnemonic constants * * @return the property value or null if the passed property is not * recognized */ public Object getProperty(final String property) { return this.propertyMap.get(property); } /** * Returns a string representing the byte order of the data file * * @return a string representing the byte order of the data file */ public String getByteOrder() { return (String) this.propertyMap.get(BYTEORDER); } /** * Layout of the binary file (see gtopo30 file format description) * * @return a String describing the binary layour */ public String getLayout() { return (String) this.propertyMap.get(LAYOUT); } /** * Returns the number of rows in the file * * @return the number of rows in the file */ public int getNRows() { return (Integer) this.propertyMap.get(NROWS); } /** * Returns the number of columns in the file * * @return the number of columns in the file */ public int getNCols() { return (Integer) this.propertyMap.get(NCOLS); } /** * Return the number of bands. Warning: official GTOPO30 files just have one * band * * @return the number of bands */ public int getNBands() { return ((Integer) this.propertyMap.get(NBANDS)).intValue(); } /** * Returns the number of bits used to encode a cell * * @return the number of bits per cell */ public int getNBits() { return (Integer) this.propertyMap.get(NBITS); } /** * Returns the number of bytes per row in a band * * @return the number of bytes per row in a band */ public int getBandRowBytes() { return (Integer) this.propertyMap.get(BANDROWBYTES); } /** * Returns the number of bytes per row * * @return the number of bytes per row */ public int getRowBytes() { return (Integer) this.propertyMap.get(TOTALROWBYTES); } /** * Returns the number of gap bytes used to separate bands, if any * * @return the number of gap bytes used to separate bands */ public int getBandGapBytes() { return (Integer) this.propertyMap.get(BANDGAPBYTES); } /** * Returns the value used to represent lack of data (usually -9999) * * @return the value used to represent lack of data */ public int getNoData() { return (Integer) this.propertyMap.get(NODATA); } /** * Returns the x coordinate (latitude) of the tile center * * @return the x coordinate of the tile center */ public double getULXMap() { return (Double) this.propertyMap.get(ULXMAP); } /** * Returns the y coordinate (longitude) of the tile center * * @return the y coordinate of the tile center */ public double getULYMap() { return (Double) this.propertyMap.get(ULYMAP); } /** * Returns the width of the tile in degrees * * @return the width of the tile in degrees */ public double getXDim() { return (Double) this.propertyMap.get(XDIM); } /** * Returns the height of the tile in degrees * * @return the height of the tile in degrees */ public double getYDim() { return (Double) this.propertyMap.get(YDIM); } /** * Initializes the map with the known properties, makes it easier to parse * the file * * @return the initialized map */ private void initMap() { propertyMap.put(BYTEORDER, "M"); propertyMap.put(LAYOUT, "BIL"); propertyMap.put(NROWS, null); propertyMap.put(NCOLS, null); propertyMap.put(NBANDS, null); propertyMap.put(NBITS, null); propertyMap.put(BANDROWBYTES, null); propertyMap.put(TOTALROWBYTES, null); propertyMap.put(BANDGAPBYTES, new Integer(0)); propertyMap.put(NODATA, new Integer(0)); propertyMap.put(ULXMAP, null); propertyMap.put(ULYMAP, null); propertyMap.put(XDIM, new Double(STD_CELL_SIZE)); propertyMap.put(YDIM, new Double(STD_CELL_SIZE)); } /** * Parses the reader for the known properties * * @param properties * the map to be filled in * @param reader * the source data * * @throws IOException * for reading errors * @throws DataSourceException * for unrecoverable data format violations */ @SuppressWarnings("unchecked") private void parseHeaderFile(final BufferedReader reader) throws IOException { String currLine = reader.readLine(); while (currLine != null) { // remove uneeded spaces currLine = currLine.trim(); // get key and value int firstSpaceIndex = currLine.indexOf(' '); if (firstSpaceIndex == -1) { throw new IOException("Illegal line in GTOPO30 header file"); } final String key = currLine.substring(0, firstSpaceIndex).toUpperCase(); final String value = currLine.substring(firstSpaceIndex).trim(); // be tolerant about unknown keys, all we need is a subset of the // knows keys, the others will be discarded if (propertyMap.containsKey(key)) { final Class propClass = getPropertyClass(key); try { if (propClass == String.class) { propertyMap.put(key, value); } else if (propClass == Integer.class) { propertyMap.put(key, Integer.valueOf(value)); } else if (propClass == Double.class) { propertyMap.put(key, Double.valueOf(value)); } } catch (NumberFormatException nfe) { final IOException ex = new IOException(); ex.initCause(nfe); throw ex; } } // read next line currLine = reader.readLine(); } // closing the reader reader.close(); } /** * Checks wheter all of the properties in the map have been assigned * * @param properties * the property map to be checked * * @return true if the map is filled in with values, false if at least one * value is null */ private static boolean fullPropertySet(final Map<String,Object> properties) { boolean full = true; final Collection<Object> values = properties.values(); for (final Iterator<Object> it = values.iterator(); it.hasNext();) { if (it.next() == null) { full = false; break; } } return full; } /** * Returns the class of the value associated with a key * * @param key * The key used to insert the class into the map * * @return the class of the value associated to the passed key */ private static Class<?> getPropertyClass(final String key) { Class<?> propClass = null; if (key.equals(BYTEORDER) || key.equals(LAYOUT)) { propClass = String.class; } else if (key.equals(ULXMAP) || key.equals(ULYMAP) || key.equals(XDIM) || key.equals(YDIM)) { propClass = Double.class; } else { propClass = Integer.class; } return propClass; } }