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: /*
017: * NOTICE OF RELEASE TO THE PUBLIC DOMAIN
018: *
019: * This work was created by employees of the USDA Forest Service's
020: * Fire Science Lab for internal use. It is therefore ineligible for
021: * copyright under title 17, section 105 of the United States Code. You
022: * may treat it as you would treat any public domain work: it may be used,
023: * changed, copied, or redistributed, with or without permission of the
024: * authors, for free or for compensation. You may not claim exclusive
025: * ownership of this code because it is already owned by everyone. Use this
026: * software entirely at your own risk. No warranty of any kind is given.
027: *
028: * A copy of 17-USC-105 should have accompanied this distribution in the file
029: * 17USC105.html. If not, you may access the law via the US Government's
030: * public websites:
031: * - http://www.copyright.gov/title17/92chap1.html#105
032: * - http://www.gpoaccess.gov/uscode/ (enter "17USC105" in the search box.)
033: */
034: package org.geotools.gce.geotiff;
035:
036: // JAI ImageIO Tools dependencies
037: import java.awt.Rectangle;
038: import java.awt.RenderingHints;
039: import java.awt.geom.AffineTransform;
040: import java.awt.image.renderable.ParameterBlock;
041: import java.io.File;
042: import java.io.IOException;
043: import java.io.InputStream;
044: import java.net.URL;
045: import java.net.URLDecoder;
046: import java.util.logging.Level;
047: import java.util.logging.Logger;
048:
049: import javax.imageio.ImageIO;
050: import javax.imageio.ImageReadParam;
051: import javax.imageio.ImageReader;
052: import javax.imageio.metadata.IIOMetadata;
053: import javax.imageio.stream.ImageInputStream;
054: import javax.media.jai.JAI;
055:
056: import org.geotools.coverage.grid.GeneralGridRange;
057: import org.geotools.coverage.grid.GridGeometry2D;
058: import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
059: import org.geotools.coverage.grid.io.AbstractGridFormat;
060: import org.geotools.data.DataSourceException;
061: import org.geotools.factory.Hints;
062: import org.geotools.gce.geotiff.IIOMetadataAdpaters.GeoTiffIIOMetadataDecoder;
063: import org.geotools.gce.geotiff.crs_adapters.GeoTiffMetadata2CRSAdapter;
064: import org.geotools.geometry.GeneralEnvelope;
065: import org.geotools.parameter.Parameter;
066: import org.geotools.referencing.CRS;
067: import org.geotools.referencing.operation.transform.ProjectiveTransform;
068: import org.geotools.resources.CRSUtilities;
069: import org.opengis.coverage.grid.Format;
070: import org.opengis.coverage.grid.GridCoverage;
071: import org.opengis.coverage.grid.GridCoverageReader;
072: import org.opengis.parameter.GeneralParameterValue;
073: import org.opengis.referencing.FactoryException;
074: import org.opengis.referencing.crs.CoordinateReferenceSystem;
075: import org.opengis.referencing.operation.TransformException;
076: import org.opengis.geometry.Envelope;
077: import org.opengis.geometry.MismatchedDimensionException;
078:
079: import com.sun.media.imageioimpl.plugins.tiff.TIFFImageReaderSpi;
080:
081: /**
082: * this class is responsible for exposing the data and the Georeferencing
083: * metadata available to the Geotools library. This reader is heavily based on
084: * the capabilities provided by the ImageIO tools and JAI libraries.
085: *
086: *
087: * @author Bryce Nordgren, USDA Forest Service
088: * @author Simone Giannecchini
089: * @since 2.1
090: * @source $URL:
091: * http://svn.geotools.org/geotools/branches/coverages_branch/trunk/gt/plugin/geotiff/src/org/geotools/gce/geotiff/GeoTiffReader.java $
092: */
093: public final class GeoTiffReader extends AbstractGridCoverage2DReader
094: implements GridCoverageReader {
095:
096: /** Logger for the {@link GeoTiffReader} class. */
097: private Logger LOGGER = org.geotools.util.logging.Logging
098: .getLogger(GeoTiffReader.class.toString());
099:
100: /** SPI for creating tiff readers in ImageIO tools */
101: private final static TIFFImageReaderSpi readerSPI = new TIFFImageReaderSpi();
102:
103: /** Decoder for the GeoTiff metadata. */
104: private GeoTiffIIOMetadataDecoder metadata;
105:
106: /** Adapter for the GeoTiff crs. */
107: private GeoTiffMetadata2CRSAdapter gtcs;
108:
109: /**
110: * Creates a new instance of GeoTiffReader
111: *
112: * @param input
113: * the GeoTiff file
114: * @throws DataSourceException
115: */
116: public GeoTiffReader(Object input) throws DataSourceException {
117: this (input, new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER,
118: Boolean.TRUE));
119:
120: }
121:
122: /**
123: * Creates a new instance of GeoTiffReader
124: *
125: * @param input
126: * the GeoTiff file
127: * @param uHints
128: * user-supplied hints TODO currently are unused
129: * @throws DataSourceException
130: */
131: public GeoTiffReader(Object input, Hints uHints)
132: throws DataSourceException {
133: // /////////////////////////////////////////////////////////////////////
134: //
135: // Forcing longitude first since the geotiff specification seems to
136: // assume that we have first longitude the latitude.
137: //
138: // /////////////////////////////////////////////////////////////////////
139: this .hints = new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER,
140: Boolean.TRUE);
141: if (uHints != null) {
142: // prevent the use from reordering axes
143: uHints.remove(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER);
144: this .hints.add(uHints);
145: }
146: coverageName = "geotiff_coverage";
147:
148: // /////////////////////////////////////////////////////////////////////
149: //
150: // Seting input
151: //
152: // /////////////////////////////////////////////////////////////////////
153: if (input == null) {
154:
155: final IOException ex = new IOException(
156: "GeoTiffReader:No source set to read this coverage.");
157: throw new DataSourceException(ex);
158: }
159: // /////////////////////////////////////////////////////////////////////
160: //
161: // Set the source being careful in case it is an URL pointing to a file
162: //
163: // /////////////////////////////////////////////////////////////////////
164: try {
165: this .source = input;
166: // setting source
167: if (input instanceof URL) {
168: final URL sourceURL = (URL) input;
169: if (sourceURL.getProtocol().equalsIgnoreCase("http")
170: || sourceURL.getProtocol().equalsIgnoreCase(
171: "ftp")) {
172: try {
173: source = sourceURL.openStream();
174: } catch (IOException e) {
175: new RuntimeException(e);
176: }
177: } else if (sourceURL.getProtocol().equalsIgnoreCase(
178: "file"))
179: source = new File(URLDecoder.decode(sourceURL
180: .getFile(), "UTF-8"));
181: }
182:
183: closeMe = true;
184: // /////////////////////////////////////////////////////////////////////
185: //
186: // Get a stream in order to read from it for getting the basic
187: // information for this coverfage
188: //
189: // /////////////////////////////////////////////////////////////////////
190: if ((source instanceof InputStream)
191: || (source instanceof ImageInputStream))
192: closeMe = false;
193: inStream = ImageIO.createImageInputStream(source);
194: if (inStream == null)
195: throw new IllegalArgumentException(
196: "No input stream for the provided source");
197:
198: // /////////////////////////////////////////////////////////////////////
199: //
200: // Informations about multiple levels and such
201: //
202: // /////////////////////////////////////////////////////////////////////
203: getHRInfo(this .hints);
204:
205: // /////////////////////////////////////////////////////////////////////
206: //
207: // Coverage name
208: //
209: // /////////////////////////////////////////////////////////////////////
210: coverageName = source instanceof File ? ((File) source)
211: .getName() : "geotiff_coverage";
212: final int dotIndex = coverageName.lastIndexOf('.');
213: if (dotIndex != -1 && dotIndex != coverageName.length())
214: coverageName = coverageName.substring(0, dotIndex);
215:
216: // /////////////////////////////////////////////////////////////////////
217: //
218: // Freeing streams
219: //
220: // /////////////////////////////////////////////////////////////////////
221: if (closeMe)//
222: inStream.close();
223: } catch (IOException e) {
224: throw new DataSourceException(e);
225: } catch (TransformException e) {
226: throw new DataSourceException(e);
227: } catch (FactoryException e) {
228: throw new DataSourceException(e);
229: }
230: }
231:
232: /**
233: *
234: * @param hints
235: * @throws IOException
236: * @throws FactoryException
237: * @throws GeoTiffException
238: * @throws TransformException
239: * @throws MismatchedDimensionException
240: * @throws DataSourceException
241: */
242: private void getHRInfo(Hints hints) throws IOException,
243: FactoryException, GeoTiffException, TransformException,
244: MismatchedDimensionException, DataSourceException {
245: // //
246: //
247: // Get a reader for this format
248: //
249: // //
250: final ImageReader reader = readerSPI.createReaderInstance();
251:
252: // //
253: //
254: // get the METADATA
255: //
256: // //
257: reader.setInput(inStream);
258: final IIOMetadata iioMetadata = reader.getImageMetadata(0);
259: metadata = new GeoTiffIIOMetadataDecoder(iioMetadata);
260: gtcs = (GeoTiffMetadata2CRSAdapter) GeoTiffMetadata2CRSAdapter
261: .get(hints);
262:
263: // //
264: //
265: // get the CRS INFO
266: //
267: // //
268: final Object tempCRS = this .hints
269: .get(Hints.DEFAULT_COORDINATE_REFERENCE_SYSTEM);
270: if (tempCRS != null) {
271: this .crs = (CoordinateReferenceSystem) tempCRS;
272: LOGGER.log(Level.WARNING, new StringBuffer(
273: "Using forced coordinate reference system ")
274: .append(crs.toWKT()).toString());
275: } else
276: crs = gtcs.createCoordinateSystem(metadata);
277:
278: // //
279: //
280: // get the dimension of the hr image and build the model as well as
281: // computing the resolution
282: // //
283: numOverviews = reader.getNumImages(true) - 1;
284: int hrWidth = reader.getWidth(0);
285: int hrHeight = reader.getHeight(0);
286: final Rectangle actualDim = new Rectangle(0, 0, hrWidth,
287: hrHeight);
288: originalGridRange = new GeneralGridRange(actualDim);
289:
290: this .raster2Model = gtcs.getRasterToModel(metadata);
291: final AffineTransform tempTransform = new AffineTransform(
292: (AffineTransform) raster2Model);
293: tempTransform.translate(-0.5, -0.5);
294: originalEnvelope = CRS.transform(ProjectiveTransform
295: .create(tempTransform), new GeneralEnvelope(actualDim));
296: originalEnvelope.setCoordinateReferenceSystem(crs);
297:
298: // ///
299: //
300: // setting the higher resolution avalaible for this coverage
301: //
302: // ///
303: highestRes = getResolution(originalEnvelope, actualDim, crs);
304:
305: // //
306: //
307: // get information for the successive images
308: //
309: // //
310: if (numOverviews > 1) {
311: overViewResolutions = new double[numOverviews][2];
312: double res[];
313: for (int i = 0; i < numOverviews; i++) {
314: res = getResolution(originalEnvelope, new Rectangle(0,
315: 0, reader.getWidth(i), reader.getHeight(i)),
316: crs);
317: overViewResolutions[i][0] = res[0];
318: overViewResolutions[i][1] = res[1];
319: }
320: } else
321: overViewResolutions = null;
322: }
323:
324: /**
325: * @see org.opengis.coverage.grid.GridCoverageReader#getFormat()
326: */
327: public Format getFormat() {
328: return new GeoTiffFormat();
329: }
330:
331: /**
332: * This method reads in the TIFF image, constructs an appropriate CRS,
333: * determines the math transform from raster to the CRS model, and
334: * constructs a GridCoverage.
335: *
336: * @param params
337: * currently ignored, potentially may be used for hints.
338: *
339: * @return grid coverage represented by the image
340: *
341: * @throws IOException
342: * on any IO related troubles
343: */
344: public GridCoverage read(GeneralParameterValue[] params)
345: throws IOException {
346: GeneralEnvelope requestedEnvelope = null;
347: Rectangle dim = null;
348: if (params != null) {
349: // /////////////////////////////////////////////////////////////////////
350: //
351: // Checking params
352: //
353: // /////////////////////////////////////////////////////////////////////
354: if (params != null) {
355: for (int i = 0; i < params.length; i++) {
356: final Parameter param = (Parameter) params[i];
357: if (param
358: .getDescriptor()
359: .getName()
360: .getCode()
361: .equals(
362: AbstractGridFormat.READ_GRIDGEOMETRY2D
363: .getName().toString())) {
364: final GridGeometry2D gg = (GridGeometry2D) param
365: .getValue();
366: requestedEnvelope = new GeneralEnvelope(
367: (Envelope) gg.getEnvelope2D());
368: dim = gg.getGridRange2D().getBounds();
369: }
370: }
371: }
372: }
373: // /////////////////////////////////////////////////////////////////////
374: //
375: // set params
376: //
377: // /////////////////////////////////////////////////////////////////////
378: Integer imageChoice = new Integer(0);
379: final ImageReadParam readP = new ImageReadParam();
380: try {
381: imageChoice = setReadParams(readP, requestedEnvelope, dim);
382: } catch (TransformException e) {
383: new DataSourceException(e);
384: }
385:
386: // /////////////////////////////////////////////////////////////////////
387: //
388: // IMAGE READ OPERATION
389: //
390: // /////////////////////////////////////////////////////////////////////
391: // final ImageReader reader = readerSPI.createReaderInstance();
392: // final ImageInputStream inStream = ImageIO
393: // .createImageInputStream(source);
394: // reader.setInput(inStream);
395: final Hints newHints = (Hints) hints.clone();
396: // if (!reader.isImageTiled(imageChoice.intValue())) {
397: // final Dimension tileSize = ImageUtilities.toTileSize(new Dimension(
398: // reader.getWidth(imageChoice.intValue()), reader
399: // .getHeight(imageChoice.intValue())));
400: // final ImageLayout layout = new ImageLayout();
401: // layout.setTileGridXOffset(0);
402: // layout.setTileGridYOffset(0);
403: // layout.setTileHeight(tileSize.height);
404: // layout.setTileWidth(tileSize.width);
405: // newHints.add(new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout));
406: // }
407: // inStream.close();
408: // reader.reset();
409: final ParameterBlock pbjRead = new ParameterBlock();
410: pbjRead.add(ImageIO.createImageInputStream(source));
411: pbjRead.add(imageChoice);
412: pbjRead.add(Boolean.FALSE);
413: pbjRead.add(Boolean.FALSE);
414: pbjRead.add(Boolean.FALSE);
415: pbjRead.add(null);
416: pbjRead.add(null);
417: pbjRead.add(readP);
418: pbjRead.add(readerSPI.createReaderInstance());
419:
420: // /////////////////////////////////////////////////////////////////////
421: //
422: // BUILDING COVERAGE
423: //
424: // /////////////////////////////////////////////////////////////////////
425: // get the raster -> model transformation and
426: // create the coverage
427: if (imageChoice.intValue() == 0) {
428:
429: final AffineTransform tempRaster2Model = new AffineTransform(
430: (AffineTransform) raster2Model);
431: tempRaster2Model.concatenate(new AffineTransform(readP
432: .getSourceXSubsampling(), 0, 0, readP
433: .getSourceYSubsampling(), 0, 0));
434: return createImageCoverage(JAI.create("ImageRead", pbjRead,
435: (RenderingHints) newHints), ProjectiveTransform
436: .create((AffineTransform) tempRaster2Model));
437:
438: }
439: return createImageCoverage(JAI.create("ImageRead", pbjRead,
440: (RenderingHints) newHints));
441:
442: }
443:
444: /**
445: * Returns the geotiff metadata for this geotiff file.
446: *
447: * @return the metadata
448: */
449: public GeoTiffIIOMetadataDecoder getMetadata() {
450: return metadata;
451: }
452:
453: }
|