Geodesic.cpp
Go to the documentation of this file.
00001 /**
00002  * \file Geodesic.cpp
00003  * \brief Implementation for GeographicLib::Geodesic class
00004  *
00005  * Copyright (c) Charles Karney (2008) <charles@karney.com>
00006  * and licensed under the LGPL.
00007  *
00008  * This is a reformulation of the geodesic problem.  The notation is as
00009  * follows:
00010  * - at a general point (no suffix or 1 or 2 as suffix)
00011  *   - phi = latitude
00012  *   - beta = latitude on auxilliary sphere
00013  *   - lambda = longitude on auxilliary sphere
00014  *   - chi = longitude
00015  *   - alpha = azimuth of great circle
00016  *   - sigma = arc length along greate circle
00017  *   - s = distance
00018  *   - tau = scaled distance (= sigma at multiples of pi/2)
00019  * - at previous northwards equator crossing
00020  *   - beta = phi = 0
00021  *   - lambda = chi = 0
00022  *   - alpha = alpha0
00023  *   - sigma = s = 0
00024  * - a 12 suffix means a difference, e.g., s12 = s2 - s1.
00025  * - s and c prefixes mean sin and cos
00026  **********************************************************************/
00027 
00028 #include "GeographicLib/Geodesic.hpp"
00029 #include "GeographicLib/Constants.hpp"
00030 #include <algorithm>
00031 #include <limits>
00032 
00033 namespace {
00034   char RCSID[] = "$Id: Geodesic.cpp 6568 2009-03-01 17:58:41Z ckarney $";
00035   char RCSID_H[] = GEODESIC_HPP;
00036 }
00037 
00038 namespace GeographicLib {
00039 
00040   using namespace std;
00041 
00042   // Underflow guard.  We require
00043   //   eps2 * epsilon() > 0
00044   //   eps2 + epsilon() == epsilon()
00045   const double Geodesic::eps2 = sqrt(numeric_limits<double>::min());
00046   const double Geodesic::tol = 100 * numeric_limits<double>::epsilon();
00047 
00048   Geodesic::Geodesic(double a, double invf) throw()
00049     : _a(a)
00050     , _f(invf > 0 ? 1 / invf : 0)
00051     , _f1(1 - _f)
00052     , _e2(_f * (2 - _f))
00053     , _ep2(_e2 / sq(_f1))       // e2 / (1 - e2)
00054     , _b(_a * _f1)
00055   {}
00056 
00057   const Geodesic Geodesic::WGS84(Constants::WGS84_a(),
00058                                  Constants::WGS84_invf());
00059 
00060   double Geodesic::SinSeries(double sinx, double cosx,
00061                              const double c[], int n) throw() {
00062     // Evaluate y = sum(c[i - 1] * sin(2 * i * x), i, 1, n) using Clenshaw
00063     // summation.  (Indices into c offset by 1.)
00064     double
00065       ar = 2 * (sq(cosx) - sq(sinx)), // 2 * cos(2 * x)
00066       y0 = c[n - 1], y1 = 0;          // Accumulators for sum
00067     for (int j = n; --j;) {           // j = n-1 .. 1
00068       double y2 = y1;
00069       y1 = y0; y0  = ar * y1 - y2 + c[j - 1];
00070     }
00071     return 2 * sinx * cosx * y0; // sin(2 * x) * y0
00072   }
00073 
00074   // The scale factor to convert tau to s / b
00075   double Geodesic::tauScale(double u2) throw() {
00076     return (u2 * (u2 * (u2 * (u2 * (u2 * (u2 * ((3624192 - 2760615 * u2) * u2
00077            - 4967424) + 7225344) - 11468800) + 20971520) - 50331648) +
00078            268435456) + 1073741824.0) / 1073741824.0;
00079   }
00080 
00081   // Coefficients of sine series to convert sigma to tau (a reversion of
00082   // tauCoeff).
00083   void Geodesic::tauCoeff(double u2, double c[]) throw() {
00084     double t = u2;
00085     c[0] = t * (u2 * (u2 * (u2 * (u2 * (u2 * (u2 * (428731 * u2 - 557402) +
00086            748544) - 1046528) + 1540096) - 2424832) + 4194304) - 8388608) / 67108864;
00087     t  *= u2;
00088     c[1] = t * (u2 * (u2 * (u2 * (u2 * ((480096 - 397645 * u2) * u2 -
00089            586016) + 720896) - 884736) + 1048576) - 1048576) / 268435456;
00090     t  *= u2;
00091     c[2] = t * (u2 * (u2 * (u2 * (u2 * (92295 * u2 - 100482) + 106880) -
00092            108288) + 98304) - 65536) / 201326592;
00093     t  *= u2;
00094     c[3] = t * (u2 * (u2 * ((128512 - 136971 * u2) * u2 - 111104) + 81920) -
00095            40960) / 1073741824.0;
00096     t  *= u2;
00097     c[4] = t * (u2 * (u2 * (9555 * u2 - 7210) + 4480) - 1792) / 335544320;
00098     t  *= u2;
00099     c[5] = t * ((672 - 1251 * u2) * u2 - 224) / 268435456;
00100     t  *= u2;
00101     c[6] = t * (231 * u2 - 66) / 469762048;
00102     t  *= u2;
00103     c[7] = -429 * t / 17179869184.0;
00104   }
00105 
00106   // Coefficients of sine series to convert tau to sigma (a reversion of
00107   // tauCoeff).
00108   void Geodesic::sigCoeff(double u2, double d[]) throw() {
00109     double t = u2;
00110     d[0] = t * (u2 * (u2 * (u2 * (u2 * (u2 * ((15107266 - 11062823 * u2) *
00111            u2 - 21467904) + 31944192) - 50135040) + 83755008) - 150994944) +
00112            301989888) / 2415919104.0;
00113     t  *= u2;
00114     d[1] = t * (u2 * (u2 * (u2 * (u2 * (u2 * (112064929 * u2 - 151134240) +
00115            206026080) - 281149440) + 376504320) - 471859200) + 471859200) / 24159191040.0;
00116     t  *= u2;
00117     d[2] = t * (u2 * (u2 * (u2 * ((2266302 - 1841049 * u2) * u2 - 2690560) +
00118            2976768) - 2850816) + 1900544) / 402653184;
00119     t  *= u2;
00120     d[3] = t * (u2 * (u2 * (u2 * (174543337 * u2 - 182201856) + 171121152) -
00121            132464640) + 66232320) / 48318382080.0;
00122     t  *= u2;
00123     d[4] = t * (u2 * ((5126290 - 6292895 * u2) * u2 - 3328320) + 1331328) / 3019898880.0;
00124     t  *= u2;
00125     d[5] = t * (u2 * (45781749 * u2 - 25590432) + 8530144) / 56371445760.0;
00126     t  *= u2;
00127     d[6] = (918970 - 3216395 * u2) * t / 16911433728.0;
00128     t  *= u2;
00129     d[7] = 109167851 * t / 5411658792960.0;
00130   }
00131 
00132   double Geodesic::dlamScale(double f, double mu) throw() {
00133     double g = (f * (f * (f * (f * (f * (f * (f * mu * (mu * (mu * (mu * (mu * (mu *
00134            (184041 * mu - 960498) + 2063880) - 2332400) + 1459200) - 479232) +
00135            65536) + mu * (mu * (mu * (mu * ((544320 - 121968 * mu) * mu -
00136            963200) + 844800) - 368640) + 65536)) + mu * (mu * (mu * (mu *
00137            (84672 * mu - 313600) + 435200) - 270336) + 65536)) + mu * (mu *
00138            ((184320 - 62720 * mu) * mu - 184320) + 65536)) + mu * (mu * (51200
00139            * mu - 110592) + 65536)) + (65536 - 49152 * mu) * mu) + 65536 * mu)
00140            - 262144) / 262144;
00141     return f * g;
00142   }
00143 
00144   double Geodesic::dlamScalemu(double f, double mu) throw() {
00145     double h = (f * (f * (f * (f * (f * (f * (mu * (mu * (mu * (mu * (mu *
00146            (1288287 * mu - 5762988) + 10319400) - 9329600) + 4377600) -
00147            958464) + 65536) + mu * (mu * (mu * ((2721600 - 731808 * mu) * mu -
00148            3852800) + 2534400) - 737280) + 65536) + mu * (mu * (mu * (423360 *
00149            mu - 1254400) + 1305600) - 540672) + 65536) + mu * ((552960 -
00150            250880 * mu) * mu - 368640) + 65536) + mu * (153600 * mu - 221184)
00151            + 65536) - 98304 * mu + 65536) + 65536) / 262144;
00152     return h * sq(f);
00153   }
00154 
00155   void Geodesic::dlamCoeff(double f, double mu, double e[]) throw() {
00156     double s = f * mu, t = s;
00157     e[0] = (f * (f * (f * (f * (f * (f * (f * (mu * (mu * (mu * (mu * (mu *
00158            ((30816920 - 5080225 * mu) * mu - 79065664) + 110840000) -
00159            91205632) + 43638784) - 11010048) + 1048576) + mu * (mu * (mu * (mu
00160            * (mu * (3213004 * mu - 17049088) + 37224832) - 42637312) +
00161            26828800) - 8650752) + 1048576) + mu * (mu * (mu * ((9543424 -
00162            2100608 * mu) * mu - 17160192) + 15196160) - 6553600) + 1048576) +
00163            mu * (mu * (mu * (1435648 * mu - 5419008) + 7626752) - 4718592) +
00164            1048576) + mu * ((3129344 - 1044480 * mu) * mu - 3145728) +
00165            1048576) + mu * (835584 * mu - 1835008) + 1048576) - 786432 * mu +
00166            1048576) + 1048576) * t / 8388608;
00167     t *= s;
00168     e[1] = (f * (f * (f * (f * (f * (f * (mu * (mu * (mu * (mu * (mu *
00169            (2092939 * mu - 12074982) + 29005488) - 37129344) + 26700800) -
00170            10207232) + 1605632) + mu * (mu * (mu * ((6316264 - 1270932 * mu) *
00171            mu - 12598272) + 12618240) - 6348800) + 1277952) + mu * (mu * (mu *
00172            (787136 * mu - 3268608) + 5143040) - 3645440) + 983040) + mu *
00173            ((1648640 - 498688 * mu) * mu - 1859584) + 720896) + mu * (323584 *
00174            mu - 778240) + 491520) - 212992 * mu + 294912) + 131072) * t / 8388608;
00175     t *= s;
00176     e[2] = (f * (f * (f * (f * (f * (mu * (mu * (mu * ((13101384 - 2474307 *
00177            mu) * mu - 28018000) + 30323072) - 16658432) + 3727360) + mu * (mu
00178            * (mu * (1386756 * mu - 6137024) + 10352064) - 7923712) + 2334720)
00179            + mu * ((2705152 - 770048 * mu) * mu - 3254272) + 1351680) + mu *
00180            (416256 * mu - 1052672) + 696320) - 208896 * mu + 294912) + 81920) * t / 25165824;
00181     t *= s;
00182     e[3] = (f * (f * (f * (f * (mu * (mu * (mu * (273437 * mu - 1265846) +
00183            2238200) - 1799088) + 557760) + mu * ((492328 - 134532 * mu) * mu -
00184            616928) + 266560) + mu * (62080 * mu - 162048) + 110080) - 25088 *
00185            mu + 35840) + 7168) * t / 8388608;
00186     t *= s;
00187     e[4] = (f * (f * (f * (mu * ((1333160 - 353765 * mu) * mu - 1718160) +
00188            761600) + mu * (142140 * mu - 379200) + 262080) - 48000 * mu +
00189            69120) + 10752) * t / 41943040;
00190     t *= s;
00191     e[5] = (f * (f * (mu * (39633 * mu - 107426) + 75152) - 11484 * mu +
00192            16632) + 2112) * t / 25165824;
00193     t *= s;
00194     e[6] = (f * (16016 - 11011 * mu) + 1716) * t / 58720256;
00195     t *= s;
00196     e[7] = 715 * t / 67108864;
00197   }
00198 
00199   void Geodesic::dlamCoeffmu(double f, double mu, double h[]) throw() {
00200     double s = f * mu, t = f;
00201     h[0] = (f * (f * (f * (f * (f * (f * (f * (mu * (mu * (mu * (mu * (mu *
00202            ((53929610 - 10160450 * mu) * mu - 118598496) + 138550000) -
00203            91205632) + 32729088) - 5505024) + 262144) + mu * (mu * (mu * (mu *
00204            (mu * (5622757 * mu - 25573632) + 46531040) - 42637312) + 20121600)
00205            - 4325376) + 262144) + mu * (mu * (mu * ((11929280 - 3150912 * mu)
00206            * mu - 17160192) + 11397120) - 3276800) + 262144) + mu * (mu * (mu
00207            * (1794560 * mu - 5419008) + 5720064) - 2359296) + 262144) + mu *
00208            ((2347008 - 1044480 * mu) * mu - 1572864) + 262144) + mu * (626688
00209            * mu - 917504) + 262144) - 393216 * mu + 262144) + 262144) * t / 2097152;
00210     t *= s;
00211     h[1] = (f * (f * (f * (f * (f * (f * (mu * (mu * (mu * (mu * (mu *
00212            (8371756 * mu - 42262437) + 87016464) - 92823360) + 53401600) -
00213            15310848) + 1605632) + mu * (mu * (mu * ((18948792 - 4448262 * mu)
00214            * mu - 31495680) + 25236480) - 9523200) + 1277952) + mu * (mu * (mu
00215            * (2361408 * mu - 8171520) + 10286080) - 5468160) + 983040) + mu *
00216            ((3297280 - 1246720 * mu) * mu - 2789376) + 720896) + mu * (647168
00217            * mu - 1167360) + 491520) - 319488 * mu + 294912) + 131072) * t / 4194304;
00218     t *= s;
00219     h[2] = (f * (f * (f * (f * (f * (mu * (mu * (mu * ((22927422 - 4948614 *
00220            mu) * mu - 42027000) + 37903840) - 16658432) + 2795520) + mu * (mu
00221            * (mu * (2426823 * mu - 9205536) + 12940080) - 7923712) + 1751040)
00222            + mu * ((3381440 - 1155072 * mu) * mu - 3254272) + 1013760) + mu *
00223            (520320 * mu - 1052672) + 522240) - 208896 * mu + 221184) + 61440) * t / 6291456;
00224     t *= s;
00225     h[3] = (f * (f * (f * (f * (mu * (mu * (mu * (1093748 * mu - 4430461) +
00226            6714600) - 4497720) + 1115520) + mu * ((1476984 - 470862 * mu) * mu
00227            - 1542320) + 533120) + mu * (186240 * mu - 405120) + 220160) -
00228            62720 * mu + 71680) + 14336) * t / 4194304;
00229     t *= s;
00230     h[4] = (f * (f * (f * (mu * ((466606 - 141506 * mu) * mu - 515448) +
00231            190400) + mu * (49749 * mu - 113760) + 65520) - 14400 * mu + 17280)
00232            + 2688) * t / 2097152;
00233     t *= s;
00234     h[5] = (f * (f * (mu * (158532 * mu - 375991) + 225456) - 40194 * mu +
00235            49896) + 6336) * t / 12582912;
00236     t *= s;
00237     h[6] = (f * (4004 - 3146 * mu) + 429) * t / 2097152;
00238     t *= s;
00239     h[7] = 715 * t / 8388608;
00240   }
00241 
00242   GeodesicLine Geodesic::Line(double lat1, double lon1, double azi1)
00243     const throw() {
00244     return GeodesicLine(*this, lat1, lon1, azi1);
00245   }
00246 
00247   void Geodesic::Direct(double lat1, double lon1, double azi1, double s12,
00248                         double& lat2, double& lon2, double& azi2)
00249     const throw() {
00250     GeodesicLine l(*this, lat1, lon1, azi1);
00251     l.Position(s12, lat2, lon2, azi2);
00252   }
00253 
00254   void Geodesic::Inverse(double lat1, double lon1, double lat2, double lon2,
00255                          double& s12, double& azi1, double& azi2)
00256     const throw() {
00257     lon1 = AngNormalize(lon1);
00258     double lon12 = AngNormalize(AngNormalize(lon2) - lon1);
00259     // If very close to being on the same meridian, then make it so.
00260     // Not sure this is necessary...
00261     lon12 = AngRound(lon12);
00262     // Make longitude difference positive.
00263     int lonsign = lon12 >= 0 ? 1 : -1;
00264     lon12 *= lonsign;
00265     // If really close to the equator, treat as on equator.
00266     lat1 = AngRound(lat1);
00267     lat2 = AngRound(lat2);
00268     // Swap points so that point with higher (abs) latitude is point 1
00269     int swapp = abs(lat1) >= abs(lat2) ? 1 : -1;
00270     if (swapp < 0) {
00271       lonsign *= -1;
00272       swap(lat1, lat2);
00273     }
00274     // Make lat1 <= 0
00275     int latsign = lat1 < 0 ? 1 : -1;
00276     lat1 *= latsign;
00277     lat2 *= latsign;
00278     // Now we have
00279     //
00280     //     0 <= lon12 <= 180
00281     //     -90 <= lat1 <= 0
00282     //     lat1 <= lat2 <= -lat1
00283     //
00284     // longsign, swapp, latsign register the transformation to bring the
00285     // coordinates to this canonical form.  In all cases, 1 means no change was
00286     // made.  We make these transformations so that there are few cases to
00287     // check, e.g., on verifying quadrants in atan2.  In addition, this
00288     // enforces some symmetries in the results returned.
00289 
00290     double phi, sbet1, cbet1, sbet2, cbet2, n1;
00291 
00292     phi = lat1 * Constants::degree();
00293     // Ensure cbet1 = +eps at poles
00294     sbet1 = _f1 * sin(phi);
00295     cbet1 = lat1 == -90 ? eps2 : cos(phi);
00296     // n = sqrt(1 - e2 * sq(sin(phi)))
00297     n1 = hypot(sbet1, cbet1);
00298     sbet1 /= n1; cbet1 /= n1;
00299 
00300     phi = lat2 * Constants::degree();
00301     // Ensure cbet2 = +eps at poles
00302     sbet2 = _f1 * sin(phi);
00303     cbet2 = abs(lat2) == 90 ? eps2 : cos(phi);
00304     SinCosNorm(sbet2, cbet2);
00305 
00306     double
00307       // How close to antipodal lat?
00308       sbet12 = sbet2 * cbet1 - cbet2 * sbet1, // bet2 - bet1 in [0, pi)
00309       // cbet12 = cbet2 * cbet1 + sbet2 * sbet1,
00310       sbet12a = sbet2 * cbet1 + cbet2 * sbet1, // bet2 + bet1 (-pi, 0]
00311       cbet12a = cbet2 * cbet1 - sbet2 * sbet1,
00312       chi12 = lon12 * Constants::degree(),
00313       cchi12 = cos(chi12),      // lon12 == 90 isn't interesting
00314       schi12 = lon12 == 180 ? 0 :sin(chi12);
00315 
00316     double calp1, salp1, calp2, salp2, c[maxpow];
00317     // Enumerate all the cases where the geodesic is a meridian.  This includes
00318     // coincident points.
00319     if (schi12 == 0 || lat1 == -90) {
00320       // Head to the target longitude
00321       calp1 = cchi12; salp1 = schi12;
00322       // At the target we're heading north
00323       calp2 = 1; salp2 = 0;
00324 
00325       double
00326         // tan(bet) = tan(sig) * cos(alp),
00327         ssig1 = sbet1, csig1 = calp1 * cbet1,
00328         ssig2 = sbet2, csig2 = calp2 * cbet2;
00329       SinCosNorm(ssig1, csig1);
00330       SinCosNorm(ssig2, csig2);
00331         
00332       // sig12 = sig2 - sig1
00333       double sig12 = atan2(max(csig1 * ssig2 - ssig1 * csig2, 0.0),
00334                            csig1 * csig2 + ssig1 * ssig2);
00335 
00336       tauCoeff(_ep2, c);
00337       s12 = _b * tauScale(_ep2) *
00338         (sig12 + (SinSeries(ssig2, csig2, c, maxpow) -
00339                   SinSeries(ssig1, csig1, c, maxpow)));
00340     } else if (sbet1 == 0 &&    // and sbet2 == 0
00341                // Mimic the way Chi12 works with calp1 = 0
00342                chi12 <= Constants::pi() - _f * Constants::pi()) {
00343       // Geodesic runs along equator
00344       calp1 = calp2 = 0; salp1 = salp2 = 1;
00345       s12 = _a * chi12;
00346     } else {
00347 
00348       // Now point1 and point2 belong within a hemisphere bounded by a line of
00349       // longitude (lon = lon12/2 +/- 90).
00350 
00351       double sig12, ssig1, csig1, ssig2, csig2, u2;
00352 
00353       // Figure a starting point for Newton's method
00354       double
00355         chicrita = -cbet1 * dlamScale(_f, sq(sbet1)) * Constants::pi(),
00356         chicrit = Constants::pi() - chicrita;
00357       if (chi12 == chicrit && cbet1 == cbet2 && sbet2 == -sbet1) {
00358         salp1 = 1; calp1 = 0;   // The singular point
00359         // This leads to
00360         //
00361         // sig12 = Constants::pi(); ssig1 = -1; salp2 = ssig2 = 1;
00362         // calp2 = csig1 = csig2 = 0; u2 = sq(sbet1) * _ep2;
00363         //
00364         // But we let Newton's method proceed so that we have fewer special
00365         // cases in the code.
00366       } else if (chi12 > chicrit && cbet12a > 0 && sbet12a > - chicrita) {
00367         salp1 = min(1.0, (Constants::pi() - chi12) / chicrita);
00368         calp1 = - sqrt(1 - sq(salp1));
00369       } else if (chi12 > Constants::pi() - 2 * chicrita &&
00370                  cbet12a > 0 && sbet12a > - 2 * chicrita) {
00371         salp1 = 1;
00372         calp1 = sbet2 <= 0 ? -eps2 : eps2;
00373       } else {
00374         salp1 = cbet2 * schi12;
00375         // calp1 = sbet2 * cbet1 - cbet2 * sbet1 * cchi12;
00376         // _f1/n1 gives ellipsoid correction for short distances.
00377         calp1 = cchi12 >= 0 ?
00378           sbet12 * _f1/n1 + cbet2 * sbet1 * sq(schi12) / (1 + cchi12) :
00379           sbet12a - cbet2 * sbet1 * sq(schi12) / (1 - cchi12);
00380         // N.B. ssig1 = hypot(salp1, calp1) (before normalization)
00381         SinCosNorm(salp1, calp1);
00382       }
00383 
00384       // Newton's method
00385       for (unsigned i = 0, trip = 0; i < 100; ++i) {
00386         double dv;
00387         double v = Chi12(sbet1, cbet1, sbet2, cbet2,
00388                          salp1, calp1, salp2, calp2,
00389                          sig12, ssig1, csig1, ssig2, csig2,
00390                          u2, trip < 1, dv, c) - chi12;
00391         if (v == 0 || !(trip < 1))
00392           break;
00393         double
00394           dalp1 = -v/dv,
00395           sdalp1 = sin(dalp1), cdalp1 = cos(dalp1),
00396           nsalp1 = salp1 * cdalp1 + calp1 * sdalp1;
00397         calp1 = calp1 * cdalp1 - salp1 * sdalp1;
00398         salp1 = max(0.0, nsalp1);
00399         SinCosNorm(salp1, calp1);
00400         if (abs(v) < tol) ++trip;
00401       }
00402         
00403       tauCoeff(u2, c);
00404       s12 =  _b * tauScale(u2) *
00405         (sig12 + (SinSeries(ssig2, csig2, c, maxpow) -
00406                   SinSeries(ssig1, csig1, c, maxpow)));
00407     }
00408 
00409     // Convert calp, salp to head accounting for
00410     // lonsign, swapp, latsign.  The minus signs up result in [-180, 180).
00411 
00412     if (swapp < 0) {
00413       swap(salp1, salp2);
00414       swap(calp1, calp2);
00415     }
00416 
00417     // minus signs give range [-180, 180). 0- converts -0 to +0.
00418     azi1 = 0-atan2(- swapp * lonsign * salp1,
00419                    + swapp * latsign * calp1) / Constants::degree();
00420     azi2 = 0-atan2(- azi2sense * swapp * lonsign * salp2,
00421                    + azi2sense * swapp * latsign * calp2) / Constants::degree();
00422     return;
00423   }
00424 
00425   double Geodesic::Chi12(double sbet1, double cbet1,
00426                          double sbet2, double cbet2,
00427                          double salp1, double calp1,
00428                          double& salp2, double& calp2,
00429                          double& sig12,
00430                          double& ssig1, double& csig1,
00431                          double& ssig2, double& csig2,
00432                          double& u2,
00433                          bool diffp, double& dchi12, double c[])
00434     const throw() {
00435 
00436     if (sbet1 == 0 && calp1 == 0)
00437       // Break degeneracy of equatorial line.  This cases has already been
00438       // handled.
00439       calp1 = -eps2;
00440 
00441     double
00442       // sin(alp1) * cos(bet1) = sin(alp0),
00443       salp0 = salp1 * cbet1,
00444       calp0 = hypot(calp1, salp1 * sbet1); // calp0 > 0
00445 
00446     double slam1, clam1, slam2, clam2, lam12, chi12, mu;
00447     // tan(bet1) = tan(sig1) * cos(alp1)
00448     // tan(lam1) = sin(alp0) * tan(sig1).
00449     ssig1 = sbet1; slam1 = salp0 * sbet1;
00450     csig1 = clam1 = calp1 * cbet1;
00451     SinCosNorm(ssig1, csig1);
00452     SinCosNorm(slam1, clam1);
00453 
00454     // Enforce symmetries in the case abs(bet2) = -bet1.  Need to be careful
00455     // about this case, since this can yield singularities in the Newton
00456     // iteration.
00457     // sin(alp2) * cos(bet2) = sin(alp0),
00458     salp2 = cbet2 != cbet1 ? salp0 / cbet2 : salp1;
00459     // calp2 = sqrt(1 - sq(salp2))
00460     //       = sqrt(sq(calp0) - sq(sbet2)) / cbet2
00461     // and subst for calp0 and rearrange to give (choose positive sqrt
00462     // to give alp2 in [0, pi/2]).
00463     calp2 = cbet2 != cbet1 || abs(sbet2) != -sbet1 ?
00464       sqrt(sq(calp1 * cbet1) + (cbet1 < -sbet1 ?
00465                                 (cbet2 - cbet1) * (cbet1 + cbet2) :
00466                                 (sbet1 - sbet2) * (sbet1 + sbet2))) / cbet2 :
00467       abs(calp1);
00468     // tan(bet2) = tan(sig2) * cos(alp2)
00469     // tan(lam2) = sin(alp0) * tan(sig2).
00470     ssig2 = sbet2; slam2 = salp0 * sbet2;
00471     csig2 = clam2 = calp2 * cbet2;
00472     SinCosNorm(ssig2, csig2);
00473     SinCosNorm(slam2, clam2);
00474 
00475     // sig12 = sig2 - sig1, limit to [0, pi]
00476     sig12 = atan2(max(csig1 * ssig2 - ssig1 * csig2, 0.0),
00477                   csig1 * csig2 + ssig1 * ssig2);
00478 
00479     // lam12 = lam2 - lam1, limit to [0, pi]
00480     lam12 = atan2(max(clam1 * slam2 - slam1 * clam2, 0.0),
00481                   clam1 * clam2 + slam1 * slam2);
00482 
00483     double eta12, lamscale;
00484     mu = sq(calp0);
00485     dlamCoeff(_f, mu, c);
00486     eta12 = SinSeries(ssig2, csig2, c, maxpow) -
00487       SinSeries(ssig1, csig1, c, maxpow);
00488     lamscale = dlamScale(_f, mu),
00489     chi12 = lam12 + salp0 * lamscale * (sig12 + eta12);
00490 
00491     if (diffp) {
00492       double dalp0, dsig1, dlam1, dalp2, dsig2, dlam2;
00493       // Differentiate sin(alp) * cos(bet) = sin(alp0),
00494       dalp0 = cbet1 * calp1 / calp0;
00495       dalp2 = calp2 != 0 ? calp1 * cbet1/ (calp2 * cbet2) :
00496         calp1 >= 0 ? 1 : -1;
00497       // Differentiate tan(bet) = tan(sig) * cos(alp) and clear
00498       // calp from the denominator with tan(alp0)=cos(sig)*tan(alp),
00499       dsig1 = ssig1 * salp0 / calp0;
00500       dsig2 = ssig2 * salp0 / calp0 * dalp2;
00501       // Differentiate tan(lam) = sin(alp0) * tan(sig).  Substitute
00502       //   tan(sig) = tan(bet) / cos(alp) = tan(lam) / sin(alp0)
00503       //   cos(lam) / cos(sig) = 1 / cos(bet)
00504       // to give
00505       dlam1 = (sbet1 * sq(clam1) + slam1 * salp0 / (calp0 * cbet1));
00506       dlam2 = (sbet2 * sq(clam2) + slam2 * salp0 / (calp0 * cbet2)) * dalp2;
00507 
00508       double deta12, dmu, dlamscale, dchisig;
00509       dlamCoeffmu(_f, mu, c);
00510       dmu = - 2 * calp0 * salp0 * dalp0;
00511       deta12 = dmu * (SinSeries(ssig2, csig2, c, maxpow) -
00512                       SinSeries(ssig1, csig1, c, maxpow));
00513       dlamscale = dlamScalemu(_f, mu) * dmu;
00514 
00515       // Derivative of salp0 * lamscale * (sig + eta) wrt sig.  This
00516       // is from integral form of this expression.
00517       dchisig =  - _e2 * salp0 *
00518         (dsig2 / (sqrt(1 - _e2 * (1 - mu * sq(ssig2))) + 1) -
00519          dsig1 / (sqrt(1 - _e2 * (1 - mu * sq(ssig1))) + 1)) ;
00520 
00521       dchi12 =
00522         (dlam2 - dlam1) + dchisig +
00523         // Derivative wrt mu
00524         (dalp0 * calp0 * lamscale + salp0 * dlamscale) * (sig12 + eta12) +
00525         salp0 * lamscale * deta12;
00526     }
00527 
00528     u2 = mu * _ep2;
00529     return chi12;
00530   }
00531 
00532   GeodesicLine::GeodesicLine(const Geodesic& g,
00533                              double lat1, double lon1, double azi1) throw() {
00534     azi1 = Geodesic::AngNormalize(azi1);
00535     // Normalize azimuth at poles.  Evaluate azimuths at lat = +/- (90 - eps).
00536     if (lat1 == 90) {
00537       lon1 -= azi1 - (azi1 >= 0 ? 180 : -180);
00538       azi1 = -180;
00539     } else if (lat1 == -90) {
00540       lon1 += azi1;
00541       azi1 = 0;
00542     }
00543     // Guard against underflow in salp0
00544     azi1 = Geodesic::AngRound(azi1);
00545     lon1 = Geodesic::AngNormalize(lon1);
00546     _bsign = azi1 >= 0 ? 1 : -1;
00547     azi1 *= _bsign;
00548     _lat1 = lat1;
00549     _lon1 = lon1;
00550     _azi1 = azi1;
00551     _f1 = g._f1;
00552     // alp1 is in [0, pi]
00553     double
00554       alp1 = azi1 * Constants::degree(),
00555       // Enforce sin(pi) == 0 and cos(pi/2) == 0.  Better to face the ensuing
00556       // problems directly than to skirt them.
00557       salp1 = azi1 == 180 ? 0 : sin(alp1),
00558       calp1 = azi1 ==  90 ? 0 : cos(alp1);
00559     double cbet1, sbet1, phi;
00560     phi = lat1 * Constants::degree();
00561     // Ensure cbet1 = +eps at poles
00562     sbet1 = _f1 * sin(phi);
00563     cbet1 = abs(lat1) == 90 ? Geodesic::eps2 : cos(phi);
00564     Geodesic::SinCosNorm(sbet1, cbet1);
00565 
00566     // Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0),
00567     _salp0 = salp1 * cbet1; // alp0 in [0, pi/2 - |bet1|]
00568     // Alt: calp0 = hypot(sbet1, calp1 * cbet1).  The following
00569     // is slightly better (consider the case salp1 = 0).
00570     _calp0 = Geodesic::hypot(calp1, salp1 * sbet1);
00571     // Evaluate sig with tan(bet1) = tan(sig1) * cos(alp1).
00572     // sig = 0 is nearest northward crossing of equator.
00573     // With bet1 = 0, alp1 = pi/2, we have sig1 = 0 (equatorial line).
00574     // With bet1 =  pi/2, alp1 = -pi, sig1 =  pi/2
00575     // With bet1 = -pi/2, alp1 =  0 , sig1 = -pi/2
00576     // Evaluate lam1 with tan(lam1) = sin(alp0) * tan(sig1).
00577     // With alp0 in (0, pi/2], quadrants for sig and lam coincide.
00578     // No atan2(0,0) ambiguity at poles sce cbet1 = +eps.
00579     // With alp0 = 0, lam1 = 0 for alp1 = 0, lam1 = pi for alp1 = pi.
00580     _ssig1 = sbet1; _slam1 = _salp0 * sbet1;
00581     _csig1 = _clam1 = sbet1 != 0 || calp1 != 0 ? cbet1 * calp1 : 1;
00582 
00583     Geodesic::SinCosNorm(_ssig1, _csig1); // sig1 in (-pi, pi]
00584     Geodesic::SinCosNorm(_slam1, _clam1);
00585     double
00586       mu = Geodesic::sq(_calp0),
00587       u2 = mu * g._ep2;
00588 
00589     _sScale =  g._b * Geodesic::tauScale(u2);
00590     Geodesic::tauCoeff(u2, _sigCoeff);
00591     _dtau1 = Geodesic::SinSeries(_ssig1, _csig1, _sigCoeff, maxpow);
00592     {
00593       double s = sin(_dtau1), c = cos(_dtau1);
00594       // tau1 = sig1 + dtau1
00595       _stau1 = _ssig1 * c + _csig1 * s;
00596       _ctau1 = _csig1 * c - _ssig1 * s;
00597     }
00598     Geodesic::sigCoeff(u2, _sigCoeff);
00599     // Not necessary because sigCoeff reverts tauCoeff
00600     //    _dtau1 = -SinSeries(_stau1, _ctau1, _sigCoeff, maxpow);
00601 
00602     _dlamScale = _salp0 * Geodesic::dlamScale(g._f, mu);
00603     Geodesic::dlamCoeff(g._f, mu, _dlamCoeff);
00604     _dchi1 = Geodesic::SinSeries(_ssig1, _csig1, _dlamCoeff, maxpow);
00605   }
00606 
00607   void GeodesicLine::Position(double s12,
00608                               double& lat2, double& lon2, double& azi2)
00609   const throw() {
00610     if (_sScale == 0)
00611       // Uninitialized
00612       return;
00613     double tau12, sig12, lam12, chi12, lon12, s, c;
00614     double ssig2, csig2, sbet2, cbet2, slam2, clam2, salp2, calp2;
00615     tau12 = s12 / _sScale;
00616     s = sin(tau12); c = cos(tau12);
00617     sig12 = tau12 + (_dtau1 +
00618                      // tau2 = tau1 + tau12
00619                      Geodesic::SinSeries(_stau1 * c + _ctau1 * s,
00620                                          _ctau1 * c - _stau1 * s,
00621                                          _sigCoeff, maxpow));
00622     s = sin(sig12); c = cos(sig12);
00623     // sig2 = sig1 + sig12
00624     ssig2 = _ssig1 * c + _csig1 * s;
00625     csig2 = _csig1 * c - _ssig1 * s;
00626     // sin(bet2) = cos(alp0) * sin(sig2)
00627     sbet2 = _calp0 * ssig2;
00628     // Alt: cbet2 = hypot(csig2, salp0 * ssig2);
00629     cbet2 = Geodesic::hypot(_salp0, _calp0 * csig2);
00630     // tan(lam2) = sin(alp0) * tan(sig2)
00631     slam2 = _salp0 * ssig2; clam2 = csig2;  // No need to normalize
00632     // tan(alp0) = cos(sig2)*tan(alp2)
00633     salp2 = _salp0; calp2 = _calp0 * csig2; // No need to normalize
00634     // lam12 = lam2 - lam1
00635     lam12 = atan2(slam2 * _clam1 - clam2 * _slam1,
00636                   clam2 * _clam1 + slam2 * _slam1);
00637     chi12 = lam12 + _dlamScale *
00638       ( sig12 +
00639         (Geodesic::SinSeries(ssig2, csig2, _dlamCoeff, maxpow)  - _dchi1));
00640     lon12 = _bsign * chi12 / Constants::degree();
00641     // Can't use AngNormalize because longitude might have wrapped multiple
00642     // times.
00643     lon12 = lon12 - 360 * floor(lon12/360 + 0.5);
00644     lat2 = atan2(sbet2, _f1 * cbet2) / Constants::degree();
00645     lon2 = Geodesic::AngNormalize(_lon1 + lon12);
00646     // minus signs give range [-180, 180). 0- converts -0 to +0.
00647     azi2 = 0-atan2(- Geodesic::azi2sense * _bsign * salp2,
00648                    + Geodesic::azi2sense * calp2) / Constants::degree();
00649   }
00650 
00651 } // namespace GeographicLib
00652