def _streprs(prec, objs, fmt, ints, floats, strepr): '''(INTERNAL) Helper for C{fstr}, C{pairs}, C{reprs} and C{strs} ''' if fmt in _EeFfGg: fGg = fmt in _Gg fmt = '%.' + str(abs(prec)) + fmt elif fmt.startswith('%'): fGg = False fmt = fmt.replace('*', str(abs(prec))) else: t = repr('[%s]%s' % ('%.*', '|'.join(_EeFfGg))) raise ValueError('%s not %s: %r' % ('fmt', t, fmt)) for o in objs: if floats or isinstance(o, float): t = fmt % (float(o), ) if ints and (isint(o, both=True) or # for ... # corner case testLcc lon0=-96.0 t.rstrip('0').endswith('.')): t = t.split('.')[0] elif prec > 1: t = fstrzs(t, ap1z=fGg) elif strepr: t = strepr(o) else: raise IsnotError('scalar', floats=o) yield t
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 pairs(items, prec=6, fmt='F', ints=False, sep='='): '''Convert items to I{name=value} strings, with C{float}s handled like L{fstr}. @arg items: Name-value pairs (C{dict} or 2-{tuple}s of any C{type}s). @kwarg prec: The C{float} precision, number of decimal digits (0..9). Trailing zero decimals are stripped if B{C{prec}} is positive, but kept for negative B{C{prec}} values. @kwarg fmt: Optional, float format (C{str}). @kwarg ints: Optionally, remove the decimal dot (C{bool}). @kwarg sep: Separator joining I{names} and I{values} (C{str}). @return: A C{tuple(sep.join(t) for t in zip(names, reprs(values,...)))} of C{str}s. ''' try: if isinstance(items, dict): items = sorted(items.items()) elif not isinstance(items, (list, tuple)): items = tuple(items) # can't unzip empty items tuple, list, etc. n, v = zip(*items) if items else ((), ()) except (TypeError, ValueError): raise IsnotError('dict', '2-tuples', items=items) v = _streprs(prec, v, fmt, ints, False, repr) return tuple(sep.join(t) for t in zip(map(str, n), v))
def latlon2(self, datum=None): '''Convert this WM coordinate to a lat- and longitude. @kwarg datum: Optional ellipsoidal datum (C{Datum}). @return: A L{LatLon2Tuple}C{(lat, lon)}. @raise TypeError: Non-ellipsoidal B{C{datum}}. @see: Method C{toLatLon}. ''' r = self.radius x = self._x / r y = 2 * atan(exp(self._y / r)) - PI_2 if datum: _xinstanceof(Datum, datum=datum) E = datum.ellipsoid if not E.isEllipsoidal: raise IsnotError('ellipsoidal', datum=datum) # <https://Earth-Info.NGA.mil/GandG/wgs84/web_mercator/ # %28U%29%20NGA_SIG_0011_1.0.0_WEBMERC.pdf> y = y / r if E.e: y -= E.e * atanh(E.e * tanh(y)) # == E.es_atanh(tanh(y)) y *= E.a x *= E.a / r r = LatLon2Tuple(degrees90(y), degrees180(x)) return self._xnamed(r)
def datum(self, datum): '''Set this cartesian's C{datum} I{without conversion}. @arg datum: New datum (L{Datum}). @raise TypeError: The B{C{datum}} is not a L{Datum}. ''' _xinstanceof(Datum, datum=datum) d = self.datum if d is not None: if d.isEllipsoidal and not datum.isEllipsoidal: raise IsnotError('ellipsoidal', datum=datum) elif d.isSpherical and not datum.isSpherical: raise IsnotError('spherical', datum=datum) self._update(datum != d) self._datum = datum
def hausdorff_(model, target, both=False, early=True, seed=None, units='', distance=None, point=_point): '''Compute the C{directed} or C{symmetric} U{Hausdorff distance<https:// WikiPedia.org/wiki/Hausdorff_distance>} between 2 sets of points with or without U{early breaking<https://Publik.TUWien.ac.AT/files/PubDat_247739.pdf>} and U{random sampling<https://Publik.TUWien.ac.AT/files/PubDat_247739.pdf>}. @arg model: First set of points (C{LatLon}[], C{Numpy2LatLon}[], C{Tuple2LatLon}[] or C{other}[]). @arg target: Second set of points (C{LatLon}[], C{Numpy2LatLon}[], C{Tuple2LatLon}[] or C{other}[]). @kwarg both: Return the C{directed} (forward only) or the C{symmetric} (combined forward and reverse) C{Hausdorff} distance (C{bool}). @kwarg early: Enable or disable U{early breaking<https://Publik.TUWien.ac.AT/ files/PubDat_247739.pdf>} (C{bool}). @kwarg seed: Random sampling seed (C{any}) or C{None}, C{0} or C{False} for no U{random sampling<https://Publik.TUWien.ac.AT/files/PubDat_247739.pdf>}. @kwarg units: Optional, name of the distance units (C{str}). @kwarg distance: Callable returning the distance between a B{C{model}} and B{C{target}} point (signature C{(point1, point2)}). @kwarg point: Callable returning the B{C{model}} or B{C{target}} point suitable for B{C{distance}} (signature C{(point)}). @return: A L{Hausdorff6Tuple}C{(hd, i, j, mn, md, units)}. @raise HausdorffError: Insufficient number of B{C{model}} or B{C{target}} points. @raise TypeError: If B{C{distance}} or B{C{point}} is not callable. ''' if not callable(distance): raise IsnotError(callable.__name__, distance=distance) if not callable(point): raise IsnotError(callable.__name__, point=point) _, ps1 = points2(model, closed=False, Error=HausdorffError) _, ps2 = points2(target, closed=False, Error=HausdorffError) return _hausdorff_(ps1, ps2, both, early, seed, units, distance, point)
def __new__(cls, cll, precision=3, name=''): '''New L{Georef} from an other L{Georef} instance or georef C{str} or from a C{LatLon} instance or lat-/longitude C{str}. @arg cll: Cell or location (L{Georef} or C{str}, C{LatLon} or C{str}). @kwarg precision: Optional, the desired georef resolution and length (C{int} 0..11), see function L{wgrs.encode} for more details. @kwarg name: Optional name (C{str}). @return: New L{Georef}. @raise RangeError: Invalid B{C{cll}} lat- or longitude. @raise TypeError: Invalid B{C{cll}}. @raise WGRSError: INValid or non-alphanumeric B{C{cll}}. ''' h = None if isinstance(cll, Georef): g, p = _2geostr2(str(cll)) self = str.__new__(cls, g) self._latlon = LatLon2Tuple(*cll._latlon) self._name = cll._name self._precision = p # cll._precision elif isstr(cll): if ',' in cll: lat, lon, h = _2fllh(*parse3llh(cll, height=None)) g = encode(lat, lon, precision=precision, height=h) # PYCHOK false self = str.__new__(cls, g) self._latlon = LatLon2Tuple(lat, lon) self._precision = Precision_(precision, high=_MaxPrec) else: self = str.__new__(cls, cll.upper()) self._decode() else: # assume LatLon try: lat, lon, h = _2fllh(cll.lat, cll.lon) h = getattr(cll, 'height', h) except AttributeError: raise IsnotError('valid', **{Georef.__name__: cll}) g = encode(lat, lon, precision=precision, height=h) # PYCHOK false self = str.__new__(cls, g) self._latlon = LatLon2Tuple(lat, lon) self._precision = Precision_(precision, high=_MaxPrec) if h not in (None, _MISSING): self._height = Height(h) if name: self.name = name return self
def _2Geohash(geohash): '''(INTERNAL) Check or create a Geohash instance. ''' if not isinstance(geohash, Geohash): try: geohash = Geohash(geohash) except (TypeError, ValueError): raise IsnotError(Geohash.__name__, str.__name__, 'LatLon', geohash=geohash) return geohash
def times(self, factor): '''Multiply this vector by a scalar. @arg factor: Scale factor (C{scalar}). @return: New, scaled vector (L{Vector3d}). @raise TypeError: Non-scalar B{C{factor}}. ''' if not isscalar(factor): raise IsnotError('scalar', factor=factor) return self.classof(self.x * factor, self.y * factor, self.z * factor)
def to4Tuple(self, datum): '''Extend this L{PhiLam3Tuple} to a L{PhiLam4Tuple}. @arg datum: The datum to add (C{Datum}). @return: A L{PhiLam4Tuple}C{(phi, lam, height, datum)}. @raise TypeError: If B{C{datum}} not a C{Datum}. ''' from pygeodesy.datum import Datum if not isinstance(datum, Datum): raise IsnotError(Datum.__name__, datum=datum) return self._xtend(PhiLam4Tuple, datum)
def datum(self, datum): '''Set this point's datum I{without conversion}. @arg datum: New datum (L{Datum}). @raise TypeError: The B{C{datum}} is not a L{Datum} or not ellipsoidal. ''' _xinstanceof(Datum, datum=datum) if not datum.isEllipsoidal: raise IsnotError('ellipsoidal', datum=datum) self._update(datum != self._datum) self._datum = datum
def _xrenamed(self, inst): '''(INTERNAL) Rename the instance' C{.name = self.name}. @arg inst: The instance (C{_Named}). @return: The B{C{inst}}, named if not named before. ''' if not isinstance(inst, _Named): raise IsnotError('valid', inst=inst) if inst.name != self.name: inst.name = self.name return inst
def toDatum(self, datum): '''Convert this conic to the given datum. @arg datum: Ellipsoidal datum to use (L{Datum}). @return: Converted conic, unregistered (L{Conic}). @raise TypeError: Non-ellipsoidal B{C{datum}}. ''' E = datum.ellipsoid if not E.isEllipsoidal: raise IsnotError('ellipsoidal', datum=datum) c = self if c._e != E.e or c._datum != datum: c = Conic(None, 0, name=self._name) self._dup2(c) c._datum = datum c._e = E.e if abs(c._par1 - c._par2) < EPS: m1 = c._mdef(c._phi0) t1 = c._tdef(c._phi0) t0 = t1 k = 1 n = sin(c._phi0) sp = 1 else: m1 = c._mdef(c._par1) m2 = c._mdef(c._par2) t1 = c._tdef(c._par1) t2 = c._tdef(c._par2) t0 = c._tdef(c._phi0) k = c._k0 n = (log(m1) - log(m2)) \ / (log(t1) - log(t2)) sp = 2 F = m1 / (n * pow(t1, n)) c._aF = k * E.a * F c._n = n c._n_ = 1 / n c._r0 = c._rdef(t0) c._SP = sp return c
def frange(start, number, step=1): '''Generate a range of C{float}s. @arg start: First value (C{float}). @arg number: The number of C{float}s to generate (C{int}). @kwarg step: Increment value (C{float}). @return: A generator (C{float}s). @see: U{NumPy.prod<https://docs.SciPy.org/doc/ numpy/reference/generated/numpy.arange.html>}. ''' if not isint(number): raise IsnotError(int.__name_, number=number) for i in range(number): yield start + i * step
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 frechet_(points1, points2, distance=None, units=''): '''Compute the I{discrete} U{Fréchet<https://WikiPedia.org/wiki/Frechet_distance>} distance between two paths given as sets of points. @arg points1: First set of points (C{LatLon}[], L{Numpy2LatLon}[], L{Tuple2LatLon}[] or C{other}[]). @arg points2: Second set of points (C{LatLon}[], L{Numpy2LatLon}[], L{Tuple2LatLon}[] or C{other}[]). @kwarg distance: Callable returning the distance between a B{C{points1}} and a B{C{points2}} point (signature C{(point1, point2)}). @kwarg units: Optional, name of the distance units (C{str}). @return: A L{Frechet6Tuple}C{(fd, fi1, fi2, r, n, units)} where C{fi1} and C{fi2} are type C{int} indices into B{C{points1}} respectively B{C{points2}}. @raise FrechetError: Insufficient number of B{C{points1}} or B{C{points2}}. @raise RecursionError: Recursion depth exceeded, see U{sys.getrecursionlimit() <https://docs.Python.org/3/library/sys.html#sys.getrecursionlimit>}. @raise TypeError: If B{C{distance}} is not a callable. @note: Function L{frechet_} does not support I{fractional} indices for intermediate B{C{points1}} and B{C{points2}}. ''' if not callable(distance): raise IsnotError(callable.__name__, distance=distance) n1, ps1 = _points2(points1, closed=False, Error=FrechetError) n2, ps2 = _points2(points2, closed=False, Error=FrechetError) def dF(i1, i2): return distance(ps1[i1], ps2[i2]) return _frechet_(n1, 1, n2, 1, dF, units)
def _2epoch(epoch): # imported by .ellipsoidalBase.py '''(INTERNAL) Validate an C{epoch}. ''' if isscalar(epoch) and epoch > 0: # XXX 1970? return _F(epoch) raise IsnotError('scalar', epoch=epoch)