Ejemplo n.º 1
0
def parseRad(strRad, suffix=_NSEW_, clip=0):
    '''Parse a string representing angle in C{radians}.

       @arg strRad: Degrees in any of several forms (C{str} or C{radians}).
       @kwarg suffix: Optional, valid compass points (C{str}, C{tuple}).
       @kwarg clip: Optionally, limit value to -clip..+clip (C{radians}).

       @return: Radians (C{float}).

       @raise ParseError: Invalid B{C{strRad}} or B{C{clip}}.

       @raise RangeError: Value of B{C{strRad}} outside the valid range
                          and L{rangerrors} set to C{True}.
    '''
    def _Rad_(strRad, suffix, clip):
        try:
            r = float(strRad)

        except (TypeError, ValueError):
            strRad = strRad.strip()

            r = float(
                strRad.lstrip(_PLUSMINUS_).rstrip(suffix.upper()).strip())
            if strRad[:1] == _MINUS_ or strRad[-1:] in _SW_:
                r = neg(r)

        return clipRadians(r, float(clip)) if clip else r

    return _parseX(_Rad_, strRad, suffix, clip, strRad=strRad, suffix=suffix)
Ejemplo n.º 2
0
def parseMGRS(strMGRS, datum=Datums.WGS84, Mgrs=Mgrs, name=NN):
    '''Parse a string representing a MGRS grid reference,
       consisting of C{"zoneBand, grid, easting, 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])

    def _MGRS_(strMGRS, datum, Mgrs, name):
        m = tuple(strMGRS.replace(',', ' ').strip().split())
        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:])

        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, force=True)

    return _parseX(_MGRS_, strMGRS, datum, Mgrs, name,
                           strMGRS=strMGRS, Error=MGRSError)
Ejemplo n.º 3
0
def parse3llh(strllh, height=0, sep=_COMMA_, clipLat=90, clipLon=180):
    '''Parse a string C{"lat lon [h]"} representing lat-, longitude in
       C{degrees} and optional height in C{meter}.

       The lat- and longitude value must be separated by a separator
       character.  If height is present it must follow, separated by
       another separator.

       The lat- and longitude values may be swapped, provided at least
       one ends with the proper compass point.

       @arg strllh: Latitude, longitude[, height] (C{str}, ...).
       @kwarg height: Optional, default height (C{meter}) or C{None}.
       @kwarg sep: Optional separator (C{str}).
       @kwarg clipLat: Keep latitude in B{C{-clipLat..+clipLat}} (C{degrees}).
       @kwarg clipLon: Keep longitude in B{C{-clipLon..+clipLon}} range (C{degrees}).

       @return: A L{LatLon3Tuple}C{(lat, lon, height)} in C{degrees},
                C{degrees} and C{float}.

       @raise RangeError: Lat- or longitude value of B{C{strllh}} outside
                          valid range and L{rangerrors} set to C{True}.

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

       @note: See the B{Notes} at function L{parseDMS}.

       @see: Functions L{parseDDDMMSS}, L{parseDMS} and L{parseDMS2}.

       @example:

       >>> parse3llh('000°00′05.31″W, 51° 28′ 40.12″ N')
       (51.4778°N, 000.0015°W, 0)
    '''
    from pygeodesy.namedTuples import LatLon3Tuple  # avoid circluar import

    def _3llh_(strllh, height, sep):
        ll = strllh.strip().split(sep)
        if len(ll) > 2:  # XXX interpret height unit
            h = float(ll.pop(2).strip().rstrip(_LETTERS).rstrip())
        else:
            h = height  # None from wgrs.Georef.__new__
        if len(ll) != 2:
            raise ValueError

        a, b = [_.strip() for _ in ll]  # PYCHOK false
        if a[-1:] in _EW_ or b[-1:] in _NS_:
            a, b = b, a
        return LatLon3Tuple(parseDMS(a, suffix=_NS_, clip=clipLat),
                            parseDMS(b, suffix=_EW_, clip=clipLon), h)

    return _parseX(_3llh_, strllh, height, sep, strllh=strllh)
Ejemplo n.º 4
0
def _parseUTMUPS5(strUTMUPS, UPS, Error=ParseError, band=NN, sep=_COMMA_):
    '''(INTERNAL) 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 band: Optional, default Band letter (C{str}).
       @kwarg sep: Optional, separator to split (",").

       @return: 5-Tuple (C{zone, hemisphere/pole, easting, northing,
                band}).

       @raise ParseError: Invalid B{C{strUTMUPS}}.
    '''
    def _UTMUPS5_(strUTMUPS, UPS, band, sep):
        u = strUTMUPS.lstrip()
        if UPS and not u.startswith(_UPS_ZONE_STR):
            raise ValueError

        u = u.replace(sep, _SPACE_).strip().split()
        if len(u) < 4:
            raise ValueError

        z, h = u[:2]
        if h[:1].upper() not in _NS_:
            raise ValueError

        if z.isdigit():
            z, B = int(z), band
        else:
            for i in range(len(z)):
                if not z[i].isdigit():
                    # int('') raises ValueError
                    z, B = int(z[:i]), z[i:]
                    break
            else:
                raise ValueError

        e, n = map(float, u[2:4])
        return z, h.upper(), e, n, B.upper()

    return _parseX(_UTMUPS5_,
                   strUTMUPS,
                   UPS,
                   band,
                   sep,
                   strUTMUPS=strUTMUPS,
                   Error=Error)
Ejemplo n.º 5
0
def parseWM(strWM, radius=R_MA, Wm=Wm, name=NN):
    '''Parse a string C{"e n [r]"} 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]
    '''
    def _WM_(strWM, radius, Wm, name):
        w = strWM.replace(_COMMA_, _SPACE_).strip().split()

        if len(w) == 2:
            w += [radius]
        elif len(w) != 3:
            raise ValueError
        x, y, r = map(float, w)

        r = EasNorRadius3Tuple(x, y, r) if Wm is None else \
                            Wm(x, y, radius=r)
        return _xnamed(r, name, force=True)

    return _parseX(_WM_,
                   strWM,
                   radius,
                   Wm,
                   name,
                   strWM=strWM,
                   Error=WebMercatorError)
Ejemplo n.º 6
0
def parseDMS(strDMS, suffix=_NSEW_, sep=S_SEP, clip=0):  # MCCABE 14
    '''Parse a lat- or longitude representation C{"lat, lon"} in C{degrees}.

       This is very flexible on formats, allowing signed decimal
       degrees, degrees and minutes or degrees minutes and seconds
       optionally suffixed by a cardinal compass point.

       A variety of symbols, separators and suffixes are accepted,
       for example "3°37′09″W".  Minutes and seconds may be omitted.

       @arg strDMS: Degrees in any of several forms (C{str}) and
                    types (C{float}, C{int}, other).
       @kwarg suffix: Optional, valid compass points (C{str}, C{tuple}).
       @kwarg sep: Optional separator between deg°, min′ and sec″ ("''").
       @kwarg clip: Optionally, limit value to -clip..+clip (C{degrees}).

       @return: Degrees (C{float}).

       @raise ParseError: Invalid B{C{strDMS}} or B{C{clip}}.

       @raise RangeError: Value of B{C{strDMS}} outside the valid range
                          and L{rangerrors} set to C{True}.

       @note: Inlike function L{parseDDDMMSS}, type C{float}, C{int}
              and other non-C{str} B{C{strDMS}} values are considered
              as decimal degrees.  For example, C{int(1230)} is returned
              as 1230.0 I{and not as 12.5} degrees and C{float(345)} as
              345.0 I{and not as 3.75} degrees!

       @see: Functions L{parseDDDMMSS}, L{parseDMS2} and L{parse3llh}.
    '''
    return _parseX(_DMS2deg,
                   strDMS,
                   suffix,
                   sep,
                   clip,
                   strDMS=strDMS,
                   suffix=suffix)
Ejemplo n.º 7
0
def parseOSGR(strOSGR, Osgr=Osgr, name=NN):
    '''Parse an OSGR coordinate string to an Osgr instance.

       Accepts standard OS Grid References like 'SU 387 148',
       with or without whitespace separators, from 2- up to
       10-digit references (1 m × 1 m square), or fully
       numeric, comma-separated references in meters, for
       example '438700,114800'.

       @arg strOSGR: An OSGR coordinate (C{str}).
       @kwarg Osgr: Optional class to return the OSGR
                    coordinate (L{Osgr}) or C{None}.
       @kwarg name: Optional B{C{Osgr}} name (C{str}).

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

       @raise OSGRError: Invalid B{C{strOSGR}}.

       @example:

       >>> g = parseOSGR('TG 51409 13177')
       >>> str(g)  # TG 51409 13177
       >>> g = parseOSGR('TG5140913177')
       >>> str(g)  # TG 51409 13177
       >>> g = parseOSGR('TG51409 13177')
       >>> str(g)  # TG 51409 13177
       >>> g = parseOSGR('651409,313177')
       >>> str(g)  # TG 51409 13177
       >>> g.toStr(prec=0)  # 651409,313177
    '''
    def _c2i(G):
        g = ord(G.upper()) - _ord_A
        if g > 7:
            g -= 1
        return g

    def _s2f(g):
        return float(g.strip())

    def _s2i(G, g):
        m = g + '00000'  # std to meter
        return int(str(G) + m[:5])

    def _OSGR_(strOSGR, Osgr, name):
        s = strOSGR.strip()
        g = s.split(_COMMA_)
        if len(g) == 2:  # "easting,northing"
            if len(s) < 13:
                raise ValueError
            e, n = map(_s2f, g)

        else:  # "GR easting northing"
            g, s = s[:2], s[2:].strip()

            e, n = map(_c2i, g)
            n, m = divmod(n, 5)
            E = ((e - 2) % 5) * 5 + m
            N = 19 - (e // 5) * 5 - n
            if 0 > E or E > 6 or \
               0 > N or N > 12:
                raise ValueError

            g = s.split()
            if len(g) == 1:  # no whitespace
                e, n = halfs2(s)
            elif len(g) == 2:
                e, n = g
            else:
                raise ValueError

            e = _s2i(E, e)
            n = _s2i(N, n)

        r = _EasNor2Tuple(e, n) if Osgr is None else Osgr(e, n)
        return _xnamed(r, name, force=True)

    return _parseX(_OSGR_,
                   strOSGR,
                   Osgr,
                   name,
                   strOSGR=strOSGR,
                   Error=OSGRError)
Ejemplo n.º 8
0
def parseDDDMMSS(strDDDMMSS, suffix=_NSEW_, sep=S_SEP, clip=0):
    '''Parse a lat- or longitude represention form [D]DDMMSS in degrees.

       @arg strDDDMMSS: Degrees in any of several forms (C{str}) and
                        types (C{float}, C{int}, other).
       @kwarg suffix: Optional, valid compass points (C{str}, C{tuple}).
       @kwarg sep: Optional separator between "[D]DD", "MM" and "SS" (%r).
       @kwarg clip: Optionally, limit value to -clip..+clip (C{degrees}).

       @return: Degrees (C{float}).

       @raise ParseError: Invalid B{C{strDDDMMSS}} or B{C{clip}} or the
                          B{C{strDDDMMSS}} form is incompatible with the
                          suffixed or B{C{suffix}} compass point.

       @raise RangeError: Value of B{C{strDDDMMSS}} outside the valid
                          range and L{rangerrors} set to C{True}.

       @note: Type C{str} values "[D]DD", "[D]DDMM" and "[D]DDMMSS" for
              B{C{strDDDMMSS}} are parsed properly only if I{either}
              unsigned and suffixed with a valid, compatible, C{cardinal}
              L{compassPoint} I{or} if unsigned or signed, unsuffixed and
              with keyword argument B{C{suffix}} set to B{%r}, B{%r} or a
              compatible L{compassPoint}.

       @note: Unlike function L{parseDMS}, type C{float}, C{int} and
              other non-C{str} B{C{strDDDMMSS}} values are interpreted
              form [D]DDMMSS.  For example, C{int(1230)} is returned as
              12.5 I{and not 1230.0} degrees.  However, C{int(345)} is
              considered form "DDD" 345 I{and not "DDMM" 0345}, unless
              B{C{suffix}} specifies compass point B{%r}.

       @see: Functions L{parseDMS}, L{parseDMS2} and L{parse3llh}.
    '''
    def _DDDMMSS_(strDDDMMSS, suffix, sep, clip):
        S = suffix.upper()
        if isstr(strDDDMMSS):
            t = strDDDMMSS.strip()
            if sep:
                t = t.replace(sep, NN).strip()

            s = t[:1]  # sign or digit
            P = t[-1:]  # compass point, digit or dot

            t = t.lstrip(_PLUSMINUS_).rstrip(S).strip()
            f = t.split(_DOT_)
            d = len(f[0])
            f = NN.join(f)
            if 1 < d < 8 and f.isdigit() and (
                (P in S and s.isdigit()) or
                (P.isdigit() and s in '-0123456789+'  # PYCHOK indent
                 and S in ((_NS_, _EW_) + _WINDS))):
                # check [D]DDMMSS form and compass point
                X = _EW_ if (d & 1) else _NS_
                if not (P in X or (S in X and (P.isdigit() or P == _DOT_))):
                    t = 'DDDMMSS'[d & 1 ^ 1:d | 1], X[:1], X[1:]
                    raise ParseError('form %s applies %s-%s' % t)
                f = 0  # fraction
            else:  # try other forms
                return _DMS2deg(strDDDMMSS, S, sep, clip)

        else:  # float or int to [D]DDMMSS[.fff]
            f = float(strDDDMMSS)
            s = _MINUS_ if f < 0 else NN
            P = _0_  # anything except _SW_
            f, i = modf(abs(f))
            t = Fmt.f(i, prec=0)  # str(i) == 'i.0'
            d = len(t)
            # bump number of digits to match
            # the given, valid compass point
            if S in (_NS_ if (d & 1) else _EW_):
                t = _0_ + t
                d += 1
            #   P = S
            # elif d > 1:
            #   P = (_EW_ if (d & 1) else _NS_)[0]

        if d < 4:  # [D]DD[.ddd]
            if f:
                t = float(t) + f
            t = t, 0, 0
        else:
            f += float(t[d - 2:])
            if d < 6:  # [D]DDMM[.mmm]
                t = t[:d - 2], f, 0
            else:  # [D]DDMMSS[.sss]
                t = t[:d - 4], t[d - 4:d - 2], f
        d = _dms2deg(s, P, *map2(float, t))

        return clipDegrees(d, float(clip)) if clip else d

    return _parseX(_DDDMMSS_,
                   strDDDMMSS,
                   suffix,
                   sep,
                   clip,
                   strDDDMMSS=strDDDMMSS,
                   suffix=suffix)