PolarStereographic.cpp
Go to the documentation of this file.
00001 /**
00002  * \file PolarStereographic.cpp
00003  * \brief Implementation for GeographicLib::PolarStereographic class
00004  *
00005  * Copyright (c) Charles Karney (2008) <charles@karney.com>
00006  * and licensed under the LGPL.
00007  **********************************************************************/
00008 
00009 #include "GeographicLib/PolarStereographic.hpp"
00010 #include "GeographicLib/Constants.hpp"
00011 #include <limits>
00012 
00013 namespace {
00014   char RCSID[] = "$Id: PolarStereographic.cpp 6568 2009-03-01 17:58:41Z ckarney $";
00015   char RCSID_H[] = POLARSTEREOGRAPHIC_HPP;
00016 }
00017 
00018 namespace GeographicLib {
00019 
00020   using namespace std;
00021 
00022   PolarStereographic::PolarStereographic(double a, double invf, double k0)
00023     throw()
00024     : _a(a)
00025     , _f(invf > 0 ? 1 / invf : 0)
00026     , _k0(k0)
00027     , _e(sqrt(_f * (2 - _f)))
00028     , _e2m(1 - sq(_e))
00029     , _c(sqrt( pow(1 + _e, 1 + _e) * pow(1 - _e, 1 - _e) ))
00030     , _tol(0.1*sqrt(numeric_limits<double>::epsilon()))
00031     , _numit(5)
00032   {}
00033 
00034   const PolarStereographic
00035   PolarStereographic::UPS(Constants::WGS84_a(), Constants::WGS84_invf(),
00036                           Constants::UPS_k0());
00037 
00038   void PolarStereographic::Forward(bool northp, double lat, double lon,
00039                                    double& x, double& y,
00040                                    double& gamma, double& k) const throw() {
00041     double theta = 90 - (northp ? lat : -lat); //  the colatitude
00042     double rho;
00043     theta *= Constants::degree();
00044     double
00045       ecos = _e * cos(theta),
00046       f = pow((1 + ecos)/(1 - ecos), _e/2),
00047       t2 = 2 * tan(theta/2) * f,           // Snyder (15-9) (t2 = 2 * t)
00048       m = sin(theta) / sqrt(1 - sq(ecos)); // Snyder (14-15)
00049     rho = _a * _k0 * t2 / _c;              // Snyder (21-33)
00050     k = m < numeric_limits<double>::epsilon() ? _k0 : rho / (_a * m);
00051     double
00052       lam = lon * Constants::degree();
00053     x = rho * sin(lam);
00054     y = (northp ? -rho : rho) * cos(lam);
00055     gamma = northp ? lon : -lon;
00056   }
00057 
00058   void PolarStereographic::Reverse(bool northp, double x, double y,
00059                                    double& lat, double& lon,
00060                                    double& gamma, double& k) const throw() {
00061     double
00062       rho = hypot(x, y),
00063       t2 = rho * _c / (_a * _k0),
00064       theta = Constants::pi()/2,        // initial estimate of colatitude
00065       ecos = _e * cos(theta);
00066     // Solve from theta using Newton's method on Snyder (15-9) which converges
00067     // more rapidly than the iteration procedure given by Snyder (7-9).  First
00068     // rewrite as
00069     // v(theta) = 2 * tan(theta/2) * f - t2 = 0
00070     for (int i = 0; i < _numit; ++i) {
00071       double
00072         f = pow((1 + ecos)/(1 - ecos), _e/2),
00073         c2 = cos(theta/2),
00074         v = 2 * tan(theta/2) * f - t2,
00075         dv = _e2m * f / ((1 - sq(ecos)) * sq(c2)), // dv/dtheta
00076         dtheta = -v/dv;
00077       theta += dtheta;
00078       ecos = _e * cos(theta);
00079       if (abs(dtheta) < _tol)
00080         break;
00081     }
00082     lat = (northp ? 1 : -1) * (90 - theta / Constants::degree());
00083     // Result is in [-180, 180).  Assume atan2(0,0) = 0.
00084     lon = -atan2( -x, northp ? -y : y ) / Constants::degree();
00085     double m = sin(theta) / sqrt(1 - sq(ecos));
00086     k = m == 0 ? _k0 : rho / (_a * m);
00087     gamma = northp ? lon : -lon;
00088   }
00089 
00090 } // namespace GeographicLib