001 //$HeadURL: $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 This file is part of deegree. 004 Copyright (C) 2001-2008 by: 005 Department of Geography, University of Bonn 006 http://www.giub.uni-bonn.de/deegree/ 007 lat/lon GmbH 008 http://www.lat-lon.de 009 010 This library is free software; you can redistribute it and/or 011 modify it under the terms of the GNU Lesser General Public 012 License as published by the Free Software Foundation; either 013 version 2.1 of the License, or (at your option) any later version. 014 This library is distributed in the hope that it will be useful, 015 but WITHOUT ANY WARRANTY; without even the implied warranty of 016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 017 Lesser General Public License for more details. 018 You should have received a copy of the GNU Lesser General Public 019 License along with this library; if not, write to the Free Software 020 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 021 Contact: 022 023 Andreas Poth 024 lat/lon GmbH 025 Aennchenstr. 19 026 53177 Bonn 027 Germany 028 E-Mail: poth@lat-lon.de 029 030 Prof. Dr. Klaus Greve 031 Department of Geography 032 University of Bonn 033 Meckenheimer Allee 166 034 53115 Bonn 035 Germany 036 E-Mail: greve@giub.uni-bonn.de 037 ---------------------------------------------------------------------------*/ 038 039 package org.deegree.crs.configuration; 040 041 import java.io.BufferedReader; 042 import java.io.File; 043 import java.io.FileNotFoundException; 044 import java.io.FileReader; 045 import java.io.IOException; 046 import java.io.StreamTokenizer; 047 import java.io.StringReader; 048 import java.util.ArrayList; 049 import java.util.Calendar; 050 import java.util.GregorianCalendar; 051 import java.util.HashMap; 052 import java.util.List; 053 import java.util.Map; 054 import java.util.Set; 055 056 import javax.vecmath.Point2d; 057 058 import org.deegree.crs.components.Axis; 059 import org.deegree.crs.components.Ellipsoid; 060 import org.deegree.crs.components.GeodeticDatum; 061 import org.deegree.crs.components.PrimeMeridian; 062 import org.deegree.crs.components.Unit; 063 import org.deegree.crs.coordinatesystems.CoordinateSystem; 064 import org.deegree.crs.coordinatesystems.GeographicCRS; 065 import org.deegree.crs.coordinatesystems.ProjectedCRS; 066 import org.deegree.crs.exceptions.CRSConfigurationException; 067 import org.deegree.crs.projections.Projection; 068 import org.deegree.crs.projections.azimuthal.LambertAzimuthalEqualArea; 069 import org.deegree.crs.projections.azimuthal.StereographicAzimuthal; 070 import org.deegree.crs.projections.conic.LambertConformalConic; 071 import org.deegree.crs.projections.cylindric.TransverseMercator; 072 import org.deegree.crs.transformations.WGS84ConversionInfo; 073 import org.deegree.framework.log.ILogger; 074 import org.deegree.framework.log.LoggerFactory; 075 import org.deegree.i18n.Messages; 076 077 /** 078 * The <code>PROJ4CRSProvider</code> class is capable of parsing the nad/epsg file and use it as a backend for crs's. 079 * This class also adds following identifiers to the coordinatesystems. 080 * <ul> 081 * <li>http://www.opengis.net/gml/srs/epsg.xml#4326</li> 082 * <li>URN:OPENGIS:DEF:CRS:EPSG::</li> 083 * <li>URN:OGC:DEF:CRS:EPSG::</li> 084 * </ul> 085 * 086 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> 087 * 088 * @author last edited by: $Author:$ 089 * 090 * @version $Revision:$, $Date:$ 091 * 092 */ 093 094 public class PROJ4CRSProvider implements CRSProvider { 095 096 private static ILogger LOG = LoggerFactory.getLogger( PROJ4CRSProvider.class ); 097 098 private static int ellipsCount = 0; 099 100 private static int toWGSCount = 0; 101 102 private static int datumCount = 0; 103 104 private static int projectionCount = 0; 105 106 private static int geographicCRSCount = 0; 107 108 private static int primeMeridianCount = 0; 109 110 private final static String EPSG_PRE = "EPSG:"; 111 112 private final static String OPENGIS_URL = "HTTP://WWW.OPENGIS.NET/GML/SRS/EPSG.XML#"; 113 114 private final static String OPENGIS_URN = "URN:OPENGIS:DEF:CRS:EPSG::"; 115 116 private static final String OGC_URN = "URN:OGC:DEF:CRS:EPSG::"; 117 118 Map<String, Map<String, String>> idToParams = new HashMap<String, Map<String, String>>( 4000 ); 119 120 Map<String, CoordinateSystem> coordinateSystems = new HashMap<String, CoordinateSystem>( 10000 ); 121 122 private String version = null; 123 124 private String[] versions = null; 125 126 private String areaOfUse = "Unknown"; 127 128 private String[] areasOfUse = new String[] { "Unknown" }; 129 130 /** 131 * Export constructor, sets the version to current date.. 132 */ 133 public PROJ4CRSProvider() { 134 // dummy constructor for exporting only. 135 GregorianCalendar cal = (GregorianCalendar) GregorianCalendar.getInstance(); 136 version = cal.get( Calendar.YEAR ) + "-" 137 + ( cal.get( Calendar.MONTH ) + 1 ) 138 + "-" 139 + cal.get( Calendar.DAY_OF_MONTH ) 140 + "T" 141 + cal.get( Calendar.HOUR_OF_DAY ) 142 + ":" 143 + cal.get( Calendar.MINUTE ); 144 versions = new String[] { version }; 145 } 146 147 /** 148 * Opens a reader on the file and parses all parameters with id, without instantiating any CoordinateSystems. 149 * 150 * @param f 151 * the file to open. 152 */ 153 public PROJ4CRSProvider( File f ) { 154 this(); 155 try { 156 BufferedReader reader = new BufferedReader( new FileReader( f ) ); 157 String line = reader.readLine(); 158 Map<String, String> kvp = new HashMap<String, String>( 15 ); 159 int lineNumber = 1; 160 while ( line != null ) { 161 if ( line.startsWith( "#" ) ) { 162 // remove the '#' from the String. 163 if ( kvp.get( "comment" ) != null ) { 164 LOG.logDebug( "(Line: " + lineNumber 165 + ") Multiple comments found, removing previous: " 166 + kvp.get( "comment" ) ); 167 } 168 kvp.put( "comment", line.substring( 1 ).trim() ); 169 } else { 170 String identifier = parseConfigString( line, Integer.toString( lineNumber ), kvp ); 171 if ( identifier != null && !"".equals( identifier.trim() ) ) { 172 LOG.logDebug( "Found identifier: " + identifier + " with following params: " + kvp ); 173 idToParams.put( identifier, kvp ); 174 } 175 kvp = new HashMap<String, String>( 15 ); 176 } 177 line = reader.readLine(); 178 lineNumber++; 179 } 180 reader.close(); 181 182 } catch ( FileNotFoundException e ) { 183 e.printStackTrace(); 184 } catch ( CRSConfigurationException e ) { 185 e.printStackTrace(); 186 } catch ( IOException e ) { 187 LOG.logError( "Could not open file: " + f.getAbsoluteFile(), e ); 188 // e.printStackTrace(); 189 } 190 } 191 192 public List<CoordinateSystem> getAvailableCRSs() 193 throws CRSConfigurationException { 194 Set<String> keys = idToParams.keySet(); 195 LOG.logDebug( "Found following keys: " + keys ); 196 List<CoordinateSystem> allSystems = new ArrayList<CoordinateSystem>( keys.size() ); 197 for ( String key : keys ) { 198 try { 199 CoordinateSystem result = getCRSByID( key ); 200 if ( result != null ) { 201 allSystems.add( result ); 202 } 203 } catch ( CRSConfigurationException e ) { 204 LOG.logInfo( Messages.getMessage( "CRS_CONFIG_PROJ4_NOT_ADDING_CRS", key, e.getMessage() ) ); 205 } 206 } 207 // get all already created crs's 208 keys = coordinateSystems.keySet(); 209 for ( String key : keys ) { 210 CoordinateSystem result = coordinateSystems.get( key ); 211 if ( result != null ) { 212 allSystems.add( result ); 213 } 214 } 215 return allSystems; 216 } 217 218 public boolean canExport() { 219 return false; 220 } 221 222 public void export( StringBuilder sb, List<CoordinateSystem> crsToExport ) { 223 throw new UnsupportedOperationException( "Exporting to PROJ4 configuration is not suppored yet." ); 224 225 } 226 227 public CoordinateSystem getCRSByID( String id ) 228 throws CRSConfigurationException { 229 if ( id != null && !"".equals( id.trim() ) ) { 230 String tmpID = getIDCode( id ); 231 LOG.logDebug( "Given id: " + id + " converted into: " + tmpID ); 232 if ( coordinateSystems.containsKey( tmpID ) && coordinateSystems.get( tmpID ) != null ) { 233 return coordinateSystems.get( tmpID ); 234 } else if ( idToParams.containsKey( tmpID ) && idToParams.get( tmpID ) != null ) { 235 tmpID = tmpID.trim().toUpperCase(); 236 LOG.logDebug( "Trying to create crs for given id: " + tmpID ); 237 Map<String, String> params = idToParams.get( tmpID ); 238 CoordinateSystem result = createCRS( params ); 239 if ( result != null ) { 240 coordinateSystems.put( tmpID, result ); 241 } 242 params.remove( "identifier" ); 243 if ( params.size() != 0 ) { 244 LOG.logInfo( "After creation of the crs with id: " + tmpID 245 + ", following parameters remain unused: " 246 + params ); 247 } 248 idToParams.put( tmpID, null ); 249 return result; 250 } 251 LOG.logDebug( "No crs found with given id: " + id ); 252 } 253 return null; 254 } 255 256 /** 257 * removes any strings in front of the last number. 258 * 259 * @param id 260 * to be normalized. 261 * @return the number of the id, or id if ':' or '#' is not found. 262 */ 263 private String getIDCode( String id ) { 264 if ( id == null || "".equals( id.trim() ) ) { 265 return id; 266 } 267 int count = id.lastIndexOf( ":" ); 268 if ( count == -1 ) { 269 count = id.lastIndexOf( "#" ); 270 if ( count == -1 ) { 271 return id; 272 } 273 } 274 return id.substring( count + 1 ); 275 } 276 277 /** 278 * Return a CoordinateSystem initialized with a PROJ.4 argument list. 279 * 280 * @throws CRSConfigurationException 281 */ 282 private CoordinateSystem createCRS( Map<String, String> params ) 283 throws CRSConfigurationException { 284 String crsType = params.remove( "proj" ); 285 if ( crsType == null || "".equals( crsType.trim() ) ) { 286 LOG.logDebug( "The given params contain: " + params ); 287 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_NO_PROJ_PARAM", 288 params.get( EPSG_PRE + "identifier" ) ) ); 289 } 290 crsType = crsType.trim(); 291 if ( "longlat".equals( crsType ) ) { 292 // the geo-crs should find it's own id and has no parent projected crs (null, null). 293 return createGeographicCRS( null, null, params ); 294 } 295 return createProjectedCRS( crsType, params ); 296 297 } 298 299 /** 300 * Creates a projected crs from given params. Because proj4 doesn't define some axisorder xy is always assumed. 301 * 302 * @param identifier 303 * to give the geographic crs (+e.g. proj=tmerc) or <code>null</code> 304 * @param params 305 * containing datum info. 306 * @return a geographic crs 307 * @throws CRSConfigurationException 308 * if an exception occurs while creating the datum. 309 */ 310 private ProjectedCRS createProjectedCRS( String projectionName, Map<String, String> params ) 311 throws CRSConfigurationException { 312 String[] names = new String[] { params.remove( "comment" ) }; 313 314 String[] descriptions = null; 315 String id = params.get( "identifier" ); 316 String[] ids = getPredefinedIDs( id ); 317 String geoID = "GEO_CRS_" + geographicCRSCount; 318 if ( "3068".equals( id ) || "31466".equals( id ) 319 || "31467".equals( id ) 320 || "31468".equals( id ) 321 || "31469".equals( id ) ) { 322 geoID = EPSG_PRE+"4314"; 323 } else { 324 geographicCRSCount++; 325 } 326 GeographicCRS underLyingCRS = createGeographicCRS( geoID, id, params ); 327 Projection projection = createProjection( projectionName, underLyingCRS, params ); 328 329 return new ProjectedCRS( projection, 330 new Axis[] { new Axis( projection.getUnits(), "x", Axis.AO_EAST ), 331 new Axis( projection.getUnits(), "y", Axis.AO_NORTH ) }, 332 ids, 333 names, 334 versions, 335 descriptions, 336 areasOfUse ); 337 } 338 339 /** 340 * Creates a geographic crs with given identifier, if the identifier is empty or <code>null</code> the 341 * params.getIdentifier will be used. Because proj4 doesn't define some axisorder xy is always assumed. 342 * 343 * @param identifier 344 * to give the geographic crs (+proj=longlat) or <code>null</code> 345 * @param params 346 * containing datum info. 347 * @param projectedID 348 * of the projected crs (only if identifier is not null). 349 * @return a geographic crs 350 * @throws CRSConfigurationException 351 * if an exception occurs while creating the datum. 352 */ 353 private GeographicCRS createGeographicCRS( String identifier, String projectedID, Map<String, String> params ) 354 throws CRSConfigurationException { 355 String name = "Proj4 defined Geographic CRS"; 356 String tmp = params.remove( "comment" ); 357 if ( tmp != null && !"".equals( tmp.trim() ) ) { 358 name = tmp; 359 } 360 String[] names = new String[] { name }; 361 String description = "Handmade proj4 geographic crs definition (parsed from nad/epsg)."; 362 String ids[] = new String[] { identifier }; 363 if ( identifier == null || "".equals( identifier.trim() ) ) { 364 identifier = params.get( "identifier" ); 365 ids = getPredefinedIDs( identifier ); 366 // if the id was not set, we create a geocrs, which means that no projectedID has been set, we want to build 367 // the datum with the id though! 368 projectedID = identifier; 369 } else { 370 description += " Used by projected crs with id: " + projectedID; 371 } 372 String[] descriptions = new String[] { description }; 373 // projectedID will also hold the id of the geo-crs if it is a top level one. 374 GeodeticDatum datum = createDatum( params, projectedID ); 375 GeographicCRS result = new GeographicCRS( datum, 376 new Axis[] { new Axis( Unit.RADIAN, "longitude", Axis.AO_EAST ), 377 new Axis( Unit.RADIAN, "latitude", Axis.AO_NORTH ) }, 378 ids, 379 names, 380 versions, 381 descriptions, 382 areasOfUse ); 383 return result; 384 } 385 386 /** 387 * Create a datum by resolving the 'datum' param or creating it from the given ellipsoid/primemeridan parameters. 388 * 389 * @param params 390 * to create the datum from 391 * @return a datum 392 * @throws CRSConfigurationException 393 * if one of the datums necessities( ellipsoid, primemeridan) could not be created. 394 */ 395 private GeodeticDatum createDatum( Map<String, String> params, String identifier ) 396 throws CRSConfigurationException { 397 398 GeodeticDatum result = null; 399 String tmpValue = params.remove( "datum" ); 400 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 401 // removing the defined ellipsoid. 402 result = getPredefinedDatum( tmpValue, params.remove( "ellps" ), identifier ); 403 if ( result == null ) { 404 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_UNKNOWN_DATUM", 405 params.get( EPSG_PRE + "identifier" ), 406 tmpValue ) ); 407 } 408 } else { 409 Ellipsoid ellipsoid = createEllipsoid( params ); 410 if ( ellipsoid == null ) { 411 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_DATUM_WITHOUT_ELLIPSOID", 412 params.get( EPSG_PRE + "identifier" ) ) ); 413 } 414 WGS84ConversionInfo convInfo = createWGS84ConversionInfo( params ); 415 String id = "DATUM_" + datumCount++; 416 String name = "Proj4 defined datum"; 417 418 String description = "Handmade proj4 datum definition (parsed from nad/epsg) used by crs with id: " + ( EPSG_PRE + identifier ); 419 PrimeMeridian pm = createPrimeMeridian( params ); 420 result = new GeodeticDatum( ellipsoid, pm, convInfo, id, name, version, description, areaOfUse ); 421 } 422 return result; 423 } 424 425 /** 426 * Creating the wgs84 aka BursaWolf conversion parameters. Either 3 or 7 parameters are supported. 427 * 428 * @param params 429 * to get the towgs84 param from 430 * @return the conversion info from the params or the 0 conversion info 431 * @throws CRSConfigurationException 432 * if the number of params are not 3 or 7. 433 */ 434 private WGS84ConversionInfo createWGS84ConversionInfo( Map<String, String> params ) 435 throws CRSConfigurationException { 436 WGS84ConversionInfo result = new WGS84ConversionInfo( "PROJ4_NO_CONVERSION_INFO" ); 437 String tmpValue = params.remove( "towgs84" ); 438 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 439 double[] values = null; 440 String[] splitter = tmpValue.trim().split( "," ); 441 if ( splitter != null && splitter.length > 0 ) { 442 values = new double[splitter.length]; 443 for ( int i = 0; i < splitter.length; ++i ) { 444 values[i] = Double.parseDouble( splitter[i] ); 445 } 446 } 447 if ( values != null ) { 448 String id = "towgs_" + toWGSCount++; 449 String description = "Handmade proj4 towgs84 definition (parsed from nad/epsg) used by crs with id: " + params.get( EPSG_PRE + "identifier" ); 450 String name = "Proj4 defined toWGS84 params"; 451 452 if ( values.length == 3 ) { 453 result = new WGS84ConversionInfo( values[0], 454 values[1], 455 values[2], 456 0, 457 0, 458 0, 459 0, 460 id, 461 name, 462 version, 463 description, 464 areaOfUse ); 465 } else if ( values.length == 7 ) { 466 result = new WGS84ConversionInfo( values[0], 467 values[1], 468 values[2], 469 values[3], 470 values[4], 471 values[5], 472 values[6], 473 id, 474 name, 475 version, 476 description, 477 areaOfUse ); 478 } else { 479 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_WGS84_PARAMS", 480 params.get( EPSG_PRE + "identifier" ), 481 Integer.toString( values.length ) ) ); 482 } 483 } 484 } 485 return result; 486 } 487 488 /** 489 * Create a prime meridian according to predefined proj4 definitions. 490 * 491 * @param params 492 * to get the 'pm' parameter from. 493 * @return a mapped primemeridian or the Greenwich if no pm parameter was found. 494 * @throws CRSConfigurationException 495 * if the pm-parameter could not be mapped. 496 */ 497 private PrimeMeridian createPrimeMeridian( Map<String, String> params ) 498 throws CRSConfigurationException { 499 String tmpValue = params.remove( "pm" ); 500 PrimeMeridian result = PrimeMeridian.GREENWICH; 501 String id = "pm_" + primeMeridianCount++; 502 String[] names = null; 503 String[] versions = null; 504 String[] descs = null; 505 String[] aous = null; 506 double longitude = Double.NaN; 507 if ( tmpValue != null && !"".equals( tmpValue.trim() ) && !"greenwich".equals( tmpValue.trim() ) ) { 508 if ( "athens".equals( tmpValue ) ) { 509 longitude = parseAngleFormat( "23d42'58.815\"E", false ); 510 id = "8912"; 511 names = new String[] { "Athens" }; 512 versions = new String[] { "1995-06-02" }; 513 descs = new String[] { "Used in Greece for older mapping based on Hatt projection." }; 514 } else if ( "bern".equals( tmpValue ) ) { 515 longitude = parseAngleFormat( "7d26'22.5\"E", false ); 516 id = "8907"; 517 names = new String[] { "Bern" }; 518 versions = new String[] { "1995-06-02" }; 519 descs = new String[] { "1895 value. Newer value of 7 deg 26 min 22.335 sec E determined in 1938." }; 520 } else if ( "bogota".equals( tmpValue ) ) { 521 longitude = parseAngleFormat( "74d04'51.3\"W", false ); 522 id = "8904"; 523 names = new String[] { "Bogota" }; 524 versions = new String[] { "1995-06-02" }; 525 descs = new String[] { "Instituto Geografico 'Augustin Cadazzi' (IGAC); Bogota" }; 526 } else if ( "brussels".equals( tmpValue ) ) { 527 longitude = parseAngleFormat( "4d22'4.71\"E", false ); 528 id = "8910"; 529 names = new String[] { "Brussel" }; 530 versions = new String[] { "1995-06-02" }; 531 } else if ( "ferro".equals( tmpValue ) ) { 532 longitude = parseAngleFormat( "17d40'W", false ); 533 id = "8909"; 534 names = new String[] { "Ferro" }; 535 versions = new String[] { "1995-06-02" }; 536 descs = new String[] { "Used in Austria and former Czechoslovakia. " }; 537 } else if ( "jakarta".equals( tmpValue ) ) { 538 longitude = parseAngleFormat( "106d48'27.79\"E", false ); 539 id = "8908"; 540 names = new String[] { "Jakarta" }; 541 versions = new String[] { "1995-06-02" }; 542 } else if ( "lisbon".equals( tmpValue ) ) { 543 longitude = parseAngleFormat( "9d07'54.862\"W", false ); 544 id = "8902"; 545 names = new String[] { "lisbon" }; 546 versions = new String[] { "1995-06-02" }; 547 descs = new String[] { "Information Source: Instituto Geografico e Cadastral; Lisbon " }; 548 } else if ( "madrid".equals( tmpValue ) ) { 549 longitude = parseAngleFormat( "3d41'16.58\"W", false ); 550 id = "8905"; 551 names = new String[] { "Madrid" }; 552 versions = new String[] { "1995-06-02" }; 553 descs = new String[] { "Value adopted by IGN (Paris) in 1936. Equivalent to 2 deg 20min 14.025sec. Preferred by EPSG to earlier value of 2deg 20min 13.95sec (2.596898 grads) used by RGS London." }; 554 } else if ( "oslo".equals( tmpValue ) ) { 555 longitude = parseAngleFormat( "10d43'22.5\"E", false ); 556 id = "8913"; 557 names = new String[] { "Oslo" }; 558 versions = new String[] { "1995-06-02" }; 559 descs = new String[] { "ormerly known as Kristiania or Christiania." }; 560 } else if ( "paris".equals( tmpValue ) ) { 561 longitude = parseAngleFormat( "2d20'14.025\"E", false ); 562 id = "8903"; 563 names = new String[] { "Paris" }; 564 versions = new String[] { "1995-06-02" }; 565 descs = new String[] { "Value adopted by IGN (Paris) in 1936. Equivalent to 2 deg 20min 14.025sec. Preferred by EPSG to earlier value of 2deg 20min 13.95sec (2.596898 grads) used by RGS London." }; 566 } else if ( "rome".equals( tmpValue ) ) { 567 longitude = parseAngleFormat( "12d27'8.4\"E", false ); 568 id = "8906"; 569 names = new String[] { "Rome" }; 570 versions = new String[] { "1995-06-02" }; 571 } else if ( "stockholm".equals( tmpValue ) ) { 572 longitude = parseAngleFormat( "18d3'29.8\"E", false ); 573 id = "8911"; 574 names = new String[] { "Stockholm" }; 575 versions = new String[] { "1995-06-02" }; 576 } else { 577 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_UNKNOWN_PM", 578 params.get( EPSG_PRE + "identifier" ), 579 tmpValue ) ); 580 } 581 } 582 if ( !Double.isNaN( longitude ) ) { 583 String[] ids = new String[] { id }; 584 if ( !id.startsWith( "pm_" ) ) { 585 ids = getPredefinedIDs( id ); 586 } 587 result = new PrimeMeridian( Unit.RADIAN, longitude, ids, names, versions, descs, aous ); 588 } 589 return result; 590 } 591 592 /** 593 * Tries to create an ellips from a predefined mapping, or from axis, eccentricities etc. if defined. 594 * 595 * @param params 596 * to create the ellipsoid from 597 * @return an (mapped) ellipsoid. 598 * @throws CRSConfigurationException 599 * if no mapping was found or the semimajor axis was not defined. 600 */ 601 private Ellipsoid createEllipsoid( Map<String, String> params ) 602 throws CRSConfigurationException { 603 604 Ellipsoid result = null; 605 double semiMajorAxis = Double.NaN; 606 double eccentricitySquared = Double.NaN; 607 double eccentricity = Double.NaN; 608 double inverseFlattening = Double.NaN; 609 double semiMinorAxis = Double.NaN; 610 611 // Get the ellipsoid 612 String tmpValue = params.remove( "ellps" ); 613 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 614 LOG.logDebug( "Creating predefined ellipsoid: " + tmpValue ); 615 result = getPredefinedEllipsoid( tmpValue ); 616 } else { 617 // if no ellipsoid is defined maybe a sphere 618 tmpValue = params.remove( "R" ); 619 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 620 LOG.logDebug( "Found a Radius instead of an ellipsoid, the projection uses a sphere!" ); 621 semiMajorAxis = Double.parseDouble( tmpValue ); 622 } else {// an ellipsoid instead of a sphere. 623 tmpValue = params.remove( "a" ); 624 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 625 semiMajorAxis = Double.parseDouble( tmpValue ); 626 } 627 tmpValue = params.remove( "es" ); 628 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 629 eccentricitySquared = Double.parseDouble( tmpValue ); 630 } else { 631 tmpValue = params.remove( "e" ); 632 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 633 eccentricity = Double.parseDouble( tmpValue ); 634 } else { 635 tmpValue = params.remove( "rf" ); 636 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 637 inverseFlattening = Double.parseDouble( tmpValue ); 638 } else { 639 tmpValue = params.remove( "f" ); 640 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 641 double flattening = Double.parseDouble( tmpValue ); 642 if ( Math.abs( flattening ) > 0.000001 ) { 643 inverseFlattening = 1 / flattening; 644 } else { 645 LOG.logDebug( "The given flattening: " + flattening 646 + " can not be inverted (divide by zero) using a sphere as ellipsoid" ); 647 } 648 } else { 649 tmpValue = params.remove( "b" ); 650 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 651 semiMinorAxis = Double.parseDouble( tmpValue ); 652 } 653 } 654 } 655 } 656 } 657 } 658 659 /** 660 * These parameters (R_A, R_V, R_a, R_g, R_h, R_lat_a, R_lat_g) were not used in the epsg file, maybe they 661 * are of future value?. <code> 662 * private final static double SIXTH = .1666666666666666667; // 1/6 663 * private final static double RA4 = .04722222222222222222; // 17/360 664 * private final static double RA6 = .02215608465608465608; // 67/3024 665 * private final static double RV4 = .06944444444444444444; // 5/72 666 * private final static double RV6 = .04243827160493827160; // 55/1296 667 * </code> 668 */ 669 // String tmpValue = params.get( "R_A" ); 670 // if ( tmpValue != null && Boolean.getBoolean( tmpValue ) ) { 671 // semiMajorAxis *= 1. - eccentricitySquared 672 // * ( SIXTH + eccentricitySquared * ( RA4 + eccentricitySquared * RA6 ) ); 673 // } else { 674 // tmpValue = params.get( "R_V" ); 675 // if ( tmpValue != null && Boolean.getBoolean( tmpValue ) ) { 676 // semiMajorAxis *= 1. - eccentricitySquared 677 // * ( SIXTH + eccentricitySquared * ( RV4 + eccentricitySquared * RV6 ) ); 678 // } else { 679 // tmpValue = params.get( "R_a" ); 680 // if ( tmpValue != null && Boolean.getBoolean( tmpValue ) ) { 681 // semiMajorAxis = .5 * ( semiMajorAxis + semiMinorAxis ); 682 // } else { 683 // tmpValue = params.get( "R_g" ); 684 // if ( tmpValue != null && Boolean.getBoolean( tmpValue ) ) { 685 // semiMajorAxis = Math.sqrt( semiMajorAxis * semiMinorAxis ); 686 // } else { 687 // tmpValue = params.get( "R_h" ); 688 // if ( tmpValue != null && Boolean.getBoolean( tmpValue ) ) { 689 // semiMajorAxis = 2. * semiMajorAxis * semiMinorAxis / ( semiMajorAxis + semiMinorAxis ); 690 // eccentricitySquared = 0.; 691 // } else { 692 // tmpValue = params.get( "R_lat_a" ); 693 // if ( tmpValue != null ) { 694 // double tmp = Math.sin( parseAngle( tmpValue ) ); 695 // if ( Math.abs( tmp ) > ProjectionUtils.HALFPI ) 696 // throw new CRSConfigurationException( "-11" ); 697 // tmp = 1. - eccentricitySquared * tmp * tmp; 698 // semiMajorAxis *= .5 * ( 1. - eccentricitySquared + tmp ) / ( tmp * Math.sqrt( tmp ) ); 699 // eccentricitySquared = 0.; 700 // } else { 701 // tmpValue = params.get( "R_lat_g" ); 702 // if ( tmpValue != null ) { 703 // double tmp = Math.sin( parseAngle( tmpValue ) ); 704 // if ( Math.abs( tmp ) > ProjectionUtils.HALFPI ) 705 // throw new CRSConfigurationException( "-11" ); 706 // tmp = 1. - eccentricitySquared * tmp * tmp; 707 // semiMajorAxis *= Math.sqrt( 1. - eccentricitySquared ) / tmp; 708 // eccentricitySquared = 0.; 709 // } 710 // } 711 // } 712 // } 713 // } 714 // } 715 // } 716 if ( Double.isNaN( semiMajorAxis ) ) { 717 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_ELLIPSOID_WITHOUT_SEMIMAJOR", 718 params.get( EPSG_PRE + "identifier" ) ) ); 719 } 720 String id = "ELLIPSOID_" + ellipsCount++; 721 String description = "Handmade proj4 ellipsoid definition (parsed from nad/epsg) used by crs with id: " + params.get( "identifier" ); 722 String name = "Proj4 defined ellipsoid"; 723 724 if ( !Double.isNaN( eccentricitySquared ) ) { 725 result = new Ellipsoid( semiMajorAxis, 726 Math.sqrt( eccentricitySquared ), 727 Unit.METRE, 728 id, 729 name, 730 version, 731 description, 732 areaOfUse ); 733 } else if ( !Double.isNaN( eccentricity ) ) { 734 result = new Ellipsoid( semiMajorAxis, 735 eccentricity, 736 Unit.METRE, 737 id, 738 name, 739 version, 740 description, 741 areaOfUse ); 742 } else if ( !Double.isNaN( inverseFlattening ) ) { 743 result = new Ellipsoid( semiMajorAxis, 744 Unit.METRE, 745 inverseFlattening, 746 id, 747 name, 748 version, 749 description, 750 areaOfUse ); 751 } else if ( !Double.isNaN( semiMinorAxis ) ) { 752 result = new Ellipsoid( Unit.METRE, 753 semiMajorAxis, 754 semiMinorAxis, 755 id, 756 name, 757 version, 758 description, 759 areaOfUse ); 760 } else { 761 LOG.logDebug( "Only a semimajor defined, assuming a sphere (instead of an ellipsoid) is to be created." ); 762 result = new Ellipsoid( Unit.METRE, 763 semiMajorAxis, 764 semiMajorAxis, 765 id, 766 name, 767 version, 768 description, 769 areaOfUse ); 770 } 771 } 772 return result; 773 } 774 775 /** 776 * @param datumName 777 * of the datum to map. 778 * @param definedEllipsoid 779 * the ellipsoid to use (gotten from 'ellps' param) or <code>null</code> if a predefined ellipsoid 780 * should be used. 781 * @return a Geodetic datum or <code>null</code> if the given name was null or empty. 782 * @throws CRSConfigurationException 783 */ 784 private GeodeticDatum getPredefinedDatum( String datumName, String definedEllipsoid, String crsID ) 785 throws CRSConfigurationException { 786 if ( datumName != null && !"".equals( datumName.trim() ) ) { 787 datumName = datumName.trim(); 788 String[] datumIDs = null; 789 String[] datumNames = null; 790 String[] datumDescriptions = null; 791 String[] datumVersions = null; 792 String[] datumAOU = null; 793 WGS84ConversionInfo confInfo = new WGS84ConversionInfo( "Created by proj4 CRSProvider" ); 794 Ellipsoid ellipsoid = null; 795 if ( "GGRS87".equalsIgnoreCase( datumName ) ) { 796 confInfo = new WGS84ConversionInfo( -199.87, 797 74.79, 798 246.62, 799 0, 800 0, 801 0, 802 0, 803 getPredefinedIDs("1272" ), 804 new String[] {}, 805 null, 806 null, 807 null ); 808 809 datumIDs = getPredefinedIDs("6121" ); 810 datumNames = new String[] { "Greek_Geodetic_Reference_System_1987" }; 811 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 812 || "GRS80".equals( definedEllipsoid.trim() ) ) { 813 ellipsoid = getPredefinedEllipsoid( "GRS80" ); 814 } else { 815 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 816 } 817 } else if ( "NAD27".equalsIgnoreCase( datumName ) ) { 818 confInfo = new WGS84ConversionInfo( -8, 819 160, 820 176, 821 0, 822 0, 823 0, 824 0, 825 getPredefinedIDs("1173" ), 826 new String[] { "North_American_Datum_1983" }, 827 null, 828 null, 829 null ); 830 831 datumIDs = getPredefinedIDs("6267"); 832 datumNames = new String[] { "North_American_Datum_1927" }; 833 // don't no how to do this. 834 // "nadgrids=@conus,@alaska,@ntv2_0.gsb,@ntv1_can.dat"; 835 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 836 || "clrk66".equals( definedEllipsoid.trim() ) ) { 837 ellipsoid = getPredefinedEllipsoid( "clrk66" ); 838 } else { 839 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 840 } 841 } else if ( "NAD83".equalsIgnoreCase( datumName ) ) { 842 confInfo = new WGS84ConversionInfo( getPredefinedIDs("1188" ), 843 null, 844 null, 845 new String[] { "Derived at 312 stations." }, 846 new String[] { "North America - all Canada and USA subunits" } ); 847 datumIDs = getPredefinedIDs("6269" ); 848 datumNames = new String[] { "North_American_Datum_1983" }; 849 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 850 || "GRS80".equals( definedEllipsoid.trim() ) ) { 851 ellipsoid = getPredefinedEllipsoid( "GRS80" ); 852 } else { 853 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 854 } 855 } else if ( "OSGB36".equalsIgnoreCase( datumName ) ) { 856 confInfo = new WGS84ConversionInfo( 446.448, 857 -125.157, 858 542.060, 859 0.1502, 860 0.2470, 861 0.8421, 862 -20.4894, 863 getPredefinedIDs( "1314" ), 864 null, 865 null, 866 new String[] { "For a more accurate transformation see OSGB 1936 / British National Grid to ETRS89 (2) (code 1039): contact the Ordnance Survey of Great Britain (http://www.gps.gov.uk/gpssurveying.asp) for details." }, 867 new String[] { "United Kingdom (UK) - Great Britain and UKCS" } ); 868 datumIDs = getPredefinedIDs("6001"); 869 datumNames = new String[] { "Airy 1830" }; 870 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 871 || "airy".equals( definedEllipsoid.trim() ) ) { 872 ellipsoid = getPredefinedEllipsoid( "airy" ); 873 } else { 874 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 875 } 876 } else if ( "WGS84".equalsIgnoreCase( datumName ) ) { 877 return GeodeticDatum.WGS84; 878 } else if ( "carthage".equalsIgnoreCase( datumName ) ) { 879 confInfo = new WGS84ConversionInfo( -263.0, 880 6.0, 881 431.0, 882 0, 883 0, 884 0, 885 0, 886 getPredefinedIDs("1130" ), 887 null, 888 null, 889 new String[] { "Derived at 5 stations." }, 890 new String[] { "Tunisia" } ); 891 datumIDs = getPredefinedIDs("6816"); 892 datumNames = new String[] { "Carthage 1934 Tunisia" }; 893 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 894 || "clark80".equals( definedEllipsoid.trim() ) ) { 895 ellipsoid = getPredefinedEllipsoid( "clark80" ); 896 } else { 897 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 898 } 899 } else if ( "hermannskogel".equalsIgnoreCase( datumName ) ) { 900 confInfo = new WGS84ConversionInfo( 653.0, 901 -212.0, 902 449.0, 903 0, 904 0, 905 0, 906 0, 907 new String[] { "kogel", EPSG_PRE + "1306" }, 908 null, 909 null, 910 new String[] { "No epsg code was found." }, 911 null ); 912 datumIDs = new String[] { "Hermannskogel" }; 913 datumNames = new String[] { "some undefined proj4 datum" }; 914 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 915 || "bessel".equals( definedEllipsoid.trim() ) ) { 916 ellipsoid = getPredefinedEllipsoid( "bessel" ); 917 } else { 918 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 919 } 920 } else if ( "ire65".equalsIgnoreCase( datumName ) ) { 921 confInfo = new WGS84ConversionInfo( 482.530, 922 -130.596, 923 564.557, 924 -1.042, 925 -0.214, 926 -0.631, 927 8.15, 928 new String[] { "ire65_conversion" }, 929 null, 930 null, 931 new String[] { "no epsg code was found" }, 932 null ); 933 datumIDs = new String[] { "Ireland 1965" }; 934 datumNames = new String[] { "no epsg code was found." }; 935 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 936 || "mod_airy".equals( definedEllipsoid.trim() ) ) { 937 ellipsoid = getPredefinedEllipsoid( "mod_airy" ); 938 } else { 939 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 940 } 941 } else if ( "nzgd49".equalsIgnoreCase( datumName ) ) { 942 confInfo = new WGS84ConversionInfo( 59.47, 943 -5.04, 944 187.44, 945 0.47, 946 -0.1, 947 1.024, 948 -4.5993, 949 getPredefinedIDs( "1564" ), 950 new String[] { "NZGD49 to WGS 84 (2)" }, 951 new String[] { "OSG-Nzl 4m" }, 952 new String[] { "hese parameter values are taken from NZGD49 to NZGD2000 (4) (code 1701) and assume that NZGD2000 and WGS 84 are coincident to within the accuracy of the transformation. For improved accuracy use NZGD49 to WGS 84 (4) (code 1670)." }, 953 new String[] { "New Zealand" } ); 954 datumIDs = getPredefinedIDs( "6272" ); 955 datumNames = new String[] { "New Zealand Geodetic Datum 1949" }; 956 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 957 || "intl".equals( definedEllipsoid.trim() ) ) { 958 ellipsoid = getPredefinedEllipsoid( "intl" ); 959 } else { 960 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 961 } 962 } else if ( "potsdam".equalsIgnoreCase( datumName ) ) { 963 if ( ( crsID != null && !"".equals( crsID ) ) && "3068".equals( crsID ) 964 || "4314".equals( crsID ) 965 || "31466".equals( crsID ) 966 || "31467".equals( crsID ) 967 || "31468".equals( crsID ) 968 || "31469".equals( crsID ) ) { 969 confInfo = new WGS84ConversionInfo( 598.1, 970 73.7, 971 418.2, 972 0.202, 973 0.045, 974 -2.455, 975 6.7, 976 getPredefinedIDs( "1777" ), 977 new String[] { "DHDN to WGS 84" }, 978 new String[] { "EPSG-Deu W 3m" }, 979 new String[] { "Parameter values from DHDN to ETRS89 (2) (code 1776) assuming that ETRS89 is equivalent to WGS 84 within the accuracy of the transformation. Replaces DHDN to WGS 84 (1) (tfm code 1673)." }, 980 new String[] { "Germany - states of former West Germany - Baden-Wurtemberg, Bayern, Hessen, Niedersachsen, Nordrhein-Westfalen, Rheinland-Pfalz, Saarland, Schleswig-Holstein." } ); 981 datumIDs = getPredefinedIDs( "6314" ); 982 datumNames = new String[] { "Deutsches Hauptdreiecksnetz" }; 983 datumVersions = new String[] { "2006-06-12" }; 984 datumDescriptions = new String[] { "Fundamental point: Rauenberg. Latitude: 52 deg 27 min 12.021 sec N; Longitude: 13 deg 22 min 04.928 sec E (of Greenwich). This station was destroyed in 1910 and the station at Potsdam substituted as the fundamental point." }; 985 datumAOU = new String[] { "Germany - states of former West Germany - Baden-Wurtemberg, Bayern, Hessen, Niedersachsen, Nordrhein-Westfalen, Rheinland-Pfalz, Saarland, Schleswig-Holstein." }; 986 } else { 987 confInfo = new WGS84ConversionInfo( 606.0, 988 23.0, 989 413.0, 990 0, 991 0, 992 0, 993 0, 994 getPredefinedIDs( "15955" ), 995 new String[] { "RD/83 to WGS 84 (1)" }, 996 new String[] { "OGP-Deu BeTA2007" }, 997 new String[] { "These parameter values are taken from DHDN to ETRS89 (8) (code 15948) as RD/83 and ETRS89 may be considered equivalent to DHDN and WGS 84 respectively within the accuracy of the transformation." }, 998 new String[] { "Germany-Sachsen" } ); 999 datumIDs = getPredefinedIDs( "6746" ); 1000 datumNames = new String[] { "Potsdam Rauenberg 1950 DHDN" }; 1001 } 1002 1003 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 1004 || "bessel".equals( definedEllipsoid.trim() ) ) { 1005 ellipsoid = getPredefinedEllipsoid( "bessel" ); 1006 } else { 1007 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 1008 } 1009 } else { 1010 return null; 1011 } 1012 1013 return new GeodeticDatum( ellipsoid, 1014 PrimeMeridian.GREENWICH, 1015 confInfo, 1016 datumIDs, 1017 datumNames, 1018 datumVersions, 1019 datumDescriptions, 1020 datumAOU ); 1021 } 1022 return null; 1023 } 1024 1025 private String[] getPredefinedIDs( String idNumber ) { 1026 return new String[] { EPSG_PRE + idNumber, OGC_URN + idNumber, OPENGIS_URL + idNumber, OPENGIS_URN + idNumber }; 1027 } 1028 1029 /** 1030 * This method was adopted from the com.jhlabs.map.proj.Projection Factory#initizalize method. 1031 * 1032 * @param projName 1033 * name of the projection 1034 * @return the deegree understandable String or <code>null</code> if the projName could not be mapped. 1035 * @throws CRSConfigurationException 1036 * if the projName could not be mapped. 1037 */ 1038 private Projection createProjection( String projName, GeographicCRS geoCRS, Map<String, String> params ) 1039 throws CRSConfigurationException { 1040 Projection result = null; 1041 // in degrees 1042 double projectionLatitude = 0; 1043 double projectionLongitude = 0; 1044 double firstParallelLatitude = Double.NaN; 1045 double secondParallelLatitude = Double.NaN; 1046 double trueScaleLatitude = Double.NaN; 1047 1048 // meter 1049 double falseNorthing = 0; 1050 double falseEasting = 0; 1051 double scale = 1; 1052 1053 String s = params.remove( "lat_0" ); 1054 if ( s != null && !"".equals( s.trim() ) ) { 1055 projectionLatitude = parseAngleFormat( s, false ); 1056 } 1057 1058 s = params.remove( "lon_0" ); 1059 if ( s != null && !"".equals( s.trim() ) ) { 1060 projectionLongitude = parseAngleFormat( s, false ); 1061 } 1062 Point2d naturalOrigin = new Point2d( projectionLongitude, projectionLatitude ); 1063 1064 s = params.remove( "lat_1" ); 1065 if ( s != null && !"".equals( s.trim() ) ) { 1066 firstParallelLatitude = parseAngleFormat( s, false ); 1067 } 1068 1069 s = params.remove( "lat_2" ); 1070 if ( s != null && !"".equals( s.trim() ) ) { 1071 secondParallelLatitude = parseAngleFormat( s, false ); 1072 } 1073 1074 s = params.remove( "lat_ts" ); 1075 if ( s != null && !"".equals( s.trim() ) ){ 1076 trueScaleLatitude = parseAngleFormat( s, false ); 1077 } 1078 1079 s = params.remove( "x_0" ); 1080 if ( s != null && !"".equals( s.trim() ) ){ 1081 falseEasting = Double.parseDouble( s ); 1082 } 1083 s = params.remove( "y_0" ); 1084 if ( s != null && !"".equals( s.trim() ) ) { 1085 falseNorthing = Double.parseDouble( s ); 1086 } 1087 1088 s = params.remove( "k_0" ); 1089 if ( s == null ) { 1090 s = params.remove( "k" ); 1091 } 1092 if ( s != null && !"".equals( s.trim() ) ) { 1093 scale = Double.parseDouble( s ); 1094 } 1095 1096 Unit units = createUnit( params ); 1097 1098 String id = "PROJECTION_" + projectionCount++; 1099 1100 String description = "Handmade proj4 projection definition (parsed from nad/epsg) used by crs with id: " + params.get( EPSG_PRE + "identifier" ); 1101 1102 if ( projName != null && !"".equals( projName ) ) { 1103 projName = projName.trim(); 1104 if ( "aea".equals( projName ) ) {// "Albers Equal Area" 1105 } else if ( "aeqd".equals( projName ) ) {// "Azimuthal Equidistant" 1106 } else if ( "airy".equals( projName ) ) {// "Airy" 1107 } else if ( "aitoff".equals( projName ) ) {// "Aitoff" 1108 } else if ( "alsk".equals( projName ) ) {// "Mod. Stereographics of Alaska" 1109 } else if ( "apian".equals( projName ) ) {// "Apian Globular I" 1110 } else if ( "august".equals( projName ) ) {// "August Epicycloidal" 1111 } else if ( "bacon".equals( projName ) ) {// "Bacon Globular" 1112 } else if ( "bipc".equals( projName ) ) {// "Bipolar conic of western hemisphere" 1113 } else if ( "boggs".equals( projName ) ) {// "Boggs Eumorphic" 1114 } else if ( "bonne".equals( projName ) ) {// "Bonne (Werner lat_1=90)" 1115 } else if ( "cass".equals( projName ) ) {// "Cassini" 1116 } else if ( "cc".equals( projName ) ) {// "Central Cylindrical" 1117 } else if ( "cea".equals( projName ) ) {// "Equal Area Cylindrical" 1118 } else if ( "chamb".equals( projName ) ) {// "Chamberlin Trimetric" 1119 } else if ( "collg".equals( projName ) ) {// "Collignon" 1120 } else if ( "crast".equals( projName ) ) {// "Craster Parabolic (Putnins P4)" 1121 } else if ( "denoy".equals( projName ) ) {// "Denoyer Semi-Elliptical" 1122 } else if ( "eck1".equals( projName ) ) {// "Eckert I" 1123 } else if ( "eck2".equals( projName ) ) {// "Eckert II" 1124 } else if ( "eck3".equals( projName ) ) {// "Eckert III" 1125 } else if ( "eck4".equals( projName ) ) {// "Eckert IV" 1126 } else if ( "eck5".equals( projName ) ) {// "Eckert V" 1127 } else if ( "eck6".equals( projName ) ) {// "Eckert VI" 1128 } else if ( "eqc".equals( projName ) ) {// "Equidistant Cylindrical (Plate Caree)" 1129 } else if ( "eqdc".equals( projName ) ) {// "Equidistant Conic" 1130 } else if ( "euler".equals( projName ) ) {// "Euler" 1131 } else if ( "fahey".equals( projName ) ) {// "Fahey" 1132 } else if ( "fouc".equals( projName ) ) {// "Foucaut" 1133 } else if ( "fouc_s".equals( projName ) ) {// "Foucaut Sinusoidal" 1134 } else if ( "gall".equals( projName ) ) {// "Gall (Gall Stereographic)" 1135 } else if ( "gins8".equals( projName ) ) {// "Ginsburg VIII (TsNIIGAiK)" 1136 } else if ( "gn_sinu".equals( projName ) ) {// "General Sinusoidal Series" 1137 } else if ( "gnom".equals( projName ) ) {// "Gnomonic" 1138 } else if ( "goode".equals( projName ) ) {// "Goode Homolosine" 1139 } else if ( "gs48".equals( projName ) ) {// "Mod. Stererographics of 48 U.S." 1140 } else if ( "gs50".equals( projName ) ) {// "Mod. Stererographics of 50 U.S." 1141 } else if ( "hammer".equals( projName ) ) {// "Hammer & Eckert-Greifendorff" 1142 } else if ( "hatano".equals( projName ) ) {// "Hatano Asymmetrical Equal Area" 1143 } else if ( "imw_p".equals( projName ) ) {// "Internation Map of the World Polyconic" 1144 } else if ( "kav5".equals( projName ) ) {// "Kavraisky V" 1145 } else if ( "kav7".equals( projName ) ) {// "Kavraisky VII" 1146 } else if ( "labrd".equals( projName ) ) {// "Laborde" 1147 } else if ( "laea".equals( projName ) ) {// "Lambert Azimuthal Equal Area" 1148 result = new LambertAzimuthalEqualArea( geoCRS, 1149 falseNorthing, 1150 falseEasting, 1151 naturalOrigin, 1152 units, 1153 scale, 1154 id, 1155 "Lambert Azimuthal Equal Area Projection", 1156 version, 1157 description, 1158 areaOfUse ); 1159 } else if ( "lagrng".equals( projName ) ) {// "Lagrange" 1160 } else if ( "larr".equals( projName ) ) {// "Larrivee" 1161 } else if ( "lask".equals( projName ) ) {// "Laskowski" 1162 } else if ( "latlong".equals( projName ) ) {// "Lat/Long" 1163 } else if ( "lcc".equals( projName ) ) {// "Lambert Conformal Conic" 1164 result = new LambertConformalConic( firstParallelLatitude, 1165 secondParallelLatitude, 1166 geoCRS, 1167 falseNorthing, 1168 falseEasting, 1169 naturalOrigin, 1170 units, 1171 scale, 1172 id, 1173 "Lamber Conformal Conic Projection", 1174 version, 1175 description, 1176 areaOfUse ); 1177 } else if ( "leac".equals( projName ) ) {// "Lambert Equal Area Conic" 1178 } else if ( "lee_os".equals( projName ) ) {// "Lee Oblated Stereographic" 1179 } else if ( "loxim".equals( projName ) ) {// "Loximuthal" 1180 } else if ( "lsat".equals( projName ) ) {// "Space oblique for LANDSAT" 1181 } else if ( "mbt_s".equals( projName ) ) {// "McBryde-Thomas Flat-Polar Sine" 1182 } else if ( "mbt_fps".equals( projName ) ) {// "McBryde-Thomas Flat-Pole Sine (No. 2)" 1183 } else if ( "mbtfpp".equals( projName ) ) {// "McBride-Thomas Flat-Polar Parabolic" 1184 } else if ( "mbtfpq".equals( projName ) ) {// "McBryde-Thomas Flat-Polar Quartic" 1185 } else if ( "mbtfps".equals( projName ) ) {// "McBryde-Thomas Flat-Polar Sinusoidal" 1186 } else if ( "merc".equals( projName ) ) {// "Mercator" 1187 } else if ( "mil_os".equals( projName ) ) {// "Miller Oblated Stereographic" 1188 } else if ( "mill".equals( projName ) ) {// "Miller Cylindrical" 1189 } else if ( "mpoly".equals( projName ) ) {// "Modified Polyconic" 1190 } else if ( "moll".equals( projName ) ) {// "Mollweide" 1191 } else if ( "murd1".equals( projName ) ) {// "Murdoch I" 1192 } else if ( "murd2".equals( projName ) ) {// "Murdoch II" 1193 } else if ( "murd3".equals( projName ) ) {// "Murdoch III" 1194 } else if ( "nell".equals( projName ) ) {// "Nell" 1195 } else if ( "nell_h".equals( projName ) ) {// "Nell-Hammer" 1196 } else if ( "nicol".equals( projName ) ) {// "Nicolosi Globular" 1197 } else if ( "nsper".equals( projName ) ) {// "Near-sided perspective" 1198 } else if ( "nzmg".equals( projName ) ) {// "New Zealand Map Grid" 1199 } else if ( "ob_tran".equals( projName ) ) {// "General Oblique Transformation" 1200 } else if ( "ocea".equals( projName ) ) {// "Oblique Cylindrical Equal Area" 1201 } else if ( "oea".equals( projName ) ) {// "Oblated Equal Area" 1202 } else if ( "omerc".equals( projName ) ) {// "Oblique Mercator" 1203 } else if ( "ortel".equals( projName ) ) {// "Ortelius Oval" 1204 } else if ( "ortho".equals( projName ) ) {// "Orthographic" 1205 } else if ( "pconic".equals( projName ) ) {// "Perspective Conic" 1206 } else if ( "poly".equals( projName ) ) {// "Polyconic (American)" 1207 } else if ( "putp1".equals( projName ) ) {// "Putnins P1" 1208 } else if ( "putp2".equals( projName ) ) {// "Putnins P2" 1209 } else if ( "putp3".equals( projName ) ) {// "Putnins P3" 1210 } else if ( "putp3p".equals( projName ) ) {// "Putnins P3'" 1211 } else if ( "putp4p".equals( projName ) ) {// "Putnins P4'" 1212 } else if ( "putp5".equals( projName ) ) {// "Putnins P5" 1213 } else if ( "putp5p".equals( projName ) ) {// "Putnins P5'" 1214 } else if ( "putp6".equals( projName ) ) {// "Putnins P6" 1215 } else if ( "putp6p".equals( projName ) ) {// "Putnins P6'" 1216 } else if ( "qua_aut".equals( projName ) ) {// "Quartic Authalic" 1217 } else if ( "robin".equals( projName ) ) {// "Robinson" 1218 } else if ( "rpoly".equals( projName ) ) {// "Rectangular Polyconic" 1219 } else if ( "sinu".equals( projName ) ) {// "Sinusoidal (Sanson-Flamsteed)" 1220 } else if ( "somerc".equals( projName ) ) {// "Swiss. Obl. Mercator" 1221 } else if ( "stere".equals( projName ) ) {// "Stereographic" 1222 } else if ( "stere".equals( projName ) || "sterea".equals( projName ) ) {//"Oblique Stereographic Alternative" 1223 result = new StereographicAzimuthal( trueScaleLatitude, 1224 geoCRS, 1225 falseNorthing, 1226 falseEasting, 1227 naturalOrigin, 1228 units, 1229 scale, 1230 id, 1231 "Stereographic Azimuthal Projection", 1232 version, 1233 description, 1234 areaOfUse ); 1235 } else if ( "tcc".equals( projName ) ) {// "Transverse Central Cylindrical" 1236 } else if ( "tcea".equals( projName ) ) {// "Transverse Cylindrical Equal Area" 1237 } else if ( "tissot".equals( projName ) ) {// "Tissot Conic" 1238 } else if ( "tmerc".equals( projName ) || "utm".equals( projName ) ) {// "Transverse Mercator" 1239 s = params.remove( "south" ); 1240 boolean north = ( s == null || "".equals( s.trim() ) ); 1241 result = new TransverseMercator( north, 1242 geoCRS, 1243 falseNorthing, 1244 falseEasting, 1245 naturalOrigin, 1246 units, 1247 scale, 1248 id, 1249 "Transverse Mercator Projection", 1250 version, 1251 description, 1252 areaOfUse ); 1253 s = params.remove( "zone" ); 1254 if ( s != null && !"".equals( s.trim() ) ) { 1255 int zone = Integer.parseInt( s ); 1256 ( (TransverseMercator) result ).setUTMZone( zone ); 1257 } 1258 } else if ( "tpeqd".equals( projName ) ) {// "Two Point Equidistant" 1259 } else if ( "tpers".equals( projName ) ) {// "Tilted perspective" 1260 } else if ( "ups".equals( projName ) ) {// "Universal Polar Stereographic" 1261 } else if ( "urm5".equals( projName ) ) {// "Urmaev V" 1262 } else if ( "urmfps".equals( projName ) ) {// "Urmaev Flat-Polar Sinusoidal" 1263 } else if ( "utm".equals( projName ) ) {// "Universal Transverse Mercator (UTM)" 1264 } else if ( "vandg".equals( projName ) ) {// "van der Grinten (I)" 1265 } else if ( "vandg2".equals( projName ) ) {// "van der Grinten II" 1266 } else if ( "vandg3".equals( projName ) ) {// "van der Grinten III" 1267 } else if ( "vandg4".equals( projName ) ) {// "van der Grinten IV" 1268 } else if ( "vitk1".equals( projName ) ) {// "Vitkovsky I" 1269 } else if ( "wag1".equals( projName ) ) {// "Wagner I (Kavraisky VI)" 1270 } else if ( "wag2".equals( projName ) ) {// "Wagner II" 1271 } else if ( "wag3".equals( projName ) ) {// "Wagner III" 1272 } else if ( "wag4".equals( projName ) ) {// "Wagner IV" 1273 } else if ( "wag5".equals( projName ) ) {// "Wagner V" 1274 } else if ( "wag6".equals( projName ) ) {// "Wagner VI" 1275 } else if ( "wag7".equals( projName ) ) {// "Wagner VII" 1276 } else if ( "weren".equals( projName ) ) {// "Werenskiold I" 1277 } else if ( "wink1".equals( projName ) ) {// "Winkel I" 1278 } else if ( "wink2".equals( projName ) ) {// "Winkel II" 1279 } else if ( "wintri".equals( projName ) ) {// "Winkel Tripel" 1280 } 1281 if ( result == null ) { 1282 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_UNKNOWN_PROJECTION", 1283 projName ) ); 1284 } 1285 } 1286 return result; 1287 } 1288 1289 /** 1290 * @param params 1291 * the values to get the units or to_meter from. 1292 * @return a unit create from the +unit parameter or Unit.METRE if not found. 1293 * @throws CRSConfigurationException 1294 * if the given unit parameter could not be mapped to a valid deegree-crs unit. 1295 */ 1296 private Unit createUnit( Map<String, String> params ) 1297 throws CRSConfigurationException { 1298 Unit result = Unit.METRE; 1299 String tmpValue = params.remove( "units" ); 1300 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 1301 result = Unit.createUnitFromString( tmpValue ); 1302 if ( result == null ) { 1303 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_UNKNOWN_UNIT", 1304 params.get( EPSG_PRE + "identifier" ), 1305 tmpValue ) ); 1306 } 1307 } else { 1308 tmpValue = params.remove( "to_meter" ); 1309 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 1310 result = new Unit( "Unknown", "unknown", Double.parseDouble( tmpValue ), Unit.METRE ); 1311 } 1312 } 1313 return result; 1314 } 1315 1316 /** 1317 * Maps the given proj4 name to an id (if any) and creates an ellipsoid accordingly. 1318 * 1319 * @param ellipsoidName 1320 * defined in the proj lib 1321 * @return an ellipsoid with an id and name or <code>null</code> if the ellipsoidName was null or empty. 1322 * @throws CRSConfigurationException 1323 * if the given name could not be mapped. 1324 */ 1325 private Ellipsoid getPredefinedEllipsoid( String ellipsoidName ) 1326 throws CRSConfigurationException { 1327 if ( ellipsoidName != null && !"".equals( ellipsoidName.trim() ) ) { 1328 ellipsoidName = ellipsoidName.trim(); 1329 double semiMajorAxis = 0; 1330 double semiMinorAxis = Double.NaN; 1331 double inverseFlattening = 1; 1332 String id = ellipsoidName; 1333 String name = ""; 1334 if ( "APL4.9".equalsIgnoreCase( ellipsoidName ) ) { 1335 semiMajorAxis = 6378137.0; 1336 inverseFlattening = 298.25; 1337 name = "Appl. Physics. 1965"; 1338 // no epsg 1339 } else if ( "CPM".equalsIgnoreCase( ellipsoidName ) ) { 1340 semiMajorAxis = 6375738.7; 1341 inverseFlattening = 334.29; 1342 name = "Comm. des Poids et Mesures 1799"; 1343 // no epsg 1344 } else if ( "GRS67".equalsIgnoreCase( ellipsoidName ) ) { 1345 semiMajorAxis = 6378160.0; 1346 inverseFlattening = 298.2471674270; 1347 name = "GRS 67(IUGG 1967)"; 1348 id = "7036"; 1349 } else if ( "GRS80".equalsIgnoreCase( ellipsoidName ) ) { 1350 semiMajorAxis = 6378137.0; 1351 inverseFlattening = 298.257222101; 1352 name = "GRS 1980(IUGG, 1980)"; 1353 id = "7019"; 1354 } else if ( "IAU76".equalsIgnoreCase( ellipsoidName ) ) { 1355 semiMajorAxis = 6378140.0; 1356 inverseFlattening = 298.257; 1357 name = "IAU 1976"; 1358 // probably clarke 1359 // no epsg 1360 } else if ( "MERIT".equalsIgnoreCase( ellipsoidName ) ) { 1361 semiMajorAxis = 6378137.0; 1362 inverseFlattening = 298.257; 1363 name = "MERIT 1983"; 1364 // no epsg 1365 } else if ( "NWL9D".equalsIgnoreCase( ellipsoidName ) ) { 1366 semiMajorAxis = 6378145.0; 1367 inverseFlattening = 298.25; 1368 name = "Naval Weapons Lab., 1965"; 1369 // no epsg 1370 } else if ( "SEasia".equalsIgnoreCase( ellipsoidName ) ) { 1371 semiMajorAxis = 6378155.0; 1372 semiMinorAxis = 6356773.3205; 1373 name = "Southeast Asia"; 1374 // no epsg 1375 } else if ( "SGS85".equalsIgnoreCase( ellipsoidName ) ) { 1376 semiMajorAxis = 6378136.0; 1377 inverseFlattening = 298.257; 1378 name = "Soviet Geodetic System 85"; 1379 // no epsg 1380 } else if ( "WGS60".equalsIgnoreCase( ellipsoidName ) ) { 1381 semiMajorAxis = 6378165.0; 1382 inverseFlattening = 298.3; 1383 name = "WGS 60"; 1384 // no epsg 1385 } else if ( "WGS66".equalsIgnoreCase( ellipsoidName ) ) { 1386 semiMajorAxis = 6378145.0; 1387 inverseFlattening = 298.25; 1388 name = "WGS 66"; 1389 // no epsg 1390 } else if ( "WGS72".equalsIgnoreCase( ellipsoidName ) ) { 1391 semiMajorAxis = 6378135.0; 1392 inverseFlattening = 298.26; 1393 name = "WGS 72"; 1394 id = "7043"; 1395 } else if ( "WGS84".equalsIgnoreCase( ellipsoidName ) ) { 1396 semiMajorAxis = 6378137.0; 1397 inverseFlattening = 298.257223563; 1398 name = "WGS 84"; 1399 id = "7030"; 1400 } else if ( "airy".equalsIgnoreCase( ellipsoidName ) ) { 1401 semiMajorAxis = 6377563.396; 1402 semiMinorAxis = 6356256.910; 1403 name = "Airy 1830"; 1404 id = "7001"; 1405 } else if ( "andrae".equalsIgnoreCase( ellipsoidName ) ) { 1406 semiMajorAxis = 6377104.43; 1407 inverseFlattening = 300.0; 1408 name = "Andrae 1876 (Den., Iclnd.)"; 1409 // no epsg 1410 } else if ( "aust_SA".equalsIgnoreCase( ellipsoidName ) ) { 1411 semiMajorAxis = 6378160.0; 1412 inverseFlattening = 298.25; 1413 name = "Australian Natl & S. Amer. 1969"; 1414 id = "7050"; 1415 } else if ( "bess_nam".equalsIgnoreCase( ellipsoidName ) ) { 1416 semiMajorAxis = 6377483.865; 1417 inverseFlattening = 299.1528128; 1418 name = "Bessel 1841 (Namibia)"; 1419 id = "7046"; 1420 } else if ( "bessel".equalsIgnoreCase( ellipsoidName ) ) { 1421 semiMajorAxis = 6377397.155; 1422 inverseFlattening = 299.1528128; 1423 name = "Bessel 1841"; 1424 id = "7004"; 1425 } else if ( "clrk66".equalsIgnoreCase( ellipsoidName ) ) { 1426 semiMajorAxis = 6378206.4; 1427 semiMinorAxis = 6356583.8; 1428 name = "Clarke 1866"; 1429 id = "7008"; 1430 } else if ( "clrk80".equalsIgnoreCase( ellipsoidName ) ) { 1431 semiMajorAxis = 6378249.145; 1432 inverseFlattening = 293.4663; 1433 name = "Clarke 1880 mod."; 1434 id = "7034"; 1435 } else if ( "delmbr".equalsIgnoreCase( ellipsoidName ) ) { 1436 semiMajorAxis = 6376428.; 1437 inverseFlattening = 311.5; 1438 name = "Delambre 1810 (Belgium)"; 1439 // epsg closed 1440 } else if ( "engelis".equalsIgnoreCase( ellipsoidName ) ) { 1441 semiMajorAxis = 6378136.05; 1442 inverseFlattening = 298.2566; 1443 name = "Engelis 1985"; 1444 // espg closed 1445 } else if ( "evrst30".equalsIgnoreCase( ellipsoidName ) ) { 1446 semiMajorAxis = 6377276.345; 1447 inverseFlattening = 300.8017; 1448 name = "Everest 1830"; 1449 id = "7042"; 1450 } else if ( "evrst48".equalsIgnoreCase( ellipsoidName ) ) { 1451 semiMajorAxis = 6377304.063; 1452 inverseFlattening = 300.8017; 1453 name = "Everest 1948"; 1454 id = "7018"; 1455 } else if ( "evrst56".equalsIgnoreCase( ellipsoidName ) ) { 1456 semiMajorAxis = 6377301.243; 1457 inverseFlattening = 300.8017; 1458 name = "Everest 1956"; 1459 id = "7044"; 1460 } else if ( "evrst69".equalsIgnoreCase( ellipsoidName ) ) { 1461 semiMajorAxis = 6377295.664; 1462 inverseFlattening = 300.8017; 1463 name = "Everest 1969"; 1464 id = "7056"; 1465 } else if ( "evrstSS".equalsIgnoreCase( ellipsoidName ) ) { 1466 semiMajorAxis = 6377298.556; 1467 inverseFlattening = 300.8017; 1468 name = "Everest (Sabah & Sarawak)"; 1469 id = "7016"; 1470 } else if ( "fschr60".equalsIgnoreCase( ellipsoidName ) ) { 1471 semiMajorAxis = 6378166.; 1472 inverseFlattening = 298.3; 1473 name = "Fischer (Mercury Datum) 1960"; 1474 // epsg closed 1475 } else if ( "fschr60m".equalsIgnoreCase( ellipsoidName ) ) { 1476 semiMajorAxis = 6378155.; 1477 inverseFlattening = 298.3; 1478 name = "Modified Fischer 1960"; 1479 // epsg closed 1480 } else if ( "fschr68".equalsIgnoreCase( ellipsoidName ) ) { 1481 semiMajorAxis = 6378150.; 1482 inverseFlattening = 298.3; 1483 name = "Fischer 1968"; 1484 // epsg closed 1485 } else if ( "helmert".equalsIgnoreCase( ellipsoidName ) ) { 1486 semiMajorAxis = 6378200.; 1487 inverseFlattening = 298.3; 1488 name = "Helmert 1906"; 1489 id = "7020"; 1490 } else if ( "hough".equalsIgnoreCase( ellipsoidName ) ) { 1491 semiMajorAxis = 6378270.0; 1492 inverseFlattening = 297.; 1493 name = "Hough"; 1494 id = "7053"; 1495 } else if ( "intl".equalsIgnoreCase( ellipsoidName ) ) { 1496 semiMajorAxis = 6378388.0; 1497 inverseFlattening = 297.; 1498 name = "International 1909 (Hayford)"; 1499 id = "7022"; 1500 } else if ( "kaula".equalsIgnoreCase( ellipsoidName ) ) { 1501 semiMajorAxis = 6378163.; 1502 inverseFlattening = 298.24; 1503 name = "Kaula 1961"; 1504 // no epsg 1505 } else if ( "krass".equalsIgnoreCase( ellipsoidName ) ) { 1506 semiMajorAxis = 6378245.0; 1507 inverseFlattening = 298.3; 1508 name = "Krassowsky, 1942"; 1509 id = "7024"; 1510 } else if ( "lerch".equalsIgnoreCase( ellipsoidName ) ) { 1511 semiMajorAxis = 6378139.; 1512 inverseFlattening = 298.257; 1513 name = "Lerch 1979"; 1514 // no epsg 1515 } else if ( "mod_airy".equalsIgnoreCase( ellipsoidName ) ) { 1516 semiMajorAxis = 6377340.189; 1517 semiMinorAxis = 6356034.446; 1518 name = "Modified Airy"; 1519 id = "7002"; 1520 } else if ( "mprts".equalsIgnoreCase( ellipsoidName ) ) { 1521 semiMajorAxis = 6397300.; 1522 inverseFlattening = 191.; 1523 name = "Maupertius 1738"; 1524 // no epsg 1525 } else if ( "new_intl".equalsIgnoreCase( ellipsoidName ) ) { 1526 semiMajorAxis = 6378157.5; 1527 semiMinorAxis = 6356772.2; 1528 name = "New International 1967"; 1529 id = "7036"; 1530 } else if ( "plessis".equalsIgnoreCase( ellipsoidName ) ) { 1531 semiMajorAxis = 6376523.; 1532 semiMinorAxis = 6355863.; 1533 name = "Plessis 1817 (France)"; 1534 id = "7027"; 1535 } else if ( "sphere".equalsIgnoreCase( ellipsoidName ) ) { 1536 semiMajorAxis = 6370997.0; 1537 semiMinorAxis = 6370997.0; 1538 name = "Normal Sphere (r=6370997)"; 1539 } else if ( "walbeck".equalsIgnoreCase( ellipsoidName ) ) { 1540 semiMajorAxis = 6376896.0; 1541 semiMinorAxis = 6355834.8467; 1542 name = "Walbeck"; 1543 // epsg closed 1544 } else { 1545 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_UNKNOWN_ELLIPSOID", 1546 ellipsoidName ) ); 1547 } 1548 String[] ids = new String[] { id }; 1549 if ( !ellipsoidName.equals( id ) ) { 1550 ids = new String[] { EPSG_PRE + id, OGC_URN + id, OPENGIS_URL + id, OPENGIS_URN + id }; 1551 } 1552 Ellipsoid ellips = null; 1553 if ( Double.isNaN( semiMinorAxis ) ) { 1554 ellips = new Ellipsoid( semiMajorAxis, 1555 Unit.METRE, 1556 inverseFlattening, 1557 ids, 1558 new String[] { name }, 1559 null, 1560 null, 1561 null ); 1562 } else {// semiMinorAxis was given. 1563 ellips = new Ellipsoid( Unit.METRE, 1564 semiMajorAxis, 1565 semiMinorAxis, 1566 ids, 1567 new String[] { name }, 1568 null, 1569 null, 1570 null ); 1571 } 1572 return ellips; 1573 } 1574 return null; 1575 } 1576 1577 /** 1578 * Helper method to parse day, month second formats. With a little help from com.jhlabs.map.AngleFormat 1579 * 1580 * @param text 1581 * to pe parsed into degrees (or radians). 1582 * @param toDegrees 1583 * if the given text is in degrees 1584 * @return a 1585 */ 1586 private double parseAngleFormat( String text, boolean toDegrees ) { 1587 double d = 0, m = 0, s = 0; 1588 double result; 1589 boolean negate = false; 1590 int length = text.length(); 1591 if ( length > 0 ) { 1592 char c = Character.toUpperCase( text.charAt( length - 1 ) ); 1593 switch ( c ) { 1594 case 'W': 1595 case 'S': 1596 negate = true; 1597 case 'E': 1598 case 'N': 1599 text = text.substring( 0, length - 1 ); 1600 break; 1601 } 1602 } 1603 int i = text.indexOf( 'd' ); 1604 if ( i == -1 ) 1605 i = text.indexOf( '\u00b0' ); 1606 if ( i != -1 ) { 1607 String dd = text.substring( 0, i ); 1608 String mmss = text.substring( i + 1 ); 1609 d = Double.valueOf( dd ).doubleValue(); 1610 i = mmss.indexOf( 'm' ); 1611 if ( i == -1 ) 1612 i = mmss.indexOf( '\'' ); 1613 if ( i != -1 ) { 1614 if ( i != 0 ) { 1615 String mm = mmss.substring( 0, i ); 1616 m = Double.valueOf( mm ).doubleValue(); 1617 } 1618 if ( mmss.endsWith( "s" ) || mmss.endsWith( "\"" ) ) 1619 mmss = mmss.substring( 0, mmss.length() - 1 ); 1620 if ( i != mmss.length() - 1 ) { 1621 String ss = mmss.substring( i + 1 ); 1622 s = Double.valueOf( ss ).doubleValue(); 1623 } 1624 if ( m < 0 || m > 59 ) 1625 throw new NumberFormatException( "Minutes must be between 0 and 59" ); 1626 if ( s < 0 || s >= 60 ) 1627 throw new NumberFormatException( "Seconds must be between 0 and 59" ); 1628 } else if ( i != 0 ) { 1629 m = Double.valueOf( mmss ).doubleValue(); 1630 } 1631 if ( toDegrees ) { 1632 result = dmsToDeg( d, m, s ); 1633 } else { 1634 result = dmsToRad( d, m, s ); 1635 } 1636 } else { 1637 result = Double.parseDouble( text ); 1638 if ( !toDegrees ) 1639 result = Math.toRadians( result ); 1640 } 1641 if ( negate ) {// South 1642 result = -result; 1643 } 1644 return result; 1645 } 1646 1647 /** 1648 * Converts angle information to radians. With a little help from com.jhlabs.map.MapMath. For negative angles, d 1649 * should be negative, m & s positive. 1650 * 1651 * @param d 1652 * days 1653 * @param m 1654 * months 1655 * @param s 1656 * seconds. 1657 * @return the converted value in radians. 1658 */ 1659 private double dmsToRad( double d, double m, double s ) { 1660 if ( d >= 0 ) { 1661 return ( d + m / 60 + s / 3600 ) * Math.PI / 180.0; 1662 } 1663 return ( d - m / 60 - s / 3600 ) * Math.PI / 180.0; 1664 } 1665 1666 /** 1667 * Converts angle information to degrees. With a little help from com.jhlabs.map.MapMath. For negative angles, d 1668 * should be negative, m & s positive. 1669 * 1670 * @param d 1671 * days 1672 * @param m 1673 * months 1674 * @param s 1675 * seconds. 1676 * @return the converted value in degrees. 1677 */ 1678 private double dmsToDeg( double d, double m, double s ) { 1679 if ( d >= 0 ) { 1680 return ( d + m / 60 + s / 3600 ); 1681 } 1682 return ( d - m / 60 - s / 3600 ); 1683 } 1684 1685 /** 1686 * Parses the configured proj4 parameters from the given String using a StreamTokenizer and saves them in the Map. 1687 * 1688 * @param params 1689 * to be parsed 1690 * @param lineNumber 1691 * in the config file. 1692 * @param kvp 1693 * in which the key-value pairs will be saved. 1694 * @return the parsed Identifier or <code>null</code> if no identifier was found. 1695 * @throws IOException 1696 * if the StreamTokenizer finds an error. 1697 * @throws CRSConfigurationException 1698 * If the config was malformed. 1699 */ 1700 private String parseConfigString( String params, String lineNumber, Map<String, String> kvp ) 1701 throws IOException, 1702 CRSConfigurationException { 1703 BufferedReader br = new BufferedReader( new StringReader( params ) ); 1704 StreamTokenizer t = new StreamTokenizer( br ); 1705 t.commentChar( '#' ); 1706 t.ordinaryChars( '0', '9' ); 1707 // t.ordinaryChars( '.', '.' ); 1708 t.ordinaryChar( '.' ); 1709 // t.ordinaryChars( '-', '-' ); 1710 t.ordinaryChar( '-' ); 1711 // t.ordinaryChars( '+', '+' ); 1712 t.ordinaryChar( '+' ); 1713 t.wordChars( '0', '9' ); 1714 t.wordChars( '\'', '\'' ); 1715 t.wordChars( '"', '"' ); 1716 t.wordChars( '_', '_' ); 1717 t.wordChars( '.', '.' ); 1718 t.wordChars( '-', '-' ); 1719 t.wordChars( '+', '+' ); 1720 t.wordChars( ',', ',' ); 1721 t.nextToken(); 1722 String identifier = null; 1723 /** 1724 * PROJ4 type defintions have following format, <number> +proj .... So the first type must start with an '<' 1725 */ 1726 if ( t.ttype == '<' ) { 1727 t.nextToken(); 1728 // if the next value is not a word, e.g. a number (see t.wordChars('0','9')) it is the wrong format. 1729 if ( t.ttype != StreamTokenizer.TT_WORD ) { 1730 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_INVALID_ID", 1731 lineNumber, 1732 "An identifier (e.g. number)", 1733 "<", 1734 getTokenizerSymbolToString( t.ttype ) ) ); 1735 } 1736 // it's a word so get the identifier. 1737 identifier = t.sval; 1738 // 1739 kvp.put( "identifier", identifier ); 1740 t.nextToken(); 1741 1742 // check for closing bracket. 1743 if ( t.ttype != '>' ) { 1744 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_MISSING_EXPECTED_CHAR", 1745 lineNumber, 1746 ">" ) ); 1747 } 1748 t.nextToken(); 1749 1750 // get the parameters. 1751 while ( t.ttype != '<' ) { 1752 if ( t.ttype == '+' ) { 1753 t.nextToken(); 1754 } 1755 if ( t.ttype != StreamTokenizer.TT_WORD ) { 1756 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_INVALID_ID", 1757 lineNumber, 1758 "A parameter", 1759 "+", 1760 getTokenizerSymbolToString( t.ttype ) ) ); 1761 } 1762 String key = t.sval; 1763 if ( key != null && !"".equals( key ) ) { 1764 if ( key.startsWith( "+" ) ) { 1765 key = key.substring( 1 ); 1766 } 1767 t.nextToken(); 1768 if ( t.ttype == '=' ) { 1769 t.nextToken(); 1770 if ( t.ttype != StreamTokenizer.TT_WORD ) { 1771 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_INVALID_ID", 1772 lineNumber, 1773 "A Value", 1774 "=", 1775 getTokenizerSymbolToString( t.ttype ) ) ); 1776 } 1777 String value = t.sval; 1778 LOG.logDebug( "Putting key: " + key + " with value: " + value ); 1779 kvp.put( key, value ); 1780 // take the next token. 1781 t.nextToken(); 1782 } 1783 } 1784 } 1785 t.nextToken(); 1786 if ( t.ttype != '>' ) { 1787 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_MISSING_EXPECTED_CHAR", 1788 lineNumber, 1789 "<> (End of defintion)" ) ); 1790 } 1791 } else { 1792 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_MISSING_EXPECTED_CHAR", 1793 lineNumber, 1794 "< (Start of defintion)" ) ); 1795 } 1796 br.close(); 1797 return identifier; 1798 } 1799 1800 /** 1801 * Creates a helpfull string of the given StreamTokenizer value. 1802 * 1803 * @param val 1804 * an int gotten from streamTokenizer.ttype. 1805 * @return a human readable String. 1806 */ 1807 private String getTokenizerSymbolToString( int val ) { 1808 String result = new String( "" + val ); 1809 if ( val == StreamTokenizer.TT_EOF ) { 1810 result = "End of file"; 1811 } else if ( val == StreamTokenizer.TT_EOL ) { 1812 result = "End of line"; 1813 } else if ( val == StreamTokenizer.TT_NUMBER ) { 1814 result = "A number with value: " + val; 1815 } 1816 return result; 1817 } 1818 }