AlbersEqualArea.cpp

Go to the documentation of this file.
00001 /**
00002  * \file AlbersEqualArea.cpp
00003  * \brief Implementation for GeographicLib::AlbersEqualArea class
00004  *
00005  * Copyright (c) Charles Karney (2010, 2011) <charles@karney.com> and licensed
00006  * under the LGPL.  For more information, see
00007  * http://geographiclib.sourceforge.net/
00008  **********************************************************************/
00009 
00010 #include "GeographicLib/AlbersEqualArea.hpp"
00011 
00012 #define GEOGRAPHICLIB_ALBERSEQUALAREA_CPP "$Id: AlbersEqualArea.cpp 6937 2011-02-01 20:17:13Z karney $"
00013 
00014 RCSID_DECL(GEOGRAPHICLIB_ALBERSEQUALAREA_CPP)
00015 RCSID_DECL(GEOGRAPHICLIB_ALBERSEQUALAREA_HPP)
00016 
00017 namespace GeographicLib {
00018 
00019   using namespace std;
00020 
00021   const Math::real AlbersEqualArea::eps = numeric_limits<real>::epsilon();
00022   const Math::real AlbersEqualArea::epsx = sq(eps);
00023   const Math::real AlbersEqualArea::epsx2 = sq(epsx);
00024   const Math::real AlbersEqualArea::tol = sqrt(eps);
00025   const Math::real AlbersEqualArea::tol0 = tol * sqrt(sqrt(eps));
00026   const Math::real AlbersEqualArea::ahypover =
00027     real(numeric_limits<real>::digits) * log(real(numeric_limits<real>::radix))
00028     + 2;
00029 
00030   AlbersEqualArea::AlbersEqualArea(real a, real r,
00031                                    real stdlat, real k0)
00032     : _a(a)
00033     , _r(r)
00034     , _f(_r != 0 ? 1 / _r : 0)
00035     , _fm(1 - _f)
00036     , _e2(_f * (2 - _f))
00037     , _e(sqrt(abs(_e2)))
00038     , _e2m(1 - _e2)
00039     , _qZ(1 + _e2m * atanhee(real(1)))
00040     , _qx(_qZ / ( 2 * _e2m ))
00041   {
00042     if (!(_a > 0))
00043       throw GeographicErr("Major radius is not positive");
00044     if (!(_f < 1))
00045       throw GeographicErr("Minor radius is not positive");
00046     if (!(k0 > 0))
00047       throw GeographicErr("Scale is not positive");
00048     if (!(abs(stdlat) <= 90))
00049       throw GeographicErr("Standard latitude not in [-90, 90]");
00050     real
00051       phi = stdlat * Math::degree<real>(),
00052       sphi = sin(phi),
00053       cphi = abs(stdlat) != 90 ? cos(phi) : 0;
00054     Init(sphi, cphi, sphi, cphi, k0);
00055   }
00056 
00057   AlbersEqualArea::AlbersEqualArea(real a, real r,
00058                                    real stdlat1, real stdlat2,
00059                                    real k1)
00060     : _a(a)
00061     , _r(r)
00062     , _f(_r != 0 ? 1 / _r : 0)
00063     , _fm(1 - _f)
00064     , _e2(_f * (2 - _f))
00065     , _e(sqrt(abs(_e2)))
00066     , _e2m(1 - _e2)
00067     , _qZ(1 + _e2m * atanhee(real(1)))
00068     , _qx(_qZ / ( 2 * _e2m ))
00069   {
00070     if (!(_a > 0))
00071       throw GeographicErr("Major radius is not positive");
00072     if (!(_f < 1))
00073       throw GeographicErr("Minor radius is not positive");
00074     if (!(k1 > 0))
00075       throw GeographicErr("Scale is not positive");
00076     if (!(abs(stdlat1) <= 90))
00077       throw GeographicErr("Standard latitude 1 not in [-90, 90]");
00078     if (!(abs(stdlat2) <= 90))
00079       throw GeographicErr("Standard latitude 2 not in [-90, 90]");
00080     real
00081       phi1 = stdlat1 * Math::degree<real>(),
00082       phi2 = stdlat2 * Math::degree<real>();
00083     Init(sin(phi1), abs(stdlat1) != 90 ? cos(phi1) : 0,
00084          sin(phi2), abs(stdlat2) != 90 ? cos(phi2) : 0, k1);
00085   }
00086 
00087   AlbersEqualArea::AlbersEqualArea(real a, real r,
00088                                                real sinlat1, real coslat1,
00089                                                real sinlat2, real coslat2,
00090                                                real k1)
00091     : _a(a)
00092     , _r(r)
00093     , _f(_r != 0 ? 1 / _r : 0)
00094     , _fm(1 - _f)
00095     , _e2(_f * (2 - _f))
00096     , _e(sqrt(abs(_e2)))
00097     , _e2m(1 - _e2)
00098     , _qZ(1 + _e2m * atanhee(real(1)))
00099     , _qx(_qZ / ( 2 * _e2m ))
00100   {
00101     if (!(_a > 0))
00102       throw GeographicErr("Major radius is not positive");
00103     if (!(_f < 1))
00104       throw GeographicErr("Minor radius is not positive");
00105     if (!(k1 > 0))
00106       throw GeographicErr("Scale is not positive");
00107     if (!(coslat1 >= 0))
00108       throw GeographicErr("Standard latitude 1 not in [-90, 90]");
00109     if (!(coslat2 >= 0))
00110       throw GeographicErr("Standard latitude 2 not in [-90, 90]");
00111     if (!(abs(sinlat1) <= 1 && coslat1 <= 1) || (coslat1 == 0 && sinlat1 == 0))
00112       throw GeographicErr("Bad sine/cosine of standard latitude 1");
00113     if (!(abs(sinlat2) <= 1 && coslat2 <= 1) || (coslat2 == 0 && sinlat2 == 0))
00114       throw GeographicErr("Bad sine/cosine of standard latitude 2");
00115     if (coslat1 == 0 && coslat2 == 0 && sinlat1 * sinlat2 <= 0)
00116       throw GeographicErr
00117         ("Standard latitudes cannot be opposite poles");
00118     Init(sinlat1, coslat1, sinlat2, coslat2, k1);
00119   }
00120 
00121   void AlbersEqualArea::Init(real sphi1, real cphi1,
00122                                    real sphi2, real cphi2, real k1) throw() {
00123     {
00124       real r;
00125       r = Math::hypot(sphi1, cphi1);
00126       sphi1 /= r; cphi1 /= r;
00127       r = Math::hypot(sphi2, cphi2);
00128       sphi2 /= r; cphi2 /= r;
00129     }
00130     bool polar = (cphi1 == 0);
00131     cphi1 = max(epsx, cphi1);   // Avoid singularities at poles
00132     cphi2 = max(epsx, cphi2);
00133     // Determine hemisphere of tangent latitude
00134     _sign = sphi1 + sphi2 >= 0 ? 1 : -1;
00135     // Internally work with tangent latitude positive
00136     sphi1 *= _sign; sphi2 *= _sign;
00137     if (sphi1 > sphi2) {
00138       swap(sphi1, sphi2); swap(cphi1, cphi2); // Make phi1 < phi2
00139     }
00140     real
00141       tphi1 = sphi1/cphi1, tphi2 = sphi2/cphi2;
00142 
00143     // q = (1-e^2)*(sphi/(1-e^2*sphi^2) - atanhee(sphi))
00144     // qZ = q(pi/2) = (1 + (1-e^2)*atanhee(1))
00145     // atanhee(x) = atanh(e*x)/e
00146     // q = sxi * qZ
00147     // dq/dphi = 2*(1-e^2)*cphi/(1-e^2*sphi^2)^2
00148     //
00149     // n = (m1^2-m2^2)/(q2-q1) -> sin(phi0) for phi1, phi2 -> phi0
00150     // C = m1^2 + n*q1 = (m1^2*q2-m2^2*q1)/(q2-q1)
00151     // let
00152     //   rho(pi/2)/rho(-pi/2) = (1-s)/(1+s)
00153     //   s = n*qZ/C
00154     //     = qZ * (m1^2-m2^2)/(m1^2*q2-m2^2*q1)
00155     //     = qZ * (scbet2^2 - scbet1^2)/(scbet2^2*q2 - scbet1^2*q1)
00156     //     = (scbet2^2 - scbet1^2)/(scbet2^2*sxi2 - scbet1^2*sxi1)
00157     //     = (tbet2^2 - tbet1^2)/(scbet2^2*sxi2 - scbet1^2*sxi1)
00158     // 1-s = -((1-sxi2)*scbet2^2 - (1-sxi1)*scbet1^2)/
00159     //         (scbet2^2*sxi2 - scbet1^2*sxi1)
00160     //
00161     // Define phi0 to give same value of s, i.e.,
00162     //  s = sphi0 * qZ / (m0^2 + sphi0*q0)
00163     //    = sphi0 * scbet0^2 / (1/qZ + sphi0 * scbet0^2 * sxi0)
00164 
00165     real tphi0, C;
00166     if (polar || tphi1 == tphi2) {
00167       tphi0 = tphi2;
00168       C = 1;                    // ignored
00169     } else {
00170       real
00171         tbet1 = _fm * tphi1, scbet12 = 1 + sq(tbet1),
00172         tbet2 = _fm * tphi2, scbet22 = 1 + sq(tbet2),
00173         txi1 = txif(tphi1), cxi1 = 1/hyp(txi1), sxi1 = txi1 * cxi1,
00174         txi2 = txif(tphi2), cxi2 = 1/hyp(txi2), sxi2 = txi2 * cxi2,
00175         dtbet2 = _fm * (tbet1 + tbet2),
00176         es1 = 1 - _e2 * sq(sphi1), es2 = 1 - _e2 * sq(sphi2),
00177         /*
00178         dsxi = ( (_e2 * sq(sphi2 + sphi1) + es2 + es1) / (2 * es2 * es1) +
00179                  Datanhee(sphi2, sphi1) ) * Dsn(tphi2, tphi1, sphi2, sphi1) /
00180         ( 2 * _qx ),
00181         */
00182         dsxi = ( (1 + _e2 * sphi1 * sphi2) / (es2 * es1) +
00183                  Datanhee(sphi2, sphi1) ) * Dsn(tphi2, tphi1, sphi2, sphi1) /
00184         ( 2 * _qx ),
00185         den = (sxi2 + sxi1) * dtbet2 + (scbet22 + scbet12) * dsxi,
00186         // s = (sq(tbet2) - sq(tbet1)) / (scbet22*sxi2 - scbet12*sxi1)
00187         s = 2 * dtbet2 / den,
00188         // 1-s = -(sq(scbet2)*(1-sxi2) - sq(scbet1)*(1-sxi1)) /
00189         //        (scbet22*sxi2 - scbet12*sxi1)
00190         // Write
00191         //   sq(scbet)*(1-sxi) = sq(scbet)*(1-sphi) * (1-sxi)/(1-sphi)
00192         sm1 = -Dsn(tphi2, tphi1, sphi2, sphi1) *
00193         ( -( ((sphi2 <= 0 ? (1 - sxi2) / (1 - sphi2) :
00194                sq(cxi2/cphi2) * (1 + sphi2) / (1 + sxi2)) +
00195               (sphi1 <= 0 ? (1 - sxi1) / (1 - sphi1) :
00196                sq(cxi1/cphi1) * (1 + sphi1) / (1 + sxi1))) ) *
00197           (1 + _e2 * (sphi1 + sphi2 + sphi1 * sphi2)) /
00198           (1 +       (sphi1 + sphi2 + sphi1 * sphi2)) +
00199           (scbet22 * (sphi2 <= 0 ? 1 - sphi2 : sq(cphi2) / ( 1 + sphi2)) +
00200            scbet12 * (sphi1 <= 0 ? 1 - sphi1 : sq(cphi1) / ( 1 + sphi1))) *
00201          (_e2 * (1 + sphi1 + sphi2 + _e2 * sphi1 * sphi2)/(es1 * es2)
00202           +_e2m * DDatanhee(sphi1, sphi2) ) / _qZ ) / den;
00203       // C = (scbet22*sxi2 - scbet12*sxi1) / (scbet22 * scbet12 * (sx2 - sx1))
00204       C = den / (2 * scbet12 * scbet22 * dsxi);
00205       tphi0 = (tphi2 + tphi1)/2;
00206       real stol = tol0 * max(real(1), abs(tphi0));
00207       for (int i = 0; i < 2*numit0; ++i) {
00208         // Solve (scbet0^2 * sphi0) / (1/qZ + scbet0^2 * sphi0 * sxi0) = s
00209         // for tphi0 by Newton's method on
00210         // v(tphi0) = (scbet0^2 * sphi0) - s * (1/qZ + scbet0^2 * sphi0 * sxi0)
00211         //          = 0
00212         // Alt:
00213         // (scbet0^2 * sphi0) / (1/qZ - scbet0^2 * sphi0 * (1-sxi0)) = s / (1-s)
00214         // w(tphi0) = (1-s) * (scbet0^2 * sphi0)
00215         //             - s  * (1/qZ - scbet0^2 * sphi0 * (1-sxi0))
00216         //          = (1-s) * (scbet0^2 * sphi0)
00217         //             - S/qZ  * (1 - scbet0^2 * sphi0 * (qZ-q0))
00218         // Now
00219         // qZ-q0 = (1+e2*sphi0)*(1-sphi0)/(1-e2*sphi0^2) +
00220         //         (1-e2)*atanhee((1-sphi0)/(1-e2*sphi0))
00221         // In limit sphi0 -> 1, qZ-q0 -> 2*(1-sphi0)/(1-e2), so wrte
00222         // qZ-q0 = 2*(1-sphi0)/(1-e2) + A + B
00223         // A = (1-sphi0)*( (1+e2*sphi0)/(1-e2*sphi0^2) - (1+e2)/(1-e2) )
00224         //   = -e2 *(1-sphi0)^2 * (2+(1+e2)*sphi0) / ((1-e2)*(1-e2*sphi0^2))
00225         // B = (1-e2)*atanhee((1-sphi0)/(1-e2*sphi0)) - (1-sphi0)
00226         //   = (1-sphi0)*(1-e2)/(1-e2*sphi0)*
00227         //     ((atanhee(x)/x-1) - e2*(1-sphi0)/(1-e2))
00228         // x = (1-sphi0)/(1-e2*sphi0), atanhee(x)/x = atanh(e*x)/(e*x)
00229         //
00230         // 1 - scbet0^2 * sphi0 * (qZ-q0)
00231         //   = 1 - scbet0^2 * sphi0 * (2*(1-sphi0)/(1-e2) + A + B)
00232         //   = D - scbet0^2 * sphi0 * (A + B)
00233         // D = 1 - scbet0^2 * sphi0 * 2*(1-sphi0)/(1-e2)
00234         //   = (1-sphi0)*(1-e2*(1+2*sphi0*(1+sphi0)))/((1-e2)*(1+sphi0))
00235         // dD/dsphi0 = -2*(1-e2*sphi0^2*(2*sphi0+3))/((1-e2)*(1+sphi0)^2)
00236         // d(A+B)/dsphi0 = 2*(1-sphi0^2)*e2*(2-e2*(1+sphi0^2))/
00237         //                 ((1-e2)*(1-e2*sphi0^2)^2)
00238 
00239         real
00240           scphi02 = 1 + sq(tphi0), scphi0 = sqrt(scphi02),
00241           // sphi0m = 1-sin(phi0) = 1/( sec(phi0) * (tan(phi0) + sec(phi0)) )
00242           sphi0 = tphi0 / scphi0, sphi0m = 1/(scphi0 * (tphi0 + scphi0)),
00243           // scbet0^2 * sphi0
00244           g = (1 + sq( _fm * tphi0 )) * sphi0,
00245           // dg/dsphi0 = dg/dtphi0 * scphi0^3
00246           dg = _e2m * scphi02 * (1 + 2 * sq(tphi0)) + _e2,
00247           D = sphi0m * (1 - _e2*(1 + 2*sphi0*(1+sphi0))) / (_e2m * (1+sphi0)),
00248           // dD/dsphi0
00249           dD = -2 * (1 - _e2*sq(sphi0) * (2*sphi0+3)) / (_e2m * sq(1+sphi0)),
00250           A = -_e2 * sq(sphi0m) * (2+(1+_e2)*sphi0) / (_e2m*(1-_e2*sq(sphi0))),
00251           B = (sphi0m * _e2m / (1 - _e2*sphi0) *
00252                (atanhxm1(_e2 * sq(sphi0m / (1-_e2*sphi0))) - _e2*sphi0m/_e2m)),
00253           // d(A+B)/dsphi0
00254           dAB = (2 * _e2 * (2 - _e2 * (1 + sq(sphi0))) /
00255                  (_e2m * sq(1 - _e2*sq(sphi0)) * scphi02)),
00256           u = sm1 * g - s/_qZ * ( D - g * (A + B) ),
00257           // du/dsphi0
00258           du = sm1 * dg - s/_qZ * (dD - dg * (A + B) - g * dAB),
00259           dtu = -u/du * (scphi0 * scphi02);
00260         tphi0 += dtu;
00261         if (!(abs(dtu) >= stol))
00262           break;
00263       }
00264     }
00265     _txi0 = txif(tphi0); _scxi0 = hyp(_txi0); _sxi0 = _txi0 / _scxi0;
00266     _n0 = tphi0/hyp(tphi0);
00267     _m02 = 1 / (1 + sq(_fm * tphi0));
00268     _nrho0 = polar ? 0 : _a * sqrt(_m02);
00269     _k0 = sqrt(tphi1 == tphi2 ? 1 : C / (_m02 + _n0 * _qZ * _sxi0)) * k1;
00270     _k2 = sq(_k0);
00271     _lat0 = _sign * atan(tphi0)/Math::degree<real>();
00272   }
00273 
00274   const AlbersEqualArea
00275   AlbersEqualArea::CylindricalEqualArea(Constants::WGS84_a<real>(),
00276                                         Constants::WGS84_r<real>(),
00277                                         real(0), real(1), real(0), real(1),
00278                                         real(1));
00279 
00280   const AlbersEqualArea
00281   AlbersEqualArea::AzimuthalEqualAreaNorth(Constants::WGS84_a<real>(),
00282                                            Constants::WGS84_r<real>(),
00283                                            real(1), real(0), real(1), real(0),
00284                                            real(1));
00285 
00286   const AlbersEqualArea
00287   AlbersEqualArea::AzimuthalEqualAreaSouth(Constants::WGS84_a<real>(),
00288                                            Constants::WGS84_r<real>(),
00289                                            real(-1), real(0), real(-1), real(0),
00290                                            real(1));
00291 
00292   Math::real AlbersEqualArea::txif(real tphi) const throw() {
00293     // sxi = ( sphi/(1-e2*sphi^2) + atanhee(sphi) ) /
00294     //       ( 1/(1-e2) + atanhee(1) )
00295     //
00296     // txi = ( sphi/(1-e2*sphi^2) + atanhee(sphi) ) /
00297     //       sqrt( ( (1+e2*sphi)*(1-sphi)/( (1-e2*sphi^2) * (1-e2) ) +
00298     //               atanhee((1-sphi)/(1-e2*sphi)) ) *
00299     //             ( (1-e2*sphi)*(1+sphi)/( (1-e2*sphi^2) * (1-e2) ) +
00300     //               atanhee((1+sphi)/(1+e2*sphi)) ) )
00301     //
00302     // subst 1-sphi = cphi^2/(1+sphi)
00303     int s = tphi < 0 ? -1 : 1;  // Enforce odd parity
00304     tphi *= s;
00305     real
00306       cphi2 = 1 / (1 + sq(tphi)),
00307       sphi = tphi * sqrt(cphi2),
00308       es1 = _e2 * sphi,
00309       es2m1 = 1 - es1 * sphi,
00310       sp1 = 1 + sphi,
00311       es1m1 = (1 - es1) * sp1,
00312       es2m1a = _e2m * es2m1,
00313       es1p1 = sp1 / (1 + es1);
00314     return s * ( sphi / es2m1 + atanhee(sphi) ) /
00315       sqrt( ( cphi2 / (es1p1 * es2m1a) + atanhee(cphi2 / es1m1) ) *
00316             ( es1m1 / es2m1a + atanhee(es1p1) ) );
00317   }
00318 
00319   Math::real AlbersEqualArea::tphif(real txi) const throw() {
00320     real
00321       tphi = txi,
00322       stol = tol * max(real(1), abs(txi));
00323     // CHECK: min iterations = 1, max iterations = 2; mean = 1.99
00324     for (int i = 0; i < numit; ++i) {
00325       // dtxi/dtphi = (scxi/scphi)^3 * 2*(1-e^2)/(qZ*(1-e^2*sphi^2)^2)
00326       real
00327         txia = txif(tphi),
00328         tphi2 = sq(tphi),
00329         scphi2 = 1 + tphi2,
00330         scterm = scphi2/(1 + sq(txia)),
00331         dtphi = (txi - txia) * scterm * sqrt(scterm) *
00332         _qx * sq(1 - _e2 * tphi2 / scphi2);
00333       tphi += dtphi;
00334       if (!(abs(dtphi) >= stol))
00335         break;
00336     }
00337     return tphi;
00338   }
00339 
00340   // return atanh(sqrt(x))/sqrt(x) - 1 = y/3 + y^2/5 + y^3/7 + ...
00341   // typical x < e^2 = 2*f
00342   Math::real AlbersEqualArea::atanhxm1(real x) throw() {
00343     real s = 0;
00344     if (abs(x) < real(0.5)) {
00345       real os = -1, y = 1, k = 1;
00346       while (os != s) {
00347         os = s;
00348         y *= x;                 // y = x^n
00349         k += 2;                 // k = 2*n + 1
00350         s += y/k;               // sum( x^n/(2*n + 1) )
00351       }
00352     } else {
00353       real xs = sqrt(abs(x));
00354       s = (x > 0 ? Math::atanh(xs) : atan(xs)) / xs - 1;
00355     }
00356     return s;
00357   }
00358 
00359   // return (Datanhee(1,y) - Datanhee(1,x))/(y-x)
00360   Math::real AlbersEqualArea::DDatanhee(real x, real y) const throw() {
00361     real s = 0;
00362     if (_e2 * (abs(x) + abs(y)) < real(0.5)) {
00363       real os = -1, z = 1, k = 1, t = 0, c = 0, en = 1;
00364       while (os != s) {
00365         os = s;
00366         t = y * t + z; c += t; z *= x;
00367         t = y * t + z; c += t; z *= x;
00368         k += 2; en *= _e2;
00369         // Here en[l] = e2^l, k[l] = 2*l + 1,
00370         // c[l] = sum( x^i * y^j; i >= 0, j >= 0, i+j < 2*l)
00371         s += en * c / k;
00372       }
00373       // Taylor expansion is
00374       // s = sum( c[l] * e2^l / (2*l + 1), l, 1, N)
00375     } else
00376       s = (Datanhee(1, y) - Datanhee(x, y))/(1 - x);
00377     return s;
00378   }
00379 
00380   void AlbersEqualArea::Forward(real lon0, real lat, real lon,
00381                                 real& x, real& y, real& gamma, real& k)
00382     const throw() {
00383     if (lon - lon0 >= 180)
00384       lon -= lon0 + 360;
00385     else if (lon - lon0 < -180)
00386       lon -= lon0 - 360;
00387     else
00388       lon -= lon0;
00389     lat *= _sign;
00390     real
00391       lam = lon * Math::degree<real>(),
00392       phi = lat * Math::degree<real>(),
00393       sphi = sin(phi), cphi = abs(lat) != 90 ? cos(phi) : epsx,
00394       tphi = sphi/cphi, txi = txif(tphi), sxi = txi/hyp(txi),
00395       dq = _qZ * Dsn(txi, _txi0, sxi, _sxi0) * (txi - _txi0),
00396       drho = - _a * dq / (sqrt(_m02 - _n0 * dq) + _nrho0 / _a),
00397       theta = _k2 * _n0 * lam, stheta = sin(theta), ctheta = cos(theta),
00398       t = _nrho0 + _n0 * drho;
00399     x = t * (_n0 != 0 ? stheta / _n0 : _k2 * lam) / _k0;
00400     y = (_nrho0 *
00401          (_n0 != 0 ?
00402           (ctheta < 0 ? 1 - ctheta : sq(stheta)/(1 + ctheta)) / _n0 :
00403           0)
00404          - drho * ctheta) / _k0;
00405     k = _k0 * (t != 0 ? t * hyp(_fm * tphi) / _a : 1);
00406     y *= _sign;
00407     gamma = _sign * theta / Math::degree<real>();
00408   }
00409 
00410   void AlbersEqualArea::Reverse(real lon0, real x, real y,
00411                                 real& lat, real& lon,
00412                                 real& gamma, real& k)
00413     const throw() {
00414     y *= _sign;
00415     real
00416       nx = _k0 * _n0 * x, ny = _k0 * _n0 * y, y1 =  _nrho0 - ny,
00417       den = Math::hypot(nx, y1) + _nrho0, // 0 implies origin with polar aspect
00418       drho = den != 0 ? (_k0*x*nx - 2*_k0*y*_nrho0 + _k0*y*ny) / den : 0,
00419       // dsxia = scxi0 * dsxi
00420       dsxia = - _scxi0 * (2 * _nrho0 + _n0 * drho) * drho / (sq(_a) * _qZ),
00421       txi = (_txi0 + dsxia) / sqrt(max(1 - dsxia * (2*_txi0 + dsxia), epsx2)),
00422       tphi = tphif(txi),
00423       phi = _sign * atan(tphi),
00424       theta = atan2(nx, y1),
00425       lam = _n0 != 0 ? theta / (_k2 * _n0) : x / (y1 * _k0);
00426     gamma = _sign * theta / Math::degree<real>();
00427     lat = phi / Math::degree<real>();
00428     lon = lam / Math::degree<real>();
00429     k = _k0 * (den != 0 ? (_nrho0 + _n0 * drho) * hyp(_fm * tphi) / _a : 1);
00430   }
00431 
00432   void AlbersEqualArea::SetScale(real lat, real k) {
00433     if (!(k > 0))
00434       throw GeographicErr("Scale is not positive");
00435     if (!(abs(lat) < 90))
00436       throw GeographicErr("Latitude for SetScale not in (-90, 90)");
00437     real x, y, gamma, kold;
00438     Forward(0, lat, 0, x, y, gamma, kold);
00439     k /= kold;
00440     _k0 *= k;
00441     _k2 = sq(_k0);
00442   }
00443 
00444 } // namespace GeographicLib