def latlon2(self, datum=None): '''Convert this WM coordinate to a lat- and longitude. @kwarg datum: Optional, ellipsoidal datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}) or C{None}. @return: A L{LatLon2Tuple}C{(lat, lon)}. @raise TypeError: Invalid or 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 is not None: E = _ellipsoidal_datum(datum, name=self.name).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(Lat(degrees90(y)), Lon(degrees180(x))) return self._xnamed(r)
def _streprs(prec, objs, fmt, ints, force, 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(_PERCENT_): fGg = False fmt = fmt.replace(_STAR_, str(abs(prec))) else: t = '[%s]%s' % ('%.*', '|'.join(_EeFfGg)) raise _ValueError(fmt=fmt, txt='not %r' % (t,)) for o in objs: if force 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(_DOT_)): t = t.split(_DOT_)[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(x=x, txt=_not_(_finite_)) if not isint(n): raise _IsnotError(int.__name__, n=n) elif n < 1: raise _ValueError(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 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 _streprs(prec, objs, fmt, ints, force, strepr): '''(INTERNAL) Helper for C{fstr}, C{pairs}, C{reprs} and C{strs} ''' # <https://docs.Python.org/3/library/stdtypes.html#printf-style-string-formatting> if fmt in _FfEeGg: fGg = fmt in _Gg fmt = NN(_PERCENT_, _DOT_, abs(prec), fmt) elif fmt.startswith(_PERCENT_): fGg = False try: # to make sure fmt is valid f = fmt.replace(_DOTSTAR_, Fmt.DOT(abs(prec))) _ = f % (0.0, ) except (TypeError, ValueError): raise _ValueError(fmt=fmt, txt=_not_(repr(_DOTSTAR_))) fmt = f else: raise _ValueError(fmt=fmt, txt=_not_(repr(_Fspec_))) for o in objs: if force 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(_DOT_)): t = t.split(_DOT_)[0] elif prec > 1: t = fstrzs(t, ap1z=fGg) elif strepr: t = strepr(o) else: raise _IsnotError(_scalar_, floats=o) yield t
def _xUnits(units, Base=_NamedUnit): # in .frechet, .hausdorff '''(INTERNAL) Set property C{units} as C{Unit} or C{Str}. ''' if not issubclassof(Base, _NamedUnit): raise _IsnotError(_NamedUnit.__name__, Base=Base) elif issubclassof(units, Base): return units elif isstr(units): return Str( units, name=_units_) # XXX Str to _Pass and for backward compatibility else: raise _IsnotError(Base.__name__, Str.__name__, str.__name__, units=units)
def pairs(items, prec=6, fmt=_Fmt, ints=False, sep=_EQUAL_): '''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.__name__, '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 _xUnit(units, Base): # in .frechet, .hausdorff '''(INTERNAL) Get C{Unit} from C{Unit} or C{name}, ortherwise C{Base}. ''' if not issubclassof(Base, _NamedUnit): raise _IsnotError(_NamedUnit.__name__, Base=Base) U = globals().get(units.capitalize(), Base) if isstr(units) else ( units if issubclassof(units, Base) else Base) return U if issubclassof(U, Base) else Base
def hausdorff_(model, target, both=False, early=True, seed=None, units=NN, 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, the distance units (C{Unit} or 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) # PYCHOK non-sequence _, ps2 = points2(target, closed=False, Error=HausdorffError) # PYCHOK non-sequence return _hausdorff_(ps1, ps2, both, early, seed, units, distance, point)
def _Equidistant2(equidistant, datum): # (INTERNAL) Get an C{Equidistant} or C{EquidistantKarney} instance import pygeodesy.azimuthal as _az if equidistant is None or not callable(equidistant): equidistant = _az.equidistant elif not (issubclassof(equidistant, _az.Equidistant) or issubclassof(equidistant, _az.EquidistantKarney)): raise _IsnotError(_az.Equidistant.__name__, _az.EquidistantKarney.__name__, equidistant=equidistant) return equidistant(0, 0, datum)
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 isfinite(obj): '''Check for C{Inf} and C{NaN} values. @arg obj: Value (C{scalar}). @return: C{False} if B{C{obj}} is C{INF} or C{NAN}, C{True} otherwise. @raise TypeError: Non-scalar B{C{obj}}. ''' if not isscalar(obj): raise _IsnotError(isscalar.__name__, obj=obj) return not (isinf(obj) or isnan(obj))
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 datum(self, datum): '''Set this point's datum I{without conversion}. @arg datum: New datum (L{Datum}). @raise TypeError: If B{C{datum}} is not a L{Datum} or not spherical. ''' _xinstanceof(Datum, datum=datum) if not datum.isSpherical: raise _IsnotError(_spherical_, datum=datum) self._update(datum != self._datum) self._datum = datum
def toDatum(self, datum): '''Convert this conic to the given datum. @arg datum: Ellipsoidal datum to use (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @return: Converted conic, unregistered (L{Conic}). @raise TypeError: Non-ellipsoidal B{C{datum}}. ''' d = _ellipsoidal_datum(datum, name=self.name) E = d.ellipsoid if not E.isEllipsoidal: raise _IsnotError(_ellipsoidal_, datum=datum) c = self if c._e != E.e or c._datum != d: c = Conic(None, 0, name=self._name) self._dup2(c) c._datum = d 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 # _1_0 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_0 / 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) as x: raise VectorError(factor=factor, txt=str(x))
def _spherical_datum(radius, name=NN, raiser=False): '''(INTERNAL) Create a L{Datum} from an L{Ellipsoid}, L{Ellipsoid2} or scalar earth C{radius}. ''' try: d = _ellipsoidal_datum(radius, name=name) except TypeError: d = None if d is None: if not isscalar(radius): _xinstanceof(Datum, Ellipsoid, Ellipsoid2, a_f2Tuple, Scalar, datum=radius) n = _UNDERSCORE_ + name r = Radius_(radius, Error=TypeError) E = Ellipsoid(r, r, name=n) d = Datum(E, transform=Transforms.Identity, name=n) elif raiser and not d.isSpherical: # raiser if no spherical raise _IsnotError(_spherical_, datum=radius) return d
def frechet_(points1, points2, distance=None, units=NN): '''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 utmupsValidate(coord, falsed=False, MGRS=False, Error=UTMUPSError): '''Check a UTM or UPS coordinate. @arg coord: The UTM or UPS coordinate (L{Utm}, L{Ups} or C{5+Tuple}). @kwarg falsed: C{5+Tuple} easting and northing are falsed (C{bool}). @kwarg MGRS: Increase easting and northing ranges (C{bool}). @kwarg Error: Optional error to raise, overriding the default (L{UTMUPSError}). @return: C{None} if validation passed. @raise Error: Validation failed. @see: Function L{utmupsValidateOK}. ''' def _en(en, lo, hi, ename): # U, Error try: if lo <= float(en) <= hi: return except (TypeError, ValueError): pass t = _SPACE_.join( (_outside_, U, _range_, '[%.0F' % (lo, ), '%.0F]' % (hi, ))) raise Error(ename, en, txt=t) if isinstance(coord, (Ups, Utm)): hemi = coord.hemisphere enMM = coord.falsed elif isinstance(coord, (UtmUps5Tuple, UtmUps8Tuple)): hemi = coord.hemipole enMM = falsed else: raise _IsnotError(Error=Error, coord=coord, *map1(modulename, Utm, Ups, UtmUps5Tuple, UtmUps8Tuple)) band = coord.band zone = coord.zone z, B, h = _to3zBhp(zone, band, hemipole=hemi) if z == _UPS_ZONE: # UPS import pygeodesy.ups as u # PYCHOK expected U, M = _UPS_, _UpsMinMax else: # UTM import pygeodesy.utm as u # PYCHOK expected U, M = _UTM_, _UtmMinMax if MGRS: U, s = _MGRS_, _MGRS_TILE else: s = 0 i = _NS_.find(h) if i < 0 or z < _UTMUPS_ZONE_MIN \ or z > _UTMUPS_ZONE_MAX \ or B not in u._Bands: t = '%s(%s%s %s)' % (U, z, B, h) raise Error(coord=t, zone=zone, band=band, hemisphere=hemi) if enMM: _en(coord.easting, M.eMin[i] - s, M.eMax[i] + s, _easting_) # PYCHOK .eMax .eMin _en(coord.northing, M.nMin[i] - s, M.nMax[i] + s, _northing_) # PYCHOK .nMax .nMin
def _xStrError(*Refs, **name_value_Error): '''(INTERNAL) Create a C{TypeError} for C{Garef}, C{Geohash}, C{Wgrs}. ''' r = tuple(r.__name__ for r in Refs) + (Str.__name__, _LatLon_, 'LatLon*Tuple') return _IsnotError(*r, **name_value_Error)