001: // $HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/model/coverage/grid/ImageGridCoverageReader.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.BufferedImage;
048: import java.io.IOException;
049: import java.io.InputStream;
050: import java.net.URL;
051:
052: import org.deegree.datatypes.CodeList;
053: import org.deegree.datatypes.parameter.GeneralParameterValueIm;
054: import org.deegree.datatypes.parameter.InvalidParameterNameException;
055: import org.deegree.datatypes.parameter.InvalidParameterValueException;
056: import org.deegree.datatypes.parameter.OperationParameterIm;
057: import org.deegree.datatypes.parameter.ParameterNotFoundException;
058: import org.deegree.framework.log.ILogger;
059: import org.deegree.framework.log.LoggerFactory;
060: import org.deegree.framework.util.ImageUtils;
061: import org.deegree.framework.util.StringTools;
062: import org.deegree.graphics.transformation.GeoTransform;
063: import org.deegree.graphics.transformation.WorldToScreenTransform;
064: import org.deegree.io.ecwapi.ECWReader;
065: import org.deegree.model.crs.GeoTransformer;
066: import org.deegree.model.spatialschema.Envelope;
067: import org.deegree.model.spatialschema.GeometryFactory;
068: import org.deegree.ogcwebservices.LonLatEnvelope;
069: import org.deegree.ogcwebservices.wcs.configuration.File;
070: import org.deegree.ogcwebservices.wcs.describecoverage.CoverageOffering;
071:
072: import com.ermapper.ecw.JNCSException;
073:
074: /**
075: * GridCoverageReader for reading files as defined by the deegree CoverageOffering Extension type
076: * 'File'. Known formats are: tiff, GeoTiff, jpeg, bmp, gif, png and img (IDRISI)
077: *
078: * @version $Revision: 9437 $
079: * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
080: * @author last edited by: $Author: rbezema $
081: *
082: * @version 1.0. $Revision: 9437 $, $Date: 2008-01-08 02:50:52 -0800 (Tue, 08 Jan 2008) $
083: *
084: * @since 2.0
085: */
086:
087: public class ImageGridCoverageReader extends AbstractGridCoverageReader {
088:
089: private static final ILogger LOGGER = LoggerFactory
090: .getLogger(ImageGridCoverageReader.class);
091:
092: /**
093: * @param source
094: * source file of the coverage
095: * @param description
096: * description of the data contained in the source file
097: * @param envelope
098: * desired envelope of the coverage to be read
099: * @param format
100: * image format of the source file
101: */
102: public ImageGridCoverageReader(File source,
103: CoverageOffering description, Envelope envelope,
104: Format format) {
105: super (source, description, envelope, format);
106: }
107:
108: /**
109: * @param source
110: * @param description
111: * description of the data contained in the source file
112: * @param envelope
113: * desired envelope of the coverage to be read
114: * @param format
115: * image format of the source file
116: */
117: public ImageGridCoverageReader(InputStream source,
118: CoverageOffering description, Envelope envelope,
119: Format format) {
120: super (source, description, envelope, format);
121: }
122:
123: /**
124: * Read the grid coverage from the current stream position, and move to the next grid coverage.
125: *
126: * @param parameters
127: * An optional set of parameters. Should be any or all of the parameters returned by
128: * {@link "org.opengis.coverage.grid.Format#getReadParameters"}.
129: * @return A new {@linkplain GridCoverage grid coverage} from the input source.
130: * @throws InvalidParameterNameException
131: * if a parameter in <code>parameters</code> doesn't have a recognized name.
132: * @throws InvalidParameterValueException
133: * if a parameter in <code>parameters</code> doesn't have a valid value.
134: * @throws ParameterNotFoundException
135: * if a parameter was required for the operation but was not provided in the
136: * <code>parameters</code> list.
137: * @throws IOException
138: * if a read operation failed for some other input/output reason, including
139: * {@link java.io.FileNotFoundException} if no file with the given <code>name</code>
140: * can be found, or {@link javax.imageio.IIOException} if an error was thrown by the
141: * underlying image library.
142: */
143: public GridCoverage read(GeneralParameterValueIm[] parameters)
144: throws InvalidParameterNameException,
145: InvalidParameterValueException, ParameterNotFoundException,
146: IOException {
147:
148: String frmt = description.getSupportedFormats()
149: .getNativeFormat().getCode();
150: GridCoverage gc = null;
151: if (frmt.equalsIgnoreCase("ecw")) {
152: gc = performECW(parameters);
153: } else if (frmt.equalsIgnoreCase("png")
154: || frmt.equalsIgnoreCase("bmp")
155: || frmt.equalsIgnoreCase("tif")
156: || frmt.equalsIgnoreCase("tiff")
157: || frmt.equalsIgnoreCase("gif")
158: || frmt.equalsIgnoreCase("jpg")
159: || frmt.equalsIgnoreCase("jpeg")) {
160: gc = performImage();
161: } else {
162: throw new InvalidParameterValueException("unknown format",
163: "native format", format);
164: }
165:
166: return gc;
167: }
168:
169: /**
170: * performs the creation of a <tt>ImageGridCoverage</tt> from the source assigned to this
171: * reader.
172: *
173: * @param parameters
174: * @return
175: * @throws IOException
176: */
177: private GridCoverage performECW(GeneralParameterValueIm[] parameters)
178: throws IOException {
179:
180: BufferedImage bi = null;
181: CoverageOffering co = null;
182: Object[] o = null;
183:
184: ECWReader ecwFile = null;
185: try {
186: String s = ((File) source).getName();
187: ecwFile = new ECWReader(s);
188:
189: // get the requested dimension in pixels
190: int reqWidth = 0;
191: int reqHeight = 0;
192: for (int i = 0; i < parameters.length; i++) {
193: OperationParameterIm op = (OperationParameterIm) parameters[i]
194: .getDescriptor();
195: String name = op.getName();
196: if (name.equalsIgnoreCase("WIDTH")) {
197: Object vo = op.getDefaultValue();
198: reqWidth = ((Integer) vo).intValue();
199: } else if (name.equalsIgnoreCase("HEIGHT")) {
200: Object vo = op.getDefaultValue();
201: reqHeight = ((Integer) vo).intValue();
202: }
203: }
204:
205: // calculate image region of interest
206: o = getECWImageRegion(reqWidth, reqHeight);
207: Envelope envl = (Envelope) o[1];
208:
209: Rectangle rect = (Rectangle) o[0];
210: bi = ecwFile
211: .getBufferedImage(envl, rect.width, rect.height);
212:
213: // create a coverage description that matches the sub image (coverage)
214: // for this a new LonLatEnvelope must be set
215: co = (CoverageOffering) description.clone();
216: co.setLonLatEnvelope((LonLatEnvelope) o[2]);
217:
218: } catch (JNCSException e) {
219: throw new IOException(StringTools.stackTraceToString(e));
220: } finally {
221: // free the ECW cache memory
222: if (ecwFile != null) {
223: ecwFile.close();
224: }
225: }
226:
227: return new ImageGridCoverage(co, (Envelope) o[1], bi);
228:
229: }
230:
231: /**
232: * performs the creation of a <tt>ImageGridCoverage</tt> from the source assigned to this
233: * reader.
234: *
235: * @return
236: * @throws IOException
237: * @throws ParameterNotFoundException
238: */
239: private GridCoverage performImage()
240: throws ParameterNotFoundException, IOException {
241:
242: BufferedImage bi = readImage();
243:
244: // get image rectangle of interrest, envelope and lonlatenvelope
245: Object[] o = getImageRegion(bi.getWidth(), bi.getHeight());
246: Rectangle rect = (Rectangle) o[0];
247: // return null if the result GC would have a width or height of zero
248: if (rect.width == 0 || rect.height == 0) {
249: return null;
250: }
251: bi = bi.getSubimage(rect.x, rect.y, rect.width, rect.height);
252:
253: // create a coverage description that matches the sub image (coverage)
254: // for this a new LonLatEnvelope must be set
255: CoverageOffering co = (CoverageOffering) description.clone();
256: co.setLonLatEnvelope((LonLatEnvelope) o[2]);
257:
258: return new ImageGridCoverage(co, (Envelope) o[1], bi);
259: }
260:
261: /**
262: * reads an image from its source
263: *
264: * @return
265: * @throws IOException
266: */
267: private BufferedImage readImage() throws IOException {
268: BufferedImage bi = null;
269: if (source.getClass() == File.class) {
270: String s = ((File) source).getName();
271: String tmp = s.toLowerCase();
272: if (tmp.startsWith("file:")) {
273: tmp = s.substring(6, s.length());
274: bi = ImageUtils.loadImage(new java.io.File(tmp));
275: } else if (tmp.startsWith("http:")) {
276: bi = ImageUtils.loadImage(new URL(s));
277: } else {
278: bi = ImageUtils.loadImage(new java.io.File(s));
279: }
280: } else {
281: bi = ImageUtils.loadImage((InputStream) source);
282: }
283: return bi;
284: }
285:
286: /**
287: * Return the SRS code of our native SRS.
288: */
289: private String getNativeSRSCode() {
290: CodeList[] cl = description.getSupportedCRSs().getNativeSRSs();
291: return cl[0].getCodes()[0];
292: }
293:
294: /**
295: * return the LonLatEnvelope of the entire image in "EPSG:4326"
296: */
297: private Envelope getLLEAsEnvelope() {
298: String code = getNativeSRSCode();
299: LonLatEnvelope lle = description.getLonLatEnvelope();
300: Envelope tmp = GeometryFactory.createEnvelope(lle.getMin()
301: .getX(), lle.getMin().getY(), lle.getMax().getX(), lle
302: .getMax().getY(), null);
303: try {
304: if (!code.equals("EPSG:4326")) {
305: GeoTransformer trans = new GeoTransformer(code);
306: tmp = trans.transform(tmp, "EPSG:4326");
307: }
308: } catch (Exception e) {
309: LOGGER.logError(StringTools.stackTraceToString(e));
310: }
311:
312: return tmp;
313: }
314:
315: /**
316: * Calculate the rectangle that belongs to the given destination envelope in the given source
317: * image.
318: */
319: private Object[] calcRegionRectangle(Envelope dstenv,
320: Envelope srcenv, double srcwidth, double srcheight) {
321: GeoTransform gt = new WorldToScreenTransform(srcenv.getMin()
322: .getX(), srcenv.getMin().getY(),
323: srcenv.getMax().getX(), srcenv.getMax().getY(), 0, 0,
324: srcwidth - 1, srcheight - 1);
325:
326: int minx = (int) Math
327: .round(gt.getDestX(dstenv.getMin().getX()));
328: int miny = (int) Math
329: .round(gt.getDestY(dstenv.getMax().getY()));
330: int maxx = (int) Math
331: .round(gt.getDestX(dstenv.getMax().getX()));
332: int maxy = (int) Math
333: .round(gt.getDestY(dstenv.getMin().getY()));
334: Rectangle rect = new Rectangle(minx, miny, maxx - minx + 1,
335: maxy - miny + 1);
336: LonLatEnvelope lonLatEnvelope = calcLonLatEnvelope(dstenv,
337: getNativeSRSCode());
338:
339: return new Object[] { rect, dstenv, lonLatEnvelope };
340: }
341:
342: /**
343: * returns the region of the source image that intersects with the GridCoverage to be created as
344: * Rectangle as well as the Envelope of the region in the native CRS and the LonLatEnvelope of
345: * this region.
346: *
347: * @param imgwidth
348: * width of the source image
349: * @param imgheight
350: * height of the source image
351: * @return the region of the source image that intersects with the GridCoverage to be created as
352: * Rectangle as well as the Envelope of the region in the native CRS and the
353: * LonLatEnvelope of this region.
354: */
355: private Object[] getImageRegion(int imgwidth, int imgheight) {
356:
357: Envelope imgenvelope = getLLEAsEnvelope();
358: Envelope dstenvelope = envelope.createIntersection(imgenvelope);
359: return calcRegionRectangle(dstenvelope, imgenvelope, imgwidth,
360: imgheight);
361: }
362:
363: /**
364: * returns the region of the source image that intersects with the GridCoverage to be created as
365: * Rectangle as well as the Envelope of the region in the native CRS and the LonLatEnvelope of
366: * this region. For ECW files these values reflect exactly the desired result, as the library
367: * will cut and sample the source image during read.
368: *
369: * @param reqwidth
370: * width of the requested region
371: * @param reqheight
372: * height of the requested region
373: * @return the region of the source image that intersects with the GridCoverage to be created as
374: * Rectangle as well as the Envelope of the region in the native CRS and the
375: * LonLatEnvelope of this region.
376: */
377: private Object[] getECWImageRegion(int reqwidth, int reqheight) {
378:
379: Envelope imgenvelope = getLLEAsEnvelope();
380: Envelope dstenvelope = envelope.createIntersection(imgenvelope);
381:
382: // In case of ECW files, we calculate the rectangle according
383: // to the desired result, not according to the source image,
384: // as clipping and sampling will be done by the ECW library.
385: // Hence dstenvelope and srcenvelope are the same and width/height
386: // must be clipped with our grid-cell (if necessary).
387: double dWidth = (dstenvelope.getWidth() * reqwidth)
388: / envelope.getWidth();
389: double dHeight = (dstenvelope.getHeight() * reqheight)
390: / envelope.getHeight();
391: return calcRegionRectangle(dstenvelope, dstenvelope, dWidth,
392: dHeight);
393: }
394:
395: /**
396: * Allows any resources held by this object to be released. The result of calling any other
397: * method subsequent to a call to this method is undefined. It is important for applications to
398: * call this method when they know they will no longer be using this
399: * <code>GridCoverageReader</code>. Otherwise, the reader may continue to hold on to
400: * resources indefinitely.
401: *
402: * @throws IOException
403: * if an error occured while disposing resources (for example while closing a file).
404: */
405: public void dispose() throws IOException {
406: if (source instanceof InputStream) {
407: ((InputStream) source).close();
408: }
409: }
410:
411: }
|