def __new__(cls, arg=None, name=NN, Error=UnitError, low=0, high=None, **name_arg): '''New named C{int} instance with limits. @kwarg cls: This class (C{Int_} or sub-class). @arg arg: The value (any C{type} convertable to C{int}). @kwarg name: Optional instance name (C{str}). @kwarg Error: Optional error to raise, overriding the default C{UnitError}. @kwarg low: Optional lower B{C{arg}} limit (C{float} or C{None}). @kwarg high: Optional upper B{C{arg}} limit (C{float} or C{None}). @kwarg name_arg: Optional C{name=arg} keyword argument, inlieu of B{C{name}} and B{C{arg}}. @returns: An L{Int_} instance. @raise Error: Invalid B{C{arg}} or B{C{arg}} below B{C{low}} or above B{C{high}}. ''' if name_arg: name, arg = _xkwds_popitem(name_arg) self = Int.__new__(cls, arg=arg, name=name, Error=Error) if (low is not None) and self < low: txt = Fmt.limit(below=low) elif (high is not None) and self > high: txt = Fmt.limit(above=high) else: return self raise _Error(cls, arg, name=name, Error=Error, txt=txt)
def toStr(self, **unused): # PYCHOK expected '''Return this reference frame as a text string. @return: This L{RefFrame}'s attributes (C{str}). ''' e = self.ellipsoid t = (Fmt.EQUAL(_name_, repr(self.name)), Fmt.EQUAL(_epoch_, self.epoch), Fmt.PAREN(Fmt.EQUAL(_ellipsoid_, classname(e)), Fmt.EQUAL(_name_, repr(e.name)))) return _COMMASPACE_.join(t)
def trilaterate5( self, distance1, point2, distance2, point3, distance3, # PYCHOK signature area=False, eps=EPS1, radius=R_M, wrap=False): '''B{Not implemented} for C{B{area}=True} or C{B{wrap}=True} and falls back to method C{trilaterate} otherwise. @return: A L{Trilaterate5Tuple}C{(min, minPoint, max, maxPoint, n)} with a single trilaterated intersection C{minPoint I{is} maxPoint}, C{min I{is} max} the nearest intersection margin and count C{n = 1}. @raise IntersectionError: No intersection, trilateration failed. @raise NotImplementedError: Keyword argument C{B{area}=True} or B{C{wrap}=True} not (yet) supported. @raise TypeError: Invalid B{C{point2}} or B{C{point3}}. @raise ValueError: Some B{C{points}} coincide or invalid B{C{distance1}}, B{C{distance2}}, B{C{distance3}} or B{C{radius}}. ''' if area or wrap: from pygeodesy.named import notImplemented notImplemented(self, self.trilaterate5, area=area, wrap=wrap) t = _trilaterate(self, distance1, self.others(point2=point2), distance2, self.others(point3=point3), distance3, radius=radius, height=None, useZ=True, LatLon=self.classof) # ... and handle B{C{eps}} and C{IntersectionError} as # method C{.latlonBase.LatLonBase.trilaterate2} d = self.distanceTo(t, radius=radius, wrap=wrap) # PYCHOK distanceTo d = abs(distance1 - d), abs(distance2 - d), abs(distance3 - d) d = float(min(d)) if d < eps: # min is max, minPoint is maxPoint return Trilaterate5Tuple(d, t, d, t, 1) # n = 1 t = _SPACE_(_no_(_intersection_), Fmt.PAREN(min.__name__, Fmt.f(d, prec=3))) raise IntersectionError(area=area, eps=eps, wrap=wrap, txt=t)
def __setitem__(self, key, value): '''Set item B{C{key}} to B{C{value}}. ''' if key == _name_: raise KeyError( _EQUAL_(Fmt.SQUARE(self.classname, key), repr(value))) dict.__setitem__(self, key, value)
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=Fmt.SQUARE(latlonh=2)) elif len(latlonh) != 2: raise _ValueError(latlonh=latlonh) else: h = self._height lat = Lat(latlonh[0]) # parseDMS2(latlonh[0], latlonh[1]) lon = Lon(latlonh[1]) self._update(lat != self._lat or lon != self._lon or h != self._height) self._lat, self._lon, self._height = lat, lon, h
def _notError(inst, name, args, kwds): # PYCHOK no cover '''(INTERNAL) Format an error message. ''' n = _DOT_(classname(inst, prefixed=True), _dunder_name(name, name)) m = _COMMASPACE_.join( modulename(c, prefixed=True) for c in inst.__class__.__mro__[1:-1]) return _COMMASPACE_(unstr(n, *args, **kwds), Fmt.PAREN(_MRO_, m))
def _all(globalocals): from pygeodesy.interns import NN as _NN, _attribute_, _COMMASPACE_, \ _DOT_, _module_, _s_ # PYCHOK expected from pygeodesy.streprs import Fmt as _Fmt # PYCHOK expected # collect all public module and attribute names and check # that modules are imported from this package, 'pygeodesy' # (but the latter only when not bundled with PyInstaller or # Py2Exe, since the file-layout is different. Courtesy of # GilderGeek<https://GitHub.com/mrJean1/PyGeodesy/issues/31>) ns = list(lazily._ALL_INIT) # XXX ps = () if _isfrozen else set([_pygeodesy_] + __name__.split(_DOT_)) for mod, attrs in lazily._ALL_LAZY.enums(): if mod not in globalocals: t = _DOT_(_pygeodesy_, mod) raise ImportError('missing %s%s: %s' % (_module_, _NN, t)) ns.append(mod) # check that all other public attributes do exist if attrs and isinstance(attrs, tuple): t = tuple(a for a in attrs if a not in globalocals) if t: s = _Fmt.SQUARE(_s_, len(t)) if len(t) > 1 else _NN t = _COMMASPACE_.join( _DOT_(_pygeodesy_, mod, a) for a in t) raise ImportError('missing %s%s: %s' % (_attribute_, s, t)) ns.extend(attrs) # XXX if ps: # check that mod is a _pygeodesy_ module # XXX m = globalocals[mod] # assert(m.__name__ == mod) # XXX f = getattr(m, '__file__', _NN) # XXX d = dirname(abspath(f)) if f else pygeodesy_abspath # XXX p = getattr(m, '__package__', _NN) or _pygeodesy_ # XXX if p not in ps or d != pygeodesy_abspath: # XXX raise ImportError('foreign module: %s from %r' % (_DOT_(p, mod), f or p)) return tuple(set(ns)) # remove duplicates
def utmupsStr(self, B=False): '''Get the UTM/UPS zone, band and hemisphere/-pole (C{str}). ''' b = self.band if B else NN h = s = self.hemisphere if h: s = _SPACE_ return NN(Fmt.zone(self.zone), b, s, h)
def _xkwds_Error(_xkwds_func, kwds, name_txt, txt='default'): # Helper for _xkwds_get and _xkwds_pop below from pygeodesy.streprs import Fmt, pairs f = _COMMASPACE_.join(pairs(kwds) + pairs(name_txt)) f = Fmt.PAREN(_xkwds_func.__name__, f) t = _multiple_ if name_txt else _no_ t = _SPACE_(t, _EQUAL_(_name_, txt), 'kwargs') return _AssertionError(f, txt=t)
def __getattr__(self, name): '''Get the value of an attribute or item by B{C{name}}. ''' try: return tuple.__getitem__(self, self._Names_.index(name)) except IndexError: raise _IndexError(_DOT_(self.classname, Fmt.ANGLE(_name_)), name) except ValueError: return tuple.__getattribute__(self, name)
def toRepr(self, **kwds): # PYCHOK expected '''(INTERNAL) I{Could be overloaded}. @kwarg kwds: Optional, keyword arguments. @return: C{toStr}() with keyword arguments (as C{str}). ''' t = self.toStr(**kwds).lstrip('([{').rstrip('}])') return Fmt.PAREN(self.classname, t) # XXX (self.named, t)
def _intersects2(center1, r1, center2, r2, sphere=True, too_d=None, # in .ellipsoidalBase._intersections2 Vector=None, **Vector_kwds): # (INTERNAL) Intersect two spheres or circles, see L{intersections2} # above, separated to allow callers to embellish any exceptions def _V3(x, y, z): v = Vector3d(x, y, z) n = intersections2.__name__ return _V_n(v, n, Vector, Vector_kwds) def _xV3(c1, u, x, y): xy1 = x, y, _1_0 # transform to original space return _V3(fdot(xy1, u.x, -u.y, c1.x), fdot(xy1, u.y, u.x, c1.y), _0_0) c1 = _otherV3d(sphere=sphere, center1=center1) c2 = _otherV3d(sphere=sphere, center2=center2) if r1 < r2: # r1, r2 == R, r c1, c2 = c2, c1 r1, r2 = r2, r1 m = c2.minus(c1) d = m.length if d < max(r2 - r1, EPS): raise ValueError(_near_concentric_) o = fsum_(-d, r1, r2) # overlap == -(d - (r1 + r2)) # compute intersections with c1 at (0, 0) and c2 at (d, 0), like # <https://MathWorld.Wolfram.com/Circle-CircleIntersection.html> if o > EPS: # overlapping, r1, r2 == R, r x = _radical2(d, r1, r2).xline y = _1_0 - (x / r1)**2 if y > EPS: y = r1 * sqrt(y) # y == a / 2 elif y < 0: raise ValueError(_invalid_) else: # abutting y = _0_0 elif o < 0: t = d if too_d is None else too_d raise ValueError(_too_(Fmt.distant(t))) else: # abutting x, y = r1, _0_0 u = m.unit() if sphere: # sphere center and radius c = c1 if x < EPS else ( c2 if x > EPS1 else c1.plus(u.times(x))) t = _V3(c.x, c.y, c.z), Radius(y) elif y > 0: # intersecting circles t = _xV3(c1, u, x, y), _xV3(c1, u, x, -y) else: # abutting circles t = _xV3(c1, u, x, 0) t = t, t return t
def _callname(name, class_name, self_name, up=1): # imported by .points '''(INTERNAL) Assemble the name for an invokation. ''' n, c = class_name, callername(up=up + 1) if c: n = _DOT_(n, Fmt.PAREN(c, name)) if self_name: n = _SPACE_(n, repr(self_name)) return n
def reverse(self, x, y, name=NN, LatLon=None, **LatLon_kwds): '''Convert an azimuthal gnomonic location to (ellipsoidal) geodetic lat- and longitude. @arg x: Easting of the location (C{meter}). @arg y: Northing of the location (C{meter}). @kwarg name: Optional name for the location (C{str}). @kwarg LatLon: Class to use (C{LatLon}) or C{None}. @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword arguments, ignored if C{B{LatLon}=None}. @return: The geodetic (C{LatLon}) or if B{C{LatLon}} is C{None} an L{Azimuthal7Tuple}C{(x, y, lat, lon, azimuth, scale, datum)}. @raise AzimuthalError: No convergence. @note: The C{lat} will be in the range C{[-90..90] degrees} and C{lon} in the range C{[-180..180] degrees}. The C{azimuth} is clockwise from true North. The scale is C{1 / reciprocal**2} in C{radial} direction and C{1 / reciprocal} in the direction perpendicular to this. ''' x = Scalar(x=x) y = Scalar(y=y) z = atan2d(x, y) # (x, y) for azimuth from true North q = hypot(x, y) d = e = self.equatoradius s = e * atan(q / e) if q > e: def _d(r, q): return (r.M12 - q * r.m12) * r.m12 # negated q = 1 / q else: # little == True def _d(r, q): # PYCHOK _d return (q * r.M12 - r.m12) * r.M12 # negated e *= _Karney_eps S = Fsum(s) g = self.geodesic.Line(self.lat0, self.lon0, z, self._mask) for self._iteration in range(1, _TRIPS): r = g.Position(s, self._mask) if abs(d) < e: break s, d = S.fsum2_(_d(r, q)) else: raise AzimuthalError(x=x, y=y, txt=_no_(Fmt.convergence(e))) t = self._7Tuple(x, y, r, r.M12) if LatLon is None else \ self._toLatLon(r.lat2, r.lon2, LatLon, LatLon_kwds) t._iteration = self._iteration return self._xnamed(t, name=name)
def _xml(tag, xml): '''(INTERNAL) Get a <tag>value</tag> from XML. ''' # b'<?xml version="1.0" encoding="utf-8" ?> # <USGS_Elevation_Point_Query_Service> # <Elevation_Query x="-121.914200" y="37.881600"> # <Data_Source>3DEP 1/3 arc-second</Data_Source> # <Elevation>3851.03</Elevation> # <Units>Feet</Units> # </Elevation_Query> # </USGS_Elevation_Point_Query_Service>' i = xml.find(Fmt.TAG(tag)) if i > 0: i += len(tag) + 2 j = xml.find(Fmt.TAGEND(tag), i) if j > i: return Str(xml[i:j].strip(), name=tag) return _no_(_XML_, Fmt.TAG(tag))
def __init__(self, knots, weight=None, name=NN): '''New L{HeightLSQBiSpline} interpolator. @arg knots: The points with known height (C{LatLon}s). @kwarg weight: Optional weight or weights for each B{C{knot}} (C{scalar} or C{scalar}s). @kwarg name: Optional name for this height interpolator (C{str}). @raise HeightError: Insufficient number of B{C{knots}} or an invalid B{C{knot}} or B{C{weight}}. @raise LenError: Unequal number of B{C{knots}} and B{C{weight}}s. @raise ImportError: Package C{numpy} or C{scipy} not found or not installed. @raise SciPyError: A C{LSQSphereBivariateSpline} issue. @raise SciPyWarning: A C{LSQSphereBivariateSpline} warning as exception. ''' np, spi = self._NumSciPy() xs, ys, hs = self._xyhs3(knots) n = len(hs) w = weight if isscalar(w): w = float(w) if w <= 0: raise HeightError(weight=w) w = [w] * n elif w is not None: m, w = len2(w) if m != n: raise LenError(HeightLSQBiSpline, weight=m, knots=n) w = map2(float, w) m = min(w) if m <= 0: raise HeightError(Fmt.SQUARE(weight=w.find(m)), m) try: T = 1.0e-4 # like SciPy example ps = np.array(_ordedup(xs, T, PI2 - T)) ts = np.array(_ordedup(ys, T, PI - T)) self._ev = spi.LSQSphereBivariateSpline(ys, xs, hs, ts, ps, eps=EPS, w=w).ev except Exception as x: raise _SciPyIssue(x) if name: self.name = name
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
def _points2(self, points): '''(INTERNAL) Check a set of points. ''' np, ps = Hausdorff._points2(self, points) for i, p in enumerate(ps): if not callable(getattr(p, _distanceTo_, None)): raise HausdorffError(Fmt.SQUARE(_points_, i), p, txt=_distanceTo_) return np, ps
def _txt(c1, r1, c2, r2): # check for concentric or too distant spheres d = c1.minus(c2).length if d < abs(r1 - r2): t = _near_concentric_ elif d > (r1 + r2): t = _too_(Fmt.distant(d)) else: return NN return _SPACE_(c1.name, 'and', c2.name, t)
def __new__(cls, arg=None, name=_degrees_, Error=UnitError, suffix=_NSEW_, low=None, high=None, **name_arg): '''New named C{Degrees} instance. @arg cls: This class (C{Degrees_} or sub-class). @kwarg arg: The value (any C{type} convertable to C{float} or parsable by L{parseDMS}). @kwarg name: Optional instance name (C{str}). @kwarg Error: Optional error to raise, overriding the default L{UnitError}. @kwarg suffix: Optional, valid compass direction suffixes (C{NSEW}). @kwarg low: Optional lower B{C{arg}} limit (C{float} or C{None}). @kwarg high: Optional upper B{C{arg}} limit (C{float} or C{None}). @kwarg name_arg: Optional C{name=arg} keyword argument, inlieu of B{C{name}} and B{C{arg}}. @returns: A C{Degrees} instance. @raise Error: Invalid B{C{arg}} or B{C{abs(deg)}} below B{C{low}} or above B{C{high}}. ''' if name_arg: name, arg = _xkwds_popitem(name_arg) self = Degrees.__new__(cls, arg=arg, name=name, Error=Error, suffix=suffix, clip=0) if (low is not None) and self < low: txt = Fmt.limit(below=low) elif (high is not None) and self > high: txt = Fmt.limit(above=high) else: return self raise _Error(cls, arg, name=name, Error=Error, txt=txt)
def _toStr(self, hemipole, B, cs, prec, sep): '''(INTERNAL) Return a string for this ETM/UTM/UPS coordinate. ''' z = NN(Fmt.zone(self.zone), (self.band if B else NN)) # PYCHOK band t = (z, hemipole) + _fstrENH2(self, prec, None)[0] if cs: prec = cs if isint(cs) else 8 # for backward compatibility t += (_n_a_ if self.convergence is None else degDMS( self.convergence, prec=prec, pos=_PLUS_), _n_a_ if self.scale is None else fstr(self.scale, prec=prec)) return t if sep is None else sep.join(t)
def _instr(self, prec, *attrs, **kwds): '''(INTERNAL) Format, used by C{Conic}, C{Ellipsoid}, C{Transform}. ''' t = Fmt.EQUAL(_name_, repr(self.name)), if attrs: t += pairs(((a, getattr(self, a)) for a in attrs), prec=prec, ints=True) if kwds: t += pairs(kwds, prec=prec) return _COMMASPACE_.join(t)
def toStr(self, prec=6, sep=_COMMASPACE_, **unused): # PYCHOK signature '''Return this C{Named-Tuple} items as string(s). @kwarg prec: The C{float} precision, number of decimal digits (0..9). Trailing zero decimals are stripped for B{C{prec}} values of 1 and above, but kept for negative B{C{prec}} values. @kwarg sep: Optional separator to join (C{str}). @return: Tuple items (C{str}). ''' return Fmt.PAREN(sep.join(reprs(self, prec=prec)))
def _validate(self, _OK=False): # see .EcefMatrix '''(INTERNAL) One-time check of C{_Names_} and C{_Units_} for each C{_NamedUnit} I{sub-class separately}. ''' ns = self._Names_ if not (isinstance(ns, tuple) and len(ns) > 1): # XXX > 0 raise _TypeError(_DOT_(self.classname, _Names_), ns) for i, n in enumerate(ns): if not _xvalid(n, _OK=_OK): t = Fmt.SQUARE(_Names_, i) raise _ValueError(_DOT_(self.classname, t), n) us = self._Units_ if not isinstance(us, tuple): raise _TypeError(_DOT_(self.classname, _Units_), us) if len(us) != len(ns): raise LenError(self.__class__, _Units_=len(us), _Names_=len(ns)) for i, u in enumerate(us): if not (u is None or callable(u)): t = Fmt.SQUARE(_Units_, i) raise _TypeError(_DOT_(self.classname, t), u) self.__class__._validated = True
def _xyhs(lls, off=True, name='llis'): # map (lat, lon, h) to (x, y, h) in radians, offset as # x: 0 <= lon <= PI2, y: 0 <= lat <= PI if off is True # else x: -PI <= lon <= PI, y: -PI_2 <= lat <= PI_2 if off: xf = yf = _0_0 else: # undo offset xf, yf = PI, PI_2 try: for i, ll in enumerate(lls): yield (max(0.0, radiansPI2(ll.lon + 180.0)) - xf), \ (max(0.0, radiansPI( ll.lat + 90.0)) - yf), ll.height except AttributeError as x: raise HeightError(Fmt.SQUARE(name, i), ll, txt=str(x))
def add(self, key, value, *values): '''Add C{[key] = value}, typically C{[attr] = mod}. @raise AssertionError: The B{C{key}} already exists with different B{C{value}}. ''' if key in self: val = self[key] # duplicate OK if val != value and val not in values: # PYCHOK no cover from pygeodesy.streprs import Fmt as _Fmt t = _Fmt.SQUARE(_imports_, key), val, value raise AssertionError('%s: %r, not %r' % t) else: self[key] = value
def __init__(self, where, **lens_txt): # txt=None '''New L{LenError}. @arg where: Object with C{.__name__} attribute (C{class}, C{method}, or C{function}). @kwarg lens_txt: Two or more C{name=len(name)} pairs (C{keyword arguments}). ''' from pygeodesy.streprs import Fmt as _Fmt x = _xkwds_pop(lens_txt, txt=_invalid_) ns, vs = zip(*sorted(lens_txt.items())) ns = _COMMASPACE_.join(ns) vs = ' vs '.join(map(str, vs)) t = _SPACE_(_Fmt.PAREN(where.__name__, ns), _len_, vs) _ValueError.__init__(self, t, txt=x)
def elevation2(lat, lon, timeout=2.0): '''Get the geoid elevation at an C{NAD83} to C{NAVD88} location. @arg lat: Latitude (C{degrees}). @arg lon: Longitude (C{degrees}). @kwarg timeout: Optional, query timeout (seconds). @return: An L{Elevation2Tuple}C{(elevation, data_source)} or (C{None, "error"}) in case of errors. @raise ValueError: Invalid B{C{timeout}}. @note: The returned C{elevation} is C{None} if B{C{lat}} or B{C{lon}} is invalid or outside the C{Conterminous US (CONUS)}, if conversion failed or if the query timed out. The C{error} is the C{HTTP-, IO-, SSL-, Type-, URL-} or C{ValueError} as a string (C{str}). @see: U{USGS National Map<https://NationalMap.gov/epqs>}, the U{FAQ<https://www.USGS.gov/faqs/what-are-projection- horizontal-and-vertical-datum-units-and-resolution-3dep-standard-dems>}, U{geoid.py<https://Gist.GitHub.com/pyRobShrk>}, module L{geoids}, classes L{GeoidG2012B}, L{GeoidKarney} and L{GeoidPGM}. ''' try: x = _qURL('https://NED.USGS.gov/epqs/pqs.php', # 'https://NationalMap.gov/epqs/pqs.php' x=Lon(lon).toStr(prec=6), y=Lat(lat).toStr(prec=6), units='Meters', # 'Feet', capitalized output=_XML_.lower(), # _JSON_, lowercase only timeout=Scalar(timeout=timeout)) if x[:6] == '<?xml ': e = _xml('Elevation', x) try: e = float(e) if -1000000 < e < 1000000: return Elevation2Tuple(e, _xml('Data_Source', x)) e = 'non-CONUS %.2F' % (e,) except (TypeError, ValueError): pass else: e = _no_(_XML_, Fmt.QUOTE2(clips(x, limit=128, white=_SPACE_))) except (HTTPError, IOError, TypeError, ValueError) as x: e = repr(x) e = _error(elevation2, lat, lon, e) return Elevation2Tuple(None, e)
def _qURL(url, timeout=2, **params): '''(INTERNAL) Build B{C{url}} query, get and verify response. ''' if params: # build url query, don't map(quote, params)! p = '&'.join(Fmt.EQUAL(p, v) for p, v in params.items() if v) if p: url = NN(url, '?', p) u = urlopen(url, timeout=timeout) # secs s = u.getcode() if s != 200: # http.HTTPStatus.OK or http.client.OK raise IOError('code %d: %s' % (s, u.geturl())) r = u.read() u.close() # urlcleanup() return ub2str(r).strip()
def geoidHeight2(lat, lon, model=0, timeout=2.0): '''Get the C{NAVD88} geoid height at an C{NAD83} location. @arg lat: Latitude (C{degrees}). @arg lon: Longitude (C{degrees}). @kwarg model: Optional, geoid model ID (C{int}). @kwarg timeout: Optional, query timeout (seconds). @return: An L{GeoidHeight2Tuple}C{(height, model_name)} or C{(None, "error"}) in case of errors. @raise ValueError: Invalid B{C{timeout}}. @note: The returned C{height} is C{None} if B{C{lat}} or B{C{lon}} is invalid or outside the C{Conterminous US (CONUS)}, if the B{C{model}} was invalid, if conversion failed or if the query timed out. The C{error} is the C{HTTP-, IO-, SSL-, Type-, URL-} or C{ValueError} as a string (C{str}). @see: U{NOAA National Geodetic Survey <https://www.NGS.NOAA.gov/INFO/geodesy.shtml>}, U{Geoid<https://www.NGS.NOAA.gov/web_services/geoid.shtml>}, U{USGS10mElev.py<https://Gist.GitHub.com/pyRobShrk>}, module L{geoids}, classes L{GeoidG2012B}, L{GeoidKarney} and L{GeoidPGM}. ''' try: j = _qURL('https://Geodesy.NOAA.gov/api/geoid/ght', lat=Lat(lat).toStr(prec=6), lon=Lon(lon).toStr(prec=6), model=(model if model else NN), timeout=Scalar(timeout=timeout)) # PYCHOK indent if j[:1] == '{' and j[-1:] == '}' and j.find('"error":') > 0: d, e = _json(j), 'geoidHeight' if isinstance(_xkwds_get(d, error=_n_a_), float): h = d.get(e, None) if h is not None: m = _xkwds_get(d, geoidModel=_n_a_) return GeoidHeight2Tuple(h, m) else: e = _JSON_ e = _no_(e, Fmt.QUOTE2(clips(j, limit=256, white=_SPACE_))) except (HTTPError, IOError, ParseError, TypeError, ValueError) as x: e = repr(x) e = _error(geoidHeight2, lat, lon, e) return GeoidHeight2Tuple(None, e)