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