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 }