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 __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 __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 _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 _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 _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 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 __call__(self, lo, hi, prec=0, lopen=False, ropen=False, join=_COMMASPACE_): '''Return the range as C{"(lo, hi)"}, C{"(lo, hi]"}, C{"[lo, hi)"} or C{"[lo, hi]"}. ''' from pygeodesy.streprs import Fmt # PYCHOK re-imported r = NN(Fmt.f(lo, prec=prec), join, Fmt.f(hi, prec=prec)) if lopen: r = Fmt.PAREN(r) if ropen else Fmt.LOPEN(r) else: r = Fmt.ROPEN(r) if ropen else Fmt.SQUARE(r) return r
def __init__(self, knots, beta=2, name=NN, **distanceTo_kwds): '''New L{HeightIDWdistanceTo} interpolator. @arg knots: The points with known height (C{LatLon}s). @kwarg beta: Inverse distance power (C{int} 1, 2, or 3). @kwarg name: Optional name for this height interpolator (C{str}). @kwarg distanceTo_kwds: Optional keyword arguments for the B{C{points}}' C{LatLon.distanceTo} method. @raise HeightError: Insufficient number of B{C{knots}} or an invalid B{C{knot}} or B{C{beta}}. @raise ImportError: Package U{geographiclib <https://PyPI.org/project/geographiclib>} missing iff B{C{points}} are L{ellipsoidalKarney.LatLon}s. @note: All B{C{points}} I{must} be instances of the same ellipsoidal or spherical C{LatLon} class, I{not checked however}. ''' n, self._ks = len2(knots) if n < self._kmin: raise _insufficientError(self._kmin, knots=n) for i, k in enumerate(self._ks): if not callable(getattr(k, _distanceTo_, None)): raise HeightError(Fmt.SQUARE(_knots_, i), k, txt=_distanceTo_) # use knots[0] class and datum to create # compatible points in _HeightBase._height # instead of class LatLon_ and datum None self._datum = self._ks[0].datum self._LLis = self._ks[0].classof self.beta = beta if name: self.name = name if distanceTo_kwds: self._distanceTo_kwds = distanceTo_kwds
def points2(points, closed=True, base=None, Error=PointsError): '''Check a path or polygon represented by points. @arg points: The path or polygon points (C{LatLon}[]) @kwarg closed: Optionally, consider the polygon closed, ignoring any duplicate or closing final B{C{points}} (C{bool}). @kwarg base: Optionally, check all B{C{points}} against this base class, if C{None} don't check. @kwarg Error: Exception to raise (C{ValueError}). @return: A L{Points2Tuple}C{(number, points)} with the number of points and the points C{list} or C{tuple}. @raise PointsError: Insufficient number of B{C{points}}. @raise TypeError: Some B{C{points}} are not B{C{base}} compatible. ''' n, points = len2(points) if closed: # remove duplicate or closing final points while n > 1 and points[n-1] in (points[0], points[n-2]): n -= 1 # XXX following line is unneeded if points # are always indexed as ... i in range(n) points = points[:n] # XXX numpy.array slice is a view! if n < (3 if closed else 1): raise Error(points=n, txt=_too_(_few_)) if base and not (isNumpy2(points) or isTuple2(points)): for i in range(n): base.others(points[i], name=Fmt.SQUARE(points=i)) return Points2Tuple(n, points)
def fidw(xs, ds, beta=2): '''Interpolate using using U{Inverse Distance Weighting <https://WikiPedia.org/wiki/Inverse_distance_weighting>} (IDW). @arg xs: Known values (C{scalar}[]). @arg ds: Non-negative distances (C{scalar}[]). @kwarg beta: Inverse distance power (C{int}, 0, 1, 2, or 3). @return: Interpolated value C{x} (C{float}). @raise LenError: Unequal or zero C{len(B{ds})} and C{len(B{xs})}. @raise ValueError: Invalid B{C{beta}}, negative B{C{ds}} value, weighted B{C{ds}} below L{EPS}. @note: Using C{B{beta}=0} returns the mean of B{C{xs}}. ''' n, xs = len2(xs) d, ds = len2(ds) if n != d or n < 1: raise LenError(fidw, xs=n, ds=d) d, x = min(zip(ds, xs)) if d > EPS and n > 1: b = -Int_(beta=beta, low=0, high=3) if b < 0: ds = tuple(d**b for d in ds) d = fsum(ds) if d < EPS: raise _ValueError(ds=d) x = fdot(xs, *ds) / d else: x = fmean(xs) elif d < 0: raise _ValueError(Fmt.SQUARE(ds=ds.index(d)), d) return x
def _xy2(lls): try: # like _xyhs above, but keeping degrees for i, ll in enumerate(lls): yield ll.lon, ll.lat except AttributeError as x: raise HeightError(Fmt.SQUARE(llis=i), ll, txt=str(x))
def _Error(i): return WGRSError(Fmt.SQUARE(georef=i), georef)
def __getitem__(self, key): '''Get the value of an item by B{C{key}}. ''' if key == _name_: raise KeyError(Fmt.SQUARE(self.classname, key)) return dict.__getitem__(self, key)
def _Error(i): return GARSError(garef=Fmt.SQUARE(repr(garef), i))