001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/crs/coordinatesystems/CoordinateSystem.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.coordinatesystems; 038 039 import java.io.Serializable; 040 import java.util.LinkedList; 041 import java.util.List; 042 043 import javax.vecmath.Point3d; 044 045 import org.deegree.crs.Identifiable; 046 import org.deegree.crs.components.Axis; 047 import org.deegree.crs.components.Datum; 048 import org.deegree.crs.components.GeodeticDatum; 049 import org.deegree.crs.components.Unit; 050 import org.deegree.crs.transformations.polynomial.PolynomialTransformation; 051 052 /** 053 * Three kinds of <code>CoordinateSystem</code>s (in this class abbreviated with CRS) are supported in this lib. 054 * <ul> 055 * <li>Geographic CRS: A position (on the ellipsoid) is given in Lattitude / Longitude (Polar Cooridnates) given in rad° 056 * min''sec. The order of the position's coordinates are to be contrued to the axis order of the CRS. These lat/lon 057 * coordinates are to be tranformed to x,y,z values to define their location on the underlying datum.</li> 058 * <li>GeoCentric CRS: A position (on the ellipsoid) is given in x, y, z (cartesian) coordinates with the same units 059 * defined as the ones in the underlying datum. The order of the position's coordinates are to be contrued to the axis 060 * order of the datum.</li> 061 * <li>Projected CRS: The position (on the map) is given in a 2D-tuple in pre-defined units. The Axis of the CRS are 062 * defined (through a transformation) for an underlying Datum, which can have it's own axis with their own units. The 063 * order of the position's coordinates are to be contrued to the axis order of the CRS</li> 064 * </ul> 065 * 066 * Summarizing it can be said, that each CRS has following features 067 * <ul> 068 * <li>A reference code (an casesensitive String identifying this CRS, for example 'EPGS:4326' or 069 * 'urn:ogc:def:crs:OGC:2:84' or 'luref')</li> 070 * <li>An optional version.</li> 071 * <li>A humanly readable name.</li> 072 * <li>An optional description.</li> 073 * <li>An optional area of use, describing where this CRS is used.</li> 074 * <li>The order in which the axis of ther crs are defined.</li> 075 * <li>The underlying Datum</li> 076 * 077 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> 078 * 079 * @author last edited by: $Author: lbuesching $ 080 * 081 * @version $Revision: 23399 $, $Date: 2010-04-07 10:05:58 +0200 (Mi, 07 Apr 2010) $ 082 * 083 */ 084 085 public abstract class CoordinateSystem extends Identifiable implements Serializable { 086 087 private static final long serialVersionUID = -1817981955729114332L; 088 089 private Axis[] axisOrder; 090 091 private Datum usedDatum; 092 093 private final List<PolynomialTransformation> transformations; 094 095 /** 096 * Defines this CRS as a GeoCentric one. 097 */ 098 public static final int GEOCENTRIC_CRS = 0; 099 100 /** 101 * Defines this CRS as a Geographic one. 102 */ 103 public static final int GEOGRAPHIC_CRS = 1; 104 105 /** 106 * Defines this CRS as a Projected one. 107 */ 108 public static final int PROJECTED_CRS = 2; 109 110 /** 111 * Defines this CRS as a Compound one. 112 */ 113 public static final int COMPOUND_CRS = 3; 114 115 /** 116 * Defines this CRS as a Vertical one. 117 */ 118 public static final int VERTICAL_CRS = 4; 119 120 /** 121 * @param datum 122 * of this coordinate system. 123 * @param axisOrder 124 * the axisorder of this coordinate system. 125 * @param identity 126 */ 127 public CoordinateSystem( Datum datum, Axis[] axisOrder, Identifiable identity ) { 128 this( null, datum, axisOrder, identity ); 129 } 130 131 /** 132 * @param datum 133 * of this coordinate system. 134 * @param axisOrder 135 * the axisorder of this coordinate system. 136 * @param identifiers 137 * of this coordinate system. 138 * @param names 139 * @param versions 140 * @param descriptions 141 * @param areasOfUse 142 */ 143 public CoordinateSystem( Datum datum, Axis[] axisOrder, String[] identifiers, String[] names, String[] versions, 144 String[] descriptions, String[] areasOfUse ) { 145 super( identifiers, names, versions, descriptions, areasOfUse ); 146 this.axisOrder = axisOrder; 147 this.usedDatum = datum; 148 this.transformations = new LinkedList<PolynomialTransformation>(); 149 } 150 151 /** 152 * @param transformations 153 * to use instead of the helmert transformation(s). 154 * @param geodeticDatum 155 * of this crs 156 * @param axisOrder 157 * @param identity 158 */ 159 public CoordinateSystem( List<PolynomialTransformation> transformations, Datum geodeticDatum, Axis[] axisOrder, 160 Identifiable identity ) { 161 super( identity ); 162 this.axisOrder = axisOrder; 163 this.usedDatum = geodeticDatum; 164 if ( transformations == null ) { 165 transformations = new LinkedList<PolynomialTransformation>(); 166 } 167 this.transformations = transformations; 168 } 169 170 /** 171 * @return (all) axis' in their defined order. 172 */ 173 public Axis[] getAxis() { 174 return axisOrder; 175 } 176 177 /** 178 * @return the usedDatum or <code>null</code> if the datum was not a Geodetic one. 179 */ 180 public final GeodeticDatum getGeodeticDatum() { 181 return ( usedDatum instanceof GeodeticDatum ) ? (GeodeticDatum) usedDatum : null; 182 } 183 184 /** 185 * @return the datum of this coordinate system. 186 */ 187 public final Datum getDatum() { 188 return usedDatum; 189 } 190 191 /** 192 * @return the units of all axis of the coordinatesystem. 193 */ 194 public Unit[] getUnits() { 195 Axis[] allAxis = getAxis(); 196 Unit[] result = new Unit[allAxis.length]; 197 for ( int i = 0; i < allAxis.length; ++i ) { 198 result[i] = allAxis[i].getUnits(); 199 } 200 return result; 201 } 202 203 /** 204 * @return the dimension of this CRS. 205 */ 206 public abstract int getDimension(); 207 208 /** 209 * @return one of the *_CRS types defined in this class. 210 */ 211 public abstract int getType(); 212 213 /** 214 * @param targetCRS 215 * to get the alternative Transformation for. 216 * @return true if this crs has an alternative transformation for the given coordinatesystem, false otherwise. 217 */ 218 public boolean hasDirectTransformation( CoordinateSystem targetCRS ) { 219 if ( targetCRS == null ) { 220 return false; 221 } 222 for ( PolynomialTransformation transformation : transformations ) { 223 if ( transformation != null && targetCRS.equals( transformation.getTargetCRS() ) ) { 224 return true; 225 } 226 } 227 return false; 228 } 229 230 /** 231 * @param targetCRS 232 * to get the alternative transformation for. 233 * @return the transformation associated with the given crs, <code>null</code> otherwise. 234 */ 235 public PolynomialTransformation getDirectTransformation( CoordinateSystem targetCRS ) { 236 if ( targetCRS == null ) { 237 return null; 238 } 239 for ( PolynomialTransformation transformation : transformations ) { 240 if ( transformation.getTargetCRS().equals( targetCRS ) ) { 241 return transformation; 242 } 243 } 244 return null; 245 } 246 247 /** 248 * Converts the given coordinates in given to the unit of the respective axis. 249 * 250 * @param coordinates 251 * to convert to. 252 * @param units 253 * in which the coordinates were given. 254 * @param invert 255 * if the operation should be inverted, e.g. the coordinates are given in the axis units and should be 256 * converted to the given units. 257 * @return the converted coordinates. 258 */ 259 public Point3d convertToAxis( Point3d coordinates, Unit[] units, boolean invert ) { 260 if ( units != null && units.length < getDimension() && units.length > 0 ) { 261 Unit[] axisUnits = getUnits(); 262 for ( int i = 0; i < axisUnits.length; i++ ) { 263 Unit axisUnit = axisUnits[i]; 264 double value = ( i == 0 ) ? coordinates.x : ( i == 1 ) ? coordinates.y : coordinates.z; 265 if ( i < units.length ) { 266 Unit coordinateUnit = units[i]; 267 if ( invert ) { 268 value = axisUnit.convert( value, coordinateUnit ); 269 } else { 270 value = coordinateUnit.convert( value, axisUnit ); 271 } 272 } 273 if ( i == 0 ) { 274 coordinates.x = value; 275 } else if ( i == 1 ) { 276 coordinates.y = value; 277 } else { 278 coordinates.z = value; 279 } 280 } 281 } 282 return coordinates; 283 } 284 285 /** 286 * Helper function to get the typename as a String. 287 * 288 * @return either the type as a name or 'Unknown' if the type is not known. 289 */ 290 protected String getTypeName() { 291 switch ( getType() ) { 292 case GEOCENTRIC_CRS: 293 return "Geocentric CRS"; 294 case PROJECTED_CRS: 295 return "Projected CRS"; 296 case GEOGRAPHIC_CRS: 297 return "Geographic CRS"; 298 case COMPOUND_CRS: 299 return "Compound CRS"; 300 default: 301 return "Unknown CRS"; 302 } 303 } 304 305 /** 306 * Checks if the given axis match this.axisOrder[] in length and order. 307 * 308 * @param otherAxis 309 * the axis to check 310 * @return true if the given axis match this.axisOrder[] false otherwise. 311 */ 312 private boolean matchAxis( Axis[] otherAxis ) { 313 Axis[] allAxis = getAxis(); 314 if ( otherAxis.length != allAxis.length ) { 315 return false; 316 } 317 for ( int i = 0; i < allAxis.length; ++i ) { 318 Axis a = allAxis[i]; 319 Axis b = otherAxis[i]; 320 if ( !a.equals( b ) ) { 321 return false; 322 } 323 } 324 return true; 325 } 326 327 @Override 328 public boolean equals( Object other ) { 329 if ( other != null && other instanceof CoordinateSystem ) { 330 final CoordinateSystem that = (CoordinateSystem) other; 331 return that.getType() == this.getType() && that.getDimension() == this.getDimension() 332 && matchAxis( that.getAxis() ) && super.equals( that ) 333 && that.getGeodeticDatum().equals( this.getGeodeticDatum() ); 334 } 335 return false; 336 } 337 338 /** 339 * Implementation as proposed by Joshua Block in Effective Java (Addison-Wesley 2001), which supplies an even 340 * distribution and is relatively fast. It is created from field <b>f</b> as follows: 341 * <ul> 342 * <li>boolean -- code = (f ? 0 : 1)</li> 343 * <li>byte, char, short, int -- code = (int)f</li> 344 * <li>long -- code = (int)(f ^ (f >>>32))</li> 345 * <li>float -- code = Float.floatToIntBits(f);</li> 346 * <li>double -- long l = Double.doubleToLongBits(f); code = (int)(l ^ (l >>> 32))</li> 347 * <li>all Objects, (where equals( ) calls equals( ) for this field) -- code = f.hashCode( )</li> 348 * <li>Array -- Apply above rules to each element</li> 349 * </ul> 350 * <p> 351 * Combining the hash code(s) computed above: result = 37 * result + code; 352 * </p> 353 * 354 * @return (int) ( result >>> 32 ) ^ (int) result; 355 * 356 * @see java.lang.Object#hashCode() 357 */ 358 @Override 359 public int hashCode() { 360 // the 2.nd million th. prime, :-) 361 long code = 32452843; 362 if ( getAxis() != null ) { 363 for ( Axis ax : getAxis() ) { 364 code = code * 37 + ax.hashCode(); 365 } 366 } 367 if ( usedDatum != null ) { 368 code = code * 37 + usedDatum.hashCode(); 369 } 370 code = code * 37 + getType(); 371 code = code * 37 + getDimension(); 372 return (int) ( code >>> 32 ) ^ (int) code; 373 } 374 375 @Override 376 public String toString() { 377 StringBuilder sb = new StringBuilder( super.toString() ); 378 sb.append( "\n - type: " ).append( getTypeName() ); 379 sb.append( "\n - datum: " ).append( usedDatum ); 380 sb.append( "\n - dimension: " ).append( getDimension() ); 381 for ( Axis a : getAxis() ) { 382 sb.append( "\n - axis: " ).append( a.toString() ); 383 } 384 return sb.toString(); 385 386 } 387 388 /** 389 * @return the polynomial transformations. 390 */ 391 public final List<PolynomialTransformation> getTransformations() { 392 return transformations; 393 } 394 395 396 /** 397 * Return the axis index associated with an easting value, if the axis could not be determined {@link Axis#AO_OTHER} 398 * 0 will be returned. 399 * 400 * @return the index of the axis which represents the easting/westing component of a coordinate tuple. 401 */ 402 public int getEasting() { 403 Axis[] axis = getAxis(); 404 for ( int i = 0; i < axis.length; ++i ) { 405 Axis a = axis[i]; 406 if ( a != null ) { 407 if ( a.getOrientation() == Axis.AO_EAST || a.getOrientation() == Axis.AO_WEST ) { 408 return i; 409 } 410 } 411 } 412 return 0; 413 } 414 415 /** 416 * Return the axis index associated with a northing value, if the axis could not be determined (e.g not is 417 * {@link Axis#AO_NORTH} {@link Axis#AO_SOUTH} or {@link Axis#AO_UP} or {@link Axis#AO_DOWN}) 1 will be returned. 418 * 419 * @return the index of the axis which represents the easting/westing component of a coordinate tuple. 420 */ 421 public int getNorthing() { 422 Axis[] axis = getAxis(); 423 for ( int i = 0; i < axis.length; ++i ) { 424 Axis a = axis[i]; 425 if ( a != null ) { 426 if ( a.getOrientation() == Axis.AO_NORTH || a.getOrientation() == Axis.AO_SOUTH 427 || a.getOrientation() == Axis.AO_DOWN || a.getOrientation() == Axis.AO_UP ) { 428 return i; 429 } 430 } 431 } 432 return 1; 433 } 434 }