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 }