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