    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/model/coverage/grid/GridCoverageExchange.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.io.IOException;
    039    import java.io.InputStream;
    040    import java.net.URI;
    041    import java.util.ArrayList;
    042    import java.util.HashMap;
    043    import java.util.List;
    044    import java.util.Map;
    045
    046    import org.deegree.datatypes.CodeList;
    047    import org.deegree.datatypes.QualifiedName;
    048    import org.deegree.framework.log.ILogger;
    049    import org.deegree.framework.log.LoggerFactory;
    050    import org.deegree.framework.util.ConvenienceFileFilter;
    051    import org.deegree.framework.util.StringTools;
    052    import org.deegree.io.shpapi.ShapeFile;
    053    import org.deegree.model.crs.CRSFactory;
    054    import org.deegree.model.crs.CoordinateSystem;
    055    import org.deegree.model.crs.UnknownCRSException;
    056    import org.deegree.model.feature.Feature;
    057    import org.deegree.model.spatialschema.Envelope;
    058    import org.deegree.model.spatialschema.Geometry;
    059    import org.deegree.model.spatialschema.GeometryFactory;
    060    import org.deegree.ogcbase.CommonNamespaces;
    061    import org.deegree.ogcwebservices.InvalidParameterValueException;
    062    import org.deegree.ogcwebservices.wcs.configuration.Directory;
    063    import org.deegree.ogcwebservices.wcs.configuration.Extension;
    064    import org.deegree.ogcwebservices.wcs.configuration.File;
    065    import org.deegree.ogcwebservices.wcs.configuration.GridDirectory;
    066    import org.deegree.ogcwebservices.wcs.configuration.Shape;
    067    import org.deegree.ogcwebservices.wcs.describecoverage.CoverageOffering;
    068
    069    /**
    070     * Support for creation of grid coverages from persistent formats as well as exporting a grid
    071     * coverage to a persistent formats. For example, it allows for creation of grid coverages from the
    072     * GeoTIFF Well-known binary format and exporting to the GeoTIFF file format. Basic implementations
    073     * only require creation of grid coverages from a file format or resource. More sophesticated
    074     * implementations may extract the grid coverages from a database. In such case, a
    075     * <code>GridCoverageExchange</code> instance will hold a connection to a specific database and
    076     * the dispose method will need to be invoked in order to close this connection.
    077     * <p>
    078     *
    079     * @author Andreas Poth
    080     * @version 1.0
    081     * @since 2.0
    082     */
    083    public class GridCoverageExchange {
    084
    085        private static final ILogger LOG = LoggerFactory.getLogger( GridCoverageExchange.class );
    086
    087        private static final URI DEEGREEAPP = CommonNamespaces.buildNSURI( "http://www.deegree.org/app" );
    088
    089        private static final String APP_PREFIX = "app";
    090
    091        /**
    092         *
    093         */
    094        public static final String SHAPE_IMAGE_FILENAME = "FILENAME";
    095
    096        /**
    097         *
    098         */
    099        public static final String SHAPE_DIR_NAME = "FOLDER";
    100
    101        private CoverageOffering coverageOffering;
    102
    103        /**
    104         * @param coverageOffering
    105         */
    106        public GridCoverageExchange( CoverageOffering coverageOffering ) {
    107            this.coverageOffering = coverageOffering;
    108        }
    109
    110        /**
    111         * Returns a grid coverage reader that can manage the specified source
    112         *
    113         * @param source
    114         *            An object that specifies somehow the data source. Can be a
    115         *            {@link java.lang.String}, an {@link java.io.InputStream}, a
    116         *            {@link java.nio.channels.FileChannel}, whatever. It's up to the associated grid
    117         *            coverage reader to make meaningful use of it.
    118         * @return The grid coverage reader.
    119         * @throws IOException
    120         *             if an error occurs during reading.
    121         *
    122         * @revisit We need a mechanism to allow the right GridCoverageReader Something like an SPI.
    123         *          What if we can't find a GridCoverageReader? Do we return null or throw an Exception?
    124         */
    125        public GridCoverageReader getReader( Object source )
    126                                throws IOException {
    127            if ( !( source instanceof InputStream ) ) {
    128                throw new IOException( "source parameter must be an instance of InputStream" );
    129            }
    130            return null;
    131        }
    132
    133        /**
    134         * This method is a deegree specific enhancement of the <tt>GridCoverageExchange</tt>
    135         * class/interface as defined by GeoAPI. Returns a grid coverage reader that can manage the
    136         * specified source
    137         *
    138         * @param source
    139         *            An object that specifies somehow the data source.
    140         * @param description
    141         *            an object describing the grid coverage and the access to avaiable metadata
    142         * @param envelope
    143         * @param format
    144         * @return The grid coverage reader.
    145         * @throws IOException
    146         *             if an error occurs during reading.
    147         *
    148         * @revisit We need a mechanism to allow the right GridCoverageReader Something like an SPI.
    149         *          What if we can't find a GridCoverageReader? Do we return null or throw an Exception?
    150         */
    151        public GridCoverageReader getReader( InputStream source, CoverageOffering description, Envelope envelope,
    152                                             Format format )
    153                                throws IOException {
    154            GridCoverageReader gcr = null;
    155            Extension ext = description.getExtension();
    156            String type = ext.getType();
    157            if ( type.equals( Extension.FILEBASED ) || type.equals( Extension.SCRIPTBASED ) ) {
    158                if ( format.getName().toUpperCase().indexOf( "GEOTIFF" ) > -1 ) {
    159                    gcr = new GeoTIFFGridCoverageReader( source, description, envelope, format );
    160                } else if ( isImageFormat( format ) ) {
    161                    gcr = new ImageGridCoverageReader( source, description, envelope, format );
    162                } else {
    163                    throw new IOException( "not supported file format: " + format.getName() );
    164                }
    165            } else {
    166                throw new IOException( "coverage storage type: " + type
    167                                       + " is not supported with method: getReader(InputStream, "
    168                                       + "CoverageOffering, Envelope, Format )" );
    169            }
    170            return gcr;
    171        }
    172
    173        /**
    174         * This method is a deegree specific enhancement of the <tt>GridCoverageExchange</tt>
    175         * class/interface as defined by GeoAPI. Returns a grid coverage reader that can manage the
    176         * specified source
    177         *
    178         * @param resource
    179         *            a string that specifies somehow the data source (e.g. a file).
    180         * @param description
    181         *            an object describing the grid coverage and the access to avaiable metadata
    182         * @param envelope
    183         * @param format
    184         *
    185         * @return The grid coverage reader.
    186         * @throws IOException
    187         *             if an error occurs during reading.
    188         * @throws InvalidParameterValueException
    189         *
    190         * @revisit We need a mechanism to allow the right GridCoverageReader Something like an SPI.
    191         *          What if we can't find a GridCoverageReader? Do we return null or throw an Exception?
    192         */
    193        public GridCoverageReader getReader( Object resource, CoverageOffering description, Envelope envelope, Format format )
    194                                throws IOException, InvalidParameterValueException {
    195            GridCoverageReader gcr = null;
    196            Extension ext = description.getExtension();
    197            String type = ext.getType();
    198            if ( type.equals( Extension.FILEBASED ) || type.equals( Extension.SCRIPTBASED )) {
    199                File file = new File( null, (String) resource, envelope );
    200                if ( format.getName().toUpperCase().indexOf( "GEOTIFF" ) > -1 ) {
    201                    LOG.logDebug( "creating GeoTIFFGridCoverageReader" );
    202                    gcr = new GeoTIFFGridCoverageReader( file, description, envelope, format );
    203                } else if ( isImageFormat( format ) ) {
    204                    LOG.logDebug( "creating ImageGridCoverageReader" );
    205                    gcr = new ImageGridCoverageReader( file, description, envelope, format );
    206                } else {
    207                    throw new IOException( "not supported file format: " + format.getName() );
    208                }
    209            } else if ( type.equals( Extension.NAMEINDEXED ) ) {
    210                LOG.logDebug( "creating nameIndexed CompoundGridCoverageReader" );
    211                Directory[] dirs = new Directory[] { (Directory) resource };
    212                gcr = getReader( dirs, description, envelope, format );
    213            } else if ( type.equals( Extension.SHAPEINDEXED ) ) {
    214                LOG.logDebug( "creating shapeIndexed CompoundGridCoverageReader" );
    215                File[] files = null;
    216                try {
    217                    files = getFilesFromShape( (Shape) resource, envelope, description );
    218                } catch ( UnknownCRSException e ) {
    219                    throw new InvalidParameterValueException( e );
    220                }
    221                if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
    222                    for ( int i = 0; i < files.length; i++ ) {
    223                        LOG.logDebug( "matching tile: ", files[i].getName() );
    224                    }
    225                }
    226                gcr = getReader( files, description, envelope, format );
    227            } else if ( type.equals( Extension.ORACLEGEORASTER ) ) {
    228                LOG.logDebug( "creating OracleGeoRasterGridCoverageReader" );
    229                Class<?> clzz;
    230                try {
    231                    clzz = Class.forName( "org.deegree.model.coverage.grid.oracle.GeoRasterReaderAccess" );
    232                } catch ( ClassNotFoundException e ) {
    233                    LOG.logError( e.getMessage(), e );
    234                    throw new InvalidParameterValueException( e );
    235                }
    236                GCReaderAccess acc;
    237                try {
    238                    acc = (GCReaderAccess) clzz.newInstance();
    239                } catch ( InstantiationException e ) {
    240                    LOG.logError( e.getMessage(), e );
    241                    throw new InvalidParameterValueException( e );
    242                } catch ( IllegalAccessException e ) {
    243                    LOG.logError( e.getMessage(), e );
    244                    throw new InvalidParameterValueException( e );
    245                }
    246                gcr = acc.createGridCoverageReader( resource, description, envelope, format );
    247            } else if ( type.equals( Extension.DATABASEINDEXED ) ) {
    248                LOG.logDebug( "creating databaseIndexed CompoundGridCoverageReader" );
    249                File[] files = null;
    250                try {
    251                    files = getFilesFromDatabase( (DatabaseIndexedGCMetadata) resource, envelope, description );
    252                } catch ( UnknownCRSException e ) {
    253                    LOG.logError( e.getMessage(), e );
    254                    throw new InvalidParameterValueException( e );
    255                }
    256                if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
    257                    for ( int i = 0; i < files.length; i++ ) {
    258                        LOG.logDebug( "matching tile: ", files[i].getName() );
    259                    }
    260                }
    261                gcr = new CompoundGridCoverageReader( files, description, envelope, format );
    262            } else {
    263                throw new IOException( "coverage storage type: " + type + " is not supported" );
    264            }
    265            return gcr;
    266        }
    267
    268        /**
    269         * reads the names of the grid coverage files intersecting the requested region from the passed
    270         * database.
    271         *
    272         * @param dbigcmd
    273         * @param envelope
    274         * @param description
    275         * @return file list
    276         * @throws UnknownCRSException
    277         * @throws InvalidParameterValueException
    278         */
    279        private File[] getFilesFromDatabase( DatabaseIndexedGCMetadata dbigcmd, Envelope envelope,
    280                                             CoverageOffering description )
    281                                throws UnknownCRSException, InvalidParameterValueException {
    282
    283            CoordinateSystem crs = createNativeCRS( description );
    284
    285            String className = null;
    286            if ( dbigcmd.getJDBCConnection().getDriver().toUpperCase().indexOf( "ORACLE" ) > -1 ) {
    287                className = DatabaseIndexAccessMessages.getString( "oracle" );
    288            } else if ( dbigcmd.getJDBCConnection().getDriver().toUpperCase().indexOf( "POSTGRES" ) > -1 ) {
    289                className = DatabaseIndexAccessMessages.getString( "postgres" );
    290            }
    291            Class<?> clzz;
    292            try {
    293                clzz = Class.forName( className );
    294            } catch ( ClassNotFoundException e ) {
    295                LOG.logError( e.getMessage(), e );
    296                throw new InvalidParameterValueException( className, e );
    297            }
    298            DatabaseIndexAccess dbia;
    299            try {
    300                dbia = (DatabaseIndexAccess) clzz.newInstance();
    301            } catch ( InstantiationException e ) {
    302                LOG.logError( e.getMessage(), e );
    303                throw new InvalidParameterValueException( className, e );
    304            } catch ( IllegalAccessException e ) {
    305                LOG.logError( e.getMessage(), e );
    306                throw new InvalidParameterValueException( className, e );
    307            }
    308
    309            return dbia.getFiles( dbigcmd, envelope, crs );
    310        }
    311
    312        /**
    313         * This method is a deegree specific enhancement of the <tt>GridCoverageExchange</tt>
    314         * class/interface as defined by GeoAPI. Returns a grid coverage reader that can manage the
    315         * specified source
    316         *
    317         * @param resources
    318         *            an array strings that specifies somehow the data sources (e.g. some files).
    319         * @param description
    320         *            an object describing the grid coverage and the access to avaiable metadata
    321         * @param envelope
    322         * @param format
    323         * @return The grid coverage reader.
    324         * @throws IOException
    325         *             if an error occurs during reading.
    326         * @throws InvalidParameterValueException
    327         *
    328         * @revisit We need a mechanism to allow the right GridCoverageReader Something like an SPI.
    329         *          What if we can't find a GridCoverageReader? Do we return null or throw an Exception?
    330         */
    331        public GridCoverageReader getReader( Object[] resources, CoverageOffering description, Envelope envelope,
    332                                             Format format )
    333                                throws IOException, InvalidParameterValueException {
    334
    335            // CS_CoordinateSystem crs = createNativeCRS( description );
    336            GridCoverageReader gcr = null;
    337            Extension ext = description.getExtension();
    338            String type = ext.getType();
    339            File[] files = null;
    340            if ( type.equals( Extension.FILEBASED ) ||  type.equals( Extension.SCRIPTBASED ) ) {
    341                LOG.logDebug( "creating filebased CompoundGridCoverageReader" );
    342                files = (File[]) resources;
    343                gcr = new CompoundGridCoverageReader( files, description, envelope, format );
    344            } else if ( type.equals( Extension.NAMEINDEXED ) ) {
    345                LOG.logDebug( "creating nameIndexed CompoundGridCoverageReader" );
    346                try {
    347                    files = getFilesFromDirectories( (Directory[]) resources, envelope, description );
    348                } catch ( UnknownCRSException e ) {
    349                    throw new InvalidParameterValueException( e );
    350                }
    351                gcr = new CompoundGridCoverageReader( files, description, envelope, format );
    352            } else if ( type.equals( Extension.SHAPEINDEXED ) ) {
    353                LOG.logDebug( "creating shapeIndexed CompoundGridCoverageReader" );
    354                files = (File[]) resources;
    355                gcr = new CompoundGridCoverageReader( files, description, envelope, format );
    356            } else if ( type.equals( Extension.ORACLEGEORASTER ) ) {
    357                LOG.logDebug( "creating OracleGeoRasterGridCoverageReader" );
    358                Class<?> clzz;
    359                try {
    360                    clzz = Class.forName( "org.deegree.model.coverage.grid.oracle.GeoRasterReaderAccess" );
    361                } catch ( ClassNotFoundException e ) {
    362                    LOG.logError( e.getMessage(), e );
    363                    throw new InvalidParameterValueException( e );
    364                }
    365                GCReaderAccess acc;
    366                try {
    367                    acc = (GCReaderAccess) clzz.newInstance();
    368                } catch ( InstantiationException e ) {
    369                    LOG.logError( e.getMessage(), e );
    370                    throw new InvalidParameterValueException( e );
    371                } catch ( IllegalAccessException e ) {
    372                    LOG.logError( e.getMessage(), e );
    373                    throw new InvalidParameterValueException( e );
    374                }
    375                gcr = acc.createGridCoverageReader( resources[0], description, envelope, format );
    376            } else {
    377                throw new IOException( "coverage storage type: " + type + " is not supported" );
    378            }
    379
    380            return gcr;
    381        }
    382
    383        /**
    384         * returns true if the passed format is an image format
    385         *
    386         * @param format
    387         * @return <code>true</code> if the passed format is an image format
    388         */
    389        private boolean isImageFormat( Format format ) {
    390            String frmt = format.getName().toUpperCase();
    391            return frmt.equalsIgnoreCase( "png" ) || frmt.equalsIgnoreCase( "bmp" ) || frmt.equalsIgnoreCase( "tif" )
    392                   || frmt.equalsIgnoreCase( "tiff" ) || frmt.equalsIgnoreCase( "gif" ) || frmt.equalsIgnoreCase( "jpg" )
    393                   || frmt.equalsIgnoreCase( "jpeg" ) || frmt.indexOf( "ECW" ) > -1;
    394        }
    395
    396        /**
    397         * reads the names of the grid coverage files intersecting the requested region from the passed
    398         * shape (name).
    399         *
    400         * @param shape
    401         * @param envelope
    402         *            requested envelope
    403         * @param description
    404         *            description (metadata) of the source coverage
    405         * @return file list
    406         * @throws IOException
    407         * @throws UnknownCRSException
    408         */
    409        private File[] getFilesFromShape( Shape shape, Envelope envelope, CoverageOffering description )
    410                                throws IOException, UnknownCRSException {
    411
    412            CoordinateSystem crs = createNativeCRS( description );
    413
    414            String shapeBaseName = StringTools.replace( shape.getRootFileName(), "\\", "/", true );
    415            String shapeDir = shapeBaseName.substring( 0, shapeBaseName.lastIndexOf( "/" ) + 1 );
    416
    417            ShapeFile shp = new ShapeFile( shapeBaseName );
    418            File[] files = null;
    419            int[] idx = shp.getGeoNumbersByRect( envelope );
    420            if ( idx != null ) {
    421                files = new File[idx.length];
    422                try {
    423                    for ( int i = 0; i < files.length; i++ ) {
    424                        Feature feature = shp.getFeatureByRecNo( idx[i] );
    425                        QualifiedName qn = new QualifiedName( APP_PREFIX, SHAPE_IMAGE_FILENAME, DEEGREEAPP );
    426                        String img = (String) feature.getDefaultProperty( qn ).getValue();
    427                        qn = new QualifiedName( APP_PREFIX, SHAPE_DIR_NAME, DEEGREEAPP );
    428                        String dir = (String) feature.getDefaultProperty( qn ).getValue();
    429                        if ( !( new java.io.File( dir ).isAbsolute() ) ) {
    430                            // solve relative path; it is assumed that the tile directories
    431                            // are located in the same directory as the shape file
    432                            dir = shapeDir + dir;
    433                        }
    434                        Geometry geom = feature.getGeometryPropertyValues()[0];
    435                        Envelope env = geom.getEnvelope();
    436                        env = GeometryFactory.createEnvelope( env.getMin(), env.getMax(), crs );
    437                        files[i] = new File( crs, dir.concat( "/".concat( img ) ), env );
    438                    }
    439                } catch ( Exception e ) {
    440                    shp.close();
    441                    LOG.logError( e.getMessage(), e );
    442                    throw new IOException( e.getMessage() );
    443                }
    444            } else {
    445                files = new File[0];
    446            }
    447            shp.close();
    448
    449            return files;
    450
    451        }
    452
    453        /**
    454         * reads the names of the grid coverage files intersecting the requested region from raster data
    455         * files contained in the passed directories
    456         *
    457         * @param directories
    458         *            list of directories searched for matching raster files
    459         * @param envelope
    460         *            requested envelope
    461         * @param description
    462         *            description (metadata) of the source coverage
    463         * @return list of files intersecting the requested envelope
    464         * @throws UnknownCRSException
    465         */
    466        private File[] getFilesFromDirectories( Directory[] directories, Envelope envelope, CoverageOffering description )
    467                                throws UnknownCRSException {
    468
    469            CoordinateSystem crs = createNativeCRS( description );
    470
    471            List<File> list = new ArrayList<File>( 1000 );
    472
    473            for ( int i = 0; i < directories.length; i++ ) {
    474
    475                double widthCRS = ( (GridDirectory) directories[i] ).getTileWidth();
    476                double heightCRS = ( (GridDirectory) directories[i] ).getTileHeight();
    477                String[] extensions = directories[i].getFileExtensions();
    478                String dirName = directories[i].getName();
    479
    480                ConvenienceFileFilter fileFilter = new ConvenienceFileFilter( false, extensions );
    481                java.io.File iofile = new java.io.File( dirName );
    482                String[] tiles = iofile.list( fileFilter );
    483                for ( int j = 0; j < tiles.length; j++ ) {
    484                    int pos1 = tiles[j].indexOf( '_' );
    485                    int pos2 = tiles[j].lastIndexOf( '.' );
    486                    String tmp = tiles[j].substring( 0, pos1 );
    487                    double x1 = Double.parseDouble( tmp ) / 1000d;
    488                    tmp = tiles[j].substring( pos1 + 1, pos2 );
    489                    double y1 = Double.parseDouble( tmp ) / 1000d;
    490                    Envelope env = GeometryFactory.createEnvelope( x1, y1, x1 + widthCRS, y1 + heightCRS, crs );
    491                    if ( env.intersects( envelope ) ) {
    492                        File file = new File( crs, dirName + '/' + tiles[j], env );
    493                        list.add( file );
    494                    }
    495                }
    496
    497            }
    498
    499            File[] files = list.toArray( new File[list.size()] );
    500
    501            return files;
    502        }
    503
    504        /**
    505         * creates an instance of <tt>CS_CoordinateSystem</tt> from the name of the native CRS of the
    506         * grid coverage
    507         *
    508         * @param description
    509         * @return the crs
    510         * @throws UnknownCRSException
    511         */
    512        private CoordinateSystem createNativeCRS( CoverageOffering description )
    513                                throws UnknownCRSException {
    514            String srs = description.getSupportedCRSs().getNativeSRSs()[0].getCodes()[0];
    515
    516            return CRSFactory.create( srs );
    517        }
    518
    519        /**
    520         * Returns a GridCoverageWriter that can write the specified format. The file format name is
    521         * determined from the {@link Format} interface. Sample file formats include:
    522         *
    523         * <blockquote><table>
    524         * <tr>
    525         * <td>"GeoTIFF"</td>
    526         * <td>&nbsp;&nbsp;- GeoTIFF</td>
    527         * </tr>
    528         * <tr>
    529         * <td>"PIX"</td>
    530         * <td>&nbsp;&nbsp;- PCI Geomatics PIX</td>
    531         * </tr>
    532         * <tr>
    533         * <td>"HDF-EOS"</td>
    534         * <td>&nbsp;&nbsp;- NASA HDF-EOS</td>
    535         * </tr>
    536         * <tr>
    537         * <td>"NITF"</td>
    538         * <td>&nbsp;&nbsp;- National Image Transfer Format</td>
    539         * </tr>
    540         * <tr>
    541         * <td>"STDS-DEM"</td>
    542         * <td>&nbsp;&nbsp;- Standard Transfer Data Standard</td>
    543         * </tr>
    544         * </table></blockquote>
    545         *
    546         * @param destination
    547         *            An object that specifies somehow the data destination. Can be a
    548         *            {@link java.lang.String}, an {@link java.io.OutputStream}, a
    549         *            {@link java.nio.channels.FileChannel}, whatever. It's up to the associated grid
    550         *            coverage writer to make meaningful use of it.
    551         * @param format
    552         *            the output format.
    553         * @return The grid coverage writer.
    554         * @throws IOException
    555         *             if an error occurs during reading.
    556         */
    557        public GridCoverageWriter getWriter( Object destination, Format format )
    558                                throws IOException {
    559
    560            LOG.logDebug( "requested format: ", format.getName() );
    561
    562            GridCoverageWriter gcw = null;
    563
    564            if ( !isKnownFormat( format ) ) {
    565                throw new IOException( "not supported Format: " + format );
    566            }
    567
    568            Map<String, Object> metadata = new HashMap<String, Object>();
    569            metadata.put( "offset", coverageOffering.getExtension().getOffset() );
    570            metadata.put( "scaleFactor", coverageOffering.getExtension().getScaleFactor() );
    571            if ( format.getName().equalsIgnoreCase( "GEOTIFF" ) ) {
    572                gcw = new GeoTIFFGridCoverageWriter( destination, metadata, null, null, format );
    573            } else if ( isImageFormat( format ) ) {
    574                gcw = new ImageGridCoverageWriter( destination, metadata, null, null, format );
    575            } else if ( format.getName().equalsIgnoreCase( "GML" ) || format.getName().equalsIgnoreCase( "GML2" )
    576                        || format.getName().equalsIgnoreCase( "GML3" ) ) {
    577                gcw = new GMLGridCoverageWriter( destination, metadata, null, null, format );
    578            } else if ( format.getName().equalsIgnoreCase( "XYZ" ) ) {
    579                gcw = new XYZGridCoverageWriter( destination, metadata, null, null, format );
    580            } else {
    581                throw new IOException( "not supported Format: " + format );
    582            }
    583
    584            return gcw;
    585        }
    586
    587        /**
    588         * validates if a passed format is known to an instance of <tt>GridCoverageExchange</tt>
    589         *
    590         * @param format
    591         * @return <code>true</code> if the format is known, <code>false</code> otherwise.
    592         */
    593        private boolean isKnownFormat( Format format ) {
    594            CodeList[] codeList = coverageOffering.getSupportedFormats().getFormats();
    595            for ( int i = 0; i < codeList.length; i++ ) {
    596                String[] codes = codeList[i].getCodes();
    597                for ( int j = 0; j < codes.length; j++ ) {
    598                    if ( format.getName().equalsIgnoreCase( codes[j] ) ) {
    599                        return true;
    600                    }
    601                }
    602            }
    603            LOG.logDebug( format.getName() + " not supported" );
    604            return false;
    605        }
    606
    607    }