001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-2006, 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: package org.geotools.data.coverage.grid;
017:
018: import java.awt.Color;
019: import java.awt.Rectangle;
020: import java.awt.color.ColorSpace;
021: import java.awt.geom.AffineTransform;
022: import java.awt.geom.Rectangle2D;
023: import java.awt.image.ColorModel;
024: import java.awt.image.DataBuffer;
025: import java.awt.image.IndexColorModel;
026: import java.io.IOException;
027: import java.text.ParseException;
028: import java.util.HashMap;
029: import java.util.logging.Level;
030: import java.util.logging.Logger;
031:
032: import javax.imageio.ImageReadParam;
033: import javax.imageio.ImageReader;
034: import javax.imageio.stream.ImageInputStream;
035: import javax.media.jai.IHSColorSpace;
036: import javax.media.jai.PlanarImage;
037: import javax.units.Unit;
038: import javax.units.UnitFormat;
039:
040: import org.geotools.coverage.Category;
041: import org.geotools.coverage.FactoryFinder;
042: import org.geotools.coverage.GridSampleDimension;
043: import org.geotools.coverage.grid.GeneralGridRange;
044: import org.geotools.coverage.grid.GridCoverage2D;
045: import org.geotools.coverage.grid.GridCoverageFactory;
046: import org.geotools.data.DataSourceException;
047: import org.geotools.factory.Hints;
048: import org.geotools.geometry.GeneralEnvelope;
049: import org.geotools.referencing.operation.BufferedCoordinateOperationFactory;
050: import org.geotools.referencing.operation.transform.LinearTransform1D;
051: import org.geotools.resources.CRSUtilities;
052: import org.geotools.util.NumberRange;
053: import org.geotools.util.logging.Logging;
054: import org.opengis.coverage.MetadataNameNotFoundException;
055: import org.opengis.coverage.grid.GridCoverage;
056: import org.opengis.coverage.grid.GridCoverageReader;
057: import org.opengis.coverage.grid.GridRange;
058: import org.opengis.referencing.FactoryException;
059: import org.opengis.referencing.crs.CoordinateReferenceSystem;
060: import org.opengis.referencing.operation.CoordinateOperationFactory;
061: import org.opengis.referencing.operation.MathTransform;
062: import org.opengis.referencing.operation.TransformException;
063:
064: /**
065: * This class is a first attempt for providing a way to get more informations
066: * out of a single 2D raster datasets (x,y). It is worth to remark that for the
067: * moment this is thought for 2D rasters not for 3D or 4D rasters (x,y,z,t).
068: *
069: * <p>
070: * The main drawback I see with the current GeoApi GridCoverageReader interface
071: * is that there is no way to get real information about a raster source unless
072: * you instantiate a GridCoverage. As an instance it is impossible to know the
073: * envelope, the number of overviews, the tile size. This information is needed
074: * in order to perform decimation on reading or to use built-in overviews<br>
075: * This really impacts the ability to exploit raster datasets in a desktop
076: * environment where caching is crucial.
077: *
078: * @author Simone Giannecchini
079: * @since 2.3
080: * @version 0.2
081: * @deprecated use {@link org.geotools.coverage.grid.io.AbstractGridCoverage2DReader} instead.
082: */
083: public abstract class AbstractGridCoverage2DReader implements
084: GridCoverageReader {
085:
086: /** The {@link Logger} for this {@link AbstractGridCoverage2DReader}. */
087: private final static Logger LOGGER = Logging
088: .getLogger("org.geotools.data.coverage.grid");
089:
090: /** Caches a default GridCoverageFactory for usage in plugins. */
091: protected final static GridCoverageFactory coverageFactory = FactoryFinder
092: .getGridCoverageFactory(null);
093:
094: protected static final double EPS = 1E-6;
095:
096: /** Buffered factory for coordinate operations. */
097: protected final static CoordinateOperationFactory operationFactory = new BufferedCoordinateOperationFactory(
098: new Hints(Hints.LENIENT_DATUM_SHIFT, Boolean.TRUE));
099:
100: /**
101: * Default color ramp. Preset colors used to generate an Image from the raw
102: * data
103: */
104: protected final static Color[] demColors = new Color[] {
105: new Color(5, 90, 5), new Color(150, 200, 150),
106: new Color(190, 150, 20), new Color(100, 100, 50),
107: new Color(200, 210, 220), Color.WHITE, Color.WHITE,
108: Color.WHITE, Color.WHITE };
109:
110: /**
111: * This contains the maximum number of grid coverages in the file/stream.
112: * Until multi-image files are supported, this is going to be 0 or 1.
113: */
114: protected volatile int numOverviews = 0;
115:
116: /** 2DGridToWorld math transform. */
117: protected MathTransform raster2Model = null;
118:
119: /** crs for this coverage */
120: protected CoordinateReferenceSystem crs = null;
121:
122: /** Envelope read from file */
123: protected GeneralEnvelope originalEnvelope = null;
124:
125: /** Coverage name */
126: protected String coverageName = "geotools_coverage";
127:
128: /** Source to read from */
129: protected Object source = null;
130:
131: /** Hints used by the {@link AbstractGridCoverage2DReader} subclasses. */
132: protected Hints hints = new Hints(new HashMap(5));
133:
134: /**
135: * Highest resolution availaible for this reader.
136: */
137: protected double[] highestRes = null;
138:
139: /** Temp variable used in many readers. */
140: protected boolean closeMe;
141:
142: /**
143: * In case we are trying to read from a GZipped file this will be set to
144: * true.
145: */
146: protected boolean gzipped;
147:
148: /**
149: * The original {@link GridRange} for the {@link GridCoverage2D} of this
150: * reader.
151: */
152: protected GeneralGridRange originalGridRange = null;
153:
154: /**
155: * Input stream that can be used to initialize subclasses of
156: * {@link AbstractGridCoverage2DReader}.
157: */
158: protected ImageInputStream inStream = null;
159:
160: /** Resolutions avialaible through an overviews based mechanism. */
161: protected double[][] overViewResolutions = null;
162:
163: // -------------------------------------------------------------------------
164: //
165: // old support methods
166: //
167: // -------------------------------------------------------------------------
168: /**
169: * This method is responsible for preparing the read param for doing an
170: * {@link ImageReader#read(int, ImageReadParam)}.
171: *
172: *
173: * <p>
174: * This method is responsible for preparing the read param for doing an
175: * {@link ImageReader#read(int, ImageReadParam)}. It sets the passed
176: * {@link ImageReadParam} in terms of decimation on reading using the
177: * provided requestedEnvelope and requestedDim to evaluate the needed
178: * resolution. It also returns and {@link Integer} representing the index of
179: * the raster to be read when dealing with multipage raster.
180: *
181: * @param readP
182: * an instance of {@link ImageReadParam} for setting the
183: * subsampling factors.
184: * @param requestedEnvelope
185: * the {@link GeneralEnvelope} we are requesting.
186: * @param requestedDim
187: * the requested dimensions.
188: * @return the index of the raster to read in the underlying data source.
189: * @throws IOException
190: * @throws TransformException
191: */
192: protected Integer setReadParams(ImageReadParam readP,
193: GeneralEnvelope requestedEnvelope, Rectangle requestedDim)
194: throws IOException, TransformException {
195:
196: readP.setSourceSubsampling(1, 1, 0, 0);// default values for
197: // subsampling
198: // //
199: //
200: // Default image index 0
201: //
202: // //
203: Integer imageChoice = new Integer(0);
204:
205: // we are able to handle overviews properly only if the transformation is
206: // an affine transform with pure scale and translation, no rotational components
207: if (raster2Model != null && !isScaleTranslate(raster2Model))
208: return imageChoice;
209:
210: // //
211: //
212: // Check Hint to ignore overviews
213: //
214: // //
215: Object o = hints.get(Hints.IGNORE_COVERAGE_OVERVIEW);
216: if (o != null && ((Boolean) o).booleanValue()) {
217: return imageChoice;
218:
219: }
220:
221: // //
222: //
223: // Am I going to decimate or to use overviews? If this geotiff has only
224: // one page we use decimation, otherwise we use the best page avalabile.
225: // Future versions should use both.
226: //
227: // //
228: final boolean decimate = (numOverviews <= 0) ? true : false;
229:
230: // //
231: //
232: // Resolution requested. I am here computing the resolution required by
233: // the user.
234: //
235: // //
236: double[] requestedRes = getResolution(requestedEnvelope,
237: requestedDim, crs);
238: if (requestedRes == null)
239: return imageChoice;
240:
241: // //
242: //
243: // overviews or decimation
244: //
245: // //
246: if (!decimate) {
247: // /////////////////////////////////////////////////////////////////////
248: // OVERVIEWS
249: // /////////////////////////////////////////////////////////////////////
250: // Should we leave now? In case the resolution of the first level is
251: // already lower than the requested one we should use the first
252: // level and leave.
253: if (highestRes[0] - requestedRes[0] > EPS
254: && highestRes[1] - requestedRes[1] > EPS)
255: return imageChoice;
256:
257: // Should we leave now? In case the resolution of the first level is
258: // already lower than the requested one we should use the first
259: // level and leave.
260: int axis = 0;
261: if (requestedRes[0] - requestedRes[1] > EPS)
262: axis = 1;
263:
264: // //
265: //
266: // looking for the overview with the highest lower resolution
267: // compared
268: // to the requested one.
269: // This ensure more speed but less quality. In the future we should
270: // provide a hint to control this behaviour.
271: //
272: // //
273: double actRes;
274: int i = 0;
275: for (; i < numOverviews; i++) {
276: actRes = (axis == 0) ? overViewResolutions[i][0]
277: : overViewResolutions[i][1];
278: // is actual resolution lower than the requested resolution?
279: if (actRes - requestedRes[axis] > EPS) {
280:
281: i--;
282: break;
283:
284: }
285:
286: }
287: // checking that we did not exceeded the maximum number of pages.
288: if (i == numOverviews) {
289: // int subsamplingFactor=
290: imageChoice = new Integer(numOverviews);
291: } else
292: // keeping the first image at highest resolution into account in
293: // order to get the overview wit
294: imageChoice = new Integer(i + 1);
295: }
296: // /////////////////////////////////////////////////////////////////////
297: // DECIMATION ON READING
298: // /////////////////////////////////////////////////////////////////////
299: decimationOnReadingControl(imageChoice, readP, requestedRes);
300: return imageChoice;
301: }
302:
303: /**
304: * Checks the transformation is a pure scale/translate instance (using a tolerance)
305: * @param transform
306: * @return
307: */
308: protected final boolean isScaleTranslate(MathTransform transform) {
309: if (!(transform instanceof AffineTransform))
310: return false;
311: AffineTransform at = (AffineTransform) transform;
312: return at.getShearX() < EPS && at.getShearY() < EPS;
313: }
314:
315: /**
316: * This method is responsible for evaluating possible subsampling factors
317: * once the best resolution level has been found, in case we have support
318: * for overviews, or starting from the original coverage in case there are
319: * no overviews availaible.
320: *
321: * Anyhow this methof should not be called directly but subclasses should
322: * make use of the setReadParams method instead in order to transparently
323: * look for overviews.
324: *
325: * @param imageChoice
326: * @param readP
327: * @param requestedRes
328: */
329: protected final void decimationOnReadingControl(
330: Integer imageChoice, ImageReadParam readP,
331: double[] requestedRes) {
332: {
333:
334: int w, h;
335: double selectedRes[] = new double[2];
336: final int choice = imageChoice.intValue();
337: if (choice == 0) {
338: // highest resolution
339: w = originalGridRange.getLength(0);
340: h = originalGridRange.getLength(1);
341: selectedRes[0] = highestRes[0];
342: selectedRes[1] = highestRes[1];
343: } else {
344: // some overview
345: selectedRes[0] = overViewResolutions[choice - 1][0];
346: selectedRes[1] = overViewResolutions[choice - 1][1];
347: w = (int) Math.round(originalEnvelope.getLength(0)
348: / selectedRes[0]);
349: h = (int) Math.round(originalEnvelope.getLength(1)
350: / selectedRes[1]);
351:
352: }
353: // /////////////////////////////////////////////////////////////////////
354: // DECIMATION ON READING
355: // Setting subsampling factors with some checkings
356: // 1) the subsampling factors cannot be zero
357: // 2) the subsampling factors cannot be such that the w or h are
358: // zero
359: // /////////////////////////////////////////////////////////////////////
360: if (requestedRes == null) {
361: readP.setSourceSubsampling(1, 1, 0, 0);
362:
363: } else {
364: int subSamplingFactorX = (int) Math
365: .floor(requestedRes[0] / selectedRes[0]);
366: subSamplingFactorX = subSamplingFactorX == 0 ? 1
367: : subSamplingFactorX;
368:
369: while (w / subSamplingFactorX <= 0
370: && subSamplingFactorX >= 0)
371: subSamplingFactorX--;
372: subSamplingFactorX = subSamplingFactorX == 0 ? 1
373: : subSamplingFactorX;
374:
375: int subSamplingFactorY = (int) Math
376: .floor(requestedRes[1] / selectedRes[1]);
377: subSamplingFactorY = subSamplingFactorY == 0 ? 1
378: : subSamplingFactorY;
379:
380: while (h / subSamplingFactorY <= 0
381: && subSamplingFactorY >= 0)
382: subSamplingFactorY--;
383: subSamplingFactorY = subSamplingFactorY == 0 ? 1
384: : subSamplingFactorY;
385:
386: readP.setSourceSubsampling(subSamplingFactorX,
387: subSamplingFactorY, 0, 0);
388: }
389:
390: }
391: }
392:
393: /**
394: * Creates a {@link GridCoverage} for the provided {@link PlanarImage} using
395: * the {@link #originalEnvelope} that was provided for this coverage.
396: *
397: * @param image contains the data for the coverage to create.
398: * @return a {@link GridCoverage}
399: * @throws IOException
400: */
401: protected final GridCoverage createImageCoverage(PlanarImage image)
402: throws IOException {
403: return createImageCoverage(image, null);
404:
405: }
406:
407: /**
408: * Creates a {@link GridCoverage} for the provided {@link PlanarImage} using
409: * the {@link #raster2Model} that was provided for this coverage.
410: *
411: * <p>
412: * This method is vital when working with coverages that have a raster to model transformation
413: * that is not a simple scale and translate.
414: *
415: * @param image contains the data for the coverage to create.
416: * @param raster2Model is the {@link MathTransform} that maps from the
417: * raster space to the model space.
418: * @return a {@link GridCoverage}
419: * @throws IOException
420: */
421: protected final GridCoverage createImageCoverage(PlanarImage image,
422: MathTransform raster2Model) throws IOException {
423:
424: // deciding the number range
425: NumberRange geophysicRange = null;
426:
427: switch (image.getSampleModel().getTransferType()) {
428: case DataBuffer.TYPE_BYTE:
429: geophysicRange = new NumberRange(0, 255);
430:
431: break;
432:
433: case DataBuffer.TYPE_USHORT:
434: geophysicRange = new NumberRange(0, 65535);
435:
436: break;
437: // going to treat following cases as DEM
438: case DataBuffer.TYPE_INT:
439: geophysicRange = new NumberRange(Integer.MIN_VALUE,
440: Integer.MAX_VALUE);
441: break;
442: case DataBuffer.TYPE_SHORT:
443: geophysicRange = new NumberRange(Short.MIN_VALUE,
444: Short.MAX_VALUE);
445: break;
446: case DataBuffer.TYPE_DOUBLE:
447:
448: geophysicRange = new NumberRange(Double.MIN_VALUE,
449: Double.MAX_VALUE);
450: break;
451: case DataBuffer.TYPE_FLOAT:
452: geophysicRange = new NumberRange(Float.MIN_VALUE,
453: Float.MAX_VALUE);
454: return createDEMCoverage(image);
455: default:
456: throw new DataSourceException(
457: "createImageCoverage:Data buffer type not supported by this world image reader! Use byte, ushort or int");
458: }
459:
460: /**
461: * Now we shuld be able to create the sample dimensions and the
462: * categories for this coverage in a much better and meaningful way
463: * using the color model and the color space type.
464: *
465: * @todo How do we handle the NoData when it is 0?
466: *
467: */
468: // convenieience category in order to
469: final Category values = new Category("values",
470: new Color[] { Color.BLACK }, geophysicRange,
471: LinearTransform1D.IDENTITY);
472:
473: // creating bands
474: final int numBands = image.getSampleModel().getNumBands();
475: final GridSampleDimension[] bands = new GridSampleDimension[numBands];
476: // checking the names
477: final ColorModel cm = image.getColorModel();
478: final String names[] = new String[numBands];
479: // in case of index color model we are already done.
480: if (cm instanceof IndexColorModel) {
481: names[0] = "index band";
482: } else {
483: // in case of multiband image we are not done yet.
484: final ColorSpace cs = cm.getColorSpace();
485:
486: if (cs instanceof IHSColorSpace) {
487: names[0] = "Intensity band";
488: names[1] = "Hue band";
489: names[2] = "Saturation band";
490:
491: } else {
492: /**
493: *
494: *
495: * @TODO we need to support more types than the ones we have
496: * here.
497: *
498: *
499: */
500: // not IHS, let's take the type
501: final int type = cs.getType();
502: switch (type) {
503: case ColorSpace.CS_GRAY:
504: case ColorSpace.TYPE_GRAY:
505: names[0] = "grayscale band";
506: break;
507: case ColorSpace.CS_sRGB:
508: case ColorSpace.CS_LINEAR_RGB:
509: case ColorSpace.TYPE_RGB:
510: names[0] = "Red band";
511: names[1] = "Green band";
512: names[2] = "Blue band";
513: break;
514: case ColorSpace.TYPE_CMY:
515: names[0] = "Cyan band";
516: names[1] = "Magenta band";
517: names[2] = "Yellow band";
518: break;
519: case ColorSpace.TYPE_CMYK:
520: names[0] = "Cyan band";
521: names[1] = "Magenta band";
522: names[2] = "Yellow band";
523: names[3] = "K band";
524: break;
525:
526: }
527: }
528: }
529: // setting bands names.
530: for (int i = 0; i < numBands; i++) {
531:
532: bands[i] = new GridSampleDimension(names[i],
533: new Category[] { values }, null).geophysics(true);
534: }
535:
536: // creating coverage
537: if (raster2Model != null) {
538: return coverageFactory.create(coverageName, image, crs,
539: raster2Model, bands, null, null);
540: }
541:
542: return coverageFactory.create(coverageName, image,
543: new GeneralEnvelope(originalEnvelope), bands, null,
544: null);
545:
546: }
547:
548: /**
549: * Creates a {@link GridCoverage} for a coverage that is not a simple image
550: * but that contains complex dadta from measurements.
551: *
552: *
553: * <p>
554: * This usually means that the original {@link DataBuffer#getDataType()} is
555: * of one of the following types:
556: *
557: * <ul>
558: * <li>{@link DataBuffer#TYPE_FLOAT}</li>
559: * <li>{@link DataBuffer#TYPE_DOUBLE}</li>
560: * <li>{@link DataBuffer#TYPE_INT}</li>
561: * <li>{@link DataBuffer#TYPE_SHORT}</li>
562: * </ul>
563: *
564: * and it implies that we have to prepare a transformation from geophysics
565: * values to non-geophysics values.
566: *
567: * @param coverage
568: * a {@link PlanarImage} containing the source coverage.
569: * @return a {@link GridCoverage}.
570: */
571: private GridCoverage createDEMCoverage(PlanarImage coverage) {
572: // Create the SampleDimension, with colors and byte transformation
573: // needed for visualization
574: final UnitFormat unitFormat = UnitFormat.getStandardInstance();
575: Unit uom = null;
576:
577: // unit of measure is meter usually, is this a good guess?
578: try {
579: uom = unitFormat.parseUnit("m");
580: } catch (ParseException e) {
581: uom = null;
582: }
583:
584: final Category values = new Category("elevation", demColors,
585: new NumberRange(2, 10), new NumberRange(-1, 8849));
586:
587: final GridSampleDimension band = new GridSampleDimension(
588: "digital elevation", new Category[] { values }, uom)
589: .geophysics(true);
590:
591: return coverageFactory.create(coverageName, coverage,
592: originalEnvelope, new GridSampleDimension[] { band },
593: null, null);
594:
595: }
596:
597: /**
598: * This method is responsible for computing the resolutions in for the
599: * provided grid geometry in the provided crs.
600: *
601: * <P>
602: * It is worth to note that the returned resolution array is of length of 2
603: * and it always is lon, lat for the moment.<br>
604: * It might be worth to remove the axes reordering code when we are
605: * confident enough with the code to handle the north-up crs.
606: * <p>
607: * TODO use orthodromic distance?
608: *
609: * @param envelope
610: * the GeneralEnvelope
611: * @param dim
612: * @param crs
613: * @throws DataSourceException
614: */
615: protected final double[] getResolution(GeneralEnvelope envelope,
616: Rectangle2D dim, CoordinateReferenceSystem crs)
617: throws DataSourceException {
618: double[] requestedRes = null;
619: try {
620: if (dim != null && envelope != null) {
621: // do we need to transform the originalEnvelope?
622: final CoordinateReferenceSystem crs2D = CRSUtilities
623: .getCRS2D(envelope
624: .getCoordinateReferenceSystem());
625:
626: if (crs != null
627: && !CRSUtilities.equalsIgnoreMetadata(crs,
628: crs2D)) {
629: final MathTransform tr = operationFactory
630: .createOperation(crs2D, crs)
631: .getMathTransform();
632: if (!tr.isIdentity())
633: envelope = CRSUtilities.transform(tr, envelope);
634: }
635: requestedRes = new double[2];
636: requestedRes[0] = envelope.getLength(0)
637: / dim.getWidth();
638: requestedRes[1] = envelope.getLength(1)
639: / dim.getHeight();
640: }
641: return requestedRes;
642: } catch (TransformException e) {
643: throw new DataSourceException(
644: "Unable to get the resolution", e);
645: } catch (FactoryException e) {
646: throw new DataSourceException(
647: "Unable to get the resolution", e);
648: }
649: }
650:
651: /**
652: * Retrieves the {@link CoordinateReferenceSystem} for dataset pointed by
653: * this {@link AbstractGridCoverage2DReader}.
654: *
655: * @return the {@link CoordinateReferenceSystem} for dataset pointed by this
656: * {@link AbstractGridCoverage2DReader}.
657: */
658: public final CoordinateReferenceSystem getCrs() {
659: return crs;
660: }
661:
662: /**
663: * Retrieves the {@link GeneralGridRange} that represents the raster grid
664: * dimensions of the highest resolution level in this dataset.
665: *
666: * @return the {@link GeneralGridRange} that represents the raster grid
667: * dimensions of the highest resolution level in this dataset.
668: */
669: public final GeneralGridRange getOriginalGridRange() {
670: return originalGridRange;
671: }
672:
673: /**
674: * Retrieves the {@link GeneralEnvelope} for this
675: * {@link AbstractGridCoverage2DReader}.
676: *
677: * @return the {@link GeneralEnvelope} for this
678: * {@link AbstractGridCoverage2DReader}.
679: */
680: public final GeneralEnvelope getOriginalEnvelope() {
681: return originalEnvelope;
682: }
683:
684: /**
685: * Retrieves the source for this {@link AbstractGridCoverage2DReader}.
686: *
687: * @return the source for this {@link AbstractGridCoverage2DReader}.
688: */
689: public final Object getSource() {
690: return source;
691: }
692:
693: /**
694: * Disposes this reader.
695: *
696: * <p>
697: * This method just tries to close the underlying {@link ImageInputStream}.
698: */
699: public void dispose() {
700: if (inStream != null) {
701: try {
702: inStream.close();
703: } catch (IOException e) {
704: if (LOGGER.isLoggable(Level.FINE))
705: LOGGER.log(Level.FINE, e.getLocalizedMessage(), e);
706: }
707: }
708:
709: }
710:
711: /**
712: * @see org.opengis.coverage.grid.GridCoverageReader#skip()
713: */
714: public void skip() {
715: throw new UnsupportedOperationException("Unsupported opertion.");
716: }
717:
718: /**
719: * @see org.opengis.coverage.grid.GridCoverageReader#hasMoreGridCoverages()
720: */
721: public boolean hasMoreGridCoverages() {
722: throw new UnsupportedOperationException("Unsupported opertion.");
723: }
724:
725: /**
726: * @see org.opengis.coverage.grid.GridCoverageReader#listSubNames()
727: */
728: public String[] listSubNames() {
729: throw new UnsupportedOperationException("Unsupported opertion.");
730: }
731:
732: /**
733: * @see org.opengis.coverage.grid.GridCoverageReader#getCurrentSubname()
734: */
735: public String getCurrentSubname() {
736: throw new UnsupportedOperationException("Unsupported opertion.");
737: }
738:
739: /**
740: * @see org.opengis.coverage.grid.GridCoverageReader#getMetadataNames()
741: */
742: public String[] getMetadataNames() {
743: throw new UnsupportedOperationException("Unsupported opertion.");
744: }
745:
746: /**
747: * @see org.opengis.coverage.grid.GridCoverageReader#getMetadataValue(java.lang.String)
748: */
749: public String getMetadataValue(final String name)
750: throws MetadataNameNotFoundException {
751: throw new UnsupportedOperationException("Unsupported opertion.");
752: }
753:
754: /**
755: * @see org.opengis.coverage.grid.GridCoverageReader#getGridCoverageCount()
756: */
757: public int getGridCoverageCount() {
758: throw new UnsupportedOperationException("Unsupported opertion.");
759: }
760: }
|