001    //$HeadURL: $
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.wcts.capabilities;
038    
039    import java.util.ArrayList;
040    import java.util.HashMap;
041    import java.util.LinkedList;
042    import java.util.List;
043    import java.util.Map;
044    
045    import org.deegree.crs.exceptions.TransformationException;
046    import org.deegree.crs.transformations.Transformation;
047    import org.deegree.crs.transformations.TransformationFactory;
048    import org.deegree.crs.transformations.helmert.Helmert;
049    import org.deegree.framework.log.ILogger;
050    import org.deegree.framework.log.LoggerFactory;
051    import org.deegree.model.crs.CRSFactory;
052    import org.deegree.model.crs.CoordinateSystem;
053    import org.deegree.model.crs.UnknownCRSException;
054    import org.deegree.ogcwebservices.wcts.capabilities.mdprofiles.MetadataProfile;
055    import org.deegree.ogcwebservices.wcts.capabilities.mdprofiles.TransformationMetadata;
056    import org.deegree.owscommon_1_1_0.Metadata;
057    
058    /**
059     * <code>Content</code> encapsulates the Content element of the WCTS_0.4.0 Capabilities document.
060     * 
061     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
062     * 
063     * @author last edited by: $Author:$
064     * 
065     * @version $Revision:$, $Date:$
066     * 
067     */
068    public class Content {
069    
070        private static ILogger LOG = LoggerFactory.getLogger( Content.class );
071    
072        private final Map<String, Transformation> transformations;
073    
074        private final List<String> methods;
075    
076        private List<CoordinateSystem> sourceCRSs;
077    
078        private List<CoordinateSystem> targetCRSs;
079    
080        private final CoverageAbilities coverageAbilities;
081    
082        private final FeatureAbilities featureAbilities;
083    
084        private final List<Metadata> metadata;
085    
086        private final boolean userDefinedCRS;
087    
088        private final List<MetadataProfile<?>> transformMetadata;
089    
090        /**
091         * @param configuredTransforms
092         *            Unordered list of zero or more identifiers of well-known coordinate operations which the server can
093         *            perform.
094         * @param methods
095         *            Unordered list of zero or more identifiers of well-known operation methods which the server can apply
096         *            in user-defined coordinate Transformations and Conversions.
097         * @param sourceCRSs
098         *            Unordered list of one or more identifiers of well-known CRSs in which the server can accept sourceCRS
099         *            values.
100         * @param targetCRSs
101         *            Unordered list of one or more identifiers of well-known CRSs in which the server can accept targetCRS
102         *            values.
103         * @param coverageAbilities
104         *            Specifies coverage transformation abilities of WCTS server.
105         * @param featureAbilities
106         *            Specifies feature transformation abilities of WCTS server.
107         * @param metadata
108         *            Optional unordered list of additional metadata about the data served by this WCTS implementation. For
109         *            example, this metadata could include more detailed definitions of the Methods, Transformations, and
110         *            CRSs known to this server, perhaps in the form of a gml:Dictionary of such information.
111         * @param userDefinedCRS
112         *            Specifies if this server supports user-defined Coordinate Reference Systems (CRSs).
113         * @param transformMetadata
114         */
115        public Content( Map<String, Transformation> configuredTransforms, List<String> methods,
116                        List<CoordinateSystem> sourceCRSs, List<CoordinateSystem> targetCRSs,
117                        CoverageAbilities coverageAbilities, FeatureAbilities featureAbilities, List<Metadata> metadata,
118                        boolean userDefinedCRS, List<MetadataProfile<?>> transformMetadata ) {
119            if ( configuredTransforms == null ) {
120                this.transformations = new HashMap<String, Transformation>();
121            } else {
122                this.transformations = configuredTransforms;
123            }
124            this.methods = methods;
125            this.sourceCRSs = sourceCRSs;
126            this.targetCRSs = targetCRSs;
127            this.coverageAbilities = coverageAbilities;
128            this.featureAbilities = featureAbilities;
129            if ( transformMetadata == null ) {
130                this.transformMetadata = new ArrayList<MetadataProfile<?>>();
131            } else {
132                this.transformMetadata = transformMetadata;
133            }
134            if ( metadata == null ) {
135                this.metadata = new ArrayList<Metadata>();
136            } else {
137                this.metadata = metadata;
138            }
139            this.userDefinedCRS = userDefinedCRS;
140            this.transformMetadata.addAll( createMDForConfiguredTransforms( transformations, transformMetadata ) );
141        }
142    
143        /**
144         * @return the transformations.
145         */
146        public final Map<String, Transformation> getTransformations() {
147            return transformations;
148        }
149    
150        /**
151         * @return the methods.
152         */
153        public final List<String> getMethods() {
154            return methods;
155        }
156    
157        /**
158         * @return the sourceCRSs.
159         */
160        public final List<CoordinateSystem> getSourceCRSs() {
161            return sourceCRSs;
162        }
163    
164        /**
165         * @return the targetCRSs.
166         */
167        public final List<CoordinateSystem> getTargetCRSs() {
168            return targetCRSs;
169        }
170    
171        /**
172         * @return the coverageAbilities.
173         */
174        public final CoverageAbilities getCoverageAbilities() {
175            return coverageAbilities;
176        }
177    
178        /**
179         * @return the featureAbilities.
180         */
181        public final FeatureAbilities getFeatureAbilities() {
182            return featureAbilities;
183        }
184    
185        /**
186         * @return the metadatas, may be empty but will never be <code>null</code>
187         */
188        public final List<Metadata> getMetadata() {
189            return metadata;
190        }
191    
192        /**
193         * @return the userDefinedCRS.
194         */
195        public final boolean supportsUserDefinedCRS() {
196            return userDefinedCRS;
197        }
198    
199        /**
200         * @return the transformMetadata elements.
201         */
202        public final List<MetadataProfile<?>> getTransformMetadata() {
203            return transformMetadata;
204        }
205    
206        /**
207         * Create the transformations and their MetaData for all source / target combinations, e.g. create identifiers for
208         * the default transformations of the crs package which is the usage of a wgs 84 pivot crs and the helmert
209         * transformation.
210         * 
211         * @param transformationPrefix
212         *            to be used for the transformations
213         */
214        public void describeDefaultTransformations( String transformationPrefix ) {
215            List<TransformationMetadata> result = new ArrayList<TransformationMetadata>( transformMetadata.size() );
216            List<String> allKeys = new LinkedList<String>( transformations.keySet() );
217            int tCount = 0;
218            for ( CoordinateSystem sourceCRS : sourceCRSs ) {
219                if ( sourceCRS != null ) {
220                    // lets create metadatas for all target crs's.
221                    for ( CoordinateSystem tCRS : targetCRSs ) {
222                        if ( !sourceCRS.getCRS().equals( tCRS.getCRS() ) ) {
223                            Transformation trans = createTransformation( sourceCRS, tCRS );
224                            if ( trans != null ) {
225                                // create a unique key.
226                                String key = transformationPrefix + tCount;
227                                while ( allKeys.contains( key ) ) {
228                                    key = transformationPrefix + ( ++tCount );
229                                }
230                                allKeys.add( key );
231                                result.add( new TransformationMetadata( trans, key, sourceCRS, tCRS,
232                                                                        createTransformationMDDescription( sourceCRS, tCRS ) ) );
233                                // add it to the configured transforms as well.
234                                transformations.put( key, trans );
235                            }
236                        }
237                    }
238                }
239            }
240            transformMetadata.addAll( result );
241        }
242    
243        /**
244         * Create a description saying:<code>
245         * Transforming from sourceCRS.getIdentifer (sourceCRS.getName() ) to targetCRS.getIdentifer (targetCRS.getName() ) using the helmert transformation.
246         * </code>
247         * if either the sourceCRS or the targetCRS are null the String "Transform using the helmert transformation." will
248         * be returned.
249         * 
250         * @param sourceCRS
251         * @param targetCRS
252         * @return a possible description for a Transformation Metadata.
253         */
254        protected String createTransformationMDDescription( CoordinateSystem sourceCRS, CoordinateSystem targetCRS ) {
255            if ( sourceCRS == null || targetCRS == null ) {
256                return "Transform using the helmert transformation.";
257            }
258            return "Transforming from " + sourceCRS.getIdentifier()
259                   + ( ( sourceCRS.getCRS().getName() == null ) ? "" : " (" + sourceCRS.getCRS().getName() + ")" ) + " to "
260                   + targetCRS.getIdentifier()
261                   + ( ( targetCRS.getCRS().getName() == null ) ? "" : " (" + targetCRS.getCRS().getName() + ")" )
262                   + " using the helmert transformation.";
263        }
264    
265        /**
266         * Creates a {@link TransformationMetadata} for the configured transformations and create Transformations for
267         * {@link TransformationMetadata} which do not have a {@link Transformation}.
268         * 
269         * @param configuredTransformations
270         *            to which the transformations will be added
271         * @param transformationMetadata
272         *            containing all configured {@link TransformationMetadata}
273         * 
274         * @return The list of {@link TransformationMetadata} that were created.
275         */
276        private List<TransformationMetadata> createMDForConfiguredTransforms(
277                                                                              Map<String, Transformation> configuredTransformations,
278                                                                              List<MetadataProfile<?>> transformationMetadata ) {
279            List<TransformationMetadata> result = new ArrayList<TransformationMetadata>( transformationMetadata.size() );
280            for ( String key : configuredTransformations.keySet() ) {
281                if ( key != null && !"".equals( key ) ) {
282                    LOG.logDebug( "Finding transformation metadata for key:" + key );
283                    TransformationMetadata sync = null;
284                    for ( MetadataProfile<?> mp : transformationMetadata ) {
285                        if ( mp != null && ( mp instanceof TransformationMetadata ) ) {
286                            if ( key.equalsIgnoreCase( ( (TransformationMetadata) mp ).getTransformID() ) ) {
287                                if ( sync != null ) {
288                                    LOG.logWarning( "The key: " + key + " has multiple metadatas, this may not be." );
289                                } else {
290                                    LOG.logDebug( "Found a metadata for key: ", key );
291                                    sync = (TransformationMetadata) mp;
292                                    if ( configuredTransformations.get( sync.getTransformID() ) == null
293                                         || configuredTransformations.get( sync.getTransformID() ) instanceof Helmert ) {
294                                        LOG.logDebug( "The transformation metadata referencing transformation: ",
295                                                      sync.getTransformID(),
296                                                      " did not have a transformation, creating a default one." );
297                                        Transformation trans = createTransformation( sync.getSourceCRS(),
298                                                                                     sync.getTargetCRS() );
299                                        if ( trans != null ) {
300                                            configuredTransformations.put( key, trans );
301                                        }
302                                    }
303                                }
304                            }
305                        }
306                    }
307                    if ( sync == null ) {
308                        Transformation trans = configuredTransformations.get( key );
309                        if ( trans != null ) {
310                            LOG.logDebug( "Creating a metadata for key: ", key );
311                            result.add( new TransformationMetadata( trans, key, CRSFactory.create( trans.getSourceCRS() ),
312                                                                    CRSFactory.create( trans.getTargetCRS() ),
313                                                                    trans.getDescription() ) );
314                        } else {
315                            LOG.logWarning( "Unable to create metadata from id: " + key
316                                            + " because no transformation was given." );
317                        }
318    
319                    }
320                }
321            }
322            return result;
323        }
324    
325        /**
326         * Uses the configured identifiers of the source and targets to create new (cached) coordinatesystems.
327         * 
328         * @param configuredProvider
329         *            may be <code>null</code> to use the default crs provider.
330         * 
331         */
332        public synchronized void updateFromProvider( String configuredProvider ) {
333            LOG.logWarning( "Updating the transformations is currently not supported." );
334            List<CoordinateSystem> newCRSs = updateCRSs( configuredProvider, sourceCRSs );
335            sourceCRSs = newCRSs;
336    
337            newCRSs = updateCRSs( configuredProvider, targetCRSs );
338            targetCRSs = newCRSs;
339        }
340    
341        private List<CoordinateSystem> updateCRSs( String configuredProvider, List<CoordinateSystem> crss ) {
342            List<CoordinateSystem> newCRSs = new ArrayList<CoordinateSystem>( crss.size() );
343            for ( CoordinateSystem crs : crss ) {
344                if ( crs != null ) {
345                    CoordinateSystem newCRS = null;
346                    try {
347                        newCRS = CRSFactory.create( configuredProvider, crs.getIdentifier() );
348                    } catch ( UnknownCRSException e ) {
349                        LOG.logError( e );
350                    }
351                    if ( newCRS != null ) {
352                        newCRSs.add( newCRS );
353                    } else {
354                        LOG.logWarning( "Removing old crs with id: " + crs.getIdentifier()
355                                        + " because it is no longer available in the crs registry." );
356                    }
357                }
358            }
359            return newCRSs;
360        }
361    
362        private Transformation createTransformation( CoordinateSystem sourceCRS, CoordinateSystem targetCRS ) {
363            Transformation result = null;
364            try {
365                result = TransformationFactory.getInstance().createFromCoordinateSystems( sourceCRS.getCRS(),
366                                                                                          targetCRS.getCRS() );
367            } catch ( IllegalArgumentException e ) {
368                LOG.logError( "Error while creating a default transformation for sourceCRS: " + sourceCRS, e );
369            } catch ( TransformationException e ) {
370                LOG.logError( "Error while creating a default transformation for sourceCRS: " + sourceCRS, e );
371            }
372            return result;
373    
374        }
375    
376    }