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.awt.Color;
020: import java.awt.Dimension;
021: import java.awt.Rectangle;
022: import java.awt.RenderingHints;
023: import java.awt.Transparency;
024: import java.awt.color.ColorSpace;
025: import java.awt.image.ColorModel;
026: import java.awt.image.ComponentColorModel;
027: import java.awt.image.DataBuffer;
028: import java.awt.image.SampleModel;
029: import java.awt.image.renderable.ParameterBlock;
030: import java.io.BufferedReader;
031: import java.io.File;
032: import java.io.FileReader;
033: import java.io.IOException;
034: import java.io.UnsupportedEncodingException;
035: import java.net.MalformedURLException;
036: import java.net.URL;
037: import java.net.URLDecoder;
038: import java.nio.ByteOrder;
039: import java.text.ParseException;
040: import java.util.Collections;
041: import java.util.HashMap;
042: import java.util.Map;
043: import java.util.logging.Level;
044: import java.util.logging.Logger;
045:
046: import javax.imageio.ImageIO;
047: import javax.imageio.ImageReadParam;
048: import javax.imageio.ImageTypeSpecifier;
049: import javax.imageio.stream.ImageInputStream;
050: import javax.media.jai.ImageLayout;
051: import javax.media.jai.JAI;
052: import javax.media.jai.RenderedOp;
053: import javax.units.Unit;
054: import javax.units.UnitFormat;
055:
056: import org.geotools.coverage.Category;
057: import org.geotools.coverage.GridSampleDimension;
058: import org.geotools.coverage.grid.GeneralGridRange;
059: import org.geotools.coverage.grid.GridCoverage2D;
060: import org.geotools.coverage.grid.GridGeometry2D;
061: import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
062: import org.geotools.coverage.grid.io.AbstractGridFormat;
063: import org.geotools.data.DataSourceException;
064: import org.geotools.factory.Hints;
065: import org.geotools.geometry.GeneralEnvelope;
066: import org.geotools.parameter.Parameter;
067: import org.geotools.referencing.CRS;
068: import org.geotools.referencing.ReferencingFactoryFinder;
069: import org.geotools.referencing.crs.DefaultGeographicCRS;
070: import org.geotools.referencing.factory.FactoryGroup;
071: import org.geotools.resources.image.ImageUtilities;
072: import org.geotools.util.NumberRange;
073: import org.opengis.coverage.grid.Format;
074: import org.opengis.coverage.grid.GridCoverageReader;
075: import org.opengis.parameter.GeneralParameterValue;
076: import org.opengis.parameter.ParameterValueGroup;
077: import org.opengis.referencing.FactoryException;
078: import org.opengis.referencing.crs.CoordinateReferenceSystem;
079: import org.opengis.referencing.cs.CartesianCS;
080: import org.opengis.referencing.operation.MathTransformFactory;
081: import org.opengis.referencing.operation.TransformException;
082: import org.opengis.geometry.Envelope;
083:
084: import com.sun.media.imageio.stream.RawImageInputStream;
085: import com.sun.media.imageioimpl.plugins.raw.RawImageReader;
086: import com.sun.media.imageioimpl.plugins.raw.RawImageReaderSpi;
087:
088: /**
089: * This class provides a GridCoverageReader for the GTopo30Format.
090: *
091: * @author Simone Giannecchini
092: * @author jeichar
093: * @author mkraemer
094: * @source $URL:
095: * http://svn.geotools.org/geotools/trunk/gt/plugin/gtopo30/src/org/geotools/gce/gtopo30/GTopo30Reader.java $
096: */
097: public final class GTopo30Reader extends AbstractGridCoverage2DReader
098: implements GridCoverageReader {
099:
100: /** Logger. */
101: private final static Logger LOGGER = org.geotools.util.logging.Logging
102: .getLogger("org.geotools.gce.gtopo30");
103: /**
104: * Cached {@link ImageIO} SPI for creating instances of
105: * {@link RawImageReader}.
106: */
107: private final static RawImageReaderSpi imageIOSPI = new RawImageReaderSpi();
108:
109: private final static String dmext = ".dem";
110:
111: private final static String dhext = ".hdr";
112:
113: private final static String srext = ".src";
114:
115: private final static String shext = ".sch";
116:
117: private final static String stext = ".stx";
118:
119: private final static String prjext = ".prj";
120:
121: /** Dem data header URL */
122: private final URL demURL;
123:
124: /** Dem statistics file URL */
125: private final URL statsURL;
126:
127: /** Projection file. */
128: private URL prjURL;
129:
130: /** The header for this GTOPO30 file. */
131: private final GT30Header header;
132:
133: /** The file holding the statistics for this GTOPO30 file. */
134: private final GT30Stats stats;
135:
136: /** The {@link URL} that points to the file to use. */
137: private URL urlToUse;
138:
139: /** URL of the header file. */
140: private final URL demHeaderURL;
141:
142: /**
143: * GTopo30Reader constructor.
144: *
145: * @param source
146: * The source object (can be a File, an URL or a String
147: * representing a File or an URL).
148: * @throws MalformedURLException
149: * if the URL does not correspond to one of the GTopo30 files
150: * @throws IOException
151: * @throws DataSourceException
152: * if the given url points to an unrecognized file
153: * @throws IllegalArgumentException
154: * DOCUMENT ME!
155: */
156: public GTopo30Reader(final Object source) throws IOException {
157: this (source, null);
158:
159: }
160:
161: /**
162: * GTopo30Reader constructor.
163: *
164: * @param source
165: * The source object (can be a File, an URL or a String
166: * representing a File or an URL).
167: * @throws MalformedURLException
168: * if the URL does not correspond to one of the GTopo30 files
169: * @throws IOException
170: * @throws DataSourceException
171: * if the given url points to an unrecognized file
172: * @throws IllegalArgumentException
173: * DOCUMENT ME!
174: */
175: public GTopo30Reader(final Object source, final Hints hints)
176: throws IOException {
177: if (source == null) {
178: throw new DataSourceException(
179: "GTopo30Reader:No source set to read this coverage.");
180: }
181: if (source instanceof File) {
182: urlToUse = ((File) source).toURL();
183: } else if (source instanceof URL) {
184: // we only allow files
185: urlToUse = (URL) source;
186: } else if (source instanceof String) {
187: try {
188: // is it a filename?
189: urlToUse = new File((String) source).toURL();
190: } catch (MalformedURLException e) {
191: // is it a URL
192: urlToUse = new URL((String) source);
193: }
194: } else {
195: throw new IllegalArgumentException(
196: "Illegal input argument!");
197: }
198: if (hints != null)
199: this .hints.add(hints);
200: this .source = source;
201: coverageName = "gtopo30_coverage";
202: // ///////////////////////////////////////////////////////////
203: //
204: // decoding source
205: //
206: // ///////////////////////////////////////////////////////////
207: final String filename;
208:
209: try {
210: filename = URLDecoder.decode(urlToUse.getFile(), "UTF-8");
211: } catch (UnsupportedEncodingException use) {
212: MalformedURLException exception = new MalformedURLException(
213: new StringBuffer("Unable to decode ").append(
214: urlToUse).append(" cause ").append(
215: use.getMessage()).toString());
216: exception.initCause(exception);
217: throw exception;
218: }
219:
220: boolean recognized = false;
221: boolean extUpperCase = false;
222:
223: if (filename.endsWith(dmext) || filename.endsWith(dhext)
224: || filename.endsWith(srext) || filename.endsWith(shext)
225: || filename.endsWith(stext)
226: || filename.endsWith(prjext)) {
227: recognized = true;
228: } else {
229:
230: if (filename.endsWith(dmext.toUpperCase())
231: || filename.endsWith(dhext.toUpperCase())
232: || filename.endsWith(srext.toUpperCase())
233: || filename.endsWith(shext.toUpperCase())
234: || filename.endsWith(stext.toUpperCase())
235: || filename.endsWith(prjext.toUpperCase())) {
236: recognized = true;
237: extUpperCase = true;
238: }
239: }
240:
241: if (!recognized) {
242: throw new IOException(
243: "Unrecognized file (file extension doesn't match)");
244: }
245:
246: this .coverageName = filename
247: .substring(0, filename.length() - 4);
248: demURL = new URL(urlToUse, this .coverageName
249: + (!extUpperCase ? dmext : dmext.toUpperCase()));
250: prjURL = new URL(urlToUse, this .coverageName
251: + (!extUpperCase ? prjext : prjext.toUpperCase()));
252: demHeaderURL = new URL(urlToUse, this .coverageName
253: + (!extUpperCase ? dhext : dhext.toUpperCase()));
254: statsURL = new URL(urlToUse, this .coverageName
255: + (!extUpperCase ? stext : stext.toUpperCase()));
256:
257: // ///////////////////////////////////////////////////////////
258: //
259: // Reading header and statistics
260: //
261: // ///////////////////////////////////////////////////////////
262: header = new GT30Header(demHeaderURL);
263: // get information from the header
264: originalGridRange = new GeneralGridRange(new Rectangle(0, 0,
265: header.getNCols(), header.getNRows()));
266: stats = new GT30Stats(this .statsURL);
267:
268: // ///////////////////////////////////////////////////////////
269: //
270: // Build the coordinate system and the envelope
271: //
272: // ///////////////////////////////////////////////////////////
273: final Object tempCRS = this .hints
274: .get(Hints.DEFAULT_COORDINATE_REFERENCE_SYSTEM);
275: if (tempCRS != null) {
276: this .crs = (CoordinateReferenceSystem) tempCRS;
277: LOGGER.log(Level.WARNING, new StringBuffer(
278: "Using forced coordinate reference system ")
279: .append(crs.toWKT()).toString());
280: } else
281: crs = initCRS();
282: this .originalEnvelope = getBounds(crs);
283:
284: // /////////////////////////////////////////////////////////////////////
285: //
286: // Compute source Resolution
287: //
288: // /////////////////////////////////////////////////////////////////////
289: highestRes = getResolution(originalEnvelope, new Rectangle(0,
290: 0, header.getNCols(), header.getNRows()), crs);
291: numOverviews = 0;
292: overViewResolutions = null;
293: }
294:
295: /**
296: * @see org.opengis.coverage.grid.GridCoverageReader#getFormat()
297: */
298: public Format getFormat() {
299: return new GTopo30Format();
300: }
301:
302: /**
303: * @see org.opengis.coverage.grid.GridCoverageReader#read(org.opengis.parameter.GeneralParameterValue[])
304: */
305: public org.opengis.coverage.grid.GridCoverage read(
306: final GeneralParameterValue[] params)
307: throws java.lang.IllegalArgumentException,
308: java.io.IOException {
309: // /////////////////////////////////////////////////////////////////////
310: //
311: // do we have paramters to use for reading from the specified source
312: //
313: // /////////////////////////////////////////////////////////////////////
314: GeneralEnvelope requestedEnvelope = null;
315: Rectangle dim = null;
316: if (params != null) {
317: // /////////////////////////////////////////////////////////////////////
318: //
319: // Checking params
320: //
321: // /////////////////////////////////////////////////////////////////////
322: if (params != null) {
323: Parameter param;
324: final int length = params.length;
325: for (int i = 0; i < length; i++) {
326: param = (Parameter) params[i];
327:
328: if (param
329: .getDescriptor()
330: .getName()
331: .getCode()
332: .equals(
333: AbstractGridFormat.READ_GRIDGEOMETRY2D
334: .getName().toString())) {
335: final GridGeometry2D gg = (GridGeometry2D) param
336: .getValue();
337: requestedEnvelope = new GeneralEnvelope(
338: (Envelope) gg.getEnvelope2D());
339: dim = gg.getGridRange2D().getBounds();
340: }
341: }
342: }
343: }
344:
345: // /////////////////////////////////////////////////////////////////////
346: //
347: // Building the required coverage
348: //
349: // /////////////////////////////////////////////////////////////////////
350: return getGridCoverage(requestedEnvelope, dim);
351: }
352:
353: /**
354: * Gets the bounding box of this datasource using the default speed of this
355: * datasource as set by the implementer.
356: *
357: * @param lonFirst
358: *
359: * @return The bounding box of the datasource or null if unknown and too
360: * expensive for the method to calculate.
361: *
362: * @throws IOException
363: *
364: */
365: private GeneralEnvelope getBounds(CoordinateReferenceSystem crs)
366: throws IOException {
367: GeneralEnvelope env = new GeneralEnvelope(
368: new double[] { 0, 0 }, new double[] { 0, 0 });
369:
370: // preparing data for the envelope
371: final double xULC = header.getULXMap();
372: final double yULC = header.getULYMap();
373: final double xDim = header.getXDim();// dx
374: final double yDim = header.getYDim();// dy
375: final int imageWidth = header.getNCols();
376: final int imageHeight = header.getNRows();
377: final double longMin;
378: final double latMax;
379: final double longMax;
380: final double latMin;
381:
382: longMin = xULC - xDim / 2.0;
383: latMax = yULC + yDim / 2.0;
384: longMax = longMin + imageWidth * xDim;
385: latMin = latMax - imageHeight * yDim;
386:
387: // longitude
388: env.setRange(0, longMin, longMax);
389: // latitude
390: env.setRange(1, latMin, latMax);
391:
392: env.setCoordinateReferenceSystem(crs);
393:
394: return env;
395: }
396:
397: /**
398: * Retrieves a grid coverage based on the DEM assoicated to this gtopo
399: * coverage. The color palette is fixed and there is no possibility for the
400: * final user to change it.
401: *
402: * @param dim
403: * @param requestedEnvelope
404: *
405: * @return the GridCoverage object
406: *
407: * @throws DataSourceException
408: * if an error occurs
409: */
410: private GridCoverage2D getGridCoverage(
411: GeneralEnvelope requestedEnvelope, Rectangle dim)
412: throws IOException {
413: int hrWidth = originalGridRange.getLength(0);
414: int hrHeight = originalGridRange.getLength(1);
415:
416: // /////////////////////////////////////////////////////////////////////
417: //
418: // Setting subsampling factors with some checkings
419: // 1) the subsampling factors cannot be zero
420: // 2) the subsampling factors cannot be such that the w or h are zero
421: //
422: // /////////////////////////////////////////////////////////////////////
423: final ImageReadParam readP = new ImageReadParam();
424: final Integer imageChoice;
425: try {
426: imageChoice = setReadParams(readP, requestedEnvelope, dim);
427: } catch (IOException e) {
428: LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
429: return null;
430: } catch (TransformException e) {
431: LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
432: return null;
433: }
434:
435: // /////////////////////////////////////////////////////////////////////
436: //
437: // Statistics
438: //
439: // /////////////////////////////////////////////////////////////////////
440: final int max = stats.getMax();
441: final int min = stats.getMin();
442:
443: // /////////////////////////////////////////////////////////////////////
444: //
445: // Preparing to load
446: //
447: // /////////////////////////////////////////////////////////////////////
448: // trying to create a channel to the file to read
449: final String filePath = URLDecoder.decode(
450: this .demURL.getFile(), "UTF-8");
451: final ImageInputStream iis = ImageIO
452: .createImageOutputStream(new File(filePath));
453: if (header.getByteOrder().compareToIgnoreCase("M") == 0) {
454: iis.setByteOrder(ByteOrder.BIG_ENDIAN);
455: } else {
456: iis.setByteOrder(ByteOrder.LITTLE_ENDIAN);
457: }
458:
459: // Prepare temporaray colorModel and sample model, needed to build the
460: // RawImageInputStream
461: final ColorModel cm = new ComponentColorModel(ColorSpace
462: .getInstance(ColorSpace.CS_GRAY), false, false,
463: Transparency.OPAQUE, DataBuffer.TYPE_SHORT);
464: final SampleModel sm = cm.createCompatibleSampleModel(hrWidth,
465: hrHeight);
466: final ImageTypeSpecifier its = new ImageTypeSpecifier(cm, sm);
467: // Finally, build the image input stream
468: final RawImageInputStream raw = new RawImageInputStream(iis,
469: its, new long[] { 0 }, new Dimension[] { new Dimension(
470: hrWidth, hrHeight) });
471:
472: // building the final image layout
473: final Dimension tileSize = ImageUtilities
474: .toTileSize(new Dimension(hrWidth, hrHeight));
475: final ImageLayout il = new ImageLayout(0, 0, hrWidth
476: / readP.getSourceXSubsampling(), hrHeight
477: / readP.getSourceYSubsampling(), 0, 0, (int) tileSize
478: .getWidth(), (int) tileSize.getHeight(), sm, cm);
479:
480: // First operator: read the image
481: final RenderingHints hints = new RenderingHints(
482: JAI.KEY_IMAGE_LAYOUT, il);
483: final ParameterBlock pbjImageRead = new ParameterBlock();
484: pbjImageRead.add(raw);
485: pbjImageRead.add(imageChoice);
486: pbjImageRead.add(Boolean.FALSE);
487: pbjImageRead.add(Boolean.FALSE);
488: pbjImageRead.add(Boolean.FALSE);
489: pbjImageRead.add(null);
490: pbjImageRead.add(null);
491: pbjImageRead.add(readP);
492: pbjImageRead.add(imageIOSPI.createReaderInstance());
493: RenderedOp image = JAI.create("ImageRead", pbjImageRead, hints);
494:
495: // sample dimension for this coverage
496: final GridSampleDimension band = getSampleDimension(max, min);
497:
498: // setting metadata
499: final Map metadata = new HashMap();
500: metadata.put("maximum", new Double(stats.getMax()));
501: metadata.put("minimum", new Double(stats.getMin()));
502: metadata.put("mean", new Double(stats.getAverage()));
503: metadata.put("std_dev", new Double(stats.getStdDev()));
504: metadata.put("nodata", new Double(-9999.0));
505:
506: // /////////////////////////////////////////////////////////////////////
507: //
508: // Creating coverage
509: //
510: // /////////////////////////////////////////////////////////////////////
511: // cleaning name
512: String coverageName = (new File(this .coverageName)).getName();
513: final int extension = coverageName.lastIndexOf(".");
514: if (extension != -1) {
515: String ext = coverageName.substring(extension + 1);
516:
517: if ((dmext.compareToIgnoreCase(ext) == 0)
518: || (dhext.compareToIgnoreCase(ext) == 0)
519: || (srext.compareToIgnoreCase(ext) == 0)
520: || (shext.compareToIgnoreCase(ext) == 0)
521: || (stext.compareToIgnoreCase(ext) == 0)) {
522: coverageName = coverageName.substring(0, extension);
523: }
524: }
525:
526: // return the coverage
527: return (GridCoverage2D) coverageFactory.create(coverageName,
528: image, new GeneralEnvelope(originalEnvelope),
529: new GridSampleDimension[] { band }, null, metadata);
530: }
531:
532: /**
533: * This method is responsible for the creation of the CRS for this GTOPO30.
534: * The possible options are two, EPSG:4326 and POlar Stereographc. Inc ase
535: * an error occurs the default CRS is chosen.
536: *
537: * @return CoordinateReferenceSystem a CRS for this coverage.
538: * @throws IOException
539: * @throws FactoryException
540: */
541: private CoordinateReferenceSystem initCRS() {
542: try {
543: // getting a reader
544: final BufferedReader reader = new BufferedReader(
545: new FileReader(prjURL.getFile()));
546:
547: // reading the first line to see if I need to read it all
548: final StringBuffer buffer = new StringBuffer(reader
549: .readLine());
550:
551: if (buffer != null) {
552: String line = buffer.toString().trim();
553:
554: if (!line.endsWith("POLAR")
555: && !line.endsWith("GEOGRAPHIC")) {
556: // in case I have a wkt string a need to read it all
557: while ((line = reader.readLine()) != null)
558: buffer.append(line);
559: }
560: }
561: // closing the reader
562: reader.close();
563: // getting the content
564: final String crsDescription = buffer.toString().trim();
565: final DefaultGeographicCRS geoCRS = (DefaultGeographicCRS) CRS
566: .decode("EPSG:4326", true);
567: if (crsDescription != null) {
568: if (crsDescription.endsWith("POLAR")) {
569: // we need to build a polar stereographic crs based on wgs
570: // 84. I am not so sure about the parameters I used. we
571: // should check them again
572:
573: final CartesianCS cartCS = org.geotools.referencing.cs.DefaultCartesianCS.PROJECTED;
574: final MathTransformFactory mtFactory = ReferencingFactoryFinder
575: .getMathTransformFactory(null);
576: final ParameterValueGroup parameters = mtFactory
577: .getDefaultParameters("Polar_Stereographic");
578: parameters.parameter("central_meridian").setValue(
579: 0.0);
580: parameters.parameter("latitude_of_origin")
581: .setValue(-71.0);
582: parameters.parameter("scale_factor").setValue(1);
583: parameters.parameter("false_easting").setValue(0.0);
584: parameters.parameter("false_northing")
585: .setValue(0.0);
586: final FactoryGroup factories = FactoryGroup
587: .createInstance(null);
588: final Map properties = Collections.singletonMap(
589: "name",
590: "WGS 84 / Antartic Polar Stereographic");
591:
592: return factories.createProjectedCRS(properties,
593: geoCRS, null, parameters, cartCS);
594: }
595:
596: if (crsDescription.endsWith("GEOGRAPHIC")) {
597: // in case I do not have a polar stereographic I build my
598: // own CRS using either the supplied wkt
599: // description or, in case none is supplied, a custom
600: // Geographic WGS84 with lon, lat axes.
601: return geoCRS;
602: }
603: return CRS.parseWKT(crsDescription);
604: }
605: } catch (IOException e) {
606: // do nothing and return a default CRS but write down a message
607: LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
608:
609: } catch (FactoryException e) {
610: // do nothing and return a default CRS but write down a message
611: LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
612: }
613:
614: final CoordinateReferenceSystem crs = AbstractGridFormat
615: .getDefaultCRS();
616: LOGGER
617: .info(new StringBuffer(
618: "PRJ file not found, proceeding with EPSG:4326 as follows: ")
619: .append(crs.toWKT()).toString());
620: return crs;
621: }
622:
623: /**
624: * This method was implemented in order to reformat the input data to double
625: * to introduce NaN as NoData instead of using -9999 since such a value for
626: * NoData is automatically recognized from the SampleDimension code in order
627: * to build the No Data category. This method has been used during tests in
628: * order to try the creation of a GTOPO30 file from a floating point
629: * coverage.
630: *
631: * @param max
632: * Max value in the input data
633: * @param min
634: * Min Value in the input data
635: *
636: * @return the reformatted image.
637: */
638:
639: // final private RenderedOp reFormat2Float(RenderedOp image, int min, int
640: // max) {
641: //
642: // //number of elements in the lookup table
643: // final int numElem=max+9999+1;
644: // //offset is -9999
645: // final double lookup[]=new double[numElem];
646: // //changing NaN
647: // lookup[0]=Double.NaN;
648: // for(int i=min+9999;i<=max+9999;i++){
649: // lookup[i]=i-9999;
650: // }
651: //
652: // //building the lookup table jai
653: // final LookupTableJAI lt= new LookupTableJAI(lookup,-9999);
654: // final ParameterBlockJAI pbj= new ParameterBlockJAI("lookup");
655: // pbj.addSource(image);
656: // pbj.setParameter("table",lt);
657: // return JAI.create("lookup",pbj,new
658: // RenderingHints(JAI.KEY_IMAGE_LAYOUT,new ImageLayout(image)));
659: //
660: //
661: // }
662: /**
663: * The purpose of this method is to build the sample dimensions for this
664: * coverage.
665: *
666: * @param max
667: * Maximum value for this coverage.
668: * @param min
669: * Minimum value for this coverage.
670: *
671: * @return The newly created sample dimensions.
672: */
673: final private GridSampleDimension getSampleDimension(final int max,
674: final int min) {
675: // Create the SampleDimension, with colors and byte transformation
676: // needed for visualization
677: UnitFormat unitFormat = UnitFormat.getStandardInstance();
678: Unit uom = null;
679:
680: try {
681: // unit of measure is meter
682: uom = unitFormat.parseUnit("m");
683: } catch (ParseException ex1) {
684: uom = null;
685: }
686:
687: final Category values = new Category("values", demColors,
688: new NumberRange(1, 255), new NumberRange((short) min,
689: (short) max));
690: final Category nan =
691: // new Category("No data",
692: // new Color(0, 0, 0, 0),0);
693: new Category("No data", new Color[] { new Color(0, 0, 0, 0) },
694: new NumberRange(0, 0), new NumberRange((short) -9999,
695: (short) -9999));
696: final GridSampleDimension band = new GridSampleDimension(
697: "digital elevation", new Category[] { values, nan },
698: uom);
699:
700: return band.geophysics(true);
701: }
702:
703: }
|