예제 #1
0
    def _zeta3(self, snu, cnu, dnu, snv, cnv, dnv):
        '''
           @return: 3-Tuple C{(taup, lambda, d2)}.

           @see: C{void TMExact::zeta(real /*u*/, real snu, real cnu, real dnu,
                                      real /*v*/, real snv, real cnv, real dnv,
                                      real &taup, real &lam)}
        '''
        e = self._e
        # Lee 54.17 but write
        # atanh(snu * dnv)      = asinh(snu * dnv / sqrt(cnu^2 + _mv * snu^2 * snv^2))
        # atanh(_e * snu / dnv) = asinh(_e * snu / sqrt(_mu * cnu^2 + _mv * cnv^2))
        d1 = cnu**2 + self._mv * (snu * snv)**2
        d2 = self._mu * cnu**2 + self._mv * cnv**2
        # Overflow value s.t. atan(overflow) = pi/2
        t1 = t2 = copysign(_OVERFLOW, snu)
        if d1 > 0:
            t1 = snu * dnv / sqrt(d1)
        if d2 > 0:
            t2 = sinh(e * asinh(e * snu / sqrt(d2)))
        # psi = asinh(t1) - asinh(t2)
        # taup = sinh(psi)
        taup = t1 * hypot1(t2) - t2 * hypot1(t1)
        lam  = (atan2(    dnu * snv, cnu * cnv) - e *
                atan2(e * cnu * snv, dnu * cnv)) if (d1 > 0 and
                                                     d2 > 0) else 0
        return taup, lam, d2
예제 #2
0
def _r3(a, f):
    '''(INTERNAL) Reduced cos, sin, tan.
    '''
    t = (1 - f) * tan(radians(a))
    c = 1 / hypot1(t)
    s = t * c
    return c, s, t
예제 #3
0
    def _zetaInv(self, taup, lam):
        '''Invert C{zeta} using Newton's method.

           @return: 2-Tuple C{(u, v)}.

           @see: C{void TMExact::zetainv(real taup, real lam,
                                         real &u, real &v)}.

           @raise EllipticError: No convergence.
        '''
        psi = asinh(taup)
        sca = 1.0 / hypot1(taup)
        u, v, trip = self._zetaInv0(psi, lam)
        if not trip:
            stol2 = _TOL_10 / max(psi, 1.0)**2
            U, V = Fsum(u), Fsum(v)
            # min iterations = 2, max = 6, mean = 4.0
            for _ in range(self._trips_):  # GEOGRAPHICLIB_PANIC
                snu, cnu, dnu = self._Eu.sncndn(u)
                snv, cnv, dnv = self._Ev.sncndn(v)
                T, L, _ = self._zeta3(  snu, cnu, dnu, snv, cnv, dnv)
                dw, dv  = self._zetaDwd(snu, cnu, dnu, snv, cnv, dnv)
                T  = (taup - T) * sca
                L -= lam
                u, du = U.fsum2_(T * dw,  L * dv)
                v, dv = V.fsum2_(T * dv, -L * dw)
                if trip:
                    break
                trip = (du**2 + dv**2) < stol2
            else:
                raise EllipticError('no %s convergence' % ('zetaInv',))
        return u, v
예제 #4
0
파일: etm.py 프로젝트: itzmejawad/PyGeodesy
    def _zetaInv(self, taup, lam):
        '''(INTERNAL) Invert C{zeta} using Newton's method.

           @return: 2-Tuple C{(u, v)}.

           @see: C{void TMExact::zetainv(real taup, real lam,
                                         real &u, real &v)}.

           @raise EllipticError: No convergence.
        '''
        psi = asinh(taup)
        sca = 1.0 / hypot1(taup)
        u, v, trip = self._zetaInv0(psi, lam)
        if trip:
            self._iteration = 0
        else:
            stol2 = _TOL_10 / max(psi**2, 1.0)
            U, V = Fsum(u), Fsum(v)
            # min iterations = 2, max = 6, mean = 4.0
            for self._iteration in range(1, _TRIPS):  # GEOGRAPHICLIB_PANIC
                sncndn6 = self._sncndn6(u, v)
                T, L, _ = self._zeta3(  *sncndn6)
                dw, dv  = self._zetaDwd(*sncndn6)
                T  = (taup - T) * sca
                L -= lam
                u, du = U.fsum2_(T * dw,  L * dv)
                v, dv = V.fsum2_(T * dv, -L * dw)
                if trip:
                    break
                trip = hypot2(du, dv) < stol2
            else:
                t = unstr(self._zetaInv.__name__, taup, lam)
                raise EllipticError(_no_convergence_, txt=t)
        return u, v
예제 #5
0
 def _cstxif3(self, ta):
     '''(INTERNAL) Get 3-tuple C{(cos, sin, tan)} of M{xi(ta)}.
     '''
     t = self._txif(ta)
     c = _1_0 / hypot1(t)
     s = c * t
     return c, s, t
예제 #6
0
    def sncndn(self, x):  # PYCHOK x not used?
        '''The Jacobi elliptic function.

           @arg x: The argument (C{float}).

           @return: An L{Elliptic3Tuple}C{(sn, cn, dn)} with
                    C{*n}C{(}B{C{x}}C{, k}C{)}.

           @raise EllipticError: No convergence.
        '''
        # Bulirsch's sncndn routine, p 89.
        mc = self.kp2
        if mc:  # never negative ...
            if mc < 0:  # PYCHOK no cover
                d  = _1_0 - mc
                mc = neg(mc / d)  # /= -d chokes PyChecker
                d  = sqrt(d)
                x *= d
            else:
                d = 0
            a, c, mn = _1_0, 0, []
            for self._iteration in range(1, _TRIPS):  # GEOGRAPHICLIB_PANIC
                # This converges quadratically, max 6 trips
                mc = sqrt(mc)
                mn.append((a, mc))
                c = (a + mc) * _0_5
                if abs(a - mc) <= (_TolJAC * a):
                    break
                mc *= a
                a = c
            else:
                raise _convergenceError(self.sncndn, x)
            x *= c
            dn = 1
            sn, cn = sincos2(x)
            if sn:
                a = cn / sn
                c *= a
                while mn:
                    m, n = mn.pop()
                    a *= c
                    c *= dn
                    dn = (n + a) / (m + a)
                    a = c / m
                sn = copysign(_1_0 / hypot1(c), sn)
                cn = c * sn
                if d:  # PYCHOK no cover
                    cn, dn = dn, cn
                    sn = sn / d  # /= d chokes PyChecker
        else:
            self._iteration = 0
            sn = tanh(x)
            cn = dn = _1_0 / cosh(x)

        r = Elliptic3Tuple(sn, cn, dn)
        r._iteration = self._iteration
        return r
예제 #7
0
    def forward(self, lat, lon, lon0=0, name=NN):
        '''Convert a geodetic location to east- and northing.

           @arg lat: Latitude of the location (C{degrees}).
           @arg lon: Longitude of the location (C{degrees}).
           @kwarg lon0: Optional central meridian longitude (C{degrees}).
           @kwarg name: Optional name for the location (C{str}).

           @return: 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 value of B{C{lat}}
                  should be in the range C{[-90..90] degrees}.  The returned
                  values C{x} and C{y} will be large but finite for points
                  projecting to infinity, i.e. one or both of the poles.
        '''
        E = self.datum.ellipsoid
        s = self._sign

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

        sa, ca = sincos2d(_Lat_(lat) * s)
        ca = max(_EPSX, ca)
        ta = sa / ca

        _, sxi, txi = self._cstxif3(ta)
        dq = self._qZ * _Dsn(txi, txi0, sxi, self._sxi0) * (txi - txi0)
        drho = -E.a * dq / (sqrt(self._m02 - n0 * dq) + self._m0)

        lon = _Lon_(lon)
        if lon0:
            lon, _ = _diff182(_Lon_(lon0, name=_lon0_), lon)
        b = radians(lon)

        th = self._k02n0 * b
        sth, cth = sincos2(th)  # XXX sin, cos
        if n0:
            x = sth / n0
            y = (1 - cth if cth < 0 else sth**2 / (1 + cth)) / n0
        else:
            x = self._k02 * b
            y = 0
        t = nrho0 + n0 * drho
        x = t * x / k0
        y = s * (nrho0 * y - drho * cth) / k0

        g = degrees360(s * th)
        if t:
            k0 *= t * hypot1(E.b_a * ta) / E.a
        t = Albers7Tuple(x, y, lat, lon, g, k0, self.datum)
        return _xnamed(t, name or self.name)
예제 #8
0
    def sncndn(self, x):  # PYCHOK x not used?
        '''The Jacobi elliptic function.

           @param x: The argument (C{float}).

           @return: An L{Elliptic3Tuple}C{(sn, cn, dn)} with
                    C{sn(B{x}, k), cn(B{x}, k), dn(B{x}, k)}.

           @raise EllipticError: No convergence.
        '''
        # Bulirsch's sncndn routine, p 89.
        mc = self.kp2
        if mc:  # never negative ...
            if mc < 0:  # PYCHOK no cover
                d = 1.0 - mc
                mc = -mc / d  # /= -d chokes PyChecker
                d = sqrt(d)
                x *= d
            else:
                d = 0
            a, c, mn = 1.0, 0, []
            for _ in range(self._trips_):  # GEOGRAPHICLIB_PANIC
                # This converges quadratically, max 6 trips
                mc = sqrt(mc)
                mn.append((a, mc))
                c = (a + mc) * 0.5
                if abs(a - mc) <= (_TolJAC * a):
                    break
                mc *= a
                a = c
            else:
                raise EllipticError('no %s%r convergence' % ('sncndn', (x,)))
            x *= c
            dn = 1
            sn, cn = sincos2(x)
            if sn:
                a = cn / sn
                c *= a
                while mn:
                    m, n = mn.pop()
                    a *= c
                    c *= dn
                    dn = (n + a) / (m + a)
                    a = c / m
                sn = copysign(1.0 / hypot1(c), sn)
                cn = c * sn
                if d:  # PYCHOK no cover
                    cn, dn = dn, cn
                    sn = sn / d  # /= d chokes PyChecker
        else:
            sn = tanh(x)
            cn = dn = 1.0 / cosh(x)
        return Elliptic3Tuple(sn, cn, dn)
예제 #9
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))
예제 #10
0
    def sncndn(self, x):
        '''The Jacobi elliptic function.

           @param x: The argument (C{float}).

           @return: 3-Tuple C{(sn(x, k), cn(x, k), dn(x, k))}.
        '''
        # Bulirsch's sncndn routine, p 89.
        mc = self._kp2
        if mc:
            if mc < 0:
                d = 1.0 - mc
                mc /= -d
                d = sqrt(d)
                x *= d
            else:
                d = 0
            a, c, mn = 1.0, 0, []
            for _ in range(self._trips_):  # GEOGRAPHICLIB_PANIC
                # This converges quadratically, max 6 trips
                mc = sqrt(mc)
                mn.append((a, mc))
                c = (a + mc) * 0.5
                if not abs(a - mc) > (_tolJAC * a):
                    break
                mc *= a
                a = c
            else:
                raise EllipticError('no %s convergence' % ('sncndn', ))
            x *= c
            dn = 1
            sn, cn = sincos2(x)
            if sn:
                a = cn / sn
                c *= a
                while mn:
                    m, n = mn.pop()
                    a *= c
                    c *= dn
                    dn = (n + a) / (m + a)
                    a = c / m
                sn = copysign(1.0 / hypot1(c), sn)
                cn = c * sn
                if d:
                    cn, dn = dn, cn
                    sn /= d
        else:
            sn = tanh(x)
            cn = dn = 1.0 / cosh(x)
        return sn, cn, dn
예제 #11
0
def _scale(E, rho, tau):
    # compute the point scale factor, ala Karney
    t = hypot1(tau)
    return (rho / E.a) * t * sqrt(E.e12 + E.e2 / t**2)
예제 #12
0
def toUps8(latlon, lon=None, datum=None, Ups=Ups, pole='',
                             falsed=True, strict=True, name=''):
    '''Convert a lat-/longitude point to a UPS coordinate.

       @param latlon: Latitude (C{degrees}) or an (ellipsoidal)
                      geodetic C{LatLon} point.
       @keyword lon: Optional longitude (C{degrees}) or C{None}
                     if B{C{latlon}} is a C{LatLon}.
       @keyword datum: Optional datum for this UPS coordinate,
                       overriding B{C{latlon}}'s datum (C{Datum}).
       @keyword Ups: Optional (sub-)class to return the UPS
                     coordinate (L{Ups}) or C{None}.
       @keyword pole: Optional top/center of (stereographic) projection
                      (C{str}, C{'N[orth]'} or C{'S[outh]'}).
       @keyword falsed: False both easting and northing (C{bool}).
       @keyword strict: Restrict B{C{lat}} to UPS ranges (C{bool}).
       @keyword name: Optional B{C{Ups}} name (C{str}).

       @return: The UPS coordinate (B{C{Ups}}) or a
                L{UtmUps8Tuple}C{(zone, hemipole, easting, northing,
                band, datum, convergence, scale)} if B{C{Ups}} is
                C{None}.  The C{hemipole} is the C{'N'|'S'} pole,
                the UPS projection top/center.

       @raise RangeError: If B{C{strict}} and B{C{lat}} outside the
                          valid UPS bands or if B{C{lat}} or B{C{lon}}
                          outside the valid range and L{rangerrors}
                          set to C{True}.

       @raise TypeError: If B{C{latlon}} is not ellipsoidal.

       @raise ValueError: If B{C{lon}} value is missing or if B{C{latlon}}
                          is invalid.

       @see: Karney's C++ class U{UPS
             <https://GeographicLib.SourceForge.io/html/classGeographicLib_1_1UPS.html>}.
    '''
    lat, lon, d, name = _to4lldn(latlon, lon, datum, name)
    z, B, p, lat, lon = upsZoneBand5(lat, lon, strict=strict)  # PYCHOK UtmUpsLatLon5Tuple

    E = d.ellipsoid

    p = str(pole or p)[:1].upper()
    N = p == 'N'  # is north

    a = lat if N else -lat
    A = abs(a - 90) < _TOL  # at pole

    t = tan(radians(a))
    T = E.es_taupf(t)
    r = hypot1(T) + abs(T)
    if T >= 0:
        r = 0 if A else 1 / r

    k0 = getattr(Ups, '_scale0', _K0)  # Ups is class or None
    r *= 2 * k0 * E.a / E.es_c

    k = k0 if A else _scale(E, r, t)
    c = lon  # [-180, 180) from .upsZoneBand5
    x, y = sincos2d(c)
    x *= r
    y *= r
    if N:
        y = -y
    else:
        c = -c

    if falsed:
        x += _Falsing
        y += _Falsing

    if Ups is None:
        r = UtmUps8Tuple(z, p, x, y, B, d, c, k)
    else:
        if z != _UPS_ZONE and not strict:
            z = _UPS_ZONE  # ignore UTM zone
        r = Ups(z, p, x, y, band=B, datum=d, falsed=falsed,
                                    convergence=c, scale=k)
        r._hemisphere = _hemi(lat)
        if isinstance(latlon, _LLEB) and d is latlon.datum:
            r._latlon_to(latlon, falsed)  # XXX weakref(latlon)?
    return _xnamed(r, name)
예제 #13
0
파일: utm.py 프로젝트: itzmejawad/PyGeodesy
def toUtm8(latlon, lon=None, datum=None, Utm=Utm, falsed=True, name=NN,
                                         zone=None, **cmoff):
    '''Convert a lat-/longitude point to a UTM coordinate.

       @arg latlon: Latitude (C{degrees}) or an (ellipsoidal)
                    geodetic C{LatLon} point.
       @kwarg lon: Optional longitude (C{degrees}) or C{None}.
       @kwarg datum: Optional datum for this UTM coordinate,
                     overriding B{C{latlon}}'s datum (L{Datum},
                     L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}).
       @kwarg Utm: Optional class to return the UTM coordinate
                   (L{Utm}) or C{None}.
       @kwarg falsed: False both easting and northing (C{bool}).
       @kwarg name: Optional B{C{Utm}} name (C{str}).
       @kwarg zone: Optional UTM zone to enforce (C{int} or C{str}).
       @kwarg cmoff: DEPRECATED, use B{C{falsed}}.  Offset longitude
                     from the zone's central meridian (C{bool}).

       @return: The UTM coordinate (B{C{Utm}}) or if B{C{Utm}} is
                C{None} or not B{C{falsed}}, a L{UtmUps8Tuple}C{(zone,
                hemipole, easting, northing, band, datum, convergence,
                scale)}.  The C{hemipole} is the C{'N'|'S'} hemisphere.

       @raise RangeError: If B{C{lat}} outside the valid UTM bands or
                          if B{C{lat}} or B{C{lon}} outside the valid
                          range and L{rangerrors} set to C{True}.

       @raise TypeError: Invalid B{C{datum}} or B{C{latlon}} not ellipsoidal.

       @raise UTMError: Invalid B{C{zone}}.

       @raise ValueError: If B{C{lon}} value is missing or if
                          B{C{latlon}} is invalid.

       @note: Implements Karney’s method, using 8-th order Krüger series,
              giving results accurate to 5 nm (or better) for distances
              up to 3900 km from the central meridian.

       @example:

       >>> p = LatLon(48.8582, 2.2945)  # 31 N 448251.8 5411932.7
       >>> u = toUtm(p)  # 31 N 448252 5411933
       >>> p = LatLon(13.4125, 103.8667) # 48 N 377302.4 1483034.8
       >>> u = toUtm(p)  # 48 N 377302 1483035
    '''
    z, B, lat, lon, d, f, name = _to7zBlldfn(latlon, lon, datum,
                                             falsed, name, zone,
                                             UTMError, **cmoff)
    d = _ellipsoidal_datum(d, name=name)
    E = d.ellipsoid

    a, b = radians(lat), radians(lon)
    # easting, northing: Karney 2011 Eq 7-14, 29, 35
    sb, cb = sincos2(b)

    T = tan(a)
    T12 = hypot1(T)
    S = sinh(E.e * atanh(E.e * T / T12))

    T_ = T * hypot1(S) - S * T12
    H = hypot(T_, cb)

    y = atan2(T_, cb)  # ξ' ksi
    x = asinh(sb / H)  # η' eta

    A0 = E.A * getattr(Utm, '_scale0', _K0)  # Utm is class or None

    K = _Kseries(E.AlphaKs, x, y)  # Krüger series
    y = K.ys(y) * A0  # ξ
    x = K.xs(x) * A0  # η

    # convergence: Karney 2011 Eq 23, 24
    p_ = K.ps(1)
    q_ = K.qs(0)
    c = degrees(atan(T_ / hypot1(T_) * tan(b)) + atan2(q_, p_))

    # scale: Karney 2011 Eq 25
    k = E.e2s(sin(a)) * T12 / H * (A0 / E.a * hypot(p_, q_))

    return _toXtm8(Utm, z, lat, x, y,
                        B, d, c, k, f, name, latlon, EPS)
예제 #14
0
파일: utm.py 프로젝트: itzmejawad/PyGeodesy
    def toLatLon(self, LatLon=None, eps=EPS, unfalse=True, **LatLon_kwds):
        '''Convert this UTM coordinate to an (ellipsoidal) geodetic point.

           @kwarg LatLon: Optional, ellipsoidal class to return the
                          geodetic point (C{LatLon}) or C{None}.
           @kwarg eps: Optional convergence limit, L{EPS} or above
                       (C{float}).
           @kwarg unfalse: Unfalse B{C{easting}} and B{C{northing}}
                           if falsed (C{bool}).
           @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
                               arguments, ignored if B{C{LatLon=None}}.

           @return: This UTM coordinate (B{C{LatLon}}) or if B{C{LatLon}}
                    is C{None}, a L{LatLonDatum5Tuple}C{(lat, lon, datum,
                    convergence, scale)}.

           @raise TypeError: If B{C{LatLon}} is not ellipsoidal.

           @raise UTMError: Invalid meridional radius or H-value.

           @example:

           >>> u = Utm(31, 'N', 448251.795, 5411932.678)
           >>> from pygeodesy import ellipsoidalVincenty as eV
           >>> ll = u.toLatLon(eV.LatLon)  # 48°51′29.52″N, 002°17′40.20″E
        '''
        if eps < EPS:
            eps = EPS  # less doesn't converge

        if self._latlon and self._latlon_args == (eps, unfalse):
            return self._latlon5(LatLon)

        E = self.datum.ellipsoid  # XXX vs LatLon.datum.ellipsoid

        x, y = self.to2en(falsed=not unfalse)

        # from Karney 2011 Eq 15-22, 36
        A0 = self.scale0 * E.A
        if A0 < EPS:
            raise self._Error(meridional=A0)
        x /= A0  # η eta
        y /= A0  # ξ ksi

        K = _Kseries(E.BetaKs, x, y)  # Krüger series
        y = -K.ys(-y)  # ξ'
        x = -K.xs(-x)  # η'

        shx = sinh(x)
        sy, cy = sincos2(y)

        H = hypot(shx, cy)
        if H < EPS:
            raise self._Error(H=H)

        T = t0 = sy / H  # τʹ
        S = Fsum(T)
        q = 1.0 / E.e12
        P = 7  # -/+ toggle trips
        d = 1.0 + eps
        while abs(d) > eps and P > 0:
            p = -d  # previous d, toggled
            h = hypot1(T)
            s = sinh(E.e * atanh(E.e * T / h))
            t = T * hypot1(s) - s * h
            d = (t0 - t) / hypot1(t) * ((q + T**2) / h)
            T, d = S.fsum2_(d)  # τi, (τi - τi-1)
            if d == p:  # catch -/+ toggling of d
                P -= 1
            # else:
            #   P = 0

        a = atan(T)  # lat
        b = atan2(shx, cy)
        if unfalse and self.falsed:
            b += radians(_cmlon(self.zone))
        ll = _LLEB(degrees90(a), degrees180(b), datum=self.datum, name=self.name)

        # convergence: Karney 2011 Eq 26, 27
        p = -K.ps(-1)
        q =  K.qs(0)
        ll._convergence = degrees(atan(tan(y) * tanh(x)) + atan2(q, p))

        # scale: Karney 2011 Eq 28
        ll._scale = E.e2s(sin(a)) * hypot1(T) * H * (A0 / E.a / hypot(p, q))

        self._latlon_to(ll, eps, unfalse)
        return self._latlon5(LatLon, **LatLon_kwds)
예제 #15
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)}
           transcribed from I{Chris Veness}' U{JavaScript
           <https://www.Movable-Type.co.UK/scripts/geodesy/docs/latlon-ellipsoidal.js.html>}.

           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.

           @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}, 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}}.

           @see: 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, both from Lawrence Livermore National Laboratory (LLNL).
        '''
        x, y, z, name = _xyzn4(xyz, y, z, Error=EcefError)

        E = self.ellipsoid

        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))

            C, lat, lon = 1, degrees90(a), degrees180(b)

        # see <https://GIS.StackExchange.com/questions/28446>
        elif p > EPS:  # lat arbitrarily zero
            C, lat, lon, h = 2, 0.0, degrees180(atan2(y, x)), p - E.a

        else:  # polar lat, lon arbitrarily zero
            C, lat, lon, h = 3, copysign(90.0, z), 0.0, abs(z) - E.b

        r = Ecef9Tuple(x, y, z, lat, lon, h, C, None, self.datum)
        return self._xnamed(r, name)
예제 #16
0
 def _azik(self, t, ta):
     '''(INTERNAL) Compute the azimuthal scale C{_Ks(k0=k0)}.
     '''
     E = self.datum.ellipsoid
     return _Ks(k=self._k0 * t * hypot1(E.b_a * ta) / E.a)
예제 #17
0
    def __init__(self, sa1, ca1, sa2, ca2, k, datum, name):
        '''(INTERNAL) New C{AlbersEqualArea...} instance.
        '''
        if datum not in (None, self._datum):
            self._datum = _ellipsoidal_datum(datum, name=name)
        if name:
            self.name = name

        E = self.datum.ellipsoid
        b_a = E.b_a  # fm  = 1 - E.f
        e2 = E.e2
        e12 = E.e12  # e2m = 1 - E.e2

        self._qZ = qZ = _1_0 + e12 * self._atanhee(1)
        self._qZa2 = qZ * E.a2
        self._qx = qZ / (_2_0 * e12)

        c = min(ca1, ca2)
        if c < 0:
            raise AlbersError(clat1=ca1, clat2=ca2)
        polar = c < _EPS__2  # == 0
        # determine hemisphere of tangent latitude
        if sa1 < 0:  # and sa2 < 0:
            self._sign = -1
            # internally, tangent latitude positive
            sa1, sa2 = -sa1, neg(sa2)
        if sa1 > sa2:  # make phi1 < phi2
            sa1, sa2 = sa2, sa1
            ca1, ca2 = ca2, ca1
        if sa1 < 0:  # or sa2 < 0:
            raise AlbersError(slat1=sa1, slat2=sa2)
        # avoid singularities at poles
        ca1, ca2 = max(_EPS__2, ca1), max(_EPS__2, ca2)
        ta1, ta2 = sa1 / ca1, sa2 / ca2

        par1 = abs(ta1 - ta2) < _EPS__4  # ta1 == ta2
        if par1 or polar:
            C, ta0 = _1_0, ta2
        else:
            s1_qZ, C = self._s1_qZ_C2(ca1, sa1, ta1, ca2, sa2, ta2)

            ta0 = (ta2 + ta1) * _0_5
            Ta0 = Fsum(ta0)
            tol = _tol(_TOL0, ta0)
            for self._iteration in range(1, _NUMIT0):
                ta02 = ta0**2
                sca02 = ta02 + _1_0
                sca0 = sqrt(sca02)
                sa0 = ta0 / sca0
                sa01 = sa0 + _1_0
                sa02 = sa0**2
                # sa0m = 1 - sa0 = 1 / (sec(a0) * (tan(a0) + sec(a0)))
                sa0m = _1_0 / (sca0 * (ta0 + sca0))  # scb0^2 * sa0
                g = (_1_0 + (b_a * ta0)**2) * sa0
                dg = e12 * sca02 * (_1_0 + 2 * ta02) + e2
                D = sa0m * (_1_0 - e2 *
                            (_1_0 + sa01 * 2 * sa0)) / (e12 * sa01)  # dD/dsa0
                dD = -2 * (_1_0 - e2 * sa02 *
                           (_3_0 + 2 * sa0)) / (e12 * sa01**2)
                sa02_ = _1_0 - e2 * sa02
                sa0m_ = sa0m / (_1_0 - e2 * sa0)
                BA = sa0m_ * (self._atanhx1(e2 * sa0m_**2) * e12 - e2 * sa0m) \
                   - sa0m**2 * e2 * (2 + (_1_0 + e2) * sa0) / (e12 * sa02_)  # == B + A
                dAB = 2 * e2 * (2 - e2 *
                                (_1_0 + sa02)) / (e12 * sa02_**2 * sca02)
                u_du = fsum_(s1_qZ *  g,  -D,  g * BA) \
                     / fsum_(s1_qZ * dg, -dD, dg * BA, g * dAB)  # == u/du
                ta0, d = Ta0.fsum2_(-u_du * (sca0 * sca02))
                if abs(d) < tol:
                    break
            else:
                raise AlbersError(iteration=_NUMIT0,
                                  txt=_no_(Fmt.convergence(tol)))

        self._txi0 = txi0 = self._txif(ta0)
        self._scxi0 = hypot1(txi0)
        self._sxi0 = sxi0 = txi0 / self._scxi0
        self._m02 = m02 = _1_0 / (_1_0 + (b_a * ta0)**2)
        self._n0 = n0 = ta0 / hypot1(ta0)
        if polar:
            self._polar = True
            self._nrho0 = self._m0 = _0_0
        else:
            self._m0 = sqrt(m02)  # == nrho0 / E.a
            self._nrho0 = E.a * self._m0  # == E.a * sqrt(m02)
        self._k0_(_1_0 if par1 else (k * sqrt(C / (m02 + n0 * qZ * sxi0))))
        self._lat0 = _Lat(lat0=self._sign * atand(ta0))
예제 #18
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 B{C{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 = Scalar(x, name=_x_)
        y = Scalar(y, name=_y_)
        E = self.datum.ellipsoid

        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 * y_ * nrho0, y_ * ny) * k0 / den
        # dsxia = scxi0 * dsxi
        dsxia = -self._scxi0 * (2 * nrho0 + n0 * drho) * drho / self._qZa2
        t = 1 - dsxia * (2 * txi0 + dsxia)
        txi = (txi0 + dsxia) / (sqrt(t) if t > _EPSX2 else _EPSX)

        ta = self._tanf(txi)
        lat = degrees(atan(ta * self._sign))

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

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