/*---------------- 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
Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: klaus.greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.model.coverage.grid;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.deegree.datatypes.QualifiedName;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.framework.util.StringTools;
import org.deegree.io.oraclegeoraster.GeoRasterDescription;
import org.deegree.io.shpapi.ShapeFile;
import org.deegree.model.crs.CRSFactory;
import org.deegree.model.crs.CoordinateSystem;
import org.deegree.model.crs.UnknownCRSException;
import org.deegree.model.feature.Feature;
import org.deegree.model.spatialschema.Envelope;
import org.deegree.model.spatialschema.Geometry;
import org.deegree.model.spatialschema.GeometryFactory;
import org.deegree.ogcbase.CommonNamespaces;
import org.deegree.ogcwebservices.InvalidParameterValueException;
import org.deegree.ogcwebservices.wcs.configuration.Directory;
import org.deegree.ogcwebservices.wcs.configuration.Extension;
import org.deegree.ogcwebservices.wcs.configuration.File;
import org.deegree.ogcwebservices.wcs.configuration.GridDirectory;
import org.deegree.ogcwebservices.wcs.configuration.Shape;
import org.deegree.ogcwebservices.wcs.describecoverage.CoverageOffering;
import org.opengis.coverage.grid.Format;
import org.opengis.coverage.grid.GridCoverageExchange;
import org.opengis.coverage.grid.GridCoverageReader;
import org.opengis.coverage.grid.GridCoverageWriter;
/**
* Support for creation of grid coverages from persistent formats as well as exporting a grid
* coverage to a persistent formats. For example, it allows for creation of grid coverages from the
* GeoTIFF Well-known binary format and exporting to the GeoTIFF file format. Basic implementations
* only require creation of grid coverages from a file format or resource. More sophesticated
* implementations may extract the grid coverages from a database. In such case, a
* <code>GridCoverageExchange</code> instance will hold a connection to a specific database and
* the {@link #dispose} method will need to be invoked in order to close this connection.
* <p>
*
* @author Andreas Poth
* @version 1.0
* @since 2.0
*/
public class GridCoverageExchangeIm implements GridCoverageExchange {
private static final ILogger LOG = LoggerFactory.getLogger( GridCoverageExchangeIm.class );
private static final URI DEEGREEAPP = CommonNamespaces.buildNSURI( "http://www.deegree.org/app" );
private static final String APP_PREFIX = "app";
public static final String SHAPE_IMAGE_FILENAME = "FILENAME";
public static final String SHAPE_DIR_NAME = "FOLDER";
private List formats = null;
/**
* @param formats
*/
public GridCoverageExchangeIm( Format[] formats ) {
setFormats( formats );
}
/**
* @param formats The formats to set.
*/
public void setFormats( Format[] formats ) {
if ( formats != null ) {
this.formats = Arrays.asList( formats );
} else {
this.formats = new ArrayList();
}
}
/**
* Retrieve information on file formats or resources available with the
* <code>GridCoverageExchange</code> implementation.
*
* @return Information on file formats or resources available with the
* <code>GridCoverageExchange</code> implementation.
*/
public Format[] getFormats() {
return (Format[]) formats.toArray( new Format[formats.size()] );
}
/**
* Returns a grid coverage reader that can manage the specified source
*
* @param source
* An object that specifies somehow the data source. Can be a
* {@link java.lang.String}, an {@link java.io.InputStream}, a
* {@link java.nio.channels.FileChannel}, whatever. It's up to the associated grid
* coverage reader to make meaningful use of it.
* @return The grid coverage reader.
* @throws IOException
* if an error occurs during reading.
*
* @revisit We need a mechanism to allow the right GridCoverageReader Something like an SPI.
* What if we can't find a GridCoverageReader? Do we return null or throw an Exception?
*/
public GridCoverageReader getReader( Object source )
throws IOException {
LOG.entering();
if ( !( source instanceof InputStream ) ) {
throw new IOException( "source parameter must be an instance of InputStream" );
}
LOG.exiting();
return null;
}
/**
* This method is a deegree specific enhancement of the <tt>GridCoverageExchange</tt>
* class/interface as defined by GeoAPI. Returns a grid coverage reader that can manage the
* specified source
*
* @param source
* An object that specifies somehow the data source.
* @param description
* an object describing the grid coverage and the access to avaiable metadata
* @param envelope
* @param format
* @return The grid coverage reader.
* @throws IOException
* if an error occurs during reading.
*
* @revisit We need a mechanism to allow the right GridCoverageReader Something like an SPI.
* What if we can't find a GridCoverageReader? Do we return null or throw an Exception?
*/
public GridCoverageReader getReader( InputStream source, CoverageOffering description,
Envelope envelope, Format format )
throws IOException {
LOG.entering();
GridCoverageReader gcr = null;
Extension ext = description.getExtension();
String type = ext.getType();
if ( type.equals( Extension.FILEBASED ) ) {
if ( format.getName().toUpperCase().indexOf( "GEOTIFF" ) > -1 ) {
gcr = new GeoTIFFGridCoverageReader( source, description, envelope, format );
} else if ( isImageFormat( format ) ) {
gcr = new ImageGridCoverageReader( source, description, envelope, format );
} else {
throw new IOException( "not supported file format: " + format.getName() );
}
} else {
throw new IOException( "coverage storage type: " + type
+ " is not supported with method: getReader(InputStream, "
+ "CoverageOffering, Envelope, Format )" );
}
LOG.exiting();
return gcr;
}
/**
* This method is a deegree specific enhancement of the <tt>GridCoverageExchange</tt>
* class/interface as defined by GeoAPI. Returns a grid coverage reader that can manage the
* specified source
*
* @param resource
* a string that specifies somehow the data source (e.g. a file).
* @param description
* an object describing the grid coverage and the access to avaiable metadata
* @param envelope
* @param format
*
* @return The grid coverage reader.
* @throws IOException
* if an error occurs during reading.
*
* @revisit We need a mechanism to allow the right GridCoverageReader Something like an SPI.
* What if we can't find a GridCoverageReader? Do we return null or throw an Exception?
*/
public GridCoverageReader getReader( Object resource, CoverageOffering description,
Envelope envelope, Format format )
throws IOException, InvalidParameterValueException {
LOG.entering();
GridCoverageReader gcr = null;
Extension ext = description.getExtension();
String type = ext.getType();
if ( type.equals( Extension.FILEBASED ) ) {
File file = new File( null, (String) resource, envelope );
if ( format.getName().toUpperCase().indexOf( "GEOTIFF" ) > -1 ) {
LOG.logInfo( "creating GeoTIFFGridCoverageReader" );
gcr = new GeoTIFFGridCoverageReader( file, description, envelope, format );
} else if ( isImageFormat( format ) ) {
LOG.logInfo( "creating ImageGridCoverageReader" );
gcr = new ImageGridCoverageReader( file, description, envelope, format );
} else {
throw new IOException( "not supported file format: " + format.getName() );
}
} else if ( type.equals( Extension.NAMEINDEXED ) ) {
LOG.logInfo( "creating nameIndexed CompoundGridCoverageReader" );
Directory[] dirs = new Directory[] { (Directory) resource };
gcr = getReader( dirs, description, envelope, format );
} else if ( type.equals( Extension.SHAPEINDEXED ) ) {
LOG.logInfo( "creating shapeIndexed CompoundGridCoverageReader" );
File[] files = null;
try {
files = getFilesFromShape( (Shape) resource, envelope, description );
} catch ( UnknownCRSException e ) {
throw new InvalidParameterValueException( e );
}
gcr = getReader( files, description, envelope, format );
} else if ( type.equals( Extension.ORACLEGEORASTER ) ) {
LOG.logInfo( "creating OracleGeoRasterGridCoverageReader" );
gcr = createOracleGeoRasterGridCoverageReader( (GeoRasterDescription) resource,
description, envelope, format );
} else {
throw new IOException( "coverage storage type: " + type + " is not supported" );
}
LOG.exiting();
return gcr;
}
/**
* This method is a deegree specific enhancement of the <tt>GridCoverageExchange</tt>
* class/interface as defined by GeoAPI. Returns a grid coverage reader that can manage the
* specified source
*
* @param resources
* an array strings that specifies somehow the data sources (e.g. some files).
* @param description
* an object describing the grid coverage and the access to avaiable metadata
* @param envelope
* @return The grid coverage reader.
* @throws IOException
* if an error occurs during reading.
*
* @revisit We need a mechanism to allow the right GridCoverageReader Something like an SPI.
* What if we can't find a GridCoverageReader? Do we return null or throw an Exception?
*/
public GridCoverageReader getReader( Object[] resources, CoverageOffering description,
Envelope envelope, Format format )
throws IOException, InvalidParameterValueException {
LOG.entering();
//CS_CoordinateSystem crs = createNativeCRS( description );
GridCoverageReader gcr = null;
Extension ext = description.getExtension();
String type = ext.getType();
File[] files = null;
if ( type.equals( Extension.FILEBASED ) ) {
LOG.logInfo( "creating filebased CompoundGridCoverageReader" );
files = (File[]) resources;
gcr = new CompoundGridCoverageReader( files, description, envelope, format );
} else if ( type.equals( Extension.NAMEINDEXED ) ) {
LOG.logInfo( "creating nameIndexed CompoundGridCoverageReader" );
try {
files = getFilesFromDirectories( (Directory[]) resources, envelope, description );
} catch ( UnknownCRSException e ) {
throw new InvalidParameterValueException( e );
}
gcr = new CompoundGridCoverageReader( files, description, envelope, format );
} else if ( type.equals( Extension.SHAPEINDEXED ) ) {
LOG.logInfo( "creating shapeIndexed CompoundGridCoverageReader" );
files = (File[]) resources;
gcr = new CompoundGridCoverageReader( files, description, envelope, format );
} else if ( type.equals( Extension.ORACLEGEORASTER ) ) {
LOG.logInfo( "creating OracleGeoRasterGridCoverageReader" );
gcr = createOracleGeoRasterGridCoverageReader( (GeoRasterDescription) resources[0],
description, envelope, format );
} else {
throw new IOException( "coverage storage type: " + type + " is not supported" );
}
LOG.exiting();
return gcr;
}
/**
* Creates a OracleGeoRasterGridCoverageReader instance from the given parameters.
*
* @param grDesc
* @param description
* @param envelope
* @param format
* @return
* @throws IOException
*/
private GridCoverageReader createOracleGeoRasterGridCoverageReader(
GeoRasterDescription grDesc,
CoverageOffering description,
Envelope envelope,
Format format )
throws IOException {
GridCoverageReader gcr = null;
// gcr = new OracleGeoRasterGridCoverageReader( (GeoRasterDescription) resource,
// description, envelope, format );
try {
Class gridCoverageReaderClass = Class.forName( "org.deegree.model.coverage.grid.OracleGeoRasterGridCoverageReader" );
// get constructor
Class[] parameterTypes = new Class[] { GeoRasterDescription.class,
CoverageOffering.class, Envelope.class,
Format.class };
Constructor constructor = gridCoverageReaderClass.getConstructor( parameterTypes );
// call constructor
Object arglist[] = new Object[] { grDesc, description, envelope, format };
gcr = (GridCoverageReader) constructor.newInstance( arglist );
} catch ( ClassNotFoundException e ) {
throw new IOException( "Cannot find Oracle raster library: " + e.getMessage() );
} catch ( Exception e ) {
throw new IOException( e.getMessage() );
}
return gcr;
}
/**
* returns true if the passed format is an image format
*
* @param format
* @return
*/
private boolean isImageFormat( Format format ) {
String frmt = format.getName().toUpperCase();
return frmt.equalsIgnoreCase( "png" ) || frmt.equalsIgnoreCase( "bmp" )
|| frmt.equalsIgnoreCase( "tif" ) || frmt.equalsIgnoreCase( "tiff" )
|| frmt.equalsIgnoreCase( "gif" ) || frmt.equalsIgnoreCase( "jpg" )
|| frmt.equalsIgnoreCase( "jpeg" ) || frmt.indexOf( "ECW" ) > -1;
}
/**
* reads the names of the grid coverage files intersecting the requested region from the passed
* shape (name).
*
* @param shape
* @param envelope
* requested envelope
* @param description
* description (metadata) of the source coverage
* @return
* @throws IOException
* @throws UnknownCRSException
*/
private File[] getFilesFromShape( Shape shape, Envelope envelope, CoverageOffering description )
throws IOException, UnknownCRSException {
LOG.entering();
CoordinateSystem crs = createNativeCRS( description );
String shapeBaseName = StringTools.replace( shape.getRootFileName(), "\\", "/", true );
String shapeDir = shapeBaseName.substring( 0, shapeBaseName.lastIndexOf( "/" ) + 1 );
ShapeFile shp = new ShapeFile( shapeBaseName );
File[] files = null;
int[] idx = shp.getGeoNumbersByRect( envelope );
if ( idx != null ) {
files = new File[idx.length];
try {
for ( int i = 0; i < files.length; i++ ) {
Feature feature = shp.getFeatureByRecNo( idx[i] );
QualifiedName qn = new QualifiedName( APP_PREFIX, SHAPE_IMAGE_FILENAME,
DEEGREEAPP );
String img = (String) feature.getDefaultProperty( qn ).getValue();
qn = new QualifiedName( APP_PREFIX, SHAPE_DIR_NAME, DEEGREEAPP );
String dir = (String) feature.getDefaultProperty( qn ).getValue();
if ( !( new java.io.File( dir ).isAbsolute() ) ) {
// solve relative path; it is assumed that the tile directories
// are located in the same directory as the shape file
dir = shapeDir + dir;
}
Geometry geom = feature.getGeometryPropertyValues()[0];
Envelope env = geom.getEnvelope();
env = GeometryFactory.createEnvelope( env.getMin(), env.getMax(), crs );
files[i] = new File( crs, dir.concat( "/".concat( img ) ), env );
}
} catch ( Exception e ) {
throw new IOException( e.getMessage() + "\n" + StringTools.stackTraceToString( e ) );
}
} else {
files = new File[0];
}
LOG.exiting();
return files;
}
/**
* reads the names of the grid coverage files intersecting the requested region from raster data
* files contained in the passed directories
*
* @param directories
* list of directories searched for matching raster files
* @param envelope
* requested envelope
* @param description
* description (metadata) of the source coverage
* @return list of files intersecting the requested envelope
* @throws UnknownCRSException
* @throws IOException
*/
private File[] getFilesFromDirectories( Directory[] directories, Envelope envelope,
CoverageOffering description ) throws UnknownCRSException {
CoordinateSystem crs = createNativeCRS( description );
List<File> list = new ArrayList<File>( 1000 );
for ( int i = 0; i < directories.length; i++ ) {
double widthCRS = ( (GridDirectory) directories[i] ).getTileWidth();
double heightCRS = ( (GridDirectory) directories[i] ).getTileHeight();
String[] extensions = directories[i].getFileExtensions();
String dirName = directories[i].getName();
DFileFilter fileFilter = new DFileFilter( extensions );
java.io.File iofile = new java.io.File( dirName );
String[] tiles = iofile.list( fileFilter );
for ( int j = 0; j < tiles.length; j++ ) {
int pos1 = tiles[j].indexOf( '_' );
int pos2 = tiles[j].lastIndexOf( '.' );
String tmp = tiles[j].substring( 0, pos1 );
double x1 = Double.parseDouble( tmp ) / 1000d;
tmp = tiles[j].substring( pos1 + 1, pos2 );
double y1 = Double.parseDouble( tmp ) / 1000d;
Envelope env = GeometryFactory.createEnvelope( x1, y1, x1 + widthCRS, y1
+ heightCRS,
crs );
if ( env.intersects( envelope ) ) {
File file = new File( crs, dirName + '/' + tiles[j], env );
list.add( file );
}
}
}
File[] files = list.toArray( new File[list.size()] );
return files;
}
/**
* creates an instance of <tt>CS_CoordinateSystem</tt> from the name of the native CRS of the
* grid coverage
*
* @param description
* @return
* @throws UnknownCRSException
*/
private CoordinateSystem createNativeCRS( CoverageOffering description ) throws UnknownCRSException {
String srs = description.getSupportedCRSs().getNativeSRSs()[0].getCodes()[0];
return CRSFactory.create( srs );
}
/**
* Returns a GridCoverageWriter that can write the specified format. The file format name is
* determined from the {@link Format} interface. Sample file formats include:
*
* <blockquote><table>
* <tr>
* <td>"GeoTIFF"</td>
* <td> - GeoTIFF</td>
* </tr>
* <tr>
* <td>"PIX"</td>
* <td> - PCI Geomatics PIX</td>
* </tr>
* <tr>
* <td>"HDF-EOS"</td>
* <td> - NASA HDF-EOS</td>
* </tr>
* <tr>
* <td>"NITF"</td>
* <td> - National Image Transfer Format</td>
* </tr>
* <tr>
* <td>"STDS-DEM"</td>
* <td> - Standard Transfer Data Standard</td>
* </tr>
* </table></blockquote>
*
* @param destination
* An object that specifies somehow the data destination. Can be a
* {@link java.lang.String}, an {@link java.io.OutputStream}, a
* {@link java.nio.channels.FileChannel}, whatever. It's up to the associated grid
* coverage writer to make meaningful use of it.
* @param format
* the output format.
* @return The grid coverage writer.
* @throws IOException
* if an error occurs during reading.
*/
public GridCoverageWriter getWriter( Object destination, Format format )
throws IOException {
LOG.logInfo( "requested format: " + format.getName() );
GridCoverageWriter gcw = null;
if ( !isKnownFormat( format ) ) {
throw new IOException( "not supported Format: " + format );
}
if ( format.getName().equalsIgnoreCase( "GEOTIFF" ) ) {
gcw = new GeoTIFFGridCoverageWriter( destination, null, null, null, format );
} else if ( isImageFormat( format ) ) {
gcw = new ImageGridCoverageWriter( destination, null, null, null, format );
} else if ( format.getName().equalsIgnoreCase( "GML" ) ) {
gcw = new GMLGridCoverageWriter( destination, null, null, null, format );
} else {
throw new IOException( "not supported Format: " + format );
}
return gcw;
}
/**
* validates if a passed format is known to an instance of <tt>GridCoverageExchange</tt>
*
* @param format
* @return
*/
private boolean isKnownFormat( Format format ) {
for ( Iterator iter = formats.iterator(); iter.hasNext(); ) {
Format element = (Format) iter.next();
if ( element.equals( format ) ) {
return true;
}
}
return false;
}
/**
* 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. Applications should call this method
* when they know they will no longer be using this <code>GridCoverageExchange</code>,
* especially if it was holding a connection to a database.
*
* @throws IOException
* if an error occured while disposing resources (for example closing a database
* connection).
*/
public void dispose()
throws IOException {
formats = null;
}
/**
* class: official version of a FilenameFilter
*/
class DFileFilter implements FilenameFilter {
private Map extensions = null;
public DFileFilter( String[] extensions ) {
this.extensions = new HashMap();
for ( int i = 0; i < extensions.length; i++ ) {
this.extensions.put( extensions[i].toUpperCase(), extensions[i] );
}
}
public String getDescription() {
return "*.*";
}
/*
* (non-Javadoc)
*
* @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
*/
public boolean accept( java.io.File arg0, String name ) {
int pos = name.lastIndexOf( "." );
String ext = name.substring( pos + 1 ).toUpperCase();
return extensions.get( ext ) != null;
}
}
}
/* **************************************************************************************************
* Changes to this class. What the people have been up to:
* $Log: GridCoverageExchangeIm.java,v $
* Revision 1.25 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.24 2006/08/30 16:59:13 mschneider
* Moved definitions of DEEGREEAPP and APP_PREFIX here (this are *not* constant bindings).
*
* Revision 1.23 2006/05/18 15:56:46 mschneider
* Removed compile time dependency to OracleGeoRasterGridCoverageReader.
*
* Revision 1.22 2006/05/18 15:45:19 mschneider
* Removed compile time dependency to OracleGeoRasterGridCoverageReader.
*
* Revision 1.21 2006/05/18 07:58:22 poth
* correction of file comment footer
*
* Revision 1.20 2006/05/18 07:57:17 poth
* bug fix reading shape indexed files
*
* Revision 1.19 2006/05/01 20:15:27 poth
* *** empty log message ***
*
* Revision 1.18 2006/04/06 20:25:26 poth
* *** empty log message ***
*
* Revision 1.17 2006/03/30 21:20:26 poth
* *** empty log message ***
*
* Revision 1.16 2006/03/15 22:20:09 poth
* *** empty log message ***
*
* Revision 1.15 2006/03/05 17:41:07 poth
* *** empty log message ***
*
* Revision 1.14 2006/03/02 21:39:38 poth
* *** empty log message ***
*
* Revision 1.13 2006/03/02 11:06:04 poth
* *** empty log message ***
*
* Revision 1.12 2006/02/23 07:45:24 poth
* *** empty log message ***
*
* Revision 1.11 2006/02/06 16:37:38 poth
* *** empty log message ***
*
* Revision 1.10 2006/02/05 20:33:09 poth
* *** empty log message ***
*
* Revision 1.9 2006/01/29 20:59:08 poth
* *** empty log message ***
*
* Revision 1.8 2005/11/21 18:42:10 mschneider
* Refactoring due to changes in Feature class.
*
* Revision 1.7 2005/11/21 14:58:25 deshmukh
* CRS to SRS
*
* Revision 1.6 2005/11/16 13:45:01
* mschneider Merge of wfs development
* branch. Changes to this class. What the
* people have been up to: Revision 1.5.2.1 2005/11/15 13:36:55 deshmukh Changes to this class. What
* the people have been up to: Modified Object to FeatureProperty Changes to this class. What the
* people have been up to: Revision 1.5 2005/09/27 19:53:18 poth no message
*
* Revision 1.4 2005/08/30 13:40:03 poth no message
*
* Revision 1.3 2005/06/15 16:16:53 poth no message
*
* Revision 1.2 2005/01/18 22:08:54 poth no message
*
* Revision 1.14 2004/08/30 15:44:32 ap no message
*
* Revision 1.13 2004/08/23 06:59:52 ap no message
*
* Revision 1.12 2004/08/12 10:39:32 ap no message
*
* Revision 1.11 2004/08/06 06:41:51 ap grid coverage implementation extension
*
* Revision 1.10 2004/07/20 15:34:30 ap no message
*
* Revision 1.9 2004/07/19 06:20:00 ap no message
*
* Revision 1.8 2004/07/16 06:19:38 ap no message
*
* Revision 1.7 2004/07/15 15:29:42 ap no message
*
* Revision 1.6 2004/07/15 11:31:09 ap no message
*
*
*
************************************************************************************************* */