/*
* Geotools 2 - OpenSource mapping toolkit
* (C) 2003, Geotools Project Managment Committee (PMC)
* (C) 2001, Institut de Recherche pour le Développement
*
* 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
*
*
* This package contains documentation from OpenGIS specifications.
* OpenGIS consortium's work is fully acknowledged here.
*
* This class contains formulas from the public FTP area of NOAA.
* NOAAS's work is fully acknowledged here.
*/
package org.geotools.cs;
// OpenGIS dependencies
import java.awt.geom.Point2D;
import java.rmi.RemoteException;
import org.geotools.geometry.GeneralDirectPosition;
import org.geotools.measure.CoordinateFormat;
import org.geotools.resources.Utilities;
import org.geotools.resources.XMath;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
import org.geotools.units.Unit;
import org.opengis.cs.CS_Ellipsoid;
import org.opengis.cs.CS_LinearUnit;
/**
* 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.
*
* @source $URL$
* @version $Id$
* @author OpenGIS (www.opengis.org)
* @author Martin Desruisseaux
*
* @see org.opengis.cs.CS_Ellipsoid
*
* @deprecated Replaced by {@link org.geotools.referencing.datum.DefaultEllipsoid}.
*/
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 systems
* and is the default for most org.geotools
packages.
*/
public static final Ellipsoid WGS84;
static {
Ellipsoid wgs84 = createFlattenedSphere("WGS84", 6378137.0, 298.257223563, Unit.METRE);
WGS84 = (Ellipsoid) pool.canonicalize( wgs84 );
System.out.println( WGS84 );
}
/**
* The equatorial radius.
* @see #getSemiMajorAxis
*/
private final double semiMajorAxis;
/**
* The polar radius.
* @see #getSemiMinorAxis
*/
private final double semiMinorAxis;
/**
* The inverse of the flattening value, or {@link Double#POSITIVE_INFINITY}
* if the ellipsoid is a sphere.
*
* @see #getInverseFlattening
*/
private final double inverseFlattening;
/**
* Tells if the Inverse Flattening definitive for this ellipsoid.
*
* @see #isIvfDefinitive
*/
private final boolean ivfDefinitive;
/**
* The units of the semi-major and semi-minor axis values.
*/
private final Unit unit;
/**
* Constructs 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 true
if the inverse flattening is definitive.
* @param unit The units of the semi-major and semi-minor axis values.
*/
protected Ellipsoid(final CharSequence 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);
}
/**
* Constructs 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.
*
* @see org.geotools.cs.CoordinateSystemFactory#createEllipsoid
*
* @deprecated Replaced by {@link org.geotools.referencing.datum.DefaultEllipsoid#createEllipsoid}.
*/
public static Ellipsoid createEllipsoid(final CharSequence name,
final double semiMajorAxis,
final double semiMinorAxis,
final Unit unit)
{
if (semiMajorAxis == semiMinorAxis) {
return new Spheroid(name, semiMajorAxis, false, unit);
} else {
return new Ellipsoid(name, semiMajorAxis, semiMinorAxis,
semiMajorAxis/(semiMajorAxis-semiMinorAxis), false, unit);
}
}
/**
* Constructs 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.
*
* @see org.geotools.cs.CoordinateSystemFactory#createFlattenedSphere
*
* @deprecated Replaced by {@link org.geotools.referencing.datum.DefaultEllipsoid#createFlattenedSphere}.
*/
public static Ellipsoid createFlattenedSphere(final CharSequence name,
final double semiMajorAxis,
final double inverseFlattening,
final Unit unit)
{
if (Double.isInfinite(inverseFlattening)) {
return new Spheroid(name, semiMajorAxis, true, unit);
} else {
return new Ellipsoid(name, semiMajorAxis,
semiMajorAxis*(1-1/inverseFlattening),
inverseFlattening, true, unit);
}
}
/**
* Checks the argument validity. Argument value
should be
* greater than zero.
*
* @param name Argument name.
* @param value Argument value.
* @return value
.
* @throws IllegalArgumentException if value
is not greater
* than 0.
*/
static double check(final String name, final double value) throws IllegalArgumentException {
if (value>0) {
return value;
}
throw new IllegalArgumentException(Errors.format(
ErrorKeys.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()
*
* @deprecated Replaced by {@link org.geotools.referencing.datum.DefaultEllipsoid#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()
*
* @deprecated Replaced by {@link org.geotools.referencing.datum.DefaultEllipsoid#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: e=sqrt(2f-f˛)
.
*
* @deprecated Replaced by {@link org.geotools.referencing.datum.DefaultEllipsoid#getEccentricity}.
*/
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 (re and
* rp respectively) by the formula
* ivf=re/(re-rp)
.
* For perfect spheres, this method returns {@link Double#POSITIVE_INFINITY}
* (which is the correct value).
*
* @see org.opengis.cs.CS_Ellipsoid#getInverseFlattening()
*
* @deprecated Replaced by {@link org.geotools.referencing.datum.DefaultEllipsoid#getInverseFlattening}.
*/
public double getInverseFlattening() {
return inverseFlattening;
}
/**
* Tells if 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.
*
* @see org.opengis.cs.CS_Ellipsoid#isIvfDefinitive()
*
* @deprecated Replaced by {@link org.geotools.referencing.datum.DefaultEllipsoid#isIvfDefinitive}.
*/
public boolean isIvfDefinitive() {
return ivfDefinitive;
}
/**
* Returns the orthodromic distance between two geographic coordinates.
* The orthodromic distance is the shortest distance between two points
* on a sphere's surface. The default implementation delegates the work
* to {@link #orthodromicDistance(double,double,double,double)}.
*
* @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).
*
* @deprecated Replaced by {@link org.geotools.referencing.datum.DefaultEllipsoid#orthodromicDistance(Point2D,Point2D)}.
*/
public double orthodromicDistance(final Point2D P1, final Point2D P2) {
return orthodromicDistance(P1.getX(), P1.getY(), P2.getX(), P2.getY());
}
/**
* Returns the 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.
* This is different from the loxodromic distance, which is a
* longer distance on a path with a constant direction on the compass.
*
* @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's axis).
*
* @deprecated Replaced by {@link org.geotools.referencing.datum.DefaultEllipsoid#orthodromicDistance(double,double,double,double)}.
*/
public double orthodromicDistance(double x1, double y1, double x2, double y2) {
x1 = Math.toRadians(x1);
y1 = Math.toRadians(y1);
x2 = Math.toRadians(x2);
y2 = Math.toRadians(y2);
/*
* Solution of the geodetic inverse problem after T.Vincenty.
* Modified Rainsford's method with Helmert's elliptical terms.
* Effective in any azimuth and at any distance short of antipodal.
*
* Latitudes and longitudes in radians positive North and East.
* Forward azimuths at both points returned in radians from North.
*
* Programmed for CDC-6600 by LCDR L.Pfeifer NGS ROCKVILLE MD 18FEB75
* Modified for IBM SYSTEM 360 by John G.Gergen NGS ROCKVILLE MD 7507
* Ported from Fortran to Java by Martin Desruisseaux.
*
* Source: ftp://ftp.ngs.noaa.gov/pub/pcsoft/for_inv.3d/source/inverse.for
* subroutine INVER1
*/
final int MAX_ITERATIONS = 100;
final double EPS = 0.5E-13;
final double F = 1/getInverseFlattening();
final double R = 1-F;
double tu1 = R * Math.sin(y1) / Math.cos(y1);
double tu2 = R * Math.sin(y2) / Math.cos(y2);
double cu1 = 1 / Math.sqrt(tu1*tu1 + 1);
double cu2 = 1 / Math.sqrt(tu2*tu2 + 1);
double su1 = cu1*tu1;
double s = cu1*cu2;
double baz = s*tu2;
double faz = baz*tu1;
double x = x2-x1;
for (int i=0; i 0) {
cz = -cz/c2a + cy;
}
double e = cz*cz*2 - 1;
double c = ((-3*c2a+4)*F+4)*c2a*F/16;
double d = x;
x = ((e*cy*c+cz)*sy*c+y)*SA;
x = (1-c)*x*F + x2-x1;
if (Math.abs(d-x) <= EPS) {
if (false) {
// 'faz' and 'baz' are forward azimuths at both points.
// Since the current API can't returns this result, it
// doesn't worth to compute it at this time.
faz = Math.atan2(tu1, tu2);
baz = Math.atan2(cu1*sx, baz*cx - su1*cu2)+Math.PI;
}
x = Math.sqrt((1/(R*R)-1) * c2a + 1)+1;
x = (x-2)/x;
c = 1-x;
c = (x*x/4 + 1)/c;
d = (0.375*x*x - 1)*x;
x = e*cy;
s = 1-2*e;
s = ((((sy*sy*4 - 3)*s*cz*d/6-x)*d/4+cz)*sy*d+y)*c*R*getSemiMajorAxis();
return s;
}
}
// No convergence. It may be because coordinate points
// are equals or because they are at antipodes.
final double LEPS = 1E-10;
if (Math.abs(x1-x2)<=LEPS && Math.abs(y1-y2)<=LEPS) {
return 0; // Coordinate points are equals
}
if (Math.abs(y1)<=LEPS && Math.abs(y2)<=LEPS) {
return Math.abs(x1-x2) * getSemiMajorAxis(); // Points are on the equator.
}
// Other cases: no solution for this algorithm.
final CoordinateFormat format = new CoordinateFormat();
throw new ArithmeticException(Errors.format(ErrorKeys.NO_CONVERGENCE_$2,
format.format(new GeneralDirectPosition(Math.toDegrees(x1),Math.toDegrees(y1))),
format.format(new GeneralDirectPosition(Math.toDegrees(x2),Math.toDegrees(y2)))));
}
/**
* Returns the units of the semi-major and semi-minor axis values.
*
* @see org.opengis.cs.CS_Ellipsoid#getAxisUnit()
*
* @deprecated Replaced by {@link org.geotools.referencing.datum.DefaultEllipsoid#getAxisUnit}.
*/
public Unit getAxisUnit() {
return unit;
}
/**
* Compare this ellipsoid with the specified object for equality.
*
* @param object The object to compare to this
.
* @param compareNames true
to comparare the {@linkplain #getName name},
* {@linkplain #getAlias alias}, {@linkplain #getAuthorityCode authority
* code}, etc. as well, or false
to compare only properties
* relevant to transformations.
* @return true
if both objects are equal.
*/
public boolean equals(final Info object, final boolean compareNames) {
if (object == this) {
return true;
}
if (super.equals(object, compareNames)) {
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. {@linkplain #getName Name},
* {@linkplain #getAlias alias}, {@linkplain #getAuthorityCode authority code}
* and the like are not taken in account. In other words, two ellipsoids
* will return the same hash value if they are equal in the sense of
* {@link #equals equals}(Info, false)
.
*
* @return The hash code value. This value doesn't need to be the same
* in past or future versions of this class.
*/
public int hashCode() {
long longCode = 37*Double.doubleToLongBits(semiMajorAxis);
if (ivfDefinitive) {
longCode += inverseFlattening;
} else {
longCode += semiMinorAxis;
}
return (((int)(longCode >>> 32)) ^ (int)longCode);
}
/**
* Fills the part inside "[...]".
* Used for formatting Well Known Text (WKT).
*/
String addString(final StringBuffer buffer, final Unit context) {
final double ivf = getInverseFlattening();
buffer.append(", ");
buffer.append(getSemiMajorAxis());
buffer.append(", ");
buffer.append(Double.isInfinite(ivf) ? 0 : ivf);
return "SPHEROID";
}
/**
* Returns an OpenGIS interface for this ellipsoid.
* The returned object is suitable for RMI use.
*
* Note: The returned type is a generic {@link Object} in order
* to avoid premature class loading of OpenGIS interface.
*/
final Object toOpenGIS(final Object adapters) throws RemoteException {
return new Export(adapters);
}
/////////////////////////////////////////////////////////////////////////
//////////////// ////////////////
//////////////// OPENGIS ADAPTER ////////////////
//////////////// ////////////////
/////////////////////////////////////////////////////////////////////////
/**
* Wrap a {@link Ellipsoid} object for use with OpenGIS.
* This class is suitable for RMI use.
*/
private final class Export extends Info.Export implements CS_Ellipsoid {
/**
* Constructs a remote object.
*/
protected Export(final Object adapters) throws RemoteException {
super(adapters);
}
/**
* Gets the equatorial radius.
*/
public double getSemiMajorAxis() throws RemoteException {
return Ellipsoid.this.getSemiMajorAxis();
}
/**
* Gets the polar radius.
*/
public double getSemiMinorAxis() throws RemoteException {
return Ellipsoid.this.getSemiMinorAxis();
}
/**
* Returns the value of the inverse of the flattening constant.
*/
public double getInverseFlattening() throws RemoteException {
final double ivf=Ellipsoid.this.getInverseFlattening();
return Double.isInfinite(ivf) ? 0 : ivf;
}
/**
* Tell if the Inverse Flattening definitive for this ellipsoid.
*/
public boolean isIvfDefinitive() throws RemoteException {
return Ellipsoid.this.isIvfDefinitive();
}
/**
* Returns the linear unit.
*/
public CS_LinearUnit getAxisUnit() throws RemoteException {
return (CS_LinearUnit) adapters.export(Ellipsoid.this.getAxisUnit());
}
}
}