    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_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    }