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 }