Beispiel #1
0
    def _s1_qZ_C2(self, ca1, sa1, ta1, ca2, sa2, ta2):
        '''(INTERNAL) Compute C{sm1 / (s / qZ)} and C{C} for .__init__.
        '''
        E = self.datum.ellipsoid
        b_a = E.b_a
        e2 = E.e2

        tb1 = b_a * ta1
        tb2 = b_a * ta2
        dtb12 = b_a * (tb1 + tb2)
        scb12 = _1_0 + tb1**2
        scb22 = _1_0 + tb2**2

        esa1_2 = (_1_0 - e2 * sa1**2) \
               * (_1_0 - e2 * sa2**2)
        esa12 = _1_0 + e2 * sa1 * sa2

        dsn = _Dsn(ta2, ta1, sa2, sa1)
        axi, bxi, sxi = self._a_b_sxi3((ca1, sa1, ta1, scb12),
                                       (ca2, sa2, ta2, scb22))

        dsxi = (esa12 / esa1_2 + self._Datanhee(sa2, sa1)) * dsn / (_2_0 *
                                                                    self._qx)
        C = fsum_(sxi * dtb12 / dsxi, scb22, scb12) / (_2_0 * scb22 * scb12)

        sa12 = fsum_(sa1, sa2, sa1 * sa2)
        axi *= (_1_0 + e2 * sa12) / (_1_0 + sa12)
        bxi *= e2 * fsum_(sa1, sa2, esa12) / esa1_2 + E.e12 * self._D2atanhee(
            sa1, sa2)
        s1_qZ = dsn * (axi * self._qZ - bxi) / (_2_0 * dtb12)
        return s1_qZ, C
Beispiel #2
0
def thomas_(phi2, phi1, lam21, datum=Datums.WGS84):
    '''Compute the I{angular} distance between two (ellipsoidal) points using
       U{Thomas'<https://apps.DTIC.mil/dtic/tr/fulltext/u2/703541.pdf>}
       formula.

       @arg phi2: End latitude (C{radians}).
       @arg phi1: Start latitude (C{radians}).
       @arg lam21: Longitudinal delta, M{end-start} (C{radians}).
       @kwarg datum: Ellipsoidal datum to use (L{Datum}).

       @return: Angular distance (C{radians}).

       @raise TypeError: Invalid B{C{datum}}.

       @see: Functions L{thomas}, L{cosineAndoyerLambert_},
             L{cosineForsytheAndoyerLambert_}, L{cosineLaw_},
             L{equirectangular_}, L{euclidean_}, L{flatLocal_}/L{hubeny_},
             L{flatPolar_}, L{haversine_} and L{vincentys_} and U{Geodesy-PHP
             <https://GitHub.com/jtejido/geodesy-php/blob/master/src/Geodesy/
             Distance/ThomasFormula.php>}.
    '''
    _xinstanceof(Datum, datum=datum)

    s2, c2, s1, c1, _, c21 = sincos2(phi2, phi1, lam21)
    E = datum.ellipsoid
    if E.f and abs(c1) > EPS and abs(c2) > EPS:
        r1 = atan(E.b_a * s1 / c1)
        r2 = atan(E.b_a * s2 / c2)

        j = (r2 + r1) / 2.0
        k = (r2 - r1) / 2.0
        sj, cj, sk, ck, sl_2, _ = sincos2(j, k, lam21 / 2.0)

        h = fsum_(sk**2, (ck * sl_2)**2, -(sj * sl_2)**2)
        if EPS < abs(h) < EPS1:
            u = 1 / (1 - h)
            d = 2 * atan(sqrt(h * u))  # == acos(1 - 2 * h)
            sd, cd = sincos2(d)
            if abs(sd) > EPS:
                u = 2 * (sj * ck)**2 * u
                v = 2 * (sk * cj)**2 / h
                x = u + v
                y = u - v

                t = d / sd
                s = 4 * t**2
                e = 2 * cd
                a = s * e
                b = 2 * d
                c = t - (a - e) / 2.0

                s = fsum_(a * x, c * x**2, -b * y, -e * y**2,
                          s * x * y) * E.f / 16.0
                s = fsum_(t * x, -y, -s) * E.f / 4.0
                return d - s * sd
    # fall back to cosineLaw_
    return acos(s1 * s2 + c1 * c2 * c21)
Beispiel #3
0
    def reverse(self, xyz, y=None, z=None, **no_M):  # PYCHOK unused M
        '''Convert from geocentric C{(x, y, z)} to geodetic C{(lat, lon, height)}
           using I{Rey-Jer You}'s transformation.

           @arg xyz: Either an L{Ecef9Tuple}, an C{(x, y, z)} 3-tuple or C{scalar}
                     ECEF C{x} coordinate in C{meter}.
           @kwarg y: ECEF C{y} coordinate in C{meter} for C{scalar} B{C{xyz}}
                     and B{C{z}}.
           @kwarg z: ECEF C{z} coordinate in C{meter} for C{scalar} B{C{xyz}}
                     and B{C{y}}.
           @kwarg no_M: Rotation matrix C{M} not available.

           @return: An L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M, datum)}
                    with geodetic coordinates C{(lat, lon, height)} for the given
                    geocentric ones C{(x, y, z)}, case C{C} 1, L{EcefMatrix} C{M}
                    always C{None} and C{datum} if available.

           @raise EcefError: If B{C{xyz}} not L{Ecef9Tuple} or C{scalar} C{x}
                             or B{C{y}} and/or B{C{z}} not C{scalar} for C{scalar}
                             B{C{xyz}}.
        '''
        x, y, z, name = _xyzn4(xyz, y, z, Error=EcefError)

        x2, y2, z2 = x**2, y**2, z**2
        r2 = fsum_(x2, y2, z2)  # = hypot3(x2, y2, z2)**2

        E = self.ellipsoid
        e = sqrt(E.a2 - E.b2)
        e2 = e**2

        u = sqrt(fsum_(r2, -e2, hypot(r2 - e2, 2 * e * z)) / 2)
        p = hypot(u, e)
        q = hypot(x, y)
        B = atan2(p * z, u * q)  # beta0 = atan(p / u * z / q)
        sB, cB = sincos2(B)
        p *= E.a
        B += fsum_(u * E.b, -p, e2) * sB / (p / cB - e2 * cB)
        sB, cB = sincos2(B)

        h = hypot(z - E.b * sB, q - E.a * cB)
        if fsum_(x2, y2, z2 * E.a_b**2) < E.a2:
            h = -h  # inside ellipsoid

        r = Ecef9Tuple(
            x,
            y,
            z,
            degrees(atan2(E.a * sB, E.b * cB)),  # atan(E.a_b * tan(B))
            degrees(atan2(y, x)),
            h,
            1,  # C=1
            None,  # M=None
            self.datum)
        return self._xnamed(r, name)
Beispiel #4
0
def _Hf(e0, e1, e2, e3, e4, e5):
    '''(INTERNAL) Horner form for C{_RD} and C{_RJ} below.
    '''
    # Polynomial is <https://DLMF.NIST.gov/19.36.E2>
    # (1 - 3*E2/14 + E3/6 + 9*E2^2/88 - 3*E4/22 - 9*E2*E3/52 + 3*E5/26
    #    - E2^3/16 + 3*E3^2/40 + 3*E2*E4/20 + 45*E2^2*E3/272
    #    - 9*(E3*E4+E2*E5)/68)
    return fsum_(
        fsum_(471240, -540540 * e2) * e5,
        fsum_(612612 * e2, -540540 * e3, -556920) * e4,
        fsum_(306306 * e3, 675675 * e2**2, -706860 * e2, 680680) * e3,
        fsum_(417690 * e2, -255255 * e2**2, -875160) * e2,
        4084080) / (4084080 * e1) + e0
Beispiel #5
0
def cosineForsytheAndoyerLambert_(phi2, phi1, lam21, datum=Datums.WGS84):
    '''Compute the I{angular} distance between two (ellipsoidal) points using the
       U{Forsythe-Andoyer-Lambert correction<https://www2.UNB.CA/gge/Pubs/TR77.pdf>} of
       the U{Law of Cosines<https://www.Movable-Type.co.UK/scripts/latlong.html#cosine-law>}
       formula.

       @arg phi2: End latitude (C{radians}).
       @arg phi1: Start latitude (C{radians}).
       @arg lam21: Longitudinal delta, M{end-start} (C{radians}).
       @kwarg datum: Ellipsoidal datum to use (L{Datum}).

       @return: Angular distance (C{radians}).

       @raise TypeError: Invalid B{C{datum}}.

       @see: Functions L{cosineForsytheAndoyerLambert}, L{cosineAndoyerLambert_},
             L{cosineLaw_}, L{equirectangular_}, L{euclidean_}, L{flatLocal_}/L{hubeny_},
             L{flatPolar_}, L{haversine_}, L{thomas_} and L{vincentys_} and U{Geodesy-PHP
             <https://GitHub.com/jtejido/geodesy-php/blob/master/src/Geodesy/
             Distance/ForsytheCorrection.php>}.
    '''
    _xinstanceof(Datum, datum=datum)

    s2, c2, s1, c1, _, c21 = sincos2(phi2, phi1, lam21)
    r = acos(s1 * s2 + c1 * c2 * c21)
    E = datum.ellipsoid
    if E.f:
        sr, cr, s2r, _ = sincos2(r, r * 2)
        if abs(sr) > EPS:
            r2 = r**2

            p = (s1 + s2)**2 / (1 + cr)
            q = (s1 - s2)**2 / (1 - cr)
            x = p + q
            y = p - q
            s = 8 * r2 / sr

            a = 64 * r + 2 * s * cr  # 16 * r2 / tan(r)
            d = 48 * sr + s  # 8 * r2 / tan(r)
            b = -2 * d
            e = 30 * s2r
            c = fsum_(30 * r, e / 2, s * cr)  # 8 * r2 / tan(r)

            d = fsum_(a * x, b * y, -c * x**2, d * x * y,
                      e * y**2) * E.f / 32.0
            d = fsum_(d, -x * r, 3 * y * sr) * E.f / 4.0
            r += d
    return r
Beispiel #6
0
def heightOf(angle, distance, radius=R_M):
    '''Determine the height above the (spherical) earth after
       traveling along a straight line at a given tilt.

       @param angle: Tilt angle above horizontal (C{degrees}).
       @param distance: Distance along the line (C{meter} or same units
                        as B{C{radius}}).
       @keyword radius: Optional mean earth radius (C{meter}).

       @return: Height (C{meter}, same units as B{C{distance}} and B{C{radius}}).

       @raise ValueError: Invalid B{C{angle}}, B{C{distance}} or B{C{radius}}.

       @see: U{MultiDop geog_lib.GeogBeamHt<https://GitHub.com/NASA/MultiDop>}
             (U{Shapiro et al. 2009, JTECH
             <https://Journals.AMetSoc.org/doi/abs/10.1175/2009JTECHA1256.1>}
             and U{Potvin et al. 2012, JTECH
             <https://Journals.AMetSoc.org/doi/abs/10.1175/JTECH-D-11-00019.1>}).
    '''
    d, r = distance, radius
    if d > r:
        d, r = r, d

    if d > EPS:
        d = d / float(r)
        s = sin(radians(angle))
        s = fsum_(1, 2 * s * d, d**2)
        if s > 0:
            return r * sqrt(s) - float(radius)

    raise ValueError('%s%r' % ('heightOf', (angle, distance, radius)))
Beispiel #7
0
def _RD(x, y, z):  # used by testElliptic.py
    '''Degenerate symmetric integral of the third kind C{_RD}.

       @return: C{_RD(x, y, z) = _RJ(x, y, z, z)}.

       @see: U{C{_RD} definition<https://DLMF.NIST.gov/19.16.E5>}.
    '''
    # Carlson, eqs 2.28 - 2.34
    m = 1.0
    S = Fsum()
    A = fsum_(x, y, z, z, z) * 0.2
    T = (A, x, y, z)
    Q = _Q(A, T, _TolRD)
    for _ in range(_TRIPS):
        if Q < abs(m * T[0]):  # max 7 trips
            break
        t = T[3]  # z0
        r, s, T = _rsT(T)
        S += 1.0 / (m * s[2] * (t + r))
        m *= 4
    else:
        raise _convergenceError(_RD, x, y, z)

    m *= T[0]  # An
    x = (x - A) / m
    y = (y - A) / m
    z = (x + y) / 3.0
    z2 = z**2
    xy = x * y
    return _Horner(3 * S.fsum(), m * sqrt(T[0]),
                   xy - 6 * z2,
                  (xy * 3 - 8 * z2) * z,
                  (xy - z2) * 3 * z2,
                   xy * z2 * z)
Beispiel #8
0
def _RG(x, y, z=None):
    '''Symmetric integral of the second kind C{_RG}.

       @return: C{_RG(x, y, z)}.

       @see: U{C{_RG} definition<https://DLMF.NIST.gov/19.16.E3>}
             and in Carlson, eq. 1.5.
    '''
    if z is None:
        # Carlson, eqs 2.36 - 2.39
        a, b = sqrt(x), sqrt(y)
        if a < b:
            a, b = b, a
        S = Fsum(0.25 * (a + b)**2)
        m = -0.25  # note, negative
        while abs(a - b) > (_tolRG0 * a):  # max 4 trips
            b, a = sqrt(a * b), (a + b) * 0.5
            m *= 2
            S.fadd_(m * (a - b)**2)
        return S.fsum() * PI_2 / (a + b)

    if not z:
        y, z = z, y
    # Carlson, eq 1.7
    return fsum_(
        _RF(x, y, z) * z,
        _RD_3(x, y, z) * (x - z) * (z - y), sqrt(x * y / z)) * 0.5
Beispiel #9
0
def _RF(x, y, z):  # used by testElliptic.py
    '''Symmetric integral of the first kind C{_RF}.

       @return: C{_RF(x, y, z)}.

       @see: U{C{_RF} definition<https://DLMF.NIST.gov/19.16.E1>}.
    '''
    # Carlson, eqs 2.2 - 2.7
    m = 1.0
    A = fsum_(x, y, z) / 3.0
    T = (A, x, y, z)
    Q = _Q(A, T, _TolRF)
    for _ in range(_TRIPS):
        if Q < abs(m * T[0]):  # max 6 trips
            break
        _, _, T = _rsT(T)
        m *= 4
    else:
        raise _convergenceError(_RF, x, y, z)

    m *= T[0]  # An
    x = (A - x) / m
    y = (A - y) / m
    z = -(x + y)

    e2 = x * y - z**2
    e3 = x * y * z
    # Polynomial is <https://DLMF.NIST.gov/19.36.E1>
    # (1 - E2/10 + E3/14 + E2**2/24 - 3*E2*E3/44
    #    - 5*E2**3/208 + 3*E3**2/104 + E2**2*E3/16)
    # converted to Horner form ...
    H  = Fsum( 6930 * e3, 15015 * e2**2, -16380  * e2, 17160) * e3
    H += Fsum(10010 * e2, -5775 * e2**2, -24024) * e2
    return H.fsum_(240240) / (240240 * sqrt(T[0]))
Beispiel #10
0
def heightOf(angle, distance, radius=R_M):
    '''Determine the height above the (spherical) earth after
       traveling along a straight line at a given tilt.

       @arg angle: Tilt angle above horizontal (C{degrees}).
       @arg distance: Distance along the line (C{meter} or same units as
                      B{C{radius}}).
       @kwarg radius: Optional mean earth radius (C{meter}).

       @return: Height (C{meter}, same units as B{C{distance}} and B{C{radius}}).

       @raise ValueError: Invalid B{C{angle}}, B{C{distance}} or B{C{radius}}.

       @see: U{MultiDop geog_lib.GeogBeamHt<https://GitHub.com/NASA/MultiDop>}
             (U{Shapiro et al. 2009, JTECH
             <https://Journals.AMetSoc.org/doi/abs/10.1175/2009JTECHA1256.1>}
             and U{Potvin et al. 2012, JTECH
             <https://Journals.AMetSoc.org/doi/abs/10.1175/JTECH-D-11-00019.1>}).
    '''
    r = h = Radius(radius)
    d = abs(Distance(distance))
    if d > h:
        d, h = h, d

    if d > EPS:
        d = d / h  # PyChecker chokes on ... /= ...
        s = sin(Phi_(angle, name='angle', clip=180))
        s = fsum_(1, 2 * s * d, d**2)
        if s > 0:
            return h * sqrt(s) - r

    raise InvalidError(angle=angle, distance=distance, radius=radius)
Beispiel #11
0
def flatPolar_(phi2, phi1, lam21):
    '''Compute the I{angular} distance between two (spherical) points
       using the U{polar coordinate flat-Earth<https://WikiPedia.org/wiki/
       Geographical_distance#Polar_coordinate_flat-Earth_formula>}
       formula.

       @arg phi2: End latitude (C{radians}).
       @arg phi1: Start latitude (C{radians}).
       @arg lam21: Longitudinal delta, M{end-start} (C{radians}).

       @return: Angular distance (C{radians}).

       @see: Functions L{flatPolar}, L{cosineAndoyerLambert_},
             L{cosineForsytheAndoyerLambert_}, L{cosineLaw_},
             L{equirectangular_}, L{euclidean_}, L{flatLocal_}/L{hubeny_},
             L{haversine_}, L{thomas_} and L{vincentys_}.
    '''
    a1 = abs(PI_2 - phi1)  # co-latitude
    a2 = abs(PI_2 - phi2)  # co-latitude
    ab = abs(2 * a1 * a2 * cos(lam21))
    a = max(a1, a2, ab)
    if a > EPS:
        s = fsum_((a1 / a)**2, (a2 / a)**2, -ab / a**2)
        a *= sqrt(s) if s > 0 else 0
    return a
Beispiel #12
0
def _intersects2(center1, r1, center2, r2, sphere=True, too_d=None,  # in .ellipsoidalBase._intersections2
                                           Vector=None, **Vector_kwds):
    # (INTERNAL) Intersect two spheres or circles, see L{intersections2}
    # above, separated to allow callers to embellish any exceptions

    def _V3(x, y, z):
        v = Vector3d(x, y, z)
        n = intersections2.__name__
        return _V_n(v, n, Vector, Vector_kwds)

    def _xV3(c1, u, x, y):
        xy1 = x, y, _1_0  # transform to original space
        return _V3(fdot(xy1, u.x, -u.y, c1.x),
                   fdot(xy1, u.y,  u.x, c1.y), _0_0)

    c1 = _otherV3d(sphere=sphere, center1=center1)
    c2 = _otherV3d(sphere=sphere, center2=center2)

    if r1 < r2:  # r1, r2 == R, r
        c1, c2 = c2, c1
        r1, r2 = r2, r1

    m = c2.minus(c1)
    d = m.length
    if d < max(r2 - r1, EPS):
        raise ValueError(_near_concentric_)

    o = fsum_(-d, r1, r2)  # overlap == -(d - (r1 + r2))
    # compute intersections with c1 at (0, 0) and c2 at (d, 0), like
    # <https://MathWorld.Wolfram.com/Circle-CircleIntersection.html>
    if o > EPS:  # overlapping, r1, r2 == R, r
        x = _radical2(d, r1, r2).xline
        y = _1_0 - (x / r1)**2
        if y > EPS:
            y = r1 * sqrt(y)  # y == a / 2
        elif y < 0:
            raise ValueError(_invalid_)
        else:  # abutting
            y = _0_0
    elif o < 0:
        t = d if too_d is None else too_d
        raise ValueError(_too_(Fmt.distant(t)))
    else:  # abutting
        x, y = r1, _0_0

    u = m.unit()
    if sphere:  # sphere center and radius
        c = c1 if x < EPS  else (
            c2 if x > EPS1 else c1.plus(u.times(x)))
        t = _V3(c.x, c.y, c.z), Radius(y)

    elif y > 0:  # intersecting circles
        t = _xV3(c1, u, x, y), _xV3(c1, u, x, -y)
    else:  # abutting circles
        t = _xV3(c1, u, x, 0)
        t = t, t
    return t
Beispiel #13
0
def _RF(x, y, z=None):
    '''Symmetric integral of the first kind C{_RF}.

       @return: C{_RF(x, y, z)}.

       @see: U{C{_RF} definition<https://DLMF.NIST.gov/19.16.E1>}.
    '''
    if z is None:
        # Carlson, eqs 2.36 - 2.38
        a, b = sqrt(x), sqrt(y)
        if a < b:
            a, b = b, a
        while abs(a - b) > (_tolRG0 * a):  # max 4 trips
            b, a = sqrt(a * b), (a + b) * 0.5
        return PI / (a + b)

    # Carlson, eqs 2.2 - 2.7
    m = 1.0
    A = fsum_(x, y, z) / 3.0
    T = [A, x, y, z]
    Q = _Q(A, T, _tolRF)
    for _ in range(_TRIPS):
        if Q < abs(m * T[0]):  # max 6 trips
            break
        _, _, T = _rsT(T)
        m *= 4
    else:
        raise EllipticError('no %s convergence' % ('RF', ))

    m *= T[0]  # An
    x = (A - x) / m
    y = (A - y) / m
    z = -(x + y)

    e2 = x * y - z**2
    e3 = x * y * z
    # Polynomial is <https://DLMF.NIST.gov/19.36.E1>
    # (1 - E2/10 + E3/14 + E2^2/24 - 3*E2*E3/44
    #    - 5*E2^3/208 + 3*E3^2/104 + E2^2*E3/16)
    # convert to Horner form ...
    return fsum_(
        fsum_(6930 * e3, 15015 * e2**2, -16380 * e2, 17160) * e3,
        fsum_(10010 * e2, -5775 * e2**2, -24024) * e2,
        240240) / (240240 * sqrt(T[0]))
Beispiel #14
0
def _RJ(x, y, z, p):  # used by testElliptic.py
    '''Symmetric integral of the third kind C{_RJ(x, y, z, p)}.

       @return: C{_RJ(x, y, z, p)}.

       @see: U{C{_RJ} definition<https://DLMF.NIST.gov/19.16.E2>} and
             U{Carlson<https://ArXiv.org/pdf/math/9409227.pdf>}.
    '''
    def _xyzp(x, y, z, p):
        return (x + p) * (y + p) * (z + p)

    # Carlson, eqs 2.17 - 2.25
    m = m3 = _1_0
    S = Fsum()
    D = neg(_xyzp(x, y, z, -p))
    A = fsum_(x, y, z, 2 * p) * _0_2
    T = (A, x, y, z, p)
    Q = _Q(A, T, _TolRD)
    for _ in range(_TRIPS):
        if Q < abs(m * T[0]):  # max 7 trips
            break
        _, s, T = _rsT(T)
        d = _xyzp(*s)
        e = D / (m3 * d**2)
        S += _RC(1, 1 + e) / (m * d)
        m *= _4_0
        m3 *= 64
    else:
        raise _convergenceError(_RJ, x, y, z, p)

    m *= T[0]  # An
    x = (A - x) / m
    y = (A - y) / m
    z = (A - z) / m
    xyz = x * y * z
    p  = neg(x + y + z) * _0_5
    p2 = p**2

    e2 = fsum_(x * y, x * z, y * z, -3 * p2)
    return _horner(6 * S.fsum(), m * sqrt(T[0]), e2,
                   fsum_(xyz, 2 * p * e2, 4 * p * p2),
                   fsum_(xyz * 2, p * e2, 3 * p * p2) * p,
                   p2 * xyz)
Beispiel #15
0
def _RJ(x, y, z, p):
    '''Symmetric integral of the third kind C{_RJ}.

       @return: C{_RJ(x, y, z, p)}.

       @see: U{C{_RJ} definition<https://DLMF.NIST.gov/19.16.E2>}.
    '''
    def _xyzp(x, y, z, p):
        return (x + p) * (y + p) * (z + p)

    # Carlson, eqs 2.17 - 2.25
    m = m3 = 1.0
    S = Fsum()
    D = -_xyzp(x, y, z, -p)
    A = fsum_(x, y, z, 2 * p) * 0.2
    T = [A, x, y, z, p]
    Q = _Q(A, T, _tolRD)
    for _ in range(_TRIPS):
        if Q < abs(m * T[0]):  # max 7 trips
            break
        _, s, T = _rsT(T)
        d = _xyzp(*s)
        e = D / (m3 * d**2)
        S.fadd_(_RC(1, 1 + e) / (m * d))
        m *= 4
        m3 *= 64
    else:
        raise EllipticError('no %s convergence' % ('RJ', ))

    S *= 6
    m *= T[0]  # An
    x = (A - x) / m
    y = (A - y) / m
    z = (A - z) / m
    xyz = x * y * z
    p = -(x + y + z) * 0.5
    p2 = p**2

    e2 = fsum_(x * y, x * z, y * z, -3 * p2)
    return _Hf(S.fsum(), m * sqrt(T[0]), e2, fsum_(xyz, 2 * p * e2,
                                                   4 * p * p2),
               fsum_(xyz * 2, p * e2, 3 * p * p2) * p, p2 * xyz)
Beispiel #16
0
 def _fE(sn, cn, dn):
     sn2, cn2, dn2 = sn**2, cn**2, dn**2
     kp2, k2 = self.kp2, self.k2
     if k2 <= 0:  # Carlson, eq. 4.6, <https://DLMF.NIST.gov/19.25.E9>
         ei = _RF(cn2, dn2, 1) - k2 * sn2 * _RD_3(cn2, dn2, 1)
     elif kp2 >= 0:  # <https://DLMF.NIST.gov/19.25.E10>
         ei = fsum_(kp2 * _RF(cn2, dn2, 1),
                    kp2 * k2 * sn2 * _RD_3(cn2, 1, dn2),
                    k2 * abs(cn) / dn)
     else:  # <https://DLMF.NIST.gov/19.25.E11>
         ei = dn / abs(cn) - kp2 * sn2 * _RD_3(dn2, 1, cn2)
     return ei * abs(sn)
Beispiel #17
0
    def to3llh(self, datum=Datums.WGS84):
        '''Convert this (geocentric) Cartesian (x/y/z) point to
           (ellipsoidal, geodetic) lat-, longitude and height on
           the given datum.

           Uses B. R. Bowring’s formulation for μm precision in concise
           form: U{'The accuracy of geodetic latitude and height equations'
           <https://www.ResearchGate.net/publication/
           233668213_The_Accuracy_of_Geodetic_Latitude_and_Height_Equations>},
           Survey Review, Vol 28, 218, Oct 1985.

           See also Ralph M. Toms U{'An Efficient Algorithm for Geocentric to
           Geodetic Coordinate Conversion'<https://www.OSTI.gov/scitech/biblio/110235>},
           Sept 1995 and U{'An Improved Algorithm for Geocentric to Geodetic Coordinate
           Conversion'<https://www.OSTI.gov/scitech/servlets/purl/231228>},
           Apr 1996, from Lawrence Livermore National Laboratory.

           @keyword datum: Optional datum to use (L{Datum}).

           @return: A L{LatLon3Tuple}C{(lat, lon, height)}.
        '''
        E = datum.ellipsoid
        x, y, z = self.to3xyz()

        p = hypot(x, y)  # distance from minor axis
        r = hypot(p, z)  # polar radius

        if min(p, r) > EPS:
            # parametric latitude (Bowring eqn 17, replaced)
            t = (E.b * z) / (E.a * p) * (1 + E.e22 * E.b / r)
            c = 1 / hypot1(t)
            s = t * c

            # geodetic latitude (Bowring eqn 18)
            a = atan2(z + E.e22 * E.b * s**3, p - E.e2 * E.a * c**3)
            b = atan2(y, x)  # ... and longitude

            # height above ellipsoid (Bowring eqn 7)
            sa, ca = sincos2(a)
            #           r = E.a / E.e2s(sa)  # length of normal terminated by minor axis
            #           h = p * ca + z * sa - (E.a * E.a / r)
            h = fsum_(p * ca, z * sa, -E.a * E.e2s(sa))

            a, b = degrees90(a), degrees180(b)

        # see <https://GIS.StackExchange.com/questions/28446>
        elif p > EPS:  # latitude arbitrarily zero
            a, b, h = 0.0, degrees180(atan2(y, x)), p - E.a
        else:  # polar latitude, longitude arbitrarily zero
            a, b, h = copysign(90.0, z), 0.0, abs(z) - E.b
        return self._xnamed(LatLon3Tuple(a, b, h))
Beispiel #18
0
def _RG(x, y, z):  # used by testElliptic.py
    '''Symmetric integral of the second kind C{_RG(x, y, z)}.

       @return: C{_RG(x, y, z)}.

       @see: U{C{_RG} definition<https://DLMF.NIST.gov/19.16.E3>} and
             U{Carlson<https://ArXiv.org/pdf/math/9409227.pdf>}.
    '''
    if not z:
        y, z = z, y
    # Carlson, eq 1.7
    return fsum_(_RF(x, y, z) * z,
                 _RD_3(x, y, z) * (x - z) * (z - y),
                  sqrt(x * y / z)) * _0_5
Beispiel #19
0
def _RG(x, y, z):  # used by testElliptic.py
    '''Symmetric integral of the second kind C{_RG}.

       @return: C{_RG(x, y, z)}.

       @see: U{C{_RG} definition<https://DLMF.NIST.gov/19.16.E3>}
             and in Carlson, eq. 1.5.
    '''
    if not z:
        y, z = z, y
    # Carlson, eq 1.7
    return fsum_(_RF(x, y, z) * z,
                 _RD_3(x, y, z) * (x - z) * (z - y),
                  sqrt(x * y / z)) * 0.5
def _intersects2(c1, rad1, c2, rad2, radius=R_M,  # in .ellipsoidalBase._intersects2
                                     height=None, wrap=True, too_d=None,
                                     LatLon=LatLon, **LatLon_kwds):
    # (INTERNAL) Intersect two spherical circles, see L{intersections2}
    # above, separated to allow callers to embellish any exceptions

    def _dest3(bearing, h):
        a, b = _destination2(a1, b1, r1, bearing)
        return _latlon3(degrees90(a), degrees180(b), h,
                        intersections2, LatLon, **LatLon_kwds)

    r1, r2, f = _rads3(rad1, rad2, radius)
    if f:  # swapped
        c1, c2 = c2, c1  # PYCHOK swap

    a1, b1 = c1.philam
    a2, b2 = c2.philam

    db, b2 = unrollPI(b1, b2, wrap=wrap)
    d = vincentys_(a2, a1, db)  # radians
    if d < max(r1 - r2, EPS):
        raise ValueError(_near_concentric_)

    x = fsum_(r1, r2, -d)  # overlap
    if x > EPS:
        sd, cd, sr1, cr1, _, cr2 = sincos2(d, r1, r2)
        x = sd * sr1
        if abs(x) < EPS:
            raise ValueError(_invalid_)
        x = acos1((cr2 - cd * cr1) / x)  # 0 <= x <= PI

    elif x < 0:
        t = (d * radius) if too_d is None else too_d
        raise ValueError(_too_distant_fmt_ % (t,))

    if height is None:  # "radical height"
        f = _radical2(d, r1, r2).ratio
        h = Height(favg(c1.height, c2.height, f=f))
    else:
        h = Height(height)

    b = bearing_(a1, b1, a2, b2, final=False, wrap=wrap)
    if x < _EPS_I2:  # externally ...
        r = _dest3(b, h)
    elif x > _PI_EPS_I2:  # internally ...
        r = _dest3(b + PI, h)
    else:
        return _dest3(b + x, h), _dest3(b - x, h)
    return r, r  # ... abutting circles
Beispiel #21
0
 def intersect(self, p1, p2, edge):  # compute intersection
     # of polygon edge p1 to p2 and the current clip edge,
     # where p1 and p2 are known to NOT be located on the
     # same side of or on the current clip edge
     # <https://StackOverflow.com/questions/563198/
     #       how-do-you-detect-where-two-line-segments-intersect>
     fy = float(p2.lat - p1.lat)
     fx = float(p2.lon - p1.lon)
     fp = fy * self._dx - fx * self._dy
     if abs(fp) < EPS:
         raise AssertionError('clipSH.intersect')
     h = fsum_(self._xy, -p1.lat * self._dx, p1.lon * self._dy) / fp
     y = p1.lat + h * fy
     x = p1.lon + h * fx
     return _LLi_(y, x, p1.classof, edge)
Beispiel #22
0
    def toNvector(self,
                  Nvector=None,
                  datum=None,
                  **kwds):  # PYCHOK Datums.WGS84
        '''Convert this cartesian to C{n-vector} components.

           @keyword Nvector: Optional (sub-)class to return the
                             C{n-vector} components (C{Nvector})
                             or C{None}.
           @keyword datum: Optional datum (L{Datum}) overriding this
                           cartesian's datum.
           @keyword kwds: Optional, additional B{C{Nvector}} keyword
                          arguments, ignored if C{B{Nvector}=None}.

           @return: Unit vector B{C{Nvector}} or a L{Vector4Tuple}C{(x,
                    y, z, h)} if B{C{Nvector}=None}.

           @raise ValueError: The B{C{Cartesian}} at origin.
        '''
        d = datum or self.datum
        r = self._v4t
        if r is None or self.datum != d:
            E = d.ellipsoid
            x, y, z = self.to3xyz()

            # Kenneth Gade eqn 23
            p = hypot2(x, y) * E.a2_
            q = (z**2 * E.e12) * E.a2_
            r = fsum_(p, q, -E.e4) / 6
            s = (p * q * E.e4) / (4 * r**3)
            t = cbrt(fsum_(1, s, sqrt(s * (2 + s))))

            u = r * fsum_(1, t, 1 / t)
            v = sqrt(u**2 + E.e4 * q)
            w = E.e2 * fsum_(u, v, -q) / (2 * v)

            k = sqrt(fsum_(u, v, w**2)) - w
            if abs(k) < EPS:
                raise ValueError('%s: %r' % ('origin', self))
            e = k / (k + E.e2)

            t = hypot(e * hypot(x, y), z)
            if t < EPS:
                raise ValueError('%s: %r' % ('origin', self))
            h = fsum_(k, E.e2, -1) / k * t

            s = e / t
            r = Vector4Tuple(x * s, y * s, z / t, h)
            self._v4t = r if d == self.datum else None

        if Nvector is not None:
            r = Nvector(r.x, r.y, r.z, h=r.h, datum=d, **kwds)
        return self._xnamed(r)
Beispiel #23
0
    def rhumbMidpointTo(self, other, height=None):
        '''Return the (loxodromic) midpoint between this and
           an other point.

           @arg other: The other point (spherical LatLon).
           @kwarg height: Optional height, overriding the mean height
                          (C{meter}).

           @return: The midpoint (spherical C{LatLon}).

           @raise TypeError: The I{other} point is not spherical.

           @raise ValueError: Invalid B{C{height}}.

           @example:

           >>> p = LatLon(51.127, 1.338)
           >>> q = LatLon(50.964, 1.853)
           >>> m = p.rhumb_midpointTo(q)
           >>> m.toStr()  # '51.0455°N, 001.5957°E'
        '''
        self.others(other)

        # see <https://MathForum.org/library/drmath/view/51822.html>
        a1, b1 = self.philam
        a2, b2 = other.philam
        if abs(b2 - b1) > PI:
            b1 += PI2  # crossing anti-meridian

        a3 = favg(a1, a2)
        b3 = favg(b1, b2)

        f1 = tanPI_2_2(a1)
        if abs(f1) > EPS:
            f2 = tanPI_2_2(a2)
            f = f2 / f1
            if abs(f) > EPS:
                f = log(f)
                if abs(f) > EPS:
                    f3 = tanPI_2_2(a3)
                    b3 = fsum_(b1 * log(f2), -b2 * log(f1),
                               (b2 - b1) * log(f3)) / f

        h = self._havg(other) if height is None else Height(height)
        return self.classof(degrees90(a3), degrees180(b3), height=h)
    def toNvector(self, datum=Datums.WGS84):
        '''Convert this cartesian to an (ellipsoidal) n-vector.

           @keyword datum: Optional datum to use (L{Datum}).

           @return: The ellipsoidal n-vector (L{Nvector}).

           @raise ValueError: The B{C{Cartesian}} at origin.

           @example:

           >>> from ellipsoidalNvector import LatLon
           >>> c = Cartesian(3980581, 97, 4966825)
           >>> n = c.toNvector()  # (0.62282, 0.000002, 0.78237, +0.24)
        '''
        if self._Nv is None or datum != self._Nv.datum:
            E = datum.ellipsoid
            x, y, z = self.to3xyz()

            # Kenneth Gade eqn 23
            p = (x**2 + y**2) * E.a2_
            q = (z**2 * E.e12) * E.a2_
            r = fsum_(p, q, -E.e4) / 6
            s = (p * q * E.e4) / (4 * r**3)
            t = cbrt(fsum_(1, s, sqrt(s * (2 + s))))

            u = r * fsum_(1, t, 1 / t)
            v = sqrt(u**2 + E.e4 * q)
            w = E.e2 * fsum_(u, v, -q) / (2 * v)

            k = sqrt(fsum_(u, v, w**2)) - w
            if abs(k) < EPS:
                raise ValueError('%s: %r' % ('origin', self))
            e = k / (k + E.e2)
            d = e * hypot(x, y)

            t = hypot(d, z)
            if t < EPS:
                raise ValueError('%s: %r' % ('origin', self))
            h = fsum_(k, E.e2, -1) / k * t

            s = e / t
            self._Nv = Nvector(x * s,
                               y * s,
                               z / t,
                               h=h,
                               datum=datum,
                               name=self.name)
        return self._Nv
Beispiel #25
0
def horizon(height, radius=R_M, refraction=False):
    '''Determine the distance to the horizon from a given altitude
       above the (spherical) earth.

       @param height: Altitude (C{meter} or same units as B{C{radius}}).
       @keyword radius: Optional mean earth radius (C{meter}).
       @keyword refraction: Consider atmospheric refraction (C{bool}).

       @return: Distance (C{meter}, same units as B{C{height}} and B{C{radius}}).

       @raise ValueError: Invalid B{C{height}} or B{C{radius}}.

       @see: U{Distance to horizon<https://www.EdWilliams.org/avform.htm#Horizon>}.
    '''
    if min(height, radius) < 0:
        raise ValueError('%s%r' % ('horizon', (height, radius)))

    if refraction:
        d2 = 2.415750694528 * height * radius  # 2.0 / 0.8279
    else:
        d2 = height * fsum_(radius, radius, height)
    return sqrt(d2)
Beispiel #26
0
def horizon(height, radius=R_M, refraction=False):
    '''Determine the distance to the horizon from a given altitude
       above the (spherical) earth.

       @arg height: Altitude (C{meter} or same units as B{C{radius}}).
       @kwarg radius: Optional mean earth radius (C{meter}).
       @kwarg refraction: Consider atmospheric refraction (C{bool}).

       @return: Distance (C{meter}, same units as B{C{height}} and B{C{radius}}).

       @raise ValueError: Invalid B{C{height}} or B{C{radius}}.

       @see: U{Distance to horizon<https://www.EdWilliams.org/avform.htm#Horizon>}.
    '''
    h, r = Height(height), Radius(radius)
    if min(h, r) < 0:
        raise InvalidError(height=height, radius=radius)

    if refraction:
        d2 = 2.415750694528 * h * r  # 2.0 / 0.8279
    else:
        d2 = h * fsum_(r, r, h)
    return sqrt(d2)
Beispiel #27
0
    def _scaled(self, tau, d2, snu, cnu, dnu, snv, cnv, dnv):
        '''(INTERNAL) C{scaled}.

           @note: Argument B{C{d2}} is C{_mu * cnu**2 + _mv * cnv**2}
                  from C{._sigma3} or C{._zeta3}.

           @return: 2-Tuple C{(convergence, scale)}.

           @see: C{void TMExact::Scale(real tau, real /*lam*/,
                                       real snu, real cnu, real dnu,
                                       real snv, real cnv, real dnv,
                                       real &gamma, real &k)}.
        '''
        mu, mv = self._mu, self._mv
        cnudnv = cnu * dnv
        # Lee 55.12 -- negated for our sign convention.  g gives
        # the bearing (clockwise from true north) of grid north
        g = atan2(mv * cnv * snv * snu, cnudnv * dnu)
        # Lee 55.13 with nu given by Lee 9.1 -- in sqrt change
        # the numerator from
        #
        #  (1 - snu^2 * dnv^2) to (_mv * snv^2 + cnu^2 * dnv^2)
        #
        # to maintain accuracy near phi = 90 and change the
        # denomintor from
        #  (dnu^2 + dnv^2 - 1) to (_mu * cnu^2 + _mv * cnv^2)
        #
        # to maintain accuracy near phi = 0, lam = 90 * (1 - e).
        # Similarly rewrite sqrt term in 9.1 as
        #
        #  _mv + _mu * c^2 instead of 1 - _mu * sin(phi)^2
        q2 = (mv * snv**2 + cnudnv**2) / d2
        # originally: sec2 = 1 + tau**2  # sec(phi)^2
        # k = sqrt(mv + mu / sec2) * sqrt(sec2) * sqrt(q2)
        #   = sqrt(mv + mv * tau**2 + mu) * sqrt(q2)
        k = sqrt(fsum_(mu, mv, mv * tau**2)) * sqrt(q2)
        return degrees(g), k * self._k0
Beispiel #28
0
def toOsgr(latlon,
           lon=None,
           datum=Datums.WGS84,
           Osgr=Osgr,
           name=NN,
           **Osgr_kwds):
    '''Convert a lat-/longitude point to an OSGR coordinate.

       @arg latlon: Latitude (C{degrees}) or an (ellipsoidal) geodetic
                    C{LatLon} point.
       @kwarg lon: Optional longitude in degrees (scalar or C{None}).
       @kwarg datum: Optional datum to convert B{C{lat, lon}} from
                     (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or
                     L{a_f2Tuple}).
       @kwarg Osgr: Optional class to return the OSGR coordinate
                    (L{Osgr}) or C{None}.
       @kwarg name: Optional B{C{Osgr}} name (C{str}).
       @kwarg Osgr_kwds: Optional, additional B{C{Osgr}} keyword
                         arguments, ignored if B{C{Osgr=None}}.

       @return: The OSGR coordinate (B{C{Osgr}}) or an
                L{EasNor2Tuple}C{(easting, northing)} if B{C{Osgr}}
                is C{None}.

       @raise OSGRError: Invalid B{C{latlon}} or B{C{lon}}.

       @raise TypeError: Non-ellipsoidal B{C{latlon}} or invalid
                         B{C{datum}} or conversion failed.

       @example:

       >>> p = LatLon(52.65798, 1.71605)
       >>> r = toOsgr(p)  # TG 51409 13177
       >>> # for conversion of (historical) OSGB36 lat-/longitude:
       >>> r = toOsgr(52.65757, 1.71791, datum=Datums.OSGB36)
    '''
    if not isinstance(latlon, _LLEB):
        # XXX fix failing _LLEB.convertDatum()
        latlon = _LLEB(*parseDMS2(latlon, lon), datum=datum)
    elif lon is not None:
        raise OSGRError(lon=lon, txt='not %s' % (None, ))
    elif not name:  # use latlon.name
        name = nameof(latlon)

    # if necessary, convert to OSGB36 first
    ll = _ll2datum(latlon, _Datums_OSGB36, _latlon_)
    try:
        a, b = ll.philam
    except AttributeError:
        a, b = map1(radians, ll.lat, ll.lon)
    sa, ca = sincos2(a)

    E = _Datums_OSGB36.ellipsoid

    s = E.e2s2(sa)  # r, v = E.roc2_(sa, _F0); r = v / r
    v = E.a * _F0 / sqrt(s)  # nu
    r = s / E.e12  # nu / rho == v / (v * E.e12 / s) == s / E.e12

    x2 = r - 1  # η2
    ta = tan(a)

    ca3, ca5 = fpowers(ca, 5, 3)  # PYCHOK false!
    ta2, ta4 = fpowers(ta, 4, 2)  # PYCHOK false!

    vsa = v * sa
    I4 = (E.b * _F0 * _M(E.Mabcd, a) + _N0, (vsa / 2) * ca,
          (vsa / 24) * ca3 * fsum_(5, -ta2, 9 * x2),
          (vsa / 720) * ca5 * fsum_(61, ta4, -58 * ta2))

    V4 = (_E0, (v * ca), (v / 6) * ca3 * (r - ta2), (v / 120) * ca5 * fdot(
        (-18, 1, 14, -58), ta2, 5 + ta4, x2, ta2 * x2))

    d, d2, d3, d4, d5, d6 = fpowers(b - _B0, 6)  # PYCHOK false!
    n = fdot(I4, 1, d2, d4, d6)
    e = fdot(V4, 1, d, d3, d5)

    if Osgr is None:
        r = _EasNor2Tuple(e, n)
    else:
        r = Osgr(e, n, datum=_Datums_OSGB36, **Osgr_kwds)
        if lon is None and isinstance(latlon, _LLEB):
            r._latlon = latlon  # XXX weakref(latlon)?
    return _xnamed(r, name)
Beispiel #29
0
    def toLatLon(self, LatLon=None, datum=Datums.WGS84):
        '''Convert this OSGR coordinate to an (ellipsoidal) geodetic
           point.

           While OS grid references are based on the OSGB36 datum, the
           I{Ordnance Survey} have deprecated the use of OSGB36 for
           lat-/longitude coordinates (in favour of WGS84). Hence, this
           method returns WGS84 by default with OSGB36 as an option,
           U{see<https://www.OrdnanceSurvey.co.UK/blog/2014/12/2>}.

           I{Note formulation implemented here due to Thomas, Redfearn,
           etc. is as published by OS, but is inferior to Krüger as
           used by e.g. Karney 2011.}

           @kwarg LatLon: Optional ellipsoidal class to return the
                          geodetic point (C{LatLon}) or C{None}.
           @kwarg datum: Optional datum to convert to (L{Datum},
                         L{Ellipsoid}, L{Ellipsoid2}, L{Ellipsoid2}
                         or L{a_f2Tuple}).

           @return: The geodetic point (B{C{LatLon}}) or a
                    L{LatLonDatum3Tuple}C{(lat, lon, datum)}
                    if B{C{LatLon}} is C{None}.

           @raise OSGRError: No convergence.

           @raise TypeError: If B{C{LatLon}} is not ellipsoidal or
                             B{C{datum}} is invalid or conversion failed.

           @example:

           >>> from pygeodesy import ellipsoidalVincenty as eV
           >>> g = Osgr(651409.903, 313177.270)
           >>> p = g.toLatLon(eV.LatLon)  # 52°39′28.723″N, 001°42′57.787″E
           >>> # to obtain (historical) OSGB36 lat-/longitude point
           >>> p = g.toLatLon(eV.LatLon, datum=Datums.OSGB36)  # 52°39′27.253″N, 001°43′04.518″E
        '''
        if self._latlon:
            return self._latlon3(LatLon, datum)

        E = self.datum.ellipsoid  # _Datums_OSGB36.ellipsoid, Airy130
        a_F0 = E.a * _F0
        b_F0 = E.b * _F0

        e, n = self.easting, self.northing
        n_N0 = n - _N0

        a, m = _A0, n_N0
        sa = Fsum(a)
        for self._iteration in range(1, _TRIPS):
            a = sa.fsum_(m / a_F0)
            m = n_N0 - b_F0 * _M(E.Mabcd, a)  # meridional arc
            if abs(m) < _10um:
                break
        else:
            t = _dot_(_item_ps(self.classname, self.toStr(prec=-3)),
                      self.toLatLon.__name__)
            raise OSGRError(_no_convergence_, txt=t)
        sa, ca = sincos2(a)

        s = E.e2s2(sa)  # r, v = E.roc2_(sa, _F0)
        v = a_F0 / sqrt(s)  # nu
        r = v * E.e12 / s  # rho = a_F0 * E.e12 / pow(s, 1.5) == a_F0 * E.e12 / (s * sqrt(s))

        vr = v / r  # == s / E.e12
        x2 = vr - 1  # η2
        ta = tan(a)

        v3, v5, v7 = fpowers(v, 7, 3)  # PYCHOK false!
        ta2, ta4, ta6 = fpowers(ta**2, 3)  # PYCHOK false!

        tar = ta / r
        V4 = (a, tar / (2 * v), tar / (24 * v3) * fdot(
            (1, 3, -9), 5 + x2, ta2, ta2 * x2), tar / (720 * v5) * fdot(
                (61, 90, 45), 1, ta2, ta4))

        csa = 1.0 / ca
        X5 = (_B0, csa / v, csa / (6 * v3) * fsum_(vr, ta2, ta2),
              csa / (120 * v5) * fdot(
                  (5, 28, 24), 1, ta2, ta4), csa / (5040 * v7) * fdot(
                      (61, 662, 1320, 720), 1, ta2, ta4, ta6))

        d, d2, d3, d4, d5, d6, d7 = fpowers(e - _E0, 7)  # PYCHOK false!
        a = fdot(V4, 1, -d2, d4, -d6)
        b = fdot(X5, 1, d, -d3, d5, -d7)

        r = _LLEB(degrees90(a),
                  degrees180(b),
                  datum=self.datum,
                  name=self.name)
        r._iteration = self._iteration  # only ellipsoidal LatLon
        self._latlon = r
        return self._latlon3(LatLon, datum)
Beispiel #30
0
    def reverse(self, x, y, lon0=0, name=NN, LatLon=None, **LatLon_kwds):
        '''Convert an east- and northing location to geodetic lat- and longitude.

           @arg x: Easting of the location (C{meter}).
           @arg y: Northing of the location (C{meter}).
           @kwarg lon0: Optional central meridian longitude (C{degrees}).
           @kwarg name: Optional name for the location (C{str}).
           @kwarg LatLon: Class to use (C{LatLon}) or C{None}.
           @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
                               arguments, ignored if C{B{LatLon}=None}.

           @return: The geodetic (C{LatLon}) or if B{C{LatLon}} is C{None} an
                    L{Albers7Tuple}C{(x, y, lat, lon, gamma, scale, datum)}.

           @note: The origin latitude is returned by C{property lat0}.  No
                  false easting or northing is added.  The returned value of
                  C{lon} is in the range C{[-180..180] degrees} and C{lat}
                  is in the range C{[-90..90] degrees}.  If the given
                  B{C{x}} or B{C{y}} point is outside the valid projected
                  space the nearest pole is returned.
        '''
        x = Meter(x=x)
        y = Meter(y=y)

        k0 = self._k0
        n0 = self._n0
        k0n0 = self._k0n0
        nrho0 = self._nrho0
        txi0 = self._txi0

        y_ = self._sign * y
        nx = k0n0 * x
        ny = k0n0 * y_
        y1 = nrho0 - ny

        drho = den = nrho0 + hypot(nx,
                                   y1)  # 0 implies origin with polar aspect
        if den:
            drho = fsum_(x * nx, -_2_0 * y_ * nrho0, y_ * ny) * k0 / den
        # dsxia = scxi0 * dsxi
        dsxia = -self._scxi0 * (_2_0 * nrho0 + n0 * drho) * drho / self._qZa2
        t = _1_0 - dsxia * (_2_0 * txi0 + dsxia)
        txi = (txi0 + dsxia) / (sqrt(t) if t > _EPS__4 else _EPS__2)

        ta = self._tanf(txi)
        lat = atand(ta * self._sign)

        th = atan2(nx, y1)
        lon = degrees(th / self._k02n0 if n0 else x / (y1 * k0))
        if lon0:
            lon += _norm180(lon0)
        lon = _norm180(lon)

        if LatLon is None:
            g = degrees360(self._sign * th)
            if den:
                k0 = self._azik(nrho0 + n0 * drho, ta)
            r = Albers7Tuple(x, y, lat, lon, g, k0, self.datum)
        else:
            kwds = _xkwds(LatLon_kwds, datum=self.datum)
            r = LatLon(lat, lon, **kwds)
        return self._xnamed(r, name=name)