internal void IntReverse(double X, double Y, double Z, out double lat, out double lon, out double h, double[] M) { double R = GeoMath.Hypot(X, Y), slam = R != 0 ? Y / R : 0, clam = R != 0 ? X / R : 1; h = GeoMath.Hypot(R, Z); // Distance to center of earth double sphi, cphi; if (h > _maxrad) { // We doublely far away (> 12 million light years); treat the earth as a // point and h, above, is an acceptable approximation to the height. // This avoids overflow, e.g., in the computation of disc below. It's // possible that h has overflowed to inf; but that's OK. // // Treat the case X, Y finite, but R overflows to +inf by scaling by 2. R = GeoMath.Hypot(X / 2, Y / 2); slam = R != 0 ? (Y / 2) / R : 0; clam = R != 0 ? (X / 2) / R : 1; double H = GeoMath.Hypot(Z / 2, R); sphi = (Z / 2) / H; cphi = R / H; } else if (_e4a == 0) { // Treat the spherical case. Dealing with underflow in the general case // with _e2 = 0 is difficult. Origin maps to N pole same as with // ellipsoid. double H = GeoMath.Hypot(h == 0 ? 1 : Z, R); sphi = (h == 0 ? 1 : Z) / H; cphi = R / H; h -= _a; } else { // Treat prolate spheroids by swapping R and Z here and by switching // the arguments to phi = atan2(...) at the end. double p = GeoMath.Square(R / _a), q = _e2m * GeoMath.Square(Z / _a), r = (p + q - _e4a) / 6; if (_f < 0) { Utility.Swap(ref p, ref q); } if (!(_e4a * q == 0 && r <= 0)) { double // Avoid possible division by zero when r = 0 by multiplying // equations for s and t by r^3 and r, resp. S = _e4a * p * q / 4, // S = r^3 * s r2 = GeoMath.Square(r), r3 = r * r2, disc = S * (2 * r3 + S); double u = r; if (disc >= 0) { double T3 = S + r3; // Pick the sign on the sqrt to maximize abs(T3). This minimizes // loss of precision due to cancellation. The result is unchanged // because of the way the T is used in definition of u. T3 += T3 < 0 ? -Math.Sqrt(disc) : Math.Sqrt(disc); // T3 = (r * t)^3 // N.B. cbrt always returns the double root. cbrt(-8) = -2. double T = GeoMath.CubeRoot(T3); // T = r * t // T can be zero; but then r2 / T -> 0. u += T + (T != 0 ? r2 / T : 0); } else { // T is complex, but the way u is defined the result is double. double ang = Math.Atan2(Math.Sqrt(-disc), -(S + r3)); // There are three possible cube roots. We choose the root which // avoids cancellation. Note that disc < 0 implies that r < 0. u += 2 * r * Math.Cos(ang / 3); } double v = Math.Sqrt(GeoMath.Square(u) + _e4a * q), // guaranteed positive // Avoid loss of accuracy when u < 0. Underflow doesn't occur in // e4 * q / (v - u) because u ~ e^4 when q is small and u < 0. uv = u < 0 ? _e4a * q / (v - u) : u + v, // u+v, guaranteed positive // Need to guard against w going negative due to roundoff in uv - q. w = Math.Max(0, _e2a * (uv - q) / (2 * v)), // Rearrange expression for k to avoid loss of accuracy due to // subtraction. Division by 0 not possible because uv > 0, w >= 0. k = uv / (Math.Sqrt(uv + GeoMath.Square(w)) + w), k1 = _f >= 0 ? k : k - _e2, k2 = _f >= 0 ? k + _e2 : k, d = k1 * R / k2, H = GeoMath.Hypot(Z / k1, R / k2); sphi = (Z / k1) / H; cphi = (R / k2) / H; h = (1 - _e2m / k1) * GeoMath.Hypot(d, Z); } else { // e4 * q == 0 && r <= 0 // This leads to k = 0 (oblate, equatorial plane) and k + e^2 = 0 // (prolate, rotation axis) and the generation of 0/0 in the general // formulas for phi and h. using the general formula and division by 0 // in formula for h. So handle this case by taking the limits: // f > 0: z -> 0, k -> e2 * Math.Sqrt(q)/Math.Sqrt(e4 - p) // f < 0: R -> 0, k + e2 -> - e2 * Math.Sqrt(q)/Math.Sqrt(e4 - p) double zz = Math.Sqrt((_f >= 0 ? _e4a - p : p) / _e2m), xx = Math.Sqrt(_f < 0 ? _e4a - p : p), H = GeoMath.Hypot(zz, xx); sphi = zz / H; cphi = xx / H; if (Z < 0) { sphi = -sphi; // for tiny negative Z (not for prolate) } h = -_a * (_f >= 0 ? _e2m : 1) * H / _e2a; } } lat = GeoMath.Atan2d(sphi, cphi); lon = GeoMath.Atan2d(slam, clam); if (M != null) { Rotation(sphi, cphi, slam, clam, M); } }