/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001 by:
EXSE, Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/exse/
lat/lon GmbH
http://www.lat-lon.de
It has been implemented within SEAGIS - An OpenSource implementation of OpenGIS specification
(C) 2001, Institut de Recherche pour le D�veloppement (http://sourceforge.net/projects/seagis/)
SEAGIS Contacts: Surveillance de l'Environnement Assist�e par Satellite
Institut de Recherche pour le D�veloppement / US-Espace
mailto:seasnet@teledetection.fr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53115 Bonn
Germany
E-Mail: poth@lat-lon.de
Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: klaus.greve@uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.model.csct.cs;
// OpenGIS dependencies
import java.awt.geom.Point2D;
import java.util.Map;
import org.deegree.model.csct.resources.Utilities;
import org.deegree.model.csct.resources.XMath;
import org.deegree.model.csct.resources.css.ResourceKeys;
import org.deegree.model.csct.resources.css.Resources;
import org.deegree.model.csct.units.Unit;
/**
* The figure formed by the rotation of an ellipse about an axis.
* In this context, the axis of rotation is always the minor axis. It is named geodetic
* ellipsoid if the parameters are derived by the measurement of the shape and the size
* of the Earth to approximate the geoid as close as possible.
*
* @version 1.00
* @author OpenGIS (www.opengis.org)
* @author Martin Desruisseaux
*
* @see org.opengis.cs.CS_Ellipsoid
*/
public class Ellipsoid extends Info {
/**
* Serial number for interoperability with different versions.
*/
private static final long serialVersionUID = -1047804526105439230L;
/**
* WGS 1984 ellipsoid. This ellipsoid is used in GPS system
* and is the default for most <code>org.deegree.model</code> packages.
*/
public static final Ellipsoid WGS84 = (Ellipsoid) pool.intern( createFlattenedSphere(
"WGS84",
6378137.0,
298.257223563,
Unit.METRE ) );
/**
* The equatorial radius.
*/
private final double semiMajorAxis;
/**
* The polar radius.
*/
private final double semiMinorAxis;
/**
* The inverse of the flattening value, or
* {@link Double#POSITIVE_INFINITY} if the
* ellipsoid is a sphere.
*
*/
private final double inverseFlattening;
/**
* Is the Inverse Flattening definitive for this ellipsoid?
*
*/
private final boolean ivfDefinitive;
/**
* The units of the semi-major
* and semi-minor axis values.
*/
private final Unit unit;
/**
* Construct a new sphere using the specified radius.
*
* @param name Name of this sphere.
* @param radius The equatorial and polar radius.
* @param unit The units of the semi-major and semi-minor axis values.
*/
public Ellipsoid( final String name, final double radius, final Unit unit ) {
this( name, check( "radius", radius ), radius, Double.POSITIVE_INFINITY, false, unit );
}
/**
* Construct a new ellipsoid using the specified axis length.
*
* @param name Name of this ellipsoid.
* @param semiMajorAxis The equatorial radius.
* @param semiMinorAxis The polar radius.
* @param unit The units of the semi-major and semi-minor axis values.
*
*/
public Ellipsoid( final String name, final double semiMajorAxis, final double semiMinorAxis,
final Unit unit ) {
this( name, semiMajorAxis, semiMinorAxis,
semiMajorAxis / ( semiMajorAxis - semiMinorAxis ), false, unit );
}
/**
* Construct a new ellipsoid using the specified axis length.
*
* @param name Name of this ellipsoid.
* @param semiMajorAxis The equatorial radius.
* @param semiMinorAxis The polar radius.
* @param inverseFlattening The inverse of the flattening value.
* @param ivfDefinitive Is the Inverse Flattening definitive for this ellipsoid?
* @param unit The units of the semi-major and semi-minor axis values.
*/
private Ellipsoid( final String name, final double semiMajorAxis, final double semiMinorAxis,
final double inverseFlattening, final boolean ivfDefinitive, final Unit unit ) {
super( name );
this.unit = unit;
this.semiMajorAxis = check( "semiMajorAxis", semiMajorAxis );
this.semiMinorAxis = check( "semiMinorAxis", semiMinorAxis );
this.inverseFlattening = check( "inverseFlattening", inverseFlattening );
this.ivfDefinitive = ivfDefinitive;
ensureNonNull( "unit", unit );
ensureLinearUnit( unit );
}
/**
* Construct a new ellipsoid using the specified axis length.
*
* @param properties The set of properties (see {@link Info}).
* @param semiMajorAxis The equatorial radius.
* @param semiMinorAxis The polar radius.
* @param inverseFlattening The inverse of the flattening value.
* @param ivfDefinitive Is the Inverse Flattening definitive for this ellipsoid?
* @param unit The units of the semi-major and semi-minor axis values.
*/
Ellipsoid( final Map properties, final double semiMajorAxis, final double semiMinorAxis,
final double inverseFlattening, final boolean ivfDefinitive, final Unit unit ) {
super( properties );
this.unit = unit;
this.semiMajorAxis = semiMajorAxis;
this.semiMinorAxis = semiMinorAxis;
this.inverseFlattening = inverseFlattening;
this.ivfDefinitive = ivfDefinitive;
// Accept null values.
}
/**
* Construct a new ellipsoid using the specified axis length
* and inverse flattening value.
*
* @param name Name of this ellipsoid.
* @param semiMajorAxis The equatorial radius.
* @param inverseFlattening The inverse flattening value.
* @param unit The units of the semi-major and semi-minor axis values.
*
*/
public static Ellipsoid createFlattenedSphere( final String name, final double semiMajorAxis,
final double inverseFlattening, final Unit unit ) {
return new Ellipsoid( name, semiMajorAxis, semiMajorAxis * ( 1 - 1 / inverseFlattening ),
inverseFlattening, true, unit );
}
/**
* Check the argument validity. Argument
* <code>value</code> should be greater
* than zero.
*
* @param name Argument name.
* @param value Argument value.
* @return <code>value</code>.
* @throws IllegalArgumentException if <code>value</code> is not greater than 0.
*/
private static double check( final String name, final double value )
throws IllegalArgumentException {
if ( value > 0 )
return value;
throw new IllegalArgumentException(
Resources.format(
ResourceKeys.ERROR_ILLEGAL_ARGUMENT_$2,
name, new Double( value ) ) );
}
/**
* Gets the equatorial radius.
* The returned length is expressed in this object's axis units.
*
* @see org.opengis.cs.CS_Ellipsoid#getSemiMajorAxis()
*/
public double getSemiMajorAxis() {
return semiMajorAxis;
}
/**
* Gets the polar radius.
* The returned length is expressed in this object's axis units.
*
* @see org.opengis.cs.CS_Ellipsoid#getSemiMinorAxis()
*/
public double getSemiMinorAxis() {
return semiMinorAxis;
}
/**
* The ratio of the distance between the center and a focus of the ellipse
* to the length of its semimajor axis. The eccentricity can alternately be
* computed from the equation: <code>e=sqrt(2f-f�)</code>.
*/
public double getEccentricity() {
final double f = 1 - getSemiMinorAxis() / getSemiMajorAxis();
return Math.sqrt( 2 * f - f * f );
}
/**
* Returns the value of the inverse of the flattening constant.
* Flattening is a value used to indicate how closely an ellipsoid approaches a
* spherical shape. The inverse flattening is related to the equatorial/polar
* radius (<var>r<sub>e</sub></var> and <var>r<sub>p</sub></var> respectively)
* by the formula <code>ivf=r<sub>e</sub>/(r<sub>e</sub>-r<sub>p</sub>)</code>.
* For perfect spheres, this method returns {@link Double#POSITIVE_INFINITY}
* (which is the correct value).
*
* @see org.opengis.cs.CS_Ellipsoid#getInverseFlattening()
*/
public double getInverseFlattening() {
return inverseFlattening;
}
/**
* Is the Inverse Flattening definitive for this ellipsoid?
* Some ellipsoids use the IVF as the defining value, and calculate the
* polar radius whenever asked. Other ellipsoids use the polar radius to
* calculate the IVF whenever asked. This distinction can be important to
* avoid floating-point rounding errors.
*/
public boolean isIvfDefinitive() {
return ivfDefinitive;
}
/**
* Returns an <em>estimation</em> of orthodromic distance between two geographic coordinates.
* The orthodromic distance is the shortest distance between two points on a sphere's surface.
* The orthodromic path is always on a great circle. An other possible distance measurement
* is the loxodromic distance, which is a longer distance on a path with a constant direction
* on the compas.
*
* @param P1 Longitude and latitude of first point (in degrees).
* @param P2 Longitude and latitude of second point (in degrees).
* @return The orthodromic distance (in the units of this ellipsoid).
*/
public double orthodromicDistance( final Point2D P1, final Point2D P2 ) {
return orthodromicDistance( P1.getX(), P1.getY(), P2.getX(), P2.getY() );
}
/**
* Returns an <em>estimation</em> of orthodromic distance between two geographic coordinates.
* The orthodromic distance is the shortest distance between two points on a sphere's surface.
* The orthodromic path is always on a great circle. An other possible distance measurement
* is the loxodromic distance, which is a longer distance on a path with a constant direction
* on the compas.
*
* @param x1 Longitude of first point (in degrees).
* @param y1 Latitude of first point (in degrees).
* @param x2 Longitude of second point (in degrees).
* @param y2 Latitude of second point (in degrees).
* @return The orthodromic distance (in the units of this ellipsoid).
*/
public double orthodromicDistance( double x1, double y1, double x2, double y2 ) {
/*
* Le calcul de la distance orthodromique sur une surface ellipso�dale est complexe,
* sujet � des erreurs d'arrondissements et sans solution � proximit� des p�les.
* Nous utilisont plut�t un calcul bas� sur une forme sph�rique de la terre. Un
* programme en Fortran calculant les distances orthodromiques sur une surface
* ellipso�dale peut �tre t�l�charg� � partir du site de NOAA:
*
* ftp://ftp.ngs.noaa.gov/pub/pcsoft/for_inv.3d/source/
*/
y1 = Math.toRadians( y1 );
y2 = Math.toRadians( y2 );
final double y = 0.5 * ( y1 + y2 );
final double dx = Math.toRadians( Math.abs( x2 - x1 ) % 360 );
double rho = Math.sin( y1 ) * Math.sin( y2 ) + Math.cos( y1 ) * Math.cos( y2 )
* Math.cos( dx );
if ( rho > +1 )
rho = +1; // Catch rounding error.
if ( rho < -1 )
rho = -1; // Catch rounging error.
return Math.acos( rho )
/ XMath.hypot( Math.sin( y ) / getSemiMajorAxis(), Math.cos( y )
/ getSemiMinorAxis() );
// 'hypot' calcule l'inverse du rayon **apparent** de la terre � la latitude 'y'.
}
/**
* Returns the units of the semi-major
* and semi-minor axis values.
*
* @see org.opengis.cs.CS_Ellipsoid#getAxisUnit()
*/
public Unit getAxisUnit() {
return unit;
}
/**
* Compares the specified object with
* this ellipsoid for equality.
*/
public boolean equals( final Object object ) {
if ( super.equals( object ) ) {
final Ellipsoid that = (Ellipsoid) object;
return this.ivfDefinitive == that.ivfDefinitive
&& Double.doubleToLongBits( this.semiMajorAxis ) == Double.doubleToLongBits( that.semiMajorAxis )
&& Double.doubleToLongBits( this.semiMinorAxis ) == Double.doubleToLongBits( that.semiMinorAxis )
&& Double.doubleToLongBits( this.inverseFlattening ) == Double.doubleToLongBits( that.inverseFlattening )
&& Utilities.equals( this.unit, that.unit );
}
return false;
}
/**
* Returns a hash value for this ellipsoid.
*/
public int hashCode() {
final long longCode = Double.doubleToLongBits( getSemiMajorAxis() );
return ( ( (int) ( longCode >>> 32 ) ) ^ (int) longCode ) + 37 * super.hashCode();
}
/**
* Fill the part inside "[...]".
* Used for formatting Well Know Text (WKT).
*/
String addString( final StringBuffer buffer ) {
buffer.append( ", " );
buffer.append( semiMajorAxis );
buffer.append( ", " );
buffer.append( Double.isInfinite( inverseFlattening ) ? 0 : inverseFlattening );
return "SPHEROID";
}
}