001    // $HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/ogcwebservices/wcs/WCService.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2007 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     Prof. Dr. Klaus Greve
035     Department of Geography
036     University of Bonn
037     Meckenheimer Allee 166
038     53115 Bonn
039     Germany
040     E-Mail: greve@giub.uni-bonn.de
041     
042     ---------------------------------------------------------------------------*/
043    package org.deegree.ogcwebservices.wcs;
044    
045    import java.awt.image.BufferedImage;
046    import java.io.IOException;
047    import java.io.InputStream;
048    import java.net.URL;
049    import java.util.ArrayList;
050    import java.util.List;
051    import java.util.Properties;
052    
053    import org.deegree.datatypes.parameter.GeneralParameterValueIm;
054    import org.deegree.datatypes.parameter.OperationParameterIm;
055    import org.deegree.framework.log.ILogger;
056    import org.deegree.framework.log.LoggerFactory;
057    import org.deegree.framework.util.StringTools;
058    import org.deegree.io.JDBCConnection;
059    import org.deegree.io.oraclegeoraster.GeoRasterDescription;
060    import org.deegree.model.coverage.Coverage;
061    import org.deegree.model.coverage.grid.AbstractGridCoverage;
062    import org.deegree.model.coverage.grid.Format;
063    import org.deegree.model.coverage.grid.GridCoverageExchange;
064    import org.deegree.model.coverage.grid.GridCoverageReader;
065    import org.deegree.model.coverage.grid.ImageGridCoverage;
066    import org.deegree.model.crs.CRSException;
067    import org.deegree.model.crs.CRSTransformationException;
068    import org.deegree.model.crs.GeoTransformer;
069    import org.deegree.model.crs.IGeoTransformer;
070    import org.deegree.model.spatialschema.Envelope;
071    import org.deegree.ogcwebservices.InvalidParameterValueException;
072    import org.deegree.ogcwebservices.OGCWebService;
073    import org.deegree.ogcwebservices.OGCWebServiceException;
074    import org.deegree.ogcwebservices.OGCWebServiceRequest;
075    import org.deegree.ogcwebservices.getcapabilities.OGCCapabilities;
076    import org.deegree.ogcwebservices.wcs.configuration.Directory;
077    import org.deegree.ogcwebservices.wcs.configuration.DirectoryResolution;
078    import org.deegree.ogcwebservices.wcs.configuration.Extension;
079    import org.deegree.ogcwebservices.wcs.configuration.File;
080    import org.deegree.ogcwebservices.wcs.configuration.FileResolution;
081    import org.deegree.ogcwebservices.wcs.configuration.OracleGeoRasterResolution;
082    import org.deegree.ogcwebservices.wcs.configuration.Resolution;
083    import org.deegree.ogcwebservices.wcs.configuration.Shape;
084    import org.deegree.ogcwebservices.wcs.configuration.ShapeResolution;
085    import org.deegree.ogcwebservices.wcs.configuration.WCSConfiguration;
086    import org.deegree.ogcwebservices.wcs.describecoverage.CoverageDescription;
087    import org.deegree.ogcwebservices.wcs.describecoverage.CoverageOffering;
088    import org.deegree.ogcwebservices.wcs.describecoverage.DescribeCoverage;
089    import org.deegree.ogcwebservices.wcs.describecoverage.InvalidCoverageDescriptionExcpetion;
090    import org.deegree.ogcwebservices.wcs.getcapabilities.ContentMetadata;
091    import org.deegree.ogcwebservices.wcs.getcapabilities.WCSGetCapabilities;
092    import org.deegree.ogcwebservices.wcs.getcapabilities.WCSRequestValidator;
093    import org.deegree.ogcwebservices.wcs.getcoverage.GetCoverage;
094    import org.deegree.ogcwebservices.wcs.getcoverage.ResultCoverage;
095    import org.deegree.ogcwebservices.wcs.getcoverage.SpatialSubset;
096    import org.xml.sax.SAXException;
097    
098    /**
099     * @version $Revision: 7788 $
100     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
101     * @author last edited by: $Author: apoth $
102     * 
103     * @version 1.0. $Revision: 7788 $, $Date: 2007-07-19 17:21:54 +0200 (Do, 19 Jul 2007) $
104     * 
105     * @since 2.0
106     */
107    
108    public class WCService implements OGCWebService {
109    
110        private static final ILogger LOG = LoggerFactory.getLogger( WCService.class );
111        
112        private int nor = 5;
113        
114        private int degree = 3;    
115    
116        /**
117         * 
118         */
119        private WCSConfiguration configuration = null;
120    
121        /**
122         * creates a WCService from a configuration
123         * 
124         * @param configuration
125         */
126        public WCService( WCSConfiguration configuration ) {
127            this.configuration = configuration;
128            URL url = WCService.class.getResource( "crstransform.properties" );
129            Properties props = new Properties();
130            try {
131                InputStream is = url.openStream();
132                props.load( is );
133                is.close();
134                nor = Integer.parseInt( props.getProperty( "number_of_reference_points" ) );
135                degree = Integer.parseInt( props.getProperty( "degree" ) );
136            } catch ( Exception e ) {
137                LOG.logError( e.getMessage(), e );
138                LOG.logInfo( "could not load definiton for crs transformation parameters, use default values" );
139            }
140        }
141    
142        /**
143         * returns the capabilities of the WCS
144         * 
145         * @return capabilities of the WCS
146         */
147        public OGCCapabilities getCapabilities() {
148            return configuration;
149        }
150    
151        /**
152         * @param request
153         * @return a CoverageDescription fitting the request
154         * @throws OGCWebServiceException
155         *             if an exception occurs in the process of creating the description.
156         */
157        private CoverageDescription describeCoverage( DescribeCoverage request )
158                                throws OGCWebServiceException {
159    
160            WCSRequestValidator.validate( configuration, request );
161            CoverageOffering[] co = null;
162            try {
163                co = getCoverageOfferings( request );
164            } catch ( IOException ioe ) {
165                LOG.logError( StringTools.stackTraceToString( ioe ) );
166                throw new OGCWebServiceException( ioe.getMessage() );
167            } catch ( SAXException saxe ) {
168                LOG.logError( StringTools.stackTraceToString( saxe ) );
169                throw new OGCWebServiceException( saxe.getMessage() );
170            }
171            CoverageDescription cd = new CoverageDescription( co, request.getVersion() );
172            return cd;
173        }
174    
175        /**
176         * @param request
177         * @return a given Coverage for the request
178         * @throws OGCWebServiceException
179         *             if any kind of exception occurs
180         */
181        private Coverage getCoverage( GetCoverage request )
182                                throws OGCWebServiceException {
183    
184            WCSRequestValidator.validate( configuration, request );
185            Coverage cov = null;
186            if ( request.getOutput().getFormat().getCode().equals( "GML" ) ) {
187                CoverageOffering co;
188                try {
189                    co = getCoverageOffering( request );
190                } catch ( InvalidCoverageDescriptionExcpetion e ) {
191                    LOG.logError( "CoverageDescription is not valid", e );
192                    throw new OGCWebServiceException( getClass().getName(), "CoverageDescription is not valid: "
193                                                                            + e.getMessage() );
194                } catch ( IOException e ) {
195                    LOG.logError( "could not read CoverageDescription", e );
196                    throw new OGCWebServiceException( getClass().getName(), "could not read CoverageDescription: "
197                                                                            + e.getMessage() );
198                } catch ( SAXException e ) {
199                    LOG.logError( "could not parse CoverageDescription", e );
200                    throw new OGCWebServiceException( getClass().getName(), "could not parse CoverageDescription: "
201                                                                            + e.getMessage() );
202                }
203                Envelope env = request.getDomainSubset().getSpatialSubset().getEnvelope();
204                BufferedImage bi = new BufferedImage( 2, 2, BufferedImage.TYPE_INT_ARGB );
205                cov = new ImageGridCoverage( co, env, bi );
206            } else {
207                cov = readCoverage( request );
208            }
209    
210            return cov;
211        }
212    
213        /**
214         * method for event based request procrssing
215         * 
216         * @param request
217         *            object containing the request.
218         * @return depending on the request one of, {@link WCSGetCapabilities}, {@link GetCoverage} or
219         *         {@link DescribeCoverage}
220         */
221        public Object doService( OGCWebServiceRequest request )
222                                throws OGCWebServiceException {
223    
224            Object response = null;
225            if ( request instanceof WCSGetCapabilities ) {
226                WCSRequestValidator.validate( configuration, request );
227                response = getCapabilities();
228            } else if ( request instanceof GetCoverage ) {
229                Coverage cov = getCoverage( (GetCoverage) request );
230                response = new ResultCoverage( cov, cov.getClass(), ( (GetCoverage) request ).getOutput().getFormat(),
231                                               (GetCoverage) request );
232            } else if ( request instanceof DescribeCoverage ) {
233                response = describeCoverage( (DescribeCoverage) request );
234            }
235            return response;
236        }
237    
238        /**
239         * returns the <tt>CoverageOffering</tt> s according to the coverages names contained in the
240         * passed request. If the request doesn't contain one or more named coverage
241         * <tt>CoverageOffering</tt> s for all coverages known by the WCS will be returned.
242         * 
243         * @param request
244         *            DescribeCoverage request
245         * @return the configured coverings
246         * @throws IOException
247         * @throws SAXException
248         * @throws InvalidCoverageDescriptionExcpetion
249         */
250        private CoverageOffering[] getCoverageOfferings( DescribeCoverage request )
251                                throws IOException, SAXException, InvalidCoverageDescriptionExcpetion {
252    
253            String[] coverages = request.getCoverages();
254            CoverageOffering[] co = null;
255            ContentMetadata cm = configuration.getContentMetadata();
256            if ( coverages.length == 0 ) {
257                // get descriptions of all coverages
258                CoverageOfferingBrief[] cob = cm.getCoverageOfferingBrief();
259                co = new CoverageOffering[cob.length];
260                for ( int i = 0; i < cob.length; i++ ) {
261                    URL url = cob[i].getConfiguration();
262                    CoverageDescription cd = CoverageDescription.createCoverageDescription( url );
263                    co[i] = cd.getCoverageOffering( cob[i].getName() );
264                }
265            } else {
266                // get descriptions of all requested coverages
267                co = new CoverageOffering[coverages.length];
268                for ( int i = 0; i < coverages.length; i++ ) {
269                    CoverageOfferingBrief cob = cm.getCoverageOfferingBrief( coverages[i] );
270                    URL url = cob.getConfiguration();
271                    CoverageDescription cd = CoverageDescription.createCoverageDescription( url );
272                    co[i] = cd.getCoverageOffering( cob.getName() );
273                }
274            }
275    
276            return co;
277        }
278    
279        /**
280         * The method reads and returns the coverage described by the passed request.
281         * 
282         * @param request
283         * @return a Coverage read from the given resolution
284         * @throws InvalidCoverageDescriptionExcpetion
285         */
286        private Coverage readCoverage( GetCoverage request )
287                                throws InvalidCoverageDescriptionExcpetion, InvalidParameterValueException,
288                                OGCWebServiceException {
289    
290            Coverage result = null;
291    
292            try {
293                CoverageOffering co = getCoverageOffering( request );
294    
295                Resolution[] resolutions = getResolutions( co, request );
296                if ( resolutions == null || resolutions.length == 0 ) {
297                    throw new InvalidParameterValueException(
298                                                              "No data source defined the requested combination of spatial resolution and ranges" );
299                }
300                GridCoverageReader reader = null;
301                LOG.logDebug( "getting responsible GridCoverageReader" );
302                if ( resolutions[0] instanceof FileResolution ) {
303                    reader = getFileReader( resolutions, co, request );
304                } else if ( resolutions[0] instanceof ShapeResolution ) {
305                    reader = getShapeReader( resolutions, co, request );
306                } else if ( resolutions[0] instanceof DirectoryResolution ) {
307                    reader = getDirectoryReader( resolutions, co, request );
308                } else if ( resolutions[0] instanceof OracleGeoRasterResolution ) {
309                    reader = getOracleGeoRasterReader( resolutions, co, request );
310                }
311    
312                LOG.logDebug( "resolution reader: " + resolutions[0] );
313                LOG.logDebug( "found reader: " + reader.getClass() );
314                List<GeneralParameterValueIm> list = new ArrayList<GeneralParameterValueIm>( 20 );
315                Envelope size = (Envelope) request.getDomainSubset().getSpatialSubset().getGrid();
316                OperationParameterIm op = new OperationParameterIm( "width", null, new Integer( (int) size.getWidth() + 1 ) );
317                list.add( new GeneralParameterValueIm( op ) );
318                op = new OperationParameterIm( "height", null, new Integer( (int) size.getHeight() + 1 ) );
319                list.add( new GeneralParameterValueIm( op ) );
320                GeneralParameterValueIm[] gpvs = new GeneralParameterValueIm[list.size()];
321                result = reader.read( list.toArray( gpvs ) );
322                if ( result == null ) {
323                    throw new InvalidCoverageDescriptionExcpetion( "Couldn't read a coverage for the requested resolution and/or area" );
324                }
325                LOG.logDebug( "found result: " + result );
326    
327                // transform Coverage into another CRS if required
328                String crs = request.getOutput().getCrs().getCode();
329                if ( crs == null ) {
330                    crs = request.getDomainSubset().getRequestSRS().getCode();
331                }
332                if ( !crs.equalsIgnoreCase( co.getSupportedCRSs().getNativeSRSs()[0].getCodes()[0] ) ) {
333                    LOG.logDebug( "transforming coverage to " + crs );
334                    IGeoTransformer gt = new GeoTransformer( crs );
335                    result = gt.transform( (AbstractGridCoverage) result, nor, degree, null );
336                }
337    
338            } catch ( IOException e ) {
339                LOG.logError( e.getMessage(), e );
340                throw new OGCWebServiceException( e.getMessage() );
341            } catch ( SAXException e ) {
342                LOG.logError( e.getMessage(), e );
343                throw new OGCWebServiceException( e.getMessage() );
344            } catch ( CRSTransformationException e ) {
345                LOG.logError( e.getMessage(), e );
346                throw new OGCWebServiceException( e.getMessage() );
347            } catch (CRSException e) {
348                LOG.logError( e.getMessage(), e );
349                throw new OGCWebServiceException( e.getMessage() );
350            }
351            return result;
352        }
353    
354        /**
355         * returns the <tt>CoverageOffering</tt> describing the access to the data sources behind the
356         * requested coverage
357         * 
358         * @param request
359         *            GetCoverage request
360         * @return the Coverage Offering fitting the request
361         * @throws IOException
362         * @throws SAXException
363         * @throws InvalidCoverageDescriptionExcpetion
364         */
365        private CoverageOffering getCoverageOffering( GetCoverage request )
366                                throws IOException, SAXException, InvalidCoverageDescriptionExcpetion {
367    
368            ContentMetadata cm = configuration.getContentMetadata();
369            CoverageOfferingBrief cob = cm.getCoverageOfferingBrief( request.getSourceCoverage() );
370            URL url = cob.getConfiguration();
371            CoverageDescription cd = CoverageDescription.createCoverageDescription( url );
372            return cd.getCoverageOffering( request.getSourceCoverage() );
373        }
374    
375        /**
376         * returns the <tt>Resolution</tt> s matching the scale, region and range parameters of the
377         * passed request
378         * 
379         * @param co
380         * @param request
381         * @return the <tt>Resolution</tt> s matching the scale, region and range parameters of the
382         *         passed request
383         * @throws CRSException 
384         * @throws CRSTransformationException 
385         */
386        private Resolution[] getResolutions( CoverageOffering co, GetCoverage request ) throws CRSException, CRSTransformationException {
387    
388            Extension extension = co.getExtension();
389            SpatialSubset sps = request.getDomainSubset().getSpatialSubset();
390            // determine resolution of the requested coverage
391            Envelope env = calculateRequestEnvelope( request, co.getSupportedCRSs().getNativeSRSs()[0].getCodes()[0] );
392            Envelope grid = (Envelope) sps.getGrid();
393            double qx = env.getWidth() / grid.getWidth();
394            double qy = env.getHeight() / grid.getHeight();
395            double reso = qx;
396            // if x- and y-direction has different resolution in the GetCoverage
397            // request use the finest
398            if ( qy < qx ) {
399                reso = qy;
400            }
401            Resolution[] res = extension.getResolutions( reso );
402    
403            return res;
404        }
405    
406        /**
407         * returns a <tt>GridCoverageReader</tt> for accessing the data source of the target coverage
408         * of the passed GetCoverage request. The reader will be constructed from all <tt>File</tt> s
409         * matching the filter conditions defined in the passed GeCoverage request. <BR>
410         * At the moment just the first field of the passed <tt>Resolution</tt> array will be
411         * considered!
412         * 
413         * @param resolutions
414         *            <tT>Resolution</tt> to get a reader for
415         * @param co
416         *            description of the requested coverage
417         * @param request
418         * @return <tt>GridCoverageReader</tt>
419         * @throws IOException
420         * @throws CRSException 
421         * @throws CRSTransformationException 
422         */
423        private GridCoverageReader getFileReader( Resolution[] resolutions, CoverageOffering co, GetCoverage request )
424                                throws IOException, InvalidParameterValueException, CRSException, CRSTransformationException {
425    
426            String nativeCRS = co.getSupportedCRSs().getNativeSRSs()[0].getCodes()[0];
427            // calculates the envevole to be used by the created GridCoverageReader
428            Envelope envelope = calculateRequestEnvelope( request, nativeCRS );
429    
430            File[] files = ( (FileResolution) resolutions[0] ).getFiles();
431            List<File> list = new ArrayList<File>();
432            for ( int i = 0; i < files.length; i++ ) {
433                Envelope fileEnv = files[i].getEnvelope();
434                if ( fileEnv.intersects( envelope ) ) {
435                    list.add( files[i] );
436                }
437            }
438            files = list.toArray( new File[list.size()] );
439    
440            GridCoverageExchange gce = new GridCoverageExchange( null );
441            Format format = new Format( co.getSupportedFormats().getNativeFormat() );
442            GridCoverageReader reader = gce.getReader( files, co, envelope, format );
443    
444            return reader;
445        }
446    
447        /**
448         * returns a <tt>GridCoverageReader</tt> for accessing the data source of the target coverage
449         * of the passed GetCoverage request. The reader will be constructed from all <tt>Shape</tt> s
450         * matching the filter conditions defined in the passed GeCoverage request. At least this should
451         * be just one! <BR>
452         * At the moment just the first field of the passed <tt>Resolution</tt> array will be
453         * considered!
454         * 
455         * @param resolutions
456         * @param co
457         * @param request
458         * @return a GridCoverageReader which is able to read shape files.
459         * @throws IOException
460         * @throws CRSException 
461         * @throws CRSTransformationException 
462         */
463        private GridCoverageReader getShapeReader( Resolution[] resolutions, CoverageOffering co, GetCoverage request )
464                                throws IOException, InvalidParameterValueException, CRSException, CRSTransformationException {
465    
466            String nativeCRS = co.getSupportedCRSs().getNativeSRSs()[0].getCodes()[0];
467            // calculates the envevole to be used by the created GridCoverageReader
468            Envelope envelope = calculateRequestEnvelope( request, nativeCRS );
469    
470            Shape shape = ( (ShapeResolution) resolutions[0] ).getShape();
471    
472            GridCoverageExchange gce = new GridCoverageExchange( null );
473            Format format = new Format( co.getSupportedFormats().getNativeFormat() );
474            return gce.getReader( shape, co, envelope, format );
475    
476        }
477    
478        /**
479         * returns a <tt>GridCoverageReader</tt> for accessing the data source of the target coverage
480         * of the passed GetCoverage request. The reader will be constructed from all <tt>Directory</tt>
481         * s matching the filter conditions defined in the passed GeCoverage request. At least this
482         * should be just one! <BR>
483         * At the moment just the first field of the passed <tt>Resolution</tt> array will be
484         * considered!
485         * 
486         * @param resolutions
487         * @param co
488         * @param request
489         * @return the GridCoverageReader which reads directories
490         * @throws IOException
491         * @throws CRSException 
492         * @throws CRSTransformationException 
493         */
494        private GridCoverageReader getDirectoryReader( Resolution[] resolutions, CoverageOffering co, GetCoverage request )
495                                throws IOException, InvalidParameterValueException, CRSException, CRSTransformationException {
496    
497            String nativeCRS = co.getSupportedCRSs().getNativeSRSs()[0].getCodes()[0];
498            // calculates the envevole to be used by the created GridCoverageReader
499            Envelope envelope = calculateRequestEnvelope( request, nativeCRS );
500    
501            Directory[] dirs = ( (DirectoryResolution) resolutions[0] ).getDirectories( envelope );
502    
503            GridCoverageExchange gce = new GridCoverageExchange( null );
504            Format format = new Format( co.getSupportedFormats().getNativeFormat() );
505    
506            GridCoverageReader reader = gce.getReader( dirs, co, envelope, format );
507    
508            return reader;
509        }
510    
511        /**
512         * returns a <tt>GridCoverageReader</tt> for accessing the data source of the target coverage
513         * of the passed GetCoverage request. The reader will be constructed from the JDBCV connnection
514         * defined in the CoverageDescription extension.<BR>
515         * At the moment just the first field of the passed <tt>Resolution</tt> array will be
516         * considered!
517         * 
518         * @param resolutions
519         * @param co
520         * @param request
521         * @return a <tt>GridCoverageReader</tt>.
522         * @throws InvalidParameterValueException
523         * @throws IOException
524         * @throws CRSException 
525         * @throws CRSTransformationException 
526         */
527        private GridCoverageReader getOracleGeoRasterReader( Resolution[] resolutions, CoverageOffering co,
528                                                             GetCoverage request )
529                                throws InvalidParameterValueException, IOException, CRSException, CRSTransformationException {
530    
531            String nativeCRS = co.getSupportedCRSs().getNativeSRSs()[0].getCodes()[0];
532            // calculates the envevole to be used by the created GridCoverageReader
533            Envelope envelope = calculateRequestEnvelope( request, nativeCRS );
534    
535            JDBCConnection jdbc = ( (OracleGeoRasterResolution) resolutions[0] ).getJDBCConnection();
536            String table = ( (OracleGeoRasterResolution) resolutions[0] ).getTable();
537            String rdtTable = ( (OracleGeoRasterResolution) resolutions[0] ).getRdtTable();
538            String column = ( (OracleGeoRasterResolution) resolutions[0] ).getColumn();
539            String identification = ( (OracleGeoRasterResolution) resolutions[0] ).getIdentification();
540            int level = ( (OracleGeoRasterResolution) resolutions[0] ).getLevel();
541            GeoRasterDescription grd = new GeoRasterDescription( jdbc, table, rdtTable, column, identification, level );
542    
543            GridCoverageExchange gce = new GridCoverageExchange( null );
544            Format format = new Format( co.getSupportedFormats().getNativeFormat() );
545    
546            return gce.getReader( grd, co, envelope, format );
547    
548        }
549    
550        /**
551         * According to WCS 1.0.0 the CRS of the GetCoverage request BBOX can be different to the
552         * desired CRS of the resulting coverage. This method transforms the request CRS to the output
553         * CRS if requiered. At the moment deegree WCS doesn't support transformation of grid coverages
554         * so the output CRS will always be the native CRS of te data.
555         * 
556         * @param request
557         * @param nativeCrs
558         * @return a boundingbox of the request
559         * @throws CRSTransformationException
560         * @throws CRSException 
561         */
562        private Envelope calculateRequestEnvelope( GetCoverage request, String nativeCrs )
563                                throws CRSException, CRSTransformationException {
564    
565            SpatialSubset spsu = request.getDomainSubset().getSpatialSubset();
566            Envelope envelope = spsu.getEnvelope();
567    
568            String reqCrs = request.getDomainSubset().getRequestSRS().getCode();
569     
570            IGeoTransformer gt = new GeoTransformer( nativeCrs );
571            return gt.transform( envelope, reqCrs );
572            
573        }
574    
575    }