Exemple #1
0
def _to3zBlat(zone, band, Error=UTMError):  # imported by .mgrs.py
    '''(INTERNAL) Check and return zone, Band and band latitude.

       @arg zone: Zone number or string.
       @arg band: Band letter.
       @arg Error: Exception to raise (L{UTMError}).

       @return: 3-Tuple (zone, Band, latitude).
    '''
    try:
        z, B, _ = _to3zBhp(zone, band=band)  # in .ellipsoidalBase
        if _UTM_ZONE_MIN > z or z > _UTM_ZONE_MAX:
            raise ValueError
    except ValueError:
        raise InvalidError(zone=zone, Error=Error)

    b = None
    if B:
        b = _Bands.find(B)
        if b < 0:
            raise InvalidError(band=band or B, Error=Error)
        b = (b << 3) - 80
    elif Error is not UTMError:
        raise Error('%s missing: %r' % ('band', band))

    return z, B, b
Exemple #2
0
    def latlon(self, latlonh):
        '''Set the lat- and longitude and optionally the height.

           @arg latlonh: New lat-, longitude and height (2- or
                        3-tuple of C{degrees} and C{meter}).

           @raise TypeError: Height of B{C{latlonh}} not C{scalar} or
                             B{C{latlonh}} not C{list} or C{tuple}.

           @raise ValueError: Invalid B{C{latlonh}} or M{len(latlonh)}.

           @see: Function L{parse3llh} to parse a B{C{latlonh}} string
                 into a 3-tuple (lat, lon, h).
        '''
        _xinstanceof(list, tuple, latlonh=latlonh)

        if len(latlonh) == 3:
            h = Height(latlonh[2], name='latlonh')
        elif len(latlonh) != 2:
            raise InvalidError(latlonh=latlonh)
        else:
            h = self._height

        lat = Lat(latlonh[0], name='lat')  # parseDMS2(latlonh[0], latlonh[1])
        lon = Lon(latlonh[1], name='lon')
        self._update(lat != self._lat or lon != self._lon or h != self._height)
        self._lat, self._lon, self._height = lat, lon, h
Exemple #3
0
def precision(form, prec=None):
    '''Set the default precison for a given F_ form.

       @arg form: L{F_D}, L{F_DM}, L{F_DMS}, L{F_DEG}, L{F_MIN},
                  L{F_SEC}, L{F__E}, L{F__F}, L{F__G} or L{F_RAD}
                  (C{str}).
       @kwarg prec: Optional number of decimal digits (0..9 or
                    C{None} for default).  Trailing zero decimals
                    are stripped for B{C{prec}} values of 1 and
                    above, but kept for negative B{C{prec}}.

       @return: Previous precision (C{int}).

       @raise ValueError: Invalid B{C{form}} or B{C{prec}} or
                          B{C{prec}} outside valid range.
    '''
    try:
        p = _F_prec[form]
    except KeyError:
        raise InvalidError(form=form)

    if prec is not None:
        from pygeodesy.units import Precision_
        _F_prec[form] = Precision_(prec, name='prec', low=-9, high=9)

    return p
Exemple #4
0
def fpowers(x, n, alts=0):
    '''Return a series of powers M{[x**i for i=1..n]}.

       @arg x: Value (C{scalar}).
       @arg n: Highest exponent (C{int}).
       @kwarg alts: Only alternating powers, starting with
                    this exponent (C{int}).

       @return: Powers of B{C{x}} (C{float}[]).

       @raise TypeError: Non-scalar B{C{x}} or B{C{n}} not C{int}.

       @raise ValueError: Non-finite B{C{x}} or non-positive B{C{n}}.
    '''
    if not isfinite(x):
        raise ValueError('not %s: %r' % ('finite', x))
    if not isint(n):
        raise IsnotError(int.__name_, n=n)
    elif n < 1:
        raise InvalidError(n=n)

    xs = [x]
    for _ in range(1, n):
        xs.append(xs[-1] * x)

    if alts > 0:  # x**2, x**4, ...
        # XXX PyChecker chokes on xs[alts-1::2]
        xs = xs[slice(alts - 1, None, 2)]

    # XXX PyChecker claims result is None
    return xs
Exemple #5
0
def parseUTMUPS5(strUTMUPS, datum=Datums.WGS84, Utm=Utm, Ups=Ups, name=''):
    '''Parse a string representing a UTM or UPS coordinate, consisting
       of C{"zone[band] hemisphere/pole easting northing"}.

       @arg strUTMUPS: A UTM or UPS coordinate (C{str}).
       @kwarg datum: Optional datum to use (L{Datum}).
       @kwarg Utm: Optional class to return the UTM coordinate (L{Utm})
                   or C{None}.
       @kwarg Ups: Optional class to return the UPS coordinate (L{Ups})
                   or C{None}.
       @kwarg name: Optional name (C{str}).

       @return: The UTM or UPS coordinate (B{C{Utm}} or B{C{Ups}}) or
                a L{UtmUps5Tuple}C{(zone, hemipole, easting, northing,
                band)} if B{C{Utm}} respectively B{C{Ups}} or both are
                C{None}.  The C{hemipole} is C{'N'|'S'}, the UTM hemisphere
                or UPS pole, the UPS projection top/center.

       @raise UTMUPSError: Invalid B{C{strUTMUPS}}.

       @see: Functions L{parseUTM5} and L{parseUPS5}.
    '''
    try:
        try:
            u = parseUTM5(strUTMUPS, datum=datum, Utm=Utm, name=name)
        except UTMError:
            u = parseUPS5(strUTMUPS, datum=datum, Ups=Ups, name=name)
    except (UTMError, UPSError):
        raise InvalidError(strUTMUPS=strUTMUPS, Error=UTMUPSError)
    return u
Exemple #6
0
    def rescale0(self, lat, scale0=_K0):
        '''Set the central scale factor for this UPS projection.

           @arg lat: Northern latitude (C{degrees}).
           @arg scale0: UPS k0 scale at B{C{lat}} latitude (C{scalar}).

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

           @raise UPSError: Invalid B{C{scale}}.
        '''
        try:
            s0 = float(scale0)
            if not 0 < s0:  # <= 1.003 or 1.0016?
                raise ValueError
        except (TypeError, ValueError):
            raise InvalidError(scale0=scale0, Error=UPSError)

        lat = clipDegrees(lat, 90)  # clip and force N
        u = toUps8(abs(lat), 0, datum=self.datum, Ups=_UpsK1)
        k = s0 / u.scale
        if self.scale0 != k:
            self._band = ''  # force re-compute
            self._latlon = self._epsg = self._mgrs = self._utm = None
            self._scale0 = k
Exemple #7
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)
Exemple #8
0
def parseUPS5(strUPS, datum=Datums.WGS84, Ups=Ups, falsed=True, name=''):
    '''Parse a string representing a UPS coordinate, consisting of
       C{"[zone][band] pole easting northing"} where B{C{zone}} is
       pseudo zone C{"00"|"0"|""} and C{band} is C{'A'|'B'|'Y'|'Z'|''}.

       @arg strUPS: A UPS coordinate (C{str}).
       @kwarg datum: Optional datum to use (L{Datum}).
       @kwarg Ups: Optional class to return the UPS coordinate (L{Ups})
                   or C{None}.
       @kwarg falsed: Both B{C{easting}} and B{C{northing}} are falsed (C{bool}).
       @kwarg name: Optional B{C{Ups}} name (C{str}).

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

       @raise UPSError: Invalid B{C{strUPS}}.
    '''
    try:
        u = strUPS.lstrip()
        if not u.startswith(_UPS_ZONE_STR):
            raise ValueError

        z, p, e, n, B = _parseUTMUPS(u)
        if z != _UPS_ZONE or (B and B not in _Bands):
            raise ValueError
    except (AttributeError, TypeError, ValueError):
        raise InvalidError(strUPS=strUPS, Error=UPSError)

    r = UtmUps5Tuple(z, p, e, n, B) if Ups is None else \
                 Ups(z, p, e, n, band=B, falsed=falsed, datum=datum)
    return _xnamed(r, name)
Exemple #9
0
def parseWM(strWM, radius=R_MA, Wm=Wm, name=''):
    '''Parse a string representing a WM coordinate, consisting
       of easting, northing and an optional radius.

       @arg strWM: A WM coordinate (C{str}).
       @kwarg radius: Optional earth radius (C{meter}).
       @kwarg Wm: Optional class to return the WM coordinate (L{Wm})
                  or C{None}.
       @kwarg name: Optional name (C{str}).

       @return: The WM coordinate (B{C{Wm}}) or an
                L{EasNorRadius3Tuple}C{(easting, northing, radius)}
                if B{C{Wm}} is C{None}.

       @raise WebMercatorError: Invalid B{C{strWM}}.

       @example:

       >>> u = parseWM('448251 5411932')
       >>> u.toStr2()  # [E:448251, N:5411932]
    '''
    w = strWM.strip().replace(',', ' ').split()
    try:
        if len(w) == 2:
            w += [radius]
        elif len(w) != 3:
            raise ValueError  # caught below
        x, y, r = map(float, w)

    except (TypeError, ValueError):
        raise InvalidError(strWM=strWM, Error=WebMercatorError)

    r = EasNorRadius3Tuple(x, y, r) if Wm is None else \
                        Wm(x, y, radius=r)
    return _xnamed(r, name)
Exemple #10
0
    def toStr(self,
              prec=3,
              sep=' ',
              radius=False,
              **unused):  # PYCHOK expected
        '''Return a string representation of this WM coordinate.

           @kwarg prec: Optional number of decimals, unstripped (C{int}).
           @kwarg sep: Optional separator to join (C{str}).
           @kwarg radius: Optionally, include radius (C{bool} or C{scalar}).

           @return: This WM as "meter meter" (C{str}) plus " radius"
                    if B{C{radius}} is C{True} or C{scalar}.

           @raise WebMercatorError: Invalid B{C{radius}}.

           @example:

           >>> w = Wm(448251, 5411932.0001)
           >>> w.toStr(4)  # 448251.0 5411932.0001
           >>> w.toStr(sep=', ')  # 448251, 5411932
        '''
        fs = self._x, self._y
        if radius in (False, None):
            pass
        elif radius is True:
            fs += (self._radius, )
        elif isscalar(radius):
            fs += (radius, )
        else:
            raise InvalidError(radius=radius, Error=WebMercatorError)
        return sep.join(strs(fs, prec=prec))
Exemple #11
0
def parseMGRS(strMGRS, datum=Datums.WGS84, Mgrs=Mgrs, name=''):
    '''Parse a string representing a MGRS grid reference,
       consisting of zoneBand, grid, easting and northing.

       @arg strMGRS: MGRS grid reference (C{str}).
       @kwarg datum: Optional datum to use (L{Datum}).
       @kwarg Mgrs: Optional class to return the MGRS grid
                    reference (L{Mgrs}) or C{None}.
       @kwarg name: Optional B{C{Mgrs}} name (C{str}).

       @return: The MGRS grid reference (B{L{Mgrs}}) or an
                L{Mgrs4Tuple}C{(zone, digraph, easting, northing)}
                if B{C{Mgrs}} is C{None}.

       @raise MGRSError: Invalid B{C{strMGRS}}.

       @example:

       >>> m = parseMGRS('31U DQ 48251 11932')
       >>> str(m)  # 31U DQ 48251 11932
       >>> m = parseMGRS('31UDQ4825111932')
       >>> repr(m)  # [Z:31U, G:DQ, E:48251, N:11932]
       >>> m = mgrs.parseMGRS('42SXD0970538646')
       >>> str(m)  # 42S XD 09705 38646
       >>> m = mgrs.parseMGRS('42SXD9738')  # Km
       >>> str(m)  # 42S XD 97000 38000
    '''
    def _mg(cre, s):  # return re.match groups
        m = cre.match(s)
        if not m:
            raise ValueError
        return m.groups()

    def _s2m(g):  # e or n string to float meter
        # convert to meter if less than 5 digits
        m = g + '00000'
        return float(m[:5])

    m = tuple(strMGRS.strip().replace(',', ' ').split())
    try:
        if len(m) == 1:  # 01ABC1234512345'
            m = _mg(_MGRSre, m[0])
            m = m[:2] + halfs2(m[2])
        elif len(m) == 2:  # 01ABC 1234512345'
            m = _mg(_ZBGre, m[0]) + halfs2(m[1])
        elif len(m) == 3:  # 01ABC 12345 12345'
            m = _mg(_ZBGre, m[0]) + m[1:]
        if len(m) != 4:  # 01A BC 1234 12345
            raise ValueError
        e, n = map(_s2m, m[2:])
    except (TypeError, ValueError):
        raise InvalidError(strMGRS=strMGRS, Error=MGRSError)

    z, EN = m[0], m[1].upper()
    r = Mgrs4Tuple(z, EN, e, n) if Mgrs is None else \
              Mgrs(z, EN, e, n, datum=datum)
    return _xnamed(r, name)
Exemple #12
0
 def _split2(g, name, _2m):
     i = max(g.rfind(name[0]), g.rfind(name[0]))
     if i > _BaseLen:
         try:
             return g[:i], _2m(int(g[i+1:]))
         except (IndexError, ValueError):
             pass  # avoids nested exceptions
         raise InvalidError(Error=WGRSError, **{name: georef})
     return g, None
Exemple #13
0
def encode(zone, hemipole='', band=''):
    '''Determine the U{EPSG<https://www.EPSG-Registry.org>} code for
       a given UTM/UPS zone number, hemisphere/pole and/or Band.

       @arg zone: The (longitudinal) UTM zone (C{int}, 1..60) or UPS
                  zone (C{int}, 0) or UTM zone with/-out (latitudinal)
                  Band letter (C{str}, '01C'..'60X') or UPS zone
                  with/-out (polar) Band letter (C{str}, '00A', '00B',
                  '00Y' or '00Z').
       @kwarg hemipole: UTM/UPS hemisphere or UPS projection top/center
                        pole (C{str}, C{'N[orth]'} or C{'S[outh]'}).
       @kwarg band: Optional (latitudinal) UTM or (polar) UPS Band
                    letter (C{str}).

       @return: C{EPSG} code (L{Epsg}).

       @raise EPSGError: Invalid B{C{zone}}, B{C{hemipole}} or B{C{band}}.

       @note: Coverage of UPS as zone C{0} follows Karney's function U{UTMUPS::EncodeEPSG
              <https://GeographicLib.SourceForge.io/html/classGeographicLib_1_1UTMUPS.html>}.
    '''
    try:
        z, B, hp = _to3zBhp(zone, band,
                            hemipole=hemipole)  # in .ellipsoidalBase
        if hp not in ('N', 'S'):
            raise ValueError
    except ValueError:
        raise InvalidError(zone=zone,
                           hemipole=hemipole,
                           band=band,
                           Error=EPSGError)

    if _UTM_ZONE_MIN <= z <= _UTM_ZONE_MAX:
        e = z - _UTM_ZONE_MIN + (_EPSG_N_01 if hp == 'N' else _EPSG_S_01)
    elif z == _UPS_ZONE:
        e = _EPSG_N if hp == 'N' else _EPSG_S
    else:
        raise InvalidError(zone=zone, Error=EPSGError)

    e = Epsg(e)
    e._band = B
    # e._hemisphere = hp
    return e
Exemple #14
0
def _parsex(parser, *args, **name_value_pairs):
    '''(INTERNAL) Invoke a parser and handle exceptions.
    '''
    try:
        return parser(*args)
    except RangeError as _:
        x, E = str(_), RangeError  # avoid Python 3+ nested exception messages
    except (AttributeError, IndexError, TypeError, ValueError) as _:
        x, E = str(_), ParseError  # avoid Python 3+ nested exception messages
    raise InvalidError(Error=E, txt=x, **name_value_pairs)
Exemple #15
0
def _fraction(fraction, n):
    f = 1  # int, no fractional indices
    if fraction in (None, 1):
        pass
    elif not (isscalar(fraction) and EPS < fraction < EPS1
                                 and (float(n) - fraction) < n):
        raise InvalidError(fraction=fraction, Error=FrechetError)
    elif fraction < EPS1:
        f = float(fraction)
    return f
Exemple #16
0
def _parseUTM5(strUTM, Error):
    '''(INTERNAL) Parse a string representing a UTM coordinate,
       consisting of C{"zone[band] hemisphere easting northing"},
       see L{parseETM5} and L{parseUTM5}.
    '''
    try:
        z, h, e, n, B = _parseUTMUPS(strUTM)
        if _UTM_ZONE_MIN <= z <= _UTM_ZONE_MAX and \
                           (B in _Bands or not B):
            return UtmUps5Tuple(z, h, e, n, B)
    except ValueError:
        pass
    raise InvalidError(strUTM=strUTM, Error=Error)
Exemple #17
0
def encode(lat, lon, precision=1):  # MCCABE 14
    '''Encode a lat-/longitude as a C{garef} of the given precision.

       @arg lat: Latitude (C{degrees}).
       @arg lon: Longitude (C{degrees}).
       @kwarg precision: Optional, the desired C{garef} resolution
                         and length (C{int} 0..2).

       @return: The C{garef} (C{str}).

       @raise RangeError: Invalid B{C{lat}} or B{C{lon}}.

       @raise GARSError: Invalid B{C{precision}}.

       @note: The C{garef} length is M{precision + 5} and the C{garef}
              resolution is B{30′} for B{C{precision}} 0, B{15′} for 1
              and B{5′} for 2, respectively.
    '''
    def _digit(x, y, m):
        return _Digits[m * (m - y - 1) + x + 1],

    def _str(chars, x, n):
        s, b = [], len(chars)
        for i in range(n):
            x, i = divmod(x, b)
            s.append(chars[i])
        return tuple(reversed(s))

    try:
        p = int(precision)
        if p < 0 or p > _MaxPrec:
            raise ValueError
    except (TypeError, ValueError):
        raise InvalidError(precision=precision, Error=GARSError)

    lat, lon = _2fll(lat, lon)
    if lat == 90:
        lat *= EPS1_2

    ix, x = _2divmod2(lon, _LonOrig_M)
    iy, y = _2divmod2(lat, _LatOrig_M)

    g = _str(_Digits, ix + 1, _LonLen) + _str(_Letters, iy, _LatLen)
    if p > 0:
        ix, x = divmod(x, _M3)
        iy, y = divmod(y, _M3)
        g += _digit(ix, iy, _M2)
        if p > 1:
            g += _digit(x, y, _M3)

    return ''.join(g)
Exemple #18
0
    def beta(self, beta):
        '''Set the inverse distance power.

           @arg beta: New inverse distance power (C{int} 1, 2, or 3).

           @raise HeightError: Invalid B{C{beta}}.
        '''
        try:
            b = int(beta)
            if b != beta or 1 > b or b > 3:
                raise ValueError
        except (TypeError, ValueError):
            raise InvalidError(beta=beta, Error=HeightError)
        self._beta = b
Exemple #19
0
    def toLatLon(self, LatLon, **kwds):
        '''Return (the center of) this garef cell as an instance
           of the supplied C{LatLon} class.

           @arg LatLon: Class to use (C{LatLon}).
           @kwarg kwds: Optional keyword arguments for B{C{LatLon}}.

           @return: This garef location (B{C{LatLon}}).

           @raise GARSError: Invalid B{C{LatLon}}.
        '''
        if LatLon is None:
            raise InvalidError(LatLon=LatLon, Error=GARSError)

        return self._xnamed(LatLon(*self.latlon, **kwds))
Exemple #20
0
def _to7zBlldfn(latlon, lon, datum, falsed, name, zone, Error, **cmoff):
    '''(INTERNAL) Determine 7-tuple (zone, band, lat, lon, datum,
        falsed, name) for L{toEtm8} and L{toUtm8}.
    '''
    f = falsed and cmoff.get('cmoff', True)  # DEPRECATED
    lat, lon, d, name = _to4lldn(latlon, lon, datum, name)
    z, B, lat, lon = _to3zBll(lat, lon, cmoff=f)
    if zone:  # re-zone for ETM/UTM
        r, _, _ = _to3zBhp(zone, band=B)
        if r != z:
            if not _UTM_ZONE_MIN <= r <= _UTM_ZONE_MAX:
                raise InvalidError(zone=zone, Error=Error)
            if f:  # re-offset from central meridian
                lon += _cmlon(z) - _cmlon(r)
            z = r
    return z, B, lat, lon, d, f, name
Exemple #21
0
    def seed(self, seed):
        '''Set the random sampling seed.

           @arg seed: Valid L{Random(seed)} or C{None}, C{0} or
                      C{False} for no U{random sampling<https://
                      Publik.TUWien.ac.AT/files/PubDat_247739.pdf>}.

           @raise HausdorffError: Invalid B{C{seed}}.
        '''
        if seed:
            try:
                Random(seed)
            except (TypeError, ValueError):
                raise InvalidError(seed=seed, Error=HausdorffError)
            self._seed = seed
        else:
            self._seed = None
Exemple #22
0
    def dividedBy(self, factor):
        '''Divide this vector by a scalar.

           @arg factor: The divisor (C{scalar}).

           @return: New, scaled vector (L{Vector3d}).

           @raise TypeError: Non-scalar B{C{factor}}.

           @raise VectorError: Invalid or zero B{C{factor}}.
        '''
        if not isscalar(factor):
            raise IsnotError('scalar', factor=factor)
        try:
            return self.times(1.0 / factor)
        except (ValueError, ZeroDivisionError):
            raise InvalidError(factor=factor, Error=VectorError)
Exemple #23
0
    def parse(self, str3d, sep=','):
        '''Parse an C{"x, y, z"} string.

           @arg str3d: X, y and z values (C{str}).
           @kwarg sep: Optional separator (C{str}).

           @return: New vector (L{Vector3d}).

           @raise VectorError: Invalid B{C{str3d}}.
        '''
        try:
            v = [float(v.strip()) for v in str3d.split(sep)]
            if len(v) != 3:
                raise ValueError
        except (TypeError, ValueError):
            raise InvalidError(str3d=str3d, Error=VectorError)

        return self.classof(*v)
Exemple #24
0
def enstr2(easting, northing, prec, *extras):
    '''Return easting, northing string representations.

       @arg easting: Easting from false easting (C{meter}).
       @arg northing: Northing from from false northing (C{meter}).
       @arg prec: Precision in number of digits (C{int}).
       @arg extras: Optional leading items (C{str}s).

       @return: B{C{extras}} + 2-Tuple C{(eastingStr, northingStr)}.

       @raise ValueError: Invalid B{C{prec}}.
    '''
    w = prec // 2
    try:
        p10 = (1e-4, 1e-3, 1e-2, 1e-1, 1)[w - 1]  # 10**(5 - w)
    except IndexError:
        raise InvalidError(prec=prec)
    return extras + ('%0*d' % (w, int(easting * p10)), '%0*d' %
                     (w, int(northing * p10)))
Exemple #25
0
def compassPoint(bearing, prec=3):
    '''Convert bearing to a compass point.

       @arg bearing: Bearing from North (compass C{degrees360}).
       @kwarg prec: Optional precision (1 for cardinal or basic winds,
                    2 for intercardinal or ordinal or principal winds,
                    3 for secondary-intercardinal or half-winds or
                    4 for quarter-winds).

       @return: Compass point (1-, 2-, 3- or 4-letter C{str}).

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

       @see: U{Dms.compassPoint
             <https://GitHub.com/chrisveness/geodesy/blob/master/dms.js>}
             and U{Compass rose<https://WikiPedia.org/wiki/Compass_rose>}.

       @example:

       >>> p = compassPoint(24, 1)  # 'N'
       >>> p = compassPoint(24, 2)  # 'NE'
       >>> p = compassPoint(24, 3)  # 'NNE'
       >>> p = compassPoint(24)     # 'NNE'
       >>> p = compassPoint(11, 4)  # 'NbE'
       >>> p = compassPoint(30, 4)  # 'NEbN'

       >>> p = compassPoint(11.249)  # 'N'
       >>> p = compassPoint(11.25)   # 'NNE'
       >>> p = compassPoint(-11.25)  # 'N'
       >>> p = compassPoint(348.749) # 'NNW'
    '''
    try:  # m = 2 << prec; x = 32 // m
        m, x = _MOD_X[prec]
    except KeyError:
        raise InvalidError(prec=prec)
    # not round(), i.e. half-even rounding in Python 3,
    # but round-away-from-zero as int(b + 0.5) iff b is
    # non-negative, otherwise int(b + copysign(0.5, b))
    q = int((bearing % 360) * m / 360.0 + 0.5) % m
    return _WINDS[q * x]
Exemple #26
0
def degDMS(deg, prec=6, s_D=S_DEG, s_M=S_MIN, s_S=S_SEC, neg='-', pos=''):
    '''Convert degrees to a string in degrees, minutes B{I{or}} seconds.

       @arg deg: Value in degrees (C{scalar}).
       @kwarg prec: Optional number of decimal digits (0..9 or
                    C{None} for default).  Trailing zero decimals
                    are stripped for B{C{prec}} values of 1 and
                    above, but kept for negative B{C{prec}}.
       @kwarg s_D: Symbol for degrees (C{str}).
       @kwarg s_M: Symbol for minutes (C{str}) or C{""}.
       @kwarg s_S: Symbol for seconds (C{str}) or C{""}.
       @kwarg neg: Optional sign for negative (C{'-'}).
       @kwarg pos: Optional sign for positive (C{''}).

       @return: I{Either} degrees, minutes B{I{or}} seconds (C{str}).
    '''
    try:
        deg = float(deg)
    except (TypeError, ValueError):
        raise InvalidError(deg=deg)

    d, s = abs(deg), s_D
    if d < 1:
        if s_M:
            d *= 60
            if d < 1 and s_S:
                d *= 60
                s = s_S
            else:
                s = s_M
        elif s_S:
            d *= 3600
            s = s_S

    n = neg if deg < 0 else pos
    z = int(prec)
    t = '%s%.*F' % (n, abs(z), d)
    if z > 1:
        t = fstrzs(t)
    return t + s
Exemple #27
0
def decode5(georef, center=True):
    '''Decode a C{georef} to lat-, longitude, precision, height and radius.

       @arg georef: To be decoded (L{Georef} or C{str}).
       @kwarg center: If C{True} the center, otherwise the south-west,
                      lower-left corner (C{bool}).

       @return: A L{LatLonPrec5Tuple}C{(lat, lon,
                precision, height, radius)} where C{height} and/or
                C{radius} are C{None} if missing.

       @raise WGRSError: Invalid B{C{georef}}, INValid, non-alphanumeric
                         or odd length B{C{georef}}.
    '''
    def _h2m(kft):
        return ft2m(kft * 1000.0)

    def _r2m(NM):
        return NM / m2NM(1)

    def _split2(g, name, _2m):
        i = max(g.rfind(name[0]), g.rfind(name[0]))
        if i > _BaseLen:
            try:
                return g[:i], _2m(int(g[i+1:]))
            except (IndexError, ValueError):
                pass  # avoids nested exceptions
            raise InvalidError(Error=WGRSError, **{name: georef})
        return g, None

    try:
        g = str(georef)
    except (TypeError, ValueError):
        raise InvalidError(georef=georef, Error=WGRSError)

    g, h = _split2(g, 'Height', _h2m)  # H is last
    g, r = _split2(g, 'Radius', _r2m)  # R before H

    a, b, p = decode3(g, center=center)
    return LatLonPrec5Tuple(a, b, p, h, r)
Exemple #28
0
    def __init__(self, zone, en100k, easting, northing,
                             band='', datum=Datums.WGS84, name=''):
        '''New L{Mgrs} Military grid reference.

           @arg zone: 6° longitudinal zone (C{int}), 1..60 covering 180°W..180°E.
           @arg en100k: Two-letter EN digraph (C{str}), 100 km grid square.
           @arg easting: Easting (C{meter}), within 100 km grid square.
           @arg northing: Northing (C{meter}), within 100 km grid square.
           @kwarg band: Optional 8° latitudinal band (C{str}), C..X covering 80°S..84°N.
           @kwarg datum: Optional this reference's datum (L{Datum}).
           @kwarg name: Optional name (C{str}).

           @raise MGRSError: Invalid MGRS grid reference, B{C{zone}}, B{C{en100k}},
                             B{C{easting}}, B{C{northing}} or B{C{band}}.

           @example:

           >>> from pygeodesy import Mgrs
           >>> m = Mgrs('31U', 'DQ', 48251, 11932)  # 31U DQ 48251 11932
        '''
        if name:
            self.name = name

        self._zone, self._band, self._bandLat = _to3zBlat(zone, band, MGRSError)

        try:
            en = str(en100k).upper()
            if len(en) != 2:
                raise IndexError  # caught below
            self._en100k = en
            self._en100k2m()
        except IndexError:
            raise InvalidError(en100k=en100k, Error=MGRSError)

        self._easting  = Easting(easting,   Error=MGRSError)
        self._northing = Northing(northing, Error=MGRSError)
        if self._datum != datum:
            self._datum = datum
Exemple #29
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)
Exemple #30
0
    def toUtm(self, zone, eps=EPS, falsed=True, **unused):
        '''Convert this UTM coordinate to a different zone.

           @arg zone: New UTM zone (C{int}).
           @kwarg eps: Optional convergence limit, L{EPS} or above
                       (C{float}), see method L{Utm.toLatLon}.
           @kwarg falsed: False both easting and northing (C{bool}).

           @return: The UTM coordinate (L{Utm}).
        '''
        if zone == self.zone and falsed == self.falsed:
            return self.copy()
        elif zone:
            u = self._utm
            if u is None or u.zone != zone or falsed != u.falsed:
                ll = self.toLatLon(LatLon=_LLEB, eps=eps, unfalse=True)
                self._utm = u = toUtm8(ll,
                                       Utm=self.classof,
                                       falsed=falsed,
                                       name=self.name,
                                       zone=zone)
            return u
        raise InvalidError(zone=zone, Error=self._Error)