001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/crs/configuration/proj4/PROJ4CRSProvider.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.util.Calendar; 040 import java.util.GregorianCalendar; 041 import java.util.HashMap; 042 import java.util.LinkedList; 043 import java.util.List; 044 import java.util.Map; 045 import java.util.Properties; 046 import java.util.Set; 047 048 import javax.vecmath.Point2d; 049 050 import org.deegree.crs.Identifiable; 051 import org.deegree.crs.components.Axis; 052 import org.deegree.crs.components.Ellipsoid; 053 import org.deegree.crs.components.GeodeticDatum; 054 import org.deegree.crs.components.PrimeMeridian; 055 import org.deegree.crs.components.Unit; 056 import org.deegree.crs.configuration.AbstractCRSProvider; 057 import org.deegree.crs.coordinatesystems.CoordinateSystem; 058 import org.deegree.crs.coordinatesystems.GeographicCRS; 059 import org.deegree.crs.coordinatesystems.ProjectedCRS; 060 import org.deegree.crs.exceptions.CRSConfigurationException; 061 import org.deegree.crs.projections.Projection; 062 import org.deegree.crs.projections.azimuthal.LambertAzimuthalEqualArea; 063 import org.deegree.crs.projections.azimuthal.StereographicAlternative; 064 import org.deegree.crs.projections.azimuthal.StereographicAzimuthal; 065 import org.deegree.crs.projections.conic.LambertConformalConic; 066 import org.deegree.crs.projections.cylindric.TransverseMercator; 067 import org.deegree.crs.transformations.Transformation; 068 import org.deegree.crs.transformations.helmert.Helmert; 069 import org.deegree.framework.log.ILogger; 070 import org.deegree.framework.log.LoggerFactory; 071 import org.deegree.i18n.Messages; 072 073 /** 074 * The <code>PROJ4CRSProvider</code> class is capable of parsing the nad/epsg file and use it as a backend for crs's. 075 * This class also adds following identifiers to the coordinatesystems. 076 * <ul> 077 * <li>http://www.opengis.net/gml/srs/epsg.xml#4326</li> 078 * <li>URN:OPENGIS:DEF:CRS:EPSG::</li> 079 * <li>URN:OGC:DEF:CRS:EPSG::</li> 080 * </ul> 081 * 082 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> 083 * 084 * @author last edited by: $Author: lbuesching $ 085 * 086 * @version $Revision: 27765 $, $Date: 2010-11-04 07:50:32 +0100 (Do, 04 Nov 2010) $ 087 * 088 */ 089 090 public class PROJ4CRSProvider extends AbstractCRSProvider<Map<String, String>> { 091 092 private static ILogger LOG = LoggerFactory.getLogger( PROJ4CRSProvider.class ); 093 094 private static int ellipsCount = 0; 095 096 private static int datumCount = 0; 097 098 private static int geographicCRSCount = 0; 099 100 private static int primeMeridianCount = 0; 101 102 private final static String EPSG_PRE = "EPSG:"; 103 104 private final static String OPENGIS_URL = "HTTP://WWW.OPENGIS.NET/GML/SRS/EPSG.XML#"; 105 106 private final static String OPENGIS_URN = "URN:OPENGIS:DEF:CRS:EPSG::"; 107 108 private static final String OGC_URN = "URN:OGC:DEF:CRS:EPSG::"; 109 110 private Map<String, CoordinateSystem> coordinateSystems = new HashMap<String, CoordinateSystem>( 10000 ); 111 112 private String version = null; 113 114 private String[] versions = null; 115 116 private String areaOfUse = "Unknown"; 117 118 private String[] areasOfUse = new String[] { "Unknown" }; 119 120 /** 121 * Export constructor, sets the version to current date.. 122 */ 123 public PROJ4CRSProvider() { 124 super( new Properties(), null, null ); 125 // dummy constructor for exporting only. 126 GregorianCalendar cal = (GregorianCalendar) Calendar.getInstance(); 127 version = cal.get( Calendar.YEAR ) + "-" + ( cal.get( Calendar.MONTH ) + 1 ) + "-" 128 + cal.get( Calendar.DAY_OF_MONTH ) + "T" + cal.get( Calendar.HOUR_OF_DAY ) + ":" 129 + cal.get( Calendar.MINUTE ); 130 versions = new String[] { version }; 131 } 132 133 /** 134 * Opens a reader on the file and parses all parameters with id, without instantiating any CoordinateSystems. 135 * 136 * @param properties 137 * containing a crs.configuration property referencing a file location. 138 */ 139 public PROJ4CRSProvider( Properties properties ) { 140 super( properties, ProjFileResource.class, null ); 141 if ( getResolver() == null ) { 142 setResolver( new ProjFileResource( this, properties ) ); 143 } 144 } 145 146 @Override 147 protected ProjFileResource getResolver() { 148 return (ProjFileResource) super.getResolver(); 149 } 150 151 public List<CoordinateSystem> getAvailableCRSs() 152 throws CRSConfigurationException { 153 Set<String> keys = getResolver().getAvailableIDs(); 154 if ( LOG.isDebug() ) { 155 LOG.logDebug( "Found following keys: " + keys ); 156 } 157 List<CoordinateSystem> allSystems = new LinkedList<CoordinateSystem>(); 158 for ( String key : keys ) { 159 try { 160 CoordinateSystem result = getCRSByID( key ); 161 if ( result != null ) { 162 allSystems.add( result ); 163 } 164 } catch ( CRSConfigurationException e ) { 165 LOG.logInfo( Messages.getMessage( "CRS_CONFIG_PROJ4_NOT_ADDING_CRS", key, e.getMessage() ) ); 166 } 167 } 168 // get all already created crs's 169 keys = coordinateSystems.keySet(); 170 for ( String key : keys ) { 171 CoordinateSystem result = coordinateSystems.get( key ); 172 if ( result != null ) { 173 allSystems.add( result ); 174 } 175 } 176 return allSystems; 177 } 178 179 public boolean canExport() { 180 return false; 181 } 182 183 public void export( StringBuilder sb, List<CoordinateSystem> crsToExport ) { 184 throw new UnsupportedOperationException( "Exporting to PROJ4 configuration is not suppored yet." ); 185 186 } 187 188 /** 189 * Creates a projected crs from given params. Because proj4 doesn't define some axisorder xy is always assumed. 190 * 191 * @param projectionName 192 * to give the geographic crs (+e.g. proj=tmerc) or <code>null</code> 193 * @param params 194 * containing datum info. 195 * @return a geographic crs 196 * @throws CRSConfigurationException 197 * if an exception occurs while creating the datum. 198 */ 199 private ProjectedCRS createProjectedCRS( String projectionName, Map<String, String> params ) 200 throws CRSConfigurationException { 201 String[] names = new String[] { params.remove( "comment" ) }; 202 203 String[] descriptions = null; 204 String id = params.get( "identifier" ); 205 String[] ids = getPredefinedIDs( id ); 206 String geoID = "GEO_CRS_" + geographicCRSCount; 207 if ( "3068".equals( id ) || "31466".equals( id ) || "31467".equals( id ) || "31468".equals( id ) 208 || "31469".equals( id ) ) { 209 geoID = EPSG_PRE + "4314"; 210 } else { 211 geographicCRSCount++; 212 } 213 GeographicCRS underLyingCRS = createGeographicCRS( geoID, id, params ); 214 Projection projection = createProjection( projectionName, underLyingCRS, params ); 215 216 return new ProjectedCRS( projection, new Axis[] { new Axis( projection.getUnits(), "x", Axis.AO_EAST ), 217 new Axis( projection.getUnits(), "y", Axis.AO_NORTH ) }, ids, 218 names, versions, descriptions, areasOfUse ); 219 } 220 221 /** 222 * Creates a geographic crs with given identifier, if the identifier is empty or <code>null</code> the 223 * params.getIdentifier will be used. Because proj4 doesn't define some axisorder xy is always assumed. 224 * 225 * @param identifier 226 * to give the geographic crs (+proj=longlat) or <code>null</code> 227 * @param params 228 * containing datum info. 229 * @param projectedID 230 * of the projected crs (only if identifier is not null). 231 * @return a geographic crs 232 * @throws CRSConfigurationException 233 * if an exception occurs while creating the datum. 234 */ 235 private GeographicCRS createGeographicCRS( String identifier, String projectedID, Map<String, String> params ) 236 throws CRSConfigurationException { 237 String name = "Proj4 defined Geographic CRS"; 238 String tmp = params.remove( "comment" ); 239 if ( tmp != null && !"".equals( tmp.trim() ) ) { 240 name = tmp; 241 } 242 String[] names = new String[] { name }; 243 String description = "Handmade proj4 geographic crs definition (parsed from nad/epsg)."; 244 String ids[] = new String[] { identifier }; 245 String tmpIdentifier = identifier; 246 String tmpProjectedID = projectedID; 247 if ( tmpIdentifier == null || "".equals( tmpIdentifier.trim() ) ) { 248 tmpIdentifier = params.get( "identifier" ); 249 ids = getPredefinedIDs( tmpIdentifier ); 250 // if the id was not set, we create a geocrs, which means that no projectedID has been 251 // set, we want to build the datum with the id though! 252 tmpProjectedID = tmpIdentifier; 253 } else { 254 description += " Used by projected crs with id: " + projectedID; 255 } 256 String[] descriptions = new String[] { description }; 257 // projectedID will also hold the id of the geo-crs if it is a top level one. 258 GeodeticDatum datum = createDatum( params, tmpProjectedID ); 259 GeographicCRS result = new GeographicCRS( datum, 260 new Axis[] { new Axis( Unit.RADIAN, "longitude", Axis.AO_EAST ), 261 new Axis( Unit.RADIAN, "latitude", Axis.AO_NORTH ) }, 262 ids, names, versions, descriptions, areasOfUse ); 263 return result; 264 } 265 266 /** 267 * Create a datum by resolving the 'datum' param or creating it from the given ellipsoid/primemeridan parameters. 268 * 269 * @param params 270 * to create the datum from 271 * @return a datum 272 * @throws CRSConfigurationException 273 * if one of the datums necessities( ellipsoid, primemeridan) could not be created. 274 */ 275 private GeodeticDatum createDatum( Map<String, String> params, String identifier ) 276 throws CRSConfigurationException { 277 278 GeodeticDatum result = null; 279 String tmpValue = params.remove( "datum" ); 280 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 281 // removing the defined ellipsoid. 282 result = getPredefinedDatum( tmpValue, params.remove( "ellps" ), identifier ); 283 if ( result == null ) { 284 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_UNKNOWN_DATUM", 285 params.get( EPSG_PRE + "identifier" ), 286 tmpValue ) ); 287 } 288 } else { 289 Ellipsoid ellipsoid = createEllipsoid( params ); 290 if ( ellipsoid == null ) { 291 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_DATUM_WITHOUT_ELLIPSOID", 292 params.get( EPSG_PRE + "identifier" ) ) ); 293 } 294 String id = "DATUM_" + datumCount++; 295 String name = "Proj4 defined datum"; 296 297 String description = "Handmade proj4 datum definition (parsed from nad/epsg) used by crs with id: " 298 + ( EPSG_PRE + identifier ); 299 PrimeMeridian pm = createPrimeMeridian( params ); 300 result = new GeodeticDatum( ellipsoid, pm, null, id, name, version, description, areaOfUse ); 301 } 302 return result; 303 } 304 305 /** 306 * Create a prime meridian according to predefined proj4 definitions. 307 * 308 * @param params 309 * to get the 'pm' parameter from. 310 * @return a mapped primemeridian or the Greenwich if no pm parameter was found. 311 * @throws CRSConfigurationException 312 * if the pm-parameter could not be mapped. 313 */ 314 private PrimeMeridian createPrimeMeridian( Map<String, String> params ) 315 throws CRSConfigurationException { 316 String tmpValue = params.remove( "pm" ); 317 PrimeMeridian result = PrimeMeridian.GREENWICH; 318 String id = "pm_" + primeMeridianCount++; 319 String[] names = null; 320 String[] meridianVersions = null; 321 String[] descs = null; 322 String[] aous = null; 323 double longitude = Double.NaN; 324 if ( tmpValue != null && !"".equals( tmpValue.trim() ) && !"greenwich".equals( tmpValue.trim() ) ) { 325 if ( "athens".equals( tmpValue ) ) { 326 longitude = parseAngleFormat( "23d42'58.815\"E", false ); 327 id = "8912"; 328 names = new String[] { "Athens" }; 329 meridianVersions = new String[] { "1995-06-02" }; 330 descs = new String[] { "Used in Greece for older mapping based on Hatt projection." }; 331 } else if ( "bern".equals( tmpValue ) ) { 332 longitude = parseAngleFormat( "7d26'22.5\"E", false ); 333 id = "8907"; 334 names = new String[] { "Bern" }; 335 meridianVersions = new String[] { "1995-06-02" }; 336 descs = new String[] { "1895 value. Newer value of 7 deg 26 min 22.335 sec E determined in 1938." }; 337 } else if ( "bogota".equals( tmpValue ) ) { 338 longitude = parseAngleFormat( "74d04'51.3\"W", false ); 339 id = "8904"; 340 names = new String[] { "Bogota" }; 341 meridianVersions = new String[] { "1995-06-02" }; 342 descs = new String[] { "Instituto Geografico 'Augustin Cadazzi' (IGAC); Bogota" }; 343 } else if ( "brussels".equals( tmpValue ) ) { 344 longitude = parseAngleFormat( "4d22'4.71\"E", false ); 345 id = "8910"; 346 names = new String[] { "Brussel" }; 347 meridianVersions = new String[] { "1995-06-02" }; 348 } else if ( "ferro".equals( tmpValue ) ) { 349 longitude = parseAngleFormat( "17d40'W", false ); 350 id = "8909"; 351 names = new String[] { "Ferro" }; 352 meridianVersions = new String[] { "1995-06-02" }; 353 descs = new String[] { "Used in Austria and former Czechoslovakia. " }; 354 } else if ( "jakarta".equals( tmpValue ) ) { 355 longitude = parseAngleFormat( "106d48'27.79\"E", false ); 356 id = "8908"; 357 names = new String[] { "Jakarta" }; 358 meridianVersions = new String[] { "1995-06-02" }; 359 } else if ( "lisbon".equals( tmpValue ) ) { 360 longitude = parseAngleFormat( "9d07'54.862\"W", false ); 361 id = "8902"; 362 names = new String[] { "lisbon" }; 363 meridianVersions = new String[] { "1995-06-02" }; 364 descs = new String[] { "Information Source: Instituto Geografico e Cadastral; Lisbon " }; 365 } else if ( "madrid".equals( tmpValue ) ) { 366 longitude = parseAngleFormat( "3d41'16.58\"W", false ); 367 id = "8905"; 368 names = new String[] { "Madrid" }; 369 meridianVersions = new String[] { "1995-06-02" }; 370 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." }; 371 } else if ( "oslo".equals( tmpValue ) ) { 372 longitude = parseAngleFormat( "10d43'22.5\"E", false ); 373 id = "8913"; 374 names = new String[] { "Oslo" }; 375 meridianVersions = new String[] { "1995-06-02" }; 376 descs = new String[] { "ormerly known as Kristiania or Christiania." }; 377 } else if ( "paris".equals( tmpValue ) ) { 378 longitude = parseAngleFormat( "2d20'14.025\"E", false ); 379 id = "8903"; 380 names = new String[] { "Paris" }; 381 meridianVersions = new String[] { "1995-06-02" }; 382 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." }; 383 } else if ( "rome".equals( tmpValue ) ) { 384 longitude = parseAngleFormat( "12d27'8.4\"E", false ); 385 id = "8906"; 386 names = new String[] { "Rome" }; 387 meridianVersions = new String[] { "1995-06-02" }; 388 } else if ( "stockholm".equals( tmpValue ) ) { 389 longitude = parseAngleFormat( "18d3'29.8\"E", false ); 390 id = "8911"; 391 names = new String[] { "Stockholm" }; 392 meridianVersions = new String[] { "1995-06-02" }; 393 } else { 394 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_UNKNOWN_PM", 395 params.get( EPSG_PRE + "identifier" ), 396 tmpValue ) ); 397 } 398 } 399 if ( !Double.isNaN( longitude ) ) { 400 String[] ids = new String[] { id }; 401 if ( !id.startsWith( "pm_" ) ) { 402 ids = getPredefinedIDs( id ); 403 } 404 result = new PrimeMeridian( Unit.RADIAN, longitude, ids, names, meridianVersions, descs, aous ); 405 } 406 return result; 407 } 408 409 /** 410 * Tries to create an ellips from a predefined mapping, or from axis, eccentricities etc. if defined. 411 * 412 * @param params 413 * to create the ellipsoid from 414 * @return an (mapped) ellipsoid. 415 * @throws CRSConfigurationException 416 * if no mapping was found or the semimajor axis was not defined. 417 */ 418 private Ellipsoid createEllipsoid( Map<String, String> params ) 419 throws CRSConfigurationException { 420 421 Ellipsoid result = null; 422 double semiMajorAxis = Double.NaN; 423 double eccentricitySquared = Double.NaN; 424 double eccentricity = Double.NaN; 425 double inverseFlattening = Double.NaN; 426 double semiMinorAxis = Double.NaN; 427 428 // Get the ellipsoid 429 String tmpValue = params.remove( "ellps" ); 430 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 431 LOG.logDebug( "Creating predefined ellipsoid: " + tmpValue ); 432 result = getPredefinedEllipsoid( tmpValue ); 433 } else { 434 // if no ellipsoid is defined maybe a sphere 435 tmpValue = params.remove( "R" ); 436 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 437 LOG.logDebug( "Found a Radius instead of an ellipsoid, the projection uses a sphere!" ); 438 semiMajorAxis = Double.parseDouble( tmpValue ); 439 } else {// an ellipsoid instead of a sphere. 440 tmpValue = params.remove( "a" ); 441 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 442 semiMajorAxis = Double.parseDouble( tmpValue ); 443 } 444 tmpValue = params.remove( "es" ); 445 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 446 eccentricitySquared = Double.parseDouble( tmpValue ); 447 } else { 448 tmpValue = params.remove( "e" ); 449 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 450 eccentricity = Double.parseDouble( tmpValue ); 451 } else { 452 tmpValue = params.remove( "rf" ); 453 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 454 inverseFlattening = Double.parseDouble( tmpValue ); 455 } else { 456 tmpValue = params.remove( "f" ); 457 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 458 double flattening = Double.parseDouble( tmpValue ); 459 if ( Math.abs( flattening ) > 0.000001 ) { 460 inverseFlattening = 1 / flattening; 461 } else { 462 LOG.logDebug( "The given flattening: " + flattening 463 + " can not be inverted (divide by zero) using a sphere as ellipsoid" ); 464 } 465 } else { 466 tmpValue = params.remove( "b" ); 467 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 468 semiMinorAxis = Double.parseDouble( tmpValue ); 469 } 470 } 471 } 472 } 473 } 474 } 475 476 /** 477 * 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 478 * are of future value?. <code> 479 * private final static double SIXTH = .1666666666666666667; // 1/6 480 * private final static double RA4 = .04722222222222222222; // 17/360 481 * private final static double RA6 = .02215608465608465608; // 67/3024 482 * private final static double RV4 = .06944444444444444444; // 5/72 483 * private final static double RV6 = .04243827160493827160; // 55/1296 484 * </code> 485 */ 486 // String tmpValue = params.get( "R_A" ); 487 // if ( tmpValue != null && Boolean.getBoolean( tmpValue ) ) { 488 // semiMajorAxis *= 1. - eccentricitySquared 489 // * ( SIXTH + eccentricitySquared * ( RA4 + eccentricitySquared * RA6 ) ); 490 // } else { 491 // tmpValue = params.get( "R_V" ); 492 // if ( tmpValue != null && Boolean.getBoolean( tmpValue ) ) { 493 // semiMajorAxis *= 1. - eccentricitySquared 494 // * ( SIXTH + eccentricitySquared * ( RV4 + eccentricitySquared * RV6 ) ); 495 // } else { 496 // tmpValue = params.get( "R_a" ); 497 // if ( tmpValue != null && Boolean.getBoolean( tmpValue ) ) { 498 // semiMajorAxis = .5 * ( semiMajorAxis + semiMinorAxis ); 499 // } else { 500 // tmpValue = params.get( "R_g" ); 501 // if ( tmpValue != null && Boolean.getBoolean( tmpValue ) ) { 502 // semiMajorAxis = Math.sqrt( semiMajorAxis * semiMinorAxis ); 503 // } else { 504 // tmpValue = params.get( "R_h" ); 505 // if ( tmpValue != null && Boolean.getBoolean( tmpValue ) ) { 506 // semiMajorAxis = 2. * semiMajorAxis * semiMinorAxis / ( semiMajorAxis + semiMinorAxis 507 // ); 508 // eccentricitySquared = 0.; 509 // } else { 510 // tmpValue = params.get( "R_lat_a" ); 511 // if ( tmpValue != null ) { 512 // double tmp = Math.sin( parseAngle( tmpValue ) ); 513 // if ( Math.abs( tmp ) > HALFPI ) 514 // throw new CRSConfigurationException( "-11" ); 515 // tmp = 1. - eccentricitySquared * tmp * tmp; 516 // semiMajorAxis *= .5 * ( 1. - eccentricitySquared + tmp ) / ( tmp * Math.sqrt( tmp ) 517 // ); 518 // eccentricitySquared = 0.; 519 // } else { 520 // tmpValue = params.get( "R_lat_g" ); 521 // if ( tmpValue != null ) { 522 // double tmp = Math.sin( parseAngle( tmpValue ) ); 523 // if ( Math.abs( tmp ) > HALFPI ) 524 // throw new CRSConfigurationException( "-11" ); 525 // tmp = 1. - eccentricitySquared * tmp * tmp; 526 // semiMajorAxis *= Math.sqrt( 1. - eccentricitySquared ) / tmp; 527 // eccentricitySquared = 0.; 528 // } 529 // } 530 // } 531 // } 532 // } 533 // } 534 // } 535 if ( Double.isNaN( semiMajorAxis ) ) { 536 throw new CRSConfigurationException( 537 Messages.getMessage( 538 "CRS_CONFIG_PROJ4_ELLIPSOID_WITHOUT_SEMIMAJOR", 539 params.get( EPSG_PRE + "identifier" ) ) ); 540 } 541 String id = "ELLIPSOID_" + ellipsCount++; 542 String description = "Handmade proj4 ellipsoid definition (parsed from nad/epsg) used by crs with id: " 543 + params.get( "identifier" ); 544 String name = "Proj4 defined ellipsoid"; 545 546 if ( !Double.isNaN( eccentricitySquared ) ) { 547 result = new Ellipsoid( semiMajorAxis, Math.sqrt( eccentricitySquared ), Unit.METRE, id, name, version, 548 description, areaOfUse ); 549 } else if ( !Double.isNaN( eccentricity ) ) { 550 result = new Ellipsoid( semiMajorAxis, eccentricity, Unit.METRE, id, name, version, description, 551 areaOfUse ); 552 } else if ( !Double.isNaN( inverseFlattening ) ) { 553 result = new Ellipsoid( semiMajorAxis, Unit.METRE, inverseFlattening, id, name, version, description, 554 areaOfUse ); 555 } else if ( !Double.isNaN( semiMinorAxis ) ) { 556 result = new Ellipsoid( Unit.METRE, semiMajorAxis, semiMinorAxis, id, name, version, description, 557 areaOfUse ); 558 } else { 559 LOG.logDebug( "Only a semimajor defined, assuming a sphere (instead of an ellipsoid) is to be created." ); 560 result = new Ellipsoid( Unit.METRE, semiMajorAxis, semiMajorAxis, id, name, version, description, 561 areaOfUse ); 562 } 563 } 564 return result; 565 } 566 567 /** 568 * @param datumName 569 * of the datum to map. 570 * @param definedEllipsoid 571 * the ellipsoid to use (gotten from 'ellps' param) or <code>null</code> if a predefined ellipsoid 572 * should be used. 573 * @return a Geodetic datum or <code>null</code> if the given name was null or empty. 574 * @throws CRSConfigurationException 575 */ 576 private GeodeticDatum getPredefinedDatum( String datumName, String definedEllipsoid, String crsID ) 577 throws CRSConfigurationException { 578 if ( datumName != null && !"".equals( datumName.trim() ) ) { 579 datumName = datumName.trim(); 580 String[] datumIDs = null; 581 String[] datumNames = null; 582 String[] datumDescriptions = null; 583 String[] datumVersions = null; 584 String[] datumAOU = null; 585 Helmert confInfo = new Helmert( GeographicCRS.WGS84, GeographicCRS.WGS84, "Created by proj4 CRSProvider" ); 586 Ellipsoid ellipsoid = null; 587 if ( "GGRS87".equalsIgnoreCase( datumName ) ) { 588 confInfo = new Helmert( -199.87, 74.79, 246.62, 0, 0, 0, 0, GeographicCRS.WGS84, GeographicCRS.WGS84, 589 getPredefinedIDs( "1272" ), new String[] {}, null, null, null ); 590 591 datumIDs = getPredefinedIDs( "6121" ); 592 datumNames = new String[] { "Greek_Geodetic_Reference_System_1987" }; 593 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 594 || "GRS80".equals( definedEllipsoid.trim() ) ) { 595 ellipsoid = getPredefinedEllipsoid( "GRS80" ); 596 } else { 597 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 598 } 599 } else if ( "NAD27".equalsIgnoreCase( datumName ) ) { 600 confInfo = new Helmert( -8, 160, 176, 0, 0, 0, 0, GeographicCRS.WGS84, GeographicCRS.WGS84, 601 getPredefinedIDs( "1173" ), new String[] { "North_American_Datum_1983" }, null, 602 null, null ); 603 604 datumIDs = getPredefinedIDs( "6267" ); 605 datumNames = new String[] { "North_American_Datum_1927" }; 606 // don't no how to do this. 607 // "nadgrids=@conus,@alaska,@ntv2_0.gsb,@ntv1_can.dat"; 608 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 609 || "clrk66".equals( definedEllipsoid.trim() ) ) { 610 ellipsoid = getPredefinedEllipsoid( "clrk66" ); 611 } else { 612 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 613 } 614 } else if ( "NAD83".equalsIgnoreCase( datumName ) ) { 615 confInfo = new Helmert( GeographicCRS.WGS84, GeographicCRS.WGS84, getPredefinedIDs( "1188" ), null, 616 null, new String[] { "Derived at 312 stations." }, 617 new String[] { "North America - all Canada and USA subunits" } ); 618 datumIDs = getPredefinedIDs( "6269" ); 619 datumNames = new String[] { "North_American_Datum_1983" }; 620 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 621 || "GRS80".equals( definedEllipsoid.trim() ) ) { 622 ellipsoid = getPredefinedEllipsoid( "GRS80" ); 623 } else { 624 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 625 } 626 } else if ( "OSGB36".equalsIgnoreCase( datumName ) ) { 627 confInfo = new Helmert( 628 446.448, 629 -125.157, 630 542.060, 631 0.1502, 632 0.2470, 633 0.8421, 634 -20.4894, 635 GeographicCRS.WGS84, 636 GeographicCRS.WGS84, 637 getPredefinedIDs( "1314" ), 638 null, 639 null, 640 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." }, 641 new String[] { "United Kingdom (UK) - Great Britain and UKCS" } ); 642 datumIDs = getPredefinedIDs( "6001" ); 643 datumNames = new String[] { "Airy 1830" }; 644 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 645 || "airy".equals( definedEllipsoid.trim() ) ) { 646 ellipsoid = getPredefinedEllipsoid( "airy" ); 647 } else { 648 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 649 } 650 } else if ( "WGS84".equalsIgnoreCase( datumName ) ) { 651 return GeodeticDatum.WGS84; 652 } else if ( "carthage".equalsIgnoreCase( datumName ) ) { 653 confInfo = new Helmert( -263.0, 6.0, 431.0, 0, 0, 0, 0, GeographicCRS.WGS84, GeographicCRS.WGS84, 654 getPredefinedIDs( "1130" ), null, null, 655 new String[] { "Derived at 5 stations." }, new String[] { "Tunisia" } ); 656 datumIDs = getPredefinedIDs( "6816" ); 657 datumNames = new String[] { "Carthage 1934 Tunisia" }; 658 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 659 || "clark80".equals( definedEllipsoid.trim() ) ) { 660 ellipsoid = getPredefinedEllipsoid( "clark80" ); 661 } else { 662 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 663 } 664 } else if ( "hermannskogel".equalsIgnoreCase( datumName ) ) { 665 confInfo = new Helmert( 653.0, -212.0, 449.0, 0, 0, 0, 0, GeographicCRS.WGS84, GeographicCRS.WGS84, 666 new String[] { "kogel", EPSG_PRE + "1306" }, null, null, 667 new String[] { "No epsg code was found." }, null ); 668 datumIDs = new String[] { "Hermannskogel" }; 669 datumNames = new String[] { "some undefined proj4 datum" }; 670 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 671 || "bessel".equals( definedEllipsoid.trim() ) ) { 672 ellipsoid = getPredefinedEllipsoid( "bessel" ); 673 } else { 674 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 675 } 676 } else if ( "ire65".equalsIgnoreCase( datumName ) ) { 677 confInfo = new Helmert( 482.530, -130.596, 564.557, -1.042, -0.214, -0.631, 8.15, GeographicCRS.WGS84, 678 GeographicCRS.WGS84, new String[] { "ire65_conversion" }, null, null, 679 new String[] { "no epsg code was found" }, null ); 680 datumIDs = new String[] { "Ireland 1965" }; 681 datumNames = new String[] { "no epsg code was found." }; 682 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 683 || "mod_airy".equals( definedEllipsoid.trim() ) ) { 684 ellipsoid = getPredefinedEllipsoid( "mod_airy" ); 685 } else { 686 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 687 } 688 } else if ( "nzgd49".equalsIgnoreCase( datumName ) ) { 689 confInfo = new Helmert( 690 59.47, 691 -5.04, 692 187.44, 693 0.47, 694 -0.1, 695 1.024, 696 -4.5993, 697 GeographicCRS.WGS84, 698 GeographicCRS.WGS84, 699 getPredefinedIDs( "1564" ), 700 new String[] { "NZGD49 to WGS 84 (2)" }, 701 new String[] { "OSG-Nzl 4m" }, 702 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)." }, 703 new String[] { "New Zealand" } ); 704 datumIDs = getPredefinedIDs( "6272" ); 705 datumNames = new String[] { "New Zealand Geodetic Datum 1949" }; 706 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 707 || "intl".equals( definedEllipsoid.trim() ) ) { 708 ellipsoid = getPredefinedEllipsoid( "intl" ); 709 } else { 710 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 711 } 712 } else if ( "potsdam".equalsIgnoreCase( datumName ) ) { 713 if ( ( crsID != null && !"".equals( crsID ) ) && "3068".equals( crsID ) || "4314".equals( crsID ) 714 || "31466".equals( crsID ) || "31467".equals( crsID ) || "31468".equals( crsID ) 715 || "31469".equals( crsID ) ) { 716 confInfo = new Helmert( 717 598.1, 718 73.7, 719 418.2, 720 0.202, 721 0.045, 722 -2.455, 723 6.7, 724 GeographicCRS.WGS84, 725 GeographicCRS.WGS84, 726 getPredefinedIDs( "1777" ), 727 new String[] { "DHDN to WGS 84" }, 728 new String[] { "EPSG-Deu W 3m" }, 729 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)." }, 730 new String[] { "Germany - states of former West Germany - Baden-Wurtemberg, Bayern, Hessen, Niedersachsen, Nordrhein-Westfalen, Rheinland-Pfalz, Saarland, Schleswig-Holstein." } ); 731 datumIDs = getPredefinedIDs( "6314" ); 732 datumNames = new String[] { "Deutsches Hauptdreiecksnetz" }; 733 datumVersions = new String[] { "2006-06-12" }; 734 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." }; 735 datumAOU = new String[] { "Germany - states of former West Germany - Baden-Wurtemberg, Bayern, Hessen, Niedersachsen, Nordrhein-Westfalen, Rheinland-Pfalz, Saarland, Schleswig-Holstein." }; 736 } else { 737 confInfo = new Helmert( 738 606.0, 739 23.0, 740 413.0, 741 0, 742 0, 743 0, 744 0, 745 GeographicCRS.WGS84, 746 GeographicCRS.WGS84, 747 getPredefinedIDs( "15955" ), 748 new String[] { "RD/83 to WGS 84 (1)" }, 749 new String[] { "OGP-Deu BeTA2007" }, 750 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." }, 751 new String[] { "Germany-Sachsen" } ); 752 datumIDs = getPredefinedIDs( "6746" ); 753 datumNames = new String[] { "Potsdam Rauenberg 1950 DHDN" }; 754 } 755 756 if ( definedEllipsoid == null || "".equals( definedEllipsoid ) 757 || "bessel".equals( definedEllipsoid.trim() ) ) { 758 ellipsoid = getPredefinedEllipsoid( "bessel" ); 759 } else { 760 ellipsoid = getPredefinedEllipsoid( definedEllipsoid ); 761 } 762 } else { 763 return null; 764 } 765 766 return new GeodeticDatum( ellipsoid, PrimeMeridian.GREENWICH, confInfo, datumIDs, datumNames, 767 datumVersions, datumDescriptions, datumAOU ); 768 } 769 return null; 770 } 771 772 private String[] getPredefinedIDs( String idNumber ) { 773 return new String[] { EPSG_PRE + idNumber, OGC_URN + idNumber, OPENGIS_URL + idNumber, OPENGIS_URN + idNumber }; 774 } 775 776 /** 777 * This method was adopted from the com.jhlabs.map.proj.Projection Factory#initizalize method. 778 * 779 * @param projName 780 * name of the projection 781 * @return the deegree understandable String or <code>null</code> if the projName could not be mapped. 782 * @throws CRSConfigurationException 783 * if the projName could not be mapped. 784 */ 785 private Projection createProjection( String projName, GeographicCRS geoCRS, Map<String, String> params ) 786 throws CRSConfigurationException { 787 Projection result = null; 788 // in degrees 789 double projectionLatitude = 0; 790 double projectionLongitude = 0; 791 double firstParallelLatitude = Double.NaN; 792 double secondParallelLatitude = Double.NaN; 793 double trueScaleLatitude = Double.NaN; 794 795 // meter 796 double falseNorthing = 0; 797 double falseEasting = 0; 798 double scale = 1; 799 800 String s = params.remove( "lat_0" ); 801 if ( s != null && !"".equals( s.trim() ) ) { 802 projectionLatitude = parseAngleFormat( s, false ); 803 } 804 805 s = params.remove( "lon_0" ); 806 if ( s != null && !"".equals( s.trim() ) ) { 807 projectionLongitude = parseAngleFormat( s, false ); 808 } 809 Point2d naturalOrigin = new Point2d( projectionLongitude, projectionLatitude ); 810 811 s = params.remove( "lat_1" ); 812 if ( s != null && !"".equals( s.trim() ) ) { 813 firstParallelLatitude = parseAngleFormat( s, false ); 814 } 815 816 s = params.remove( "lat_2" ); 817 if ( s != null && !"".equals( s.trim() ) ) { 818 secondParallelLatitude = parseAngleFormat( s, false ); 819 } 820 821 s = params.remove( "lat_ts" ); 822 if ( s != null && !"".equals( s.trim() ) ) { 823 trueScaleLatitude = parseAngleFormat( s, false ); 824 } 825 826 s = params.remove( "x_0" ); 827 if ( s != null && !"".equals( s.trim() ) ) { 828 falseEasting = Double.parseDouble( s ); 829 } 830 s = params.remove( "y_0" ); 831 if ( s != null && !"".equals( s.trim() ) ) { 832 falseNorthing = Double.parseDouble( s ); 833 } 834 835 s = params.remove( "k_0" ); 836 if ( s == null ) { 837 s = params.remove( "k" ); 838 } 839 if ( s != null && !"".equals( s.trim() ) ) { 840 scale = Double.parseDouble( s ); 841 } 842 843 Unit units = createUnit( params ); 844 845 if ( projName != null && !"".equals( projName ) ) { 846 projName = projName.trim(); 847 if ( "aea".equals( projName ) ) {// "Albers Equal Area" 848 } else if ( "aeqd".equals( projName ) ) {// "Azimuthal Equidistant" 849 } else if ( "airy".equals( projName ) ) {// "Airy" 850 } else if ( "aitoff".equals( projName ) ) {// "Aitoff" 851 } else if ( "alsk".equals( projName ) ) {// "Mod. Stereographics of Alaska" 852 } else if ( "apian".equals( projName ) ) {// "Apian Globular I" 853 } else if ( "august".equals( projName ) ) {// "August Epicycloidal" 854 } else if ( "bacon".equals( projName ) ) {// "Bacon Globular" 855 } else if ( "bipc".equals( projName ) ) {// "Bipolar conic of western hemisphere" 856 } else if ( "boggs".equals( projName ) ) {// "Boggs Eumorphic" 857 } else if ( "bonne".equals( projName ) ) {// "Bonne (Werner lat_1=90)" 858 } else if ( "cass".equals( projName ) ) {// "Cassini" 859 } else if ( "cc".equals( projName ) ) {// "Central Cylindrical" 860 } else if ( "cea".equals( projName ) ) {// "Equal Area Cylindrical" 861 } else if ( "chamb".equals( projName ) ) {// "Chamberlin Trimetric" 862 } else if ( "collg".equals( projName ) ) {// "Collignon" 863 } else if ( "crast".equals( projName ) ) {// "Craster Parabolic (Putnins P4)" 864 } else if ( "denoy".equals( projName ) ) {// "Denoyer Semi-Elliptical" 865 } else if ( "eck1".equals( projName ) ) {// "Eckert I" 866 } else if ( "eck2".equals( projName ) ) {// "Eckert II" 867 } else if ( "eck3".equals( projName ) ) {// "Eckert III" 868 } else if ( "eck4".equals( projName ) ) {// "Eckert IV" 869 } else if ( "eck5".equals( projName ) ) {// "Eckert V" 870 } else if ( "eck6".equals( projName ) ) {// "Eckert VI" 871 } else if ( "eqc".equals( projName ) ) {// "Equidistant Cylindrical (Plate Caree)" 872 } else if ( "eqdc".equals( projName ) ) {// "Equidistant Conic" 873 } else if ( "euler".equals( projName ) ) {// "Euler" 874 } else if ( "fahey".equals( projName ) ) {// "Fahey" 875 } else if ( "fouc".equals( projName ) ) {// "Foucaut" 876 } else if ( "fouc_s".equals( projName ) ) {// "Foucaut Sinusoidal" 877 } else if ( "gall".equals( projName ) ) {// "Gall (Gall Stereographic)" 878 } else if ( "gins8".equals( projName ) ) {// "Ginsburg VIII (TsNIIGAiK)" 879 } else if ( "gn_sinu".equals( projName ) ) {// "General Sinusoidal Series" 880 } else if ( "gnom".equals( projName ) ) {// "Gnomonic" 881 } else if ( "goode".equals( projName ) ) {// "Goode Homolosine" 882 } else if ( "gs48".equals( projName ) ) {// "Mod. Stererographics of 48 U.S." 883 } else if ( "gs50".equals( projName ) ) {// "Mod. Stererographics of 50 U.S." 884 } else if ( "hammer".equals( projName ) ) {// "Hammer & Eckert-Greifendorff" 885 } else if ( "hatano".equals( projName ) ) {// "Hatano Asymmetrical Equal Area" 886 } else if ( "imw_p".equals( projName ) ) {// "Internation Map of the World Polyconic" 887 } else if ( "kav5".equals( projName ) ) {// "Kavraisky V" 888 } else if ( "kav7".equals( projName ) ) {// "Kavraisky VII" 889 } else if ( "labrd".equals( projName ) ) {// "Laborde" 890 } else if ( "laea".equals( projName ) ) {// "Lambert Azimuthal Equal Area" 891 result = new LambertAzimuthalEqualArea( geoCRS, falseNorthing, falseEasting, naturalOrigin, units, 892 scale ); 893 } else if ( "lagrng".equals( projName ) ) {// "Lagrange" 894 } else if ( "larr".equals( projName ) ) {// "Larrivee" 895 } else if ( "lask".equals( projName ) ) {// "Laskowski" 896 } else if ( "latlong".equals( projName ) ) {// "Lat/Long" 897 } else if ( "lcc".equals( projName ) ) {// "Lambert Conformal Conic" 898 result = new LambertConformalConic( firstParallelLatitude, secondParallelLatitude, geoCRS, 899 falseNorthing, falseEasting, naturalOrigin, units, scale ); 900 } else if ( "leac".equals( projName ) ) {// "Lambert Equal Area Conic" 901 } else if ( "lee_os".equals( projName ) ) {// "Lee Oblated Stereographic" 902 } else if ( "loxim".equals( projName ) ) {// "Loximuthal" 903 } else if ( "lsat".equals( projName ) ) {// "Space oblique for LANDSAT" 904 } else if ( "mbt_s".equals( projName ) ) {// "McBryde-Thomas Flat-Polar Sine" 905 } else if ( "mbt_fps".equals( projName ) ) {// "McBryde-Thomas Flat-Pole Sine (No. 2)" 906 } else if ( "mbtfpp".equals( projName ) ) {// "McBride-Thomas Flat-Polar Parabolic" 907 } else if ( "mbtfpq".equals( projName ) ) {// "McBryde-Thomas Flat-Polar Quartic" 908 } else if ( "mbtfps".equals( projName ) ) {// "McBryde-Thomas Flat-Polar Sinusoidal" 909 } else if ( "merc".equals( projName ) ) {// "Mercator" 910 } else if ( "mil_os".equals( projName ) ) {// "Miller Oblated Stereographic" 911 } else if ( "mill".equals( projName ) ) {// "Miller Cylindrical" 912 } else if ( "mpoly".equals( projName ) ) {// "Modified Polyconic" 913 } else if ( "moll".equals( projName ) ) {// "Mollweide" 914 } else if ( "murd1".equals( projName ) ) {// "Murdoch I" 915 } else if ( "murd2".equals( projName ) ) {// "Murdoch II" 916 } else if ( "murd3".equals( projName ) ) {// "Murdoch III" 917 } else if ( "nell".equals( projName ) ) {// "Nell" 918 } else if ( "nell_h".equals( projName ) ) {// "Nell-Hammer" 919 } else if ( "nicol".equals( projName ) ) {// "Nicolosi Globular" 920 } else if ( "nsper".equals( projName ) ) {// "Near-sided perspective" 921 } else if ( "nzmg".equals( projName ) ) {// "New Zealand Map Grid" 922 } else if ( "ob_tran".equals( projName ) ) {// "General Oblique Transformation" 923 } else if ( "ocea".equals( projName ) ) {// "Oblique Cylindrical Equal Area" 924 } else if ( "oea".equals( projName ) ) {// "Oblated Equal Area" 925 } else if ( "omerc".equals( projName ) ) {// "Oblique Mercator" 926 } else if ( "ortel".equals( projName ) ) {// "Ortelius Oval" 927 } else if ( "ortho".equals( projName ) ) {// "Orthographic" 928 } else if ( "pconic".equals( projName ) ) {// "Perspective Conic" 929 } else if ( "poly".equals( projName ) ) {// "Polyconic (American)" 930 } else if ( "putp1".equals( projName ) ) {// "Putnins P1" 931 } else if ( "putp2".equals( projName ) ) {// "Putnins P2" 932 } else if ( "putp3".equals( projName ) ) {// "Putnins P3" 933 } else if ( "putp3p".equals( projName ) ) {// "Putnins P3'" 934 } else if ( "putp4p".equals( projName ) ) {// "Putnins P4'" 935 } else if ( "putp5".equals( projName ) ) {// "Putnins P5" 936 } else if ( "putp5p".equals( projName ) ) {// "Putnins P5'" 937 } else if ( "putp6".equals( projName ) ) {// "Putnins P6" 938 } else if ( "putp6p".equals( projName ) ) {// "Putnins P6'" 939 } else if ( "qua_aut".equals( projName ) ) {// "Quartic Authalic" 940 } else if ( "robin".equals( projName ) ) {// "Robinson" 941 } else if ( "rpoly".equals( projName ) ) {// "Rectangular Polyconic" 942 } else if ( "sinu".equals( projName ) ) {// "Sinusoidal (Sanson-Flamsteed)" 943 } else if ( "somerc".equals( projName ) ) {// "Swiss. Obl. Mercator" 944 } else if ( "stere".equals( projName ) ) {// "Oblique Stereographic Alternative" 945 result = new StereographicAzimuthal( trueScaleLatitude, geoCRS, falseNorthing, falseEasting, 946 naturalOrigin, units, scale ); 947 } else if ( "sterea".equals( projName ) ) { 948 result = new StereographicAlternative( geoCRS, falseNorthing, falseEasting, naturalOrigin, units, scale ); 949 } else if ( "tcc".equals( projName ) ) {// "Transverse Central Cylindrical" 950 } else if ( "tcea".equals( projName ) ) {// "Transverse Cylindrical Equal Area" 951 } else if ( "tissot".equals( projName ) ) {// "Tissot Conic" 952 } else if ( "tmerc".equals( projName ) || "utm".equals( projName ) ) {// "Transverse 953 // Mercator" 954 s = params.remove( "south" ); 955 boolean north = ( s == null || "".equals( s.trim() ) ); 956 s = params.remove( "zone" ); 957 if ( s != null && !"".equals( s.trim() ) ) { 958 int zone = Integer.parseInt( s ); 959 result = new TransverseMercator( zone, north, geoCRS, units ); 960 } else { 961 result = new TransverseMercator( north, geoCRS, falseNorthing, falseEasting, naturalOrigin, units, 962 scale ); 963 } 964 } else if ( "tpeqd".equals( projName ) ) {// "Two Point Equidistant" 965 } else if ( "tpers".equals( projName ) ) {// "Tilted perspective" 966 } else if ( "ups".equals( projName ) ) {// "Universal Polar Stereographic" 967 } else if ( "urm5".equals( projName ) ) {// "Urmaev V" 968 } else if ( "urmfps".equals( projName ) ) {// "Urmaev Flat-Polar Sinusoidal" 969 } else if ( "utm".equals( projName ) ) {// "Universal Transverse Mercator (UTM)" 970 } else if ( "vandg".equals( projName ) ) {// "van der Grinten (I)" 971 } else if ( "vandg2".equals( projName ) ) {// "van der Grinten II" 972 } else if ( "vandg3".equals( projName ) ) {// "van der Grinten III" 973 } else if ( "vandg4".equals( projName ) ) {// "van der Grinten IV" 974 } else if ( "vitk1".equals( projName ) ) {// "Vitkovsky I" 975 } else if ( "wag1".equals( projName ) ) {// "Wagner I (Kavraisky VI)" 976 } else if ( "wag2".equals( projName ) ) {// "Wagner II" 977 } else if ( "wag3".equals( projName ) ) {// "Wagner III" 978 } else if ( "wag4".equals( projName ) ) {// "Wagner IV" 979 } else if ( "wag5".equals( projName ) ) {// "Wagner V" 980 } else if ( "wag6".equals( projName ) ) {// "Wagner VI" 981 } else if ( "wag7".equals( projName ) ) {// "Wagner VII" 982 } else if ( "weren".equals( projName ) ) {// "Werenskiold I" 983 } else if ( "wink1".equals( projName ) ) {// "Winkel I" 984 } else if ( "wink2".equals( projName ) ) {// "Winkel II" 985 } else if ( "wintri".equals( projName ) ) {// "Winkel Tripel" 986 } 987 if ( result == null ) { 988 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_UNKNOWN_PROJECTION", 989 projName ) ); 990 } 991 } 992 return result; 993 } 994 995 /** 996 * @param params 997 * the values to get the units or to_meter from. 998 * @return a unit create from the +unit parameter or Unit.METRE if not found. 999 * @throws CRSConfigurationException 1000 * if the given unit parameter could not be mapped to a valid deegree-crs unit. 1001 */ 1002 private Unit createUnit( Map<String, String> params ) 1003 throws CRSConfigurationException { 1004 Unit result = Unit.METRE; 1005 String tmpValue = params.remove( "units" ); 1006 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 1007 result = Unit.createUnitFromString( tmpValue ); 1008 if ( result == null ) { 1009 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_UNKNOWN_UNIT", 1010 params.get( EPSG_PRE + "identifier" ), 1011 tmpValue ) ); 1012 } 1013 } else { 1014 tmpValue = params.remove( "to_meter" ); 1015 if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) { 1016 result = new Unit( "Unknown", "unknown", Double.parseDouble( tmpValue ), Unit.METRE ); 1017 } 1018 } 1019 return result; 1020 } 1021 1022 /** 1023 * Maps the given proj4 name to an id (if any) and creates an ellipsoid accordingly. 1024 * 1025 * @param ellipsoidName 1026 * defined in the proj lib 1027 * @return an ellipsoid with an id and name or <code>null</code> if the ellipsoidName was null or empty. 1028 * @throws CRSConfigurationException 1029 * if the given name could not be mapped. 1030 */ 1031 private Ellipsoid getPredefinedEllipsoid( String ellipsoidName ) 1032 throws CRSConfigurationException { 1033 if ( ellipsoidName != null && !"".equals( ellipsoidName.trim() ) ) { 1034 ellipsoidName = ellipsoidName.trim(); 1035 double semiMajorAxis = 0; 1036 double semiMinorAxis = Double.NaN; 1037 double inverseFlattening = 1; 1038 String id = ellipsoidName; 1039 String name = ""; 1040 if ( "APL4.9".equalsIgnoreCase( ellipsoidName ) ) { 1041 semiMajorAxis = 6378137.0; 1042 inverseFlattening = 298.25; 1043 name = "Appl. Physics. 1965"; 1044 // no epsg 1045 } else if ( "CPM".equalsIgnoreCase( ellipsoidName ) ) { 1046 semiMajorAxis = 6375738.7; 1047 inverseFlattening = 334.29; 1048 name = "Comm. des Poids et Mesures 1799"; 1049 // no epsg 1050 } else if ( "GRS67".equalsIgnoreCase( ellipsoidName ) ) { 1051 semiMajorAxis = 6378160.0; 1052 inverseFlattening = 298.2471674270; 1053 name = "GRS 67(IUGG 1967)"; 1054 id = "7036"; 1055 } else if ( "GRS80".equalsIgnoreCase( ellipsoidName ) ) { 1056 semiMajorAxis = 6378137.0; 1057 inverseFlattening = 298.257222101; 1058 name = "GRS 1980(IUGG, 1980)"; 1059 id = "7019"; 1060 } else if ( "IAU76".equalsIgnoreCase( ellipsoidName ) ) { 1061 semiMajorAxis = 6378140.0; 1062 inverseFlattening = 298.257; 1063 name = "IAU 1976"; 1064 // probably clarke 1065 // no epsg 1066 } else if ( "MERIT".equalsIgnoreCase( ellipsoidName ) ) { 1067 semiMajorAxis = 6378137.0; 1068 inverseFlattening = 298.257; 1069 name = "MERIT 1983"; 1070 // no epsg 1071 } else if ( "NWL9D".equalsIgnoreCase( ellipsoidName ) ) { 1072 semiMajorAxis = 6378145.0; 1073 inverseFlattening = 298.25; 1074 name = "Naval Weapons Lab., 1965"; 1075 // no epsg 1076 } else if ( "SEasia".equalsIgnoreCase( ellipsoidName ) ) { 1077 semiMajorAxis = 6378155.0; 1078 semiMinorAxis = 6356773.3205; 1079 name = "Southeast Asia"; 1080 // no epsg 1081 } else if ( "SGS85".equalsIgnoreCase( ellipsoidName ) ) { 1082 semiMajorAxis = 6378136.0; 1083 inverseFlattening = 298.257; 1084 name = "Soviet Geodetic System 85"; 1085 // no epsg 1086 } else if ( "WGS60".equalsIgnoreCase( ellipsoidName ) ) { 1087 semiMajorAxis = 6378165.0; 1088 inverseFlattening = 298.3; 1089 name = "WGS 60"; 1090 // no epsg 1091 } else if ( "WGS66".equalsIgnoreCase( ellipsoidName ) ) { 1092 semiMajorAxis = 6378145.0; 1093 inverseFlattening = 298.25; 1094 name = "WGS 66"; 1095 // no epsg 1096 } else if ( "WGS72".equalsIgnoreCase( ellipsoidName ) ) { 1097 semiMajorAxis = 6378135.0; 1098 inverseFlattening = 298.26; 1099 name = "WGS 72"; 1100 id = "7043"; 1101 } else if ( "WGS84".equalsIgnoreCase( ellipsoidName ) ) { 1102 semiMajorAxis = 6378137.0; 1103 inverseFlattening = 298.257223563; 1104 name = "WGS 84"; 1105 id = "7030"; 1106 } else if ( "airy".equalsIgnoreCase( ellipsoidName ) ) { 1107 semiMajorAxis = 6377563.396; 1108 semiMinorAxis = 6356256.910; 1109 name = "Airy 1830"; 1110 id = "7001"; 1111 } else if ( "andrae".equalsIgnoreCase( ellipsoidName ) ) { 1112 semiMajorAxis = 6377104.43; 1113 inverseFlattening = 300.0; 1114 name = "Andrae 1876 (Den., Iclnd.)"; 1115 // no epsg 1116 } else if ( "aust_SA".equalsIgnoreCase( ellipsoidName ) ) { 1117 semiMajorAxis = 6378160.0; 1118 inverseFlattening = 298.25; 1119 name = "Australian Natl & S. Amer. 1969"; 1120 id = "7050"; 1121 } else if ( "bess_nam".equalsIgnoreCase( ellipsoidName ) ) { 1122 semiMajorAxis = 6377483.865; 1123 inverseFlattening = 299.1528128; 1124 name = "Bessel 1841 (Namibia)"; 1125 id = "7046"; 1126 } else if ( "bessel".equalsIgnoreCase( ellipsoidName ) ) { 1127 semiMajorAxis = 6377397.155; 1128 inverseFlattening = 299.1528128; 1129 name = "Bessel 1841"; 1130 id = "7004"; 1131 } else if ( "clrk66".equalsIgnoreCase( ellipsoidName ) ) { 1132 semiMajorAxis = 6378206.4; 1133 semiMinorAxis = 6356583.8; 1134 name = "Clarke 1866"; 1135 id = "7008"; 1136 } else if ( "clrk80".equalsIgnoreCase( ellipsoidName ) ) { 1137 semiMajorAxis = 6378249.145; 1138 inverseFlattening = 293.4663; 1139 name = "Clarke 1880 mod."; 1140 id = "7034"; 1141 } else if ( "delmbr".equalsIgnoreCase( ellipsoidName ) ) { 1142 semiMajorAxis = 6376428.; 1143 inverseFlattening = 311.5; 1144 name = "Delambre 1810 (Belgium)"; 1145 // epsg closed 1146 } else if ( "engelis".equalsIgnoreCase( ellipsoidName ) ) { 1147 semiMajorAxis = 6378136.05; 1148 inverseFlattening = 298.2566; 1149 name = "Engelis 1985"; 1150 // espg closed 1151 } else if ( "evrst30".equalsIgnoreCase( ellipsoidName ) ) { 1152 semiMajorAxis = 6377276.345; 1153 inverseFlattening = 300.8017; 1154 name = "Everest 1830"; 1155 id = "7042"; 1156 } else if ( "evrst48".equalsIgnoreCase( ellipsoidName ) ) { 1157 semiMajorAxis = 6377304.063; 1158 inverseFlattening = 300.8017; 1159 name = "Everest 1948"; 1160 id = "7018"; 1161 } else if ( "evrst56".equalsIgnoreCase( ellipsoidName ) ) { 1162 semiMajorAxis = 6377301.243; 1163 inverseFlattening = 300.8017; 1164 name = "Everest 1956"; 1165 id = "7044"; 1166 } else if ( "evrst69".equalsIgnoreCase( ellipsoidName ) ) { 1167 semiMajorAxis = 6377295.664; 1168 inverseFlattening = 300.8017; 1169 name = "Everest 1969"; 1170 id = "7056"; 1171 } else if ( "evrstSS".equalsIgnoreCase( ellipsoidName ) ) { 1172 semiMajorAxis = 6377298.556; 1173 inverseFlattening = 300.8017; 1174 name = "Everest (Sabah & Sarawak)"; 1175 id = "7016"; 1176 } else if ( "fschr60".equalsIgnoreCase( ellipsoidName ) ) { 1177 semiMajorAxis = 6378166.; 1178 inverseFlattening = 298.3; 1179 name = "Fischer (Mercury Datum) 1960"; 1180 // epsg closed 1181 } else if ( "fschr60m".equalsIgnoreCase( ellipsoidName ) ) { 1182 semiMajorAxis = 6378155.; 1183 inverseFlattening = 298.3; 1184 name = "Modified Fischer 1960"; 1185 // epsg closed 1186 } else if ( "fschr68".equalsIgnoreCase( ellipsoidName ) ) { 1187 semiMajorAxis = 6378150.; 1188 inverseFlattening = 298.3; 1189 name = "Fischer 1968"; 1190 // epsg closed 1191 } else if ( "helmert".equalsIgnoreCase( ellipsoidName ) ) { 1192 semiMajorAxis = 6378200.; 1193 inverseFlattening = 298.3; 1194 name = "Helmert 1906"; 1195 id = "7020"; 1196 } else if ( "hough".equalsIgnoreCase( ellipsoidName ) ) { 1197 semiMajorAxis = 6378270.0; 1198 inverseFlattening = 297.; 1199 name = "Hough"; 1200 id = "7053"; 1201 } else if ( "intl".equalsIgnoreCase( ellipsoidName ) ) { 1202 semiMajorAxis = 6378388.0; 1203 inverseFlattening = 297.; 1204 name = "International 1909 (Hayford)"; 1205 id = "7022"; 1206 } else if ( "kaula".equalsIgnoreCase( ellipsoidName ) ) { 1207 semiMajorAxis = 6378163.; 1208 inverseFlattening = 298.24; 1209 name = "Kaula 1961"; 1210 // no epsg 1211 } else if ( "krass".equalsIgnoreCase( ellipsoidName ) ) { 1212 semiMajorAxis = 6378245.0; 1213 inverseFlattening = 298.3; 1214 name = "Krassowsky, 1942"; 1215 id = "7024"; 1216 } else if ( "lerch".equalsIgnoreCase( ellipsoidName ) ) { 1217 semiMajorAxis = 6378139.; 1218 inverseFlattening = 298.257; 1219 name = "Lerch 1979"; 1220 // no epsg 1221 } else if ( "mod_airy".equalsIgnoreCase( ellipsoidName ) ) { 1222 semiMajorAxis = 6377340.189; 1223 semiMinorAxis = 6356034.446; 1224 name = "Modified Airy"; 1225 id = "7002"; 1226 } else if ( "mprts".equalsIgnoreCase( ellipsoidName ) ) { 1227 semiMajorAxis = 6397300.; 1228 inverseFlattening = 191.; 1229 name = "Maupertius 1738"; 1230 // no epsg 1231 } else if ( "new_intl".equalsIgnoreCase( ellipsoidName ) ) { 1232 semiMajorAxis = 6378157.5; 1233 semiMinorAxis = 6356772.2; 1234 name = "New International 1967"; 1235 id = "7036"; 1236 } else if ( "plessis".equalsIgnoreCase( ellipsoidName ) ) { 1237 semiMajorAxis = 6376523.; 1238 semiMinorAxis = 6355863.; 1239 name = "Plessis 1817 (France)"; 1240 id = "7027"; 1241 } else if ( "sphere".equalsIgnoreCase( ellipsoidName ) ) { 1242 semiMajorAxis = 6370997.0; 1243 semiMinorAxis = 6370997.0; 1244 name = "Normal Sphere (r=6370997)"; 1245 } else if ( "walbeck".equalsIgnoreCase( ellipsoidName ) ) { 1246 semiMajorAxis = 6376896.0; 1247 semiMinorAxis = 6355834.8467; 1248 name = "Walbeck"; 1249 // epsg closed 1250 } else { 1251 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_UNKNOWN_ELLIPSOID", 1252 ellipsoidName ) ); 1253 } 1254 String[] ids = new String[] { id }; 1255 if ( !ellipsoidName.equals( id ) ) { 1256 ids = new String[] { EPSG_PRE + id, OGC_URN + id, OPENGIS_URL + id, OPENGIS_URN + id }; 1257 } 1258 Ellipsoid ellips = null; 1259 if ( Double.isNaN( semiMinorAxis ) ) { 1260 ellips = new Ellipsoid( semiMajorAxis, Unit.METRE, inverseFlattening, ids, new String[] { name }, null, 1261 null, null ); 1262 } else {// semiMinorAxis was given. 1263 ellips = new Ellipsoid( Unit.METRE, semiMajorAxis, semiMinorAxis, ids, new String[] { name }, null, 1264 null, null ); 1265 } 1266 return ellips; 1267 } 1268 return null; 1269 } 1270 1271 /** 1272 * Helper method to parse day, month second formats. With a little help from com.jhlabs.map.AngleFormat 1273 * 1274 * @param text 1275 * to pe parsed into degrees (or radians). 1276 * @param toDegrees 1277 * if the given text is in degrees 1278 * @return a 1279 */ 1280 private double parseAngleFormat( String text, boolean toDegrees ) { 1281 double d = 0, m = 0, s = 0; 1282 double result; 1283 boolean negate = false; 1284 int length = text.length(); 1285 if ( length > 0 ) { 1286 char c = Character.toUpperCase( text.charAt( length - 1 ) ); 1287 switch ( c ) { 1288 case 'W': 1289 case 'S': 1290 negate = true; 1291 case 'E': 1292 case 'N': 1293 text = text.substring( 0, length - 1 ); 1294 break; 1295 } 1296 } 1297 int i = text.indexOf( 'd' ); 1298 if ( i == -1 ) 1299 i = text.indexOf( '\u00b0' ); 1300 if ( i != -1 ) { 1301 String dd = text.substring( 0, i ); 1302 String mmss = text.substring( i + 1 ); 1303 d = Double.valueOf( dd ).doubleValue(); 1304 i = mmss.indexOf( 'm' ); 1305 if ( i == -1 ) 1306 i = mmss.indexOf( '\'' ); 1307 if ( i != -1 ) { 1308 if ( i != 0 ) { 1309 String mm = mmss.substring( 0, i ); 1310 m = Double.valueOf( mm ).doubleValue(); 1311 } 1312 if ( mmss.endsWith( "s" ) || mmss.endsWith( "\"" ) ) 1313 mmss = mmss.substring( 0, mmss.length() - 1 ); 1314 if ( i != mmss.length() - 1 ) { 1315 String ss = mmss.substring( i + 1 ); 1316 s = Double.valueOf( ss ).doubleValue(); 1317 } 1318 if ( m < 0 || m > 59 ) 1319 throw new NumberFormatException( "Minutes must be between 0 and 59" ); 1320 if ( s < 0 || s >= 60 ) 1321 throw new NumberFormatException( "Seconds must be between 0 and 59" ); 1322 } else if ( i != 0 ) { 1323 m = Double.valueOf( mmss ).doubleValue(); 1324 } 1325 if ( toDegrees ) { 1326 result = dmsToDeg( d, m, s ); 1327 } else { 1328 result = dmsToRad( d, m, s ); 1329 } 1330 } else { 1331 result = Double.parseDouble( text ); 1332 if ( !toDegrees ) 1333 result = Math.toRadians( result ); 1334 } 1335 if ( negate ) {// South 1336 result = -result; 1337 } 1338 return result; 1339 } 1340 1341 /** 1342 * Converts angle information to radians. With a little help from com.jhlabs.map.MapMath. For 1343 * negative angles, d should be negative, m & s positive. 1344 * 1345 * @param d 1346 * days 1347 * @param m 1348 * months 1349 * @param s 1350 * seconds. 1351 * @return the converted value in radians. 1352 */ 1353 private double dmsToRad( double d, double m, double s ) { 1354 if ( d >= 0 ) { 1355 return ( d + m / 60 + s / 3600 ) * Math.PI / 180.0; 1356 } 1357 return ( d - m / 60 - s / 3600 ) * Math.PI / 180.0; 1358 } 1359 1360 /** 1361 * Converts angle information to degrees. With a little help from com.jhlabs.map.MapMath. For 1362 * negative angles, d should be negative, m & s positive. 1363 * 1364 * @param d 1365 * days 1366 * @param m 1367 * minutes 1368 * @param s 1369 * seconds. 1370 * @return the converted value in degrees. 1371 */ 1372 private double dmsToDeg( double d, double m, double s ) { 1373 if ( d >= 0 ) { 1374 return ( d + m / 60 + s / 3600 ); 1375 } 1376 return ( d - m / 60 - s / 3600 ); 1377 } 1378 1379 public List<String[]> getSortedAvailableCRSIds() 1380 throws CRSConfigurationException { 1381 Set<String> keys = getResolver().getAvailableIDs(); 1382 List<String[]> result = new LinkedList<String[]>(); 1383 for ( String key : keys ) { 1384 result.add( new String[] { key } ); 1385 } 1386 return result; 1387 } 1388 1389 public List<String> getAvailableCRSIds() 1390 throws CRSConfigurationException { 1391 Set<String> keys = getResolver().getAvailableIDs(); 1392 List<String> result = new LinkedList<String>(); 1393 result.addAll( keys ); 1394 return result; 1395 } 1396 1397 public Identifiable getIdentifiable( String id ) 1398 throws CRSConfigurationException { 1399 Identifiable result = getCachedIdentifiable( id ); 1400 if ( result == null ) { 1401 throw new UnsupportedOperationException( 1402 "The retrieval of an arbitrary Identifiable Object is currently not supported by the proj 4 provider." ); 1403 } 1404 return result; 1405 } 1406 1407 @Override 1408 protected CoordinateSystem parseCoordinateSystem( Map<String, String> crsDefinition ) 1409 throws CRSConfigurationException { 1410 1411 String crsType = crsDefinition.remove( "proj" ); 1412 if ( crsType == null || "".equals( crsType.trim() ) ) { 1413 LOG.logDebug( "The given params contain: " + crsDefinition ); 1414 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJ4_NO_PROJ_PARAM", 1415 crsDefinition.get( EPSG_PRE + "identifier" ) ) ); 1416 } 1417 crsType = crsType.trim(); 1418 if ( "longlat".equals( crsType ) ) { 1419 // the geo-crs should find it's own id and has no parent projected crs (null, null). 1420 return createGeographicCRS( null, null, crsDefinition ); 1421 } 1422 return createProjectedCRS( crsType, crsDefinition ); 1423 } 1424 1425 @Override 1426 public Transformation parseTransformation( Map<String, String> transformationDefinition ) 1427 throws CRSConfigurationException { 1428 throw new UnsupportedOperationException( 1429 "Parsing of transformation parameters is not applicable for proj4 configuration files yet." ); 1430 } 1431 1432 public Transformation getTransformation( CoordinateSystem sourceCRS, CoordinateSystem targetCRS ) 1433 throws CRSConfigurationException { 1434 return getResolver().getTransformation( sourceCRS, targetCRS ); 1435 } 1436 1437 public List<Transformation> getTransformations() { 1438 throw new UnsupportedOperationException( 1439 "Parsing of transformations is not applicable for proj4 configuration files yet." ); 1440 } 1441 }