001 //$HeadURL: svn+ssh://developername@svn.wald.intevation.org/deegree/base/trunk/src/org/deegree/model/csct/cs/ConvenienceCSFactory.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2007 by: 006 lat/lon GmbH 007 http://www.lat-lon.de 008 009 This library is free software; you can redistribute it and/or 010 modify it under the terms of the GNU Lesser General Public 011 License as published by the Free Software Foundation; either 012 version 2.1 of the License, or (at your option) any later version. 013 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 019 You should have received a copy of the GNU Lesser General Public 020 License along with this library; if not, write to the Free Software 021 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 022 023 Contact: 024 025 Andreas Poth 026 lat/lon GmbH 027 Aennchenstr. 19 028 53115 Bonn 029 Germany 030 E-Mail: poth@lat-lon.de 031 032 ---------------------------------------------------------------------------*/ 033 package org.deegree.model.crs; 034 035 import java.awt.geom.Point2D; 036 import java.net.URI; 037 import java.net.URL; 038 import java.util.HashMap; 039 040 import org.deegree.framework.log.ILogger; 041 import org.deegree.framework.log.LoggerFactory; 042 import org.deegree.framework.util.BootLogger; 043 import org.deegree.framework.xml.NamespaceContext; 044 import org.deegree.framework.xml.XMLFragment; 045 import org.deegree.framework.xml.XMLParsingException; 046 import org.deegree.framework.xml.XMLTools; 047 import org.deegree.model.crs.CRSException; 048 import org.deegree.model.crs.CSAccess; 049 import org.deegree.model.csct.cs.AxisInfo; 050 import org.deegree.model.csct.cs.CoordinateSystem; 051 import org.deegree.model.csct.cs.CoordinateSystemFactory; 052 import org.deegree.model.csct.cs.Datum; 053 import org.deegree.model.csct.cs.DatumType; 054 import org.deegree.model.csct.cs.Ellipsoid; 055 import org.deegree.model.csct.cs.GeographicCoordinateSystem; 056 import org.deegree.model.csct.cs.HorizontalDatum; 057 import org.deegree.model.csct.cs.PrimeMeridian; 058 import org.deegree.model.csct.cs.ProjectedCoordinateSystem; 059 import org.deegree.model.csct.cs.Projection; 060 import org.deegree.model.csct.cs.WGS84ConversionInfo; 061 import org.deegree.model.csct.units.Unit; 062 import org.deegree.ogcbase.CommonNamespaces; 063 import org.w3c.dom.Element; 064 065 /** 066 * 067 * 068 * @version $Revision: 1.1 $ 069 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 070 */ 071 public class DeegreeCSAccess implements CSAccess { 072 073 private static final ILogger LOG = LoggerFactory.getLogger( DeegreeCSAccess.class ); 074 075 private static final String CRS_DEF = "crs.xml"; 076 077 private static NamespaceContext nsc = CommonNamespaces.getNamespaceContext(); 078 static { 079 try { 080 nsc.addNamespace( "crs", new URI( "http://www.deegree.org/crs" ) ); 081 } catch ( Exception e ) { 082 BootLogger.logError( e.getMessage(), e ); 083 } 084 } 085 086 private CoordinateSystemFactory csFactory = CoordinateSystemFactory.getDefault(); 087 088 /** 089 * keys are names (i.e. "EPSG:4326"), values are CoordinateSystems 090 */ 091 private HashMap<String, CoordinateSystem> systems = new HashMap<String, CoordinateSystem>( 300 ); 092 093 /** 094 * keys are names (i.e. "EPSG:7006"), values are Ellipsoids 095 */ 096 private HashMap<String, Ellipsoid> ellipsoids = new HashMap<String, Ellipsoid>( 30 ); 097 098 /** 099 * keys are names (i.e. "EPSG:7006"), values are Ellipsoids 100 */ 101 private HashMap<String, Datum> datums = new HashMap<String, Datum>( 30 ); 102 103 /** 104 * keys are names (i.e. "EPSG:1777"), values are WGS84Conversion object 105 */ 106 private HashMap<String, WGS84ConversionInfo> toWGS84Conversions = new HashMap<String, WGS84ConversionInfo>( 30 ); 107 108 /* 109 * (non-Javadoc) 110 * 111 * @see de.latlon.vodafone.crs.CSAccess#getCSByCode(java.lang.String, java.lang.String) 112 */ 113 public synchronized CoordinateSystem getCSByCode( String code, String version ) 114 throws CRSException { 115 // normalize code 116 code = code.toUpperCase(); 117 118 // reading CRS from cache and return it if available 119 CoordinateSystem cs = systems.get( code.toUpperCase() ); 120 if ( cs != null ) { 121 return cs; 122 } 123 124 // read defintion of CRS with passed code from CRS defintion resource 125 // if no CRS with passed code is defined null will be returned 126 Element crsElement = readCRSDefinition( code ); 127 if ( crsElement == null ) { 128 throw new CRSException( "requested CRS with code: " + code + " is not defined" ); 129 } 130 131 try { 132 createCRS( crsElement ); 133 } catch ( XMLParsingException e ) { 134 LOG.logError( e.getMessage(), e ); 135 throw new CRSException( e.getMessage(), e ); 136 } 137 138 return systems.get( code ); 139 } 140 141 /** 142 * creates 143 * 144 * @see CoordinateSystem from the passed Element 145 * @param crsElement 146 * @throws CRSException 147 * @throws XMLParsingException 148 */ 149 private void createCRS( Element crsElement ) 150 throws CRSException, XMLParsingException { 151 152 String type = crsElement.getLocalName(); 153 if ( "projectedReferenceSytemen".equals( type ) ) { 154 createProjectedCRS( crsElement ); 155 } else if ( "geographicReferenceSytem".equals( type ) ) { 156 createGeographicCRS( crsElement ); 157 } else { 158 throw new CRSException( "not supoorted CRS type: " + type ); 159 } 160 161 } 162 163 /** 164 * 165 * @param crsElement 166 * @throws XMLParsingException 167 * @throws CRSException 168 */ 169 private void createGeographicCRS( Element crsElement ) 170 throws XMLParsingException, CRSException { 171 172 String code = XMLTools.getRequiredNodeAsString( crsElement, "crs:code/text()", nsc ); 173 174 GeographicCoordinateSystem cs = null; 175 if ( "EPSG:4326".equals( code ) || "WGS84".equals( code ) ) { 176 // use standard parameters because no conversion is required 177 cs = csFactory.createGeographicCoordinateSystem( code, Unit.DEGREE, HorizontalDatum.WGS84, 178 PrimeMeridian.GREENWICH, AxisInfo.LONGITUDE, 179 AxisInfo.LATITUDE ); 180 } else { 181 182 // try getting WGS84 conversion parameters from cache. If no conversion parameters with 183 // required code is available in cache: read parameter and create an object storing 184 // informations for converting current geographic reference system (better: its 185 // underlying geogr. datum) to WGS84 186 String toWGS84Code = XMLTools.getNodeAsString( crsElement, "crs:wgs84ConversionInfo/text()", nsc, null ); 187 WGS84ConversionInfo convInfo = toWGS84Conversions.get( toWGS84Code ); 188 if ( convInfo == null ) { 189 String xPath = "/crs:definitons/crs:transformation[crs:code = '" + toWGS84Code + "']"; 190 Element toWGS84Element = XMLTools.getRequiredElement( crsElement, xPath, nsc ); 191 createWGS84ConversionInfo( toWGS84Element ); 192 convInfo = toWGS84Conversions.get( toWGS84Code ); 193 } 194 195 // read code of used datum 196 String datumCode = XMLTools.getRequiredNodeAsString( crsElement, "crs:usedDatum/text()", nsc ); 197 HorizontalDatum datum = (HorizontalDatum)datums.get( code ); 198 if ( datum == null ) { 199 String xPath = "/crs:definitons/crs:datum[crs:code = '" + datumCode + "']"; 200 Element datumElement = XMLTools.getRequiredElement( crsElement, xPath, nsc ); 201 createDatum( datumElement, convInfo ); 202 datum = (HorizontalDatum)datums.get( datumCode ); 203 } 204 205 String axisOrder = XMLTools.getNodeAsString( crsElement, "crs:axisOrder/text()", nsc, "XY" ); 206 // consider axisOrder creating reference system 207 if ( "XY".equals( axisOrder ) ) { 208 cs = csFactory.createGeographicCoordinateSystem( code, Unit.DEGREE, datum, PrimeMeridian.GREENWICH, 209 AxisInfo.LONGITUDE, AxisInfo.LATITUDE ); 210 } else { 211 cs = csFactory.createGeographicCoordinateSystem( code, Unit.DEGREE, datum, PrimeMeridian.GREENWICH, 212 AxisInfo.LATITUDE, AxisInfo.LONGITUDE ); 213 } 214 215 } 216 systems.put( code, cs ); 217 218 } 219 220 /** 221 * 222 * @param datumElement 223 * @param convInfo 224 * @throws XMLParsingException 225 * @throws CRSException 226 */ 227 private void createDatum( Element datumElement, WGS84ConversionInfo convInfo ) 228 throws XMLParsingException, CRSException { 229 230 String code = XMLTools.getRequiredNodeAsString( datumElement, "crs:code/text()", nsc ); 231 String type = XMLTools.getRequiredNodeAsString( datumElement, "crs:datumType/text()", nsc ); 232 233 // read code of used ellipsoid 234 String ellCode = XMLTools.getRequiredNodeAsString( datumElement, "crs:usedEllipsoid/text()", nsc ); 235 // try getting elliposid from cache. If no ellipsoid with required code 236 // is available in cache: create it 237 Ellipsoid ellipsoid = ellipsoids.get( ellCode ); 238 if ( ellipsoid == null ) { 239 String xPath = "/crs:definitons/crs:ellipsoid[crs:code = '" + ellCode + "']"; 240 Element ellipsoidElement = XMLTools.getRequiredElement( datumElement, xPath, nsc ); 241 createEllipsoid( ellipsoidElement ); 242 ellipsoid = ellipsoids.get( ellCode ); 243 } 244 // at the moment just horizontal geogr. datum are supported 245 if ( type.toLowerCase().equals( "geodetic" ) ) { 246 datums.put( code, new HorizontalDatum( code, DatumType.CLASSIC, ellipsoid, convInfo ) ); 247 } else { 248 throw new CRSException( "not supported datum type: " + type ); 249 } 250 } 251 252 /** 253 * 254 * @param toWGS84Element 255 * @throws XMLParsingException 256 */ 257 private void createWGS84ConversionInfo( Element toWGS84Element ) 258 throws XMLParsingException { 259 String code = XMLTools.getRequiredNodeAsString( toWGS84Element, "crs:code/text()", nsc ); 260 WGS84ConversionInfo convInfo = new WGS84ConversionInfo(); 261 convInfo.dx = XMLTools.getRequiredNodeAsDouble( toWGS84Element, "crs:xAxisTranslation/text()", nsc ); 262 convInfo.dy = XMLTools.getRequiredNodeAsDouble( toWGS84Element, "crs:yAxisTranslation/text()", nsc ); 263 convInfo.dz = XMLTools.getRequiredNodeAsDouble( toWGS84Element, "crs:zAxisTranslation/text()", nsc ); 264 convInfo.ex = XMLTools.getRequiredNodeAsDouble( toWGS84Element, "crs:xAxisRotation/text()", nsc ); 265 convInfo.ey = XMLTools.getRequiredNodeAsDouble( toWGS84Element, "crs:yAxisRotation/text()", nsc ); 266 convInfo.ez = XMLTools.getRequiredNodeAsDouble( toWGS84Element, "crs:zAxisRotation/text()", nsc ); 267 convInfo.ppm = XMLTools.getRequiredNodeAsDouble( toWGS84Element, "crs:scaleDifference/text()", nsc ); 268 toWGS84Conversions.put( code, convInfo ); 269 } 270 271 /** 272 * 273 * @param ellipsoidElement 274 * @throws XMLParsingException 275 * @throws CRSException 276 */ 277 private void createEllipsoid( Element ellipsoidElement ) 278 throws XMLParsingException, CRSException { 279 280 String code = XMLTools.getRequiredNodeAsString( ellipsoidElement, "crs:code/text()", nsc ); 281 double semiMajorAxis = XMLTools.getRequiredNodeAsDouble( ellipsoidElement, "crs:semiMajorAxis/text()", nsc ); 282 double inverseFlatting = XMLTools.getRequiredNodeAsDouble( ellipsoidElement, "crs:inverseFlatting/text()", nsc ); 283 Unit unit = createUnit( ellipsoidElement ); 284 285 Ellipsoid ellipsoid = Ellipsoid.createFlattenedSphere( code, semiMajorAxis, inverseFlatting, unit ); 286 ellipsoids.put( code, ellipsoid ); 287 } 288 289 /** 290 * 291 * @param ellipsoidElement 292 * @return 293 * @throws XMLParsingException 294 * @throws CRSException 295 */ 296 private Unit createUnit( Element rootElement ) 297 throws XMLParsingException, CRSException { 298 String units = XMLTools.getRequiredNodeAsString( rootElement, "crs:units/text()", nsc ); 299 300 Unit unit = null; 301 if ( "metre".equals( units ) || "meter".equals( units ) ) { 302 unit = Unit.METRE; 303 } else if ( "degree".equals( units ) ) { 304 unit = Unit.DEGREE; 305 } else if ( "britishyard".equals( units ) ) { 306 unit = Unit.BRITISHYARD; 307 } else { 308 throw new CRSException( "unknown unit '" + units + "'" ); 309 } 310 return unit; 311 } 312 313 /** 314 * at the moment just TransverseMercator projection is supported 315 * 316 * @param crsElement 317 * @throws XMLParsingException 318 * @throws CRSException 319 */ 320 private void createProjectedCRS( Element crsElement ) 321 throws XMLParsingException, CRSException { 322 String xPath = "crs:projectionType/text()"; 323 String projection = XMLTools.getRequiredNodeAsString( crsElement, xPath, nsc ); 324 if ( !"TransverseMercator".equals( projection ) ) { 325 throw new CRSException( "not supported projection: " + projection ); 326 } 327 if ( "TransverseMercator".equals( projection ) ) { 328 createTransverseMercatorCRS( crsElement ); 329 } 330 } 331 332 /** 333 * 334 * @param crsElement 335 * @throws XMLParsingException 336 * @throws CRSException 337 */ 338 private void createTransverseMercatorCRS( Element crsElement ) 339 throws XMLParsingException, CRSException { 340 String code = XMLTools.getRequiredNodeAsString( crsElement, "crs:code/text()", nsc ); 341 String name = XMLTools.getNodeAsString( crsElement, "crs:name/text()", nsc, "-" ); 342 343 // try getting geographic CRS parameters from cache. If no geographic CRS with 344 // required code is available in cache: read parameter and create it 345 String geogrCRSCode = XMLTools.getRequiredNodeAsString( crsElement, "crs:geographicReferenceSytem/text()", nsc ); 346 GeographicCoordinateSystem geogrCRS = (GeographicCoordinateSystem) systems.get( geogrCRSCode ); 347 if ( geogrCRS == null ) { 348 String xPath = "/crs:definitons/crs:geographicReferenceSytem[crs:code = '" + geogrCRSCode + "']"; 349 Element geogrCRSElement = XMLTools.getRequiredElement( crsElement, xPath, nsc ); 350 createGeographicCRS( geogrCRSElement ); 351 } 352 geogrCRS = (GeographicCoordinateSystem) systems.get( geogrCRSCode ); 353 Ellipsoid ellipsoid = geogrCRS.getHorizontalDatum().getEllipsoid(); 354 355 // read projection parameters and create a projection object 356 double latono = XMLTools.getRequiredNodeAsDouble( crsElement, "crs:latitudeOfNaturalOrigin/text()", nsc ); 357 double lonono = XMLTools.getRequiredNodeAsDouble( crsElement, "crs:longitudeOfNaturalOrigin/text()", nsc ); 358 double scaleFactor = XMLTools.getRequiredNodeAsDouble( crsElement, "crs:scaleFactor/text()", nsc ); 359 double falseEasting = XMLTools.getRequiredNodeAsDouble( crsElement, "crs:falseEasting/text()", nsc ); 360 double falseNorthing = XMLTools.getRequiredNodeAsDouble( crsElement, "crs:falseNorthing/text()", nsc ); 361 Unit unit = createUnit( crsElement ); 362 363 Projection projection = csFactory.createProjection( name, "Transverse_Mercator", ellipsoid, 364 new Point2D.Double( lonono, latono ), 365 new Point2D.Double( falseEasting, falseNorthing ), 366 scaleFactor ); 367 368 // create projected CRS considering axisOrder 369 String axisOrder = XMLTools.getNodeAsString( crsElement, "crs:axisOrder/text()", nsc, "XY" ); 370 ProjectedCoordinateSystem cs = null; 371 if ( "XY".equals( axisOrder ) ) { 372 cs = csFactory.createProjectedCoordinateSystem( code, geogrCRS, projection, unit, AxisInfo.X, AxisInfo.Y ); 373 } else { 374 cs = csFactory.createProjectedCoordinateSystem( code, geogrCRS, projection, unit, AxisInfo.Y, AxisInfo.X ); 375 } 376 systems.put( code, cs ); 377 } 378 379 /** 380 * reads a CRS defintion having passed code from CRS definition document 381 * 382 * @param code 383 * @return 384 * @throws CRSException 385 */ 386 private Element readCRSDefinition( String code ) 387 throws CRSException { 388 389 XMLFragment xml = null; 390 try { 391 URL url = DeegreeCSAccess.class.getResource( "/crs.xml" ); 392 xml = new XMLFragment( url ); 393 } catch ( Exception e ) { 394 URL url = DeegreeCSAccess.class.getResource( CRS_DEF ); 395 try { 396 xml = new XMLFragment( url ); 397 } catch ( Exception ee ) { 398 LOG.logError( ee.getMessage(), ee ); 399 throw new CRSException( "CRS definition document could not be loaded" ); 400 } 401 } 402 403 Element crsElement; 404 try { 405 String xPath = "/crs:definitons/*[crs:code = '" + code + "']"; 406 crsElement = XMLTools.getRequiredElement( xml.getRootElement(), xPath, nsc ); 407 } catch ( XMLParsingException e ) { 408 LOG.logError( e.getMessage(), e ); 409 throw new CRSException( "CRS definition could not be read from CRS definition document" ); 410 } 411 return crsElement; 412 } 413 414 }