/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 1999-2008, Open Source Geospatial Foundation (OSGeo) * * 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; * version 2.1 of the License. * * 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. * * This package contains formulas from the PROJ package of USGS. * USGS's work is fully acknowledged here. This derived work has * been relicensed under LGPL with Frank Warmerdam's permission. */ package org.geotools.referencing.operation.projection; import java.awt.geom.Point2D; import org.opengis.parameter.ParameterValueGroup; import org.opengis.parameter.ParameterDescriptorGroup; import org.opengis.parameter.ParameterNotFoundException; import org.geotools.resources.i18n.ErrorKeys; import static java.lang.Math.*; /** * The USGS oblique/equatorial case of the Stereographic projection. This is similar but * NOT equal to EPSG code 9809 ({@code "Oblique_Stereographic"} EPSG name). * The later is rather implemented by {@link ObliqueStereographic}. *
* This class is not public in order to keep names that closely match the ones in common usage
* (i.e. this projection is called just "Stereographic" in ESRI). Furthermore, the "USGS" name
* is not really accurate for a class to be extended by {@link ObliqueStereographic}.
*
* @since 2.4
* @source $URL$
* @version $Id$
* @author Gerald I. Evenden (for original code in Proj4)
* @author André Gosselin
* @author Martin Desruisseaux (PMO, IRD)
* @author Rueben Schulz
*/
class StereographicUSGS extends Stereographic {
/**
* For compatibility with different versions during deserialization.
*/
private static final long serialVersionUID = 948619442800459871L;
/**
* Maximum number of iterations for iterative computations.
*/
private static final int MAXIMUM_ITERATIONS = 15;
/**
* Difference allowed in iterative computations.
*/
private static final double ITERATION_TOLERANCE = 1E-10;
/**
* Maximum difference allowed when comparing real numbers.
*/
private static final double EPSILON = 1E-6;
/**
* Constants used for the oblique projections. All those constants are completly determined by
* {@link #latitudeOfOrigin}. Concequently, there is no need to test them in {@link #hashCode}
* or {@link #equals} methods.
*/
final double k0, sinphi0, cosphi0, chi1, sinChi1, cosChi1;
/**
* Constructs an oblique stereographic projection (USGS equations).
*
* @param parameters The group of parameter values.
* @throws ParameterNotFoundException if a required parameter was not found.
*/
protected StereographicUSGS(final ParameterValueGroup parameters)
throws ParameterNotFoundException
{
this(parameters, Provider.PARAMETERS);
}
/**
* Constructs an oblique stereographic projection (USGS equations).
*
* @param parameters The group of parameter values.
* @param descriptor The expected parameter descriptor.
* @throws ParameterNotFoundException if a required parameter was not found.
*/
StereographicUSGS(final ParameterValueGroup parameters,
final ParameterDescriptorGroup descriptor)
throws ParameterNotFoundException
{
super(parameters, descriptor);
if (abs(latitudeOfOrigin) < EPSILON) { // Equatorial
latitudeOfOrigin = 0;
cosphi0 = 1.0;
sinphi0 = 0.0;
chi1 = 0.0;
cosChi1 = 1.0;
sinChi1 = 0.0;
} else { // Oblique
cosphi0 = cos(latitudeOfOrigin);
sinphi0 = sin(latitudeOfOrigin);
chi1 = 2.0 * atan(ssfn(latitudeOfOrigin, sinphi0)) - PI/2;
cosChi1 = cos(chi1);
sinChi1 = sin(chi1);
}
// part of (14 - 15)
k0 = 2.0 * msfn(sinphi0, cosphi0);
}
/**
* Transforms the specified (λ,φ) coordinates
* (units in radians) and stores the result in {@code ptDst} (linear distance
* on a unit sphere).
*/
protected Point2D transformNormalized(double x, double y, Point2D ptDst)
throws ProjectionException
{
final double chi = 2.0 * atan(ssfn(y, sin(y))) - PI/2;
final double sinChi = sin(chi);
final double cosChi = cos(chi);
final double cosChi_cosLon = cosChi * cos(x);
final double A = k0 / cosChi1 / (1 + sinChi1*sinChi + cosChi1*cosChi_cosLon);
x = A * cosChi * sin(x);
y = A * (cosChi1 * sinChi - sinChi1 * cosChi_cosLon);
if (ptDst != null) {
ptDst.setLocation(x,y);
return ptDst;
}
return new Point2D.Double(x,y);
}
/**
* Transforms the specified (x,y) coordinates
* and stores the result in {@code ptDst}.
*/
protected Point2D inverseTransformNormalized(double x, double y, Point2D ptDst)
throws ProjectionException
{
final double rho = hypot(x, y);
final double ce = 2.0 * atan2(rho * cosChi1, k0);
final double cosce = cos(ce);
final double since = sin(ce);
final boolean rhoIs0 = abs(rho) < EPSILON;
final double chi = rhoIs0 ? chi1 : asin(cosce*sinChi1 + (y*since*cosChi1 / rho));
final double tp = tan(PI/4.0 + chi/2.0);
// parts of (21-36) used to calculate longitude
final double t = x*since;
final double ct = rho*cosChi1*cosce - y*sinChi1*since;
// Compute latitude using iterative technique (3-4)
final double halfe = excentricity / 2.0;
double phi0 = chi;
for (int i=MAXIMUM_ITERATIONS;;) {
final double esinphi = excentricity * sin(phi0);
final double phi = 2*atan(tp*pow((1+esinphi)/(1-esinphi), halfe)) - PI/2;
if (abs(phi - phi0) < ITERATION_TOLERANCE) {
// TODO: checking rho may be redundant
x = rhoIs0 || (abs(t)