001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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    }