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