001 // $HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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 }