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 **************************************************************************************************/