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 }