001 // $HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/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 11:50:52 +0100 (Di, 08 Jan 2008) $ 084 */ 085 public class GeoTIFFGridCoverageReader extends AbstractGridCoverageReader { 086 087 private static final ILogger LOG = LoggerFactory.getLogger( GeoTIFFGridCoverageReader.class ); 088 089 private SeekableStream sst = null; 090 091 /** 092 * @param source 093 * source file of the coverage 094 * @param description 095 * description of the data contained in the source file 096 * @param envelope 097 * desired envelope of the coverage to be read 098 * @param format 099 * image format of the source file 100 */ 101 public GeoTIFFGridCoverageReader( File source, CoverageOffering description, Envelope envelope, Format format ) { 102 super( source, description, envelope, format ); 103 } 104 105 /** 106 * @param source 107 * @param description 108 * description of the data contained in the source file 109 * @param envelope 110 * desired envelope of the coverage to be read 111 * @param format 112 * image format of the source file 113 */ 114 public GeoTIFFGridCoverageReader( InputStream source, CoverageOffering description, Envelope envelope, Format format ) { 115 super( source, description, envelope, format ); 116 } 117 118 /** 119 * Read the grid coverage from the current stream position, and move to the next grid coverage. 120 * 121 * @param parameters 122 * An optional set of parameters. Should be any or all of the parameters returned by 123 * {@link "org.opengis.coverage.grid.Format#getReadParameters"}. 124 * @return A new {@linkplain GridCoverage grid coverage} from the input source. 125 * @throws InvalidParameterNameException 126 * if a parameter in <code>parameters</code> doesn't have a recognized name. 127 * @throws InvalidParameterValueException 128 * if a parameter in <code>parameters</code> doesn't have a valid value. 129 * @throws ParameterNotFoundException 130 * if a parameter was required for the operation but was not provided in the 131 * <code>parameters</code> list. 132 * @throws IOException 133 * if a read operation failed for some other input/output reason, including 134 * {@link java.io.FileNotFoundException} if no file with the given <code>name</code> 135 * can be found, or {@link javax.imageio.IIOException} if an error was thrown by the 136 * underlying image library. 137 */ 138 public GridCoverage read( GeneralParameterValueIm[] parameters ) 139 throws InvalidParameterNameException, InvalidParameterValueException, 140 ParameterNotFoundException, IOException { 141 142 RenderedOp rop = readGeoTIFF(); 143 int w = rop.getWidth(); 144 int h = rop.getHeight(); 145 146 // get image rectangle of interrest, envelope and lonlatenvelope 147 Object[] o = getRasterRegion( w, h ); 148 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 149 LOG.logDebug( "image rectangle of interrest, envelope and lonlatenvelope:" ); 150 for ( int i = 0; i < o.length; i++ ) { 151 LOG.logDebug( o[i].toString() ); 152 } 153 } 154 Rectangle rect = (Rectangle) o[0]; 155 // return null if the result GC would have a width or height of zero 156 if ( rect.width == 0 || rect.height == 0 ) { 157 return null; 158 } 159 // create a coverage description that matches the sub image (coverage) 160 // for this a new LonLatEnvelope must be set 161 CoverageOffering co = (CoverageOffering) description.clone(); 162 co.setLonLatEnvelope( (LonLatEnvelope) o[2] ); 163 164 // extract required area from the tiff data 165 Raster raster = rop.getData( rect ); 166 // use 8 BIT as default assuming a raster contains simple 167 // data like a Landsat TM Band 168 int pxSize = 8; 169 if ( rop.getColorModel() != null ) { 170 pxSize = rop.getColorModel().getPixelSize(); 171 } 172 173 return createGridCoverage( raster, co, (Envelope) o[1], pxSize ); 174 175 } 176 177 /** 178 * creates an instance of <tt>GridCoverage</tt> from the passed Raster, CoverageOffering and 179 * Envelope. Depending on the pixel size of the the passed raster different types of 180 * GirdCoverages will be created. possilbe pixel sized are: 181 * <ul> 182 * <li>8 183 * <li>16 184 * <li>32 185 * <li>64 186 * </ul> 187 * 188 * @param raster 189 * @param co 190 * @param env 191 * @param pxSize 192 * @return 193 * @throws InvalidParameterValueException 194 */ 195 private GridCoverage createGridCoverage( Raster raster, CoverageOffering co, Envelope env, int pxSize ) 196 throws InvalidParameterValueException { 197 198 GridCoverage gc = null; 199 switch ( pxSize ) { 200 case 8: { 201 gc = createByteGridCoverage( raster, co, env ); 202 break; 203 } 204 case 16: { 205 gc = createShortGridCoverage( raster, co, env ); 206 break; 207 } 208 case 32: 209 case 64: { 210 String s = Messages.getMessage( "GC_NOT_SUPPORTED_PS", pxSize ); 211 throw new InvalidParameterValueException( s, "type", pxSize ); 212 } 213 default: 214 String s = Messages.getMessage( "GC_UNKNOWN_PS", pxSize ); 215 throw new InvalidParameterValueException( s, "type", pxSize ); 216 } 217 218 return gc; 219 } 220 221 /** 222 * creates a GridCoverage from the passed Raster. The contains data in 223 * <tt>DataBuffer.TYPE_BYTE</tt> format so the result GridCoverage is of type 224 * <tt>ByteGridCoverage </tt> 225 * 226 * @param raster 227 * @param co 228 * @param env 229 * @return 230 */ 231 private ByteGridCoverage createByteGridCoverage( Raster raster, CoverageOffering co, Envelope env ) { 232 233 Rectangle rect = raster.getBounds(); 234 int bands = raster.getNumBands(); 235 byte[] data = (byte[]) raster.getDataElements( rect.x, rect.y, rect.width, rect.height, null ); 236 byte[][][] mat = new byte[bands][rect.height][rect.width]; 237 int k = 0; 238 for ( int i = 0; i < mat[0].length; i++ ) { 239 for ( int j = 0; j < mat[0][i].length; j++ ) { 240 for ( int b = 0; b < bands; b++ ) { 241 mat[b][i][j] = data[k++]; 242 } 243 } 244 } 245 246 return new ByteGridCoverage( co, env, mat ); 247 } 248 249 /** 250 * creates a GridCoverage from the passed Raster. The contains data in 251 * <tt>DataBuffer.TYPE_SHORT</tt> format so the result GridCoverage is of type 252 * <tt>ShortGridCoverage </tt> 253 * 254 * @param raster 255 * @param co 256 * @param env 257 * @return 258 */ 259 private ShortGridCoverage createShortGridCoverage( Raster raster, CoverageOffering co, Envelope env ) { 260 261 Rectangle rect = raster.getBounds(); 262 int bands = raster.getNumBands(); 263 short[] data = (short[]) raster.getDataElements( rect.x, rect.y, rect.width, rect.height, null ); 264 short[][][] mat = new short[bands][rect.height][rect.width]; 265 int k = 0; 266 for ( int i = 0; i < mat[0].length; i++ ) { 267 for ( int j = 0; j < mat[0][i].length; j++ ) { 268 for ( int b = 0; b < bands; b++ ) { 269 mat[b][i][j] = data[k++]; 270 } 271 } 272 } 273 274 return new ShortGridCoverage( co, env, mat ); 275 } 276 277 /** 278 * reads an image from its source 279 * 280 * @return 281 * @throws IOException 282 */ 283 private RenderedOp readGeoTIFF() 284 throws IOException { 285 286 RenderedOp ro = null; 287 if ( source.getClass() == File.class ) { 288 String s = ( (File) source ).getName(); 289 String tmp = s.toLowerCase(); 290 LOG.logDebug( "load: ", tmp ); 291 URL url = null; 292 if ( tmp.startsWith( "file:" ) ) { 293 tmp = s.substring( 6, s.length() ); 294 url = new java.io.File( tmp ).toURL(); 295 } else if ( tmp.startsWith( "http:" ) ) { 296 url = new URL( s ); 297 } else { 298 url = new java.io.File( s ).toURL(); 299 } 300 sst = new MemoryCacheSeekableStream( url.openStream() ); 301 ro = JAI.create( "stream", sst ); 302 } else { 303 sst = new MemoryCacheSeekableStream( (InputStream) source ); 304 ro = JAI.create( "stream", sst ); 305 } 306 307 return ro; 308 } 309 310 /** 311 * returns the region of the source image that intersects with the GridCoverage to be created as 312 * Rectange as well as the Envelope of the region in the native CRS and the LonLatEnvelope of 313 * this region. 314 * 315 * @param width 316 * width of the source image 317 * @param height 318 * height of the source image 319 * @return 320 */ 321 private Object[] getRasterRegion( int width, int height ) { 322 323 CodeList[] cl = description.getSupportedCRSs().getNativeSRSs(); 324 String code = cl[0].getCodes()[0]; 325 326 LonLatEnvelope lle = description.getLonLatEnvelope(); 327 Envelope tmp = GeometryFactory.createEnvelope( lle.getMin().getX(), lle.getMin().getY(), lle.getMax().getX(), 328 lle.getMax().getY(), null ); 329 try { 330 // transform if native CRS is <> EPSG:4326 331 if ( !( code.equals( "EPSG:4326" ) ) ) { 332 GeoTransformer trans = new GeoTransformer( code ); 333 tmp = trans.transform( tmp, "EPSG:4326" ); 334 } 335 } catch ( Exception e ) { 336 LOG.logError( StringTools.stackTraceToString( e ) ); 337 } 338 // creat tranform object to calculate raster coordinates from 339 // geo coordinates 340 GeoTransform gt = new WorldToScreenTransform( tmp.getMin().getX(), tmp.getMin().getY(), tmp.getMax().getX(), 341 tmp.getMax().getY(), 0, 0, width - 1, height - 1 ); 342 343 // calculate envelope of the part of the grid coverage that is contained 344 // within the image 345 Envelope env = envelope.createIntersection( tmp ); 346 347 LonLatEnvelope lonLatEnvelope = calcLonLatEnvelope( env, code ); 348 // calc image coordinates matching the area that is requested 349 int minx = (int) Math.round( gt.getDestX( env.getMin().getX() ) ); 350 int miny = (int) Math.round( gt.getDestY( env.getMax().getY() ) ); 351 int maxx = (int) Math.round( gt.getDestX( env.getMax().getX() ) ); 352 int maxy = (int) Math.round( gt.getDestY( env.getMin().getY() ) ); 353 Rectangle rect = new Rectangle( minx, miny, maxx - minx + 1, maxy - miny + 1 ); 354 355 return new Object[] { rect, env, lonLatEnvelope }; 356 } 357 358 /** 359 * Allows any resources held by this object to be released. The result of calling any other 360 * method subsequent to a call to this method is undefined. It is important for applications to 361 * call this method when they know they will no longer be using this 362 * <code>GridCoverageReader</code>. Otherwise, the reader may continue to hold on to 363 * resources indefinitely. 364 * 365 * @throws IOException 366 * if an error occured while disposing resources (for example while closing a file). 367 */ 368 public void dispose() 369 throws IOException { 370 if ( sst != null ) { 371 sst.close(); 372 } 373 } 374 375 }