TransverseMercator.cpp
Go to the documentation of this file.
00001 /**
00002  * \file TransverseMercator.cpp
00003  * \brief Implementation for GeographicLib::TransverseMercator class
00004  *
00005  * Copyright (c) Charles Karney (2008) <charles@karney.com>
00006  * and licensed under the LGPL.
00007  *
00008  * This implementation follows closely
00009  * <a href="http://www.jhs-suositukset.fi/suomi/jhs154"> JHS 154, ETRS89 -
00010  * j&auml;rjestelm&auml;&auml;n liittyv&auml;t karttaprojektiot,
00011  * tasokoordinaatistot ja karttalehtijako</a> (Map projections, plane
00012  * coordinates, and map sheet index for ETRS89), published by JUHTA, Finnish
00013  * Geodetic Institute, and the National Land Survey of Finland (2006).
00014  *
00015  * The relevant section is available as the 2008 PDF file
00016  * http://docs.jhs-suositukset.fi/jhs-suositukset/JHS154/JHS154_liite1.pdf
00017  *
00018  * This is a straight transcription of the formulas in this paper with the
00019  * following exceptions:
00020  *  - use of 6th order series instead of 4th order series.  This reduces the
00021  *    error to about 5nm for the UTM range of coordinates (instead of 200nm),
00022  *    with a speed penalty of only 1%;
00023  *  - use Newton's method instead of plain iteration to solve for latitude in
00024  *    terms of isometric latitude in the Reverse method;
00025  *  - use of Horner's representation for evaluating polynomials and Clenshaw's
00026  *    method for summing trigonometric series;
00027  *  - several modifications of the formulas to improve the numerical accuracy;
00028  *  - evaluating the convergence and scale using the expression for the
00029  *    projection or its inverse.
00030  *
00031  * If the preprocessor variable TM_TX_MAXPOW is set to an integer between 4 and
00032  * 8, then this specifies the order of the series used for the forward and
00033  * reverse transformations.  The default value is 6.  (The series accurate to
00034  * 12th order is given in \ref tmseries.)
00035  *
00036  * Other equivalent implementations are given in
00037  *  - http://www.ign.fr/telechargement/MPro/geodesie/CIRCE/NTG_76.pdf
00038  *  - http://www.lantmateriet.se/upload/filer/kartor/geodesi_gps_och_detaljmatning/geodesi/Formelsamling/Gauss_Conformal_Projection.pdf
00039  **********************************************************************/
00040 
00041 #include "GeographicLib/TransverseMercator.hpp"
00042 #include "GeographicLib/Constants.hpp"
00043 #include <limits>
00044 
00045 namespace {
00046   char RCSID[] = "$Id: TransverseMercator.cpp 6568 2009-03-01 17:58:41Z ckarney $";
00047   char RCSID_H[] = TRANSVERSEMERCATOR_HPP;
00048 }
00049 
00050 namespace GeographicLib {
00051 
00052   using namespace std;
00053 
00054   const double TransverseMercator::tol =
00055     0.1*sqrt(numeric_limits<double>::epsilon());
00056 
00057   TransverseMercator::TransverseMercator(double a, double invf, double k0)
00058     throw()
00059     : _a(a)
00060     , _f(invf > 0 ? 1 / invf : 0)
00061     , _k0(k0)
00062     , _e2(_f * (2 - _f))
00063     , _e(sqrt(_e2))
00064     , _e2m(1 - _e2)
00065     , _n(_f / (2 - _f))
00066   {
00067 #if TM_TX_MAXPOW <= 4
00068     _b1 = 1/(1+_n)*(sq(_n)*(sq(_n)+16)+64)/64;
00069     _h[0] = _n*(_n*((555-4*_n)*_n-960)+720)/1440;
00070     _h[1] = sq(_n)*((96-437*_n)*_n+30)/1440;
00071     _h[2] = (119-148*_n)*sq(_n)*_n/3360;
00072     _h[3] = 4397*sq(sq(_n))/161280;
00073     _hp[0] = _n*(_n*(_n*(164*_n+225)-480)+360)/720;
00074     _hp[1] = sq(_n)*(_n*(557*_n-864)+390)/1440;
00075     _hp[2] = (427-1236*_n)*sq(_n)*_n/1680;
00076     _hp[3] = 49561*sq(sq(_n))/161280;
00077 #elif TM_TX_MAXPOW == 5
00078     _b1 = 1/(1+_n)*(sq(_n)*(sq(_n)+16)+64)/64;
00079     _h[0] = _n*(_n*(_n*((-3645*_n-64)*_n+8880)-15360)+11520)/23040;
00080     _h[1] = sq(_n)*(_n*(_n*(4416*_n-3059)+672)+210)/10080;
00081     _h[2] = sq(_n)*_n*((-627*_n-592)*_n+476)/13440;
00082     _h[3] = (4397-3520*_n)*sq(sq(_n))/161280;
00083     _h[4] = 4583*sq(sq(_n))*_n/161280;
00084     _hp[0] = _n*(_n*(_n*((328-635*_n)*_n+450)-960)+720)/1440;
00085     _hp[1] = sq(_n)*(_n*(_n*(4496*_n+3899)-6048)+2730)/10080;
00086     _hp[2] = sq(_n)*_n*(_n*(15061*_n-19776)+6832)/26880;
00087     _hp[3] = (49561-171840*_n)*sq(sq(_n))/161280;
00088     _hp[4] = 34729*sq(sq(_n))*_n/80640;
00089 #elif TM_TX_MAXPOW == 6
00090     _b1 = 1/(1+_n)*(sq(_n)*(sq(_n)*(sq(_n)+4)+64)+256)/256;
00091     _h[0] = _n*(_n*(_n*(_n*(_n*(384796*_n-382725)-6720)+932400)-1612800)+
00092                1209600)/2419200;
00093     _h[1] = sq(_n)*(_n*(_n*((1695744-1118711*_n)*_n-1174656)+258048)+80640)/
00094       3870720;
00095     _h[2] = sq(_n)*_n*(_n*(_n*(22276*_n-16929)-15984)+12852)/362880;
00096     _h[3] = sq(sq(_n))*((-830251*_n-158400)*_n+197865)/7257600;
00097     _h[4] = (453717-435388*_n)*sq(sq(_n))*_n/15966720;
00098     _h[5] = 20648693*sq(sq(_n))*sq(_n)/638668800;
00099     _hp[0] = _n*(_n*(_n*(_n*(_n*(31564*_n-66675)+34440)+47250)-100800)+75600)/
00100       151200;
00101     _hp[1] = sq(_n)*(_n*(_n*((863232-1983433*_n)*_n+748608)-1161216)+524160)/
00102       1935360;
00103     _hp[2] = sq(_n)*_n*(_n*(_n*(670412*_n+406647)-533952)+184464)/725760;
00104     _hp[3] = sq(sq(_n))*(_n*(6601661*_n-7732800)+2230245)/7257600;
00105     _hp[4] = (3438171-13675556*_n)*sq(sq(_n))*_n/7983360;
00106     _hp[5] = 212378941*sq(sq(_n))*sq(_n)/319334400;
00107 #elif TM_TX_MAXPOW == 7
00108     _b1 = 1/(1+_n)*(sq(_n)*(sq(_n)*(sq(_n)+4)+64)+256)/256;
00109     _h[0] = _n*(_n*(_n*(_n*(_n*((6156736-5406467*_n)*_n-6123600)-107520)+
00110                        14918400)-25804800)+19353600)/38707200;
00111     _h[1] = sq(_n)*(_n*(_n*(_n*(_n*(829456*_n-5593555)+8478720)-5873280)+
00112                       1290240)+403200)/19353600;
00113     _h[2] = sq(_n)*_n*(_n*(_n*(_n*(9261899*_n+3564160)-2708640)-2557440)+
00114                      2056320)/58060800;
00115     _h[3] = sq(sq(_n))*(_n*(_n*(14928352*_n-9132761)-1742400)+2176515)/
00116       79833600;
00117     _h[4] = sq(sq(_n))*_n*((-8005831*_n-1741552)*_n+1814868)/63866880;
00118     _h[5] = (268433009-261810608*_n)*sq(sq(_n))*sq(_n)/8302694400.;
00119     _h[6] = 219941297*sq(sq(_n))*sq(_n)*_n/5535129600.;
00120     _hp[0] = _n*(_n*(_n*(_n*(_n*(_n*(1804025*_n+2020096)-4267200)+2204160)+
00121                        3024000)-6451200)+4838400)/9676800;
00122     _hp[1] = sq(_n)*(_n*(_n*(_n*(_n*(4626384*_n-9917165)+4316160)+3743040)-
00123                       5806080)+2620800)/9676800;
00124     _hp[2] = sq(_n)*_n*(_n*(_n*((26816480-67102379*_n)*_n+16265880)-21358080)+
00125                      7378560)/29030400;
00126     _hp[3] = sq(sq(_n))*(_n*(_n*(155912000*_n+72618271)-85060800)+24532695)/
00127       79833600;
00128     _hp[4] = sq(sq(_n))*_n*(_n*(102508609*_n-109404448)+27505368)/63866880;
00129     _hp[5] = (2760926233.-12282192400.*_n)*sq(sq(_n))*sq(_n)/4151347200.;
00130     _hp[6] = 1522256789.*sq(sq(_n))*sq(_n)*_n/1383782400.;
00131 #elif TM_TX_MAXPOW >= 8
00132     _b1 = 1/(1+_n)*(sq(_n)*(sq(_n)*(sq(_n)*(25*sq(_n)+64)+256)+4096)+16384)/
00133       16384;
00134     _h[0] = _n*(_n*(_n*(_n*(_n*(_n*(_n*(31777436*_n-37845269)+43097152)-
00135                                42865200)-752640)+104428800)-180633600)+
00136                135475200)/270950400;
00137     _h[1] = sq(_n)*(_n*(_n*(_n*(_n*(_n*(24749483*_n+14930208)-100683990)+
00138                               152616960)-105719040)+23224320)+7257600)/
00139       348364800;
00140     _h[2] = sq(_n)*_n*(_n*(_n*(_n*((101880889-232468668*_n)*_n+39205760)-
00141                              29795040)-28131840)+22619520)/638668800;
00142     _h[3] = sq(sq(_n))*(_n*(_n*(_n*(324154477*_n+1433121792.)-876745056)-
00143                             167270400)+208945440)/7664025600.;
00144     _h[4] = sq(sq(_n))*_n*(_n*(_n*(457888660*_n-312227409)-67920528)+
00145                            70779852)/2490808320.;
00146     _h[5] = sq(sq(_n))*sq(_n)*((-19841813847.*_n-3665348512.)*_n+3758062126.)/
00147       116237721600.;
00148     _h[6] = (1979471673.-1989295244.*_n)*sq(sq(_n))*sq(_n)*_n/49816166400.;
00149     _h[7] = 191773887257.*sq(sq(sq(_n)))/3719607091200.;
00150     _hp[0] = _n*(_n*(_n*(_n*(_n*(_n*((37884525-75900428*_n)*_n+42422016)-
00151                                89611200)+46287360)+63504000)-135475200)+
00152                101606400)/203212800;
00153     _hp[1] = sq(_n)*(_n*(_n*(_n*(_n*(_n*(148003883*_n+83274912)-178508970)+
00154                               77690880)+67374720)-104509440)+47174400)/
00155       174182400;
00156     _hp[2] = sq(_n)*_n*(_n*(_n*(_n*(_n*(318729724*_n-738126169)+294981280)+
00157                              178924680)-234938880)+81164160)/319334400;
00158     _hp[3] = sq(sq(_n))*(_n*(_n*((14967552000.-40176129013.*_n)*_n+
00159                                 6971354016.)-8165836800.)+2355138720.)/
00160       7664025600.;
00161     _hp[4] = sq(sq(_n))*_n*(_n*(_n*(10421654396.*_n+3997835751.)-4266773472.)+
00162                            1072709352.)/2490808320.;
00163     _hp[5] = sq(sq(_n))*sq(_n)*(_n*(175214326799.*_n-171950693600.)+
00164                               38652967262.)/58118860800.;
00165     _hp[6] = (13700311101.-67039739596.*_n)*sq(sq(_n))*sq(_n)*_n/12454041600.;
00166     _hp[7] = 1424729850961.*sq(sq(sq(_n)))/743921418240.;
00167 #endif
00168     // _a1 is the equivalent radius for computing the circumference of
00169     // ellipse.  Relative error is f^6/16384 = 8.8e-20 for WGS84.
00170     _a1 = _b1 * _a;
00171   }
00172 
00173   const TransverseMercator
00174   TransverseMercator::UTM(Constants::WGS84_a(), Constants::WGS84_invf(),
00175                           Constants::UTM_k0());
00176 
00177   void TransverseMercator::Forward(double lon0, double lat, double lon,
00178                                    double& x, double& y,
00179                                    double& gamma, double& k) const throw() {
00180     // Avoid losing a bit of accuracy in lon (assuming lon0 is an integer)
00181     if (lon - lon0 > 180)
00182       lon -= lon0 - 360;
00183     else if (lon - lon0 <= -180)
00184       lon -= lon0 + 360;
00185     else
00186       lon -= lon0;
00187     // Now lon in (-180, 180]
00188     // Explicitly enforce the parity
00189     int
00190       latsign = lat < 0 ? -1 : 1,
00191       lonsign = lon < 0 ? -1 : 1;
00192     lon *= lonsign;
00193     lat *= latsign;
00194     bool backside = lon > 90;
00195     if (backside) {
00196       if (lat == 0)
00197         latsign = -1;
00198       lon = 180 - lon;
00199     }
00200     double
00201       phi = lat * Constants::degree(),
00202       lam = lon * Constants::degree();
00203     // q is isometric latitude
00204     // JHS 154 has
00205     //
00206     //   beta = atan(sinh(q)) = conformal latitude
00207     //   [xi', eta'] = Gauss-Schreiber TM coordinates
00208     //   eta' = atanh(cos(beta) * sin(lam))
00209     //   xi' = asin(sin(beta)*cosh(eta')
00210     //
00211     // We use
00212     //
00213     //   tan(beta) = sinh(q)
00214     //   sin(beta) = tanh(q)
00215     //   cos(beta) = sech(q)
00216     //   denom^2    = 1-cos(beta)^2*sin(lam)^2 = 1-sech(q)^2*sin(lam)^2
00217     //   sin(xip)   = sin(beta)/denom          = tanh(q)/denom
00218     //   cos(xip)   = cos(beta)*cos(lam)/denom = sech(q)*cos(lam)/denom
00219     //   cosh(etap) = 1/denom                  = 1/denom
00220     //   sinh(etap) = cos(beta)*sin(lam)/denom = sech(q)*sin(lam)/denom
00221     //
00222     // to eliminate beta and derive more stable expressions for xi',eta'
00223     double etap, xip;
00224     if (lat < 90) {
00225       double
00226         qp = asinh(tan(phi)),
00227         qpp = atanh(_e * sin(phi)),
00228         q = qp - _e * qpp;
00229       xip = atan2(sinh(q), cos(lam));
00230       etap = atanh(sin(lam) / cosh(q));
00231       // convergence and scale for Gauss-Schreiber TM (xip, etap) -- gamma0 =
00232       // atan(tan(xip) * tanh(etap)) = atan(tan(lam) * sin(beta))
00233       gamma = atan(tan(lam) * tanh(q));
00234       // k0 = sqrt(1 - _e2 * sin(phi)^2) * (cos(beta) / cos(phi)) * cosh(etap)
00235       // Note 1/cos(phi) = cosh(qp);
00236       // and cos(beta) * cosh(etap) = 1/hypot(sinh(q), cos(lam))
00237       k = sqrt(_e2m + _e2 * sq(cos(phi))) * cosh(qp) / hypot(sinh(q), cos(lam));
00238     } else {
00239       xip = Constants::pi()/2;
00240       etap = 0;
00241       gamma = lam;
00242       // See, for example, Lee (1976), p 100.
00243       k = sqrt( pow(1 + _e, 1 + _e) * pow(1 - _e, 1 - _e) );
00244     }
00245     // {xi',eta'} is {northing,easting} for Gauss-Schreiber transverse mercator
00246     // (for eta' = 0, xi' = bet). {xi,eta} is {northing,easting} for transverse
00247     // mercator with constant scale on the central meridian (for eta = 0, xip =
00248     // rectifying latitude).  Define
00249     //
00250     //   zeta = xi + i*eta
00251     //   zeta' = xi' + i*eta'
00252     //
00253     // The conversion from conformal to rectifying latitude can be expresses as
00254     // a series in _n:
00255     //
00256     //   zeta = zeta' + sum(h[j-1]' * sin(2 * j * zeta'), j = 1..maxpow)
00257     //
00258     // where h[j]' = O(_n^j).  The reversion of this series gives
00259     //
00260     //   zeta' = zeta - sum(h[j-1] * sin(2 * j * zeta), j = 1..maxpow)
00261     //
00262     // which is used in Reverse.
00263     //
00264     // Evaluate sums via Clenshaw method.  See
00265     //    http://mathworld.wolfram.com/ClenshawRecurrenceFormula.html
00266     //
00267     // Let
00268     //
00269     //    S = sum(c[k] * F[k](x), k = 0..N)
00270     //    F[n+1](x) = alpha(n,x) * F[n](x) + beta(n,x) * F[n-1](x)
00271     //
00272     // Evaluate S with
00273     //
00274     //    y[N+2] = y[N+1] = 0
00275     //    y[k] = alpha(k,x) * y[k+1] + beta(k+1,x) * y[k+2] + c[k]
00276     //    S = c[0] * F[0](x) + y[1] * F[1](x) + beta(1,x) * F[0](x) * y[2]
00277     //
00278     // Here we have
00279     //
00280     //    x = 2 * zeta'
00281     //    F[n](x) = sin(n * x)
00282     //    a(n, x) = 2 * cos(x)
00283     //    b(n, x) = -1
00284     //    [ sin(A+B) - 2*cos(B)*sin(A) + sin(A-B) = 0, A = n*x, B = x ]
00285     //    N = maxpow
00286     //    c[k] = _hp[k-1]
00287     //    S = y[1] * sin(x)
00288     //
00289     // For the derivative we have
00290     //
00291     //    x = 2 * zeta'
00292     //    F[n](x) = cos(n * x)
00293     //    a(n, x) = 2 * cos(x)
00294     //    b(n, x) = -1
00295     //    [ cos(A+B) - 2*cos(B)*cos(A) + cos(A-B) = 0, A = n*x, B = x ]
00296     //    c[0] = 1; c[k] = 2*k*_hp[k-1]
00297     //    S = (c[0] - y[2]) + y[1] * cos(x)
00298     double
00299       c0 = cos(2 * xip), ch0 = cosh(2 * etap),
00300       s0 = sin(2 * xip), sh0 = sinh(2 * etap),
00301       ar = 2 * c0 * ch0, ai = -2 * s0 * sh0; // 2 * cos(2*zeta')
00302     double                      // Accumulators for zeta
00303       xi0 = _hp[maxpow - 1], eta0 = 0,
00304       xi1 = 0, eta1 = 0,
00305       xi2, eta2;
00306     double                      // Accumulators for dzeta/dzeta'
00307       yr0 = 2 * maxpow * _hp[maxpow - 1], yi0 = 0,
00308       yr1 = 0, yi1 = 0,
00309       yr2, yi2;
00310     for (int j = maxpow; --j;) { // j = maxpow-1 .. 1
00311       xi2 = xi1; eta2 = eta1; yr2 = yr1; yi2 = yi1;
00312       xi1 = xi0; eta1 = eta0; yr1 = yr0; yi1 = yi0;
00313       xi0  = ar * xi1 - ai * eta1 - xi2 + _hp[j - 1];
00314       eta0 = ai * xi1 + ar * eta1 - eta2;
00315       yr0 = ar * yr1 - ai * yi1 - yr2 + 2 * j * _hp[j - 1];
00316       yi0 = ai * yr1 + ar * yi1 - yi2;
00317     }
00318     ar /= 2; ai /= 2;           // cos(2*zeta')
00319     yr2 = 1 - yr1 + ar * yr0 - ai * yi0;
00320     yi2 =   - yi1 + ai * yr0 + ar * yi0;
00321     ar = s0 * ch0; ai = c0 * sh0; // sin(2*zeta')
00322     double
00323       xi  = xip  + ar * xi0 - ai * eta0,
00324       eta = etap + ai * xi0 + ar * eta0;
00325     // Fold in change in convergence and scale for Gauss-Schreiber TM to
00326     // Gauss-Krueger TM.
00327     gamma -= atan2(yi2, yr2);
00328     k *= _b1 * hypot(yr2, yi2);
00329     gamma /= Constants::degree();
00330     y = _a1 * _k0 * (backside ? Constants::pi() - xi : xi) * latsign;
00331     x = _a1 * _k0 * eta * lonsign;
00332     if (backside)
00333       gamma = 180 - gamma;
00334     gamma *= latsign * lonsign;
00335     k *= _k0;
00336   }
00337 
00338   void TransverseMercator::Reverse(double lon0, double x, double y,
00339                                    double& lat, double& lon,
00340                                    double& gamma, double& k) const throw() {
00341     // This undoes the steps in Forward.  The wrinkles are: (1) Use of the
00342     // reverted series to express zeta' in terms of zeta. (2) Newton's method
00343     // to solve for phi in terms of q.
00344     double
00345       xi = y / (_a1 * _k0),
00346       eta = x / (_a1 * _k0);
00347     // Explicitly enforce the parity
00348     int
00349       xisign = xi < 0 ? -1 : 1,
00350       etasign = eta < 0 ? -1 : 1;
00351     xi *= xisign;
00352     eta *= etasign;
00353     bool backside = xi > Constants::pi()/2;
00354     if (backside)
00355       xi = Constants::pi() - xi;
00356     double
00357       c0 = cos(2 * xi), ch0 = cosh(2 * eta),
00358       s0 = sin(2 * xi), sh0 = sinh(2 * eta),
00359       ar = 2 * c0 * ch0, ai = -2 * s0 * sh0; // 2 * cos(2*zeta)
00360     double                      // Accumulators for zeta'
00361       xip0 = -_h[maxpow - 1], etap0 = 0,
00362       xip1 = 0, etap1 = 0,
00363       xip2, etap2;
00364     double                      // Accumulators for dzeta'/dzeta
00365       yr0 = - 2 * maxpow * _h[maxpow - 1], yi0 = 0,
00366       yr1 = 0, yi1 = 0,
00367       yr2, yi2;
00368     for (int j = maxpow; --j;) { // j = maxpow-1 .. 1
00369       xip2 = xip1; etap2 = etap1; yr2 = yr1; yi2 = yi1;
00370       xip1 = xip0; etap1 = etap0; yr1 = yr0; yi1 = yi0;
00371       xip0  = ar * xip1 - ai * etap1 - xip2 - _h[j - 1];
00372       etap0 = ai * xip1 + ar * etap1 - etap2;
00373       yr0 = ar * yr1 - ai * yi1 - yr2 - 2 * j * _h[j - 1];
00374       yi0 = ai * yr1 + ar * yi1 - yi2;
00375     }
00376     ar /= 2; ai /= 2;           // cos(2*zeta')
00377     yr2 = 1 - yr1 + ar * yr0 - ai * yi0;
00378     yi2 =   - yi1 + ai * yr0 + ar * yi0;
00379     ar = s0 * ch0; ai = c0 * sh0; // sin(2*zeta)
00380     double
00381       xip  = xi  + ar * xip0 - ai * etap0,
00382       etap = eta + ai * xip0 + ar * etap0;
00383     // Convergence and scale for Gauss-Schreiber TM to Gauss-Krueger TM.
00384     gamma = atan2(yi2, yr2);
00385     k = _b1 / hypot(yr2, yi2);
00386     // JHS 154 has
00387     //
00388     //   beta = asin(sin(xip) / cosh(etap))
00389     //   lam = asin(tanh(etap) / cos(beta)
00390     //   q = asinh(tan(beta))
00391     //
00392     // the following eliminates beta and is more stable
00393     double lam, phi;
00394     double
00395       s = sinh(etap),
00396       c = cos(xip),
00397       r = hypot(s, c);
00398     if (r > 0) {
00399       lam = atan2(s, c);
00400       // Use Newton's method to solve
00401       // q = qp - e * atanh(e * tanh(qp))
00402       // for qp = asinh(tan(phi))
00403       double
00404         q = asinh(sin(xip)/r),
00405         qp = q;
00406       // min iterations = 1, max iterations = 3; mean = 2.8
00407       for (int i = 0; i < numit; ++i) {
00408         double
00409           t = tanh(qp),
00410           dqp = -(qp - _e * atanh(_e * t) - q) * (1 - _e2 * sq(t)) / _e2m;
00411         qp += dqp;
00412         if (abs(dqp) < tol)
00413           break;
00414       }
00415       phi = atan(sinh(qp));
00416       gamma += atan(tan(xip) * tanh(etap));
00417       // Note cos(beta) * cosh(etap) = r
00418       k *= sqrt(_e2m + _e2 * sq(cos(phi))) * cosh(qp) * r;
00419     } else {
00420       phi = Constants::pi()/2;
00421       lam = 0;
00422       k *= sqrt( pow(1 + _e, 1 + _e) * pow(1 - _e, 1 - _e) );
00423     }
00424     lat = phi / Constants::degree() * xisign;
00425     lon = lam / Constants::degree();
00426     if (backside)
00427       lon = 180 - lon;
00428     lon *= etasign;
00429     // Avoid losing a bit of accuracy in lon (assuming lon0 is an integer)
00430     if (lon + lon0 >= 180)
00431       lon += lon0 - 360;
00432     else if (lon + lon0 < -180)
00433       lon += lon0 + 360;
00434     else
00435       lon += lon0;
00436     gamma /= Constants::degree();
00437     if (backside)
00438       gamma = 180 - gamma;
00439     gamma *= xisign * etasign;
00440     k *= _k0;
00441   }
00442 
00443 } // namespace GeographicLib