00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 
00036 
00037 
00038 
00039 
00040 
00041 
00042 #include "GeographicLib/TransverseMercatorExact.hpp"
00043 
00044 #define GEOGRAPHICLIB_TRANSVERSEMERCATOREXACT_CPP "$Id: TransverseMercatorExact.cpp 6937 2011-02-01 20:17:13Z karney $"
00045 
00046 RCSID_DECL(GEOGRAPHICLIB_TRANSVERSEMERCATOREXACT_CPP)
00047 RCSID_DECL(GEOGRAPHICLIB_TRANSVERSEMERCATOREXACT_HPP)
00048 
00049 namespace GeographicLib {
00050 
00051   using namespace std;
00052 
00053   const Math::real TransverseMercatorExact::tol =
00054     numeric_limits<real>::epsilon();
00055   const Math::real TransverseMercatorExact::tol1 = real(0.1) * sqrt(tol);
00056   const Math::real TransverseMercatorExact::tol2 = real(0.1) * tol;
00057   const Math::real TransverseMercatorExact::taytol = pow(tol, real(0.6));
00058   
00059   const Math::real TransverseMercatorExact::overflow = 1 / sq(tol);
00060 
00061   TransverseMercatorExact::TransverseMercatorExact(real a, real r, real k0,
00062                                                    bool extendp)
00063     : _a(a)
00064     , _r(r)
00065     , _f(1 / _r)
00066     , _k0(k0)
00067     , _mu(_f * (2 - _f))        
00068     , _mv(1 - _mu)              
00069     , _e(sqrt(_mu))
00070     , _ep2(_mu / _mv)           
00071     , _extendp(extendp)
00072     , _Eu(_mu)
00073     , _Ev(_mv)
00074   {
00075     if (!(_a > 0))
00076       throw GeographicErr("Major radius is not positive");
00077     if (!(_r > 0))
00078       throw GeographicErr("Inverse flattening is not positive");
00079     if (!(_f < 1))
00080       throw GeographicErr("Minor radius is not positive");
00081     if (!(_k0 > 0))
00082       throw GeographicErr("Scale is not positive");
00083   }
00084 
00085   const TransverseMercatorExact
00086   TransverseMercatorExact::UTM(Constants::WGS84_a<real>(),
00087                                Constants::WGS84_r<real>(),
00088                                Constants::UTM_k0<real>());
00089 
00090   
00091   Math::real TransverseMercatorExact::taup(real tau) const throw() {
00092     real
00093       tau1 = Math::hypot(real(1), tau),
00094       sig = sinh( _e * Math::atanh(_e * tau / tau1) );
00095     return Math::hypot(real(1), sig) * tau - sig * tau1;
00096   }
00097 
00098   Math::real TransverseMercatorExact::taupinv(real taup) const throw() {
00099     real
00100       tau = taup,
00101       stol = tol * max(real(1), abs(taup));
00102     for (int i = 0; i < numit; ++i) {
00103       real
00104         tau1 = Math::hypot(real(1), tau),
00105         sig = sinh( _e * Math::atanh(_e * tau / tau1 ) ),
00106         taupa = Math::hypot(real(1), sig) * tau - sig * tau1,
00107         dtau = (taup - taupa) * (1 + _mv * sq(tau)) /
00108         ( _mv * tau1 * Math::hypot(real(1), taupa) );
00109       tau += dtau;
00110       if (!(abs(dtau) >= stol))
00111         break;
00112     }
00113     return tau;
00114   }
00115 
00116   void TransverseMercatorExact::zeta(real u, real snu, real cnu, real dnu,
00117                                      real v, real snv, real cnv, real dnv,
00118                                      real& taup, real& lam) const throw() {
00119     
00120     
00121     
00122     
00123     real
00124       d1 = sqrt(sq(cnu) + _mv * sq(snu * snv)),
00125       d2 = sqrt(_mu * sq(cnu) + _mv * sq(cnv)),
00126       t1 = (d1 ? snu * dnv / d1 : snu < 0 ? -overflow : overflow),
00127       t2 = (d2 ? sinh( _e * Math::asinh(_e * snu / d2) ) :
00128             snu < 0 ? -overflow : overflow);
00129     
00130     
00131     taup = t1 * Math::hypot(real(1), t2) - t2 * Math::hypot(real(1), t1);
00132     lam = (d1 != 0 && d2 != 0) ?
00133       atan2(dnu * snv, cnu * cnv) - _e * atan2(_e * cnu * snv, dnu * cnv) :
00134       0;
00135   }
00136 
00137   void TransverseMercatorExact::dwdzeta(real u, real snu, real cnu, real dnu,
00138                                         real v, real snv, real cnv, real dnv,
00139                                         real& du, real& dv) const throw() {
00140     
00141     
00142     real d = _mv * sq(sq(cnv) + _mu * sq(snu * snv));
00143     du =  cnu * dnu * dnv * (sq(cnv) - _mu * sq(snu * snv)) / d;
00144     dv = -snu * snv * cnv * (sq(dnu * dnv) + _mu * sq(cnu)) / d;
00145   }
00146 
00147   
00148   bool TransverseMercatorExact::zetainv0(real psi, real lam, real& u, real& v)
00149     const throw() {
00150     bool retval = false;
00151     if (psi < -_e * Math::pi<real>()/4 &&
00152         lam > (1 - 2 * _e) * Math::pi<real>()/2 &&
00153         psi < lam - (1 - _e) * Math::pi<real>()/2) {
00154       
00155       
00156       
00157       
00158       
00159       
00160       
00161       
00162       
00163       real
00164         psix = 1 - psi / _e,
00165         lamx = (Math::pi<real>()/2 - lam) / _e;
00166       u = Math::asinh(sin(lamx) / Math::hypot(cos(lamx), sinh(psix))) *
00167         (1 + _mu/2);
00168       v = atan2(cos(lamx), sinh(psix)) * (1 + _mu/2);
00169       u = _Eu.K() - u;
00170       v = _Ev.K() - v;
00171     } else if (psi < _e * Math::pi<real>()/2 &&
00172                lam > (1 - 2 * _e) * Math::pi<real>()/2) {
00173       
00174       
00175       
00176       
00177       
00178       
00179       
00180       
00181       
00182       
00183       
00184       real
00185         dlam = lam - (1 - _e) * Math::pi<real>()/2,
00186         rad = Math::hypot(psi, dlam),
00187         
00188         
00189         
00190         
00191         
00192         ang = atan2(dlam-psi, psi+dlam) - real(0.75) * Math::pi<real>();
00193       
00194       retval = rad < _e * taytol;
00195       rad = Math::cbrt(3 / (_mv * _e) * rad);
00196       ang /= 3;
00197       u = rad * cos(ang);
00198       v = rad * sin(ang) + _Ev.K();
00199     } else {
00200       
00201       
00202       
00203       v = Math::asinh(sin(lam) / Math::hypot(cos(lam), sinh(psi)));
00204       u = atan2(sinh(psi), cos(lam));
00205       
00206       u *= _Eu.K() / (Math::pi<real>()/2);
00207       v *= _Eu.K() / (Math::pi<real>()/2);
00208     }
00209     return retval;
00210   }
00211 
00212   
00213   void TransverseMercatorExact::zetainv(real taup, real lam, real& u, real& v)
00214     const throw()  {
00215     real
00216       psi = Math::asinh(taup),
00217       scal = 1/Math::hypot(real(1), taup);
00218     if (zetainv0(psi, lam, u, v))
00219       return;
00220     real stol2 = tol2 / sq(max(psi, real(1)));
00221     
00222     for (int i = 0, trip = 0; i < numit; ++i) {
00223       real snu, cnu, dnu, snv, cnv, dnv;
00224       _Eu.sncndn(u, snu, cnu, dnu);
00225       _Ev.sncndn(v, snv, cnv, dnv);
00226       real tau1, lam1, du1, dv1;
00227       zeta(u, snu, cnu, dnu, v, snv, cnv, dnv, tau1, lam1);
00228       dwdzeta(u, snu, cnu, dnu, v, snv, cnv, dnv, du1, dv1);
00229       tau1 -= taup;
00230       lam1 -= lam;
00231       tau1 *= scal;
00232       real
00233         delu = tau1 * du1 - lam1 * dv1,
00234         delv = tau1 * dv1 + lam1 * du1;
00235       u -= delu;
00236       v -= delv;
00237       if (trip)
00238         break;
00239       real delw2 = sq(delu) + sq(delv);
00240       if (!(delw2 >= stol2))
00241         ++trip;
00242     }
00243   }
00244 
00245   void TransverseMercatorExact::sigma(real u, real snu, real cnu, real dnu,
00246                                       real v, real snv, real cnv, real dnv,
00247                                       real& xi, real& eta) const throw() {
00248     
00249     
00250     real d = _mu * sq(cnu) + _mv * sq(cnv);
00251     xi = _Eu.E(snu, cnu, dnu) - _mu * snu * cnu * dnu / d;
00252     eta = v - _Ev.E(snv, cnv, dnv) + _mv * snv * cnv * dnv / d;
00253   }
00254 
00255   void TransverseMercatorExact::dwdsigma(real u, real snu, real cnu, real dnu,
00256                                          real v, real snv, real cnv, real dnv,
00257                                          real& du, real& dv) const throw() {
00258     
00259     
00260     real d = _mv * sq(sq(cnv) + _mu * sq(snu * snv));
00261     real
00262       dnr = dnu * cnv * dnv,
00263       dni = - _mu * snu * cnu * snv;
00264     du = (sq(dnr) - sq(dni)) / d;
00265     dv = 2 * dnr * dni / d;
00266   }
00267 
00268   
00269   bool TransverseMercatorExact::sigmainv0(real xi, real eta, real& u, real& v)
00270     const throw() {
00271     bool retval = false;
00272     if (eta > real(1.25) * _Ev.KE() ||
00273         (xi < -real(0.25) * _Eu.E() && xi < eta - _Ev.KE())) {
00274       
00275       
00276       
00277       
00278       real
00279         x = xi - _Eu.E(),
00280         y = eta - _Ev.KE(),
00281         r2 = sq(x) + sq(y);
00282       u = _Eu.K() + x/r2;
00283       v = _Ev.K() - y/r2;
00284     } else if ((eta > real(0.75) * _Ev.KE() && xi < real(0.25) * _Eu.E())
00285                || eta > _Ev.KE()) {
00286       
00287       
00288       
00289       
00290       
00291       
00292       
00293       
00294       
00295       
00296       
00297       
00298       real
00299         deta = eta - _Ev.KE(),
00300         rad = Math::hypot(xi, deta),
00301         
00302         
00303         ang = atan2(deta-xi, xi+deta) - real(0.75) * Math::pi<real>();
00304       
00305       retval = rad < 2 * taytol;
00306       rad = Math::cbrt(3 / _mv * rad);
00307       ang /= 3;
00308       u = rad * cos(ang);
00309       v = rad * sin(ang) + _Ev.K();
00310     } else {
00311       
00312       u = xi * _Eu.K()/_Eu.E();
00313       v = eta * _Eu.K()/_Eu.E();
00314     }
00315     return retval;
00316   }
00317 
00318   
00319   void TransverseMercatorExact::sigmainv(real xi, real eta, real& u, real& v)
00320     const throw() {
00321     if (sigmainv0(xi, eta, u, v))
00322       return;
00323     
00324     for (int i = 0, trip = 0; i < numit; ++i) {
00325       real snu, cnu, dnu, snv, cnv, dnv;
00326       _Eu.sncndn(u, snu, cnu, dnu);
00327       _Ev.sncndn(v, snv, cnv, dnv);
00328       real xi1, eta1, du1, dv1;
00329       sigma(u, snu, cnu, dnu, v, snv, cnv, dnv, xi1, eta1);
00330       dwdsigma(u, snu, cnu, dnu, v, snv, cnv, dnv, du1, dv1);
00331       xi1 -= xi;
00332       eta1 -= eta;
00333       real
00334         delu = xi1 * du1 - eta1 * dv1,
00335         delv = xi1 * dv1 + eta1 * du1;
00336       u -= delu;
00337       v -= delv;
00338       if (trip)
00339         break;
00340       real delw2 = sq(delu) + sq(delv);
00341       if (!(delw2 >= tol2))
00342         ++trip;
00343     }
00344   }
00345 
00346   void TransverseMercatorExact::Scale(real tau, real lam,
00347                                        real snu, real cnu, real dnu,
00348                                        real snv, real cnv, real dnv,
00349                                        real& gamma, real& k) const throw() {
00350     real sec2 = 1 + sq(tau);    
00351     
00352     
00353     gamma = atan2(_mv * snu * snv * cnv, cnu * dnu * dnv);
00354     
00355     
00356     
00357     
00358     
00359     
00360     
00361     
00362     
00363     
00364     
00365     
00366     
00367     k = sqrt(_mv + _mu / sec2) * sqrt(sec2) *
00368       sqrt( (_mv * sq(snv) + sq(cnu * dnv)) /
00369             (_mu * sq(cnu) + _mv * sq(cnv)) );
00370   }
00371 
00372   void TransverseMercatorExact::Forward(real lon0, real lat, real lon,
00373                                         real& x, real& y, real& gamma, real& k)
00374     const throw() {
00375     
00376     if (lon - lon0 > 180)
00377       lon -= lon0 + 360;
00378     else if (lon - lon0 <= -180)
00379       lon -= lon0 - 360;
00380     else
00381       lon -= lon0;
00382     
00383     
00384     int
00385       latsign = !_extendp && lat < 0 ? -1 : 1,
00386       lonsign = !_extendp && lon < 0 ? -1 : 1;
00387     lon *= lonsign;
00388     lat *= latsign;
00389     bool backside = !_extendp && lon > 90;
00390     if (backside) {
00391       if (lat == 0)
00392         latsign = -1;
00393       lon = 180 - lon;
00394     }
00395     real
00396       phi = lat * Math::degree<real>(),
00397       lam = lon * Math::degree<real>(),
00398       tau = tanx(phi);
00399 
00400     
00401     real u, v;
00402     if (lat == 90) {
00403       u = _Eu.K();
00404       v = 0;
00405     } else if (lat == 0 && lon == 90 * (1 - _e)) {
00406       u = 0;
00407       v = _Ev.K();
00408     } else
00409       zetainv(taup(tau), lam, u, v);
00410 
00411     real snu, cnu, dnu, snv, cnv, dnv;
00412     _Eu.sncndn(u, snu, cnu, dnu);
00413     _Ev.sncndn(v, snv, cnv, dnv);
00414 
00415     real xi, eta;
00416     sigma(u, snu, cnu, dnu, v, snv, cnv, dnv, xi, eta);
00417     if (backside)
00418       xi = 2 * _Eu.E() - xi;
00419     y = xi * _a * _k0 * latsign;
00420     x = eta * _a * _k0 * lonsign;
00421 
00422     
00423     zeta(u, snu, cnu, dnu, v, snv, cnv, dnv, tau, lam);
00424     tau=taupinv(tau);
00425     Scale(tau, lam, snu, cnu, dnu, snv, cnv, dnv, gamma, k);
00426     gamma /= Math::degree<real>();
00427     if (backside)
00428       gamma = 180 - gamma;
00429     gamma *= latsign * lonsign;
00430     k *= _k0;
00431   }
00432 
00433   void TransverseMercatorExact::Reverse(real lon0, real x, real y,
00434                                         real& lat, real& lon,
00435                                         real& gamma, real& k)
00436     const throw() {
00437     
00438     real
00439       xi = y / (_a * _k0),
00440       eta = x / (_a * _k0);
00441     
00442     int
00443       latsign = !_extendp && y < 0 ? -1 : 1,
00444       lonsign = !_extendp && x < 0 ? -1 : 1;
00445     xi *= latsign;
00446     eta *= lonsign;
00447     bool backside = !_extendp && xi > _Eu.E();
00448     if (backside)
00449       xi = 2 * _Eu.E()- xi;
00450 
00451     
00452     real u, v;
00453     if (xi == 0 && eta == _Ev.KE()) {
00454       u = 0;
00455       v = _Ev.K();
00456     } else
00457       sigmainv(xi, eta, u, v);
00458 
00459     real snu, cnu, dnu, snv, cnv, dnv;
00460     _Eu.sncndn(u, snu, cnu, dnu);
00461     _Ev.sncndn(v, snv, cnv, dnv);
00462     real phi, lam, tau;
00463     if (v != 0 || u != _Eu.K()) {
00464       zeta(u, snu, cnu, dnu, v, snv, cnv, dnv, tau, lam);
00465       tau = taupinv(tau);
00466       phi = atan(tau);
00467       lat = phi / Math::degree<real>();
00468       lon = lam / Math::degree<real>();
00469     } else {
00470       tau = overflow;
00471       phi = Math::pi<real>()/2;
00472       lat = 90;
00473       lon = lam = 0;
00474     }
00475     Scale(tau, lam, snu, cnu, dnu, snv, cnv, dnv, gamma, k);
00476     gamma /= Math::degree<real>();
00477     if (backside)
00478       lon = 180 - lon;
00479     lon *= lonsign;
00480     
00481     if (lon + lon0 >= 180)
00482       lon += lon0 - 360;
00483     else if (lon + lon0 < -180)
00484       lon += lon0 + 360;
00485     else
00486       lon += lon0;
00487     lat *= latsign;
00488     if (backside)
00489       y = 2 * _Eu.E() - y;
00490     y *= _a * _k0 * latsign;
00491     x *= _a * _k0 * lonsign;
00492     if (backside)
00493       gamma = 180 - gamma;
00494     gamma *= latsign * lonsign;
00495     k *= _k0;
00496   }
00497 
00498 }