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 }