001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/ogcwebservices/wpvs/configuration/WPVSConfigurationDocument.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2008 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     Aennchenstraße 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    
044    package org.deegree.ogcwebservices.wpvs.configuration;
045    
046    import java.awt.Color;
047    import java.net.MalformedURLException;
048    import java.net.URL;
049    import java.util.ArrayList;
050    import java.util.HashMap;
051    import java.util.Iterator;
052    import java.util.List;
053    import java.util.Map;
054    
055    import org.deegree.datatypes.QualifiedName;
056    import org.deegree.framework.log.ILogger;
057    import org.deegree.framework.log.LoggerFactory;
058    import org.deegree.framework.util.IDGenerator;
059    import org.deegree.framework.util.KVP2Map;
060    import org.deegree.framework.util.StringTools;
061    import org.deegree.framework.xml.InvalidConfigurationException;
062    import org.deegree.framework.xml.XMLParsingException;
063    import org.deegree.framework.xml.XMLTools;
064    import org.deegree.i18n.Messages;
065    import org.deegree.model.crs.CoordinateSystem;
066    import org.deegree.model.filterencoding.AbstractFilter;
067    import org.deegree.model.filterencoding.Filter;
068    import org.deegree.model.metadata.iso19115.Keywords;
069    import org.deegree.model.metadata.iso19115.OnlineResource;
070    import org.deegree.model.spatialschema.Envelope;
071    import org.deegree.model.spatialschema.GMLGeometryAdapter;
072    import org.deegree.model.spatialschema.Geometry;
073    import org.deegree.model.spatialschema.GeometryException;
074    import org.deegree.model.spatialschema.GeometryFactory;
075    import org.deegree.model.spatialschema.Surface;
076    import org.deegree.ogcbase.CommonNamespaces;
077    import org.deegree.ogcbase.PropertyPath;
078    import org.deegree.ogcwebservices.InvalidParameterValueException;
079    import org.deegree.ogcwebservices.MissingParameterValueException;
080    import org.deegree.ogcwebservices.OGCWebServiceException;
081    import org.deegree.ogcwebservices.getcapabilities.InvalidCapabilitiesException;
082    import org.deegree.ogcwebservices.wcs.getcoverage.GetCoverage;
083    import org.deegree.ogcwebservices.wms.operation.GetMap;
084    import org.deegree.ogcwebservices.wpvs.capabilities.DataProvider;
085    import org.deegree.ogcwebservices.wpvs.capabilities.Dataset;
086    import org.deegree.ogcwebservices.wpvs.capabilities.DatasetReference;
087    import org.deegree.ogcwebservices.wpvs.capabilities.Dimension;
088    import org.deegree.ogcwebservices.wpvs.capabilities.ElevationModel;
089    import org.deegree.ogcwebservices.wpvs.capabilities.FeatureListReference;
090    import org.deegree.ogcwebservices.wpvs.capabilities.Identifier;
091    import org.deegree.ogcwebservices.wpvs.capabilities.MetaData;
092    import org.deegree.ogcwebservices.wpvs.capabilities.OWSCapabilities;
093    import org.deegree.ogcwebservices.wpvs.capabilities.Style;
094    import org.deegree.ogcwebservices.wpvs.capabilities.WPVSCapabilitiesDocument;
095    import org.w3c.dom.Element;
096    import org.w3c.dom.Node;
097    import org.w3c.dom.Text;
098    
099    /**
100     * Parser for WPVS configuration documents.
101     * 
102     * @author <a href="mailto:mays@lat-lon.de">Judit Mays</a>
103     * @author last edited by: $Author: apoth $
104     * 
105     * @version $Revision: 9345 $, $Date: 2007-12-27 17:22:25 +0100 (Do, 27 Dez 2007) $
106     */
107    public class WPVSConfigurationDocument extends WPVSCapabilitiesDocument {
108    
109        private static final long serialVersionUID = 1511898601495679163L;
110    
111        private static final ILogger LOG = LoggerFactory.getLogger( WPVSConfigurationDocument.class );
112    
113        private static String PRE_DWPVS = CommonNamespaces.DEEGREEWPVS_PREFIX + ":";
114    
115        private static String PRE_OWS = CommonNamespaces.OWS_PREFIX + ":";
116    
117        // The smallestMinimalScaleDenomiator is needed to calculate the smallest resolutionstripe
118        // possible
119        private double smallestMinimalScaleDenominator = Double.MAX_VALUE;
120    
121        /**
122         * Creates a class representation of the <code>WPVSConfiguration</code> document.
123         * 
124         * @return Returns a WPVSConfiguration object.
125         * @throws InvalidConfigurationException
126         */
127        public WPVSConfiguration parseConfiguration()
128                                                     throws InvalidConfigurationException {
129            WPVSConfiguration wpvsConfiguration = null;
130            try {
131    
132                // TODO 'contents' field not verified, therefore null! Check spec.
133                Element requestedNode = (Element) XMLTools.getRequiredNode( getRootElement(),
134                                                                            PRE_DWPVS + "deegreeParams",
135                                                                            nsContext );
136                WPVSDeegreeParams wpvsDeegreeParams = parseDeegreeParams( requestedNode );
137    
138                requestedNode = (Element) XMLTools.getRequiredNode( getRootElement(), PRE_DWPVS + "Dataset", nsContext );
139                Dataset rootDataset = parseDataset( requestedNode,
140                                                    null,
141                                                    null,
142                                                    0,
143                                                    9E9,
144                                                    wpvsDeegreeParams.getMinimalWCS_DGMResolution() );
145    
146                wpvsConfiguration = new WPVSConfiguration( parseVersion(),
147                                                           parseUpdateSequence(),
148                                                           getServiceIdentification(),
149                                                           getServiceProvider(),
150                                                           parseOperationsMetadata( wpvsDeegreeParams.getDefaultOnlineResource() ),
151                                                           null,
152                                                           rootDataset,
153                                                           wpvsDeegreeParams,
154                                                           ( Double.isInfinite( smallestMinimalScaleDenominator ) ? 1.0
155                                                                                                                 : smallestMinimalScaleDenominator ) );
156    
157            } catch ( XMLParsingException e ) {
158                throw new InvalidConfigurationException( e.getMessage() + "\n" + StringTools.stackTraceToString( e ) );
159    
160            } catch ( MissingParameterValueException e ) {
161                throw new InvalidConfigurationException( e.getMessage() + "\n" + StringTools.stackTraceToString( e ) );
162    
163            } catch ( InvalidParameterValueException e ) {
164                throw new InvalidConfigurationException( e.getMessage() + "\n" + StringTools.stackTraceToString( e ) );
165    
166            } catch ( OGCWebServiceException e ) {
167                throw new InvalidConfigurationException( e.getMessage() + "\n" + StringTools.stackTraceToString( e ) );
168    
169            } catch ( InvalidConfigurationException e ) {
170                throw new InvalidConfigurationException( e.getMessage() + "\n" + StringTools.stackTraceToString( e ) );
171    
172            }
173            return wpvsConfiguration;
174        }
175    
176        /**
177         * Creates and returns a new <code>WPVSDeegreeParams</code> object from the given <code>Node</code>.
178         * 
179         * @param deegreeNode
180         * @return Returns a new WPVSDeegreeParams object.
181         * @throws XMLParsingException
182         * @throws InvalidConfigurationException
183         */
184        private WPVSDeegreeParams parseDeegreeParams( Node deegreeNode )
185                                                                        throws XMLParsingException,
186                                                                        InvalidConfigurationException {
187    
188            Element deegreeElement = (Element) XMLTools.getRequiredNode( deegreeNode,
189                                                                         PRE_DWPVS + "DefaultOnlineResource",
190                                                                         nsContext );
191            OnlineResource defaultOnlineResource = parseOnLineResource( deegreeElement );
192    
193            int cacheSize = XMLTools.getNodeAsInt( deegreeNode, PRE_DWPVS + "CacheSize", nsContext, 100 );
194    
195            int maxLifeTime = XMLTools.getNodeAsInt( deegreeNode, PRE_DWPVS + "MaxLifeTime", nsContext, 3600 );
196    
197            int reqTimeLimit = XMLTools.getNodeAsInt( deegreeNode, PRE_DWPVS + "RequestTimeLimit", nsContext, 60 );
198            reqTimeLimit *= 1000;
199    
200            int maxTextureDimension = XMLTools.getNodeAsInt( deegreeNode,
201                                                             PRE_DWPVS + "MaxTextureDimension",
202                                                             nsContext,
203                                                             Integer.MAX_VALUE );
204            
205            int quadMergeCount = XMLTools.getNodeAsInt( deegreeNode,
206                                                             PRE_DWPVS + "QuadMergeCount",
207                                                             nsContext, 10 );
208    
209            float viewQuality = (float) XMLTools.getNodeAsDouble( deegreeNode, PRE_DWPVS + "ViewQuality", nsContext, 0.95f );
210    
211            int maxMapWidth = XMLTools.getNodeAsInt( deegreeNode, PRE_DWPVS + "MaxViewWidth", nsContext, 1000 );
212    
213            int maxMapHeight = XMLTools.getNodeAsInt( deegreeNode, PRE_DWPVS + "MaxViewHeight", nsContext, 1000 );
214    
215            String charSet = XMLTools.getNodeAsString( deegreeNode, PRE_DWPVS + "CharacterSet", nsContext, "UTF-8" );
216    
217            Node copyrightNode = XMLTools.getNode( deegreeNode, PRE_DWPVS + "Copyright", nsContext );
218            String copyright = null;
219            boolean isWatermarked = false;
220            if ( copyrightNode != null ) {
221    
222                Node copyTextNode = XMLTools.getNode( copyrightNode, PRE_DWPVS + "Text", nsContext );
223                Node copyURLNode = XMLTools.getNode( copyrightNode, PRE_DWPVS + "ImageURL/@xlink:href", nsContext );
224    
225                if ( copyTextNode != null ) {
226                    copyright = XMLTools.getRequiredNodeAsString( copyrightNode, PRE_DWPVS + "Text/text()", nsContext );
227                } else if ( copyURLNode != null ) {
228                    copyright = XMLTools.getRequiredNodeAsString( copyrightNode,
229                                                                  PRE_DWPVS + "ImageURL/@xlink:href",
230                                                                  nsContext );
231    
232                    isWatermarked = XMLTools.getNodeAsBoolean( copyrightNode,
233                                                               PRE_DWPVS + "ImageURL/@watermark",
234                                                               nsContext,
235                                                               isWatermarked );
236    
237                    try {
238                        copyright = resolve( copyright ).toString();
239                    } catch ( MalformedURLException e ) {
240                        throw new InvalidConfigurationException( "Copyright/ImageURL '" + copyright
241                                                                 + "' doesn't seem to be a valid URL!" );
242                    }
243    
244                } else {
245                    throw new InvalidConfigurationException( "Copyright must contain either " + "a Text-Element or an ImageURL-Element!" );
246                }
247            }
248    
249            Map<String, URL> backgroundMap = new HashMap<String, URL>( 10 );
250            Element backgrounds = (Element) XMLTools.getNode( deegreeNode, PRE_DWPVS + "BackgroundList", nsContext );
251            if ( backgrounds != null ) {
252                List backgroundList = XMLTools.getNodes( backgrounds, PRE_DWPVS + "Background", nsContext );
253                for ( Iterator iter = backgroundList.iterator(); iter.hasNext(); ) {
254                    Element background = (Element) iter.next();
255    
256                    String bgName = background.getAttribute( "name" );
257                    String bgHref = background.getAttribute( "href" );
258    
259                    if ( bgName == null || bgName.length() == 0 || bgHref == null || bgHref.length() == 0 )
260                        throw new InvalidConfigurationException( "Background must contain a 'name' and a " + " 'href' attribute, both if which must contain non-empty strings." );
261    
262                    try {
263    
264                        backgroundMap.put( bgName, resolve( bgHref ) );
265                    } catch ( MalformedURLException e ) {
266                        throw new InvalidConfigurationException( "Background", e.getMessage() );
267                    }
268                }
269    
270            }
271    
272            boolean quality = XMLTools.getNodeAsBoolean( deegreeNode,
273                                                         PRE_DWPVS + "RequestQualityPreferred",
274                                                         nsContext,
275                                                         true );
276            double maximumFarClippingPlane = XMLTools.getNodeAsDouble( deegreeNode,
277                                                                       PRE_DWPVS + "RequestsMaximumFarClippingPlane",
278                                                                       nsContext,
279                                                                       15000 );
280    
281            double nearClippingPlane = XMLTools.getNodeAsDouble( deegreeNode, PRE_DWPVS + "NearClippingPlane", nsContext, 2 );
282    
283            String defaultSplitter = XMLTools.getNodeAsString( deegreeNode,
284                                                               PRE_DWPVS + "DefaultSplitter",
285                                                               nsContext,
286                                                               "QUAD" ).toUpperCase();
287    
288            double minimalTerrainHeight = XMLTools.getNodeAsDouble( deegreeNode,
289                                                                    PRE_DWPVS + "MinimalTerrainHeight",
290                                                                    nsContext,
291                                                                    0 );
292    
293            double minimalWCS_DGMResolution = XMLTools.getNodeAsDouble( deegreeNode,
294                                                                        PRE_DWPVS + "MinimalWCSElevationModelResolution",
295                                                                        nsContext,
296                                                                        0 );
297    
298            double extendRequestPercentage = XMLTools.getNodeAsDouble( deegreeNode,
299                                                                       PRE_DWPVS + "ExtendRequestPercentage",
300                                                                       nsContext,
301                                                                       0 );
302            if ( extendRequestPercentage > 100 ) {
303                LOG.logWarning( Messages.getMessage( "WPVS_WRONG_EXTEND_REQUEST_PERCENTAGE",
304                                                     Double.valueOf( extendRequestPercentage ),
305                                                     Double.valueOf( 100 ) ) );
306                extendRequestPercentage = 100d;
307            } else if ( extendRequestPercentage < -0.00000001 ) {
308                LOG.logWarning( Messages.getMessage( "WPVS_WRONG_EXTEND_REQUEST_PERCENTAGE",
309                                                     Double.valueOf( extendRequestPercentage ),
310                                                     Double.valueOf( 0 ) ) );
311                extendRequestPercentage = 0d;
312            }
313            WPVSDeegreeParams wpvsDeegreeParams = new WPVSDeegreeParams( defaultOnlineResource,
314                                                                         cacheSize,
315                                                                         reqTimeLimit,
316                                                                         charSet,
317                                                                         copyright,
318                                                                         isWatermarked,
319                                                                         maxLifeTime,
320                                                                         viewQuality,
321                                                                         backgroundMap,
322                                                                         maxMapWidth,
323                                                                         maxMapHeight,
324                                                                         quality,
325                                                                         maximumFarClippingPlane,
326                                                                         nearClippingPlane,
327                                                                         defaultSplitter,
328                                                                         minimalTerrainHeight,
329                                                                         minimalWCS_DGMResolution,
330                                                                         extendRequestPercentage * 0.01,
331                                                                         maxTextureDimension, quadMergeCount );
332    
333            return wpvsDeegreeParams;
334        }
335    
336        /**
337         * Creates and returns a new <code>Dataset</code> object from the given <code>Element</code> and the parent
338         * <code>Dataset</code> object.
339         * 
340         * @param datasetElement
341         * @param parent
342         *            may be null if root Dataset
343         * @return Returns a new Dataset object.
344         * @throws XMLParsingException
345         * @throws MissingParameterValueException
346         * @throws InvalidParameterValueException
347         * @throws OGCWebServiceException
348         * @throws InvalidConfigurationException
349         */
350        private Dataset parseDataset( Element datasetElement, Dataset parent, CoordinateSystem defaultCoordinateSystem,
351                                      double defaultMinScaleDonominator, double defaultMaxScaleDenominator,
352                                      double minimalWCS_DGMResolution )
353                                                                       throws XMLParsingException,
354                                                                       MissingParameterValueException,
355                                                                       InvalidParameterValueException,
356                                                                       OGCWebServiceException,
357                                                                       InvalidConfigurationException {
358            // attributes
359            boolean queryable = XMLTools.getNodeAsBoolean( datasetElement, "@queryable", nsContext, false );
360            boolean opaque = XMLTools.getNodeAsBoolean( datasetElement, "@opaque", nsContext, false );
361            boolean noSubsets = XMLTools.getNodeAsBoolean( datasetElement, "@noSubsets", nsContext, false );
362            int fixedWidth = XMLTools.getNodeAsInt( datasetElement, "@fixedWidth", nsContext, 0 );
363            int fixedHeight = XMLTools.getNodeAsInt( datasetElement, "@fixedHeight", nsContext, 0 );
364    
365            // elements
366            String name = XMLTools.getNodeAsString( datasetElement, PRE_DWPVS + "Name/text()", nsContext, null );
367            String title = XMLTools.getRequiredNodeAsString( datasetElement, PRE_DWPVS + "Title/text()", nsContext );
368            String abstract_ = XMLTools.getNodeAsString( datasetElement, PRE_DWPVS + "Abstract/text()", nsContext, null );
369            Keywords[] keywords = getKeywords( XMLTools.getNodes( datasetElement, PRE_OWS + "Keywords", nsContext ) );
370            String[] crsStrings = XMLTools.getNodesAsStrings( datasetElement, PRE_DWPVS + "CRS/text()", nsContext );
371            List<CoordinateSystem> crsList = parseCoordinateSystems( crsStrings );
372    
373            if ( parent == null ) { // root dataset
374                if ( crsList.size() == 0 || crsList.get( 0 ) == null ) {
375                    throw new InvalidCapabilitiesException( Messages.getMessage( "WPVS_NO_TOPLEVEL_DATASET_CRS", title ) );
376                }
377                defaultCoordinateSystem = crsList.get( 0 );
378            }
379    
380            String[] format = XMLTools.getRequiredNodesAsStrings( datasetElement, PRE_DWPVS + "Format/text()", nsContext );
381            // wgs84 == mandatory
382            Element boundingBoxElement = (Element) XMLTools.getRequiredNode( datasetElement,
383                                                                             PRE_OWS + "WGS84BoundingBox",
384                                                                             nsContext );
385            Envelope wgs84BoundingBox = getWGS84BoundingBoxType( boundingBoxElement );
386    
387            Envelope[] boundingBoxes = getBoundingBoxes( datasetElement, defaultCoordinateSystem );
388            Dimension[] dimensions = parseDimensions( datasetElement );
389            DataProvider dataProvider = parseDataProvider( datasetElement );
390            Identifier identifier = parseDatasetIdentifier( datasetElement, PRE_DWPVS + "Identifier" );
391            MetaData[] metaData = parseMetaData( datasetElement );
392            DatasetReference[] datasetRefs = parseDatasetReferences( datasetElement );
393            FeatureListReference[] featureListRefs = parseFeatureListReferences( datasetElement );
394            Style[] style = parseStyles( datasetElement );
395            double minScaleDenom = XMLTools.getNodeAsDouble( datasetElement,
396                                                             PRE_DWPVS + "MinimumScaleDenominator/text()",
397                                                             nsContext,
398                                                             defaultMinScaleDonominator );
399    
400            // update the smallestMinimalScaleDenomiator
401            if ( minScaleDenom < smallestMinimalScaleDenominator ) {
402                smallestMinimalScaleDenominator = minScaleDenom;
403            }
404    
405            double maxScaleDenom = XMLTools.getNodeAsDouble( datasetElement,
406                                                             PRE_DWPVS + "MaximumScaleDenominator/text()",
407                                                             nsContext,
408                                                             defaultMaxScaleDenominator );
409    
410            if ( parent == null ) {// toplevel dataset sets the default minScaleDenominator
411                defaultMinScaleDonominator = minScaleDenom;
412                defaultMaxScaleDenominator = maxScaleDenom;
413            }
414    
415            if ( minScaleDenom >= maxScaleDenom ) {
416                throw new InvalidCapabilitiesException( "MinimumScaleDenominator must be " + "less than MaximumScaleDenominator!" );
417            }
418            CoordinateSystem currentCRS = defaultCoordinateSystem;
419            if ( crsList.size() > 0 && crsList.get( 0 ) != null ) {
420                currentCRS = crsList.get( 0 );
421            }
422            ElevationModel elevationModel = parseElevationModel( datasetElement,
423                                                                 name,
424                                                                 minimalWCS_DGMResolution,
425                                                                 currentCRS,
426                                                                 defaultMinScaleDonominator,
427                                                                 defaultMaxScaleDenominator );
428            AbstractDataSource[] dataSources = parseAbstractDatasources( datasetElement,
429                                                                         name,
430                                                                         defaultMinScaleDonominator,
431                                                                         defaultMaxScaleDenominator,
432                                                                         currentCRS );
433    
434            // create new root dataset
435            Dataset dataset = new Dataset( queryable,
436                                           opaque,
437                                           noSubsets,
438                                           fixedWidth,
439                                           fixedHeight,
440                                           name,
441                                           title,
442                                           abstract_,
443                                           keywords,
444                                           crsList,
445                                           format,
446                                           wgs84BoundingBox,
447                                           boundingBoxes,
448                                           dimensions,
449                                           dataProvider,
450                                           identifier,
451                                           metaData,
452                                           datasetRefs,
453                                           featureListRefs,
454                                           style,
455                                           minScaleDenom,
456                                           maxScaleDenom,
457                                           null,
458                                           elevationModel,
459                                           dataSources,
460                                           parent );
461    
462            // get child datasets
463            List nl = XMLTools.getNodes( datasetElement, PRE_DWPVS + "Dataset", nsContext );
464            Dataset[] childDatasets = new Dataset[nl.size()];
465            for ( int i = 0; i < childDatasets.length; i++ ) {
466                childDatasets[i] = parseDataset( (Element) nl.get( i ),
467                                                 dataset,
468                                                 defaultCoordinateSystem,
469                                                 defaultMinScaleDonominator,
470                                                 defaultMaxScaleDenominator,
471                                                 minimalWCS_DGMResolution );
472    
473            }
474    
475            // set child datasets
476            dataset.setDatasets( childDatasets );
477    
478            return dataset;
479        }
480    
481        /**
482         * Creates and returns a new <code>ElevationModel</code> object from the given <code>Element</code> and the
483         * parent <code>Dataset</code>.
484         * 
485         * The OGC ElevationModel contains only a String. The Deegree ElevationModel additionaly contains a complex
486         * dataSource.
487         * 
488         * @param datasetElement
489         * @param parent
490         * @return Returns the ElevationModel object.
491         * @throws XMLParsingException
492         * @throws OGCWebServiceException
493         * @throws InvalidParameterValueException
494         * @throws MissingParameterValueException
495         * @throws InvalidConfigurationException
496         */
497        private ElevationModel parseElevationModel( Element datasetElement, String parentName,
498                                                    double minimalWCS_DGMResolution, CoordinateSystem defaultCRS,
499                                                    double defaultMinScaleDenominator, double defaultMaxScaleDenominator )
500                                                                                                                          throws XMLParsingException,
501                                                                                                                          MissingParameterValueException,
502                                                                                                                          InvalidParameterValueException,
503                                                                                                                          OGCWebServiceException,
504                                                                                                                          InvalidConfigurationException {
505    
506            Element elevationElement = null;
507            String name = null;
508            ElevationModel elevationModel = null;
509    
510            elevationElement = (Element) XMLTools.getNode( datasetElement, PRE_DWPVS + "ElevationModel", nsContext );
511    
512            AbstractDataSource[] dataSources = null;
513            if ( elevationElement != null ) {
514    
515                name = XMLTools.getRequiredNodeAsString( elevationElement, PRE_DWPVS + "Name/text()", nsContext );
516    
517                dataSources = parseAbstractDatasources( elevationElement,
518                                                        parentName,
519                                                        defaultMinScaleDenominator,
520                                                        defaultMaxScaleDenominator,
521                                                        defaultCRS );
522                if ( dataSources.length < 1 ) {
523                    throw new InvalidCapabilitiesException( "Each '" + elevationElement.getNodeName()
524                                                            + "' must contain at least one data source!" );
525                }
526                if ( !Double.isNaN( minimalWCS_DGMResolution ) ) {
527                    // little trick to know which dgm datasources have a configured minimal resolution,
528                    // if the minimalWCS_DGMResolution is not set (e.g Double.nan) nothing has to be
529                    // done (a value of 0d is presumed)
530                    for ( AbstractDataSource source : dataSources ) {
531                        if ( source.getServiceType() == AbstractDataSource.LOCAL_WCS || source.getServiceType() == AbstractDataSource.REMOTE_WCS ) {
532                            ( (LocalWCSDataSource) source ).setConfiguredMinimalDGMResolution( minimalWCS_DGMResolution );
533                        }
534                    }
535                }
536            }
537    
538            elevationModel = new ElevationModel( name, dataSources );
539    
540            return elevationModel;
541        }
542    
543        /**
544         * Creates and returns a new array of <code>AbstractDataSource</code> objects from the given <code>Element</code>.
545         * 
546         * If the objects are used within an ElevationModel object, they may be of the following types: LocalWCSDataSource,
547         * RemoteWCSDataSource, LocalWFSDataSource, RemoteWFSDataSource. If the objects are used within a Dataset object,
548         * they may additionaly be of the types: LocalWMSDataSource, RemoteWMSDataSource.
549         * 
550         * @param element
551         * @return Returns a new array of AbstractDataSource objects.
552         * @throws XMLParsingException
553         * @throws OGCWebServiceException
554         * @throws InvalidConfigurationException
555         */
556        private AbstractDataSource[] parseAbstractDatasources( Element element, String parentName,
557                                                               double defaultMinScaleDenominator,
558                                                               double defaultMaxScaleDenominator,
559                                                               CoordinateSystem defaultCRS )
560                                                                                            throws XMLParsingException,
561                                                                                            OGCWebServiceException,
562                                                                                            InvalidConfigurationException {
563    
564            List abstractDataSources = XMLTools.getNodes( element, "*", nsContext );
565            List<AbstractDataSource> tempDataSources = new ArrayList<AbstractDataSource>( abstractDataSources.size() );
566    
567            for ( int i = 0; i < abstractDataSources.size(); i++ ) {
568    
569                Element dataSourceElement = (Element) abstractDataSources.get( i );
570    
571                // String nodeName = dataSourceElement.getNodeName();
572                String nodeName = dataSourceElement.getLocalName();
573    
574                if ( nodeName != null && nodeName.endsWith( "DataSource" ) ) {
575                    QualifiedName pn = null;
576                    if ( parentName != null ) {
577                        pn = new QualifiedName( PRE_DWPVS, parentName, nsContext.getURI( PRE_DWPVS ) );
578                    }
579                    QualifiedName name = XMLTools.getNodeAsQualifiedName( dataSourceElement,
580                                                                          PRE_DWPVS + "Name/text()",
581                                                                          nsContext,
582                                                                          pn );
583    
584                    OWSCapabilities owsCapabilities = parseOWSCapabilities( dataSourceElement );
585    
586                    double minScaleDenom = XMLTools.getNodeAsDouble( dataSourceElement,
587                                                                     PRE_DWPVS + "MinimumScaleDenominator/text()",
588                                                                     nsContext,
589                                                                     defaultMinScaleDenominator );
590    
591                    // update the smallestMinimalScaleDenomiator
592                    if ( minScaleDenom < smallestMinimalScaleDenominator )
593                        smallestMinimalScaleDenominator = minScaleDenom;
594    
595                    double maxScaleDenom = XMLTools.getNodeAsDouble( dataSourceElement,
596                                                                     PRE_DWPVS + "MaximumScaleDenominator/text()",
597                                                                     nsContext,
598                                                                     defaultMaxScaleDenominator );
599    
600                    Surface validArea = parseValidArea( dataSourceElement, defaultCRS );
601                    AbstractDataSource dataSource = null;
602    
603                    if ( nodeName.equals( "LocalWCSDataSource" ) || "RemoteWCSDataSource".equals( nodeName ) ) {
604                        Element filterElement = (Element) XMLTools.getRequiredNode( dataSourceElement,
605                                                                                    PRE_DWPVS + "FilterCondition",
606                                                                                    nsContext );
607                        GetCoverage getCoverage = parseWCSFilterCondition( filterElement );
608                        Color[] transparentColors = parseTransparentColors( dataSourceElement );
609    
610                        if ( "RemoteWCSDataSource".equals( nodeName ) ) {
611                            dataSource = new RemoteWCSDataSource( name,
612                                                                  owsCapabilities,
613                                                                  validArea,
614                                                                  minScaleDenom,
615                                                                  maxScaleDenom,
616                                                                  getCoverage,
617                                                                  transparentColors );
618                            LOG.logDebug( "created remote wcs with name: " + name );
619                        } else {
620                            dataSource = new LocalWCSDataSource( name,
621                                                                 owsCapabilities,
622                                                                 validArea,
623                                                                 minScaleDenom,
624                                                                 maxScaleDenom,
625                                                                 getCoverage,
626                                                                 transparentColors );
627                            LOG.logDebug( "created local wcs with name: " + name );
628                        }
629                    } else if ( "RemoteWFSDataSource".equals( nodeName ) || "LocalWFSDataSource".equals( nodeName ) ) {
630                        Text geoPropNode = (Text) XMLTools.getRequiredNode( dataSourceElement,
631                                                                            PRE_DWPVS + "GeometryProperty/text()",
632                                                                            nsContext );
633                        PropertyPath geometryProperty = parsePropertyPath( geoPropNode );
634    
635                        Element filterElement = (Element) XMLTools.getNode( dataSourceElement,
636                                                                            PRE_DWPVS + "FilterCondition/ogc:Filter",
637                                                                            nsContext );
638                        
639                        int maxFeatures = XMLTools.getNodeAsInt( dataSourceElement, PRE_DWPVS + "MaxFeatures", nsContext, -1 );
640    
641                        Filter filterCondition = null;
642                        if ( filterElement != null ) {
643                            filterCondition = AbstractFilter.buildFromDOM( filterElement, false );
644                        }
645    
646                        // FeatureCollectionAdapter adapter = createFCAdapterFromAdapterClassName(
647                        // dataSourceElement );
648    
649                        if( "LocalWFSDataSource".equals( nodeName ) ){
650                        dataSource = new LocalWFSDataSource( name,
651                                                             owsCapabilities,
652                                                             validArea,
653                                                             minScaleDenom,
654                                                             maxScaleDenom,
655                                                             geometryProperty,
656                                                             filterCondition,
657                                                             maxFeatures );
658                        LOG.logDebug( "created local wfs with name: " + name );
659                        }                    else {
660                        dataSource = new RemoteWFSDataSource( name,
661                                                              owsCapabilities,
662                                                              validArea,
663                                                              minScaleDenom,
664                                                              maxScaleDenom,
665                                                              geometryProperty,
666                                                              filterCondition, maxFeatures );
667                        LOG.logDebug( "created remote wfs with name: " + name );
668                        }
669                    } else if ( nodeName.equals( "LocalWMSDataSource" ) ) {
670                        if ( element.getNodeName().endsWith( "ElevationModel" ) ) {
671                            throw new InvalidConfigurationException( "An ElevationModel cannot " + "contain a LocalWMSDataSource!" );
672                        }
673                        Element filterElement = (Element) XMLTools.getRequiredNode( dataSourceElement,
674                                                                                    PRE_DWPVS + "FilterCondition",
675                                                                                    nsContext );
676                        GetMap getMap = parseWMSFilterCondition( filterElement );
677    
678                        Color[] transparentColors = parseTransparentColors( dataSourceElement );
679    
680                        dataSource = new LocalWMSDataSource( name,
681                                                             owsCapabilities,
682                                                             validArea,
683                                                             minScaleDenom,
684                                                             maxScaleDenom,
685                                                             getMap,
686                                                             transparentColors );
687                        LOG.logDebug( "created local wms with name: " + name );
688    
689                    } else if ( nodeName.equals( "RemoteWMSDataSource" ) ) {
690                        if ( element.getNodeName().endsWith( "ElevationModel" ) ) {
691                            throw new InvalidConfigurationException( "An ElevationModel cannot " + "contain a LocalWMSDataSource!" );
692                        }
693                        Element filterElement = (Element) XMLTools.getRequiredNode( dataSourceElement,
694                                                                                    PRE_DWPVS + "FilterCondition",
695                                                                                    nsContext );
696                        GetMap getMap = parseWMSFilterCondition( filterElement );
697    
698                        Color[] transparentColors = parseTransparentColors( dataSourceElement );
699    
700                        dataSource = new RemoteWMSDataSource( name,
701                                                              owsCapabilities,
702                                                              validArea,
703                                                              minScaleDenom,
704                                                              maxScaleDenom,
705                                                              getMap,
706                                                              transparentColors );
707                        LOG.logDebug( "created remote wms with name: " + name );
708                    } else {
709                        throw new InvalidCapabilitiesException( "Unknown data source: '" + nodeName + "'" );
710                    }
711    
712                    tempDataSources.add( dataSource );
713                }
714            }
715    
716            AbstractDataSource[] dataSources = tempDataSources.toArray( new AbstractDataSource[tempDataSources.size()] );
717    
718            return dataSources;
719        }
720    
721        /**
722         * FIXME check content of StringBuffer and Map! This is an adapted copy from:
723         * org.deegree.ogcwebservices.wms.configuration#parseWMSFilterCondition(Node)
724         * 
725         * Creates and returns a new <code>GetMap</code> object from the given <code>Element</code>.
726         * 
727         * @param filterElement
728         * @return a partial wms GetMap request instance
729         * @throws XMLParsingException
730         */
731        private GetMap parseWMSFilterCondition( Element filterElement )
732                                                                       throws XMLParsingException {
733    
734            GetMap getMap = null;
735    
736            String wmsRequest = XMLTools.getRequiredNodeAsString( filterElement, PRE_DWPVS + "WMSRequest/text()", nsContext );
737    
738            StringBuffer sd = new StringBuffer( 1000 );
739            sd.append( "REQUEST=GetMap&LAYERS=%default%&STYLES=&SRS=EPSG:4326&" );
740            sd.append( "BBOX=0,0,1,1&WIDTH=1&HEIGHT=1&FORMAT=%default%" );
741    
742            Map<String, String> map1 = KVP2Map.toMap( sd.toString() );
743    
744            Map<String, String> map2 = KVP2Map.toMap( wmsRequest );
745            if ( map2.get( "VERSION" ) == null && map2.get( "WMTVER" ) == null ) {
746                map2.put( "VERSION", "1.1.1" );
747            }
748            // if no service is set use WMS as default
749            if ( map2.get( "SERVICE" ) == null ) {
750                map2.put( "SERVICE", "WMS" );
751            }
752    
753            map1.putAll( map2 );
754    
755            String id = Long.toString( IDGenerator.getInstance().generateUniqueID() );
756            map1.put( "ID", id );
757            try {
758                getMap = GetMap.create( map1 );
759            } catch ( Exception e ) {
760                throw new XMLParsingException( "could not create GetMap from WMS FilterCondition", e );
761            }
762    
763            return getMap;
764        }
765    
766        /**
767         * FIXME check content of StringBuffer ! This is an adapted copy from:
768         * org.deegree.ogcwebservices.wms.configuration#parseWCSFilterCondition(Node)
769         * 
770         * Creates and returns a new <code>GetCoverage</code> object from the given <code>Element</code>.
771         * 
772         * @param filterElement
773         * @return a partial GetCoverage request
774         * @throws XMLParsingException
775         */
776        private GetCoverage parseWCSFilterCondition( Element filterElement )
777                                                                            throws XMLParsingException {
778    
779            GetCoverage coverage = null;
780    
781            String wcsRequest = XMLTools.getRequiredNodeAsString( filterElement, PRE_DWPVS + "WCSRequest/text()", nsContext );
782    
783            StringBuffer sd = new StringBuffer( 1000 );
784            sd.append( "version=1.0.0&Coverage=%default%&CRS=EPSG:4326&BBOX=0,0,1,1" );
785            sd.append( "&Width=1&Height=1&Format=%default%&" );
786            sd.append( wcsRequest );
787    
788            String id = "" + IDGenerator.getInstance().generateUniqueID();
789    
790            try {
791                coverage = GetCoverage.create( id, sd.toString() );
792            } catch ( Exception e ) {
793                throw new XMLParsingException( "Could not create GetCoverage " + "from WPVS FilterCondition", e );
794            }
795    
796            return coverage;
797        }
798    
799        /**
800         * Creates and returns a new <code>OWSCapabilities</code> object from the given <code>Element</code>.
801         * 
802         * @param element
803         * @return Returns a new OWSCapabilities object.
804         * @throws XMLParsingException
805         * @throws InvalidCapabilitiesException
806         */
807        private OWSCapabilities parseOWSCapabilities( Element element )
808                                                                       throws XMLParsingException,
809                                                                       InvalidCapabilitiesException {
810    
811            Element owsCapabilitiesElement = (Element) XMLTools.getRequiredNode( element,
812                                                                                 PRE_DWPVS + "OWSCapabilities",
813                                                                                 nsContext );
814    
815            String format = null;
816    
817            // FIXME
818            // schema has onlineResourceType as not optional, so it should be mandatory.
819            // but in other examples onlineResource is never created with this onlineResourceType.
820            // therefore it gets omitted here, too.
821    
822            // String onlineResourceType = XMLTools.getRequiredNodeAsString(
823            // owsCapabilitiesElement, PRE_DWPVS+"OnlineResource/@xlink:type", nsContext );
824    
825            String onlineResourceURI = XMLTools.getRequiredNodeAsString( owsCapabilitiesElement,
826                                                                         PRE_DWPVS + "OnlineResource/@xlink:href",
827                                                                         nsContext );
828    
829            URL onlineResource;
830            try {
831                onlineResource = resolve( onlineResourceURI );
832            } catch ( MalformedURLException e ) {
833                throw new InvalidCapabilitiesException( onlineResourceURI + " does not represent a valid URL: "
834                                                        + e.getMessage() );
835            }
836            LOG.logDebug( "found following onlineResource: " + onlineResource );
837            return new OWSCapabilities( format, onlineResource );
838    
839            // FIXME
840            // if onlineResourceType is going to be used, the returned new OnlineResource should be
841            // created with different constructor:
842            // return new OWSCapabilities( format, onlineResourceType, onlineResource );
843        }
844    
845        /**
846         * Creates and returns a new <code>Geometry</code> object from the given Element.
847         * 
848         * @param dataSource
849         * @return Returns a new Geometry object.
850         * @throws XMLParsingException
851         * @throws InvalidConfigurationException
852         */
853        private Surface parseValidArea( Element dataSource, CoordinateSystem defaultCRS )
854                                                                                         throws XMLParsingException,
855                                                                                         InvalidConfigurationException {
856            List nl = XMLTools.getNodes( dataSource, PRE_DWPVS + "ValidArea/*", nsContext );
857            if ( nl.size() == 0 ) {
858                return null;
859            }
860    
861            if ( nl.size() > 1 ) {
862                LOG.logWarning( Messages.getMessage( "WPVS_WRONG_NUMBER_OF_VALID_AREAS",
863                                                     GMLNS.toASCIIString(),
864                                                     dataSource.getLocalName() ) );
865                return null;
866            }
867            Surface validArea = null;
868            if ( ( (Node) nl.get( 0 ) ).getNamespaceURI().equals( GMLNS.toASCIIString() ) ) {
869                try {
870                    String srsName = XMLTools.getNodeAsString( (Node) nl.get( 0 ), "@srsName", nsContext, null );
871                    if ( srsName == null ) {
872                        srsName = defaultCRS.getFormattedString();
873                    }
874    
875                    Geometry geom = GMLGeometryAdapter.wrap( (Element) nl.get( 0 ), srsName );
876                    if ( !( geom instanceof Surface ) ) {
877                        throw new InvalidConfigurationException( Messages.getMessage( "WPVS_WRONG_GEOMTERY_VALID_AREA",
878                                                                                      dataSource.getLocalName() ) );
879                    }
880                    validArea = (Surface) geom;
881                } catch ( GeometryException e ) {
882                    throw new InvalidConfigurationException( Messages.getMessage( "WPVS_WRONG_GEOMTERY_VALID_AREA",
883                                                                                  dataSource.getLocalName() ) + "\n"
884                                                             + e.getMessage(),
885    
886                    e );
887                }
888    
889            } else if ( ( (Node) nl.get( 0 ) ).getNamespaceURI().equals( CommonNamespaces.OWSNS ) ) {
890                try {
891                    Element bbox = (Element) nl.get( 0 );
892                    if ( !"BoundingBox".equals( bbox.getLocalName() ) ) {
893                        throw new InvalidConfigurationException( Messages.getMessage( "WPVS_WRONG_GEOMTERY_VALID_AREA",
894                                                                                      dataSource.getLocalName() ) );
895                    }
896                    Envelope env = parseBoundingBox( (Element) nl.get( 0 ), null );
897                    validArea = GeometryFactory.createSurface( env, env.getCoordinateSystem() );
898                } catch ( GeometryException e ) {
899                    throw new InvalidConfigurationException( Messages.getMessage( "WPVS_WRONG_GEOMTERY_VALID_AREA",
900                                                                                  dataSource.getLocalName() ) + "\n"
901                                                             + e.getMessage(), e );
902                } catch ( InvalidParameterValueException e ) {
903                    throw new InvalidConfigurationException( Messages.getMessage( "WPVS_WRONG_GEOMTERY_VALID_AREA",
904                                                                                  dataSource.getLocalName() ) + "\n"
905                                                             + e.getMessage(), e );
906                }
907            } else {
908                LOG.logWarning( Messages.getMessage( "WPVS_WRONG_GEOMTERY_VALID_AREA", dataSource.getLocalName() ) );
909                return null;
910            }
911            return validArea;
912        }
913    
914        /**
915         * Creates and returns a new array of <code>Color</code> objects from the given Element.
916         * 
917         * @param dataSourceElement
918         * @return Returns a new array of Color objects.
919         * @throws XMLParsingException
920         * @throws InvalidCapabilitiesException
921         */
922        private Color[] parseTransparentColors( Element dataSourceElement )
923                                                                           throws XMLParsingException {
924    
925            List colorList = XMLTools.getNodes( dataSourceElement,
926                                                PRE_DWPVS + "TransparentColors/" + PRE_DWPVS + "Color",
927                                                nsContext );
928    
929            Color[] transparentColors = null;
930            if ( colorList != null ) {
931                transparentColors = new Color[colorList.size()];
932    
933                for ( int i = 0; i < transparentColors.length; i++ ) {
934    
935                    Element colorElement = (Element) colorList.get( i );
936                    String color = XMLTools.getRequiredNodeAsString( colorElement, "text()", nsContext );
937    
938                    transparentColors[i] = Color.decode( color );
939                }
940            }
941    
942            return transparentColors;
943        }
944    
945    }