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    }