001: /*
002: * Geotools2 - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: */
017: package org.geotools.gce.gtopo30;
018:
019: import java.io.BufferedReader;
020: import java.io.File;
021: import java.io.FileReader;
022: import java.io.IOException;
023: import java.net.URL;
024: import java.util.Collection;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.Map;
028:
029: import org.geotools.data.DataSourceException;
030:
031: /**
032: * Class used to parse a GTOPO30 header (.HDR) file
033: *
034: * @author Simone Giannecchini
035: * @author aaime
036: * @author mkraemer
037: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/plugin/gtopo30/src/main/java/org/geotools/gce/gtopo30/GT30Header.java $
038: */
039: final class GT30Header {
040: /** Mnemonic constant for line labels in the header file */
041: public static final String BYTEORDER = "BYTEORDER";
042:
043: /** Mnemonic constant for line labels in the header file */
044: public static final String LAYOUT = "LAYOUT";
045:
046: /** Mnemonic constant for line labels in the header file */
047: public static final String NROWS = "NROWS";
048:
049: /** Mnemonic constant for line labels in the header file */
050: public static final String NCOLS = "NCOLS";
051:
052: /** Mnemonic constant for line labels in the header file */
053: public static final String NBANDS = "NBANDS";
054:
055: /** Mnemonic constant for line labels in the header file */
056: public static final String NBITS = "NBITS";
057:
058: /** Mnemonic constant for line labels in the header file */
059: public static final String BANDROWBYTES = "BANDROWBYTES";
060:
061: /** Mnemonic constant for line labels in the header file */
062: public static final String TOTALROWBYTES = "TOTALROWBYTES";
063:
064: /** Mnemonic constant for line labels in the header file */
065: public static final String BANDGAPBYTES = "BANDGAPBYTES";
066:
067: /** Mnemonic constant for line labels in the header file */
068: public static final String NODATA = "NODATA";
069:
070: /** Mnemonic constant for line labels in the header file */
071: public static final String ULXMAP = "ULXMAP";
072:
073: /** Mnemonic constant for line labels in the header file */
074: public static final String ULYMAP = "ULYMAP";
075:
076: /** Mnemonic constant for line labels in the header file */
077: public static final String XDIM = "XDIM";
078:
079: /** Mnemonic constant for line labels in the header file */
080: public static final String YDIM = "YDIM";
081:
082: /** The standard cell size of GTOPO30 files */
083: private static final double STD_CELL_SIZE = 0.00833333333333;
084:
085: /**
086: * A map for fast and convenient retrivial of the properties contained in
087: * the header file
088: *
089: */
090: private Map propertyMap;
091:
092: /**
093: * Creates a new instance of GTOPO30Header
094: *
095: * @param headerURL
096: * URL of a GTOPO30 header (.HDR) file
097: *
098: * @throws IOException
099: * if some problem is encountered reading the file
100: * @throws DataSourceException
101: * for problems related to the file content
102: */
103: public GT30Header(final URL headerURL) throws IOException {
104: final String path = headerURL.getFile();
105: final File header = new File(java.net.URLDecoder.decode(path,
106: "UTF-8"));
107:
108: final BufferedReader reader = new BufferedReader(
109: new FileReader(header));
110: propertyMap = initMap();
111:
112: parseHeaderFile(this .propertyMap, reader);
113:
114: if (!fullPropertySet(this .propertyMap)) {
115: throw new IOException(
116: "Needed properties missing in GTOPO30 header file");
117: }
118:
119: // freeing
120: reader.close();
121: }
122:
123: /**
124: * Returns a property value
125: *
126: * @param property
127: * use mnemonic constants
128: *
129: * @return the property value or null if the passed property is not
130: * recognized
131: */
132: public Object getProperty(final String property) {
133: return this .propertyMap.get(property);
134: }
135:
136: /**
137: * Returns a string representing the byte order of the data file
138: *
139: * @return a string representing the byte order of the data file
140: */
141: public String getByteOrder() {
142: return (String) this .propertyMap.get(BYTEORDER);
143: }
144:
145: /**
146: * Layout of the binary file (see gtopo30 file format description)
147: *
148: * @return a String describing the binary layour
149: */
150: public String getLayout() {
151: return (String) this .propertyMap.get(LAYOUT);
152: }
153:
154: /**
155: * Returns the number of rows in the file
156: *
157: * @return the number of rows in the file
158: */
159: public int getNRows() {
160: return ((Integer) this .propertyMap.get(NROWS)).intValue();
161: }
162:
163: /**
164: * Returns the number of columns in the file
165: *
166: * @return the number of columns in the file
167: */
168: public int getNCols() {
169: return ((Integer) this .propertyMap.get(NCOLS)).intValue();
170: }
171:
172: /**
173: * Return the number of bands. Warning: official GTOPO30 files just have one
174: * band
175: *
176: * @return the number of bands
177: */
178: public int getNBands() {
179: return ((Integer) this .propertyMap.get(NBANDS)).intValue();
180: }
181:
182: /**
183: * Returns the number of bits used to encode a cell
184: *
185: * @return the number of bits per cell
186: */
187: public int getNBits() {
188: return ((Integer) this .propertyMap.get(NBITS)).intValue();
189: }
190:
191: /**
192: * Returns the number of bytes per row in a band
193: *
194: * @return the number of bytes per row in a band
195: */
196: public int getBandRowBytes() {
197: return ((Integer) this .propertyMap.get(BANDROWBYTES))
198: .intValue();
199: }
200:
201: /**
202: * Returns the number of bytes per row
203: *
204: * @return the number of bytes per row
205: */
206: public int getRowBytes() {
207: return ((Integer) this .propertyMap.get(TOTALROWBYTES))
208: .intValue();
209: }
210:
211: /**
212: * Returns the number of gap bytes used to separate bands, if any
213: *
214: * @return the number of gap bytes used to separate bands
215: */
216: public int getBandGapBytes() {
217: return ((Integer) this .propertyMap.get(BANDGAPBYTES))
218: .intValue();
219: }
220:
221: /**
222: * Returns the value used to represent lack of data (usually -9999)
223: *
224: * @return the value used to represent lack of data
225: */
226: public int getNoData() {
227: return ((Integer) this .propertyMap.get(NODATA)).intValue();
228: }
229:
230: /**
231: * Returns the x coordinate (latitude) of the tile center
232: *
233: * @return the x coordinate of the tile center
234: */
235: public double getULXMap() {
236: return ((Double) this .propertyMap.get(ULXMAP)).doubleValue();
237: }
238:
239: /**
240: * Returns the y coordinate (longitude) of the tile center
241: *
242: * @return the y coordinate of the tile center
243: */
244: public double getULYMap() {
245: return ((Double) this .propertyMap.get(ULYMAP)).doubleValue();
246: }
247:
248: /**
249: * Returns the width of the tile in degrees
250: *
251: * @return the width of the tile in degrees
252: */
253: public double getXDim() {
254: return ((Double) this .propertyMap.get(XDIM)).doubleValue();
255: }
256:
257: /**
258: * Returns the height of the tile in degrees
259: *
260: * @return the height of the tile in degrees
261: */
262: public double getYDim() {
263: return ((Double) this .propertyMap.get(YDIM)).doubleValue();
264: }
265:
266: /**
267: * Initializes the map with the known properties, makes it easier to parse
268: * the file
269: *
270: * @return the initialized map
271: */
272: private Map initMap() {
273: Map map = new HashMap();
274: map.put(BYTEORDER, "M");
275: map.put(LAYOUT, "BIL");
276: map.put(NROWS, null);
277: map.put(NCOLS, null);
278: map.put(NBANDS, null);
279: map.put(NBITS, null);
280: map.put(BANDROWBYTES, null);
281: map.put(TOTALROWBYTES, null);
282: map.put(BANDGAPBYTES, new Integer(0));
283: map.put(NODATA, new Integer(0));
284: map.put(ULXMAP, null);
285: map.put(ULYMAP, null);
286: map.put(XDIM, new Double(STD_CELL_SIZE));
287: map.put(YDIM, new Double(STD_CELL_SIZE));
288:
289: return map;
290: }
291:
292: /**
293: * Parses the reader for the known properties
294: *
295: * @param properties
296: * the map to be filled in
297: * @param reader
298: * the source data
299: *
300: * @throws IOException
301: * for reading errors
302: * @throws DataSourceException
303: * for unrecoverable data format violations
304: */
305: private void parseHeaderFile(final Map properties,
306: final BufferedReader reader) throws IOException {
307: String currLine = reader.readLine();
308: String key = null;
309: String value = null;
310: Class propClass = null;
311:
312: while (currLine != null) {
313: // remove uneeded spaces
314: currLine = currLine.trim();
315:
316: // get key and value
317: int firstSpaceIndex = currLine.indexOf(' ');
318:
319: if (firstSpaceIndex == -1) {
320: throw new IOException(
321: "Illegal line in GTOPO30 header file");
322: }
323:
324: key = currLine.substring(0, firstSpaceIndex).toUpperCase();
325: value = currLine.substring(firstSpaceIndex).trim();
326:
327: // be tolerant about unknown keys, all we need is a subset of the
328: // knows keys, the others will be discarded
329: if (properties.containsKey(key)) {
330: propClass = getPropertyClass(key);
331:
332: try {
333: if (propClass == String.class) {
334: properties.put(key, value);
335: } else if (propClass == Integer.class) {
336: properties.put(key, Integer.valueOf(value));
337: } else if (propClass == Double.class) {
338: properties.put(key, Double.valueOf(value));
339: }
340: } catch (NumberFormatException nfe) {
341: final IOException ex = new IOException();
342: ex.initCause(nfe);
343: throw ex;
344: }
345: }
346:
347: // read next line
348: currLine = reader.readLine();
349: }
350:
351: // closing the reader
352: reader.close();
353: }
354:
355: /**
356: * Checks wheter all of the properties in the map have been assigned
357: *
358: * @param properties
359: * the property map to be checked
360: *
361: * @return true if the map is filled in with values, false if at least one
362: * value is null
363: */
364: private boolean fullPropertySet(final Map properties) {
365: boolean full = true;
366: final Collection values = properties.values();
367:
368: for (final Iterator it = values.iterator(); it.hasNext();) {
369: if (it.next() == null) {
370: full = false;
371:
372: break;
373: }
374: }
375:
376: return full;
377: }
378:
379: /**
380: * Returns the class of the value associated with a key
381: *
382: * @param key
383: * The key used to insert the class into the map
384: *
385: * @return the class of the value associated to the passed key
386: */
387: private Class getPropertyClass(final String key) {
388: Class propClass = null;
389:
390: if (key.equals(BYTEORDER) || key.equals(LAYOUT)) {
391: propClass = String.class;
392: } else if (key.equals(ULXMAP) || key.equals(ULYMAP)
393: || key.equals(XDIM) || key.equals(YDIM)) {
394: propClass = Double.class;
395: } else {
396: propClass = Integer.class;
397: }
398:
399: return propClass;
400: }
401: }
|