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.FileInputStream; 044 import java.io.FileNotFoundException; 045 import java.io.IOException; 046 import java.io.InputStream; 047 import java.io.InputStreamReader; 048 import java.io.Reader; 049 import java.net.MalformedURLException; 050 import java.util.ArrayList; 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.Identifiable; 059 import org.deegree.crs.components.Axis; 060 import org.deegree.crs.components.Ellipsoid; 061 import org.deegree.crs.components.GeodeticDatum; 062 import org.deegree.crs.components.PrimeMeridian; 063 import org.deegree.crs.components.Unit; 064 import org.deegree.crs.coordinatesystems.CoordinateSystem; 065 import org.deegree.crs.coordinatesystems.GeocentricCRS; 066 import org.deegree.crs.coordinatesystems.GeographicCRS; 067 import org.deegree.crs.coordinatesystems.ProjectedCRS; 068 import org.deegree.crs.exceptions.CRSConfigurationException; 069 import org.deegree.crs.projections.Projection; 070 import org.deegree.crs.projections.ProjectionUtils; 071 import org.deegree.crs.projections.azimuthal.LambertAzimuthalEqualArea; 072 import org.deegree.crs.projections.azimuthal.StereographicAzimuthal; 073 import org.deegree.crs.projections.conic.LambertConformalConic; 074 import org.deegree.crs.projections.cylindric.TransverseMercator; 075 import org.deegree.crs.transformations.WGS84ConversionInfo; 076 import org.deegree.datatypes.QualifiedName; 077 import org.deegree.framework.log.ILogger; 078 import org.deegree.framework.log.LoggerFactory; 079 import org.deegree.framework.xml.NamespaceContext; 080 import org.deegree.framework.xml.XMLFragment; 081 import org.deegree.framework.xml.XMLParsingException; 082 import org.deegree.framework.xml.XMLTools; 083 import org.deegree.i18n.Messages; 084 import org.deegree.ogcbase.CommonNamespaces; 085 import org.w3c.dom.Document; 086 import org.w3c.dom.Element; 087 import org.xml.sax.SAXException; 088 089 /** 090 * The <code>DeegreeCRSProvider</code> reads the deegree crs-config (based on it's own xml-schema) and creates the 091 * CRS's (and their datums, conversion info's, ellipsoids and projections) if requested. 092 * <p> 093 * Attention, although urn's are case-sensitive, the deegreeCRSProvider is not. All incoming id's are toLowerCased! 094 * </p> 095 * 096 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> 097 * 098 * @author last edited by: $Author:$ 099 * 100 * @version $Revision:$, $Date:$ 101 * 102 */ 103 104 public class DeegreeCRSProvider implements CRSProvider { 105 106 private static ILogger LOG = LoggerFactory.getLogger( DeegreeCRSProvider.class ); 107 108 /** 109 * The standard configuration file, points to deegree-crs-configuration.xml. 110 */ 111 private static final String STANDARD_CONFIG = "deegree-crs-configuration.xml"; 112 113 /** 114 * The mamespaces used in deegree. 115 */ 116 private static NamespaceContext nsContext = CommonNamespaces.getNamespaceContext(); 117 118 /** 119 * The prefix to use. 120 */ 121 private final static String PRE = CommonNamespaces.CRS_PREFIX + ":"; 122 123 /** 124 * The namespace to use. 125 */ 126 private final static String CRS_URI = CommonNamespaces.CRSNS.toASCIIString(); 127 128 /** 129 * The EPSG-Database defines only 48 different ellipsoids, set to 60, will --probably-- result in no collisions. 130 */ 131 private final Map<String, Ellipsoid> ellipsoids = new HashMap<String, Ellipsoid>( 60 ); 132 133 /** 134 * The EPSG-Database defines over 400 different Geodetic Datums, set to 450, will --probably-- result in no 135 * collisions. 136 */ 137 private final Map<String, GeodeticDatum> datums = new HashMap<String, GeodeticDatum>( 450 ); 138 139 /** 140 * The EPSG-Database defines over 1100 different CoordinateTransformations, set to 1200, will --probably-- result in 141 * no collisions. 142 */ 143 private final Map<String, WGS84ConversionInfo> conversionInfos = new HashMap<String, WGS84ConversionInfo>( 1200 ); 144 145 /** 146 * Theoretically infinite prime meridians could be defined, let's set it to a more practical number of 42. 147 */ 148 private final Map<String, PrimeMeridian> primeMeridians = new HashMap<String, PrimeMeridian>( 42 ); 149 150 /** 151 * The EPSG-Database defines over 2960 different ProjectedCRS's, set to 3500, will --probably-- result in no 152 * collisions. 153 */ 154 private final Map<String, Projection> projections = new HashMap<String, Projection>( 3500 ); 155 156 /** 157 * The EPSG-Database defines over 2960 different ProjectedCRS's, set to 3500, will --probably-- result in no 158 * collisions. 159 */ 160 private final Map<String, ProjectedCRS> projectedCRSs = new HashMap<String, ProjectedCRS>( 3500 ); 161 162 /** 163 * The EPSG-Database defines over 490 different GeographicCRS's (geodetic), set to 600, will --probably-- result in 164 * no collisions. 165 */ 166 private final Map<String, GeographicCRS> geographicCRSs = new HashMap<String, GeographicCRS>( 600 ); 167 168 /** 169 * The EPSG-Database doesn't define GeocentricCRS's, set to 30, will --probably-- result in no collisions. 170 */ 171 private final Map<String, GeocentricCRS> geocentricCRSs = new HashMap<String, GeocentricCRS>( 30 ); 172 173 private final List<GeographicCRS> cachedGeoCRSs = new ArrayList<GeographicCRS>( 3000 ); 174 175 private final Map<String, String> doubleGeos = new HashMap<String, String>( 3000 ); 176 177 private final List<GeodeticDatum> cachedDatums = new ArrayList<GeodeticDatum>( 3000 ); 178 179 private final Map<String, String> doubleDatums = new HashMap<String, String>( 3000 ); 180 181 private final List<WGS84ConversionInfo> cachedToWGS = new ArrayList<WGS84ConversionInfo>( 3000 ); 182 183 private final Map<String, String> doubleToWGS = new HashMap<String, String>( 3000 ); 184 185 private final List<Ellipsoid> cachedEllipsoids = new ArrayList<Ellipsoid>( 3000 ); 186 187 private final Map<String, String> doubleEllipsoids = new HashMap<String, String>( 3000 ); 188 189 private final List<PrimeMeridian> cachedMeridans = new ArrayList<PrimeMeridian>( 3000 ); 190 191 private final Map<String, String> doubleMeridians = new HashMap<String, String>( 3000 ); 192 193 private final List<Projection> cachedProjections = new ArrayList<Projection>( 3000 ); 194 195 private final Map<String, String> doubleProjections = new HashMap<String, String>( 3000 ); 196 197 /** 198 * The root element of the deegree - crs - configuration. 199 */ 200 private Element rootElement; 201 202 private boolean checkForDoubleDefinition = false; 203 204 /** 205 * Empty constructor may only be used for exporting, other usage will result in undefined behavior. 206 */ 207 public DeegreeCRSProvider() { 208 Document doc = XMLTools.create(); 209 rootElement = doc.createElementNS( CommonNamespaces.CRSNS.toASCIIString(), PRE + "definitions" ); 210 rootElement = (Element) doc.importNode( rootElement, false ); 211 rootElement = (Element) doc.appendChild( rootElement ); 212 } 213 214 /** 215 * @param f 216 * to load coordinate system definitions from if null, the standard configuration file will be used, by 217 * searching '/' first and if unsuccessful in org.deegree.crs.configuration. 218 * @throws CRSConfigurationException 219 * if the give file or the default-crs-configuration.xml file could not be loaded. 220 */ 221 public DeegreeCRSProvider( File f ) throws CRSConfigurationException { 222 InputStream is = null; 223 if ( f == null ) { 224 LOG.logDebug( "No configuration file given, trying to load standard-crs-configurtiion.xml" ); 225 is = DeegreeCRSProvider.class.getResourceAsStream( "/" + STANDARD_CONFIG ); 226 if ( is == null ) { 227 is = DeegreeCRSProvider.class.getResourceAsStream( STANDARD_CONFIG ); 228 } 229 if ( is == null ) { 230 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_NO_DEFAULT_CONFIG_FOUND" ) ); 231 } 232 } else { 233 LOG.logDebug( "Trying to load configuration from file: " + f.getAbsoluteFile() ); 234 try { 235 is = new FileInputStream( f ); 236 } catch ( FileNotFoundException e ) { 237 throw new CRSConfigurationException( e ); 238 } 239 } 240 if ( is == null ) { 241 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_NO_CONFIG_FOUND" ) ); 242 } 243 Reader read = new BufferedReader( new InputStreamReader( is ) ); 244 try { 245 XMLFragment configDocument = new XMLFragment( read, XMLFragment.DEFAULT_URL ); 246 rootElement = configDocument.getRootElement(); 247 } catch ( MalformedURLException e ) { 248 throw new CRSConfigurationException( e ); 249 } catch ( IOException e ) { 250 throw new CRSConfigurationException( e ); 251 } catch ( SAXException e ) { 252 throw new CRSConfigurationException( e ); 253 } finally { 254 try { 255 read.close(); 256 } catch ( IOException e ) { 257 // could not close the stream, just leave it as it is. 258 } 259 } 260 } 261 262 public boolean canExport() { 263 return true; 264 } 265 266 public synchronized CoordinateSystem getCRSByID( String crsId ) 267 throws CRSConfigurationException { 268 if ( crsId != null && !"".equals( crsId.trim() ) ) { 269 270 crsId = crsId.toUpperCase().trim(); 271 LOG.logDebug( "Trying to load crs with id: " + crsId + " from cache." ); 272 if ( geographicCRSs.containsKey( crsId ) && geographicCRSs.get( crsId ) != null ) { 273 return geographicCRSs.get( crsId ); 274 } else if ( projectedCRSs.containsKey( crsId ) && projectedCRSs.get( crsId ) != null ) { 275 return projectedCRSs.get( crsId ); 276 } else if ( geocentricCRSs.containsKey( crsId ) && geocentricCRSs.get( crsId ) != null ) { 277 return geocentricCRSs.get( crsId ); 278 } 279 LOG.logDebug( "No crs with id: " + crsId + " found in cache." ); 280 if ( rootElement == null ) { 281 this.notifyAll(); 282 return null; 283 } 284 Element crsElement = getTopElementFromID( crsId ); 285 if ( crsElement == null ) { 286 LOG.logDebug( "The requested crs id: " + crsId 287 + " could not be mapped to a configured CoordinateSystem." ); 288 this.notifyAll(); 289 return null; 290 } 291 String crsType = crsElement.getLocalName(); 292 if ( crsType == null || "".equals( crsType.trim() ) ) { 293 LOG.logDebug( "The requested crs id: " + crsId 294 + " could not be mapped to a configured CoordinateSystem." ); 295 this.notifyAll(); 296 return null; 297 } 298 if ( "geographicCRS".equalsIgnoreCase( crsType ) ) { 299 return parseGeographicCRS( crsElement ); 300 } else if ( "projectedCRS".equalsIgnoreCase( crsType ) ) { 301 return parseProjectedCRS( crsElement ); 302 } else if ( "geocentricCRS".equalsIgnoreCase( crsType ) ) { 303 return parseGeocentricCRS( crsElement ); 304 } 305 } 306 LOG.logDebug( "The id: " + crsId 307 + " could not be mapped to a valid deegreec-crs, currently projectedCRS, geographicCRS and geocentricCRS are supported." ); 308 return null; 309 } 310 311 public void export( StringBuilder sb, List<CoordinateSystem> crsToExport ) { 312 if ( crsToExport != null ) { 313 if ( crsToExport.size() != 0 ) { 314 LOG.logDebug( "Trying to export: " + crsToExport.size() + " coordinate systems." ); 315 XMLFragment frag = new XMLFragment( new QualifiedName( "crs", "definitions", CommonNamespaces.CRSNS ) ); 316 Element root = frag.getRootElement(); 317 ArrayList<String> exportedIDs = new ArrayList<String>( crsToExport.size() ); 318 for ( CoordinateSystem crs : crsToExport ) { 319 if ( crs.getType() == CoordinateSystem.GEOCENTRIC_CRS ) { 320 export( (GeocentricCRS) crs, root, exportedIDs ); 321 } else if ( crs.getType() == CoordinateSystem.GEOGRAPHIC_CRS ) { 322 export( (GeographicCRS) crs, root, exportedIDs ); 323 } else if ( crs.getType() == CoordinateSystem.PROJECTED_CRS ) { 324 export( (ProjectedCRS) crs, root, exportedIDs ); 325 } 326 } 327 root.normalize(); 328 Document validDoc = createValidDocument( root ); 329 try { 330 XMLFragment frag2 = new XMLFragment( validDoc, "http://www.deegree.org/crs" ); 331 sb.append( frag2.getAsPrettyString() ); 332 } catch ( MalformedURLException e ) { 333 LOG.logError( "Could not export crs definitions because: " + e.getMessage(), e ); 334 } 335 } else { 336 LOG.logWarning( "No coordinate system were given (list.size() == 0)." ); 337 } 338 } else { 339 LOG.logError( "No coordinate system were given (list == null)." ); 340 } 341 } 342 343 public List<CoordinateSystem> getAvailableCRSs() 344 throws CRSConfigurationException { 345 List<CoordinateSystem> allSystems = new ArrayList<CoordinateSystem>( 10000 ); 346 if ( rootElement != null ) { 347 List<Element> allCRSIDs = new ArrayList<Element>( 10000 ); 348 349 try { 350 allCRSIDs.addAll( XMLTools.getElements( rootElement, 351 "//" + PRE + "geographicCRS/" + PRE + "id[1]", 352 nsContext ) ); 353 allCRSIDs.addAll( XMLTools.getElements( rootElement, 354 "//" + PRE + "projectedCRS/" + PRE + "id[1]", 355 nsContext ) ); 356 allCRSIDs.addAll( XMLTools.getElements( rootElement, 357 "//" + PRE + "geocentricCRS/" + PRE + "id[1]", 358 nsContext ) ); 359 } catch ( XMLParsingException e ) { 360 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_GET_ALL_ELEMENT_IDS", 361 e.getMessage() ), e ); 362 } 363 for ( Element crsID : allCRSIDs ) { 364 if ( crsID != null ) { 365 String id = crsID.getTextContent(); 366 if ( id != null && !"".equals( id.trim() ) ) { 367 CoordinateSystem crs = getCRSByID( id ); 368 if ( crs != null ) { 369 allSystems.add( crs ); 370 } 371 } 372 373 } 374 } 375 allSystems.addAll( geocentricCRSs.values() ); 376 allSystems.addAll( geographicCRSs.values() ); 377 allSystems.addAll( projectedCRSs.values() ); 378 if ( checkForDoubleDefinition ) { 379 if ( !doubleGeos.isEmpty() ) { 380 Set<String> keys = doubleGeos.keySet(); 381 LOG.logInfo( "Following geographic crs's could probably be mapped on eachother" ); 382 for ( String key : keys ) { 383 LOG.logInfo( key + " : " + doubleGeos.get( key ) ); 384 } 385 } 386 387 if ( !doubleDatums.isEmpty() ) { 388 Set<String> keys = doubleDatums.keySet(); 389 LOG.logInfo( "Following datums could probably be mapped on eachother" ); 390 for ( String key : keys ) { 391 LOG.logInfo( key + " : " + doubleDatums.get( key ) ); 392 } 393 } 394 if ( !doubleToWGS.isEmpty() ) { 395 Set<String> keys = doubleToWGS.keySet(); 396 LOG.logInfo( "Following wgs conversion infos could probably be mapped on eachother" ); 397 for ( String key : keys ) { 398 LOG.logInfo( key + " : " + doubleToWGS.get( key ) ); 399 } 400 } 401 if ( !doubleEllipsoids.isEmpty() ) { 402 Set<String> keys = doubleEllipsoids.keySet(); 403 LOG.logInfo( "Following ellipsoids could probably be mapped on eachother" ); 404 for ( String key : keys ) { 405 LOG.logInfo( key + " : " + doubleEllipsoids.get( key ) ); 406 } 407 } 408 if ( !doubleMeridians.isEmpty() ) { 409 Set<String> keys = doubleEllipsoids.keySet(); 410 LOG.logInfo( "Following prime meridians could probably be mapped on eachother" ); 411 for ( String key : keys ) { 412 LOG.logInfo( key + " : " + doubleMeridians.get( key ) ); 413 } 414 } 415 if ( !doubleProjections.isEmpty() ) { 416 Set<String> keys = doubleProjections.keySet(); 417 LOG.logInfo( "Following projections could probably be mapped on eachother" ); 418 for ( String key : keys ) { 419 LOG.logInfo( key + " : " + doubleProjections.get( key ) ); 420 } 421 } 422 } 423 } else { 424 LOG.logDebug( "The root element is null, is this correct behaviour?" ); 425 } 426 return allSystems; 427 } 428 429 private Document createValidDocument( Element root ) { 430 // List<Element> lastInput = new ArrayList<Element>( 100 ); 431 try { 432 List<Element> valid = XMLTools.getElements( root, PRE + "ellipsoid", nsContext ); 433 valid.addAll( XMLTools.getElements( root, PRE + "geodeticDatum", nsContext ) ); 434 valid.addAll( XMLTools.getElements( root, PRE + "lambertAzimuthalEqualArea", nsContext ) ); 435 valid.addAll( XMLTools.getElements( root, PRE + "lambertConformalConic", nsContext ) ); 436 valid.addAll( XMLTools.getElements( root, PRE + "stereographicAzimuthal", nsContext ) ); 437 valid.addAll( XMLTools.getElements( root, PRE + "transverseMercator", nsContext ) ); 438 valid.addAll( XMLTools.getElements( root, PRE + "projectedCRS", nsContext ) ); 439 valid.addAll( XMLTools.getElements( root, PRE + "geographicCRS", nsContext ) ); 440 valid.addAll( XMLTools.getElements( root, PRE + "geocentricCRS", nsContext ) ); 441 valid.addAll( XMLTools.getElements( root, PRE + "primeMeridian", nsContext ) ); 442 valid.addAll( XMLTools.getElements( root, PRE + "wgs84Transformation", nsContext ) ); 443 Document doc = XMLTools.create(); 444 Element newRoot = doc.createElementNS( CommonNamespaces.CRSNS.toASCIIString(), PRE + "definitions" ); 445 newRoot = (Element) doc.importNode( newRoot, false ); 446 newRoot = (Element) doc.appendChild( newRoot ); 447 for ( int i = 0; i < valid.size(); ++i ) { 448 Element el = valid.get( i ); 449 el = (Element) doc.importNode( el, true ); 450 newRoot.appendChild( el ); 451 } 452 XMLTools.appendNSBinding( newRoot, CommonNamespaces.XSI_PREFIX, CommonNamespaces.XSINS ); 453 newRoot.setAttributeNS( CommonNamespaces.XSINS.toASCIIString(), 454 "xsi:schemaLocation", 455 "http://www.deegree.org/crs c:/windows/profiles/rutger/EIGE~VO5/eclipse-projekte/coordinate_systems/resources/schema/crsdefinition.xsd" ); 456 return doc; 457 } catch ( XMLParsingException xmle ) { 458 xmle.printStackTrace(); 459 } 460 return root.getOwnerDocument(); 461 } 462 463 /** 464 * Export the projected CRS to it's appropriate deegree-crs-definitions form. 465 * 466 * @param projectedCRS 467 * to be exported 468 * @param rootNode 469 * to export the projected CRS to. 470 * @param exportedIds 471 * a list of id's already exported. 472 */ 473 private void export( ProjectedCRS projectedCRS, Element rootNode, List<String> exportedIds ) { 474 if ( !exportedIds.contains( projectedCRS.getIdentifier() ) ) { 475 Element crsElement = XMLTools.appendElement( rootNode, CommonNamespaces.CRSNS, PRE + "projectedCRS" ); 476 exportAbstractCRS( projectedCRS, crsElement ); 477 GeographicCRS underLyingCRS = projectedCRS.getGeographicCRS(); 478 export( underLyingCRS, rootNode, exportedIds ); 479 export( projectedCRS.getUnits(), crsElement ); 480 // Add a reference from the geographicCRS element to the projectedCRS element. 481 XMLTools.appendElement( crsElement, 482 CommonNamespaces.CRSNS, 483 PRE + "usedGeographicCRS", 484 underLyingCRS.getIdentifier() ); 485 486 Projection projection = projectedCRS.getProjection(); 487 export( projection, rootNode, exportedIds ); 488 // Add a reference from the projection element to the projectedCRS element. 489 XMLTools.appendElement( crsElement, 490 CommonNamespaces.CRSNS, 491 PRE + "usedProjection", 492 projection.getIdentifier() ); 493 494 // Add the ids to the exportedID list. 495 for ( String eID : projectedCRS.getIdentifiers() ) { 496 exportedIds.add( eID ); 497 } 498 // finally add the crs node to the rootnode. 499 rootNode.appendChild( crsElement ); 500 } 501 } 502 503 /** 504 * Export the geocentric/geographic CRS to it's appropriate deegree-crs-definitions form. 505 * 506 * @param geographicCRS 507 * to be exported 508 * @param rootNode 509 * to export the geographic CRS to. 510 * @param exportedIds 511 * a list of id's already exported. 512 */ 513 private void export( GeographicCRS geographicCRS, Element rootNode, List<String> exportedIds ) { 514 if ( !exportedIds.contains( geographicCRS.getIdentifier() ) ) { 515 Element crsElement = XMLTools.appendElement( rootNode, CommonNamespaces.CRSNS, PRE + "geographicCRS" ); 516 exportAbstractCRS( geographicCRS, crsElement ); 517 518 // export the datum. 519 GeodeticDatum datum = geographicCRS.getGeodeticDatum(); 520 if ( datum != null ) { 521 export( datum, rootNode, exportedIds ); 522 // Add a reference from the datum element to the geographic element. 523 XMLTools.appendElement( crsElement, CommonNamespaces.CRSNS, PRE + "usedDatum", datum.getIdentifier() ); 524 } 525 // Add the ids to the exportedID list. 526 for ( String eID : geographicCRS.getIdentifiers() ) { 527 exportedIds.add( eID ); 528 } 529 // finally add the crs node to the rootnode. 530 rootNode.appendChild( crsElement ); 531 } 532 } 533 534 /** 535 * Export the projection to it's appropriate deegree-crs-definitions form. 536 * 537 * @param projection 538 * to be exported 539 * @param rootNode 540 * to export the projection to. 541 * @param exportedIds 542 * a list of id's already exported. 543 */ 544 private void export( Projection projection, Element rootNode, List<String> exportedIds ) { 545 if ( !exportedIds.contains( projection.getIdentifier() ) ) { 546 String elementName = projection.getDeegreeSpecificName(); 547 Element projectionElement = XMLTools.appendElement( rootNode, CommonNamespaces.CRSNS, PRE + elementName ); 548 exportIdentifiable( projection, projectionElement ); 549 Element tmp = XMLTools.appendElement( projectionElement, 550 CommonNamespaces.CRSNS, 551 PRE + "latitudeOfNaturalOrigin", 552 Double.toString( Math.toDegrees( projection.getProjectionLatitude() ) ) ); 553 tmp.setAttribute( "inDegrees", "true" ); 554 tmp = XMLTools.appendElement( projectionElement, 555 CommonNamespaces.CRSNS, 556 PRE + "longitudeOfNaturalOrigin", 557 Double.toString( Math.toDegrees( projection.getProjectionLongitude() ) ) ); 558 tmp.setAttribute( "inDegrees", "true" ); 559 560 XMLTools.appendElement( projectionElement, 561 CommonNamespaces.CRSNS, 562 PRE + "scaleFactor", 563 Double.toString( projection.getScale() ) ); 564 XMLTools.appendElement( projectionElement, 565 CommonNamespaces.CRSNS, 566 PRE + "falseEasting", 567 Double.toString( projection.getFalseEasting() ) ); 568 XMLTools.appendElement( projectionElement, 569 CommonNamespaces.CRSNS, 570 PRE + "falseNorthing", 571 Double.toString( projection.getFalseNorthing() ) ); 572 if ( "transverseMercator".equalsIgnoreCase( elementName ) ) { 573 XMLTools.appendElement( projectionElement, 574 CommonNamespaces.CRSNS, 575 PRE + "northernHemisphere", 576 Boolean.toString( ( (TransverseMercator) projection ).getHemisphere() ) ); 577 } else if ( "lambertConformalConic".equalsIgnoreCase( elementName ) ) { 578 double paralellLatitude = ( (LambertConformalConic) projection ).getFirstParallelLatitude(); 579 if ( !Double.isNaN( paralellLatitude ) && Math.abs( paralellLatitude ) > ProjectionUtils.EPS11 ) { 580 paralellLatitude = Math.toDegrees( paralellLatitude ); 581 tmp = XMLTools.appendElement( projectionElement, 582 CommonNamespaces.CRSNS, 583 PRE + "firstParallelLatitude", 584 Double.toString( paralellLatitude ) ); 585 tmp.setAttribute( "inDegrees", "true" ); 586 } 587 paralellLatitude = ( (LambertConformalConic) projection ).getSecondParallelLatitude(); 588 if ( !Double.isNaN( paralellLatitude ) && Math.abs( paralellLatitude ) > ProjectionUtils.EPS11 ) { 589 paralellLatitude = Math.toDegrees( paralellLatitude ); 590 tmp = XMLTools.appendElement( projectionElement, 591 CommonNamespaces.CRSNS, 592 PRE + "secondParallelLatitude", 593 Double.toString( paralellLatitude ) ); 594 tmp.setAttribute( "inDegrees", "true" ); 595 } 596 } else if ( "stereographicAzimuthal".equalsIgnoreCase( elementName ) ) { 597 tmp = XMLTools.appendElement( projectionElement, 598 CommonNamespaces.CRSNS, 599 PRE + "trueScaleLatitude", 600 Double.toString( ( (StereographicAzimuthal) projection ).getTrueScaleLatitude() ) ); 601 tmp.setAttribute( "inDegrees", "true" ); 602 } 603 } 604 } 605 606 /** 607 * Export the confInvo to it's appropriate deegree-crs-definitions form. 608 * 609 * @param confInvo 610 * to be exported 611 * @param rootNode 612 * to export the confInvo to. 613 * @param exportedIds 614 * a list of id's already exported. 615 */ 616 private void export( WGS84ConversionInfo confInvo, Element rootNode, final List<String> exportedIds ) { 617 if ( !exportedIds.contains( confInvo.getIdentifier() ) ) { 618 Element convElement = XMLTools.appendElement( rootNode, CommonNamespaces.CRSNS, PRE + "wgs84Transformation" ); 619 exportIdentifiable( confInvo, convElement ); 620 621 XMLTools.appendElement( convElement, 622 CommonNamespaces.CRSNS, 623 PRE + "xAxisTranslation", 624 Double.toString( confInvo.dx ) ); 625 XMLTools.appendElement( convElement, 626 CommonNamespaces.CRSNS, 627 PRE + "yAxisTranslation", 628 Double.toString( confInvo.dy ) ); 629 XMLTools.appendElement( convElement, 630 CommonNamespaces.CRSNS, 631 PRE + "zAxisTranslation", 632 Double.toString( confInvo.dz ) ); 633 XMLTools.appendElement( convElement, 634 CommonNamespaces.CRSNS, 635 PRE + "xAxisRotation", 636 Double.toString( confInvo.ex ) ); 637 XMLTools.appendElement( convElement, 638 CommonNamespaces.CRSNS, 639 PRE + "yAxisRotation", 640 Double.toString( confInvo.ey ) ); 641 XMLTools.appendElement( convElement, 642 CommonNamespaces.CRSNS, 643 PRE + "zAxisRotation", 644 Double.toString( confInvo.ez ) ); 645 XMLTools.appendElement( convElement, 646 CommonNamespaces.CRSNS, 647 PRE + "scaleDifference", 648 Double.toString( confInvo.ppm ) ); 649 650 // Add the ids to the exportedID list. 651 for ( String eID : confInvo.getIdentifiers() ) { 652 exportedIds.add( eID ); 653 } 654 655 // finally add the WGS84-Transformation node to the rootnode. 656 rootNode.appendChild( convElement ); 657 } 658 659 } 660 661 /** 662 * Export the PrimeMeridian to it's appropriate deegree-crs-definitions form. 663 * 664 * @param pMeridian 665 * to be exported 666 * @param rootNode 667 * to export the pMeridian to. 668 * @param exportedIds 669 * a list of id's already exported. 670 */ 671 private void export( PrimeMeridian pMeridian, Element rootNode, final List<String> exportedIds ) { 672 if ( !exportedIds.contains( pMeridian.getIdentifier() ) ) { 673 Element meridianElement = XMLTools.appendElement( rootNode, CommonNamespaces.CRSNS, PRE + "primeMeridian" ); 674 exportIdentifiable( pMeridian, meridianElement ); 675 export( pMeridian.getAngularUnit(), meridianElement ); 676 XMLTools.appendElement( meridianElement, 677 CommonNamespaces.CRSNS, 678 PRE + "longitude", 679 Double.toString( pMeridian.getLongitude() ) ); 680 681 // Add the ids to the exportedID list. 682 for ( String eID : pMeridian.getIdentifiers() ) { 683 exportedIds.add( eID ); 684 } 685 686 // finally add the prime meridian node to the rootnode. 687 rootNode.appendChild( meridianElement ); 688 } 689 } 690 691 /** 692 * Export the ellipsoid to it's appropriate deegree-crs-definitions form. 693 * 694 * @param ellipsoid 695 * to be exported 696 * @param rootNode 697 * to export the ellipsoid to. 698 * @param exportedIds 699 * a list of id's already exported. 700 */ 701 private void export( Ellipsoid ellipsoid, Element rootNode, final List<String> exportedIds ) { 702 if ( !exportedIds.contains( ellipsoid.getIdentifier() ) ) { 703 Element ellipsoidElement = XMLTools.appendElement( rootNode, CommonNamespaces.CRSNS, PRE + "ellipsoid" ); 704 exportIdentifiable( ellipsoid, ellipsoidElement ); 705 XMLTools.appendElement( ellipsoidElement, 706 CommonNamespaces.CRSNS, 707 PRE + "semiMajorAxis", 708 Double.toString( ellipsoid.getSemiMajorAxis() ) ); 709 XMLTools.appendElement( ellipsoidElement, 710 CommonNamespaces.CRSNS, 711 PRE + "inverseFlatting", 712 Double.toString( ellipsoid.getInverseFlattening() ) ); 713 export( ellipsoid.getUnits(), ellipsoidElement ); 714 715 // Add the ids to the exportedID list. 716 for ( String eID : ellipsoid.getIdentifiers() ) { 717 exportedIds.add( eID ); 718 } 719 // finally add the ellipsoid node to the rootnode. 720 rootNode.appendChild( ellipsoidElement ); 721 } 722 } 723 724 /** 725 * Export the datum to it's appropriate deegree-crs-definitions form. 726 * 727 * @param datum 728 * to be exported 729 * @param rootNode 730 * to export the datum to. 731 * @param exportedIds 732 * a list of id's already exported. 733 */ 734 private void export( GeodeticDatum datum, Element rootNode, List<String> exportedIds ) { 735 if ( !exportedIds.contains( datum.getIdentifier() ) ) { 736 Element datumElement = XMLTools.appendElement( rootNode, CommonNamespaces.CRSNS, PRE + "geodeticDatum" ); 737 exportIdentifiable( datum, datumElement ); 738 /** 739 * EXPORT the ELLIPSOID 740 */ 741 Ellipsoid ellipsoid = datum.getEllipsoid(); 742 if ( ellipsoid != null ) { 743 export( ellipsoid, rootNode, exportedIds ); 744 // Add a reference from the ellipsoid element to the datum element. 745 XMLTools.appendElement( datumElement, 746 CommonNamespaces.CRSNS, 747 PRE + "usedEllipsoid", 748 ellipsoid.getIdentifier() ); 749 } 750 751 /** 752 * EXPORT the PRIME_MERIDIAN 753 */ 754 PrimeMeridian pMeridian = datum.getPrimeMeridian(); 755 if ( pMeridian != null ) { 756 export( pMeridian, rootNode, exportedIds ); 757 // Add a reference from the prime meridian element to the datum element. 758 XMLTools.appendElement( datumElement, 759 CommonNamespaces.CRSNS, 760 PRE + "usedPrimeMeridian", 761 pMeridian.getIdentifier() ); 762 } 763 764 /** 765 * EXPORT the WGS-84-Conversion INFO 766 */ 767 WGS84ConversionInfo confInvo = datum.getWGS84Conversion(); 768 if ( confInvo != null ) { 769 export( confInvo, rootNode, exportedIds ); 770 // Add a reference from the prime meridian element to the datum element. 771 XMLTools.appendElement( datumElement, 772 CommonNamespaces.CRSNS, 773 PRE + "usedWGS84ConversionInfo", 774 confInvo.getIdentifier() ); 775 } 776 777 // Add the ids to the exportedID list. 778 for ( String eID : datum.getIdentifiers() ) { 779 exportedIds.add( eID ); 780 } 781 // finally add the datum node to the rootnode. 782 rootNode.appendChild( datumElement ); 783 } 784 } 785 786 /** 787 * Export toplevel crs features. 788 * 789 * @param crs 790 * to be exported 791 * @param crsElement 792 * to export to 793 */ 794 private void exportAbstractCRS( CoordinateSystem crs, Element crsElement ) { 795 exportIdentifiable( crs, crsElement ); 796 Axis[] axis = crs.getAxis(); 797 StringBuilder axisOrder = new StringBuilder( 200 ); 798 for ( int i = 0; i < axis.length; ++i ) { 799 Axis a = axis[i]; 800 export( a, crsElement ); 801 axisOrder.append( a.getName() ); 802 if ( ( i + 1 ) < axis.length ) { 803 axisOrder.append( ", " ); 804 } 805 } 806 XMLTools.appendElement( crsElement, CommonNamespaces.CRSNS, PRE + "axisOrder", axisOrder.toString() ); 807 808 } 809 810 /** 811 * Export the geocentric CRS to it's appropriate deegree-crs-definitions form. 812 * 813 * @param geocentricCRS 814 * to be exported 815 * @param rootNode 816 * to export the geocentric CRS to. 817 * @param exportedIds 818 * a list of id's already exported. 819 */ 820 private void export( GeocentricCRS geocentricCRS, Element rootNode, List<String> exportedIds ) { 821 if ( !exportedIds.contains( geocentricCRS.getIdentifier() ) ) { 822 Element crsElement = XMLTools.appendElement( rootNode, CommonNamespaces.CRSNS, PRE + "geocentricCRS" ); 823 exportAbstractCRS( geocentricCRS, crsElement ); 824 // export the datum. 825 GeodeticDatum datum = geocentricCRS.getGeodeticDatum(); 826 if ( datum != null ) { 827 export( datum, rootNode, exportedIds ); 828 // Add a reference from the datum element to the geocentric element. 829 XMLTools.appendElement( crsElement, CommonNamespaces.CRSNS, PRE + "usedDatum", datum.getIdentifier() ); 830 } 831 // Add the ids to the exportedID list. 832 for ( String eID : geocentricCRS.getIdentifiers() ) { 833 exportedIds.add( eID ); 834 } 835 // finally add the crs node to the rootnode. 836 rootNode.appendChild( crsElement ); 837 } 838 } 839 840 /** 841 * Creates the basic nodes of the identifiable object. 842 * 843 * @param id 844 * object to be exported. 845 * @param currentNode 846 * to expand 847 */ 848 private void exportIdentifiable( Identifiable id, Element currentNode ) { 849 for ( String i : id.getIdentifiers() ) { 850 if ( i != null ) { 851 XMLTools.appendElement( currentNode, CommonNamespaces.CRSNS, PRE + "id", i ); 852 } 853 854 } 855 if ( id.getNames() != null && id.getNames().length > 0 ) { 856 for ( String i : id.getNames() ) { 857 if ( i != null ) { 858 XMLTools.appendElement( currentNode, CommonNamespaces.CRSNS, PRE + "name", i ); 859 } 860 } 861 } 862 if ( id.getVersions() != null && id.getVersions().length > 0 ) { 863 for ( String i : id.getVersions() ) { 864 if ( i != null ) { 865 XMLTools.appendElement( currentNode, CommonNamespaces.CRSNS, PRE + "version", i ); 866 } 867 } 868 } 869 if ( id.getDescriptions() != null && id.getDescriptions().length > 0 ) { 870 for ( String i : id.getDescriptions() ) { 871 if ( i != null ) { 872 XMLTools.appendElement( currentNode, CommonNamespaces.CRSNS, PRE + "description", i ); 873 } 874 } 875 } 876 if ( id.getAreasOfUse() != null && id.getAreasOfUse().length > 0 ) { 877 for ( String i : id.getAreasOfUse() ) { 878 if ( i != null ) { 879 XMLTools.appendElement( currentNode, CommonNamespaces.CRSNS, PRE + "areaOfUse", i ); 880 } 881 } 882 } 883 } 884 885 /** 886 * Export an axis to xml in the crs-definitions schema layout. 887 * 888 * @param axis 889 * to be exported. 890 * @param currentNode 891 * to export to. 892 */ 893 private void export( Axis axis, Element currentNode ) { 894 Document doc = currentNode.getOwnerDocument(); 895 Element axisElement = doc.createElementNS( CRS_URI, PRE + "Axis" ); 896 // The name. 897 XMLTools.appendElement( axisElement, CommonNamespaces.CRSNS, PRE + "name", axis.getName() ); 898 899 // the units. 900 Unit units = axis.getUnits(); 901 export( units, axisElement ); 902 903 XMLTools.appendElement( axisElement, 904 CommonNamespaces.CRSNS, 905 PRE + "axisOrientation", 906 axis.getOrientationAsString() ); 907 currentNode.appendChild( axisElement ); 908 } 909 910 /** 911 * Export a unit to xml in the crs-definitions schema layout. If the given units are radians, the output will be 912 * transformed to degrees (because of the export-to-degree policy). 913 * 914 * @param units 915 * to be exported. 916 * @param currentNode 917 * to export to. 918 */ 919 private void export( Unit units, Element currentNode ) { 920 if ( units != null && currentNode != null ) { 921 String tmp = units.getName(); 922 if ( units.equals( Unit.RADIAN ) ) { 923 tmp = Unit.DEGREE.getName(); 924 } 925 XMLTools.appendElement( currentNode, CommonNamespaces.CRSNS, PRE + "units", tmp ); 926 } 927 } 928 929 /** 930 * @param crsElement 931 * from which the crs is to be created (using cached datums, conversioninfos and projections). 932 * 933 * @return a geographic coordinatesystem based on the given xml-element. 934 * @throws CRSConfigurationException 935 * if a required element could not be found, or an xmlParsingException occurred. 936 */ 937 private CoordinateSystem parseGeographicCRS( Element crsElement ) 938 throws CRSConfigurationException { 939 Identifiable id = parseIdentifiable( crsElement ); 940 Axis[] axis = parseAxisOrder( crsElement ); 941 942 // get the datum 943 GeodeticDatum usedDatum = parseReferencedGeodeticDatum( crsElement, id.getIdentifier() ); 944 GeographicCRS result = new GeographicCRS( usedDatum, axis, id ); 945 for ( String s : id.getIdentifiers() ) { 946 LOG.logDebug( "Adding id: " + s + "to geographic crs cache." ); 947 geographicCRSs.put( s, result ); 948 } 949 if ( checkForDoubleDefinition ) { 950 checkForUniqueness( cachedGeoCRSs, doubleGeos, result ); 951 } 952 // remove the child, thus resulting in a smaller dom tree. 953 rootElement.removeChild( crsElement ); 954 return result; 955 } 956 957 /** 958 * Reads an element from the configuration with xpath *[crs:id='givenID'] and returns the found element. 959 * 960 * @param id 961 * to search for 962 * @return the element or <code>null</code> if no such element was found. 963 */ 964 private Element getTopElementFromID( String id ) { 965 if ( rootElement == null ) { 966 LOG.logDebug( "The Root element is null, hence no crs's are available" ); 967 return null; 968 } 969 Element crsElement = null; 970 // String xPath ="//*[crs:id='EPSG:31466']"; 971 String xPath = "*[" + PRE + "id='" + id + "']"; 972 try { 973 crsElement = XMLTools.getElement( rootElement, xPath, nsContext ); 974 } catch ( XMLParsingException e ) { 975 LOG.logError( Messages.getMessage( "CRS_CONFIG_NO_RESULT_FOR_ID", id, e.getMessage() ), e ); 976 } 977 LOG.logDebug( "Trying to find elements with xpath: " + xPath 978 + ( ( crsElement == null ) ? " [failed]" : " [success]" ) ); 979 return crsElement; 980 } 981 982 /** 983 * @param datumID 984 * @return the 985 * @throws CRSConfigurationException 986 */ 987 private GeodeticDatum getGeodeticDatumFromID( String datumID ) 988 throws CRSConfigurationException { 989 if ( datumID != null && !"".equals( datumID.trim() ) ) { 990 datumID = datumID.trim(); 991 if ( datums.containsKey( datumID ) && datums.get( datumID ) != null ) { 992 return datums.get( datumID ); 993 } 994 Element datumElement = getTopElementFromID( datumID ); 995 if ( datumElement == null ) { 996 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_NO_ELEMENT", "datum", datumID ) ); 997 } 998 // get the identifiable. 999 Identifiable id = parseIdentifiable( datumElement ); 1000 1001 // get the ellipsoid. 1002 Ellipsoid ellipsoid = null; 1003 try { 1004 String ellipsID = XMLTools.getRequiredNodeAsString( datumElement, PRE + "usedEllipsoid", nsContext ); 1005 if ( ellipsID != null && !"".equals( ellipsID.trim() ) ) { 1006 ellipsoid = getEllipsoidFromID( ellipsID ); 1007 } 1008 } catch ( XMLParsingException e ) { 1009 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", 1010 "usedEllipsoid", 1011 ( ( datumElement == null ) ? "null" 1012 : datumElement.getLocalName() ), 1013 e.getMessage() ), 1014 e ); 1015 } 1016 1017 // get the primemeridian if any. 1018 PrimeMeridian pMeridian = null; 1019 try { 1020 String pMeridianID = XMLTools.getNodeAsString( datumElement, PRE + "usedPrimeMeridian", nsContext, null ); 1021 if ( pMeridianID != null && !"".equals( pMeridianID.trim() ) ) { 1022 pMeridian = getPrimeMeridianFromID( pMeridianID ); 1023 } 1024 if ( pMeridian == null ) { 1025 pMeridian = PrimeMeridian.GREENWICH; 1026 } 1027 } catch ( XMLParsingException e ) { 1028 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", 1029 "usedPrimeMeridian", 1030 ( ( datumElement == null ) ? "null" 1031 : datumElement.getLocalName() ), 1032 e.getMessage() ), 1033 e ); 1034 } 1035 1036 // get the WGS84 if any. 1037 WGS84ConversionInfo cInfo = null; 1038 try { 1039 String infoID = XMLTools.getNodeAsString( datumElement, 1040 PRE + "usedWGS84ConversionInfo", 1041 nsContext, 1042 null ); 1043 if ( infoID != null && !"".equals( infoID.trim() ) ) { 1044 cInfo = getConversionInfoFromID( infoID ); 1045 } 1046 if ( cInfo == null ) { 1047 cInfo = new WGS84ConversionInfo( "Created by DeegreeCRSProvider" ); 1048 } 1049 } catch ( XMLParsingException e ) { 1050 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", 1051 "wgs84ConversionInfo", 1052 ( ( datumElement == null ) ? "null" 1053 : datumElement.getLocalName() ), 1054 e.getMessage() ), 1055 e ); 1056 } 1057 if ( ellipsoid == null ) { 1058 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_DATUM_HAS_NO_ELLIPSOID", datumID ) ); 1059 } 1060 GeodeticDatum result = new GeodeticDatum( ellipsoid, 1061 pMeridian, 1062 cInfo, 1063 id.getIdentifiers(), 1064 id.getNames(), 1065 id.getVersions(), 1066 id.getDescriptions(), 1067 id.getAreasOfUse() ); 1068 for ( String s : id.getIdentifiers() ) { 1069 datums.put( s, result ); 1070 } 1071 if ( checkForDoubleDefinition ) { 1072 checkForUniqueness( cachedDatums, doubleDatums, result ); 1073 } 1074 1075 // remove the datum from the xml-tree. 1076 rootElement.removeChild( datumElement ); 1077 return result; 1078 } 1079 1080 return null; 1081 } 1082 1083 /** 1084 * @param meridianID 1085 * the id to search for. 1086 * @return the primeMeridian with given id or <code>null</code> 1087 * @throws CRSConfigurationException 1088 * if the longitude was not set or the units could not be parsed. 1089 */ 1090 private PrimeMeridian getPrimeMeridianFromID( String meridianID ) 1091 throws CRSConfigurationException { 1092 if ( meridianID != null && !"".equals( meridianID.trim() ) ) { 1093 if ( primeMeridians.containsKey( meridianID ) && primeMeridians.get( meridianID ) != null ) { 1094 return primeMeridians.get( meridianID ); 1095 } 1096 Element meridianElement = getTopElementFromID( meridianID ); 1097 if ( meridianElement == null ) { 1098 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_NO_ELEMENT", 1099 "primeMeridian", 1100 meridianID ) ); 1101 } 1102 Identifiable id = parseIdentifiable( meridianElement ); 1103 Unit units = parseUnit( meridianElement ); 1104 double longitude = 0; 1105 try { 1106 longitude = XMLTools.getRequiredNodeAsDouble( meridianElement, PRE + "longitude", nsContext ); 1107 boolean inDegrees = XMLTools.getNodeAsBoolean( meridianElement, 1108 PRE + "longitude/@inDegrees", 1109 nsContext, 1110 true ); 1111 longitude = ( longitude != 0 && inDegrees ) ? Math.toRadians( longitude ) : longitude; 1112 } catch ( XMLParsingException e ) { 1113 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", 1114 "longitude", 1115 ( ( meridianElement == null ) ? "null" 1116 : meridianElement.getLocalName() ), 1117 e.getMessage() ), 1118 e ); 1119 } 1120 PrimeMeridian result = new PrimeMeridian( units, 1121 longitude, 1122 id.getIdentifiers(), 1123 id.getNames(), 1124 id.getVersions(), 1125 id.getDescriptions(), 1126 id.getAreasOfUse() ); 1127 for ( String s : id.getIdentifiers() ) { 1128 primeMeridians.put( s, result ); 1129 } 1130 if ( checkForDoubleDefinition ) { 1131 checkForUniqueness( cachedMeridans, doubleMeridians, result ); 1132 } 1133 // remove the prime meridian, thus resulting in a smaller xml-tree. 1134 rootElement.removeChild( meridianElement ); 1135 return result; 1136 } 1137 return null; 1138 } 1139 1140 /** 1141 * Tries to find a cached ellipsoid, if not found, the config will be checked. 1142 * 1143 * @param ellipsoidID 1144 * @return an ellipsoid or <code>null</code> if no ellipsoid with given id was found, or the id was 1145 * <code>null</code> or empty. 1146 * @throws CRSConfigurationException 1147 * if something went wrong. 1148 */ 1149 private Ellipsoid getEllipsoidFromID( String ellipsoidID ) 1150 throws CRSConfigurationException { 1151 if ( ellipsoidID != null && !"".equals( ellipsoidID.trim() ) ) { 1152 if ( ellipsoids.containsKey( ellipsoidID ) && ellipsoids.get( ellipsoidID ) != null ) { 1153 return ellipsoids.get( ellipsoidID ); 1154 } 1155 Element ellipsoidElement = getTopElementFromID( ellipsoidID ); 1156 if ( ellipsoidElement == null ) { 1157 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_NO_ELEMENT", 1158 "ellipsoid", 1159 ellipsoidID ) ); 1160 } 1161 Identifiable id = parseIdentifiable( ellipsoidElement ); 1162 try { 1163 double semiMajor = XMLTools.getRequiredNodeAsDouble( ellipsoidElement, PRE + "semiMajorAxis", nsContext ); 1164 1165 Unit units = parseUnit( ellipsoidElement ); 1166 double inverseFlattening = XMLTools.getNodeAsDouble( ellipsoidElement, 1167 PRE + "inverseFlatting", 1168 nsContext, 1169 Double.NaN ); 1170 double eccentricity = XMLTools.getNodeAsDouble( ellipsoidElement, 1171 PRE + "eccentricity", 1172 nsContext, 1173 Double.NaN ); 1174 double semiMinorAxis = XMLTools.getNodeAsDouble( ellipsoidElement, 1175 PRE + "semiMinorAxis", 1176 nsContext, 1177 Double.NaN ); 1178 if ( Double.isNaN( inverseFlattening ) && Double.isNaN( eccentricity ) && Double.isNaN( semiMinorAxis ) ) { 1179 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_ELLIPSOID_MISSES_PARAM", 1180 ellipsoidID ) ); 1181 } 1182 1183 Ellipsoid result = null; 1184 if ( !Double.isNaN( inverseFlattening ) ) { 1185 result = new Ellipsoid( semiMajor, 1186 units, 1187 inverseFlattening, 1188 id.getIdentifiers(), 1189 id.getNames(), 1190 id.getVersions(), 1191 id.getDescriptions(), 1192 id.getAreasOfUse() ); 1193 } else if ( !Double.isNaN( eccentricity ) ) { 1194 result = new Ellipsoid( semiMajor, 1195 eccentricity, 1196 units, 1197 id.getIdentifiers(), 1198 id.getNames(), 1199 id.getVersions(), 1200 id.getDescriptions(), 1201 id.getAreasOfUse() ); 1202 } else { 1203 result = new Ellipsoid( units, 1204 semiMajor, 1205 semiMinorAxis, 1206 id.getIdentifiers(), 1207 id.getNames(), 1208 id.getVersions(), 1209 id.getDescriptions(), 1210 id.getAreasOfUse() ); 1211 } 1212 for ( String s : id.getIdentifiers() ) { 1213 ellipsoids.put( s, result ); 1214 } 1215 if ( checkForDoubleDefinition ) { 1216 checkForUniqueness( cachedEllipsoids, doubleEllipsoids, result ); 1217 } 1218 // remove the ellipsoid from the xml-tree. 1219 rootElement.removeChild( ellipsoidElement ); 1220 return result; 1221 } catch ( XMLParsingException e ) { 1222 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", 1223 "ellipsoid", 1224 ( ( ellipsoidElement == null ) ? "null" 1225 : ellipsoidElement.getLocalName() ), 1226 e.getMessage() ), 1227 e ); 1228 } 1229 } 1230 1231 return null; 1232 } 1233 1234 /** 1235 * @param info 1236 * @return the configured wgs84 conversion info parameters. 1237 * @throws CRSConfigurationException 1238 */ 1239 private WGS84ConversionInfo getConversionInfoFromID( String infoID ) 1240 throws CRSConfigurationException { 1241 if ( infoID != null && !"".equals( infoID.trim() ) ) { 1242 if ( conversionInfos.containsKey( infoID ) && conversionInfos.get( infoID ) != null ) { 1243 return conversionInfos.get( infoID ); 1244 } 1245 Element cInfoElement = getTopElementFromID( infoID ); 1246 if ( cInfoElement == null ) { 1247 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_NO_ELEMENT", 1248 "wgs84ConversionInfo", 1249 infoID ) ); 1250 } 1251 Identifiable identifiable = parseIdentifiable( cInfoElement ); 1252 double xT = 0, yT = 0, zT = 0, xR = 0, yR = 0, zR = 0, scale = 0; 1253 try { 1254 xT = XMLTools.getNodeAsDouble( cInfoElement, PRE + "xAxisTranslation", nsContext, 0 ); 1255 yT = XMLTools.getNodeAsDouble( cInfoElement, PRE + "yAxisTranslation", nsContext, 0 ); 1256 zT = XMLTools.getNodeAsDouble( cInfoElement, PRE + "zAxisTranslation", nsContext, 0 ); 1257 xR = XMLTools.getNodeAsDouble( cInfoElement, PRE + "xAxisRotation", nsContext, 0 ); 1258 yR = XMLTools.getNodeAsDouble( cInfoElement, PRE + "yAxisRotation", nsContext, 0 ); 1259 zR = XMLTools.getNodeAsDouble( cInfoElement, PRE + "zAxisRotation", nsContext, 0 ); 1260 scale = XMLTools.getNodeAsDouble( cInfoElement, PRE + "scaleDifference", nsContext, 0 ); 1261 } catch ( XMLParsingException e ) { 1262 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", 1263 "conversionInfo", 1264 "definitions", 1265 e.getMessage() ), e ); 1266 } 1267 WGS84ConversionInfo result = new WGS84ConversionInfo( xT, yT, zT, xR, yR, zR, scale, identifiable ); 1268 for ( String id : identifiable.getIdentifiers() ) { 1269 conversionInfos.put( id, result ); 1270 } 1271 if ( checkForDoubleDefinition ) { 1272 checkForUniqueness( cachedToWGS, doubleToWGS, result ); 1273 } 1274 // remove the child, thus resulting in a smaller xml-tree. 1275 rootElement.removeChild( cInfoElement ); 1276 return result; 1277 1278 } 1279 return null; 1280 } 1281 1282 /** 1283 * @param crsElement 1284 * from which the crs is to be created (using chached datums, conversioninfos and projections). 1285 * @return a projected coordinatesystem based on the given xml-element. 1286 * @throws CRSConfigurationException 1287 * if a required element could not be found, or an xmlParsingException occurred. 1288 */ 1289 private CoordinateSystem parseProjectedCRS( Element crsElement ) 1290 throws CRSConfigurationException { 1291 Identifiable id = parseIdentifiable( crsElement ); 1292 Axis[] axis = parseAxisOrder( crsElement ); 1293 Unit units = parseUnit( crsElement ); 1294 1295 String usedProjection = null; 1296 String usedGeographicCRS = null; 1297 try { 1298 usedProjection = XMLTools.getRequiredNodeAsString( crsElement, PRE + "usedProjection", nsContext ); 1299 usedGeographicCRS = XMLTools.getRequiredNodeAsString( crsElement, PRE + "usedGeographicCRS", nsContext ); 1300 } catch ( XMLParsingException e ) { 1301 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", 1302 "projectiontType or usedGeographicCRS", 1303 ( ( crsElement == null ) ? "null" 1304 : crsElement.getLocalName() ), 1305 e.getMessage() ), 1306 e ); 1307 1308 } 1309 // first create the datum. 1310 if ( usedGeographicCRS == null || "".equals( usedGeographicCRS.trim() ) ) { 1311 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_REFERENCE_ID_IS_EMPTY", 1312 "usedGeographicCRS", 1313 id.getIdentifier() ) ); 1314 } 1315 CoordinateSystem geoCRS = getCRSByID( usedGeographicCRS ); 1316 if ( geoCRS == null || geoCRS.getType() != CoordinateSystem.GEOGRAPHIC_CRS ) { 1317 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJECTEDCRS_FALSE_CRSREF", 1318 id.getIdentifier(), 1319 usedGeographicCRS ) ); 1320 } 1321 1322 // then the projection. 1323 if ( usedProjection == null || "".equals( usedProjection.trim() ) ) { 1324 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_REFERENCE_ID_IS_EMPTY", 1325 "projectionType", 1326 id.getIdentifier() ) ); 1327 } 1328 Projection projection = getProjectionByID( usedProjection, (GeographicCRS) geoCRS, units ); 1329 if ( projection == null ) { 1330 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJECTEDCRS_FALSE_PROJREF", 1331 id.getIdentifier(), 1332 usedProjection ) ); 1333 } 1334 ProjectedCRS result = new ProjectedCRS( projection, axis, id ); 1335 for ( String s : id.getIdentifiers() ) { 1336 LOG.logDebug( "Adding id: " + s + "to projected crs cache." ); 1337 projectedCRSs.put( s, result ); 1338 } 1339 // remove the child, thus resulting in a smaller xml-tree. 1340 rootElement.removeChild( crsElement ); 1341 return result; 1342 } 1343 1344 /** 1345 * Find or parses the projection with given id. 1346 * 1347 * @param usedProjection 1348 * @return the configured projection or <code>null</code> if not defined or found. 1349 * @throws CRSConfigurationException 1350 */ 1351 private Projection getProjectionByID( String usedProjection, GeographicCRS underlyingCRS, Unit units ) 1352 throws CRSConfigurationException { 1353 if ( usedProjection != null && !"".equals( usedProjection.trim() ) ) { 1354 usedProjection = usedProjection.trim(); 1355 if ( projections.containsKey( usedProjection ) && projections.get( usedProjection ) != null ) { 1356 return projections.get( usedProjection ); 1357 } 1358 Element projectionElement = getTopElementFromID( usedProjection ); 1359 if ( projectionElement == null ) { 1360 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_NO_ELEMENT", 1361 "projection", 1362 usedProjection ) ); 1363 } 1364 // get the identifiable. 1365 Identifiable id = parseIdentifiable( projectionElement ); 1366 Projection result = null; 1367 1368 try { 1369 double latitudeOfNaturalOrigin = XMLTools.getNodeAsDouble( projectionElement, 1370 PRE + "latitudeOfNaturalOrigin", 1371 nsContext, 1372 0 ); 1373 boolean inDegrees = XMLTools.getNodeAsBoolean( projectionElement, 1374 PRE + "latitudeOfNaturalOrigin/@inDegrees", 1375 nsContext, 1376 true ); 1377 latitudeOfNaturalOrigin = ( latitudeOfNaturalOrigin != 0 && inDegrees ) ? Math.toRadians( latitudeOfNaturalOrigin ) 1378 : latitudeOfNaturalOrigin; 1379 1380 double longitudeOfNaturalOrigin = XMLTools.getNodeAsDouble( projectionElement, 1381 PRE + "longitudeOfNaturalOrigin", 1382 nsContext, 1383 0 ); 1384 inDegrees = XMLTools.getNodeAsBoolean( projectionElement, 1385 PRE + "longitudeOfNaturalOrigin/@inDegrees", 1386 nsContext, 1387 true ); 1388 longitudeOfNaturalOrigin = ( longitudeOfNaturalOrigin != 0 && inDegrees ) ? Math.toRadians( longitudeOfNaturalOrigin ) 1389 : longitudeOfNaturalOrigin; 1390 1391 double scaleFactor = XMLTools.getNodeAsDouble( projectionElement, PRE + "scaleFactor", nsContext, 0 ); 1392 double falseEasting = XMLTools.getNodeAsDouble( projectionElement, PRE + "falseEasting", nsContext, 0 ); 1393 double falseNorthing = XMLTools.getNodeAsDouble( projectionElement, PRE + "falseNorthing", nsContext, 0 ); 1394 1395 String projectionName = projectionElement.getLocalName().trim(); 1396 Point2d naturalOrigin = new Point2d( longitudeOfNaturalOrigin, latitudeOfNaturalOrigin ); 1397 1398 if ( "transverseMercator".equalsIgnoreCase( projectionName ) ) { 1399 // change schema to let projection be identifiable. fix method geodetic 1400 boolean northernHemi = XMLTools.getNodeAsBoolean( projectionElement, 1401 PRE + "northernHemisphere", 1402 nsContext, 1403 true ); 1404 result = new TransverseMercator( northernHemi, 1405 underlyingCRS, 1406 falseNorthing, 1407 falseEasting, 1408 naturalOrigin, 1409 units, 1410 scaleFactor, 1411 id.getIdentifiers(), 1412 id.getNames(), 1413 id.getVersions(), 1414 id.getDescriptions(), 1415 id.getAreasOfUse() ); 1416 } else if ( "lambertAzimuthalEqualArea".equalsIgnoreCase( projectionName ) ) { 1417 result = new LambertAzimuthalEqualArea( underlyingCRS, 1418 falseNorthing, 1419 falseEasting, 1420 naturalOrigin, 1421 units, 1422 scaleFactor, 1423 id.getIdentifiers(), 1424 id.getNames(), 1425 id.getVersions(), 1426 id.getDescriptions(), 1427 id.getAreasOfUse() ); 1428 } else if ( "lambertConformalConic".equalsIgnoreCase( projectionName ) ) { 1429 double firstP = XMLTools.getNodeAsDouble( projectionElement, 1430 PRE + "firstParallelLatitude", 1431 nsContext, 1432 Double.NaN ); 1433 inDegrees = XMLTools.getNodeAsBoolean( projectionElement, 1434 PRE + "firstParallelLatitude/@inDegrees", 1435 nsContext, 1436 true ); 1437 firstP = ( !Double.isNaN( firstP ) && inDegrees ) ? Math.toRadians( firstP ) : firstP; 1438 1439 double secondP = XMLTools.getNodeAsDouble( projectionElement, 1440 PRE + "secondParallelLatitude", 1441 nsContext, 1442 Double.NaN ); 1443 inDegrees = XMLTools.getNodeAsBoolean( projectionElement, 1444 PRE + "secondParallelLatitude/@inDegrees", 1445 nsContext, 1446 true ); 1447 secondP = ( !Double.isNaN( secondP ) && inDegrees ) ? Math.toRadians( secondP ) : secondP; 1448 result = new LambertConformalConic( firstP, 1449 secondP, 1450 underlyingCRS, 1451 falseNorthing, 1452 falseEasting, 1453 naturalOrigin, 1454 units, 1455 scaleFactor, 1456 id.getIdentifiers(), 1457 id.getNames(), 1458 id.getVersions(), 1459 id.getDescriptions(), 1460 id.getAreasOfUse() ); 1461 } else if ( "stereographicAzimuthal".equalsIgnoreCase( projectionName ) ) { 1462 double trueScaleL = XMLTools.getNodeAsDouble( projectionElement, 1463 PRE + "trueScaleLatitude", 1464 nsContext, 1465 Double.NaN ); 1466 inDegrees = XMLTools.getNodeAsBoolean( projectionElement, 1467 PRE + "trueScaleLatitude/@inDegrees", 1468 nsContext, 1469 true ); 1470 trueScaleL = ( !Double.isNaN( trueScaleL ) && inDegrees ) ? Math.toRadians( trueScaleL ) 1471 : trueScaleL; 1472 1473 result = new StereographicAzimuthal( trueScaleL, 1474 underlyingCRS, 1475 falseNorthing, 1476 falseEasting, 1477 naturalOrigin, 1478 units, 1479 scaleFactor, 1480 id.getIdentifiers(), 1481 id.getNames(), 1482 id.getVersions(), 1483 id.getDescriptions(), 1484 id.getAreasOfUse() ); 1485 } else { 1486 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJECTEDCRS_INVALID_PROJECTION", 1487 id.getIdentifier(), 1488 usedProjection ) ); 1489 1490 } 1491 for ( String identifier : id.getIdentifiers() ) { 1492 projections.put( identifier, result ); 1493 } 1494 if ( checkForDoubleDefinition ) { 1495 checkForUniqueness( cachedProjections, doubleProjections, result ); 1496 } 1497 // remove the child, thus resulting in a smaller xml-tree. 1498 rootElement.removeChild( projectionElement ); 1499 return result; 1500 } catch ( XMLParsingException e ) { 1501 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", 1502 "projection parameters", 1503 ( ( projectionElement == null ) ? "null" 1504 : projectionElement.getLocalName() ), 1505 e.getMessage() ), 1506 e ); 1507 1508 } 1509 } 1510 1511 return null; 1512 } 1513 1514 /** 1515 * @param crsElement 1516 * from which the crs is to be created (using cached datums, conversioninfos and projections). 1517 * @return a geocentric coordinatesystem based on the given xml-element. 1518 * @throws CRSConfigurationException 1519 * if a required element could not be found, or an xmlParsingException occurred. 1520 */ 1521 private CoordinateSystem parseGeocentricCRS( Element crsElement ) 1522 throws CRSConfigurationException { 1523 Identifiable id = parseIdentifiable( crsElement ); 1524 Axis[] axis = parseAxisOrder( crsElement ); 1525 GeodeticDatum usedDatum = parseReferencedGeodeticDatum( crsElement, id.getIdentifier() ); 1526 GeocentricCRS result = new GeocentricCRS( usedDatum, 1527 axis, 1528 id.getIdentifiers(), 1529 id.getNames(), 1530 id.getVersions(), 1531 id.getDescriptions(), 1532 id.getAreasOfUse() ); 1533 for ( String identifier : id.getIdentifiers() ) { 1534 geocentricCRSs.put( identifier, result ); 1535 } 1536 rootElement.removeChild( crsElement ); 1537 return result; 1538 } 1539 1540 /** 1541 * Parses the required usedDatum element from the given parentElement (probably a crs element). 1542 * 1543 * @param parentElement 1544 * to parse the required usedDatum element from. 1545 * @param parentID 1546 * optional for an appropriate error message. 1547 * @return the Datum. 1548 * @throws CRSConfigurationException 1549 * if a parsing error occurred, the node was not defined or an illegal id reference (not found) was 1550 * given. 1551 */ 1552 private GeodeticDatum parseReferencedGeodeticDatum( Element parentElement, String parentID ) 1553 throws CRSConfigurationException { 1554 String datumID = null; 1555 try { 1556 datumID = XMLTools.getRequiredNodeAsString( parentElement, PRE + "usedDatum", nsContext ); 1557 } catch ( XMLParsingException e ) { 1558 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", 1559 "datumID", 1560 ( ( parentElement == null ) ? "null" 1561 : parentElement.getLocalName() ), 1562 e.getMessage() ), 1563 e ); 1564 } 1565 if ( datumID == null || "".equals( datumID.trim() ) ) { 1566 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_REFERENCE_ID_IS_EMPTY", 1567 "usedDatum", 1568 parentID ) ); 1569 } 1570 GeodeticDatum usedDatum = getGeodeticDatumFromID( datumID ); 1571 if ( usedDatum == null ) { 1572 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_USEDDATUM_IS_NULL", datumID, parentID ) ); 1573 } 1574 return usedDatum; 1575 } 1576 1577 /** 1578 * Parses all elements of the identifiable object. 1579 * 1580 * @param element 1581 * the xml-representation of the id-object 1582 * @return the identifiable object or <code>null</code> if no id was given. 1583 * @throws CRSConfigurationException 1584 */ 1585 private Identifiable parseIdentifiable( Element element ) 1586 throws CRSConfigurationException { 1587 try { 1588 String[] identifiers = XMLTools.getNodesAsStrings( element, PRE + "id", nsContext ); 1589 if ( identifiers == null || identifiers.length == 0 ) { 1590 String msg = Messages.getMessage( "CRS_CONFIG_NO_ID", ( ( element == null ) ? "null" 1591 : element.getLocalName() ) ); 1592 throw new CRSConfigurationException( msg ); 1593 } 1594 for ( int i = 0; i < identifiers.length; ++i ) { 1595 if ( identifiers[i] != null ) { 1596 identifiers[i] = identifiers[i].toUpperCase().trim(); 1597 } 1598 } 1599 String[] names = XMLTools.getNodesAsStrings( element, PRE + "name", nsContext ); 1600 String[] versions = XMLTools.getNodesAsStrings( element, PRE + "version", nsContext ); 1601 String[] descriptions = XMLTools.getNodesAsStrings( element, PRE + "description", nsContext ); 1602 String[] areasOfUse = XMLTools.getNodesAsStrings( element, PRE + "areaOfuse", nsContext ); 1603 return new Identifiable( identifiers, names, versions, descriptions, areasOfUse ); 1604 } catch ( XMLParsingException e ) { 1605 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", 1606 "Identifiable", 1607 ( ( element == null ) ? "null" 1608 : element.getLocalName() ), 1609 e.getMessage() ), e ); 1610 } 1611 } 1612 1613 /** 1614 * Creates an axis array for the given crs element. 1615 * 1616 * @param crsElement 1617 * to be parsed 1618 * @return an Array of axis defining their order. 1619 * @throws CRSConfigurationException 1620 * if a required element could not be found, or an xmlParsingException occurred, or the axisorder uses 1621 * names which were not defined in the axis elements. 1622 */ 1623 private Axis[] parseAxisOrder( Element crsElement ) 1624 throws CRSConfigurationException { 1625 String axisOrder = null; 1626 try { 1627 axisOrder = XMLTools.getRequiredNodeAsString( crsElement, PRE + "axisOrder", nsContext ); 1628 } catch ( XMLParsingException e ) { 1629 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", 1630 "AxisOrder", 1631 ( ( crsElement == null ) ? "null" 1632 : crsElement.getLocalName() ), 1633 e.getMessage() ), 1634 e ); 1635 } 1636 if ( axisOrder == null || "".equals( axisOrder.trim() ) ) { 1637 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", 1638 "AxisOrder", 1639 ( ( crsElement == null ) ? "null" 1640 : crsElement.getLocalName() ), 1641 " axisOrder element may not be empty" ) ); 1642 } 1643 axisOrder = axisOrder.trim(); 1644 String[] order = axisOrder.trim().split( "," ); 1645 Axis[] axis = new Axis[order.length]; 1646 String XPATH = PRE + "Axis[" + PRE + "name = '"; 1647 for ( int i = 0; i < order.length; ++i ) { 1648 String t = order[i]; 1649 if ( t != null && !"".equals( t.trim() ) ) { 1650 t = t.trim(); 1651 try { 1652 Element axisElement = XMLTools.getRequiredElement( crsElement, XPATH + t + "']", nsContext ); 1653 String axisOrientation = XMLTools.getRequiredNodeAsString( axisElement, 1654 PRE + "axisOrientation", 1655 nsContext ); 1656 Unit unit = parseUnit( axisElement ); 1657 axis[i] = new Axis( unit, t, axisOrientation ); 1658 } catch ( XMLParsingException e ) { 1659 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", 1660 "Axis", 1661 ( ( crsElement == null ) ? "null" 1662 : crsElement.getLocalName() ), 1663 e.getMessage() ), 1664 e ); 1665 } 1666 } 1667 } 1668 return axis; 1669 } 1670 1671 /** 1672 * Parses a unit from the given xml-parent, if the unit is degree, it will be mapped to radians. 1673 * 1674 * @param parent 1675 * xml-node to parse the unit from. 1676 * @return the unit object. 1677 * @throws CRSConfigurationException 1678 * if the unit object could not be created. 1679 */ 1680 private Unit parseUnit( Element parent ) 1681 throws CRSConfigurationException { 1682 String units = null; 1683 try { 1684 units = XMLTools.getNodeAsString( parent, PRE + "units", nsContext, null ); 1685 } catch ( XMLParsingException e ) { 1686 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", 1687 "units", 1688 ( ( parent == null ) ? "null" 1689 : parent.getLocalName() ), 1690 e.getMessage() ), e ); 1691 } 1692 if ( Unit.DEGREE.getName().equals( units ) ) { 1693 units = Unit.RADIAN.getName(); 1694 } 1695 Unit unit = Unit.createUnitFromString( units ); 1696 if ( unit == null ) { 1697 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", 1698 "units", 1699 ( ( parent == null ) ? "null" 1700 : parent.getLocalName() ), 1701 "unknown unit: " + units ) ); 1702 } 1703 return unit; 1704 } 1705 1706 /** 1707 * @param <T> should be at least of Type Identifiable. 1708 * @param uniqueList to check against 1709 * @param mapping to added the id of T to if it is found duplicate. 1710 * @param toBeChecked to check. 1711 */ 1712 private <T extends Identifiable> void checkForUniqueness( List<T> uniqueList, Map<String, String> mapping, T toBeChecked){ 1713 if ( uniqueList.contains( toBeChecked ) ) { 1714 int index = uniqueList.indexOf( toBeChecked ); 1715 LOG.logInfo( "The Identifiable with id: " + toBeChecked.getIdentifier() 1716 + " was found to be equal with: " 1717 + uniqueList.get( index ).getIdentifier() ); 1718 String key = uniqueList.get( index ).getIdentifier(); 1719 if ( key != null && !"".equals( key.trim() ) ) { 1720 String value = mapping.get( key ); 1721 //it would be nicest to get the epsg code if any. 1722 if ( !key.startsWith( "EPSG:" ) && toBeChecked.getIdentifier().startsWith( "EPSG:" ) ) { 1723 if ( value == null || "".equals( value ) ) { 1724 value = key; 1725 } else { 1726 value += ", " + key; 1727 } 1728 mapping.remove( key ); 1729 key = toBeChecked.getIdentifier(); 1730 } else { 1731 if ( value == null || "".equals( value ) ) { 1732 value = toBeChecked.getIdentifier(); 1733 } else { 1734 value += ", " + toBeChecked.getIdentifier(); 1735 } 1736 } 1737 mapping.put( key, value ); 1738 } 1739 } else { 1740 uniqueList.add( toBeChecked ); 1741 } 1742 } 1743 }