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 }