Esempio n. 1
0
        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);
            }
        }