001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/csct/cs/Ellipsoid.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001 by: 006 EXSE, Department of Geography, University of Bonn 007 http://www.giub.uni-bonn.de/exse/ 008 lat/lon GmbH 009 http://www.lat-lon.de 010 011 It has been implemented within SEAGIS - An OpenSource implementation of OpenGIS specification 012 (C) 2001, Institut de Recherche pour le D�veloppement (http://sourceforge.net/projects/seagis/) 013 SEAGIS Contacts: Surveillance de l'Environnement Assist�e par Satellite 014 Institut de Recherche pour le D�veloppement / US-Espace 015 mailto:seasnet@teledetection.fr 016 017 018 This library is free software; you can redistribute it and/or 019 modify it under the terms of the GNU Lesser General Public 020 License as published by the Free Software Foundation; either 021 version 2.1 of the License, or (at your option) any later version. 022 023 This library is distributed in the hope that it will be useful, 024 but WITHOUT ANY WARRANTY; without even the implied warranty of 025 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 026 Lesser General Public License for more details. 027 028 You should have received a copy of the GNU Lesser General Public 029 License along with this library; if not, write to the Free Software 030 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 031 032 Contact: 033 034 Andreas Poth 035 lat/lon GmbH 036 Aennchenstr. 19 037 53115 Bonn 038 Germany 039 E-Mail: poth@lat-lon.de 040 041 Klaus Greve 042 Department of Geography 043 University of Bonn 044 Meckenheimer Allee 166 045 53115 Bonn 046 Germany 047 E-Mail: klaus.greve@uni-bonn.de 048 049 050 ---------------------------------------------------------------------------*/ 051 package org.deegree.model.csct.cs; 052 053 // OpenGIS dependencies 054 import java.awt.geom.Point2D; 055 import java.util.Map; 056 057 import org.deegree.model.csct.resources.Utilities; 058 import org.deegree.model.csct.resources.XMath; 059 import org.deegree.model.csct.resources.css.ResourceKeys; 060 import org.deegree.model.csct.resources.css.Resources; 061 import org.deegree.model.csct.units.Unit; 062 063 /** 064 * The figure formed by the rotation of an ellipse about an axis. In this context, the axis of 065 * rotation is always the minor axis. It is named geodetic ellipsoid if the parameters are derived 066 * by the measurement of the shape and the size of the Earth to approximate the geoid as close as 067 * possible. 068 * 069 * @version 1.00 070 * @author OpenGIS (www.opengis.org) 071 * @author Martin Desruisseaux 072 * 073 * @author last edited by: $Author: bezema $ 074 * 075 * @version $Revision: 6259 $, $Date: 2007-03-20 10:15:15 +0100 (Di, 20 Mär 2007) $ 076 * 077 * @see "org.opengis.cs.CS_Ellipsoid" 078 */ 079 public class Ellipsoid extends Info { 080 /** 081 * Serial number for interoperability with different versions. 082 */ 083 private static final long serialVersionUID = -1047804526105439230L; 084 085 /** 086 * WGS 1984 ellipsoid. This ellipsoid is used in GPS system and is the default for most 087 * <code>org.deegree.model</code> packages. 088 */ 089 public static final Ellipsoid WGS84 = (Ellipsoid) pool.intern( createFlattenedSphere( 090 "WGS84", 091 6378137.0, 092 298.257223563, 093 Unit.METRE ) ); 094 095 /** 096 * The equatorial radius. 097 */ 098 private final double semiMajorAxis; 099 100 /** 101 * The polar radius. 102 */ 103 private final double semiMinorAxis; 104 105 /** 106 * The inverse of the flattening value, or {@link Double#POSITIVE_INFINITY} if the ellipsoid is 107 * a sphere. 108 * 109 */ 110 private final double inverseFlattening; 111 112 /** 113 * Is the Inverse Flattening definitive for this ellipsoid? 114 * 115 */ 116 private final boolean ivfDefinitive; 117 118 /** 119 * The units of the semi-major and semi-minor axis values. 120 */ 121 private final Unit unit; 122 123 /** 124 * Construct a new sphere using the specified radius. 125 * 126 * @param name 127 * Name of this sphere. 128 * @param radius 129 * The equatorial and polar radius. 130 * @param unit 131 * The units of the semi-major and semi-minor axis values. 132 */ 133 public Ellipsoid( final String name, final double radius, final Unit unit ) { 134 this( name, check( "radius", radius ), radius, Double.POSITIVE_INFINITY, false, unit ); 135 } 136 137 /** 138 * Construct a new ellipsoid using the specified axis length. 139 * 140 * @param name 141 * Name of this ellipsoid. 142 * @param semiMajorAxis 143 * The equatorial radius. 144 * @param semiMinorAxis 145 * The polar radius. 146 * @param unit 147 * The units of the semi-major and semi-minor axis values. 148 * 149 */ 150 public Ellipsoid( final String name, final double semiMajorAxis, final double semiMinorAxis, 151 final Unit unit ) { 152 this( name, semiMajorAxis, semiMinorAxis, 153 semiMajorAxis / ( semiMajorAxis - semiMinorAxis ), false, unit ); 154 } 155 156 /** 157 * Construct a new ellipsoid using the specified axis length. 158 * 159 * @param name 160 * Name of this ellipsoid. 161 * @param semiMajorAxis 162 * The equatorial radius. 163 * @param semiMinorAxis 164 * The polar radius. 165 * @param inverseFlattening 166 * The inverse of the flattening value. 167 * @param ivfDefinitive 168 * Is the Inverse Flattening definitive for this ellipsoid? 169 * @param unit 170 * The units of the semi-major and semi-minor axis values. 171 */ 172 private Ellipsoid( final String name, final double semiMajorAxis, final double semiMinorAxis, 173 final double inverseFlattening, final boolean ivfDefinitive, final Unit unit ) { 174 super( name ); 175 this.unit = unit; 176 this.semiMajorAxis = check( "semiMajorAxis", semiMajorAxis ); 177 this.semiMinorAxis = check( "semiMinorAxis", semiMinorAxis ); 178 this.inverseFlattening = check( "inverseFlattening", inverseFlattening ); 179 this.ivfDefinitive = ivfDefinitive; 180 ensureNonNull( "unit", unit ); 181 ensureLinearUnit( unit ); 182 } 183 184 /** 185 * Construct a new ellipsoid using the specified axis length. 186 * 187 * @param properties 188 * The set of properties (see {@link Info}). 189 * @param semiMajorAxis 190 * The equatorial radius. 191 * @param semiMinorAxis 192 * The polar radius. 193 * @param inverseFlattening 194 * The inverse of the flattening value. 195 * @param ivfDefinitive 196 * Is the Inverse Flattening definitive for this ellipsoid? 197 * @param unit 198 * The units of the semi-major and semi-minor axis values. 199 */ 200 Ellipsoid( final Map properties, final double semiMajorAxis, final double semiMinorAxis, 201 final double inverseFlattening, final boolean ivfDefinitive, final Unit unit ) { 202 super( properties ); 203 this.unit = unit; 204 this.semiMajorAxis = semiMajorAxis; 205 this.semiMinorAxis = semiMinorAxis; 206 this.inverseFlattening = inverseFlattening; 207 this.ivfDefinitive = ivfDefinitive; 208 // Accept null values. 209 } 210 211 /** 212 * Construct a new ellipsoid using the specified axis length and inverse flattening value. 213 * 214 * @param name 215 * Name of this ellipsoid. 216 * @param semiMajorAxis 217 * The equatorial radius. 218 * @param inverseFlattening 219 * The inverse flattening value. 220 * @param unit 221 * The units of the semi-major and semi-minor axis values. 222 * @return 223 * 224 */ 225 public static Ellipsoid createFlattenedSphere( final String name, final double semiMajorAxis, 226 final double inverseFlattening, final Unit unit ) { 227 return new Ellipsoid( name, semiMajorAxis, semiMajorAxis * ( 1 - 1 / inverseFlattening ), 228 inverseFlattening, true, unit ); 229 } 230 231 /** 232 * Check the argument validity. Argument <code>value</code> should be greater than zero. 233 * 234 * @param name 235 * Argument name. 236 * @param value 237 * Argument value. 238 * @return <code>value</code>. 239 * @throws IllegalArgumentException 240 * if <code>value</code> is not greater than 0. 241 */ 242 private static double check( final String name, final double value ) 243 throws IllegalArgumentException { 244 if ( value > 0 ) 245 return value; 246 throw new IllegalArgumentException( 247 Resources.format( 248 ResourceKeys.ERROR_ILLEGAL_ARGUMENT_$2, 249 name, new Double( value ) ) ); 250 } 251 252 /** 253 * Gets the equatorial radius. The returned length is expressed in this object's axis units. 254 * 255 * @return the equatorial radius. The returned length is expressed in this object's axis units. 256 * 257 * @see "org.opengis.cs.CS_Ellipsoid#getSemiMajorAxis()" 258 */ 259 public double getSemiMajorAxis() { 260 return semiMajorAxis; 261 } 262 263 /** 264 * Gets the polar radius. The returned length is expressed in this object's axis units. 265 * 266 * @return the polar radius. The returned length is expressed in this object's axis units. 267 * 268 * @see "org.opengis.cs.CS_Ellipsoid#getSemiMinorAxis()" 269 */ 270 public double getSemiMinorAxis() { 271 return semiMinorAxis; 272 } 273 274 /** 275 * The ratio of the distance between the center and a focus of the ellipse to the length of its 276 * semimajor axis. The eccentricity can alternately be computed from the equation: 277 * <code>e=sqrt(2f-f�)</code>. 278 * 279 * @return 280 */ 281 public double getEccentricity() { 282 final double f = 1 - getSemiMinorAxis() / getSemiMajorAxis(); 283 return Math.sqrt( 2 * f - f * f ); 284 } 285 286 /** 287 * Returns the value of the inverse of the flattening constant. Flattening is a value used to 288 * indicate how closely an ellipsoid approaches a spherical shape. The inverse flattening is 289 * related to the equatorial/polar radius (<var>r<sub>e</sub></var> and <var>r<sub>p</sub></var> 290 * respectively) by the formula <code>ivf=r<sub>e</sub>/(r<sub>e</sub>-r<sub>p</sub>)</code>. 291 * For perfect spheres, this method returns {@link Double#POSITIVE_INFINITY} (which is the 292 * correct value). 293 * 294 * @return the value of the inverse of the flattening constant. 295 * 296 * @see "org.opengis.cs.CS_Ellipsoid#getInverseFlattening()" 297 */ 298 public double getInverseFlattening() { 299 return inverseFlattening; 300 } 301 302 /** 303 * Is the Inverse Flattening definitive for this ellipsoid? Some ellipsoids use the IVF as the 304 * defining value, and calculate the polar radius whenever asked. Other ellipsoids use the polar 305 * radius to calculate the IVF whenever asked. This distinction can be important to avoid 306 * floating-point rounding errors. 307 * 308 * @return 309 */ 310 public boolean isIvfDefinitive() { 311 return ivfDefinitive; 312 } 313 314 /** 315 * Returns an <em>estimation</em> of orthodromic distance between two geographic coordinates. 316 * The orthodromic distance is the shortest distance between two points on a sphere's surface. 317 * The orthodromic path is always on a great circle. An other possible distance measurement is 318 * the loxodromic distance, which is a longer distance on a path with a constant direction on 319 * the compas. 320 * 321 * @param P1 322 * Longitude and latitude of first point (in degrees). 323 * @param P2 324 * Longitude and latitude of second point (in degrees). 325 * @return The orthodromic distance (in the units of this ellipsoid). 326 */ 327 public double orthodromicDistance( final Point2D P1, final Point2D P2 ) { 328 return orthodromicDistance( P1.getX(), P1.getY(), P2.getX(), P2.getY() ); 329 } 330 331 /** 332 * Returns an <em>estimation</em> of orthodromic distance between two geographic coordinates. 333 * The orthodromic distance is the shortest distance between two points on a sphere's surface. 334 * The orthodromic path is always on a great circle. An other possible distance measurement is 335 * the loxodromic distance, which is a longer distance on a path with a constant direction on 336 * the compas. 337 * 338 * @param x1 339 * Longitude of first point (in degrees). 340 * @param y1 341 * Latitude of first point (in degrees). 342 * @param x2 343 * Longitude of second point (in degrees). 344 * @param y2 345 * Latitude of second point (in degrees). 346 * @return The orthodromic distance (in the units of this ellipsoid). 347 */ 348 public double orthodromicDistance( double x1, double y1, double x2, double y2 ) { 349 /* 350 * Le calcul de la distance orthodromique sur une surface ellipso�dale est complexe, sujet � 351 * des erreurs d'arrondissements et sans solution � proximit� des p�les. Nous utilisont 352 * plut�t un calcul bas� sur une forme sph�rique de la terre. Un programme en Fortran 353 * calculant les distances orthodromiques sur une surface ellipso�dale peut �tre t�l�charg� � 354 * partir du site de NOAA: 355 * 356 * ftp://ftp.ngs.noaa.gov/pub/pcsoft/for_inv.3d/source/ 357 */ 358 y1 = Math.toRadians( y1 ); 359 y2 = Math.toRadians( y2 ); 360 final double y = 0.5 * ( y1 + y2 ); 361 final double dx = Math.toRadians( Math.abs( x2 - x1 ) % 360 ); 362 double rho = Math.sin( y1 ) * Math.sin( y2 ) + Math.cos( y1 ) * Math.cos( y2 ) 363 * Math.cos( dx ); 364 if ( rho > +1 ) 365 rho = +1; // Catch rounding error. 366 if ( rho < -1 ) 367 rho = -1; // Catch rounging error. 368 return Math.acos( rho ) 369 / XMath.hypot( Math.sin( y ) / getSemiMajorAxis(), Math.cos( y ) 370 / getSemiMinorAxis() ); 371 // 'hypot' calcule l'inverse du rayon **apparent** de la terre � la latitude 'y'. 372 } 373 374 /** 375 * Returns the units of the semi-major and semi-minor axis values. 376 * 377 * @return he units of the semi-major and semi-minor axis values. 378 * 379 * @see "org.opengis.cs.CS_Ellipsoid#getAxisUnit()" 380 */ 381 public Unit getAxisUnit() { 382 return unit; 383 } 384 385 /** 386 * Compares the specified object with this ellipsoid for equality. 387 * 388 * @param object 389 * @return 390 */ 391 public boolean equals( final Object object ) { 392 if ( super.equals( object ) ) { 393 final Ellipsoid that = (Ellipsoid) object; 394 return this.ivfDefinitive == that.ivfDefinitive 395 && Double.doubleToLongBits( this.semiMajorAxis ) == Double.doubleToLongBits( that.semiMajorAxis ) 396 && Double.doubleToLongBits( this.semiMinorAxis ) == Double.doubleToLongBits( that.semiMinorAxis ) 397 && Double.doubleToLongBits( this.inverseFlattening ) == Double.doubleToLongBits( that.inverseFlattening ) 398 && Utilities.equals( this.unit, that.unit ); 399 } 400 return false; 401 } 402 403 /** 404 * Returns a hash value for this ellipsoid. 405 * 406 * @return a hash value for this ellipsoid. 407 */ 408 public int hashCode() { 409 final long longCode = Double.doubleToLongBits( getSemiMajorAxis() ); 410 return ( ( (int) ( longCode >>> 32 ) ) ^ (int) longCode ) + 37 * super.hashCode(); 411 } 412 413 /** 414 * Fill the part inside "[...]". Used for formatting Well Know Text (WKT). 415 * 416 * @param buffer 417 * @return 418 */ 419 String addString( final StringBuffer buffer ) { 420 buffer.append( ", " ); 421 buffer.append( semiMajorAxis ); 422 buffer.append( ", " ); 423 buffer.append( Double.isInfinite( inverseFlattening ) ? 0 : inverseFlattening ); 424 return "SPHEROID"; 425 } 426 427 }