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 }