001 // $HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/coverage/grid/ImageGridCoverageReader.java $
002 /*---------------- FILE HEADER ------------------------------------------
003
004 This file is part of deegree.
005 Copyright (C) 2001-2006 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.crs.IGeoTransformer;
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.ermapper.ecw.JNCSException;
074
075 /**
076 * GridCoverageReader for reading files as defined by the deegree CoverageOffering Extension type
077 * 'File'. Known formats are: tiff, GeoTiff, jpeg, bmp, gif, png and img (IDRISI)
078 *
079 * @version $Revision: 6259 $
080 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
081 * @author last edited by: $Author: bezema $
082 *
083 * @version 1.0. $Revision: 6259 $, $Date: 2007-03-20 10:15:15 +0100 (Di, 20 Mär 2007) $
084 *
085 * @since 2.0
086 */
087
088 public class ImageGridCoverageReader extends AbstractGridCoverageReader {
089
090 private static final ILogger LOGGER = LoggerFactory.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, CoverageOffering description, Envelope envelope,
103 Format format ) {
104 super( source, description, envelope, format );
105 }
106
107 /**
108 * @param source
109 * @param description
110 * description of the data contained in the source file
111 * @param envelope
112 * desired envelope of the coverage to be read
113 * @param format
114 * image format of the source file
115 */
116 public ImageGridCoverageReader( InputStream source, CoverageOffering description,
117 Envelope envelope, Format format ) {
118 super( source, description, envelope, format );
119 }
120
121 /**
122 * Read the grid coverage from the current stream position, and move to the next grid coverage.
123 *
124 * @param parameters
125 * An optional set of parameters. Should be any or all of the parameters returned by
126 * {@link "org.opengis.coverage.grid.Format#getReadParameters"}.
127 * @return A new {@linkplain GridCoverage grid coverage} from the input source.
128 * @throws InvalidParameterNameException
129 * if a parameter in <code>parameters</code> doesn't have a recognized name.
130 * @throws InvalidParameterValueException
131 * if a parameter in <code>parameters</code> doesn't have a valid value.
132 * @throws ParameterNotFoundException
133 * if a parameter was required for the operation but was not provided in the
134 * <code>parameters</code> list.
135 * @throws IOException
136 * if a read operation failed for some other input/output reason, including
137 * {@link java.io.FileNotFoundException} if no file with the given <code>name</code>
138 * can be found, or {@link javax.imageio.IIOException} if an error was thrown by the
139 * underlying image library.
140 */
141 public GridCoverage read( GeneralParameterValueIm[] parameters )
142 throws InvalidParameterNameException, InvalidParameterValueException,
143 ParameterNotFoundException, IOException {
144
145 String frmt = description.getSupportedFormats().getNativeFormat().getCode();
146 GridCoverage gc = null;
147 if ( frmt.equalsIgnoreCase( "ecw" ) ) {
148 gc = performECW( parameters );
149 } else if ( frmt.equalsIgnoreCase( "png" ) || frmt.equalsIgnoreCase( "bmp" )
150 || frmt.equalsIgnoreCase( "tif" ) || frmt.equalsIgnoreCase( "tiff" )
151 || frmt.equalsIgnoreCase( "gif" ) || frmt.equalsIgnoreCase( "jpg" )
152 || frmt.equalsIgnoreCase( "jpeg" ) ) {
153 gc = performImage();
154 } else {
155 throw new InvalidParameterValueException( "unknown format", "native format", format );
156 }
157
158 return gc;
159 }
160
161 /**
162 * performs the creation of a <tt>ImageGridCoverage</tt> from the source assigned to this
163 * reader.
164 *
165 * @param parameters
166 * @return
167 * @throws IOException
168 */
169 private GridCoverage performECW( GeneralParameterValueIm[] parameters )
170 throws IOException {
171
172 BufferedImage bi = null;
173 CoverageOffering co = null;
174 Object[] o = null;
175
176 ECWReader ecwFile = null;
177 try {
178 String s = ( (File) source ).getName();
179 ecwFile = new ECWReader( s );
180
181 // get the requested dimension in pixels
182 int reqWidth = 0;
183 int reqHeight = 0;
184 for ( int i = 0; i < parameters.length; i++ ) {
185 OperationParameterIm op = (OperationParameterIm) parameters[i].getDescriptor();
186 String name = op.getName();
187 if ( name.equalsIgnoreCase( "WIDTH" ) ) {
188 Object vo = op.getDefaultValue();
189 reqWidth = ( (Integer) vo ).intValue();
190 } else if ( name.equalsIgnoreCase( "HEIGHT" ) ) {
191 Object vo = op.getDefaultValue();
192 reqHeight = ( (Integer) vo ).intValue();
193 }
194 }
195
196 // calculate image region of interest
197 o = getECWImageRegion( reqWidth, reqHeight );
198 Envelope envl = (Envelope) o[1];
199
200 Rectangle rect = (Rectangle) o[0];
201 bi = ecwFile.getBufferedImage( envl, rect.width, rect.height );
202
203 // create a coverage description that matches the sub image (coverage)
204 // for this a new LonLatEnvelope must be set
205 co = (CoverageOffering) description.clone();
206 co.setLonLatEnvelope( (LonLatEnvelope) o[2] );
207
208 } catch ( JNCSException e ) {
209 throw new IOException( StringTools.stackTraceToString( e ) );
210 } finally {
211 // free the ECW cache memory
212 if ( ecwFile != null ) {
213 ecwFile.close();
214 }
215 }
216
217 return new ImageGridCoverage( co, (Envelope) o[1], bi );
218
219 }
220
221 /**
222 * performs the creation of a <tt>ImageGridCoverage</tt> from the source assigned to this
223 * reader.
224 *
225 * @return
226 * @throws IOException
227 * @throws ParameterNotFoundException
228 */
229 private GridCoverage performImage()
230 throws ParameterNotFoundException, IOException {
231
232 BufferedImage bi = readImage();
233
234 // get image rectangle of interrest, envelope and lonlatenvelope
235 Object[] o = getImageRegion( bi.getWidth(), bi.getHeight() );
236 Rectangle rect = (Rectangle) o[0];
237 // return null if the result GC would have a width or height of zero
238 if ( rect.width == 0 || rect.height == 0 ) {
239 return null;
240 }
241 bi = bi.getSubimage( rect.x, rect.y, rect.width, rect.height );
242
243 // create a coverage description that matches the sub image (coverage)
244 // for this a new LonLatEnvelope must be set
245 CoverageOffering co = (CoverageOffering) description.clone();
246 co.setLonLatEnvelope( (LonLatEnvelope) o[2] );
247
248 return new ImageGridCoverage( co, (Envelope) o[1], bi );
249 }
250
251 /**
252 * reads an image from its source
253 *
254 * @return
255 * @throws IOException
256 */
257 private BufferedImage readImage()
258 throws IOException {
259 BufferedImage bi = null;
260 if ( source.getClass() == File.class ) {
261 String s = ( (File) source ).getName();
262 String tmp = s.toLowerCase();
263 if ( tmp.startsWith( "file:" ) ) {
264 tmp = s.substring( 6, s.length() );
265 bi = ImageUtils.loadImage( new java.io.File( tmp ) );
266 } else if ( tmp.startsWith( "http:" ) ) {
267 bi = ImageUtils.loadImage( new URL( s ) );
268 } else {
269 bi = ImageUtils.loadImage( new java.io.File( s ) );
270 }
271 } else {
272 bi = ImageUtils.loadImage( (InputStream) source );
273 }
274 return bi;
275 }
276
277 /**
278 * Return the SRS code of our native SRS.
279 */
280 private String getNativeSRSCode() {
281 CodeList[] cl = description.getSupportedCRSs().getNativeSRSs();
282 return cl[0].getCodes()[0];
283 }
284
285 /**
286 * return the LonLatEnvelope of the entire image in "EPSG:4326"
287 */
288 private Envelope getLLEAsEnvelope() {
289 String code = getNativeSRSCode();
290 LonLatEnvelope lle = description.getLonLatEnvelope();
291 Envelope tmp = GeometryFactory.createEnvelope( lle.getMin().getX(), lle.getMin().getY(),
292 lle.getMax().getX(), lle.getMax().getY(),
293 null );
294 try {
295 if ( !code.equals( "EPSG:4326" ) ) {
296 IGeoTransformer trans = new GeoTransformer( code );
297 tmp = trans.transform( tmp, "EPSG:4326" );
298 }
299 } catch ( Exception e ) {
300 LOGGER.logError( StringTools.stackTraceToString( e ) );
301 }
302
303 return tmp;
304 }
305
306 /**
307 * Calculate the rectangle that belongs to the given destination envelope in the given source
308 * image.
309 */
310 private Object[] calcRegionRectangle( Envelope dstenv, Envelope srcenv, double srcwidth,
311 double srcheight ) {
312 GeoTransform gt = new WorldToScreenTransform( srcenv.getMin().getX(),
313 srcenv.getMin().getY(),
314 srcenv.getMax().getX(),
315 srcenv.getMax().getY(), 0, 0, srcwidth - 1,
316 srcheight - 1 );
317
318 int minx = (int) Math.round( gt.getDestX( dstenv.getMin().getX() ) );
319 int miny = (int) Math.round( gt.getDestY( dstenv.getMax().getY() ) );
320 int maxx = (int) Math.round( gt.getDestX( dstenv.getMax().getX() ) );
321 int maxy = (int) Math.round( gt.getDestY( dstenv.getMin().getY() ) );
322 Rectangle rect = new Rectangle( minx, miny, maxx - minx + 1, maxy - miny + 1 );
323 LonLatEnvelope lonLatEnvelope = calcLonLatEnvelope( dstenv, getNativeSRSCode() );
324
325 return new Object[] { rect, dstenv, lonLatEnvelope };
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.
332 *
333 * @param imgwidth
334 * width of the source image
335 * @param imgheight
336 * height of the source image
337 * @return the region of the source image that intersects with the GridCoverage to be created as
338 * Rectangle as well as the Envelope of the region in the native CRS and the
339 * LonLatEnvelope of this region.
340 */
341 private Object[] getImageRegion( int imgwidth, int imgheight ) {
342
343 Envelope imgenvelope = getLLEAsEnvelope();
344 Envelope dstenvelope = envelope.createIntersection( imgenvelope );
345 return calcRegionRectangle( dstenvelope, imgenvelope, imgwidth, imgheight );
346 }
347
348 /**
349 * returns the region of the source image that intersects with the GridCoverage to be created as
350 * Rectangle as well as the Envelope of the region in the native CRS and the LonLatEnvelope of
351 * this region. For ECW files these values reflect exactly the desired result, as the library
352 * will cut and sample the source image during read.
353 *
354 * @param reqwidth
355 * width of the requested region
356 * @param reqheight
357 * height of the requested region
358 * @return the region of the source image that intersects with the GridCoverage to be created as
359 * Rectangle as well as the Envelope of the region in the native CRS and the
360 * LonLatEnvelope of this region.
361 */
362 private Object[] getECWImageRegion( int reqwidth, int reqheight ) {
363
364 Envelope imgenvelope = getLLEAsEnvelope();
365 Envelope dstenvelope = envelope.createIntersection( imgenvelope );
366
367 // In case of ECW files, we calculate the rectangle according
368 // to the desired result, not according to the source image,
369 // as clipping and sampling will be done by the ECW library.
370 // Hence dstenvelope and srcenvelope are the same and width/height
371 // must be clipped with our grid-cell (if necessary).
372 double dWidth = ( dstenvelope.getWidth() * reqwidth ) / envelope.getWidth();
373 double dHeight = ( dstenvelope.getHeight() * reqheight ) / envelope.getHeight();
374 return calcRegionRectangle( dstenvelope, dstenvelope, dWidth, dHeight );
375 }
376
377 /**
378 * Allows any resources held by this object to be released. The result of calling any other
379 * method subsequent to a call to this method is undefined. It is important for applications to
380 * call this method when they know they will no longer be using this
381 * <code>GridCoverageReader</code>. Otherwise, the reader may continue to hold on to
382 * resources indefinitely.
383 *
384 * @throws IOException
385 * if an error occured while disposing resources (for example while closing a file).
386 */
387 public void dispose()
388 throws IOException {
389 if ( source instanceof InputStream ) {
390 ( (InputStream) source ).close();
391 }
392 }
393
394 }
395 /***************************************************************************************************
396 * <code>
397 Changes to this class. What the people have been up to:
398
399 $Log$
400 Revision 1.23 2007/03/13 12:45:25 wanhoff
401 Fixed Javadoc (@link, @return)
402
403 Revision 1.22 2007/02/09 17:28:00 poth
404 *** empty log message ***
405
406 Revision 1.21 2006/11/27 09:07:52 poth
407 JNI integration of proj4 has been removed. The CRS functionality now will be done by native deegree code.
408
409 Revision 1.20 2006/10/27 06:29:37 poth
410 bug fix - missing else added in readImage method
411
412 Revision 1.19 2006/10/17 15:00:18 poth
413 reading image files enhanced
414
415 Revision 1.18 2006/09/27 16:46:41 poth
416 transformation method signature changed
417
418 Revision 1.17 2006/09/22 13:44:45 poth
419 bug fix reading ECW
420
421 Revision 1.16 2006/09/18 17:56:08 poth
422 bug fix - reading ECW-files
423
424 Revision 1.15 2006/09/15 09:07:48 poth
425 ecw file to free memory
426
427 Revision 1.14 2006/09/14 20:07:12 poth
428 bug fix reading ECW
429
430 Revision 1.13 2006/08/08 09:10:11 poth
431 never read parameters removed
432
433 Revision 1.12 2006/07/29 08:51:55 poth
434 bug fix - reading images from files/URLs
435
436 Revision 1.11 2006/07/25 18:33:24 poth
437 bug fix - reading single file grid coverage
438
439 Revision 1.10 2006/05/03 20:09:52 poth
440 *** empty log message ***
441
442 Revision 1.9 2006/05/01 20:15:27 poth
443 *** empty log message ***
444
445 Revision 1.8 2006/04/06 20:25:26 poth
446 *** empty log message ***
447
448 Revision 1.7 2006/04/04 20:39:44 poth
449 *** empty log message ***
450
451 Revision 1.6 2006/03/30 21:20:26 poth
452 *** empty log message ***
453
454 Revision 1.5 2006/02/23 07:45:24 poth
455 *** empty log message ***
456
457 Revision 1.4 2005/11/21 14:58:25 deshmukh
458 CRS to SRS
459
460 Revision 1.3 2005/09/27 19:53:18 poth
461 no message
462
463 Revision 1.2 2005/01/18 22:08:54 poth
464 no message
465
466 Revision 1.9 2004/08/23 06:59:52 ap
467 no message
468
469 Revision 1.8 2004/08/09 06:43:50 ap
470 no message
471
472 Revision 1.6 2004/08/06 06:41:51 ap
473 grid coverage implementation extension
474
475
476 </code>
477 **************************************************************************************************/