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 }