001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/crs/configuration/deegree/DeegreeCRSProvider.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.deegree; 038 039 import java.io.ByteArrayOutputStream; 040 import java.io.PrintWriter; 041 import java.io.UnsupportedEncodingException; 042 import java.lang.reflect.Constructor; 043 import java.lang.reflect.InvocationTargetException; 044 import java.util.LinkedList; 045 import java.util.List; 046 import java.util.Properties; 047 048 import org.deegree.crs.Identifiable; 049 import org.deegree.crs.configuration.AbstractCRSProvider; 050 import org.deegree.crs.coordinatesystems.CoordinateSystem; 051 import org.deegree.crs.exceptions.CRSConfigurationException; 052 import org.deegree.crs.projections.Projection; 053 import org.deegree.crs.transformations.Transformation; 054 import org.deegree.framework.log.ILogger; 055 import org.deegree.framework.log.LoggerFactory; 056 import org.deegree.framework.util.CharsetUtils; 057 import org.deegree.framework.xml.NamespaceContext; 058 import org.deegree.framework.xml.XMLParsingException; 059 import org.deegree.framework.xml.XMLTools; 060 import org.deegree.i18n.Messages; 061 import org.deegree.ogcbase.CommonNamespaces; 062 import org.w3c.dom.Element; 063 064 /** 065 * The <code>DeegreeCRSProvider</code> reads the deegree crs-config (based on it's own xml-schema) and creates the 066 * CRS's (and their datums, conversion info's, ellipsoids and projections) if requested. 067 * <p> 068 * Attention, although urn's are case-sensitive, the deegreeCRSProvider is not. All incoming id's are toLowerCased! 069 * </p> 070 * <h2>Automatic loading of projection/transformation classes</h2> 071 * It is possible to create your own projection/transformation classes, which can be automatically loaded. 072 * <p> 073 * You can achieve this loading by supplying the <b><code>class</code></b> attribute to a 074 * <code>crs:projectedCRS/crs:projection</code> or <code>crs:coordinateSystem/crs:transformation</code> element in 075 * the 'deegree-crs-configuration.xml'. This attribute must contain the full class name (with package), e.g. 076 * <crs:projection class='my.package.and.projection.Implementation'> 077 * </p> 078 * Because the loading is done with reflections your classes must sustain following criteria: 079 * <h3>Projections</h3> 080 * <ol> 081 * <li>It must be a sub class of {@link org.deegree.crs.projections.Projection}</li> 082 * <li>A constructor with following signature must be supplied: <br/> <code> 083 * public MyProjection( <br/> 084 *     {@link org.deegree.crs.coordinatesystems.GeographicCRS} underlyingCRS,<br/> 085 *     double falseNorthing,<br/> 086 *     double falseEasting,<br/> 087 *     javax.vecmath.Point2d naturalOrigin,<br/> 088 *     {@link org.deegree.crs.components.Unit} units,<br/> 089 *     double scale,<br/> 090 *     java.util.List<org.w3c.dom.Element> yourProjectionElements<br/> 091 * );<br/> 092 * </code> 093 * <p> 094 * The first six parameters are common to all projections (for an explanation of their meaning take a look at 095 * {@link Projection}). The last list, will contain all xml-dom elements you supplied in the deegree configuration 096 * (child elements of the crs:projection/crs:MyProjection), thus relieving you of the parsing of the 097 * deegree-crs-configuration.xml document. 098 * </p> 099 * </li> 100 * </ol> 101 * <h3>Transformations</h3> 102 * <ol> 103 * <li>It must be a sub class of {@link org.deegree.crs.transformations.polynomial.PolynomialTransformation}</li> 104 * <li>A constructor with following signature must be supplied: <br/> <code> 105 * public MyTransformation( <br/> 106 *     java.util.list<Double> aValues,<br/> 107 *     java.util.list<Double> bValues,<br/> 108 *     {@link org.deegree.crs.coordinatesystems.CoordinateSystem} targetCRS,<br/> 109 *     java.util.List<org.w3c.dom.Element> yourTransformationElements<br/> 110 * );<br/> 111 * </code> 112 * <p> 113 * The first three parameters are common to all polynomial values (for an explanation of their meaning take a look at 114 * {@link org.deegree.crs.transformations.polynomial.PolynomialTransformation}). Again, the last list, will contain all 115 * xml-dom elements you supplied in the deegree configuration (child elements of the 116 * crs:transformation/crs:MyTransformation), thus relieving you of the parsing of the deegree-crs-configuration.xml 117 * document. 118 * </p> 119 * </li> 120 * </ol> 121 * 122 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> 123 * 124 * @author last edited by: $Author: rbezema $ 125 * 126 * @version $Revision: 18308 $, $Date: 2009-07-02 14:40:19 +0200 (Do, 02. Jul 2009) $ 127 * 128 */ 129 130 public class DeegreeCRSProvider extends AbstractCRSProvider<Element> { 131 132 private static ILogger LOG = LoggerFactory.getLogger( DeegreeCRSProvider.class ); 133 134 private CRSExporter exporter; 135 136 /** 137 * The namespaces used in deegree. 138 */ 139 private static NamespaceContext nsContext = CommonNamespaces.getNamespaceContext(); 140 141 /** 142 * The prefix to use. 143 */ 144 private final static String PRE = CommonNamespaces.CRS_PREFIX + ":"; 145 146 // /** 147 // * The EPSG-Database defines only 48 different ellipsoids, set to 60, will --probably-- result in no collisions. 148 // */ 149 // private final Map<String, Ellipsoid> ellipsoids = new HashMap<String, Ellipsoid>( 60 * 5 ); 150 // 151 // /** 152 // * The EPSG-Database defines over 400 different Geodetic Datums, set to 450, will --probably-- result in no 153 // * collisions. 154 // */ 155 // private final Map<String, GeodeticDatum> datums = new HashMap<String, GeodeticDatum>( 450 * 5 ); 156 // 157 // /** 158 // * The EPSG-Database defines over 1100 different CoordinateTransformations, set to 1200, will --probably-- result 159 // in 160 // * no collisions. 161 // */ 162 // private final Map<String, Helmert> conversionInfos = new HashMap<String, Helmert>( 1200 * 5 ); 163 // 164 // /** 165 // * Theoretically infinite prime meridians could be defined, let's set it to a more practical number of 42. 166 // */ 167 // private final Map<String, PrimeMeridian> primeMeridians = new HashMap<String, PrimeMeridian>( 42 * 5 ); 168 // 169 // /** 170 // * The EPSG-Database defines over 2960 different ProjectedCRS's, set to 3500, will --probably-- result in no 171 // * collisions. 172 // */ 173 // private final Map<String, ProjectedCRS> projectedCRSs = new HashMap<String, ProjectedCRS>( 3500 * 5 ); 174 // 175 // /** 176 // * The EPSG-Database defines over 490 different GeographicCRS's (geodetic), set to 600, will --probably-- result 177 // in 178 // * no collisions. 179 // */ 180 // private final Map<String, GeographicCRS> geographicCRSs = new HashMap<String, GeographicCRS>( 600 * 5 ); 181 // 182 // /** 183 // * The EPSG-Database defines ??? 184 // */ 185 // private final Map<String, CompoundCRS> compoundCRSs = new HashMap<String, CompoundCRS>( 600 * 5 ); 186 // 187 // /** 188 // * The EPSG-Database doesn't define GeocentricCRS's, set to 30, will --probably-- result in no collisions. 189 // */ 190 // private final Map<String, GeocentricCRS> geocentricCRSs = new HashMap<String, GeocentricCRS>( 30 ); 191 // 192 // private final List<GeocentricCRS> cachedGeocentricCRSs = new LinkedList<GeocentricCRS>(); 193 // 194 // private final Map<String, String> doubleGeocentricCRSs = new HashMap<String, String>( 5000 ); 195 // 196 // private final List<CompoundCRS> cachedCompoundCRSs = new LinkedList<CompoundCRS>(); 197 // 198 // private final Map<String, String> doubleCompoundCRSs = new HashMap<String, String>( 5000 ); 199 // 200 // private final List<GeographicCRS> cachedGeoCRSs = new LinkedList<GeographicCRS>(); 201 // 202 // private final Map<String, String> doubleGeos = new HashMap<String, String>( 5000 ); 203 // 204 // private final List<ProjectedCRS> cachedProjCRSs = new LinkedList<ProjectedCRS>(); 205 // 206 // private final Map<String, String> doubleProjCRS = new HashMap<String, String>( 3000 ); 207 // 208 // private final List<GeodeticDatum> cachedDatums = new LinkedList<GeodeticDatum>(); 209 // 210 // private final Map<String, String> doubleDatums = new HashMap<String, String>( 3000 ); 211 // 212 // private final List<Helmert> cachedToWGS = new LinkedList<Helmert>(); 213 // 214 // private final Map<String, String> doubleToWGS = new HashMap<String, String>( 3000 ); 215 // 216 // private final List<Ellipsoid> cachedEllipsoids = new LinkedList<Ellipsoid>(); 217 // 218 // private final Map<String, String> doubleEllipsoids = new HashMap<String, String>( 3000 ); 219 // 220 // private final List<PrimeMeridian> cachedMeridians = new LinkedList<PrimeMeridian>(); 221 // 222 // private final Map<String, String> doubleMeridians = new HashMap<String, String>( 3000 ); 223 // 224 // private final Map<String, String> doubleProjections = new HashMap<String, String>( 3000 ); 225 226 // /** 227 // * The root element of the deegree - crs - configuration. 228 // */ 229 // private Element rootElement; 230 231 // private boolean checkForDoubleDefinition = false; 232 233 /** 234 * @param properties 235 * containing information about the crs resource class and the file location of the crs configuration. If 236 * either is null the default mechanism is using the {@link CRSParser} and the 237 * deegree-crs-configuration.xml 238 * @throws CRSConfigurationException 239 * if the give file or the default-crs-configuration.xml file could not be loaded. 240 */ 241 public DeegreeCRSProvider( Properties properties ) throws CRSConfigurationException { 242 super( properties, CRSParser.class, null ); 243 if ( getResolver() == null ) { 244 CRSParser versionedParser = new CRSParser( this, new Properties( properties ) ); 245 String version = versionedParser.getVersion(); 246 if ( !"".equals( version ) ) { 247 version = version.trim().replaceAll( "\\.", "_" ); 248 String className = "org.deegree.crs.configuration.deegree.CRSParser_" + version; 249 try { 250 Class<?> tClass = Class.forName( className ); 251 tClass.asSubclass( CRSParser.class ); 252 LOG.logDebug( "Trying to load configured CRS provider from classname: " + className ); 253 Constructor<?> constructor = tClass.getConstructor( this.getClass(), Properties.class, 254 Element.class ); 255 if ( constructor == null ) { 256 LOG.logError( "No constructor ( " + this.getClass() + ", Properties.class) found in class:" 257 + className ); 258 } else { 259 versionedParser = (CRSParser) constructor.newInstance( this, new Properties( properties ), 260 versionedParser.getRootElement() ); 261 } 262 className = "org.deegree.crs.configuration.deegree.CRSExporter_" + version; 263 try { 264 tClass = Class.forName( className ); 265 tClass.asSubclass( CRSExporter.class ); 266 constructor = tClass.getConstructor( Properties.class ); 267 LOG.logDebug( "Trying to load configured CRS exporter for version: " + version 268 + " from classname: " + className ); 269 } catch ( ClassNotFoundException e ) { 270 LOG.logDebug( "Could not load the exporter for version 1, using fallback mechanism." ); 271 constructor = null; 272 } 273 274 if ( constructor == null ) { 275 exporter = new CRSExporter( new Properties( properties ) ); 276 LOG.logDebug( "No constructor ( Properties.class ) found in class:" + className ); 277 } else { 278 versionedParser = (CRSParser) constructor.newInstance( new Properties( properties ) ); 279 } 280 } catch ( InstantiationException e ) { 281 LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", className, e.getMessage() ) ); 282 } catch ( IllegalAccessException e ) { 283 LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", className, e.getMessage() ), e ); 284 } catch ( ClassNotFoundException e ) { 285 LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", className, e.getMessage() ), e ); 286 } catch ( SecurityException e ) { 287 LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", className, e.getMessage() ), e ); 288 } catch ( NoSuchMethodException e ) { 289 LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", className, e.getMessage() ), e ); 290 } catch ( IllegalArgumentException e ) { 291 LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", className, e.getMessage() ), e ); 292 } catch ( InvocationTargetException e ) { 293 LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", className, e.getMessage() ), e ); 294 } catch ( Throwable t ) { 295 LOG.logError( Messages.getMessage( "CRS_CONFIG_INSTANTIATION_ERROR", className, t.getMessage() ), t ); 296 } 297 } else { 298 exporter = new CRSExporter( new Properties( properties ) ); 299 } 300 setResolver( versionedParser ); 301 } 302 } 303 304 public boolean canExport() { 305 return exporter != null; 306 } 307 308 public void export( StringBuilder sb, List<CoordinateSystem> crsToExport ) { 309 if ( exporter == null ) { 310 throw new UnsupportedOperationException( "Exporting is not supported for this deegree-crs version" ); 311 } 312 ByteArrayOutputStream out = new ByteArrayOutputStream(); 313 PrintWriter writer = new PrintWriter( out ); 314 exporter.export( writer, crsToExport ); 315 316 try { 317 sb.append( out.toString( CharsetUtils.getSystemCharset() ) ); 318 } catch ( UnsupportedEncodingException e ) { 319 LOG.logError( e ); 320 } 321 322 } 323 324 /** 325 * @return the casted resolver of the super class. 326 */ 327 @Override 328 public CRSParser getResolver() { 329 return (CRSParser) super.getResolver(); 330 } 331 332 public List<String> getAvailableCRSIds() 333 throws CRSConfigurationException { 334 List<Element> allCRSIDs = new LinkedList<Element>(); 335 336 try { 337 allCRSIDs.addAll( XMLTools.getElements( getResolver().getRootElement(), "//" + PRE + "geographicCRS/" + PRE 338 + "id", nsContext ) ); 339 allCRSIDs.addAll( XMLTools.getElements( getResolver().getRootElement(), "//" + PRE + "projectedCRS/" + PRE 340 + "id", nsContext ) ); 341 allCRSIDs.addAll( XMLTools.getElements( getResolver().getRootElement(), "//" + PRE + "geocentricCRS/" + PRE 342 + "id", nsContext ) ); 343 allCRSIDs.addAll( XMLTools.getElements( getResolver().getRootElement(), "//" + PRE + "compoundCRS/" + PRE 344 + "id", nsContext ) ); 345 } catch ( XMLParsingException e ) { 346 throw new CRSConfigurationException( 347 Messages.getMessage( "CRS_CONFIG_GET_ALL_ELEMENT_IDS", e.getMessage() ), 348 e ); 349 } 350 List<String> result = new LinkedList<String>(); 351 for ( Element crs : allCRSIDs ) { 352 if ( crs != null ) { 353 result.add( XMLTools.getStringValue( crs ) ); 354 } 355 } 356 return result; 357 } 358 359 public List<CoordinateSystem> getAvailableCRSs() 360 throws CRSConfigurationException { 361 List<CoordinateSystem> allSystems = new LinkedList<CoordinateSystem>(); 362 if ( getResolver().getRootElement() != null ) { 363 List<Element> allCRSIDs = new LinkedList<Element>(); 364 365 try { 366 allCRSIDs.addAll( XMLTools.getElements( getResolver().getRootElement(), "//" + PRE + "geographicCRS/" 367 + PRE + "id", nsContext ) ); 368 allCRSIDs.addAll( XMLTools.getElements( getResolver().getRootElement(), "//" + PRE + "projectedCRS/" 369 + PRE + "id", nsContext ) ); 370 allCRSIDs.addAll( XMLTools.getElements( getResolver().getRootElement(), "//" + PRE + "geocentricCRS/" 371 + PRE + "id", nsContext ) ); 372 allCRSIDs.addAll( XMLTools.getElements( getResolver().getRootElement(), "//" + PRE + "compoundCRS/" 373 + PRE + "id", nsContext ) ); 374 } catch ( XMLParsingException e ) { 375 throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_GET_ALL_ELEMENT_IDS", 376 e.getMessage() ), e ); 377 } 378 final int total = allCRSIDs.size(); 379 int count = 0; 380 int percentage = (int) Math.round( total / 100.d ); 381 int number = 0; 382 System.out.println( "Trying to create a total of " + total + " coordinate systems." ); 383 for ( Element crsID : allCRSIDs ) { 384 if ( crsID != null ) { 385 String id = crsID.getTextContent(); 386 if ( id != null && !"".equals( id.trim() ) ) { 387 if ( count++ % percentage == 0 ) { 388 System.out.print( "\r" + ( number ) + ( ( number++ < 10 ) ? " " : " " ) + "% created" ); 389 } 390 allSystems.add( getCRSByID( id ) ); 391 } 392 } 393 } 394 System.out.println(); 395 // if ( checkForDoubleDefinition ) { 396 // allSystems.addAll( cachedGeoCRSs ); 397 // allSystems.addAll( cachedProjCRSs ); 398 // allSystems.addAll( cachedGeocentricCRSs ); 399 // allSystems.addAll( cachedCompoundCRSs ); 400 // StringBuilder cachedGeos = new StringBuilder( "Cached Geographic coordinatesystems (" ); 401 // cachedGeos.append( cachedGeoCRSs.size() ).append( "):\n" ); 402 // for ( GeographicCRS geo : cachedGeoCRSs ) { 403 // cachedGeos.append( geo.getIdentifier() ).append( "\n" ); 404 // } 405 // System.out.println( cachedGeos.toString() ); 406 // if ( !doubleGeos.isEmpty() ) { 407 // Set<String> keys = doubleGeos.keySet(); 408 // LOG.logInfo( "Following geographic crs's could probably be mapped on eachother" ); 409 // for ( String key : keys ) { 410 // LOG.logInfo( key + " : " + doubleGeos.get( key ) ); 411 // } 412 // } 413 // if ( !doubleProjCRS.isEmpty() ) { 414 // Set<String> keys = doubleProjCRS.keySet(); 415 // LOG.logInfo( "Following projected crs's could probably be mapped on eachother" ); 416 // for ( String key : keys ) { 417 // LOG.logInfo( key + " : " + doubleProjCRS.get( key ) ); 418 // } 419 // 420 // } 421 // if ( !doubleGeocentricCRSs.isEmpty() ) { 422 // Set<String> keys = doubleGeocentricCRSs.keySet(); 423 // LOG.logInfo( "Following geocentric crs's could probably be mapped on eachother" ); 424 // for ( String key : keys ) { 425 // LOG.logInfo( key + " : " + doubleGeocentricCRSs.get( key ) ); 426 // } 427 // } 428 // 429 // if ( !doubleProjections.isEmpty() ) { 430 // Set<String> keys = doubleProjections.keySet(); 431 // LOG.logInfo( "Following projections could probably be mapped on eachother" ); 432 // for ( String key : keys ) { 433 // LOG.logInfo( key + " : " + doubleProjections.get( key ) ); 434 // } 435 // } 436 // if ( !doubleDatums.isEmpty() ) { 437 // Set<String> keys = doubleDatums.keySet(); 438 // LOG.logInfo( "Following datums could probably be mapped on eachother" ); 439 // for ( String key : keys ) { 440 // LOG.logInfo( key + " : " + doubleDatums.get( key ) ); 441 // } 442 // } 443 // if ( !doubleToWGS.isEmpty() ) { 444 // Set<String> keys = doubleToWGS.keySet(); 445 // LOG.logInfo( "Following wgs conversion infos could probably be mapped on eachother" ); 446 // for ( String key : keys ) { 447 // LOG.logInfo( key + " : " + doubleToWGS.get( key ) ); 448 // } 449 // } 450 // if ( !doubleEllipsoids.isEmpty() ) { 451 // Set<String> keys = doubleEllipsoids.keySet(); 452 // LOG.logInfo( "Following ellipsoids could probably be mapped on eachother" ); 453 // for ( String key : keys ) { 454 // LOG.logInfo( key + " : " + doubleEllipsoids.get( key ) ); 455 // } 456 // } 457 // if ( !doubleMeridians.isEmpty() ) { 458 // Set<String> keys = doubleEllipsoids.keySet(); 459 // LOG.logInfo( "Following prime meridians could probably be mapped on eachother" ); 460 // for ( String key : keys ) { 461 // LOG.logInfo( key + " : " + doubleMeridians.get( key ) ); 462 // } 463 // } 464 // } else { 465 // Collection<CompoundCRS> cCRSs = compoundCRSs.values(); 466 // for ( String key : compoundCRSs.keySet() ) { 467 // CompoundCRS crs = compoundCRSs.get( key ); 468 // if ( !allSystems.contains( crs ) ) { 469 // allSystems.add( crs ); 470 // } 471 // } 472 // for ( String key : geographicCRSs.keySet() ) { 473 // GeographicCRS crs = geographicCRSs.get( key ); 474 // if ( !allSystems.contains( crs ) ) { 475 // allSystems.add( crs ); 476 // } 477 // } 478 // for ( String key : geocentricCRSs.keySet() ) { 479 // GeocentricCRS crs = geocentricCRSs.get( key ); 480 // if ( !allSystems.contains( crs ) ) { 481 // allSystems.add( crs ); 482 // } 483 // } 484 // for ( String key : projectedCRSs.keySet() ) { 485 // ProjectedCRS crs = projectedCRSs.get( key ); 486 // if ( !allSystems.contains( crs ) ) { 487 // allSystems.add( crs ); 488 // } 489 // } 490 // } 491 492 } else { 493 LOG.logDebug( "The root element is null, is this correct behaviour?" ); 494 } 495 return allSystems; 496 } 497 498 public Identifiable getIdentifiable( String id ) 499 throws CRSConfigurationException { 500 Identifiable result = getCachedIdentifiable( id ); 501 if ( result == null ) { 502 result = getResolver().parseIdentifiableObject( id ); 503 } 504 return result; 505 } 506 507 @Override 508 protected CoordinateSystem parseCoordinateSystem( Element crsDefinition ) 509 throws CRSConfigurationException { 510 return getResolver().parseCoordinateSystem( crsDefinition ); 511 } 512 513 @Override 514 public Transformation parseTransformation( Element transformationDefinition ) 515 throws CRSConfigurationException { 516 return getResolver().parseTransformation( transformationDefinition ); 517 518 } 519 520 public Transformation getTransformation( CoordinateSystem sourceCRS, CoordinateSystem targetCRS ) 521 throws CRSConfigurationException { 522 return getResolver().getTransformation( sourceCRS, targetCRS ); 523 } 524 525 }