private double Lambda12(double sbet1, double cbet1, double dn1, double sbet2, double cbet2, double dn2, double salp1, double calp1, double slam120, double clam120, out double salp2, out double calp2, out double sig12, out double ssig1, out double csig1, out double ssig2, out double csig2, EllipticFunction E, out double domg12, bool diffp, out double dlam12) { dlam12 = double.NaN; if (sbet1 == 0 && calp1 == 0) { // Break degeneracy of equatorial line. This case has already been // handled. calp1 = -tiny_; } double // sin(alp1) * cos(bet1) = sin(alp0) salp0 = salp1 * cbet1, calp0 = Hypot(calp1, salp1 * sbet1); // calp0 > 0 double somg1, comg1, somg2, comg2, somg12, comg12, cchi1, cchi2, lam12; // tan(bet1) = tan(sig1) * cos(alp1) // tan(omg1) = sin(alp0) * tan(sig1) = tan(omg1)=tan(alp1)*sin(bet1) ssig1 = sbet1; somg1 = salp0 * sbet1; csig1 = comg1 = calp1 * cbet1; // Without normalization we have schi1 = somg1. cchi1 = _f1 * dn1 * comg1; Norm(ref ssig1, ref csig1); // Math::norm(somg1, comg1); -- don't need to normalize! // Math::norm(schi1, cchi1); -- don't need to normalize! // Enforce symmetries in the case abs(bet2) = -bet1. Need to be careful // about this case, since this can yield singularities in the Newton // iteration. // sin(alp2) * cos(bet2) = sin(alp0) salp2 = cbet2 != cbet1 ? salp0 / cbet2 : salp1; // calp2 = sqrt(1 - sq(salp2)) // = sqrt(sq(calp0) - sq(sbet2)) / cbet2 // and subst for calp0 and rearrange to give (choose positive sqrt // to give alp2 in [0, pi/2]). calp2 = cbet2 != cbet1 || Abs(sbet2) != -sbet1? Sqrt(Sq(calp1 *cbet1) + (cbet1 < -sbet1 ? (cbet2 - cbet1) * (cbet1 + cbet2) : (sbet1 - sbet2) * (sbet1 + sbet2))) / cbet2: Abs(calp1); // tan(bet2) = tan(sig2) * cos(alp2) // tan(omg2) = sin(alp0) * tan(sig2). ssig2 = sbet2; somg2 = salp0 * sbet2; csig2 = comg2 = calp2 * cbet2; // Without normalization we have schi2 = somg2. cchi2 = _f1 * dn2 * comg2; Norm(ref ssig2, ref csig2); // Math::norm(somg2, comg2); -- don't need to normalize! // Math::norm(schi2, cchi2); -- don't need to normalize! // sig12 = sig2 - sig1, limit to [0, pi] sig12 = Atan2(Max(0, csig1 * ssig2 - ssig1 * csig2), csig1 * csig2 + ssig1 * ssig2); // omg12 = omg2 - omg1, limit to [0, pi] somg12 = Max(0, comg1 * somg2 - somg1 * comg2); comg12 = comg1 * comg2 + somg1 * somg2; var k2 = Sq(calp0) * _ep2; E.Reset(-k2, -_ep2, 1 + k2, 1 + _ep2); // chi12 = chi2 - chi1, limit to [0, pi] double schi12 = Max(0, cchi1 * somg2 - somg1 * cchi2), cchi12 = cchi1 * cchi2 + somg1 * somg2; // eta = chi12 - lam120 var eta = Atan2(schi12 * clam120 - cchi12 * slam120, cchi12 * clam120 + schi12 * slam120); var deta12 = -_e2 / _f1 * salp0 * E.H() / (PI / 2) * (sig12 + (E.DeltaH(ssig2, csig2, dn2) - E.DeltaH(ssig1, csig1, dn1))); lam12 = eta + deta12; // domg12 = deta12 + chi12 - omg12 domg12 = deta12 + Atan2(schi12 * comg12 - cchi12 * somg12, cchi12 * comg12 + schi12 * somg12); if (diffp) { if (calp2 == 0) { dlam12 = -2 * _f1 * dn1 / sbet1; } else { Lengths(E, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, cbet1, cbet2, GeodesicFlags.ReducedLength, out _, out dlam12, out _, out _, out _); dlam12 *= _f1 / (calp2 * cbet2); } } return(lam12); }
private double InverseStart(EllipticFunction E, double sbet1, double cbet1, double dn1, double sbet2, double cbet2, double dn2, double lam12, double slam12, double clam12, out double salp1, out double calp1, out double salp2, out double calp2, out double dnm) { salp2 = calp2 = dnm = double.NaN; // Return a starting point for Newton's method in salp1 and calp1 (function // value is -1). If Newton's method doesn't need to be used, return also // salp2 and calp2 and function value is sig12. double sig12 = -1, // Return value // bet12 = bet2 - bet1 in [0, pi); bet12a = bet2 + bet1 in (-pi, 0] sbet12 = sbet2 * cbet1 - cbet2 * sbet1, cbet12 = cbet2 * cbet1 + sbet2 * sbet1; var sbet12a = sbet2 * cbet1 + cbet2 * sbet1; bool shortline = cbet12 >= 0 && sbet12 < 0.5 && cbet2 * lam12 < 0.5; double somg12, comg12; if (shortline) { var sbetm2 = Sq(sbet1 + sbet2); // sin((bet1+bet2)/2)^2 // = (sbet1 + sbet2)^2 / ((sbet1 + sbet2)^2 + (cbet1 + cbet2)^2) sbetm2 /= sbetm2 + Sq(cbet1 + cbet2); dnm = Sqrt(1 + _ep2 * sbetm2); var omg12 = lam12 / (_f1 * dnm); somg12 = Sin(omg12); comg12 = Cos(omg12); } else { somg12 = slam12; comg12 = clam12; } salp1 = cbet2 * somg12; calp1 = comg12 >= 0 ? sbet12 + cbet2 * sbet1 * Sq(somg12) / (1 + comg12) : sbet12a - cbet2 * sbet1 * Sq(somg12) / (1 - comg12); double ssig12 = Hypot(salp1, calp1), csig12 = sbet1 * sbet2 + cbet1 * cbet2 * comg12; if (shortline && ssig12 < _etol2) { // really short lines salp2 = cbet1 * somg12; calp2 = sbet12 - cbet1 * sbet2 * (comg12 >= 0 ? Sq(somg12) / (1 + comg12) : 1 - comg12); Norm(ref salp2, ref calp2); // Set return value sig12 = Atan2(ssig12, csig12); } else if (Abs(_n) > 0.1 || // Skip astroid calc if too eccentric csig12 >= 0 || ssig12 >= 6 * Abs(_n) * PI * Sq(cbet1)) { // Nothing to do, zeroth order spherical approximation is OK } else { // Scale lam12 and bet2 to x, y coordinate system where antipodal point // is at origin and singular point is at y = 0, x = -1. double y, lamscale, betscale; // Volatile declaration needed to fix inverse case // 56.320923501171 0 -56.320923501171 179.664747671772880215 // which otherwise fails with g++ 4.4.4 x86 -O3 /*GEOGRAPHICLIB_VOLATILE*/ double x; var lam12x = Atan2(-slam12, -clam12); // lam12 - pi if (_f >= 0) { // In fact f == 0 does not get here // x = dlong, y = dlat { var k2 = Sq(sbet1) * _ep2; E.Reset(-k2, -_ep2, 1 + k2, 1 + _ep2); lamscale = _e2 / _f1 * cbet1 * 2 * E.H(); } betscale = lamscale * cbet1; x = lam12x / lamscale; y = sbet12a / betscale; } else { // _f < 0 // x = dlat, y = dlong double cbet12a = cbet2 * cbet1 - sbet2 * sbet1, bet12a = Atan2(sbet12a, cbet12a); // In the case of lon12 = 180, this repeats a calculation made in // Inverse. Lengths(E, PI + bet12a, sbet1, -cbet1, dn1, sbet2, cbet2, dn2, cbet1, cbet2, GeodesicFlags.ReducedLength, out _, out var m12b, out var m0, out _, out _); x = -1 + m12b / (cbet1 * cbet2 * m0 * PI); betscale = x < -0.01 ? sbet12a / x : -_f *Sq(cbet1) * PI; lamscale = betscale / cbet1; y = lam12x / lamscale; } if (y > -tol1_ && x > -1 - xthresh_) { // strip near cut // Need real(x) here to cast away the volatility of x for min/max if (_f >= 0) { salp1 = Min(1, -x); calp1 = -Sqrt(1 - Sq(salp1)); } else { calp1 = Max(x > -tol1_ ? 0d : -1d, x); salp1 = Sqrt(1 - Sq(calp1)); } } else { // Estimate alp1, by solving the astroid problem. // // Could estimate alpha1 = theta + pi/2, directly, i.e., // calp1 = y/k; salp1 = -x/(1+k); for _f >= 0 // calp1 = x/(1+k); salp1 = -y/k; for _f < 0 (need to check) // // However, it's better to estimate omg12 from astroid and use // spherical formula to compute alp1. This reduces the mean number of // Newton iterations for astroid cases from 2.24 (min 0, max 6) to 2.12 // (min 0 max 5). The changes in the number of iterations are as // follows: // // change percent // 1 5 // 0 78 // -1 16 // -2 0.6 // -3 0.04 // -4 0.002 // // The histogram of iterations is (m = number of iterations estimating // alp1 directly, n = number of iterations estimating via omg12, total // number of trials = 148605): // // iter m n // 0 148 186 // 1 13046 13845 // 2 93315 102225 // 3 36189 32341 // 4 5396 7 // 5 455 1 // 6 56 0 // // Because omg12 is near pi, estimate work with omg12a = pi - omg12 var k = Astroid(x, y); var omg12a = lamscale * (_f >= 0 ? -x * k / (1 + k) : -y * (1 + k) / k); somg12 = Sin(omg12a); comg12 = -Cos(omg12a); // Update spherical estimate of alp1 using omg12 instead of lam12 salp1 = cbet2 * somg12; calp1 = sbet12a - cbet2 * sbet1 * Sq(somg12) / (1 - comg12); } } // Sanity check on starting guess. Backwards check allows NaN through. if (!(salp1 <= 0)) { Norm(ref salp1, ref calp1); } else { salp1 = 1; calp1 = 0; } return(sig12); }