001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/crs/configuration/CRSConfiguration.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.crs.configuration;
038    
039    import static org.deegree.crs.projections.ProjectionUtils.EPS11;
040    
041    import java.io.BufferedWriter;
042    import java.io.File;
043    import java.io.FileWriter;
044    import java.io.IOException;
045    import java.io.InputStream;
046    import java.lang.reflect.Constructor;
047    import java.lang.reflect.InvocationTargetException;
048    import java.util.HashMap;
049    import java.util.LinkedList;
050    import java.util.List;
051    import java.util.Map;
052    import java.util.Properties;
053    
054    import org.deegree.crs.configuration.deegree.DeegreeCRSProvider;
055    import org.deegree.crs.configuration.proj4.PROJ4CRSProvider;
056    import org.deegree.crs.coordinatesystems.CoordinateSystem;
057    import org.deegree.crs.coordinatesystems.ProjectedCRS;
058    import org.deegree.crs.exceptions.CRSConfigurationException;
059    import org.deegree.crs.projections.Projection;
060    import org.deegree.framework.log.ILogger;
061    import org.deegree.framework.log.LoggerFactory;
062    import org.deegree.i18n.Messages;
063    
064    /**
065     * The <code>CRSConfiguration</code> creates, instantiates and supplies a configured CRS-Provider. Because only one
066     * crs-configuration is needed inside the JVM, this implementation uses a singleton pattern.
067     * <p>
068     * The configuration will try to read the file: crs_providers.properties. It uses following strategie to load this file,
069     * first the root directory (e.g. '/' or WEB-INF/classes ) will be searched. If no file was found there, it will try to
070     * load from the package. The properties file must denote a property with name 'CRS_PROVIDER' followed by a '=' and a
071     * fully qualified name denoting the class (an instance of CRSProvider) which should be available in the classpath. This
072     * class must have an empty constructor.
073     * </p>
074     * 
075     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
076     * @author last edited by: $Author: rbezema $
077     * 
078     * @version $Revision: 18308 $, $Date: 2009-07-02 14:40:19 +0200 (Do, 02. Jul 2009) $
079     */
080    
081    public class CRSConfiguration {
082        private static ILogger LOG = LoggerFactory.getLogger( CRSConfiguration.class );
083    
084        private CRSProvider provider;
085    
086        /**
087         * public so servlets can clear it, to avoid memory leaks...
088         */
089        public static final Map<String, CRSConfiguration> DEFINED_CONFIGURATIONS = new HashMap<String, CRSConfiguration>();
090    
091        private static final String DEFAULT_PROVIDER_CLASS = "org.deegree.crs.configuration.DeegreeCRSProvider";
092    
093        private static String CONFIGURED_DEFAULT_PROVIDER_CLASS = DEFAULT_PROVIDER_CLASS;
094    
095        //
096        private final static String PROVIDER_CONFIG = "crs_providers.properties";
097    
098        private static Properties configuredProperties = null;
099    
100        static {
101            configuredProperties = new Properties();
102            LOG.logDebug( "Trying to load configured CRS provider from configuration (/crs_providers.properties)." );
103            InputStream is = CRSConfiguration.class.getResourceAsStream( "/" + PROVIDER_CONFIG );
104            if ( is == null ) {
105                LOG.logDebug( "Trying to load configured CRS provider from configuration (org.deegree.crs.configuration.crs_providers.properties)." );
106                is = CRSConfiguration.class.getResourceAsStream( PROVIDER_CONFIG );
107            }
108            if ( is == null ) {
109                LOG.logWarning( Messages.getMessage( "CRS_CONFIG_NO_PROVIDER_DEFS_FOUND", PROVIDER_CONFIG ) );
110            } else {
111                try {
112                    configuredProperties.load( is );
113                    configuredProperties.put( "crs.default.configuration", "deegree-crs-configuration.xml" );
114                    String tmp = configuredProperties.getProperty( "CRS_PROVIDER" );
115                    if ( tmp != null && !"".equals( tmp.trim() ) ) {
116                        CONFIGURED_DEFAULT_PROVIDER_CLASS = tmp;
117                        String crs_configuration = System.getProperty( "crs.configuration" );
118                        if ( crs_configuration != null && !"".equals( crs_configuration ) ) {
119                            LOG.logInfo( "Using the supplied crs.configuration property for the crs_configuration location." );
120                            configuredProperties.put( "crs.configuration", crs_configuration );
121                        }
122                    }
123    
124                } catch ( Exception e ) {
125                    LOG.logError( e.getMessage(), e );
126                } finally {
127                    try {
128                        is.close();
129                    } catch ( IOException e ) {
130                        // no output if the stream can't be closed, just leave it as it is.
131                    }
132                }
133            }
134    
135        }
136    
137        // private static CRSConfiguration CONFIG = null;
138    
139        /**
140         * @param provider
141         *            to get the CRS's from.
142         */
143        private CRSConfiguration( CRSProvider provider ) {
144            this.provider = provider;
145        }
146    
147        /**
148         * Creates or returns an instance of the CRSConfiguration by trying to instantiate the given provider class. If the
149         * name is null or "" the Provider configured in the 'crs_providers.properties' will be returned. If the
150         * instantiation of this class fails a {@link org.deegree.crs.configuration.deegree.DeegreeCRSProvider} will be
151         * returned.
152         * 
153         * @param providerName
154         *            the canonical name of the class, e.g. org.deegree.crs.MyProvider
155         * @return an instance of a CRS-Configuration with the configured CRSProvider.
156         * @throws CRSConfigurationException
157         *             if --anything-- went wrong while instantiating the CRSProvider.
158         */
159        public synchronized static CRSConfiguration getCRSConfiguration( String providerName ) {
160            String provName = null;
161            if ( providerName == null || "".equals( providerName.trim() ) ) {
162                provName = CONFIGURED_DEFAULT_PROVIDER_CLASS;
163            } else {
164                provName = providerName.trim();
165            }
166            LOG.logDebug( "Trying to find a provider for class: " + provName );
167            if ( DEFINED_CONFIGURATIONS.containsKey( provName ) && DEFINED_CONFIGURATIONS.get( provName ) != null ) {
168                LOG.logDebug( "Found a cached provider for class: " + provName );
169                return DEFINED_CONFIGURATIONS.get( provName );
170            }
171            CRSProvider provider = null;
172    
173            if ( CONFIGURED_DEFAULT_PROVIDER_CLASS.equals( provName )
174                 && CONFIGURED_DEFAULT_PROVIDER_CLASS.equals( DEFAULT_PROVIDER_CLASS ) ) {
175                provider = new DeegreeCRSProvider( new Properties( configuredProperties ) );
176            } else {
177                try {
178                    // use reflection to instantiate the configured provider.
179                    Class<?> t = Class.forName( provName );
180                    t.asSubclass( CRSProvider.class );
181                    LOG.logDebug( "Trying to load configured CRS provider from classname: " + provName );
182                    Constructor<?> constructor = t.getConstructor( Properties.class );
183                    if ( constructor != null ) {
184                        provider = (CRSProvider) constructor.newInstance( configuredProperties );
185                    }
186                } catch ( InstantiationException e ) {
187                    LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", provName, e.getMessage() ), e );
188                } catch ( IllegalAccessException e ) {
189                    LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", provName, e.getMessage() ), e );
190                } catch ( ClassNotFoundException e ) {
191                    LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", provName, e.getMessage() ), e );
192                } catch ( SecurityException e ) {
193                    LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", provName, e.getMessage() ), e );
194                } catch ( NoSuchMethodException e ) {
195                    LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", provName, e.getMessage() ), e );
196                } catch ( IllegalArgumentException e ) {
197                    LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", provName, e.getMessage() ), e );
198                } catch ( InvocationTargetException e ) {
199                    LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", provName, e.getMessage() ), e );
200                } catch ( Throwable t ) {
201                    LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", provName, t.getMessage() ), t );
202                } finally {
203                    if ( provider == null ) {
204                        LOG.logInfo( "The configured class: " + provName
205                                     + " was not created. Trying to create a deegree-crs-provider" );
206                        provider = new DeegreeCRSProvider( new Properties( configuredProperties ) );
207                    }
208                }
209            }
210            CRSConfiguration config = new CRSConfiguration( provider );
211            DEFINED_CONFIGURATIONS.put( provName, config );
212            LOG.logDebug( "Instantiated a new CRSConfiguration :" + config );
213            return config;
214        }
215    
216        /**
217         * Creates or returns an instance of the CRSConfiguration by reading the DEFAULT property configured in the
218         * 'crs_providers.properties'. If no key is given (or no string could be loaded), the {@link DeegreeCRSProvider}
219         * will be used.
220         * 
221         * @return an instance of a CRS-Configuration with the configured CRSProvider.
222         * @throws CRSConfigurationException
223         *             if --anything-- went wrong while instantiating the CRSProvider.
224         */
225        public synchronized static CRSConfiguration getCRSConfiguration()
226                                throws CRSConfigurationException {
227            return getCRSConfiguration( null );
228        }
229    
230        /**
231         * Overwrites the crs.configuration property with the given value.
232         * 
233         * @param fileName
234         *            to set the crs.configuration property to.
235         * 
236         * @return the old crs.configuration propert (if any)
237         * @throws CRSConfigurationException
238         *             if --anything-- went wrong while instantiating the CRSProvider.
239         */
240        public synchronized static String setDefaultFileProperty( String fileName ) {
241            return (String) configuredProperties.setProperty( "crs.configuration", fileName );
242        }
243    
244        /**
245         * export the given file to the deegree-crs format.
246         * 
247         * @param args
248         * @throws Exception
249         */
250        public static void main( String[] args )
251                                throws Exception {
252            if ( args.length == 0 ) {
253                outputHelp();
254            }
255            Map<String, String> params = new HashMap<String, String>( 5 );
256            for ( int i = 0; i < args.length; i++ ) {
257                String arg = args[i];
258                if ( arg != null && !"".equals( arg.trim() ) ) {
259                    arg = arg.trim();
260                    if ( arg.equalsIgnoreCase( "-?" ) || arg.equalsIgnoreCase( "-h" ) ) {
261                        outputHelp();
262                    } else {
263                        if ( i + 1 < args.length ) {
264                            String val = args[++i];
265                            if ( val != null && !"".equals( val.trim() ) ) {
266                                params.put( arg, val.trim() );
267                            } else {
268                                System.out.println( "Invalid value for parameter: " + arg );
269                            }
270                        } else {
271                            System.out.println( "No value for parameter: " + arg );
272                        }
273                    }
274                }
275            }
276            String inFormat = params.get( "-inFormat" );
277            if ( inFormat == null || "".equals( inFormat.trim() ) ) {
278                System.out.println( "No input format (inFormat) defined, setting to proj4" );
279                inFormat = "proj4";
280            }
281            String inFile = params.get( "-inFile" );
282            if ( inFile == null || "".equals( inFile.trim() ) ) {
283                System.out.println( "No input file set, exiting\n" );
284                outputHelp();
285                throw new Exception( "No input file set, exiting" );
286            }
287            // File inputFile = new File( inFile );
288    
289            String outFile = params.get( "-outFile" );
290            String outFormat = params.get( "-outFormat" );
291            if ( outFormat == null || "".equals( outFormat.trim() ) ) {
292                System.out.println( "No output format (outFormat) defined, setting to deegree" );
293                outFormat = "deegree";
294            }
295    
296            String veri = params.get( "-verify" );
297            boolean verify = ( veri != null && !"".equals( inFile.trim() ) );
298    
299            Properties inProps = new Properties();
300            inProps.put( "crs.configuration", inFile );
301            CRSProvider in = new PROJ4CRSProvider( inProps );
302            if ( "deegree".equalsIgnoreCase( inFormat ) ) {
303                try {
304                    in = new DeegreeCRSProvider( new Properties( configuredProperties ) );
305                } catch ( CRSConfigurationException e ) {
306                    e.printStackTrace();
307                }
308            }
309    
310            CRSProvider out = new DeegreeCRSProvider( new Properties( configuredProperties ) );
311            if ( "proj4".equalsIgnoreCase( outFormat ) ) {
312                out = new PROJ4CRSProvider();
313            }
314    
315            try {
316                List<CoordinateSystem> allSystems = in.getAvailableCRSs();
317                if ( verify ) {
318                    out = new DeegreeCRSProvider( null );
319                    List<CoordinateSystem> notExported = new LinkedList<CoordinateSystem>();
320                    for ( CoordinateSystem inCRS : allSystems ) {
321                        if ( inCRS.getType() == CoordinateSystem.PROJECTED_CRS ) {
322                            String id = inCRS.getIdentifier();
323                            CoordinateSystem outCRS = out.getCRSByID( id );
324                            // System.out.print( "Getting crs: " + id + " and projection: " +
325                            // ((ProjectedCRS)inCRS).getProjection().getDeegreeSpecificName() );
326                            if ( outCRS != null && outCRS.getType() == CoordinateSystem.PROJECTED_CRS ) {
327                                // System.out.println( "... [SUCCESS] to retrieve from deegree-config
328                                // with projection: " +
329                                // ((ProjectedCRS)outCRS).getProjection().getDeegreeSpecificName() );
330                                Projection inProj = ( (ProjectedCRS) inCRS ).getProjection();
331                                Projection outProj = ( (ProjectedCRS) outCRS ).getProjection();
332                                if ( Math.abs( inProj.getProjectionLatitude() - outProj.getProjectionLatitude() ) > EPS11 ) {
333                                    System.out.println( "For the projection with id: " + id
334                                                        + " the projectionLatitude differs:\n in ("
335                                                        + ( (ProjectedCRS) inCRS ).getProjection().getImplementationName()
336                                                        + "): " + Math.toDegrees( inProj.getProjectionLatitude() )
337                                                        + "\nout("
338                                                        + ( (ProjectedCRS) outCRS ).getProjection().getImplementationName()
339                                                        + " with id: "
340                                                        + ( (ProjectedCRS) outCRS ).getProjection().getImplementationName()
341                                                        + "): " + Math.toDegrees( outProj.getProjectionLatitude() ) );
342                                }
343                            } else {
344                                notExported.add( inCRS );
345                                System.out.println( id + " [FAILED] to retrieve from deegree-config." );
346                            }
347                        }
348                    }
349                    if ( notExported.size() > 0 ) {
350                        StringBuilder sb = new StringBuilder( notExported.size() * 2000 );
351                        out.export( sb, allSystems );
352                        if ( outFile != null && !"".equals( outFile.trim() ) ) {
353                            File outputFile = new File( outFile );
354                            BufferedWriter writer = new BufferedWriter( new FileWriter( outputFile ) );
355                            writer.write( sb.toString() );
356                            writer.flush();
357                            writer.close();
358                        } else {
359                            System.out.println( sb.toString() );
360                        }
361                    }
362                } else {
363    
364                    StringBuilder sb = new StringBuilder( allSystems.size() * 2000 );
365                    out.export( sb, allSystems );
366                    if ( outFile != null && !"".equals( outFile.trim() ) ) {
367                        File outputFile = new File( outFile );
368                        BufferedWriter writer = new BufferedWriter( new FileWriter( outputFile ) );
369                        writer.write( sb.toString() );
370                        writer.flush();
371                        writer.close();
372                    } else {
373                        System.out.println( sb.toString() );
374                    }
375                }
376            } catch ( CRSConfigurationException e ) {
377                e.printStackTrace();
378            } catch ( IOException e ) {
379                e.printStackTrace();
380            }
381    
382            // CRSConfiguration config = new CRSConfiguration(
383        }
384    
385        private static void outputHelp() {
386            StringBuilder sb = new StringBuilder();
387            sb.append( "The CRSConfiguration program can be used to create a deegree-crs-configuration, from other crs definition-formats. Following parameters are supported:\n" );
388            sb.append( "-inFile the /path/to/crs-definitions-file\n" );
389            sb.append( "-inFormat the format of the input file, valid values are proj4(default),deegree \n" );
390            sb.append( "-outFormat the format of the output file, valid values are deegree (default)\n" );
391            sb.append( "-outFile the /path/to/the/output/file or standard output if not supplied.\n" );
392            sb.append( "[-verify] checks the projection parameters of the inFormat against the deegree configuration.\n" );
393            sb.append( "-?|-h output this text\n" );
394            sb.append( "example usage: java -cp deegree.jar org.deegree.crs.configuration.CRSConfiguration -inFormat 'proj4' -inFile '/home/proj4/nad/epsg' -outFormat 'deegree' -outFile '/home/deegree/crs-definitions.xml'\n" );
395            System.out.println( sb.toString() );
396            System.exit( 1 );
397        }
398    
399        /**
400         * @return the crs provider.
401         */
402        public final CRSProvider getProvider() {
403            return provider;
404        }
405    
406        @Override
407        public String toString() {
408            return "CRSConfiguration is using "
409                   + ( ( provider == null ) ? "no crs provider, this is strange."
410                                           : "crs provider: " + provider.getClass().getCanonicalName() );
411        }
412    }