Exemplo n.º 1
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 Bowring’s (1985) formulation for μm precision in concise
           form: U{'The accuracy of geodetic latitude and height equations'
           <http://www.researchgate.net/publication/
           233668213_The_Accuracy_of_Geodetic_Latitude_and_Height_Equations>},
           B. R. Bowring, Survey Review, Vol 28, 218, Oct 1985.

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

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

           @return: 3-Tuple (lat, lon, heigth) in (degrees90,
                    degrees180, meter).
        '''
        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)
            ca, sa = cos(a), sin(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 <http://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 a, b, h
Exemplo n.º 2
0
    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 I{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
Exemplo n.º 3
0
def _r3(a, f):
    '''(INTERNAL) Reduced cos, sin, tan.
    '''
    t = (1 - f) * tan(radians(a))
    c = 1 / hypot(1, t)
    s = t * c
    return c, s, t
Exemplo n.º 4
0
    def to3lld(self, datum=None):
        '''Convert this L{Lcc} to a geodetic lat- and longitude.

           @keyword datum: Optional datum to use, otherwise use this
                           I{Lcc}'s conic.datum (C{Datum}).

           @return: 3-Tuple (lat, lon, datum) in (C{degrees90},
                    {degrees180}, I{datum}).

           @raise TypeError: If I{datum} is not ellipsoidal.
        '''
        c = self.conic
        if datum:
            c = c.toDatum(datum)

        e = self.easting - c._E0
        n = c._r0 - self.northing + c._N0

        r_ = copysign(hypot(e, n), c._n)
        t_ = pow(r_ / c._aF, c._n_)

        x = c._xdef(t_)  # XXX c._lon0
        while True:
            p, x = x, c._xdef(t_ * c._pdef(x))
            if abs(x - p) < 1e-9:  # XXX EPS too small?
                break
        # x, y == lon, lat
        a = degrees90(x)
        b = degrees180((atan(e / n) + c._opt3) * c._n_ + c._lon0)

        return a, b, c.datum
Exemplo n.º 5
0
    def rhumbDistanceTo(self, other, radius=R_M):
        '''Return the distance from this to an other point along
           a rhumb (loxodrome) line.

           @param other: The other point (spherical LatLon).
           @keyword radius: Optional mean radius of earth (scalar, default meter).

           @return: Distance (in the same units as radius).

           @raise TypeError: The other point is not spherical.

           @example:

           >>> p = LatLon(51.127, 1.338)
           >>> q = LatLon(50.964, 1.853)
           >>> d = p.rhumbDistanceTo(q)  # 403100
        '''
        # see <http://www.edwilliams.org/avform.htm#Rhumb>
        da, db, dp = self._rhumb3(other)

        # on Mercator projection, longitude distances shrink
        # by latitude; the 'stretch factor' q becomes ill-
        # conditioned along E-W line (0/0); use an empirical
        # tolerance to avoid it
        if abs(dp) > EPS:
            q = da / dp
        else:
            a, _ = self.to2ab()
            q = cos(a)

        return float(radius) * hypot(da, q * db)
Exemplo n.º 6
0
    def _direct(self, distance, bearing, llr, height=None):
        '''(INTERNAL) Direct Vincenty method.

           @raise TypeError: The B{C{other}} point is not L{LatLon}.

           @raise ValueError: If this and the B{C{other}} point's L{Datum}
                              ellipsoids are not compatible.

           @raise VincentyError: Vincenty fails to converge for the current
                                 L{LatLon.epsilon} and L{LatLon.iterations}
                                 limit.
        '''
        E = self.ellipsoid()

        c1, s1, t1 = _r3(self.lat, E.f)

        i = radians(bearing)  # initial bearing (forward azimuth)
        si, ci = sincos2(i)
        s12 = atan2(t1, ci) * 2

        sa = c1 * si
        c2a = 1 - sa**2
        if c2a < EPS:
            c2a = 0
            A, B = 1, 0
        else:  # e22 == (a / b)**2 - 1
            A, B = _p2(c2a * E.e22)

        s = d = distance / (E.b * A)
        for _ in range(self._iterations):
            ss, cs = sincos2(s)
            c2sm = cos(s12 + s)
            s_, s = s, d + _ds(B, cs, ss, c2sm)
            if abs(s - s_) < self._epsilon:
                break
        else:
            raise VincentyError('%s, %r' % ('no convergence', self))

        t = s1 * ss - c1 * cs * ci
        # final bearing (reverse azimuth +/- 180)
        r = degrees360(atan2(sa, -t))
        if llr:
            # destination latitude in [-90, 90)
            a = degrees90(atan2(s1 * cs + c1 * ss * ci,
                                (1 - E.f) * hypot(sa, t)))
            # destination longitude in [-180, 180)
            b = degrees180(atan2(ss * si, c1 * cs - s1 * ss * ci) -
                          _dl(E.f, c2a, sa, s, cs, ss, c2sm) +
                           radians(self.lon))
            h = self.height if height is None else height
            r = self.classof(a, b, height=h, datum=self.datum), r
        return r
Exemplo n.º 7
0
    def to2ll(self):
        '''Convert this vector to (geodetic) lat- and longitude.

           @return: 2-Tuple (lat, lon) in (degrees90, degrees180).

           @example:

           >>> v = Vector3d(0.500, 0.500, 0.707)
           >>> a, b = v.to2ll()  # 45.0, 45.0
        '''
        a = atan2(self.z, hypot(self.x, self.y))
        b = atan2(self.y, self.x)
        return degrees90(a), degrees180(b)
Exemplo n.º 8
0
    def to2ab(self):
        '''Convert this vector to (geodetic) lat- and longitude.

           @return: 2-Tuple (lat, lon) in (C{radians}, C{radians}).

           @example:

           >>> v = Vector3d(0.500, 0.500, 0.707)
           >>> a, b = v.to2ab()  # 0.785323, 0.785398
        '''
        a = atan2(self.z, hypot(self.x, self.y))
        b = atan2(self.y, self.x)
        return a, b
Exemplo n.º 9
0
    def to2ab(self):
        '''Convert this vector to (geodetic) lat- and longitude.

           @return: A L{PhiLam2Tuple}C{(phi, lambda)}.

           @example:

           >>> v = Vector3d(0.500, 0.500, 0.707)
           >>> a, b = v.to2ab()  # 0.785323, 0.785398
        '''
        a = atan2(self.z, hypot(self.x, self.y))
        b = atan2(self.y, self.x)
        return self._xnamed(PhiLam2Tuple(a, b))
Exemplo n.º 10
0
    def _sigmaInv0(self, xi, eta):
        '''Starting point for C{sigmaInv}.

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

           @see: C{bool TMExact::sigmainv0(real xi, real eta,
                                           real &u, real &v)}.
        '''
        trip = False
        if eta > self._Ev_KE_5_4 or xi < min(-self._Eu_E_1_4,
                                             eta - self._Ev_KE):
            # sigma as a simple pole at
            #  w = w0 = Eu.K() + i * Ev.K()
            # and sigma is approximated by
            #  sigma = (Eu.E() + i * Ev.KE()) + 1/(w - w0)
            x = xi - self._Eu_E
            y = eta - self._Ev_KE
            d = x**2 + y**2
            u = self._Eu_K + x / d
            v = self._Ev_K - y / d

        elif eta > self._Ev_KE or (eta > self._Ev_KE_3_4
                                   and xi < self._Eu_E_1_4):
            # At w = w0 = i * Ev.K(), we have
            #  sigma  = sigma0  = i * Ev.KE()
            #  sigma' = sigma'' = 0
            #
            # including the next term in the Taylor series gives:
            #  sigma = sigma0 - _mv / 3 * (w - w0)^3
            #
            # When inverting this, we map arg(w - w0) = [-pi/2, -pi/6]
            # to arg(sigma - sigma0) = [-pi/2, pi/2]
            # mapping arg = [-pi/2, -pi/6] to [-pi/2, pi/2]

            d = eta - self._Ev_KE
            r = hypot(xi, d)
            # Error using this guess is about 0.068 * rad^(5/3)
            trip = r < _TAYTOL2
            # Map the range [-90, 180] in sigma space to [-90, 0] in
            # w space.  See discussion in zetainv0 on the cut for ang.
            r = cbrt(r * self._3_mv)
            a = atan2(d - xi, xi + d) / 3.0 - PI_4
            s, c = sincos2(a)
            u = r * c
            v = r * s + self._Ev_K

        else:  # use w = sigma * Eu.K/Eu.E (correct in the limit _e -> 0)
            r = self._Eu_K / self._Eu_E
            u = xi * r
            v = eta * r
        return u, v, trip
Exemplo n.º 11
0
    def toLatLon(self, LatLon=None, unfalse=True):
        '''Convert this UPS coordinate to an (ellipsoidal) geodetic point.

           @keyword LatLon: Optional, ellipsoidal (sub-)class to return
                            the point (C{LatLon}) or C{None}.
           @keyword unfalse: Unfalse I{easting} and I{northing} if falsed
                             (C{bool}).

           @return: This UPS coordinate as (I{LatLon}) or 5-tuple
                    (C{lat, lon, datum, convergence, scale}) if
                    I{LatLon} is C{None}.

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

           @raise UPSError: Invalid meridional radius or H-value.
        '''
        if self._latlon:
            return self._latlon5(LatLon)

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

        f = self.falsed if unfalse else 0
        x = self.easting  - f
        y = self.northing - f

        r = hypot(x, y)
        t = (r / (2 * self.scale0 * E.a / E.es_c)) if r > 0 else EPS**2
        t = E.es_tauf((1 / t - t) * 0.5)
        if self.pole == 'N':
            a, b, c = atan(t), atan2(x, -y), 1
        else:
            a, b, c = -atan(t), atan2(x, y), -1

        a, b = degrees90(a), degrees180(b)
        if not self._band:
            self._band = _Band(a, b)
        if not self._hemisphere:
            self._hemisphere = _hemi(a)

        ll = _LLEB(a, b, datum=self._datum, name=self.name)
        ll._convergence = b * c  # gamma
        ll._scale = _scale(E, r, t) if r > 0 else self.scale0

        self._latlon = ll
        return self._latlon5(LatLon)
Exemplo n.º 12
0
def equirectangular(lat1, lon1, lat2, lon2, radius=R_M, **options):
    '''Compute the distance between two points using
       the U{Equirectangular Approximation / Projection
       <http://www.movable-type.co.uk/scripts/latlong.html>}.

       See function L{equirectangular_} for more details, the
       available I{options} and errors raised.

       @param lat1: Start latitude (degrees).
       @param lon1: Start longitude (degrees).
       @param lat2: End latitude (degrees).
       @param lon2: End longitude (degrees).
       @keyword radius: Optional, mean earth radius (meter).
       @keyword options: Optional keyword arguments for function
                         L{equirectangular_}.

       @return: Distance (meter, same units as I{radius}).

       @see: U{Local, Flat Earth<http://www.edwilliams.org/avform.htm#flat>},
             method L{Ellipsoid.distance2} or function L{haversine} for
             more accurate and/or larger distances.
    '''
    _, dy, dx, _ = equirectangular_(lat1, lon1, lat2, lon2, **options)
    return radians(hypot(dx, dy)) * radius
Exemplo n.º 13
0
    def toLatLon(self, LatLon=None, eps=EPS, unfalse=True):
        '''Convert this UTM coordinate to an (ellipsoidal) geodetic point.

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

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

           @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('%s invalid: %r' % ('meridional', E.A))
        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('%s invalid: %r' % ('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)
Exemplo n.º 14
0
def toUtm(latlon, lon=None, datum=None, Utm=Utm, name='', cmoff=True):
    '''Convert a lat-/longitude point to a UTM coordinate.

       @param latlon: Latitude (C{degrees}) or an (ellipsoidal)
                      geodetic C{LatLon} point.
       @keyword lon: Optional longitude (C{degrees} or C{None}).
       @keyword datum: Optional datum for this UTM coordinate,
                       overriding I{latlon}'s datum (C{Datum}).
       @keyword Utm: Optional (sub-)class to use for the UTM
                     coordinate (L{Utm}) or C{None}.
       @keyword name: Optional I{Utm} name (C{str}).
       @keyword cmoff: Offset longitude from zone's central meridian,
                       apply false easting and false northing (C{bool}).

       @return: The UTM coordinate (L{Utm}) or a 6-tuple (zone, easting,
                northing, band, convergence, scale) if I{Utm} is C{None}
                or I{cmoff} is C{False}.

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

       @raise RangeError: If I{lat} is outside the valid UTM bands or if
                          I{lat} or I{lon} outside the valid range and
                          I{rangerrrors} set to C{True}.

       @raise ValueError: If I{lon} value is missing or if I{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
    '''
    try:
        lat, lon = latlon.lat, latlon.lon
        if not isinstance(latlon, _LLEB):
            raise TypeError('%s not %s: %r' %
                            ('latlon', 'ellipsoidal', latlon))
        if not name:  # use latlon.name
            name = _nameof(latlon) or name  # PYCHOK no effect
        d = datum or latlon.datum
    except AttributeError:
        lat, lon = parseDMS2(latlon, lon)
        d = datum or Datums.WGS84

    E = d.ellipsoid

    z, B, a, b = _toZBab4(lat, lon, cmoff)

    # easting, northing: Karney 2011 Eq 7-14, 29, 35
    cb, sb, tb = cos(b), sin(b), tan(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 = _K0 * E.A

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

    if cmoff:
        # C.F.F. Karney, "Test data for the transverse Mercator projection (2009)",
        # <http://GeographicLib.SourceForge.io/html/transversemercator.html> and
        # <http://Zenodo.org/record/32470#.W4LEJS2ZON8>
        x += _FalseEasting  # make x relative to false easting
        if y < 0:
            y += _FalseNorthing  # y relative to false northing in S

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

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

    if cmoff and Utm is not None:
        h = 'S' if a < 0 else 'N'  # hemisphere
        return _xnamed(
            Utm(z, h, x, y, band=B, datum=d, convergence=c, scale=s), name)
    else:  # zone, easting, northing, band, convergence and scale
        return z, x, y, B, c, s
Exemplo n.º 15
0
    def toLatLon(self, LatLon=None):
        '''Convert this UTM coordinate to an (ellipsoidal) geodetic point.

           @keyword LatLon: Optional, ellipsoidal (sub-)class to use
                            for the point (C{LatLon}) or C{None}.

           @return: This UTM coordinate as (I{LatLon}) or 5-tuple
                    (lat, lon, datum, convergence, scale) if I{LatLon}
                    is C{None}.

           @raise TypeError: If I{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 self._latlon:
            return self._latlon5(LatLon)

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

        x = self._easting - _FalseEasting  # relative to central meridian
        y = self._northing
        if self._hemi == 'S':  # relative to equator
            y -= _FalseNorthing

        # from Karney 2011 Eq 15-22, 36
        A0 = _K0 * E.A
        if A0 < EPS:
            raise UTMError('%s invalid: %r' % ('meridional', E.A))
        x /= A0  # η eta
        y /= A0  # ξ ksi

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

        shx = sinh(x)
        cy, sy = cos(y), sin(y)

        H = hypot(shx, cy)
        if H < EPS:
            raise UTMError('%s invalid: %r' % ('H', H))

        T = t0 = sy / H  # τʹ
        q = 1.0 / E.e12
        d = 1
        sd = Fsum(T)
        # toggles on +/-1.12e-16 eg. 31 N 400000 5000000
        while abs(d) > EPS:  # 1e-12
            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 = sd.fsum_(d)  # τi

        a = atan(T)  # lat
        b = atan2(shx, cy) + radians(_cmlon(self._zone))
        ll = _LLEB(degrees90(a),
                   degrees180(b),
                   datum=self._datum,
                   name=self.name)

        # convergence: Karney 2011 Eq 26, 27
        p = -Ks.ps(-1)
        q = Ks.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 = ll
        return self._latlon5(LatLon)
Exemplo n.º 16
0
def toUtm8(latlon,
           lon=None,
           datum=None,
           Utm=Utm,
           falsed=True,
           name='',
           zone=None,
           **cmoff):
    '''Convert a lat-/longitude point to a UTM coordinate.

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

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

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

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

    t = z, lat, x, y, B, d, c, k, f
    return _toXtm8(Utm, t, name, latlon, EPS)
Exemplo n.º 17
0
def toUtm(latlon, lon=None, datum=None, Utm=Utm):
    '''Convert a lat-/longitude point to a UTM coordinate.

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

       @param latlon: Latitude (degrees) or an (ellipsoidal)
                      geodetic I{LatLon} point.
       @keyword lon: Optional longitude (degrees or None).
       @keyword datum: Optional datum for this UTM coordinate,
                       overriding latlon's datum (I{Datum}).
       @keyword Utm: Optional I{Utm} class to usefor the UTM
                     coordinate (L{Utm}).

       @return: The UTM coordinate (L{Utm}).

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

       @raise ValueError: If I{lon} value is missing, if I{latlon}
                          is not scalar or I{latlon} is outside
                          the valid UTM bands.

       @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
    '''
    try:
        lat, lon = latlon.lat, latlon.lon
        if not isinstance(latlon, _eLLb):
            raise TypeError('%s not %s: %r' %
                            ('latlon', 'ellipsoidal', latlon))
        d = datum or latlon.datum
    except AttributeError:
        lat, lon = parseDMS2(latlon, lon)
        d = datum or Datums.WGS84

    E = d.ellipsoid

    z, B, a, b = _toZBll(lat, lon)
    h = 'S' if a < 0 else 'N'  # hemisphere

    # easting, northing: Karney 2011 Eq 7-14, 29, 35
    cb, sb, tb = cos(b), sin(b), tan(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 = _K0 * E.A

    A6 = _K6s(E.Alpha6, x, y)  # 6th-order Krüger series, 1-origin
    y = A6.ys(y) * A0  # ξ
    x = A6.xs(x) * A0  # η

    x += _FalseEasting  # make x relative to false easting
    if y < 0:
        y += _FalseNorthing  # y relative to false northing in S

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

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

    return Utm(z, h, x, y, band=B, datum=d, convergence=c, scale=s)
Exemplo n.º 18
0
    def _inverse(self, other, azis, wrap):
        '''(INTERNAL) Inverse Vincenty method.

           @raise TypeError: The other point is not L{LatLon}.

           @raise ValueError: If this and the I{other} point's L{Datum}
                              ellipsoids are not compatible.

           @raise VincentyError: Vincenty fails to converge for the current
                                 L{LatLon.epsilon} and L{LatLon.iterations}
                                 limit and/or if this and the I{other} point
                                 are near-antipodal or coincide.
        '''
        E = self.ellipsoids(other)

        c1, s1, _ = _r3(self.lat, E.f)
        c2, s2, _ = _r3(other.lat, E.f)

        c1c2, s1c2 = c1 * c2, s1 * c2
        c1s2, s1s2 = c1 * s2, s1 * s2

        dl, _ = unroll180(self.lon, other.lon, wrap=wrap)
        ll = dl = radians(dl)
        for _ in range(self._iterations):
            cll, sll, ll_ = cos(ll), sin(ll), ll

            ss = hypot(c2 * sll, c1s2 - s1c2 * cll)
            if ss < EPS:
                raise VincentyError('%r coincides with %r' % (self, other))
            cs = s1s2 + c1c2 * cll
            s = atan2(ss, cs)

            sa = c1c2 * sll / ss
            c2a = 1 - sa**2
            if abs(c2a) < EPS:
                c2a = 0  # equatorial line
                ll = dl + E.f * sa * s
            else:
                c2sm = cs - 2 * s1s2 / c2a
                ll = dl + _dl(E.f, c2a, sa, s, cs, ss, c2sm)

            if abs(ll - ll_) < self._epsilon:
                break
            # <http://github.com/chrisveness/geodesy/blob/master/latlon-vincenty.js>
            # omitted and applied only after failure to converge, see footnote under
            # Inverse at <http://en.wikipedia.org/wiki/Vincenty's_formulae>


#           elif abs(ll) > PI and self.isantipode(other, eps=self._epsilon):
#              raise VincentyError('%r antipodal to %r' % (self, other))
        else:
            t = 'antipodal ' if self.isantipode(other,
                                                eps=self._epsilon) else ''
            raise VincentyError('no convergence, %r %sto %r' %
                                (self, t, other))

        if c2a:  # e22 == (a / b)**2 - 1
            A, B = _p2(c2a * E.e22)
            s = A * (s - _ds(B, cs, ss, c2sm))

        b = E.b
        #       if self.height or other.height:
        #           b += self._havg(other)
        d = b * s

        if azis:  # forward and reverse azimuth
            cll, sll = cos(ll), sin(ll)
            f = degrees360(atan2(c2 * sll, c1s2 - s1c2 * cll))
            r = degrees360(atan2(c1 * sll, -s1c2 + c1s2 * cll))
            d = d, f, r
        return d
Exemplo n.º 19
0
    def _zetaInv0(self, psi, lam):
        '''Starting point for C{zetaInv}.

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

           @see: C{bool TMExact::zetainv0(real psi, real lam,  # radians
                                          real &u, real &v)}.
        '''
        trip = False
        if (psi < -self._e_PI_4 and lam > self._1_e2_PI_2
                and psi < lam - self._1_e_PI_2):
            # N.B. this branch is normally not taken because psi < 0
            # is converted psi > 0 by Forward.
            #
            # There's a log singularity at w = w0 = Eu.K() + i * Ev.K(),
            # corresponding to the south pole, where we have, approximately
            #
            #  psi = _e + i * pi/2 - _e * atanh(cos(i * (w - w0)/(1 + _mu/2)))
            #
            # Inverting this gives:
            h = sinh(1 - psi / self._e)
            a = (PI_2 - lam) / self._e
            s, c = sincos2(a)
            u = self._Eu_K - asinh(s / hypot(c, h)) * self._mu_2_1
            v = self._Ev_K - atan2(c, h) * self._mu_2_1

        elif (psi < self._e_PI_2 and lam > self._1_e2_PI_2):
            # At w = w0 = i * Ev.K(), we have
            #
            #  zeta  = zeta0  = i * (1 - _e) * pi/2
            #  zeta' = zeta'' = 0
            #
            # including the next term in the Taylor series gives:
            #
            #  zeta = zeta0 - (_mv * _e) / 3 * (w - w0)^3
            #
            # When inverting this, we map arg(w - w0) = [-90, 0] to
            # arg(zeta - zeta0) = [-90, 180]

            d = lam - self._1_e_PI_2
            r = hypot(psi, d)
            # Error using this guess is about 0.21 * (rad/e)^(5/3)
            trip = r < self._e_taytol_
            # atan2(dlam-psi, psi+dlam) + 45d gives arg(zeta - zeta0)
            # in range [-135, 225).  Subtracting 180 (since multiplier
            # is negative) makes range [-315, 45).  Multiplying by 1/3
            # (for cube root) gives range [-105, 15).  In particular
            # the range [-90, 180] in zeta space maps to [-90, 0] in
            # w space as required.
            r = cbrt(r * self._3_mv_e)
            a = atan2(d - psi, psi + d) / 3.0 - PI_4
            s, c = sincos2(a)
            u = r * c
            v = r * s + self._Ev_K

        else:
            # Use spherical TM, Lee 12.6 -- writing C{atanh(sin(lam) /
            # cosh(psi)) = asinh(sin(lam) / hypot(cos(lam), sinh(psi)))}.
            # This takes care of the log singularity at C{zeta = Eu.K()},
            # corresponding to the north pole.
            s, c = sincos2(lam)
            h, r = sinh(psi), self._Eu_K / PI_2
            # But scale to put 90, 0 on the right place
            u = r * atan2(h, c)
            v = r * asinh(s / hypot(c, h))
        return u, v, trip
Exemplo n.º 20
0
def toUtm8(latlon,
           lon=None,
           datum=None,
           Utm=Utm,
           cmoff=True,
           name='',
           zone=None):
    '''Convert a lat-/longitude point to a UTM coordinate.

       @param latlon: Latitude (C{degrees}) or an (ellipsoidal)
                      geodetic C{LatLon} point.
       @keyword lon: Optional longitude (C{degrees}) or C{None}.
       @keyword datum: Optional datum for this UTM coordinate,
                       overriding I{latlon}'s datum (C{Datum}).
       @keyword Utm: Optional (sub-)class to return the UTM
                     coordinate (L{Utm}) or C{None}.
       @keyword cmoff: Offset longitude from zone's central meridian,
                       apply false easting and false northing (C{bool}).
       @keyword name: Optional I{Utm} name (C{str}).
       @keyword zone: Optional zone to enforce (C{int} or C{str}).

       @return: The UTM coordinate (L{Utm}) or a 8-tuple (C{zone, hemisphere,
                easting, northing, band, datum, convergence, scale}) if
                I{Utm} is C{None} or I{cmoff} is C{False}.

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

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

       @raise UTMError: Invlid I{zone}.

       @raise ValueError: If I{lon} value is missing or if I{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
    '''
    lat, lon, d, name = _to4lldn(latlon, lon, datum, name)
    z, B, lat, lon = _to3zBll(lat, lon, cmoff=cmoff)
    if zone:  # re-zone for UTM
        r, _, _ = _to3zBhp(zone, band=B)
        if r != z:
            if not _UTM_ZONE_MIN <= r <= _UTM_ZONE_MAX:
                raise UTMError('%s invalid: %r' % ('zone', zone))
            if cmoff:  # re-offset from central meridian
                lon += _cmlon(z) - _cmlon(r)
            z = r

    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 = _K0 * E.A

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

    if cmoff:
        # Charles Karney, "Test data for the transverse Mercator projection (2009)"
        # <http://GeographicLib.SourceForge.io/html/transversemercator.html>
        # and <http://Zenodo.org/record/32470#.W4LEJS2ZON8>
        x += _FalseEasting  # make x relative to false easting
        if y < 0:
            y += _FalseNorthing  # y relative to false northing in S

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

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

    h = _hemi(a)
    if Utm is None or not cmoff:
        r = z, h, x, y, B, d, c, s
    else:
        r = _xnamed(Utm(z, h, x, y, band=B, datum=d, convergence=c, scale=s),
                    name)
    return r
Exemplo n.º 21
0
    def toLatLon(self, LatLon=None, eps=EPS, unfalse=True):
        '''Convert this UTM coordinate to an (ellipsoidal) geodetic point.

           @keyword LatLon: Optional, ellipsoidal (sub-)class to return
                            the point (C{LatLon}) or C{None}.
           @keyword eps: Optional convergence limit, L{EPS} or above
                         (C{float}).
           @keyword unfalse: Unfalse I{easting} and I{northing} if falsed
                             (C{bool}).

           @return: This UTM coordinate as (I{LatLon}) or 5-tuple
                    (lat, lon, datum, convergence, scale) if I{LatLon}
                    is C{None}.

           @raise TypeError: If I{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_eps == eps:
            return self._latlon5(LatLon)

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

        x = self._easting
        y = self._northing
        if unfalse and self._falsed:
            x -= _FalseEasting  # relative to central meridian
            if self._hemisphere == 'S':  # relative to equator
                y -= _FalseNorthing

        # from Karney 2011 Eq 15-22, 36
        A0 = _K0 * E.A
        if A0 < EPS:
            raise UTMError('%s invalid: %r' % ('meridional', E.A))
        x /= A0  # η eta
        y /= A0  # ξ ksi

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

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

        H = hypot(shx, cy)
        if H < EPS:
            raise UTMError('%s invalid: %r' % ('H', H))

        d = 1.0 + eps
        q = 1.0 / E.e12
        T = t0 = sy / H  # τʹ
        sd = Fsum(T)
        while abs(d) > eps:
            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 = sd.fsum2_(d)  # τi, (τi - τi-1)

        a = atan(T)  # lat
        b = atan2(shx, cy) + radians(_cmlon(self._zone))
        ll = _LLEB(degrees90(a),
                   degrees180(b),
                   datum=self._datum,
                   name=self.name)

        # convergence: Karney 2011 Eq 26, 27
        p = -Ks.ps(-1)
        q = Ks.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, self._latlon_eps = ll, eps
        return self._latlon5(LatLon)