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 }