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 _xtend(self, xTuple, *items): '''(INTERNAL) Extend this C{Named-Tuple} with C{items} to an other B{C{xTuple}}. ''' if not (issubclassof(xTuple, _NamedTuple) and (len(self._Names_) + len(items)) == len(xTuple._Names_) and self._Names_ == xTuple._Names_[:len(self)]): c = NN(self.classname, repr(self._Names_)) x = NN(xTuple.__name__, repr(xTuple._Names_)) raise TypeError(_SPACE_(c, _vs_, x)) return self._xnamed(xTuple(*(self + items)))
def clips(bstr, limit=50, white=NN): '''Clip a string to the given length limit. @arg bstr: String (C{bytes} or C{str}). @kwarg limit: Length limit (C{int}). @kwarg white: Optionally, replace all whitespace (C{str}). @return: The clipped or unclipped B{C{bstr}}. ''' if len(bstr) > limit > 8: h = limit // 2 bstr = NN(bstr[:h], type(bstr)('....'), bstr[-h:]) if white: # replace whitespace bstr = type(bstr)(white).join(bstr.split()) return bstr
def callername(up=1, dflt=NN, source=False): '''Get the name of the calling callable. @kwarg up: Number of call stack frames up (C{int}). @kwarg dflt: Default return value (C{any}). @kwarg source: Include source file name and line number (C{bool}). @return: Name of the non-internal callable (C{str}) or B{C{dflt}} if none found. ''' try: from sys import _getframe for u in range(up, up + 32): f = _getframe(u) n = f.f_code.co_name if n and (n.startswith(_DUNDER_) or not n.startswith(_UNDERSCORE_)): if source: from os.path import basename n = NN.join( (n, _AT_, basename(f.f_code.co_filename), _COLON_, str(f.f_lineno))) return n except (AttributeError, ImportError): pass return dflt
def utmupsStr(self, B=False): '''Get the UTM/UPS zone, band and hemisphere/-pole (C{str}). ''' b = self.band if B else NN h = s = self.hemisphere if h: s = _SPACE_ return NN(Fmt.zone(self.zone), b, s, h)
def fstrzs(efstr, ap1z=False): '''Strip trailing zero decimals from a C{float} string. @arg efstr: Float with or without exponent (C{str}). @kwarg ap1z: Append the decimal point and one zero decimal if the B{C{efstr}} is all digits (C{bool}). @return: Float (C{str}). ''' s = efstr.find(_DOT_) if s >= 0: e = efstr.rfind(Fmt.e) if e < 0: e = efstr.rfind(Fmt.E) if e < 0: e = len(efstr) s += 2 # keep 1st _DOT_ + _0_ if s < e and efstr[e - 1] == _0_: efstr = NN(efstr[:s], efstr[s:e].rstrip(_0_), efstr[e:]) elif ap1z: # %.G and %.g formats may drop the decimal # point and all trailing zeros, ... if efstr.isdigit(): efstr += _DOT_ + _0_ # ... append or ... else: # ... insert one dot and zero e = efstr.rfind(Fmt.e) if e < 0: e = efstr.rfind(Fmt.E) if e > 0: efstr = NN(efstr[:e], _DOT_, _0_, efstr[e:]) return efstr
def hStr(self, prec=-2, m=NN): '''Return a string for the height B{C{h}}. @kwarg prec: Optional number of decimals, unstripped (C{int}). @kwarg m: Optional unit of the height (C{str}). @see: Function L{hstr}. ''' return NN(self.H, hstr(self.h, prec=prec, m=m))
def _clipped_(angle, limit, units): '''(INTERNAL) Helper for C{clipDegrees} and C{clipRadians}. ''' c = min(limit, max(-limit, angle)) if c != angle and _rangerrors: t = NN(fstr(angle, prec=6, ints=True), 'beyond', copysign(limit, angle), units) raise RangeError(t, txt=None) return c
def encode(lat, lon, precision=3, height=None, radius=None): # MCCABE 14 '''Encode a lat-/longitude as a C{georef} of the given precision. @arg lat: Latitude (C{degrees}). @arg lon: Longitude (C{degrees}). @kwarg precision: Optional, the desired C{georef} resolution and length (C{int} 0..11). @kwarg height: Optional, height in C{meter}, see U{Designation of area <https://WikiPedia.org/wiki/World_Geographic_Reference_System>}. @kwarg radius: Optional, radius in C{meter}, see U{Designation of area <https://WikiPedia.org/wiki/World_Geographic_Reference_System>}. @return: The C{georef} (C{str}). @raise RangeError: Invalid B{C{lat}} or B{C{lon}}. @raise WGRSError: Invalid B{C{precision}}, B{C{height}} or B{C{radius}}. @note: The B{C{precision}} value differs from U{Georef<https:// GeographicLib.SourceForge.io/html/classGeographicLib_1_1Georef.html>}. The C{georef} length is M{2 * (precision + 1)} and the C{georef} resolution is I{15°} for B{C{precision}} 0, I{1°} for 1, I{1′} for 2, I{0.1′} for 3, I{0.01′} for 4, ... M{10**(2 - precision)}. ''' def _option(name, m, m2_, K): f = Scalar_(m, name=name, Error=WGRSError) return '%s%d' % (name[0].upper(), int(m2_(f * K) + 0.5)) def _pstr(p, x): return '%0*d' % (p, x) p = _2Precision(precision) lat, lon, _ = _2fllh(lat, lon) if lat == 90: lat *= EPS1_2 xt, xd, x = _2divmod3(lon, _LonOrig_M_) yt, yd, y = _2divmod3(lat, _LatOrig_M_) g = _LonTile[xt], _LatTile[yt] if p > 0: g += _DegChar[xd], _DegChar[yd] p -= 1 if p > 0: d = pow(_Base, _MaxPrec - p) x = _pstr(p, x // d) y = _pstr(p, y // d) g += x, y if radius is not None: # R before H g += _option(_radius_, radius, m2NM, 1.0), if height is not None: # H is last g += _option(_height_, height, m2ft, 1e-3), return NN.join(g) # XXX Georef(''.join(g))
def _DDDMMSS_(strDDDMMSS, suffix, sep, clip): S = suffix.upper() if isstr(strDDDMMSS): t = strDDDMMSS.strip() if sep: t = t.replace(sep, NN).strip() s = t[:1] # sign or digit P = t[-1:] # compass point, digit or dot t = t.lstrip(_PLUSMINUS_).rstrip(S).strip() f = t.split(_DOT_) d = len(f[0]) f = NN.join(f) if 1 < d < 8 and f.isdigit() and ( (P in S and s.isdigit()) or (P.isdigit() and s in '-0123456789+' # PYCHOK indent and S in ((_NS_, _EW_) + _WINDS))): # check [D]DDMMSS form and compass point X = _EW_ if (d & 1) else _NS_ if not (P in X or (S in X and (P.isdigit() or P == _DOT_))): t = 'DDDMMSS'[d & 1 ^ 1:d | 1], X[:1], X[1:] raise ParseError('form %s applies %s-%s' % t) f = 0 # fraction else: # try other forms return _DMS2deg(strDDDMMSS, S, sep, clip) else: # float or int to [D]DDMMSS[.fff] f = float(strDDDMMSS) s = _MINUS_ if f < 0 else NN P = _0_ # anything except _SW_ f, i = modf(abs(f)) t = Fmt.f(i, prec=0) # str(i) == 'i.0' d = len(t) # bump number of digits to match # the given, valid compass point if S in (_NS_ if (d & 1) else _EW_): t = _0_ + t d += 1 # P = S # elif d > 1: # P = (_EW_ if (d & 1) else _NS_)[0] if d < 4: # [D]DD[.ddd] if f: t = float(t) + f t = t, 0, 0 else: f += float(t[d - 2:]) if d < 6: # [D]DDMM[.mmm] t = t[:d - 2], f, 0 else: # [D]DDMMSS[.sss] t = t[:d - 4], t[d - 4:d - 2], f d = _dms2deg(s, P, *map2(float, t)) return clipDegrees(d, float(clip)) if clip else d
def _update(self, updated, *attrs): '''(INTERNAL) Zap cached instance attributes. ''' if updated and attrs: for a in attrs: # zap attrs to None if getattr(self, a, None) is not None: setattr(self, a, None) elif not hasattr(self, a): a = NN(_DOT_, a, _SPACE_, _invalid_) raise _AssertionError(a, txt=repr(self))
def __init__(self, Class, *Classes, **name): '''New C{_NamedEnum}. @arg Class: Initial class or type acceptable as enum values (C{str}). @arg Classes: Additional, acceptable classes or types. ''' self._item_Classes = (Class, ) + Classes n = name.get(_name_, NN) or NN(Class.__name__, _s_) if n and _xvalid(n, _OK=True): _Named.name.fset(self, n) # see _Named.name
def _toStr(self, hemipole, B, cs, prec, sep): '''(INTERNAL) Return a string for this ETM/UTM/UPS coordinate. ''' z = NN(Fmt.zone(self.zone), (self.band if B else NN)) # PYCHOK band t = (z, hemipole) + _fstrENH2(self, prec, None)[0] if cs: prec = cs if isint(cs) else 8 # for backward compatibility t += (_n_a_ if self.convergence is None else degDMS( self.convergence, prec=prec, pos=_PLUS_), _n_a_ if self.scale is None else fstr(self.scale, prec=prec)) return t if sep is None else sep.join(t)
def _boolkwds(inst, **name_value_pairs): # in .frechet, .hausdorff, .heights '''(INTERNAL) Set applicable C{bool} properties/attributes. ''' for n, v in name_value_pairs.items(): b = getattr(inst, n, None) if b is None: # invalid bool attr t = _SPACE_(_EQUAL_(n, repr(v)), 'for', inst.__class__.__name__) # XXX .classname raise _AttributeError(t, txt=_not_('applicable')) if v in (False, True) and v != b: setattr(inst, NN(_UNDER_, n), v)
def degDMS(deg, prec=6, s_D=S_DEG, s_M=S_MIN, s_S=S_SEC, neg=_MINUS_, pos=NN): '''Convert degrees to a string in degrees, minutes B{I{or}} seconds. @arg deg: Value in degrees (C{scalar}). @kwarg prec: Optional number of decimal digits (0..9 or C{None} for default). Trailing zero decimals are stripped for B{C{prec}} values of 1 and above, but kept for negative B{C{prec}}. @kwarg s_D: Symbol for degrees (C{str}). @kwarg s_M: Symbol for minutes (C{str}) or C{""}. @kwarg s_S: Symbol for seconds (C{str}) or C{""}. @kwarg neg: Optional sign for negative (C{'-'}). @kwarg pos: Optional sign for positive (C{''}). @return: I{Either} degrees, minutes B{I{or}} seconds (C{str}). ''' try: deg = float(deg) except (TypeError, ValueError) as x: raise _ValueError(deg=deg, txt=str(x)) d, s = abs(deg), s_D if d < 1: if s_M: d *= _60_0 if d < 1 and s_S: d *= _60_0 s = s_S else: s = s_M elif s_S: d *= 3600 s = s_S n = neg if deg < 0 else pos z = int(prec) t = NN(n, Fmt.F(d, prec=abs(z))) if z > 1: t = fstrzs(t) return NN(t, s)
def hstr(height, prec=2, fmt=Fmt.h, ints=False, m=NN): '''Return a string for the height value. @arg height: Height value (C{float}). @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, C{float} format (C{str}). @kwarg ints: Optionally, remove the decimal dot for C{int} values (C{bool}). @kwarg m: Optional unit of the height (C{str}). ''' h = next(_streprs(prec, (height, ), fmt, ints, True, None)) return NN(h, str(m)) if m else h
def __call__(self, *name_value_, **name_value): '''Format a C{name=value} pair or C{name, value} pair or just a single C{value}. ''' for n, v in name_value.items(): break else: if len(name_value_) > 1: n, v = name_value_[:2] elif name_value_: n, v = NN, name_value_[0] else: n, v = NN, MISSING t = str.__mod__(self, v) return NN(n, t) if n else t
def encode(lat, lon, precision=1): # MCCABE 14 '''Encode a lat-/longitude as a C{garef} of the given precision. @arg lat: Latitude (C{degrees}). @arg lon: Longitude (C{degrees}). @kwarg precision: Optional, the desired C{garef} resolution and length (C{int} 0..2). @return: The C{garef} (C{str}). @raise RangeError: Invalid B{C{lat}} or B{C{lon}}. @raise GARSError: Invalid B{C{precision}}. @note: The C{garef} length is M{precision + 5} and the C{garef} resolution is B{30′} for B{C{precision}} 0, B{15′} for 1 and B{5′} for 2, respectively. ''' def _digit(x, y, m): return _Digits[m * (m - y - 1) + x + 1], def _str(chars, x, n): s, b = [], len(chars) for i in range(n): x, i = divmod(x, b) s.append(chars[i]) return tuple(reversed(s)) p = _2Precision(precision) lat, lon = _2fll(lat, lon) if lat == 90: lat *= EPS1_2 ix, x = _2divmod2(lon, _LonOrig_M_) iy, y = _2divmod2(lat, _LatOrig_M_) g = _str(_Digits, ix + 1, _LonLen) + _str(_Letters, iy, _LatLen) if p > 0: ix, x = divmod(x, _M3) iy, y = divmod(y, _M3) g += _digit(ix, iy, _M2) if p > 1: g += _digit(x, y, _M3) return NN.join(g)
def _qURL(url, timeout=2, **params): '''(INTERNAL) Build B{C{url}} query, get and verify response. ''' if params: # build url query, don't map(quote, params)! p = '&'.join(Fmt.EQUAL(p, v) for p, v in params.items() if v) if p: url = NN(url, '?', p) u = urlopen(url, timeout=timeout) # secs s = u.getcode() if s != 200: # http.HTTPStatus.OK or http.client.OK raise IOError('code %d: %s' % (s, u.geturl())) r = u.read() u.close() # urlcleanup() return ub2str(r).strip()
def _error_init(Error, inst, name_value, fmt_name_value='%s (%r)', txt=_invalid_, **name_values): # by .lazily '''(INTERNAL) Format an error text and initialize an C{Error} instance. @arg Error: The error super-class (C{Exception}). @arg inst: Sub-class instance to be initialized (C{_Exception}). @arg name_value: Either just a value or several name, value, ... positional arguments (C{str}, any C{type}), in particular for name conflicts with keyword arguments of C{error_init} or which can't be used as C{name=value} keyword arguments. @kwarg name_value_fmt: Format for (name, value) (C{str}). @kwarg txt: Optional explanation of the error (C{str}). @kwarg name_values: One or more B{C{name=value}} pairs overriding any B{C{name_value}} positional arguments. ''' if name_values: t = _or(*(fmt_name_value % t for t in name_values.items())) # XXX sorted elif len(name_value) > 1: t = _or(*(fmt_name_value % t for t in zip(name_value[0::2], name_value[1::2]))) elif name_value: t = str(name_value[0]) else: t = '%s %s' % (_Missing, _Missing) if txt is None: x = NN else: x = str(txt) or _invalid_ c = _COMMA_ if _COLON_ in t else _COLON_ t = NN.join((t, c, _SPACE_, x)) Error.__init__(inst, t) # inst.__x_txt__ = x # hold explanation _cause_(inst) # no Python 3+ exception chaining _ename_(inst)
def callername(up=1, dflt=NN, source=False): '''Get the name of the calling callable. @kwarg up: Number of call stack frames up (C{int}). @kwarg dflt: Default return value (C{any}). @kwarg source: Include source file name and line number (C{bool}). @return: Name of the non-internal callable (C{str}) or B{C{dflt}} if none found. ''' try: # see .lazily._caller3 for u in range(up, up + 32): n, f, s = _caller3(u) if n and (n.startswith(_DUNDER_) or not n.startswith(_UNDER_)): if source: n = NN(n, _AT_, f, _COLON_, str(s)) return n except (AttributeError, ValueError): # PYCHOK no cover pass return dflt
def toStr(self, prec=10, sep=_SPACE_): # PYCHOK expected '''Return a string representation of this MGRS grid reference. Note that MGRS grid references are truncated, not rounded (unlike UTM coordinates). @kwarg prec: Optional number of digits (C{int}), 4:km, 10:m. @kwarg sep: Optional separator to join (C{str}) or C{None} to return an unjoined C{tuple} of C{str}s. @return: This Mgrs as "00B EN easting northing" (C{str}). @raise ValueError: Invalid B{C{prec}}. @example: >>> m = Mgrs(31, 'DQ', 48251, 11932, band='U') >>> m.toStr() # '31U DQ 48251 11932' ''' t = NN(Fmt.zone(self._zone), self._band) t = enstr2(self._easting, self._northing, prec, t, self._en100k) return t if sep is None else sep.join(t)
def __init__(self): for n, a in self.__class__.__dict__.items(): if isinstance(a, (Fstr, _Fmt)): setattr(a, _name_, n) Fmt = Fmt() # PYCHOK singleton Fmt.__name__ = Fmt.__class__.__name__ _DOTSTAR_ = Fmt.DOT(_STAR_) # formats %G and %g drop all trailing zeros and the # decimal point, making the float appear as an int _Gg = (Fmt.G, Fmt.g) _FfEeGg = (Fmt.F, Fmt.f, Fmt.E, Fmt.e) + _Gg # float formats _Fspec_ = NN('[%[<flags>][<width>]', _DOTSTAR_, ']', _BAR_.join(_FfEeGg)) # in testStreprs 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, )
def _toRepr(self, value): '''(INTERNAL) Representation "<name> (<value>)" or "<classname>(<value>)". ''' t = NN(self.name, _SPACE_) if self.name else self.classname return Fmt.PAREN(t, value)
def _toDMS(deg, form, prec, sep, ddd, suff): # MCCABE 15 by .units.py '''(INTERNAL) Convert degrees to C{str}, with/-out sign and/or suffix. ''' try: deg = float(deg) except (TypeError, ValueError) as x: raise _ValueError(deg=deg, txt=str(x)) form = form.lower() sign = form[:1] if sign in _PLUSMINUS_: form = form[1:] else: sign = S_NUL if prec is None: z = p = _F_prec.get(form, 6) else: z = int(prec) p = abs(z) w = p + (1 if p else 0) d = abs(deg) if form in _F_symb: s_deg = s_min = s_sec = S_NUL # no symbols else: s_deg, s_min, s_sec = S_DEG, S_MIN, S_SEC F = _F_case.get(form, F_DMS) if F is F_DMS: # 'deg+min+sec' d, s = divmod(round(d * _3600_0, p), _3600_0) m, s = divmod(s, _60_0) t = NN(_0wpF(ddd, 0, d), s_deg, sep, _0wpF(2, 0, m), s_min, sep, _0wpF(w + 2, p, s)) s = s_sec elif F is F_DM: # 'deg+min' d, m = divmod(round(d * _60_0, p), _60_0) t = NN(_0wpF(ddd, 0, d), s_deg, sep, _0wpF(w + 2, p, m)) s = s_min elif F is F_D: # 'deg' t = _0wpF(ddd + w, p, d) s = s_deg elif F is F_RAD: t = NN(_PERCENTDOTSTAR_, 'F') % (p, radians(d)) s = S_RAD else: # F in (F__E, F__F, F__G) t = NN(_PERCENTDOTSTAR_, F) % (p, d) s = S_NUL if z > 1: t = fstrzs(t, ap1z=F is F__G) if sign: if deg < 0: t = _MINUS_ + t elif deg > 0 and sign == _PLUS_: t = _PLUS_ + t elif suff: # and deg: # zero suffix? s += sep + suff return t + s
def _option(name, m, m2_, K): f = Scalar_(m, name=name, Error=WGRSError) return NN(name[0].upper(), int(m2_(f * K) + _0_5))
def _invokationError(name, *args): # PYCHOK no cover '''(INTERNAL) Return an L{EllipticError}. ''' return EllipticError(_SPACE_('invokation', NN(name, repr(args)))) # unstr
def _trilaterate(point1, distance1, point2, distance2, point3, distance3, radius=R_M, height=None, useZ=False, **LatLon_LatLon_kwds): # (INTERNAL) Locate a point at given distances from # three other points, see LatLon.triangulate above def _nd2(p, d, r, _i_, *qs): # .toNvector and angular distance squared for q in qs: if p.isequalTo(q, EPS): raise _ValueError(points=p, txt=_coincident_) return p.toNvector(), (Scalar(d, name=_distance_ + _i_) / r)**2 r = Radius_(radius) n1, r12 = _nd2(point1, distance1, r, _1_) n2, r22 = _nd2(point2, distance2, r, _2_, point1) n3, r32 = _nd2(point3, distance3, r, _3_, point1, point2) # the following uses x,y coordinate system with origin at n1, x axis n1->n2 y = n3.minus(n1) x = n2.minus(n1) z = None d = x.length # distance n1->n2 if d > EPS_2: # and y.length > EPS_2: X = x.unit() # unit vector in x direction n1->n2 i = X.dot(y) # signed magnitude of x component of n1->n3 Y = y.minus(X.times(i)).unit() # unit vector in y direction j = Y.dot(y) # signed magnitude of y component of n1->n3 if abs(j) > EPS_2: # courtesy Carlos Freitas <https://GitHub.com/mrJean1/PyGeodesy/issues/33> x = fsum_(r12, -r22, d**2) / (2 * d) # n1->intersection x- and ... y = fsum_(r12, -r32, i**2, j**2, -2 * x * i) / ( 2 * j) # ... y-component # courtesy AleixDev <https://GitHub.com/mrJean1/PyGeodesy/issues/43> z = fsum_(max(r12, r22, r32), -(x**2), -(y**2)) # XXX not just r12! if z > EPS: n = n1.plus(X.times(x)).plus(Y.times(y)) if useZ: # include Z component Z = X.cross(Y) # unit vector perpendicular to plane n = n.plus(Z.times(sqrt(z))) if height is None: h = fidw((point1.height, point2.height, point3.height), map1(fabs, distance1, distance2, distance3)) else: h = Height(height) kwds = _xkwds(LatLon_LatLon_kwds, height=h) return n.toLatLon(** kwds) # Nvector(n.x, n.y, n.z).toLatLon(...) # no intersection, d < EPS_2 or abs(j) < EPS_2 or z < EPS t = NN(_no_, _intersection_, _SPACE_) raise IntersectionError(point1=point1, distance1=distance1, point2=point2, distance2=distance2, point3=point3, distance3=distance3, txt=unstr(t, z=z, useZ=useZ))
def _inverse(self, other, azis, wrap): '''(INTERNAL) Inverse Vincenty method. @raise TypeError: The B{C{other}} point is not L{LatLon}. @raise ValueError: If this and the B{C{other}} point's L{Datum} ellipsoids are not compatible. @raise VincentyError: Vincenty fails to converge for the current L{LatLon.epsilon} and L{LatLon.iterations} limit and/or if this and the B{C{other}} point are coincident or near-antipodal. ''' E = self.ellipsoids(other) c1, s1, _ = _r3(self.lat, E.f) c2, s2, _ = _r3(other.lat, E.f) c1c2, s1c2 = c1 * c2, s1 * c2 c1s2, s1s2 = c1 * s2, s1 * s2 dl, _ = unroll180(self.lon, other.lon, wrap=wrap) ll = dl = radians(dl) for self._iteration in range(1, self._iterations + 1): ll_ = ll sll, cll = sincos2(ll) ss = hypot(c2 * sll, c1s2 - s1c2 * cll) if ss < EPS: # coincident or antipodal, ... if self.isantipodeTo(other, eps=self._epsilon): t = '%r %sto %r' % (self, _antipodal_, other) raise VincentyError(_ambiguous_, txt=t) # return zeros like Karney, but unlike Veness return Distance3Tuple(0.0, 0, 0) cs = s1s2 + c1c2 * cll s = atan2(ss, cs) sa = c1c2 * sll / ss c2a = 1 - sa**2 if abs(c2a) < EPS: c2a = 0 # equatorial line ll = dl + E.f * sa * s else: c2sm = cs - 2 * s1s2 / c2a ll = dl + _dl(E.f, c2a, sa, s, cs, ss, c2sm) if abs(ll - ll_) < self._epsilon: break # # omitted and applied only after failure to converge below, see footnote # # under Inverse at <https://WikiPedia.org/wiki/Vincenty's_formulae> # # <https://GitHub.com/ChrisVeness/geodesy/blob/master/latlon-vincenty.js> # elif abs(ll) > PI and self.isantipodeTo(other, eps=self._epsilon): # raise VincentyError('%s, %r %sto %r' % ('ambiguous', self, # _antipodal_, other)) else: t = _antipodal_ if self.isantipodeTo(other, eps=self._epsilon) else NN t = _SPACE_(repr(self), NN(t, _to_), repr(other)) raise VincentyError(_no_(_convergence_), txt=t) if c2a: # e22 == (a / b)**2 - 1 A, B = _p2(c2a * E.e22) s = A * (s - _ds(B, cs, ss, c2sm)) b = E.b # if self.height or other.height: # b += self._havg(other) d = b * s if azis: # forward and reverse azimuth sll, cll = sincos2(ll) f = atan2b(c2 * sll, c1s2 - s1c2 * cll) r = atan2b(c1 * sll, -s1c2 + c1s2 * cll) else: f = r = 0 return Distance3Tuple(d, f, r)
def _property(method): '''(INTERNAL) Return C{method} as documented C{property.getter}. ''' t = 'get and set' if doc.startswith(_SPACE_) else NN return property(method, None, None, NN('Property to ', t, doc))