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