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 ValueError: Invalid B{C{beta}}, negative B{C{ds}} value, weighted B{C{ds}} below L{EPS} or unequal C{len}C{(}B{C{ds}}C{)} and C{len}C{(}B{C{xs}}C{)}. @note: Using B{C{beta}}C{=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, name=_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(_item_sq('ds', ds.index(d)), d) return x
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=_item_sq(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 _points2(self, points): '''(INTERNAL) Check a set of points. ''' np, ps = Frechet._points2(self, points) for i, p in enumerate(ps): if not callable(getattr(p, _distanceTo_, None)): raise FrechetError(_item_sq(_points_, i), p, txt=_distanceTo_) return np, ps
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: Number of B{C{knots}} and B{C{weight}}s don't match. @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(_item_sq(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 _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(_item_sq(name, i), ll, txt=str(x))
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(_item_sq(_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=_item_sq(points=i)) return Points2Tuple(n, points)
def _all_imports(**more): '''(INTERNAL) Build C{dict} of all lazy imports. ''' # imports naming conventions stored below - [<key>] = <from>: # import <module> - [<module>] = <module> # from <module> import <attr> - [<attr>] = <module> # from pygeodesy import <attr> - [<attr>] = <attr> # from <module> import <attr> as <name> - [<name>] = <module>.<attr> imports = {} for _all_ in (_ALL_LAZY, _ALL_OVERRIDING, more): for mod, attrs in _all_.items(): if isinstance(attrs, tuple) and not mod.startswith(_UNDERSCORE_): if mod not in imports: imports[mod] = mod elif imports[mod] != mod: t = _item_sq('imports', 'mod'), imports[mod], mod raise AssertionError('%s: %r, not %r' % t) for attr in attrs: attr, _, _as_ = attr.partition(' as ') if _as_: imports[_as_] = mod + _DOT_ + attr else: imports[attr] = mod return imports
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(_item_sq('llis', i), ll, txt=str(x))
def __setitem__(self, key, value): if key == _name_: raise KeyError('%s = %r' % (_item_sq(self.classname, key), value)) dict.__setitem__(self, key, value)
def __getitem__(self, key): if key == _name_: raise KeyError(_item_sq(self.classname, key)) return dict.__getitem__(self, key)
def _Error(i): return WGRSError(_item_sq(georef=i), georef)
def isenclosedBy(self, points): '''Check whether a (convex) polygon encloses this point. @arg points: The polygon points (L{LatLon}[]). @return: C{True} if the polygon encloses this point, C{False} otherwise. @raise PointsError: Insufficient number of B{C{points}}. @raise TypeError: Some B{C{points}} are not L{LatLon}. @raise ValueError: Invalid B{C{points}}, non-convex polygon. @example: >>> b = LatLon(45,1), LatLon(45,2), LatLon(46,2), LatLon(46,1) >>> p = LatLon(45,1, 1.1) >>> inside = p.isEnclosedBy(b) # True ''' n, points = self.points2(points, closed=True) n0 = self._N_vector if iterNumpy2(points): v1 = points[n - 1]._N_vector v2 = points[n - 2]._N_vector gc1 = v2.cross(v1) t0 = gc1.angleTo(n0) > PI_2 for i in range(n): v2 = points[i]._N_vector gc = v1.cross(v2) v1 = v2 ti = gc.angleTo(n0) > PI_2 if ti != t0: return False # outside if gc1.angleTo(gc, vSign=n0) < 0: raise _ValueError(_item_sq(points=i), points[i], txt=_not_convex_) gc1 = gc else: # get great-circle vector for each edge gc, v1 = [], points[n - 1]._N_vector for i in range(n): v2 = points[i]._N_vector gc.append(v1.cross(v2)) v1 = v2 # check whether this point on same side of all # polygon edges (to the left or right depending # on anti-/clockwise polygon direction) t0 = gc[0].angleTo(n0) > PI_2 # True if on the right for i in range(1, n): ti = gc[i].angleTo(n0) > PI_2 if ti != t0: # different sides of edge i return False # outside # check for convex polygon (otherwise # the test above is not reliable) gc1 = gc[n - 1] for i, gc2 in enumerate(gc): # angle between gc vectors, signed by direction of n0 if gc1.angleTo(gc2, vSign=n0) < 0: raise _ValueError(_item_sq(points=i), points[i], txt=_not_convex_) gc1 = gc2 return True # inside
def _Error(i): return GARSError(garef=_item_sq(repr(garef), i))