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