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