001: // $HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/model/coverage/grid/GeoTIFFGridCoverageReader.java $
002: /*---------------- FILE HEADER ------------------------------------------
003:
004: This file is part of deegree.
005: Copyright (C) 2001-2008 by:
006: EXSE, Department of Geography, University of Bonn
007: http://www.giub.uni-bonn.de/deegree/
008: lat/lon GmbH
009: http://www.lat-lon.de
010:
011: This library is free software; you can redistribute it and/or
012: modify it under the terms of the GNU Lesser General Public
013: License as published by the Free Software Foundation; either
014: version 2.1 of the License, or (at your option) any later version.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: Contact:
026:
027: Andreas Poth
028: lat/lon GmbH
029: Aennchenstr. 19
030: 53115 Bonn
031: Germany
032: E-Mail: poth@lat-lon.de
033:
034: Prof. Dr. Klaus Greve
035: Department of Geography
036: University of Bonn
037: Meckenheimer Allee 166
038: 53115 Bonn
039: Germany
040: E-Mail: greve@giub.uni-bonn.de
041:
042:
043: ---------------------------------------------------------------------------*/
044: package org.deegree.model.coverage.grid;
045:
046: import java.awt.Rectangle;
047: import java.awt.image.Raster;
048: import java.io.IOException;
049: import java.io.InputStream;
050: import java.net.URL;
051:
052: import javax.media.jai.JAI;
053: import javax.media.jai.RenderedOp;
054:
055: import org.deegree.datatypes.CodeList;
056: import org.deegree.datatypes.parameter.GeneralParameterValueIm;
057: import org.deegree.datatypes.parameter.InvalidParameterNameException;
058: import org.deegree.datatypes.parameter.InvalidParameterValueException;
059: import org.deegree.datatypes.parameter.ParameterNotFoundException;
060: import org.deegree.framework.log.ILogger;
061: import org.deegree.framework.log.LoggerFactory;
062: import org.deegree.framework.util.StringTools;
063: import org.deegree.graphics.transformation.GeoTransform;
064: import org.deegree.graphics.transformation.WorldToScreenTransform;
065: import org.deegree.i18n.Messages;
066: import org.deegree.model.crs.GeoTransformer;
067: import org.deegree.model.spatialschema.Envelope;
068: import org.deegree.model.spatialschema.GeometryFactory;
069: import org.deegree.ogcwebservices.LonLatEnvelope;
070: import org.deegree.ogcwebservices.wcs.configuration.File;
071: import org.deegree.ogcwebservices.wcs.describecoverage.CoverageOffering;
072:
073: import com.sun.media.jai.codec.MemoryCacheSeekableStream;
074: import com.sun.media.jai.codec.SeekableStream;
075:
076: /**
077: * GridCoverageReader for reading files as defined by the deegree CoverageOffering Extension type
078: * 'File'. Known formats are: tiff, GeoTiff, jpeg, bmp, gif, png and img (IDRISI)
079: *
080: * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
081: * @author last edited by: $Author: rbezema $
082: *
083: * @version $Revision: 9437 $, $Date: 2008-01-08 02:50:52 -0800 (Tue, 08 Jan 2008) $
084: */
085: public class GeoTIFFGridCoverageReader extends
086: AbstractGridCoverageReader {
087:
088: private static final ILogger LOG = LoggerFactory
089: .getLogger(GeoTIFFGridCoverageReader.class);
090:
091: private SeekableStream sst = null;
092:
093: /**
094: * @param source
095: * source file of the coverage
096: * @param description
097: * description of the data contained in the source file
098: * @param envelope
099: * desired envelope of the coverage to be read
100: * @param format
101: * image format of the source file
102: */
103: public GeoTIFFGridCoverageReader(File source,
104: CoverageOffering description, Envelope envelope,
105: Format format) {
106: super (source, description, envelope, format);
107: }
108:
109: /**
110: * @param source
111: * @param description
112: * description of the data contained in the source file
113: * @param envelope
114: * desired envelope of the coverage to be read
115: * @param format
116: * image format of the source file
117: */
118: public GeoTIFFGridCoverageReader(InputStream source,
119: CoverageOffering description, Envelope envelope,
120: Format format) {
121: super (source, description, envelope, format);
122: }
123:
124: /**
125: * Read the grid coverage from the current stream position, and move to the next grid coverage.
126: *
127: * @param parameters
128: * An optional set of parameters. Should be any or all of the parameters returned by
129: * {@link "org.opengis.coverage.grid.Format#getReadParameters"}.
130: * @return A new {@linkplain GridCoverage grid coverage} from the input source.
131: * @throws InvalidParameterNameException
132: * if a parameter in <code>parameters</code> doesn't have a recognized name.
133: * @throws InvalidParameterValueException
134: * if a parameter in <code>parameters</code> doesn't have a valid value.
135: * @throws ParameterNotFoundException
136: * if a parameter was required for the operation but was not provided in the
137: * <code>parameters</code> list.
138: * @throws IOException
139: * if a read operation failed for some other input/output reason, including
140: * {@link java.io.FileNotFoundException} if no file with the given <code>name</code>
141: * can be found, or {@link javax.imageio.IIOException} if an error was thrown by the
142: * underlying image library.
143: */
144: public GridCoverage read(GeneralParameterValueIm[] parameters)
145: throws InvalidParameterNameException,
146: InvalidParameterValueException, ParameterNotFoundException,
147: IOException {
148:
149: RenderedOp rop = readGeoTIFF();
150: int w = rop.getWidth();
151: int h = rop.getHeight();
152:
153: // get image rectangle of interrest, envelope and lonlatenvelope
154: Object[] o = getRasterRegion(w, h);
155: if (LOG.getLevel() == ILogger.LOG_DEBUG) {
156: LOG
157: .logDebug("image rectangle of interrest, envelope and lonlatenvelope:");
158: for (int i = 0; i < o.length; i++) {
159: LOG.logDebug(o[i].toString());
160: }
161: }
162: Rectangle rect = (Rectangle) o[0];
163: // return null if the result GC would have a width or height of zero
164: if (rect.width == 0 || rect.height == 0) {
165: return null;
166: }
167: // create a coverage description that matches the sub image (coverage)
168: // for this a new LonLatEnvelope must be set
169: CoverageOffering co = (CoverageOffering) description.clone();
170: co.setLonLatEnvelope((LonLatEnvelope) o[2]);
171:
172: // extract required area from the tiff data
173: Raster raster = rop.getData(rect);
174: // use 8 BIT as default assuming a raster contains simple
175: // data like a Landsat TM Band
176: int pxSize = 8;
177: if (rop.getColorModel() != null) {
178: pxSize = rop.getColorModel().getPixelSize();
179: }
180:
181: return createGridCoverage(raster, co, (Envelope) o[1], pxSize);
182:
183: }
184:
185: /**
186: * creates an instance of <tt>GridCoverage</tt> from the passed Raster, CoverageOffering and
187: * Envelope. Depending on the pixel size of the the passed raster different types of
188: * GirdCoverages will be created. possilbe pixel sized are:
189: * <ul>
190: * <li>8
191: * <li>16
192: * <li>32
193: * <li>64
194: * </ul>
195: *
196: * @param raster
197: * @param co
198: * @param env
199: * @param pxSize
200: * @return
201: * @throws InvalidParameterValueException
202: */
203: private GridCoverage createGridCoverage(Raster raster,
204: CoverageOffering co, Envelope env, int pxSize)
205: throws InvalidParameterValueException {
206:
207: GridCoverage gc = null;
208: switch (pxSize) {
209: case 8: {
210: gc = createByteGridCoverage(raster, co, env);
211: break;
212: }
213: case 16: {
214: gc = createShortGridCoverage(raster, co, env);
215: break;
216: }
217: case 32:
218: case 64: {
219: String s = Messages.getMessage("GC_NOT_SUPPORTED_PS",
220: pxSize);
221: throw new InvalidParameterValueException(s, "type", pxSize);
222: }
223: default:
224: String s = Messages.getMessage("GC_UNKNOWN_PS", pxSize);
225: throw new InvalidParameterValueException(s, "type", pxSize);
226: }
227:
228: return gc;
229: }
230:
231: /**
232: * creates a GridCoverage from the passed Raster. The contains data in
233: * <tt>DataBuffer.TYPE_BYTE</tt> format so the result GridCoverage is of type
234: * <tt>ByteGridCoverage </tt>
235: *
236: * @param raster
237: * @param co
238: * @param env
239: * @return
240: */
241: private ByteGridCoverage createByteGridCoverage(Raster raster,
242: CoverageOffering co, Envelope env) {
243:
244: Rectangle rect = raster.getBounds();
245: int bands = raster.getNumBands();
246: byte[] data = (byte[]) raster.getDataElements(rect.x, rect.y,
247: rect.width, rect.height, null);
248: byte[][][] mat = new byte[bands][rect.height][rect.width];
249: int k = 0;
250: for (int i = 0; i < mat[0].length; i++) {
251: for (int j = 0; j < mat[0][i].length; j++) {
252: for (int b = 0; b < bands; b++) {
253: mat[b][i][j] = data[k++];
254: }
255: }
256: }
257:
258: return new ByteGridCoverage(co, env, mat);
259: }
260:
261: /**
262: * creates a GridCoverage from the passed Raster. The contains data in
263: * <tt>DataBuffer.TYPE_SHORT</tt> format so the result GridCoverage is of type
264: * <tt>ShortGridCoverage </tt>
265: *
266: * @param raster
267: * @param co
268: * @param env
269: * @return
270: */
271: private ShortGridCoverage createShortGridCoverage(Raster raster,
272: CoverageOffering co, Envelope env) {
273:
274: Rectangle rect = raster.getBounds();
275: int bands = raster.getNumBands();
276: short[] data = (short[]) raster.getDataElements(rect.x, rect.y,
277: rect.width, rect.height, null);
278: short[][][] mat = new short[bands][rect.height][rect.width];
279: int k = 0;
280: for (int i = 0; i < mat[0].length; i++) {
281: for (int j = 0; j < mat[0][i].length; j++) {
282: for (int b = 0; b < bands; b++) {
283: mat[b][i][j] = data[k++];
284: }
285: }
286: }
287:
288: return new ShortGridCoverage(co, env, mat);
289: }
290:
291: /**
292: * reads an image from its source
293: *
294: * @return
295: * @throws IOException
296: */
297: private RenderedOp readGeoTIFF() throws IOException {
298:
299: RenderedOp ro = null;
300: if (source.getClass() == File.class) {
301: String s = ((File) source).getName();
302: String tmp = s.toLowerCase();
303: LOG.logDebug("load: ", tmp);
304: URL url = null;
305: if (tmp.startsWith("file:")) {
306: tmp = s.substring(6, s.length());
307: url = new java.io.File(tmp).toURL();
308: } else if (tmp.startsWith("http:")) {
309: url = new URL(s);
310: } else {
311: url = new java.io.File(s).toURL();
312: }
313: sst = new MemoryCacheSeekableStream(url.openStream());
314: ro = JAI.create("stream", sst);
315: } else {
316: sst = new MemoryCacheSeekableStream((InputStream) source);
317: ro = JAI.create("stream", sst);
318: }
319:
320: return ro;
321: }
322:
323: /**
324: * returns the region of the source image that intersects with the GridCoverage to be created as
325: * Rectange as well as the Envelope of the region in the native CRS and the LonLatEnvelope of
326: * this region.
327: *
328: * @param width
329: * width of the source image
330: * @param height
331: * height of the source image
332: * @return
333: */
334: private Object[] getRasterRegion(int width, int height) {
335:
336: CodeList[] cl = description.getSupportedCRSs().getNativeSRSs();
337: String code = cl[0].getCodes()[0];
338:
339: LonLatEnvelope lle = description.getLonLatEnvelope();
340: Envelope tmp = GeometryFactory.createEnvelope(lle.getMin()
341: .getX(), lle.getMin().getY(), lle.getMax().getX(), lle
342: .getMax().getY(), null);
343: try {
344: // transform if native CRS is <> EPSG:4326
345: if (!(code.equals("EPSG:4326"))) {
346: GeoTransformer trans = new GeoTransformer(code);
347: tmp = trans.transform(tmp, "EPSG:4326");
348: }
349: } catch (Exception e) {
350: LOG.logError(StringTools.stackTraceToString(e));
351: }
352: // creat tranform object to calculate raster coordinates from
353: // geo coordinates
354: GeoTransform gt = new WorldToScreenTransform(tmp.getMin()
355: .getX(), tmp.getMin().getY(), tmp.getMax().getX(), tmp
356: .getMax().getY(), 0, 0, width - 1, height - 1);
357:
358: // calculate envelope of the part of the grid coverage that is contained
359: // within the image
360: Envelope env = envelope.createIntersection(tmp);
361:
362: LonLatEnvelope lonLatEnvelope = calcLonLatEnvelope(env, code);
363: // calc image coordinates matching the area that is requested
364: int minx = (int) Math.round(gt.getDestX(env.getMin().getX()));
365: int miny = (int) Math.round(gt.getDestY(env.getMax().getY()));
366: int maxx = (int) Math.round(gt.getDestX(env.getMax().getX()));
367: int maxy = (int) Math.round(gt.getDestY(env.getMin().getY()));
368: Rectangle rect = new Rectangle(minx, miny, maxx - minx + 1,
369: maxy - miny + 1);
370:
371: return new Object[] { rect, env, lonLatEnvelope };
372: }
373:
374: /**
375: * Allows any resources held by this object to be released. The result of calling any other
376: * method subsequent to a call to this method is undefined. It is important for applications to
377: * call this method when they know they will no longer be using this
378: * <code>GridCoverageReader</code>. Otherwise, the reader may continue to hold on to
379: * resources indefinitely.
380: *
381: * @throws IOException
382: * if an error occured while disposing resources (for example while closing a file).
383: */
384: public void dispose() throws IOException {
385: if (sst != null) {
386: sst.close();
387: }
388: }
389:
390: }
|