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