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