001 // $HeadURL: http://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/model/coverage/grid/ImageGridCoverageReader.java $
002 /*----------------------------------------------------------------------------
003 This file is part of deegree, http://deegree.org/
004 Copyright (C) 2001-2009 by:
005 Department of Geography, University of Bonn
006 and
007 lat/lon GmbH
008
009 This library is free software; you can redistribute it and/or modify it under
010 the terms of the GNU Lesser General Public License as published by the Free
011 Software Foundation; either version 2.1 of the License, or (at your option)
012 any later version.
013 This library is distributed in the hope that it will be useful, but WITHOUT
014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016 details.
017 You should have received a copy of the GNU Lesser General Public License
018 along with this library; if not, write to the Free Software Foundation, Inc.,
019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020
021 Contact information:
022
023 lat/lon GmbH
024 Aennchenstr. 19, 53177 Bonn
025 Germany
026 http://lat-lon.de/
027
028 Department of Geography, University of Bonn
029 Prof. Dr. Klaus Greve
030 Postfach 1147, 53001 Bonn
031 Germany
032 http://www.geographie.uni-bonn.de/deegree/
033
034 e-mail: info@deegree.org
035 ----------------------------------------------------------------------------*/
036 package org.deegree.model.coverage.grid;
037
038 import java.awt.Rectangle;
039 import java.awt.image.BufferedImage;
040 import java.io.IOException;
041 import java.io.InputStream;
042 import java.net.URL;
043
044 import org.deegree.datatypes.CodeList;
045 import org.deegree.datatypes.parameter.GeneralParameterValueIm;
046 import org.deegree.datatypes.parameter.InvalidParameterNameException;
047 import org.deegree.datatypes.parameter.InvalidParameterValueException;
048 import org.deegree.datatypes.parameter.OperationParameterIm;
049 import org.deegree.datatypes.parameter.ParameterNotFoundException;
050 import org.deegree.framework.log.ILogger;
051 import org.deegree.framework.log.LoggerFactory;
052 import org.deegree.framework.util.ImageUtils;
053 import org.deegree.framework.util.StringTools;
054 import org.deegree.graphics.transformation.GeoTransform;
055 import org.deegree.graphics.transformation.WorldToScreenTransform;
056 import org.deegree.io.ecwapi.ECWReader;
057 import org.deegree.model.crs.GeoTransformer;
058 import org.deegree.model.spatialschema.Envelope;
059 import org.deegree.model.spatialschema.GeometryFactory;
060 import org.deegree.ogcwebservices.LonLatEnvelope;
061 import org.deegree.ogcwebservices.wcs.configuration.File;
062 import org.deegree.ogcwebservices.wcs.describecoverage.CoverageOffering;
063
064 /**
065 * GridCoverageReader for reading files as defined by the deegree CoverageOffering Extension type
066 * 'File'. Known formats are: tiff, GeoTiff, jpeg, bmp, gif, png and img (IDRISI)
067 *
068 * @version $Revision: 21400 $
069 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
070 * @author last edited by: $Author: apoth $
071 *
072 * @version 1.0. $Revision: 21400 $, $Date: 2009-12-11 12:01:34 +0100 (Fri, 11 Dec 2009) $
073 *
074 * @since 2.0
075 */
076
077 public class ImageGridCoverageReader extends AbstractGridCoverageReader {
078
079 private static final ILogger LOGGER = LoggerFactory.getLogger( ImageGridCoverageReader.class );
080
081 /**
082 * @param source
083 * source file of the coverage
084 * @param description
085 * description of the data contained in the source file
086 * @param envelope
087 * desired envelope of the coverage to be read
088 * @param format
089 * image format of the source file
090 */
091 public ImageGridCoverageReader( File source, CoverageOffering description, Envelope envelope, Format format ) {
092 super( source, description, envelope, format );
093 }
094
095 /**
096 * @param source
097 * @param description
098 * description of the data contained in the source file
099 * @param envelope
100 * desired envelope of the coverage to be read
101 * @param format
102 * image format of the source file
103 */
104 public ImageGridCoverageReader( InputStream source, CoverageOffering description, Envelope envelope, Format format ) {
105 super( source, description, envelope, format );
106 }
107
108 /**
109 * Read the grid coverage from the current stream position, and move to the next grid coverage.
110 *
111 * @param parameters
112 * An optional set of parameters. Should be any or all of the parameters returned by
113 * {@link "org.opengis.coverage.grid.Format#getReadParameters"}.
114 * @return A new {@linkplain GridCoverage grid coverage} from the input source.
115 * @throws InvalidParameterNameException
116 * if a parameter in <code>parameters</code> doesn't have a recognized name.
117 * @throws InvalidParameterValueException
118 * if a parameter in <code>parameters</code> doesn't have a valid value.
119 * @throws ParameterNotFoundException
120 * if a parameter was required for the operation but was not provided in the
121 * <code>parameters</code> list.
122 * @throws IOException
123 * if a read operation failed for some other input/output reason, including
124 * {@link java.io.FileNotFoundException} if no file with the given <code>name</code>
125 * can be found, or {@link javax.imageio.IIOException} if an error was thrown by the
126 * underlying image library.
127 */
128 public GridCoverage read( GeneralParameterValueIm[] parameters )
129 throws InvalidParameterNameException, InvalidParameterValueException,
130 ParameterNotFoundException, IOException {
131
132 String frmt = description.getSupportedFormats().getNativeFormat().getCode();
133 GridCoverage gc = null;
134 if ( frmt.equalsIgnoreCase( "ecw" ) ) {
135 gc = performECW( parameters );
136 } else if ( frmt.equalsIgnoreCase( "png" ) || frmt.equalsIgnoreCase( "bmp" ) || frmt.equalsIgnoreCase( "tif" )
137 || frmt.equalsIgnoreCase( "tiff" ) || frmt.equalsIgnoreCase( "gif" )
138 || frmt.equalsIgnoreCase( "jpg" ) || frmt.equalsIgnoreCase( "jpeg" ) ) {
139 gc = performImage();
140 } else {
141 throw new InvalidParameterValueException( "unknown format", "native format", format );
142 }
143
144 return gc;
145 }
146
147 /**
148 * performs the creation of a <tt>ImageGridCoverage</tt> from the source assigned to this
149 * reader.
150 *
151 * @param parameters
152 * @throws IOException
153 */
154 private GridCoverage performECW( GeneralParameterValueIm[] parameters )
155 throws IOException {
156
157 BufferedImage bi = null;
158 CoverageOffering co = null;
159 Object[] o = null;
160
161 ECWReader ecwFile = null;
162 try {
163 String s = ( (File) source ).getName();
164 ecwFile = new ECWReader( s );
165
166 // get the requested dimension in pixels
167 int reqWidth = 0;
168 int reqHeight = 0;
169 for ( int i = 0; i < parameters.length; i++ ) {
170 OperationParameterIm op = (OperationParameterIm) parameters[i].getDescriptor();
171 String name = op.getName();
172 if ( name.equalsIgnoreCase( "WIDTH" ) ) {
173 Object vo = op.getDefaultValue();
174 reqWidth = ( (Integer) vo ).intValue();
175 } else if ( name.equalsIgnoreCase( "HEIGHT" ) ) {
176 Object vo = op.getDefaultValue();
177 reqHeight = ( (Integer) vo ).intValue();
178 }
179 }
180
181 // calculate image region of interest
182 o = getECWImageRegion( reqWidth, reqHeight );
183 Envelope envl = (Envelope) o[1];
184
185 Rectangle rect = (Rectangle) o[0];
186 bi = ecwFile.getBufferedImage( envl, rect.width, rect.height );
187
188 // create a coverage description that matches the sub image (coverage)
189 // for this a new LonLatEnvelope must be set
190 co = (CoverageOffering) description.clone();
191 co.setLonLatEnvelope( (LonLatEnvelope) o[2] );
192
193 } catch ( Exception e ) {
194 e.printStackTrace();
195 throw new IOException( StringTools.stackTraceToString( e ) );
196 } finally {
197 // free the ECW cache memory
198 if ( ecwFile != null ) {
199 ecwFile.close();
200 }
201 }
202
203 return new ImageGridCoverage( co, (Envelope) o[1], bi );
204
205 }
206
207 /**
208 * performs the creation of a <tt>ImageGridCoverage</tt> from the source assigned to this
209 * reader.
210 *
211 * @throws IOException
212 * @throws ParameterNotFoundException
213 */
214 private GridCoverage performImage()
215 throws ParameterNotFoundException, IOException {
216
217 BufferedImage bi = readImage();
218
219 // get image rectangle of interrest, envelope and lonlatenvelope
220 Object[] o = getImageRegion( bi.getWidth(), bi.getHeight() );
221 Rectangle rect = (Rectangle) o[0];
222 // return null if the result GC would have a width or height of zero
223 if ( rect.width == 0 || rect.height == 0 ) {
224 return null;
225 }
226 bi = bi.getSubimage( rect.x, rect.y, rect.width, rect.height );
227
228 // create a coverage description that matches the sub image (coverage)
229 // for this a new LonLatEnvelope must be set
230 CoverageOffering co = (CoverageOffering) description.clone();
231 co.setLonLatEnvelope( (LonLatEnvelope) o[2] );
232
233 return new ImageGridCoverage( co, (Envelope) o[1], bi );
234 }
235
236 /**
237 * reads an image from its source
238 *
239 * @throws IOException
240 */
241 private BufferedImage readImage()
242 throws IOException {
243 BufferedImage bi = null;
244 if ( source.getClass() == File.class ) {
245 String s = ( (File) source ).getName();
246 String tmp = s.toLowerCase();
247 if ( tmp.startsWith( "file:" ) ) {
248 tmp = s.substring( 6, s.length() );
249 bi = ImageUtils.loadImage( new java.io.File( tmp ) );
250 } else if ( tmp.startsWith( "http:" ) ) {
251 bi = ImageUtils.loadImage( new URL( s ) );
252 } else {
253 bi = ImageUtils.loadImage( new java.io.File( s ) );
254 }
255 } else {
256 bi = ImageUtils.loadImage( (InputStream) source );
257 }
258 return bi;
259 }
260
261 /**
262 * Return the SRS code of our native SRS.
263 */
264 private String getNativeSRSCode() {
265 CodeList[] cl = description.getSupportedCRSs().getNativeSRSs();
266 return cl[0].getCodes()[0];
267 }
268
269 /**
270 * return the LonLatEnvelope of the entire image in "EPSG:4326"
271 */
272 private Envelope getLLEAsEnvelope() {
273 String code = getNativeSRSCode();
274 LonLatEnvelope lle = description.getLonLatEnvelope();
275 Envelope tmp = GeometryFactory.createEnvelope( lle.getMin().getX(), lle.getMin().getY(), lle.getMax().getX(),
276 lle.getMax().getY(), null );
277 try {
278 if ( !code.equals( "EPSG:4326" ) ) {
279 GeoTransformer trans = new GeoTransformer( code );
280 tmp = trans.transform( tmp, "EPSG:4326" );
281 }
282 } catch ( Exception e ) {
283 LOGGER.logError( StringTools.stackTraceToString( e ) );
284 }
285
286 return tmp;
287 }
288
289 /**
290 * Calculate the rectangle that belongs to the given destination envelope in the given source
291 * image.
292 */
293 private Object[] calcRegionRectangle( Envelope dstenv, Envelope srcenv, double srcwidth, double srcheight ) {
294 GeoTransform gt = new WorldToScreenTransform( srcenv.getMin().getX(), srcenv.getMin().getY(),
295 srcenv.getMax().getX(), srcenv.getMax().getY(), 0, 0,
296 srcwidth - 1, srcheight - 1 );
297
298 int minx = (int) Math.round( gt.getDestX( dstenv.getMin().getX() ) );
299 int miny = (int) Math.round( gt.getDestY( dstenv.getMax().getY() ) );
300 int maxx = (int) Math.round( gt.getDestX( dstenv.getMax().getX() ) );
301 int maxy = (int) Math.round( gt.getDestY( dstenv.getMin().getY() ) );
302 Rectangle rect = new Rectangle( minx, miny, maxx - minx + 1, maxy - miny + 1 );
303 LonLatEnvelope lonLatEnvelope = calcLonLatEnvelope( dstenv, getNativeSRSCode() );
304
305 return new Object[] { rect, dstenv, lonLatEnvelope };
306 }
307
308 /**
309 * returns the region of the source image that intersects with the GridCoverage to be created as
310 * Rectangle as well as the Envelope of the region in the native CRS and the LonLatEnvelope of
311 * this region.
312 *
313 * @param imgwidth
314 * width of the source image
315 * @param imgheight
316 * height of the source image
317 * @return the region of the source image that intersects with the GridCoverage to be created as
318 * Rectangle as well as the Envelope of the region in the native CRS and the
319 * LonLatEnvelope of this region.
320 */
321 private Object[] getImageRegion( int imgwidth, int imgheight ) {
322
323 Envelope imgenvelope = getLLEAsEnvelope();
324 Envelope dstenvelope = envelope.createIntersection( imgenvelope );
325 return calcRegionRectangle( dstenvelope, imgenvelope, imgwidth, imgheight );
326 }
327
328 /**
329 * returns the region of the source image that intersects with the GridCoverage to be created as
330 * Rectangle as well as the Envelope of the region in the native CRS and the LonLatEnvelope of
331 * this region. For ECW files these values reflect exactly the desired result, as the library
332 * will cut and sample the source image during read.
333 *
334 * @param reqwidth
335 * width of the requested region
336 * @param reqheight
337 * height of the requested region
338 * @return the region of the source image that intersects with the GridCoverage to be created as
339 * Rectangle as well as the Envelope of the region in the native CRS and the
340 * LonLatEnvelope of this region.
341 */
342 private Object[] getECWImageRegion( int reqwidth, int reqheight ) {
343
344 Envelope imgenvelope = getLLEAsEnvelope();
345 Envelope dstenvelope = envelope.createIntersection( imgenvelope );
346
347 // In case of ECW files, we calculate the rectangle according
348 // to the desired result, not according to the source image,
349 // as clipping and sampling will be done by the ECW library.
350 // Hence dstenvelope and srcenvelope are the same and width/height
351 // must be clipped with our grid-cell (if necessary).
352 double dWidth = ( dstenvelope.getWidth() * reqwidth ) / envelope.getWidth();
353 double dHeight = ( dstenvelope.getHeight() * reqheight ) / envelope.getHeight();
354 return calcRegionRectangle( dstenvelope, dstenvelope, dWidth, dHeight );
355 }
356
357 /**
358 * Allows any resources held by this object to be released. The result of calling any other
359 * method subsequent to a call to this method is undefined. It is important for applications to
360 * call this method when they know they will no longer be using this
361 * <code>GridCoverageReader</code>. Otherwise, the reader may continue to hold on to
362 * resources indefinitely.
363 *
364 * @throws IOException
365 * if an error occured while disposing resources (for example while closing a file).
366 */
367 public void dispose()
368 throws IOException {
369 if ( source instanceof InputStream ) {
370 ( (InputStream) source ).close();
371 }
372 }
373
374 }