def _sigmaInv(self, xi, eta): '''(INTERNAL) Invert C{sigma} using Newton's method. @return: 2-Tuple C{(u, v)}. @see: C{void TMExact::sigmainv(real xi, real eta, real &u, real &v)}. @raise EllipticError: No convergence. ''' u, v, trip = self._sigmaInv0(xi, eta) if trip: self._iteration = 0 else: U, V = Fsum(u), Fsum(v) # min iterations = 2, max = 7, mean = 3.9 for self._iteration in range(1, _TRIPS): # GEOGRAPHICLIB_PANIC sncndn6 = self._sncndn6(u, v) X, E, _ = self._sigma3(v, *sncndn6) dw, dv = self._sigmaDwd( *sncndn6) X = xi - X E -= eta u, du = U.fsum2_(X * dw, E * dv) v, dv = V.fsum2_(X * dv, -E * dw) if trip: break trip = hypot2(du, dv) < _TOL_10 else: t = unstr(self._sigmaInv.__name__, xi, eta) raise EllipticError(_no_convergence_, txt=t) return u, v
def distance(self, p1, p2): '''Return the L{equirectangular_} distance in C{radians squared}. ''' r, _ = unrollPI(p1.lam, p2.lam, wrap=self._wrap) if self._adjust: r *= _scale_rad(p1.phi, p2.phi) return hypot2(r, p2.phi - p1.phi) # like equirectangular_ d2
def _zetaInv(self, taup, lam): '''(INTERNAL) Invert C{zeta} using Newton's method. @return: 2-Tuple C{(u, v)}. @see: C{void TMExact::zetainv(real taup, real lam, real &u, real &v)}. @raise EllipticError: No convergence. ''' psi = asinh(taup) sca = 1.0 / hypot1(taup) u, v, trip = self._zetaInv0(psi, lam) if trip: self._iteration = 0 else: stol2 = _TOL_10 / max(psi**2, 1.0) U, V = Fsum(u), Fsum(v) # min iterations = 2, max = 6, mean = 4.0 for self._iteration in range(1, _TRIPS): # GEOGRAPHICLIB_PANIC sncndn6 = self._sncndn6(u, v) T, L, _ = self._zeta3( *sncndn6) dw, dv = self._zetaDwd(*sncndn6) T = (taup - T) * sca L -= lam u, du = U.fsum2_(T * dw, L * dv) v, dv = V.fsum2_(T * dv, -L * dw) if trip: break trip = hypot2(du, dv) < stol2 else: t = unstr(self._zetaInv.__name__, taup, lam) raise EllipticError(_no_convergence_, txt=t) return u, v
def _sigmaInv(self, xi, eta): '''(INTERNAL) Invert C{sigma} using Newton's method. @return: 2-Tuple C{(u, v)}. @see: C{void TMExact::sigmainv(real xi, real eta, real &u, real &v)}. @raise EllipticError: No convergence. ''' u, v, trip = self._sigmaInv0(xi, eta) if not trip: U, V = Fsum(u), Fsum(v) # min iterations = 2, max = 7, mean = 3.9 for _ in range(self._trips_): # GEOGRAPHICLIB_PANIC sncndn6 = self._sncndn6(u, v) X, E, _ = self._sigma3(v, *sncndn6) dw, dv = self._sigmaDwd( *sncndn6) X = xi - X E -= eta u, du = U.fsum2_(X * dw, E * dv) v, dv = V.fsum2_(X * dv, -E * dw) if trip: break trip = hypot2(du, dv) < _TOL_10 else: raise EllipticError('no %s%r convergence' % ('_sigmaInv', (xi, eta))) return u, v
def equirectangular_(lat1, lon1, lat2, lon2, adjust=True, limit=45, wrap=False): '''Compute the distance between two points using the U{Equirectangular Approximation / Projection <https://www.Movable-Type.co.UK/scripts/latlong.html#equirectangular>}. This approximation is valid for short distance of several hundred Km or Miles, see the B{C{limit}} keyword argument and the L{LimitError}. @arg lat1: Start latitude (C{degrees}). @arg lon1: Start longitude (C{degrees}). @arg lat2: End latitude (C{degrees}). @arg lon2: End longitude (C{degrees}). @kwarg adjust: Adjust the wrapped, unrolled longitudinal delta by the cosine of the mean latitude (C{bool}). @kwarg limit: Optional limit for lat- and longitudinal deltas (C{degrees}) or C{None} or C{0} for unlimited. @kwarg wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: A L{Distance4Tuple}C{(distance2, delta_lat, delta_lon, unroll_lon2)}. @raise LimitError: If the lat- and/or longitudinal delta exceeds the B{C{-limit..+limit}} range and L{limiterrors} set to C{True}. @see: U{Local, flat earth approximation <https://www.EdWilliams.org/avform.htm#flat>}, functions L{equirectangular}, L{cosineAndoyerLambert}, L{cosineForsytheAndoyerLambert}, L{cosineLaw}, L{euclidean}, L{flatLocal}/L{hubeny}, L{flatPolar}, L{haversine}, L{thomas} and L{vincentys} and methods L{Ellipsoid.distance2}, C{LatLon.distanceTo*} and C{LatLon.equirectangularTo}. ''' d_lat = lat2 - lat1 d_lon, ulon2 = unroll180(lon1, lon2, wrap=wrap) if limit and _limiterrors \ and max(abs(d_lat), abs(d_lon)) > limit > 0: t = unstr(equirectangular_.__name__, lat1, lon1, lat2, lon2, limit=limit) raise LimitError('delta exceeds limit', txt=t) if adjust: # scale delta lon d_lon *= _scale_deg(lat1, lat2) d2 = hypot2(d_lat, d_lon) # degrees squared! return Distance4Tuple(d2, d_lat, d_lon, ulon2 - lon2)
def toNvector(self, Nvector=None, datum=None, **kwds): # PYCHOK Datums.WGS84 '''Convert this cartesian to C{n-vector} components. @keyword Nvector: Optional (sub-)class to return the C{n-vector} components (C{Nvector}) or C{None}. @keyword datum: Optional datum (L{Datum}) overriding this cartesian's datum. @keyword kwds: Optional, additional B{C{Nvector}} keyword arguments, ignored if C{B{Nvector}=None}. @return: Unit vector B{C{Nvector}} or a L{Vector4Tuple}C{(x, y, z, h)} if B{C{Nvector}=None}. @raise ValueError: The B{C{Cartesian}} at origin. ''' d = datum or self.datum r = self._v4t if r is None or self.datum != d: E = d.ellipsoid x, y, z = self.to3xyz() # Kenneth Gade eqn 23 p = hypot2(x, y) * E.a2_ q = (z**2 * E.e12) * E.a2_ r = fsum_(p, q, -E.e4) / 6 s = (p * q * E.e4) / (4 * r**3) t = cbrt(fsum_(1, s, sqrt(s * (2 + s)))) u = r * fsum_(1, t, 1 / t) v = sqrt(u**2 + E.e4 * q) w = E.e2 * fsum_(u, v, -q) / (2 * v) k = sqrt(fsum_(u, v, w**2)) - w if abs(k) < EPS: raise ValueError('%s: %r' % ('origin', self)) e = k / (k + E.e2) t = hypot(e * hypot(x, y), z) if t < EPS: raise ValueError('%s: %r' % ('origin', self)) h = fsum_(k, E.e2, -1) / k * t s = e / t r = Vector4Tuple(x * s, y * s, z / t, h) self._v4t = r if d == self.datum else None if Nvector is not None: r = Nvector(r.x, r.y, r.z, h=r.h, datum=d, **kwds) return self._xnamed(r)
def _sigmaInv0(self, xi, eta): '''(INTERNAL) Starting point for C{sigmaInv}. @return: 3-Tuple C{(u, v, trip)}. @see: C{bool TMExact::sigmainv0(real xi, real eta, real &u, real &v)}. ''' trip = False if eta > self._Ev_cKE_5_4 or xi < min(- self._Eu_cE_1_4, eta - self._Ev.cKE): # sigma as a simple pole at # w = w0 = Eu.K() + i * Ev.K() # and sigma is approximated by # sigma = (Eu.E() + i * Ev.KE()) + 1 / (w - w0) x = xi - self._Eu.cE y = eta - self._Ev.cKE d = hypot2(x, y) u = self._Eu.cK + x / d v = self._Ev.cK - y / d elif eta > self._Ev.cKE or (xi < self._Eu_cE_1_4 and eta > self._Ev_cKE_3_4): # At w = w0 = i * Ev.K(), we have # sigma = sigma0 = i * Ev.KE() # sigma' = sigma'' = 0 # # including the next term in the Taylor series gives: # sigma = sigma0 - _mv / 3 * (w - w0)^3 # # When inverting this, we map arg(w - w0) = [-pi/2, -pi/6] # to arg(sigma - sigma0) = [-pi/2, pi/2] # mapping arg = [-pi/2, -pi/6] to [-pi/2, pi/2] d = eta - self._Ev.cKE r = hypot(xi, d) # Error using this guess is about 0.068 * rad^(5/3) trip = r < _TAYTOL2 # Map the range [-90, 180] in sigma space to [-90, 0] in # w space. See discussion in zetainv0 on the cut for ang. r = cbrt(r * self._3_mv) a = atan2(d - xi, xi + d) / 3.0 - PI_4 s, c = sincos2(a) u = r * c v = r * s + self._Ev.cK else: # use w = sigma * Eu.K/Eu.E (correct in the limit _e -> 0) u = xi * self._Eu_cK_cE v = eta * self._Eu_cK_cE return u, v, trip
def _distances(self, x, y): # (x, y) radians**2 for xk, yk in zip(self._xs, self._ys): d, _ = unrollPI(xk, x, wrap=self._wrap) if self._adjust: d *= _scale_rad(yk, y) yield hypot2(d, yk - y) # like equirectangular_ distance2
def toNvector(self, Nvector=None, datum=None, **Nvector_kwds): # PYCHOK Datums.WGS84 '''Convert this cartesian to C{n-vector} components. @kwarg Nvector: Optional class to return the C{n-vector} components (C{Nvector}) or C{None}. @kwarg datum: Optional datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}) overriding this cartesian's datum. @kwarg Nvector_kwds: Optional, additional B{C{Nvector}} keyword arguments, ignored if B{C{Nvector=None}}. @return: The C{unit, n-vector} components (B{C{Nvector}}) or a L{Vector4Tuple}C{(x, y, z, h)} if B{C{Nvector}} is C{None}. @raise TypeError: Invalid B{C{datum}}. @raise ValueError: The B{C{Cartesian}} at origin. @example: >>> c = Cartesian(3980581, 97, 4966825) >>> n = c.toNvector() # (x=0.622818, y=0.00002, z=0.782367, h=0.242887) ''' d = _ellipsoidal_datum(datum or self.datum, name=self.name) r = self._v4t if r is None or d != self.datum: # <https://www.Movable-Type.co.UK/scripts/geodesy/docs/ # latlon-nvector-ellipsoidal.js.html#line309> E = d.ellipsoid x, y, z = self.xyz # Kenneth Gade eqn 23 p = hypot2(x, y) * E.a2_ q = (z**2 * E.e12) * E.a2_ r = fsum_(p, q, -E.e4) / 6 s = (p * q * E.e4) / (4 * r**3) t = cbrt(fsum_(1, s, sqrt(s * (2 + s)))) u = r * fsum_(_1_0, t, _1_0 / t) v = sqrt(u**2 + E.e4 * q) w = E.e2 * fsum_(u, v, -q) / (2 * v) k = sqrt(fsum_(u, v, w**2)) - w if abs(k) < EPS: raise _ValueError(origin=self) e = k / (k + E.e2) # d = e * hypot(x, y) # tmp = 1 / hypot(d, z) == 1 / hypot(e * hypot(x, y), z) t = hypot_(e * x, e * y, z) # == 1 / tmp if t < EPS: raise _ValueError(origin=self) h = fsum_(k, E.e2, -_1_0) / k * t s = e / t # == e * tmp r = Vector4Tuple(x * s, y * s, z / t, h) self._v4t = r if d == self.datum else None if Nvector is not None: r = Nvector(r.x, r.y, r.z, h=r.h, datum=d, **Nvector_kwds) return self._xnamed(r)