// $Header: /home/deegree/jail/deegreerepository/deegree/src/org/deegree/model/coverage/grid/GeoTIFFGridCoverageReader.java,v 1.15 2006/11/27 09:07:52 poth Exp $ /*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2006 by: EXSE, Department of Geography, University of Bonn http://www.giub.uni-bonn.de/deegree/ lat/lon GmbH http://www.lat-lon.de This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: Andreas Poth lat/lon GmbH Aennchenstr. 19 53115 Bonn Germany E-Mail: poth@lat-lon.de Prof. Dr. Klaus Greve Department of Geography University of Bonn Meckenheimer Allee 166 53115 Bonn Germany E-Mail: greve@giub.uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.model.coverage.grid; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.io.IOException; import java.io.InputStream; import java.net.URL; import javax.media.jai.JAI; import javax.media.jai.RenderedOp; import org.deegree.datatypes.CodeList; import org.deegree.framework.log.ILogger; import org.deegree.framework.log.LoggerFactory; import org.deegree.framework.util.StringTools; import org.deegree.graphics.transformation.GeoTransform; import org.deegree.graphics.transformation.WorldToScreenTransform; import org.deegree.model.crs.GeoTransformer; import org.deegree.model.crs.IGeoTransformer; import org.deegree.model.spatialschema.Envelope; import org.deegree.model.spatialschema.GeometryFactory; import org.deegree.ogcwebservices.LonLatEnvelope; import org.deegree.ogcwebservices.wcs.configuration.File; import org.deegree.ogcwebservices.wcs.describecoverage.CoverageOffering; import org.opengis.coverage.grid.Format; import org.opengis.coverage.grid.GridCoverage; import org.opengis.parameter.GeneralParameterValue; import org.opengis.parameter.InvalidParameterNameException; import org.opengis.parameter.InvalidParameterValueException; import org.opengis.parameter.ParameterNotFoundException; import com.sun.media.jai.codec.MemoryCacheSeekableStream; import com.sun.media.jai.codec.SeekableStream; /** * GridCoverageReader for reading files as defined by the deegree * CoverageOffering Extension type 'File'. Known formats are: * tiff, GeoTiff, jpeg, bmp, gif, png and img (IDRISI) * * @version $Revision: 1.15 $ * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> * @author last edited by: $Author: poth $ * * @version 1.0. $Revision: 1.15 $, $Date: 2006/11/27 09:07:52 $ * * @since 2.0 */ public class GeoTIFFGridCoverageReader extends AbstractGridCoverageReader { private static final ILogger LOGGER = LoggerFactory.getLogger(GeoTIFFGridCoverageReader.class); private SeekableStream sst = null; /** * @param source source file of the coverage * @param description description of the data contained in the source file * @param envelope desired envelope of the coverage to be read * @param format image format of the source file */ public GeoTIFFGridCoverageReader(File source, CoverageOffering description, Envelope envelope, Format format) { super(source, description, envelope, format ); } /** * @param source * @param description description of the data contained in the source file * @param envelope desired envelope of the coverage to be read * @param format image format of the source file */ public GeoTIFFGridCoverageReader(InputStream source, CoverageOffering description, Envelope envelope, Format format) { super(source, description, envelope, format ); } /** * Read the grid coverage from the current stream position, and move to the next grid * coverage. * * @param parameters An optional set of parameters. Should be any or all of the * parameters returned by {@link org.opengis.coverage.grid.Format#getReadParameters}. * @return A new {@linkplain GridCoverage grid coverage} from the input source. * @throws InvalidParameterNameException if a parameter in <code>parameters</code> * doesn't have a recognized name. * @throws InvalidParameterValueException if a parameter in <code>parameters</code> * doesn't have a valid value. * @throws ParameterNotFoundException if a parameter was required for the operation but was * not provided in the <code>parameters</code> list. * @throws IOException if a read operation failed for some other input/output reason, including * {@link java.io.FileNotFoundException} if no file with the given <code>name</code> can * be found, or {@link javax.imageio.IIOException} if an error was thrown by the * underlying image library. */ public GridCoverage read(GeneralParameterValue[] parameters) throws InvalidParameterNameException, InvalidParameterValueException, ParameterNotFoundException, IOException { RenderedOp rop = readGeoTIFF(); int w = rop.getWidth(); int h = rop.getHeight(); // get image rectangle of interrest, envelope and lonlatenvelope Object[] o = getRasterRegion( w, h ); Rectangle rect = (Rectangle)o[0]; //return null if the result GC would have a width or height of zero if ( rect.width == 0 || rect.height == 0 ) { return null; } // create a coverage description that matches the sub image (coverage) // for this a new LonLatEnvelope must be set CoverageOffering co = (CoverageOffering)description.clone(); co.setLonLatEnvelope( (LonLatEnvelope)o[2] ); // extract required area from the tiff data Raster raster = rop.getData( rect ); GridCoverage gc = createGridCoverage( raster, co, (Envelope)o[1] ); return gc; } /** * creates an instance of <tt>GridCoverage</tt> from the passed Raster, * CoverageOffering and Envelope. Depending on the transfer type of the * the passed raster different types of GirdCoverages will be created. * possilbe transfer types are: * <ul> * <li>DataBuffer.TYPE_BYTE * <li>DataBuffer.TYPE_DOUBLE * <li>DataBuffer.TYPE_FLOAT * <li>DataBuffer.TYPE_INT * <li>DataBuffer.TYPE_SHORT * <li>DataBuffer.TYPE_BYTE * <li>DataBuffer.TYPE_USHORT * </ul> * @param raster * @param co * @param env * @return * @throws InvalidParameterValueException */ private GridCoverage createGridCoverage(Raster raster, CoverageOffering co, Envelope env) throws InvalidParameterValueException { GridCoverage gc = null; int type = raster.getTransferType(); switch (type) { case DataBuffer.TYPE_BYTE: { gc = createByteGridCoverage(raster, co, env); break; } case DataBuffer.TYPE_DOUBLE: case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_INT: { throw new InvalidParameterValueException("not supported transfertype "+ "for geotiff ", "type", type); } case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: { gc = createShortGridCoverage(raster, co, env); break; } case DataBuffer.TYPE_UNDEFINED: default: throw new InvalidParameterValueException("unkown transfertype for geotiff ", "type", type); } return gc; } /** * creates a GridCoverage from the passed Raster. The contains data in * <tt>DataBuffer.TYPE_BYTE</tt> format so the result GridCoverage is of * type <tt>ByteGridCoverage </tt> * @param raster * @param co * @param env * @return */ private ByteGridCoverage createByteGridCoverage(Raster raster, CoverageOffering co, Envelope env) { Rectangle rect = raster.getBounds(); int bands = raster.getNumBands(); byte[] data = (byte[])raster.getDataElements( rect.x, rect.y, rect.width, rect.height, null); byte[][][] mat = new byte[bands][rect.height][rect.width]; int k = 0; for (int i = 0; i < mat[0].length; i++) { for (int j = 0; j < mat[0][i].length; j++) { for ( int b = 0; b < bands; b++ ){ mat[b][i][j] = data[k++]; } } } return new ByteGridCoverage( co, env, mat ); } /** * creates a GridCoverage from the passed Raster. The contains data in * <tt>DataBuffer.TYPE_SHORT</tt> format so the result GridCoverage is of * type <tt>ShortGridCoverage </tt> * @param raster * @param co * @param env * @return */ private ShortGridCoverage createShortGridCoverage(Raster raster, CoverageOffering co, Envelope env) { Rectangle rect = raster.getBounds(); int bands = raster.getNumBands(); short[] data = (short[])raster.getDataElements( rect.x, rect.y, rect.width, rect.height, null); short[][][] mat = new short[bands][rect.height][rect.width]; int k = 0; for (int i = 0; i < mat[0].length; i++) { for (int j = 0; j < mat[0][i].length; j++) { for ( int b = 0; b < bands; b++ ){ mat[b][i][j] = data[k++]; } } } return new ShortGridCoverage( co, env, mat ); } /** * reads an image from its source * @return * @throws IOException */ private RenderedOp readGeoTIFF() throws IOException { RenderedOp ro = null; if ( source.getClass() == File.class ) { String s = ((File)source).getName(); String tmp = s.toLowerCase(); URL url = null; if ( tmp.startsWith("file:") ) { tmp = s.substring( 6, s.length() ); url = new java.io.File( tmp ).toURL(); } else if ( tmp.startsWith("http:") ) { url = new URL( s ); } else { url = new java.io.File( s ).toURL(); } sst = new MemoryCacheSeekableStream( url.openStream() ); ro = JAI.create( "stream", sst ); } else { sst = new MemoryCacheSeekableStream( (InputStream)source ); ro = JAI.create( "stream", sst ); } return ro; } /** * returns the region of the source image that intersects with the * GridCoverage to be created as Rectange as well as the Envelope * of the region in the native CRS and the LonLatEnvelope of this region. * @param width width of the source image * @param height height of the source image * @return */ private Object[] getRasterRegion(int width, int height) { CodeList[] cl = description.getSupportedCRSs().getNativeSRSs(); String code = cl[0].getCodes()[0]; LonLatEnvelope lle = description.getLonLatEnvelope(); Envelope tmp = GeometryFactory.createEnvelope(lle.getMin().getX(), lle.getMin().getY(), lle.getMax().getX(), lle.getMax().getY(), null ); try { // transform if native CRS is <> EPSG:4326 if ( !(code.equals("EPSG:4326")) ) { IGeoTransformer trans = new GeoTransformer( code ); tmp = trans.transform( tmp, "EPSG:4326" ); } } catch (Exception e) { LOGGER.logError( StringTools.stackTraceToString(e) ); } // creat tranform object to calculate raster coordinates from // geo coordinates GeoTransform gt = new WorldToScreenTransform( tmp.getMin().getX(), tmp.getMin().getY(), tmp.getMax().getX(), tmp.getMax().getY(), 0, 0, width-1, height-1 ); // calculate envelope of the part of the grid coverage that is contained // within the image Envelope env = envelope.createIntersection( tmp ); LonLatEnvelope lonLatEnvelope = calcLonLatEnvelope(env, code); // calc image coordinates matching the area that is requested int minx = (int)Math.round( gt.getDestX( env.getMin().getX() ) ); int miny = (int)Math.round( gt.getDestY( env.getMax().getY() ) ); int maxx = (int)Math.round( gt.getDestX( env.getMax().getX() ) ); int maxy = (int)Math.round( gt.getDestY( env.getMin().getY() ) ); Rectangle rect = new Rectangle( minx, miny, maxx-minx, maxy-miny ); return new Object[] { rect, env, lonLatEnvelope }; } /** * Allows any resources held by this object to be released. The result * of calling any other method subsequent to a call to this method is * undefined. It is important for applications to call this method when * they know they will no longer be using this <code>GridCoverageReader</code>. * Otherwise, the reader may continue to hold on to resources indefinitely. * * @throws IOException if an error occured while disposing resources * (for example while closing a file). */ public void dispose() throws IOException { if ( sst != null ) { sst.close(); } } } /* ******************************************************************** Changes to this class. What the people have been up to: $Log: GeoTIFFGridCoverageReader.java,v $ Revision 1.15 2006/11/27 09:07:52 poth JNI integration of proj4 has been removed. The CRS functionality now will be done by native deegree code. Revision 1.14 2006/11/07 20:03:26 poth bug fix reading GeoTiff Revision 1.13 2006/09/27 16:46:41 poth transformation method signature changed Revision 1.12 2006/08/15 12:06:26 poth bug fix - reading file Revision 1.11 2006/05/03 20:09:52 poth *** empty log message *** Revision 1.10 2006/05/01 20:15:27 poth *** empty log message *** Revision 1.9 2006/04/06 20:25:26 poth *** empty log message *** Revision 1.8 2006/04/04 20:39:44 poth *** empty log message *** Revision 1.7 2006/03/30 21:20:26 poth *** empty log message *** Revision 1.6 2006/03/15 22:20:09 poth *** empty log message *** Revision 1.5 2006/02/23 07:45:24 poth *** empty log message *** Revision 1.4 2005/11/21 14:58:25 deshmukh CRS to SRS Revision 1.3 2005/09/27 19:53:18 poth no message Revision 1.2 2005/01/18 22:08:54 poth no message Revision 1.2 2004/08/24 07:31:33 ap no message Revision 1.1 2004/08/23 07:00:16 ap no message ********************************************************************** */