def __init__(self, a_ellipsoid, f, name): '''(INTERNAL) New C{Ecef...}. ''' try: E = a_ellipsoid if f is None: if isinstance(E, Datum): self._datum = E E = E.ellipsoid elif not isinstance(E, Ellipsoid): raise TypeError if not name: name = E.name elif isscalar(E) and isscalar(f): a = float(E) f_ = (1.0 / f) if f else 0 # sphere b = None if f_ else a E = Ellipsoid(a, b, f_, name='_' + name) else: raise ValueError if not (E.a > 0 and E.f < 1): raise ValueError except (TypeError, ValueError): t = unstr(self.classname, a=E, f=f) raise EcefError('%s invalid: %s' % ('ellipsoid', t)) self._E = E if name: self.name = name
def _height(self, lats, lons, Error=HeightError): if isscalar(lats) and isscalar(lons): llis = LatLon_(lats, lons) else: n, lats = len2(lats) m, lons = len2(lons) if n != m: raise Error('non-matching %s: %s vs %s' % ('len', n, m)) llis = [LatLon_(*ll) for ll in zip(lats, lons)] return self(llis) # __call__(lli) or __call__(llis)
def _height(self, lats, lons, Error=HeightError): LLis, d = self._LLis, self.datum if isscalar(lats) and isscalar(lons): llis = LLis(lats, lons, datum=d) else: n, lats = len2(lats) m, lons = len2(lons) if n != m: # format a LenError, but raise an Error e = LenError(self.__class__, lats=n, lons=m, txt=None) raise e if Error is LenError else Error(str(e)) llis = [LLis(*ll, datum=d) for ll in zip(lats, lons)] return self(llis) # __call__(lli) or __call__(llis)
def fractional(points, fi, LatLon=None, **LatLon_kwds): '''Return the point at a given I{fractional} index. @arg points: The points (C{LatLon}[], L{Numpy2LatLon}[], L{Tuple2LatLon}[] or C{other}[]). @arg fi: The fractional index (C{float} or C{int}). @kwarg LatLon: Optional class to return the I{intermediate}, I{fractional} point (C{LatLon}) or C{None}. @kwarg LatLon_kwds: Optional B{C{LatLon}} keyword arguments, ignored of B{C{LatLon=None}}. @return: A B{C{LatLon}} or if B{C{LatLon}} is C{None}, a L{LatLon2Tuple}C{(lat, lon)} for B{C{points[fi]}} if I{fractional} index B{C{fi}} is C{int}, otherwise the intermediate point between B{C{points[int(fi)]}} and B{C{points[int(fi) + 1]}} for C{float} I{fractional} index B{C{fi}}. @raise IndexError: Fractional index B{C{fi}} invalid or B{C{points}} not subscriptable. ''' try: if not (isscalar(fi) and 0 <= fi < len(points)): raise IndexError p = _fractional(points, fi) except (IndexError, TypeError): raise _IndexError(fractional.__name__, fi) if LatLon and isinstance(p, LatLon2Tuple): p = LatLon(*p, **LatLon_kwds) return p
def fadd(self, iterable): '''Accumulate more values from an iterable. @arg iterable: Sequence, list, tuple, etc. (C{scalar}s). @raise OverflowError: Partial C{2sum} overflow. @raise TypeError: Non-scalar B{C{iterable}} value. @raise ValueError: Invalid or non-finite B{C{iterable}} value. ''' if isscalar(iterable): # for backward compatibility iterable = tuple(iterable) ps = self._ps for a in map(float, iterable): # _iter() if not isfinite(a): raise _ValueError(iterable=a, txt=_not_(_finite_)) i = 0 for p in ps: a, p = _2sum(a, p) if p: ps[i] = p i += 1 ps[i:] = [a] self._n += 1 # assert self._ps is ps self._fsum2_ = None
def __imul__(self, other): '''Multiply this instance by a scalar or an other instance. @arg other: L{Fsum} instance or C{scalar}. @return: This instance, updated (L{Fsum}). @raise TypeError: Invalid B{C{other}} type. @see: Method L{Fsum.fmul}. ''' if isscalar(other): self.fmul(other) elif isinstance(other, Fsum): ps = list(other._ps) # copy if ps: s = self.fcopy() self.fmul(ps.pop()) while ps: # self += s * ps.pop() p = s.fcopy() p.fmul(ps.pop()) self.fadd(p._ps) else: self._ps = [] # zero self._fsum2_ = None else: raise _TypeError(_SPACE_(self, '*=', repr(other))) return self
def _x3d2(start, end, wrap, n, hs): # see <https://www.EdWilliams.org/intersect.htm> (5) ff a1, b1 = start.philam if isscalar(end): # bearing, make a point a2, b2 = _destination2(a1, b1, PI_4, radians(end)) else: # must be a point _Trll.others(end, name='end' + n) hs.append(end.height) a2, b2 = end.philam db, b2 = unrollPI(b1, b2, wrap=wrap) if max(abs(db), abs(a2 - a1)) < EPS: raise ValueError('intersection %s%s null: %r' % ('path', n, (start, end))) # note, in EdWilliams.org/avform.htm W is + and E is - b21, b12 = db * 0.5, -(b1 + b2) * 0.5 sb21, cb21, sb12, cb12, \ sa21, _, sa12, _ = sincos2(b21, b12, a1 - a2, a1 + a2) x = _Nvector(sa21 * sb12 * cb21 - sa12 * cb12 * sb21, sa21 * cb12 * cb21 + sa12 * sb12 * sb21, cos(a1) * cos(a2) * sin(db)) # ll=start return x.unit(), (db, (a2 - a1)) # negated d
def __mod__(self, arg, **unused): '''Regular C{%} operator. @arg arg: A C{scalar} value to be formatted (either the C{scalar}, or a 1-tuple C{(scalar,)}, or 2-tuple C{(prec, scalar)}. @raise TypeError: Non-scalar B{C{arg}} value. @raise ValueError: Invalid B{C{arg}}. ''' def _error(arg): n = _DOT_(Fstr.__name__, self.name or self) return _SPACE_(n, _PERCENT_, repr(arg)) prec = 6 # default std %f and %F if isinstance(arg, (tuple, list)): n = len(arg) if n == 1: arg = arg[0] elif n == 2: prec, arg = arg else: raise _ValueError(_error(arg)) if not isscalar(arg): raise _TypeError(_error(arg)) return self(arg, prec=prec)
def toStr(self, prec=3, sep=_SPACE_, radius=False, **unused): # PYCHOK expected '''Return a string representation of this WM coordinate. @kwarg prec: Optional number of decimals, unstripped (C{int}). @kwarg sep: Optional separator to join (C{str}) or C{None} to return an unjoined C{tuple} of C{str}s. @kwarg radius: Optionally, include radius (C{bool} or C{scalar}). @return: This WM as "meter meter" (C{str}) plus " radius" if B{C{radius}} is C{True} or C{scalar}. @raise WebMercatorError: Invalid B{C{radius}}. @example: >>> w = Wm(448251, 5411932.0001) >>> w.toStr(4) # 448251.0 5411932.0001 >>> w.toStr(sep=', ') # 448251, 5411932 ''' fs = self._x, self._y if radius in (False, None): pass elif radius is True: fs += (self._radius, ) elif isscalar(radius): fs += (radius, ) else: raise WebMercatorError(radius=radius) t = strs(fs, prec=prec) return t if sep is None else sep.join(t)
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 _fraction(fraction, n): f = 1 # int, no fractional indices if fraction in (None, 1): pass elif not (isscalar(fraction) and EPS < fraction < EPS1 and (float(n) - fraction) < n): raise FrechetError(fraction=fraction) elif fraction < EPS1: f = float(fraction) return f
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 _gc3(self, start, end, namend, raiser=_points_): '''(INTERNAL) Return great circle, start and end Nvectors. ''' s = start.toNvector() if isscalar(end): # bearing gc = s.greatCircle(end) e = None else: self.others(end, name=namend) e = end.toNvector() gc = s.cross(e, raiser=raiser) # XXX .unit()? return gc, s, e
def _to3zBhp(zone, band, hemipole=NN, Error=_ValueError): # imported by .epsg, .ups, .utm, .utmups '''Parse UTM/UPS zone, Band letter and hemisphere/pole letter. @arg zone: Zone with/-out Band (C{scalar} or C{str}). @kwarg band: Optional (longitudinal/polar) Band letter (C{str}). @kwarg hemipole: Optional hemisphere/pole letter (C{str}). @kwarg Error: Optional error to raise, overriding the default C{ValueError}. @return: 3-Tuple (C{zone, Band, hemisphere/pole}) as (C{int, str, 'N'|'S'}) where C{zone} is C{0} for UPS or C{1..60} for UTM and C{Band} is C{'A'..'Z'} I{NOT} checked for valid UTM/UPS bands. @raise ValueError: Invalid B{C{zone}}, B{C{band}} or B{C{hemipole}}. ''' try: B, z = band, _UTMUPS_ZONE_INVALID if isscalar(zone): z = int(zone) elif zone and isstr(zone): if zone.isdigit(): z = int(zone) elif len(zone) > 1: B = zone[-1:] z = int(zone[:-1]) elif zone in 'AaBbYyZz': # single letter B = zone z = _UPS_ZONE if _UTMUPS_ZONE_MIN <= z <= _UTMUPS_ZONE_MAX: hp = hemipole[:1].upper() if hp in _NS_ or not hp: z = Zone(z) B = Band(B.upper()) if B.isalpha(): return z, B, (hp or _NS_[B < _N_]) elif not B: return z, B, hp t = _invalid_ except (AttributeError, IndexError, TypeError, ValueError) as x: t = str(x) # no Python 3+ exception chaining raise Error(zone=zone, band=B, hemipole=hemipole, txt=t)
def fstr(floats, prec=6, fmt='F', ints=False, sep=', '): '''Convert one or more floats to string, optionally stripped of trailing zero decimals. @arg floats: Single or a list, sequence, tuple, etc. (C{scalar}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 the B{C{floats}} (C{str}). @return: The C{sep.join(strs(floats, ...)} joined (C{str}) or single C{strs((floats,), ...)} (C{str}) if B{C{floats}} is C{scalar}. ''' if isscalar(floats): floats = (floats, ) return sep.join(_streprs(prec, floats, fmt, ints, True, None))
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 __iadd__(self, other): '''Add a scalar or an other instance to this instance. @arg other: L{Fsum} instance or C{scalar}. @return: This instance, updated (L{Fsum}). @raise TypeError: Invalid B{C{other}} type. @see: Method L{Fsum.fadd}. ''' if isscalar(other): self.fadd_(other) elif other is self: self.fmul(2) elif isinstance(other, Fsum): self.fadd(other._ps) else: raise _TypeError(_SPACE_(self, '+=', repr(other))) return self
def __isub__(self, other): '''Subtract a scalar or an other instance from this instance. @arg other: L{Fsum} instance or C{scalar}. @return: This instance, updated (L{Fsum}). @raise TypeError: Invalid B{C{other}} type. @see: Method L{Fsum.fadd}. ''' if isscalar(other): self.fadd_(-other) elif other is self: self._ps = [] # zero self._fsum2_ = None elif isinstance(other, Fsum): self.fadd(-p for p in other._ps) else: raise _TypeError(_SPACE_(self, '-=', repr(other))) return self
def _to3zBhp(zone, band, hemipole=''): # imported by .epsg, .ups, .utm, .utmups '''Parse UTM/UPS zone, Band letter and hemisphere/pole letter. @arg zone: Zone with/-out Band (C{scalar} or C{str}). @kwarg band: Optional (longitudinal/polar) Band letter (C{str}). @kwarg hemipole: Optional hemisphere/pole letter (C{str}). @return: 3-Tuple (C{zone, Band, hemisphere/pole}) as (C{int, str, 'N'|'S'}) where C{zone} is C{0} for UPS or C{1..60} for UTM and C{Band} is C{'A'..'Z'} I{NOT} checked for valid UTM/UPS bands. @raise ValueError: Invalid B{C{zone}}, B{C{band}} or B{C{hemipole}}. ''' B = band try: z = _UTMUPS_ZONE_INVALID if isscalar(zone) or zone.isdigit(): z = int(zone) elif zone and isstr(zone): if len(zone) > 1: B = zone[-1:] z = int(zone[:-1]) elif zone in 'AaBbYyZz': # single letter B = zone z = _UPS_ZONE if _UTMUPS_ZONE_MIN <= z <= _UTMUPS_ZONE_MAX: hp = hemipole[:1].upper() if hp in ('N', 'S') or not hp: B = B.upper() if B.isalpha(): return z, B, (hp or ('S' if B < 'N' else 'N')) elif not B: return z, B, hp except (AttributeError, TypeError, ValueError): pass raise ValueError('%s invalid: %r' % (_or('zone', 'band', 'hemipole'), (zone, B, hemipole)))
def fadd(self, iterable): '''Accumulate more values from an iterable. @arg iterable: Sequence, list, tuple, etc. (C{scalar}s). @raise OverflowError: Partial C{2sum} overflow. @raise TypeError: Non-scalar B{C{iterable}} value. @raise ValueError: Invalid or non-finite B{C{iterable}} value. ''' if isscalar(iterable): # for backward compatibility iterable = tuple(iterable) # def _iter(): # for a in iterable: # if isinstance(a, Fsum): # if a is self: # self.fmul(2) # else: # for a in a._ps: # yield a # else: # yield a ps = self._ps for a in iterable: # _iter() if not isfinite(a): raise ValueError('%s, not %s: %r' % (self, 'finite', a)) i = 0 for p in ps: a, p = _2sum(a, p) if p: ps[i] = p i += 1 ps[i:] = [a] self._n += 1 # assert self._ps is ps self._fsum2_ = None
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 fstr(floats, prec=6, fmt=Fmt.F, ints=False, sep=_COMMASPACE_, strepr=None): '''Convert one or more floats to string, optionally stripped of trailing zero decimals. @arg floats: Single or a list, sequence, tuple, etc. (C{scalar}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. In addition, trailing decimal zeros are stripped for U{alternate, form '#'<https://docs.Python.org/3/library/stdtypes.html #printf-style-string-formatting>}. @kwarg fmt: Optional, C{float} format (C{str}). @kwarg ints: Optionally, remove the decimal dot for C{int} values (C{bool}). @kwarg sep: Separator joining the B{C{floats}} (C{str}). @kwarg strepr: Optional callable to format non-C{floats} (typically C{repr}, C{str}) or C{None} to raise a TypeError. @return: The C{sep.join(strs(floats, ...)} joined (C{str}) or single C{strs((floats,), ...)} (C{str}) if B{C{floats}} is C{scalar}. ''' if isscalar(floats): # see Fstr.__call__ above return next(_streprs(prec, (floats, ), fmt, ints, True, strepr)) else: return sep.join(_streprs(prec, floats, fmt, ints, True, strepr))
def intersection(start1, end1, start2, end2, height=None, wrap=False, LatLon=LatLon, **LatLon_kwds): '''Compute the intersection point of two paths both defined by two points or a start point and bearing from North. @arg start1: Start point of the first path (L{LatLon}). @arg end1: End point ofthe first path (L{LatLon}) or the initial bearing at the first start point (compass C{degrees360}). @arg start2: Start point of the second path (L{LatLon}). @arg end2: End point of the second path (L{LatLon}) or the initial bearing at the second start point (compass C{degrees360}). @kwarg height: Optional height for the intersection point, overriding the mean height (C{meter}). @kwarg wrap: Wrap and unroll longitudes (C{bool}). @kwarg LatLon: Optional class to return the intersection point (L{LatLon}) or C{None}. @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword arguments, ignored if B{C{LatLon=None}}. @return: The intersection point (B{C{LatLon}}) or a L{LatLon3Tuple}C{(lat, lon, height)} if B{C{LatLon}} is C{None}. An alternate intersection point might be the L{antipode} to the returned result. @raise TypeError: A B{C{start}} or B{C{end}} point not L{LatLon}. @raise ValueError: Intersection is ambiguous or infinite or the paths are parallel, coincident or null or invalid B{C{height}}. @example: >>> p = LatLon(51.8853, 0.2545) >>> s = LatLon(49.0034, 2.5735) >>> i = intersection(p, 108.547, s, 32.435) # '50.9078°N, 004.5084°E' ''' _Trll.others(start1, name='start1') _Trll.others(start2, name='start2') hs = [start1.height, start2.height] a1, b1 = start1.philam a2, b2 = start2.philam db, b2 = unrollPI(b1, b2, wrap=wrap) r12 = haversine_(a2, a1, db) if abs(r12) < EPS: # [nearly] coincident points a, b = favg(a1, a2), favg(b1, b2) # see <https://www.EdWilliams.org/avform.htm#Intersection> elif isscalar(end1) and isscalar(end2): # both bearings sa1, ca1, sa2, ca2, sr12, cr12 = sincos2(a1, a2, r12) x1, x2 = (sr12 * ca1), (sr12 * ca2) if abs(x1) < EPS or abs(x2) < EPS: raise ValueError('intersection %s: %r vs %r' % ('parallel', (start1, end1), (start2, end2))) # handle domain error for equivalent longitudes, # see also functions asin_safe and acos_safe at # <https://www.EdWilliams.org/avform.htm#Math> t1, t2 = map1(acos1, (sa2 - sa1 * cr12) / x1, (sa1 - sa2 * cr12) / x2) if sin(db) > 0: t12, t21 = t1, PI2 - t2 else: t12, t21 = PI2 - t1, t2 t13, t23 = map1(radiansPI2, end1, end2) x1, x2 = map1( wrapPI, t13 - t12, # angle 2-1-3 t21 - t23) # angle 1-2-3 sx1, cx1, sx2, cx2 = sincos2(x1, x2) if sx1 == 0 and sx2 == 0: # max(abs(sx1), abs(sx2)) < EPS raise ValueError('intersection %s: %r vs %r' % ('infinite', (start1, end1), (start2, end2))) sx3 = sx1 * sx2 # if sx3 < 0: # raise ValueError('intersection %s: %r vs %r' % ('ambiguous', # (start1, end1), (start2, end2))) x3 = acos1(cr12 * sx3 - cx2 * cx1) r13 = atan2(sr12 * sx3, cx2 + cx1 * cos(x3)) a, b = _destination2(a1, b1, r13, t13) # choose antipode for opposing bearings if _xb(a1, b1, end1, a, b, wrap) < 0 or \ _xb(a2, b2, end2, a, b, wrap) < 0: a, b = antipode_(a, b) # PYCHOK PhiLam2Tuple else: # end point(s) or bearing(s) x1, d1 = _x3d2(start1, end1, wrap, '1', hs) x2, d2 = _x3d2(start2, end2, wrap, '2', hs) x = x1.cross(x2) if x.length < EPS: # [nearly] colinear or parallel paths raise ValueError('intersection %s: %r vs %r' % ('colinear', (start1, end1), (start2, end2))) a, b = x.philam # choose intersection similar to sphericalNvector d1 = _xdot(d1, a1, b1, a, b, wrap) if d1: d2 = _xdot(d2, a2, b2, a, b, wrap) if (d2 < 0 and d1 > 0) or (d2 > 0 and d1 < 0): a, b = antipode_(a, b) # PYCHOK PhiLam2Tuple h = fmean(hs) if height is None else Height(height) return _latlon3(degrees90(a), degrees180(b), h, intersection, LatLon, **LatLon_kwds)
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)
def _2epoch(epoch): # imported by .ellipsoidalBase '''(INTERNAL) Validate an C{epoch}. ''' if isscalar(epoch) and epoch > 0: # XXX 1970? return _F(epoch) raise _TypeError(epoch=epoch, txt=_not_scalar_)