001    //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/crs/configuration/proj4/ProjFileResource.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.proj4;
038    
039    import java.io.BufferedReader;
040    import java.io.FileNotFoundException;
041    import java.io.FileReader;
042    import java.io.IOException;
043    import java.io.StreamTokenizer;
044    import java.io.StringReader;
045    import java.util.HashMap;
046    import java.util.List;
047    import java.util.Map;
048    import java.util.Properties;
049    import java.util.Set;
050    
051    import org.deegree.crs.configuration.resources.CRSResource;
052    import org.deegree.crs.coordinatesystems.CoordinateSystem;
053    import org.deegree.crs.coordinatesystems.GeographicCRS;
054    import org.deegree.crs.exceptions.CRSConfigurationException;
055    import org.deegree.crs.transformations.Transformation;
056    import org.deegree.crs.transformations.helmert.Helmert;
057    import org.deegree.framework.log.ILogger;
058    import org.deegree.framework.log.LoggerFactory;
059    import org.deegree.i18n.Messages;
060    
061    /**
062     * The <code>ProjFileResource</code> class TODO add class documentation here.
063     * 
064     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
065     * 
066     * @author last edited by: $Author: lbuesching $
067     * 
068     * @version $Revision: 27765 $, $Date: 2010-11-04 07:50:32 +0100 (Do, 04 Nov 2010) $
069     * 
070     */
071    public class ProjFileResource implements CRSResource<Map<String, String>> {
072    
073        private Map<String, Map<String, String>> idToParams = new HashMap<String, Map<String, String>>( 4000 );
074    
075        private static ILogger LOG = LoggerFactory.getLogger( ProjFileResource.class );
076    
077        /**
078         * @param provider
079         * @param properties
080         */
081        public ProjFileResource( PROJ4CRSProvider provider, Properties properties ) {
082            String fileName = properties.getProperty( "crs.configuration" );
083            try {
084                BufferedReader reader = new BufferedReader( new FileReader( fileName ) );
085                String line = reader.readLine();
086                Map<String, String> kvp = new HashMap<String, String>( 15 );
087                int lineNumber = 1;
088                while ( line != null ) {
089                    if ( line.startsWith( "#" ) ) {
090                        // remove the '#' from the String.
091                        if ( kvp.get( "comment" ) != null ) {
092                            LOG.logDebug( "(Line: " + lineNumber + ") Multiple comments found, removing previous: "
093                                          + kvp.get( "comment" ) );
094                        }
095                        kvp.put( "comment", line.substring( 1 ).trim() );
096                    } else {
097                        String identifier = parseConfigString( line, Integer.toString( lineNumber ), kvp );
098                        if ( identifier != null && !"".equals( identifier.trim() ) ) {
099                            LOG.logDebug( "Found identifier: " + identifier + " with following params: " + kvp );
100                            idToParams.put( identifier, kvp );
101                        }
102                        kvp = new HashMap<String, String>( 15 );
103                    }
104                    line = reader.readLine();
105                    lineNumber++;
106                }
107                reader.close();
108    
109            } catch ( FileNotFoundException e ) {
110                e.printStackTrace();
111            } catch ( IOException e ) {
112                LOG.logError( "Could not open file: " + fileName, e );
113                throw new CRSConfigurationException( e );
114                // e.printStackTrace();
115            }
116        }
117    
118        public Helmert getWGS84Transformation( GeographicCRS sourceCRS ) {
119            if ( sourceCRS == null ) {
120                return null;
121            }
122            for ( String id : sourceCRS.getIdentifiers() ) {
123                Map<String, String> params = null;
124                try {
125                    params = getURIAsType( id );
126                } catch ( IOException e ) {
127                    throw new CRSConfigurationException( e );
128                }
129                if ( params != null ) {
130                    return createWGS84ConversionInfo( sourceCRS, params );
131                }
132            }
133            return null;
134        }
135    
136        public Map<String, String> getURIAsType( String uri )
137                                throws IOException {
138            String tmpID = getIDCode( uri );
139            if ( LOG.isDebug() ) {
140                LOG.logDebug( "Given id: " + uri + " converted into: " + tmpID );
141            }
142            Map<String, String> result = idToParams.get( tmpID );
143            if ( result != null ) {
144                result = new HashMap<String, String>( result );
145            }
146            return result;
147        }
148    
149        /**
150         * @return a set containing all available ids.
151         */
152        public Set<String> getAvailableIDs() {
153            return idToParams.keySet();
154        }
155    
156        /**
157         * Creating the wgs84 aka BursaWolf conversion parameters. Either 3 or 7 parameters are supported.
158         * 
159         * @param params
160         *            to get the towgs84 param from
161         * @return the conversion info from the params or <code>null<code> if no conversion info is available.
162         * @throws CRSConfigurationException
163         *             if the number of params are not 3 or 7.
164         */
165        private Helmert createWGS84ConversionInfo( CoordinateSystem sourceCRS, Map<String, String> params )
166                                throws CRSConfigurationException {
167            Helmert result = null;
168            String tmpValue = params.remove( "towgs84" );
169            if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) {
170                double[] values = null;
171                String[] splitter = tmpValue.trim().split( "," );
172                if ( splitter != null && splitter.length > 0 ) {
173                    values = new double[splitter.length];
174                    for ( int i = 0; i < splitter.length; ++i ) {
175                        values[i] = Double.parseDouble( splitter[i] );
176                    }
177                }
178                if ( values != null ) {
179                    String description = "Handmade proj4 towgs84 definition (parsed from nad/epsg) used by crs with id: "
180                                         + sourceCRS.getIdentifier() + "identifier";
181                    String name = "Proj4 defined toWGS84 params";
182    
183                    String id = Transformation.createFromTo( sourceCRS.getIdentifier(), GeographicCRS.WGS84.getIdentifier() );
184    
185                    if ( values.length == 3 ) {
186                        result = new Helmert( values[0], values[1], values[2], 0, 0, 0, 0, sourceCRS, GeographicCRS.WGS84,
187                                              id, name, sourceCRS.getVersion(), description, sourceCRS.getAreaOfUse() );
188                    } else if ( values.length == 7 ) {
189                        result = new Helmert( values[0], values[1], values[2], values[3], values[4], values[5], values[6],
190                                              sourceCRS, GeographicCRS.WGS84, id, name, sourceCRS.getVersion(),
191                                              description, sourceCRS.getAreaOfUse() );
192                    } else {
193                        throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_WGS84_PARAMS",
194                                                                                  sourceCRS.getIdentifier() + "identifier",
195                                                                                  Integer.toString( values.length ) ) );
196                    }
197                }
198            }
199            return result;
200        }
201    
202        /**
203         * Parses the configured proj4 parameters from the given String using a StreamTokenizer and saves them in the Map.
204         * 
205         * @param params
206         *            to be parsed
207         * @param lineNumber
208         *            in the config file.
209         * @param kvp
210         *            in which the key-value pairs will be saved.
211         * @return the parsed Identifier or <code>null</code> if no identifier was found.
212         * @throws IOException
213         *             if the StreamTokenizer finds an error.
214         * @throws CRSConfigurationException
215         *             If the config was malformed.
216         */
217        private String parseConfigString( String params, String lineNumber, Map<String, String> kvp )
218                                throws IOException, CRSConfigurationException {
219            BufferedReader br = new BufferedReader( new StringReader( params ) );
220            StreamTokenizer t = new StreamTokenizer( br );
221            t.commentChar( '#' );
222            t.ordinaryChars( '0', '9' );
223            // t.ordinaryChars( '.', '.' );
224            t.ordinaryChar( '.' );
225            // t.ordinaryChars( '-', '-' );
226            t.ordinaryChar( '-' );
227            // t.ordinaryChars( '+', '+' );
228            t.ordinaryChar( '+' );
229            t.wordChars( '0', '9' );
230            t.wordChars( '\'', '\'' );
231            t.wordChars( '"', '"' );
232            t.wordChars( '_', '_' );
233            t.wordChars( '.', '.' );
234            t.wordChars( '-', '-' );
235            t.wordChars( '+', '+' );
236            t.wordChars( ',', ',' );
237            t.nextToken();
238            String identifier = null;
239    /**
240             * PROJ4 type definitions have following format, <number> +proj .... So the first type must start with an '<'
241             */
242            if ( t.ttype == '<' ) {
243                t.nextToken();
244                // if the next value is not a word, e.g. a number (see t.wordChars('0','9')) it is the
245                // wrong format.
246                if ( t.ttype != StreamTokenizer.TT_WORD ) {
247                    throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_INVALID_ID", lineNumber,
248                                                                              "An identifier (e.g. number)", "<",
249                                                                              getTokenizerSymbolToString( t.ttype ) ) );
250                }
251                // it's a word so get the identifier.
252                identifier = t.sval;
253                //
254                kvp.put( "identifier", identifier );
255                t.nextToken();
256    
257                // check for closing bracket.
258                if ( t.ttype != '>' ) {
259                    throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_MISSING_EXPECTED_CHAR",
260                                                                              lineNumber, ">" ) );
261                }
262                t.nextToken();
263    
264                // get the parameters.
265                while ( t.ttype != '<' ) {
266                    if ( t.ttype == '+' ) {
267                        t.nextToken();
268                    }
269                    if ( t.ttype != StreamTokenizer.TT_WORD ) {
270                        throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_INVALID_ID",
271                                                                                  lineNumber, "A parameter", "+",
272                                                                                  getTokenizerSymbolToString( t.ttype ) ) );
273                    }
274                    String key = t.sval;
275                    if ( key != null && !"".equals( key ) ) {
276                        if ( key.startsWith( "+" ) ) {
277                            key = key.substring( 1 );
278                        }
279                        t.nextToken();
280                        if ( t.ttype == '=' ) {
281                            t.nextToken();
282                            if ( t.ttype != StreamTokenizer.TT_WORD ) {
283                                throw new CRSConfigurationException(
284                                                                     Messages.getMessage(
285                                                                                          "CRS_CONFIG_PROJ4_INVALID_ID",
286                                                                                          lineNumber,
287                                                                                          "A Value",
288                                                                                          "=",
289                                                                                          getTokenizerSymbolToString( t.ttype ) ) );
290                            }
291                            String value = t.sval;
292                            LOG.logDebug( "Putting key: " + key + " with value: " + value );
293                            kvp.put( key, value );
294                            // take the next token.
295                            t.nextToken();
296                        }
297                    }
298                }
299                t.nextToken();
300                if ( t.ttype != '>' ) {
301                    throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_MISSING_EXPECTED_CHAR",
302                                                                              lineNumber, "<> (End of defintion)" ) );
303                }
304            } else {
305                throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_MISSING_EXPECTED_CHAR",
306                                                                          lineNumber, "< (Start of defintion)" ) );
307            }
308            br.close();
309            return identifier;
310        }
311    
312        /**
313         * Creates a helpfull string of the given StreamTokenizer value.
314         * 
315         * @param val
316         *            an int gotten from streamTokenizer.ttype.
317         * @return a human readable String.
318         */
319        private String getTokenizerSymbolToString( int val ) {
320            String result = new String( "" + val );
321            if ( val == StreamTokenizer.TT_EOF ) {
322                result = "End of file";
323            } else if ( val == StreamTokenizer.TT_EOL ) {
324                result = "End of line";
325            } else if ( val == StreamTokenizer.TT_NUMBER ) {
326                result = "A number with value: " + val;
327            }
328            return result;
329        }
330    
331        /**
332         * removes any strings in front of the last number.
333         * 
334         * @param id
335         *            to be normalized.
336         * @return the number of the id, or id if ':' or '#' is not found.
337         */
338        private String getIDCode( String id ) {
339            if ( id == null || "".equals( id.trim() ) ) {
340                return id;
341            }
342            int count = id.lastIndexOf( ":" );
343            if ( count == -1 ) {
344                count = id.lastIndexOf( "#" );
345                if ( count == -1 ) {
346                    return id;
347                }
348            }
349            return id.substring( count + 1 );
350        }
351    
352        public Transformation getTransformation( CoordinateSystem sourceCRS, CoordinateSystem targetCRS ) {
353            LOG.logError( "Parsing of transformations is currently not supported for the Proj4 file resource." );
354            return null;
355        }
356    
357        public List<Transformation> getTransformations() {
358            LOG.logError( "Parsing of transformations is currently not supported for the Proj4 file resource." );
359            return null;
360        }
361    
362    }