001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/ogcwebservices/wcs/getcoverage/GetCoverage.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.ogcwebservices.wcs.getcoverage; 037 038 import java.net.URI; 039 import java.util.List; 040 import java.util.Map; 041 042 import org.deegree.datatypes.Code; 043 import org.deegree.datatypes.time.TimeSequence; 044 import org.deegree.framework.log.ILogger; 045 import org.deegree.framework.log.LoggerFactory; 046 import org.deegree.framework.util.KVP2Map; 047 import org.deegree.framework.util.StringTools; 048 import org.deegree.framework.xml.NamespaceContext; 049 import org.deegree.framework.xml.XMLTools; 050 import org.deegree.model.coverage.grid.Grid; 051 import org.deegree.model.crs.CRSFactory; 052 import org.deegree.model.crs.CoordinateSystem; 053 import org.deegree.model.crs.UnknownCRSException; 054 import org.deegree.model.spatialschema.Envelope; 055 import org.deegree.model.spatialschema.GeometryFactory; 056 import org.deegree.model.spatialschema.Position; 057 import org.deegree.ogcbase.CommonNamespaces; 058 import org.deegree.ogcbase.ExceptionCode; 059 import org.deegree.ogcbase.GMLDocument; 060 import org.deegree.ogcwebservices.InvalidParameterValueException; 061 import org.deegree.ogcwebservices.MissingParameterValueException; 062 import org.deegree.ogcwebservices.OGCWebServiceException; 063 import org.deegree.ogcwebservices.wcs.InterpolationMethod; 064 import org.deegree.ogcwebservices.wcs.WCSException; 065 import org.deegree.ogcwebservices.wcs.WCSRequestBase; 066 import org.w3c.dom.Document; 067 import org.w3c.dom.Element; 068 import org.w3c.dom.Node; 069 070 /** 071 * encapsulates a WCS GetCoverage request 072 * 073 * @version $Revision: 24440 $ 074 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 075 * @author last edited by: $Author: apoth $ 076 * 077 * @version 1.0. $Revision: 24440 $, $Date: 2010-05-18 11:53:54 +0200 (Di, 18 Mai 2010) $ 078 * 079 * @since 2.0 080 */ 081 082 public class GetCoverage extends WCSRequestBase { 083 084 private static final ILogger LOG = LoggerFactory.getLogger( GetCoverage.class ); 085 086 private static final long serialVersionUID = 44735033754048955L; 087 088 private static final NamespaceContext nsContext = CommonNamespaces.getNamespaceContext(); 089 090 private String sourceCoverage = null; 091 092 private DomainSubset domainSubset = null; 093 094 private RangeSubset rangeSubset = null; 095 096 private InterpolationMethod interpolationMethod = null; 097 098 private Output output = null; 099 100 /** 101 * @param id 102 * @param version 103 * @param sourceCoverage 104 * @param domainSubset 105 * @param output 106 * @throws WCSException 107 * @throws OGCWebServiceException 108 */ 109 public GetCoverage( String id, String version, String sourceCoverage, DomainSubset domainSubset, Output output ) 110 throws WCSException, OGCWebServiceException { 111 this( id, version, sourceCoverage, domainSubset, null, null, output ); 112 } 113 114 /** 115 * @param id 116 * @param version 117 * @param sourceCoverage 118 * @param domainSubset 119 * @param interpolationMethod 120 * @param output 121 * @throws WCSException 122 * @throws OGCWebServiceException 123 */ 124 public GetCoverage( String id, String version, String sourceCoverage, DomainSubset domainSubset, 125 InterpolationMethod interpolationMethod, Output output ) throws WCSException, 126 OGCWebServiceException { 127 this( id, version, sourceCoverage, domainSubset, null, interpolationMethod, output ); 128 } 129 130 /** 131 * @param id 132 * @param version 133 * @param sourceCoverage 134 * @param domainSubset 135 * @param rangeSubset 136 * @param output 137 * @throws WCSException 138 * @throws OGCWebServiceException 139 */ 140 public GetCoverage( String id, String version, String sourceCoverage, DomainSubset domainSubset, 141 RangeSubset rangeSubset, Output output ) throws WCSException, OGCWebServiceException { 142 this( id, version, sourceCoverage, domainSubset, rangeSubset, null, output ); 143 } 144 145 /** 146 * @param id 147 * @param version 148 * @param sourceCoverage 149 * @param domainSubset 150 * @param rangeSubset 151 * @param interpolationMethod 152 * @param output 153 * @throws WCSException 154 * @throws OGCWebServiceException 155 */ 156 public GetCoverage( String id, String version, String sourceCoverage, DomainSubset domainSubset, 157 RangeSubset rangeSubset, InterpolationMethod interpolationMethod, Output output ) 158 throws WCSException, OGCWebServiceException { 159 super( id, version ); 160 if ( sourceCoverage == null || sourceCoverage.length() == 0 ) { 161 throw new WCSException( "sourceCoverage must be a valid string with length > 0" ); 162 } 163 if ( domainSubset == null ) { 164 throw new WCSException( "domainSubset must be <> null in GetCoverage" ); 165 } 166 if ( output == null ) { 167 throw new WCSException( "output must be <> null in GetCoverage" ); 168 } 169 this.sourceCoverage = sourceCoverage; 170 this.domainSubset = domainSubset; 171 this.rangeSubset = rangeSubset; 172 this.interpolationMethod = interpolationMethod; 173 this.output = output; 174 } 175 176 /** 177 * creates a GetCoverage request from its KVP representation 178 * 179 * @param id 180 * unique ID of the request 181 * @param kvp 182 * request 183 * @return created <tt>GetCoverage</tt> 184 * @throws OGCWebServiceException 185 * will be thrown if something general is wrong 186 * @throws WCSException 187 * will be thrown if a WCS/GetCoverage specific part of the request is erroreous 188 */ 189 public static GetCoverage create( String id, String kvp ) 190 throws OGCWebServiceException, WCSException { 191 Map<String, String> map = KVP2Map.toMap( kvp ); 192 map.put( "ID", id ); 193 return create( map ); 194 } 195 196 /** 197 * creates a GetCoverage request from its KVP representation 198 * 199 * @param map 200 * request 201 * @return created <tt>GetCoverage</tt> 202 * @throws OGCWebServiceException 203 * will be thrown if something general is wrong 204 * @throws MissingParameterValueException 205 * @throws InvalidParameterValueException 206 * @throws WCSException 207 * will be thrown if a WCS/GetCoverage specific part of the request is erroreous 208 */ 209 public static GetCoverage create( Map<String, String> map ) 210 throws OGCWebServiceException, MissingParameterValueException, 211 InvalidParameterValueException { 212 213 String version = map.remove( "VERSION" ); 214 if ( version == null ) { 215 throw new MissingParameterValueException( "WCS", "'version' must be set" ); 216 } 217 if ( !"1.0.0".equals( version ) ) { 218 ExceptionCode ecode = ExceptionCode.INVALIDPARAMETERVALUE; 219 throw new InvalidParameterValueException( "WCS", "'version' must be 1.0.0", ecode ); 220 } 221 String coverage = map.remove( "COVERAGE" ); 222 String crs = map.remove( "CRS" ); 223 if ( crs == null ) { 224 ExceptionCode code = ExceptionCode.MISSINGPARAMETERVALUE; 225 throw new MissingParameterValueException( "WCS", "'crs' is missing", code ); 226 } 227 String response_crs = map.remove( "RESPONSE_CRS" ); 228 if ( response_crs == null ) { 229 response_crs = crs; 230 } 231 String format = map.remove( "FORMAT" ); 232 Output output = createOutput( response_crs, null, format, null ); 233 SpatialSubset sps = createSpatialSubset( map, crs ); 234 235 String time = map.remove( "TIME" ); 236 TimeSequence temporalSubset = null; 237 if ( time != null ) { 238 temporalSubset = new TimeSequence( time ); 239 } 240 241 Code code = new Code( crs, null ); 242 DomainSubset domainSubset = new DomainSubset( code, sps, temporalSubset ); 243 244 String except = map.remove( "EXCEPTIONS" ); 245 if ( except == null ) { 246 except = "application/vnd.ogc.se_xml"; 247 } else if ( !except.equals( "application/vnd.ogc.se_xml" ) ) { 248 ExceptionCode ecode = ExceptionCode.INVALIDPARAMETERVALUE; 249 throw new InvalidParameterValueException( "WCS", "exceptions != application/vnd.ogc.se_xml", ecode ); 250 } 251 String id = map.remove( "ID" ); 252 253 GetCoverage gc = new GetCoverage( id, version, coverage, domainSubset, null, null, output ); 254 gc.validate(); 255 return gc; 256 } 257 258 /** 259 * creates a GetCoverage request from its XML representation 260 * 261 * @param id 262 * unique ID of the request 263 * @param doc 264 * XML representation of the request 265 * @return created <tt>DescribeCoverage</tt> 266 * @throws OGCWebServiceException 267 * will be thrown if something general is wrong 268 * @throws WCSException 269 * will be thrown if a WCS/GetCoverage specific part of the request is erroreous 270 */ 271 public static GetCoverage create( String id, Document doc ) 272 throws OGCWebServiceException, WCSException { 273 274 GetCoverage gc = null; 275 try { 276 277 String version = XMLTools.getNodeAsString( doc, "/wcs:GetCoverage/@version", nsContext, null ); 278 if ( version == null ) { 279 throw new MissingParameterValueException( "WCS", "'version' must be set" ); 280 } 281 if ( !"1.0.0".equals( version ) ) { 282 ExceptionCode ecode = ExceptionCode.INVALIDPARAMETERVALUE; 283 throw new InvalidParameterValueException( "WCS", "'version' must be 1.0.0", ecode ); 284 } 285 286 String coverage = XMLTools.getRequiredNodeAsString( doc, "/wcs:GetCoverage/wcs:sourceCoverage", nsContext ); 287 String interpol = XMLTools.getNodeAsString( doc, "/wcs:GetCoverage/wcs:interpolationMethod", nsContext, 288 null ); 289 InterpolationMethod interpolMeth = null; 290 if ( interpol == null || "nearest neighbor".equals( interpol ) ) { 291 interpolMeth = new InterpolationMethod( "nearest neighbor" ); 292 } 293 String path = "/wcs:GetCoverage/wcs:domainSubset/wcs:spatialSubset"; 294 List<Node> nl = XMLTools.getNodes( doc, path, nsContext ); 295 SpatialSubset sp = null; 296 if ( nl.size() > 0 ) { 297 Node node = (Node) nl.get( 0 ); 298 sp = createSpatialSubset( (Element) node ); 299 } else { 300 // TODO 301 // temporal subset 302 } 303 // TODO 304 // path = "/wcs:GetCoverage/wcs:rangeSubset/wcs:axisSubset"; 305 // nl = XMLTools.getXPath(path, doc, nsContext); 306 // evaluate possible ranges; e.g.time, extent 307 String format = XMLTools.getRequiredNodeAsString( doc, "/wcs:GetCoverage/wcs:output/wcs:format", nsContext ); 308 // use crs defined for the requested envelope if no CRS is defined 309 // in the request 310 String crsName = "EPSG:4326"; 311 if ( sp.getEnvelope().getCoordinateSystem() != null ) { 312 crsName = sp.getEnvelope().getCoordinateSystem().getPrefixedName(); 313 } 314 String crs = XMLTools.getNodeAsString( doc, "/wcs:GetCoverage/wcs:output/wcs:crs", nsContext, crsName ); 315 316 String ipm = XMLTools.getNodeAsString( doc, "/wcs:GetCoverage/wcs:interpolationMethod", nsContext, null ); 317 if ( ipm != null && !ipm.equals( "nearest neighbor" ) ) { 318 throw new InvalidParameterValueException( "interpolationMethod must " 319 + "have the value 'nearest neighbor'" ); 320 } 321 322 Output output = createOutput( crs, null, format, null ); 323 DomainSubset domainSubset = new DomainSubset( new Code( crsName ), sp ); 324 325 gc = new GetCoverage( id, version, coverage, domainSubset, null, interpolMeth, output ); 326 } catch ( Exception e ) { 327 ExceptionCode code = ExceptionCode.INVALID_FORMAT; 328 throw new WCSException( "WCS", StringTools.stackTraceToString( e ), code ); 329 } 330 331 gc.validate(); 332 return gc; 333 } 334 335 /** 336 * @param element 337 * @return a new Spatial subset 338 * @throws WCSException 339 */ 340 private static SpatialSubset createSpatialSubset( Element element ) 341 throws WCSException { 342 SpatialSubset sp = null; 343 try { 344 List<Node> nl = XMLTools.getNodes( element, "gml:Envelope", nsContext ); 345 Envelope env = GMLDocument.parseEnvelope( (Element) nl.get( 0 ) ); 346 nl = XMLTools.getNodes( element, "gml:Grid", nsContext ); 347 Grid grid = GMLDocument.parseGrid( (Element) nl.get( 0 ) ); 348 sp = new SpatialSubset( env, grid.getGridEnvelope() ); 349 } catch ( Exception e ) { 350 ExceptionCode code = ExceptionCode.INVALID_FORMAT; 351 throw new WCSException( "WCS", StringTools.stackTraceToString( e ), code ); 352 } 353 return sp; 354 } 355 356 /** 357 * @param map 358 * @param crs 359 * @return a new SpatialSubset with given crs 360 * @throws WCSException 361 */ 362 public static final SpatialSubset createSpatialSubset( Map<String,String> map, String crs ) 363 throws WCSException { 364 Envelope envelope = createEnvelope( map, crs ); 365 366 int width = (int) getNumber( (String) map.remove( "WIDTH" ), "WIDTH" ); 367 int height = (int) getNumber( (String) map.remove( "HEIGHT" ), "HEIGHT" ); 368 int depth = (int) getNumber( (String) map.remove( "DEPTH" ), "DEPTH" ); 369 370 double resx = getNumber( (String) map.remove( "RESX" ), "RESX" ); 371 double resy = getNumber( (String) map.remove( "RESY" ), "RESY" ); 372 double resz = getNumber( (String) map.remove( "RESZ" ), "RESZ" ); 373 374 Position low = null; 375 Position high = null; 376 if ( width > 0 && height > 0 ) { 377 if ( depth > 0 ) { 378 low = GeometryFactory.createPosition( 0, 0, 0 ); 379 high = GeometryFactory.createPosition( width - 1, height - 1, depth ); 380 } else { 381 low = GeometryFactory.createPosition( 0, 0 ); 382 high = GeometryFactory.createPosition( width - 1, height - 1 ); 383 } 384 } else if ( resx > 0 && resy > 0 ) { 385 if ( resz > 0 ) { 386 ExceptionCode code = ExceptionCode.INVALIDPARAMETERVALUE; 387 throw new WCSException( "WCS", "resz is not supported yet", code ); 388 } 389 width = (int) Math.round( envelope.getWidth() / resx ); 390 height = (int) Math.round( envelope.getHeight() / resy ); 391 low = GeometryFactory.createPosition( 0, 0 ); 392 high = GeometryFactory.createPosition( width, height ); 393 } else { 394 ExceptionCode code = ExceptionCode.MISSINGPARAMETERVALUE; 395 throw new WCSException( "WCS", "width/height or resx/resy must be set", code ); 396 } 397 398 Envelope grid = GeometryFactory.createEnvelope( low, high, null ); 399 400 return new SpatialSubset( envelope, grid ); 401 402 } 403 404 /** 405 * @param map 406 * @return an envelope. 407 * @throws WCSException 408 */ 409 private static Envelope createEnvelope( Map<String,String> map, String crs ) 410 throws WCSException { 411 String tmp = (String) map.remove( "BBOX" ); 412 double[] bbox = null; 413 if ( tmp != null ) { 414 try { 415 bbox = StringTools.toArrayDouble( tmp, "," ); 416 } catch ( Exception e ) { 417 ExceptionCode code = ExceptionCode.INVALIDPARAMETERVALUE; 418 throw new WCSException( "WCS", "can't read BBOX", code ); 419 } 420 421 Position min = null; 422 Position max = null; 423 if ( bbox.length == 4 ) { 424 min = GeometryFactory.createPosition( bbox[0], bbox[1] ); 425 max = GeometryFactory.createPosition( bbox[2], bbox[3] ); 426 } else { 427 min = GeometryFactory.createPosition( bbox[0], bbox[1], bbox[2] ); 428 max = GeometryFactory.createPosition( bbox[3], bbox[4], bbox[5] ); 429 } 430 CoordinateSystem srs; 431 try { 432 srs = CRSFactory.create( crs ); 433 } catch ( UnknownCRSException e ) { 434 throw new WCSException( GetCoverage.class.getName(), e.getMessage() ); 435 } 436 return GeometryFactory.createEnvelope( min, max, srs ); 437 } 438 return null; 439 440 } 441 442 /** 443 * creates an <tt>Output</tt> object for a GetCoverage request 444 * 445 * @param response_crs 446 * @param crsNS 447 * @param format 448 * @param formatNS 449 * @return an Output 450 * @throws WCSException 451 * will be thrown if the response_crs prefix isn't a valid URI 452 */ 453 public static final Output createOutput( String response_crs, String crsNS, String format, String formatNS ) 454 throws WCSException { 455 URI crsURI = null; 456 if ( crsNS != null ) { 457 try { 458 crsURI = new URI( crsNS ); 459 } catch ( Exception e ) { 460 throw new WCSException( "invalid response crs namespace: " + crsNS ); 461 } 462 } 463 464 URI formatURI = null; 465 if ( formatNS != null ) { 466 try { 467 formatURI = new URI( formatNS ); 468 } catch ( Exception e ) { 469 throw new WCSException( "invalid response crs namespace: " + formatNS ); 470 } 471 } 472 473 Code crs = new Code( response_crs, crsURI ); 474 Code cformat = new Code( format, formatURI ); 475 return new Output( crs, cformat ); 476 } 477 478 /** 479 * @param val 480 * @param name 481 * @return a Number 482 * @throws WCSException 483 */ 484 private static double getNumber( String val, String name ) 485 throws WCSException { 486 if ( val == null ) 487 return -1; 488 double d = -1; 489 try { 490 d = Double.parseDouble( val ); 491 } catch ( Exception e ) { 492 ExceptionCode code = ExceptionCode.INVALIDPARAMETERVALUE; 493 throw new WCSException( "WCS", name + " isn't a valid number format", code ); 494 } 495 return d; 496 } 497 498 /** 499 * @return Returns the domainSubset. 500 */ 501 public DomainSubset getDomainSubset() { 502 return domainSubset; 503 } 504 505 /** 506 * @return Returns the interpolationMethod. 507 */ 508 public InterpolationMethod getInterpolationMethod() { 509 return interpolationMethod; 510 } 511 512 /** 513 * @return Returns the output. 514 */ 515 public Output getOutput() { 516 return output; 517 } 518 519 /** 520 * @return Returns the rangeSubset. 521 */ 522 public RangeSubset getRangeSubset() { 523 return rangeSubset; 524 } 525 526 /** 527 * @return Returns the sourceCoverage. 528 * 529 */ 530 public String getSourceCoverage() { 531 return sourceCoverage; 532 } 533 534 /** 535 * @throws WCSException 536 */ 537 protected void validate() 538 throws WCSException { 539 540 if ( getVersion() == null ) { 541 ExceptionCode code = ExceptionCode.MISSINGPARAMETERVALUE; 542 throw new WCSException( "WCS", "'version' is missing", code ); 543 } 544 545 if ( getSourceCoverage() == null ) { 546 ExceptionCode code = ExceptionCode.MISSINGPARAMETERVALUE; 547 throw new WCSException( "WCS", "'coverage' is missing", code ); 548 } 549 550 DomainSubset ds = getDomainSubset(); 551 if ( ds.getRequestSRS() == null ) { 552 ExceptionCode code = ExceptionCode.MISSINGPARAMETERVALUE; 553 throw new WCSException( "WCS", "'crs' is missing", code ); 554 } 555 556 if ( ds.getSpatialSubset() == null && ds.getTemporalSubset() == null ) { 557 ExceptionCode code = ExceptionCode.MISSINGPARAMETERVALUE; 558 throw new WCSException( "WCS", "either temporal subset or spatial " + "subset must be defined", code ); 559 } 560 561 if ( getOutput().getFormat() == null ) { 562 ExceptionCode code = ExceptionCode.MISSINGPARAMETERVALUE; 563 throw new WCSException( "WCS", "'format' is missing", code ); 564 } 565 566 } 567 568 569 570 @Override 571 public String getRequestParameter() 572 throws OGCWebServiceException { 573 StringBuffer sb = new StringBuffer(1000); 574 sb.append( "REQUEST=GetCoverage&VERSION=1.0.0&coverage=" ); 575 sb.append( getSourceCoverage() ).append( "&TRANSPARENT=true&Format=" ); 576 sb.append( getOutput().getFormat().getCode() ).append( "&EXCEPTIONS=application/vnd.ogc.se_xml&Width=" ); 577 Envelope grid = (Envelope)getDomainSubset().getSpatialSubset().getGrid(); 578 sb.append( Math.round( grid.getWidth( )) ).append( "&height=" ).append( Math.round( grid.getHeight() ) ); 579 sb.append( "&crs=" ).append( getOutput().getCrs().getCode() ).append( "&bbox=" ); 580 Envelope bbox = getDomainSubset().getSpatialSubset().getEnvelope(); 581 sb.append( bbox.getMin().getX() ).append( ',' ).append( bbox.getMin().getY() ).append( ',' ); 582 sb.append( bbox.getMax().getX() ).append( ',' ).append( bbox.getMax().getY() ); 583 584 LOG.logDebug( "GetCoverage request parameters", sb ); 585 586 return sb.toString(); 587 588 } 589 590 @Override 591 public String toString() { 592 String response = super.toString(); 593 response += "\nOutput: " + output; 594 response += "\ndomainSubset: " + domainSubset; 595 response += "\nsourceCoverage: " + sourceCoverage; 596 response += "\ninterpolationMethod: " + interpolationMethod; 597 return response; 598 } 599 600 }