def fEinv(self, x): '''The inverse of the incomplete integral of the second kind. @arg x: Argument (C{float}). @return: φ = 1 / E(B{C{x}}, k), such that E(φ, k) = B{C{x}} (C{float}). @raise EllipticError: No convergence. ''' E2 = self.cE * 2.0 n = floor(x / E2 + 0.5) x -= E2 * n # x now in [-ec, ec) # linear approximation phi = PI * x / E2 # phi in [-pi/2, pi/2) Phi = Fsum(phi) # first order correction phi = Phi.fsum_(-self.eps * sin(2 * phi) * 0.5) # For kp2 close to zero use asin(x/.cE) or J. P. Boyd, # Applied Math. and Computation 218, 7005-7013 (2012) # <https://DOI.org/10.1016/j.amc.2011.12.021> for _ in range(self._trips_): # GEOGRAPHICLIB_PANIC sn, cn, dn = self._sncndn3(phi) phi, e = Phi.fsum2_((x - self.fE(sn, cn, dn)) / dn) if abs(e) < _TolJAC: return n * PI + phi raise _convergenceError(self.fEinv, x)
def _zetaInv(self, taup, lam): '''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 not trip: stol2 = _TOL_10 / max(psi, 1.0)**2 U, V = Fsum(u), Fsum(v) # min iterations = 2, max = 6, mean = 4.0 for _ in range(self._trips_): # GEOGRAPHICLIB_PANIC snu, cnu, dnu = self._Eu.sncndn(u) snv, cnv, dnv = self._Ev.sncndn(v) T, L, _ = self._zeta3( snu, cnu, dnu, snv, cnv, dnv) dw, dv = self._zetaDwd(snu, cnu, dnu, snv, cnv, dnv) 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 = (du**2 + dv**2) < stol2 else: raise EllipticError('no %s convergence' % ('zetaInv',)) return u, v
def _RD(x, y, z): # used by testElliptic.py '''Degenerate symmetric integral of the third kind C{_RD}. @return: C{_RD(x, y, z) = _RJ(x, y, z, z)}. @see: U{C{_RD} definition<https://DLMF.NIST.gov/19.16.E5>}. ''' # Carlson, eqs 2.28 - 2.34 m = 1.0 S = Fsum() A = fsum_(x, y, z, z, z) * 0.2 T = (A, x, y, z) Q = _Q(A, T, _TolRD) for _ in range(_TRIPS): if Q < abs(m * T[0]): # max 7 trips break t = T[3] # z0 r, s, T = _rsT(T) S += 1.0 / (m * s[2] * (t + r)) m *= 4 else: raise _convergenceError(_RD, x, y, z) m *= T[0] # An x = (x - A) / m y = (y - A) / m z = (x + y) / 3.0 z2 = z**2 xy = x * y return _Horner(3 * S.fsum(), m * sqrt(T[0]), xy - 6 * z2, (xy * 3 - 8 * z2) * z, (xy - z2) * 3 * z2, xy * z2 * z)
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): '''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 snu, cnu, dnu = self._Eu.sncndn(u) snv, cnv, dnv = self._Ev.sncndn(v) X, E, _ = self._sigma3(v, snu, cnu, dnu, snv, cnv, dnv) dw, dv = self._sigmaDwd( snu, cnu, dnu, snv, cnv, dnv) 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 = (du**2 + dv**2) < _TOL_10 else: raise EllipticError('no %s convergence' % ('sigmaInv',)) 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 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 _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 _RF(x, y, z): # used by testElliptic.py '''Symmetric integral of the first kind C{_RF}. @return: C{_RF(x, y, z)}. @see: U{C{_RF} definition<https://DLMF.NIST.gov/19.16.E1>}. ''' # Carlson, eqs 2.2 - 2.7 m = 1.0 A = fsum_(x, y, z) / 3.0 T = (A, x, y, z) Q = _Q(A, T, _TolRF) for _ in range(_TRIPS): if Q < abs(m * T[0]): # max 6 trips break _, _, T = _rsT(T) m *= 4 else: raise _convergenceError(_RF, x, y, z) m *= T[0] # An x = (A - x) / m y = (A - y) / m z = -(x + y) e2 = x * y - z**2 e3 = x * y * z # Polynomial is <https://DLMF.NIST.gov/19.36.E1> # (1 - E2/10 + E3/14 + E2**2/24 - 3*E2*E3/44 # - 5*E2**3/208 + 3*E3**2/104 + E2**2*E3/16) # converted to Horner form ... H = Fsum( 6930 * e3, 15015 * e2**2, -16380 * e2, 17160) * e3 H += Fsum(10010 * e2, -5775 * e2**2, -24024) * e2 return H.fsum_(240240) / (240240 * sqrt(T[0]))
def _D2atanhee(self, x, y): '''(INTERNAL) Function M{D2atanhee(x, y)}, defined as M{(Datanhee(1, y) - Datanhee(1, x)) / (y - x)}. ''' e2 = self.datum.ellipsoid.e2 if (abs(x) + abs(y)) * e2 < _0_5: e = z = _1_0 k = 1 C = Fsum() S = Fsum() T = Fsum() # Taylor expansion for _ in range(_TERMS): T *= y p = T.fsum_(z) z *= x # PYCHOK ; T *= y t = T.fsum_(z) z *= x # PYCHOK ; e *= e2 k += 2 s, d = S.fsum2_(e * C.fsum_(p, t) / k) if not d: break elif (_1_0 - x): s = (self._Datanhee(_1_0, y) - self._Datanhee(x, y)) / (_1_0 - x) else: raise AlbersError(x=x, y=y, txt=_AlbersBase._D2atanhee.__name__) return s
def _RG(x, y, z=None): '''Symmetric integral of the second kind C{_RG}. @return: C{_RG(x, y, z)}. @see: U{C{_RG} definition<https://DLMF.NIST.gov/19.16.E3>} and in Carlson, eq. 1.5. ''' if z is None: # Carlson, eqs 2.36 - 2.39 a, b = sqrt(x), sqrt(y) if a < b: a, b = b, a S = Fsum(0.25 * (a + b)**2) m = -0.25 # note, negative while abs(a - b) > (_tolRG0 * a): # max 4 trips b, a = sqrt(a * b), (a + b) * 0.5 m *= 2 S.fadd_(m * (a - b)**2) return S.fsum() * PI_2 / (a + b) if not z: y, z = z, y # Carlson, eq 1.7 return fsum_( _RF(x, y, z) * z, _RD_3(x, y, z) * (x - z) * (z - y), sqrt(x * y / z)) * 0.5
def reverse(self, x, y, name=NN, LatLon=None, **LatLon_kwds): '''Convert an azimuthal gnomonic location to (ellipsoidal) geodetic lat- and longitude. @arg x: Easting of the location (C{meter}). @arg y: Northing of the location (C{meter}). @kwarg name: Optional name for the location (C{str}). @kwarg LatLon: Class to use (C{LatLon}) or C{None}. @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword arguments, ignored if C{B{LatLon}=None}. @return: The geodetic (C{LatLon}) or if B{C{LatLon}} is C{None} an L{Azimuthal7Tuple}C{(x, y, lat, lon, azimuth, scale, datum)}. @raise AzimuthalError: No convergence. @note: The C{lat} will be in the range C{[-90..90] degrees} and C{lon} in the range C{[-180..180] degrees}. The C{azimuth} is clockwise from true North. The scale is C{1 / reciprocal**2} in C{radial} direction and C{1 / reciprocal} in the direction perpendicular to this. ''' x = Scalar(x=x) y = Scalar(y=y) z = atan2d(x, y) # (x, y) for azimuth from true North q = hypot(x, y) d = e = self.equatoradius s = e * atan(q / e) if q > e: def _d(r, q): return (r.M12 - q * r.m12) * r.m12 # negated q = 1 / q else: # little == True def _d(r, q): # PYCHOK _d return (q * r.M12 - r.m12) * r.M12 # negated e *= _Karney_eps S = Fsum(s) g = self.geodesic.Line(self.lat0, self.lon0, z, self._mask) for self._iteration in range(1, _TRIPS): r = g.Position(s, self._mask) if abs(d) < e: break s, d = S.fsum2_(_d(r, q)) else: raise AzimuthalError(x=x, y=y, txt=_no_(Fmt.convergence(e))) t = self._7Tuple(x, y, r, r.M12) if LatLon is None else \ self._toLatLon(r.lat2, r.lon2, LatLon, LatLon_kwds) t._iteration = self._iteration return self._xnamed(t, name=name)
def _RG_(x, y): '''Symmetric integral of the second kind C{_RG}. @return: C{_RG(x, y)}. @see: U{C{_RG} definition<https://DLMF.NIST.gov/19.16.E3>} and in Carlson, eq. 1.5. ''' # Carlson, eqs 2.36 - 2.39 a, b = sqrt(x), sqrt(y) if a < b: a, b = b, a m = 0.25 S = Fsum(m * (a + b)**2) while abs(a - b) > (_TolRG0 * a): # max 4 trips b, a = sqrt(a * b), (a + b) * 0.5 m *= 2 S -= m * (a - b)**2 return S.fsum() * PI_2 / (a + b)
def _RJ(x, y, z, p): # used by testElliptic.py '''Symmetric integral of the third kind C{_RJ(x, y, z, p)}. @return: C{_RJ(x, y, z, p)}. @see: U{C{_RJ} definition<https://DLMF.NIST.gov/19.16.E2>} and U{Carlson<https://ArXiv.org/pdf/math/9409227.pdf>}. ''' def _xyzp(x, y, z, p): return (x + p) * (y + p) * (z + p) # Carlson, eqs 2.17 - 2.25 m = m3 = _1_0 S = Fsum() D = neg(_xyzp(x, y, z, -p)) A = fsum_(x, y, z, 2 * p) * _0_2 T = (A, x, y, z, p) Q = _Q(A, T, _TolRD) for _ in range(_TRIPS): if Q < abs(m * T[0]): # max 7 trips break _, s, T = _rsT(T) d = _xyzp(*s) e = D / (m3 * d**2) S += _RC(1, 1 + e) / (m * d) m *= _4_0 m3 *= 64 else: raise _convergenceError(_RJ, x, y, z, p) m *= T[0] # An x = (A - x) / m y = (A - y) / m z = (A - z) / m xyz = x * y * z p = neg(x + y + z) * _0_5 p2 = p**2 e2 = fsum_(x * y, x * z, y * z, -3 * p2) return _horner(6 * S.fsum(), m * sqrt(T[0]), e2, fsum_(xyz, 2 * p * e2, 4 * p * p2), fsum_(xyz * 2, p * e2, 3 * p * p2) * p, p2 * xyz)
def _atanhx1(self, x): '''(INTERNAL) Function M{atanh(sqrt(x)) / sqrt(x) - 1}. ''' s = abs(x) if s < _0_5: # for typical ... # x < E.e^2 = 2 * E.f use ... # x / 3 + x^2 / 5 + x^3 / 7 + ... y, k = x, 3 S = Fsum(y / k) for _ in range(_TERMS): y *= x # x**n k += 2 # 2*n + 1 s, d = S.fsum2_(y / k) if not d: break else: s = sqrt(s) s = (atanh(s) if x > 0 else atan(s)) / s - _1_0 return s
def _RJ(x, y, z, p): '''Symmetric integral of the third kind C{_RJ}. @return: C{_RJ(x, y, z, p)}. @see: U{C{_RJ} definition<https://DLMF.NIST.gov/19.16.E2>}. ''' def _xyzp(x, y, z, p): return (x + p) * (y + p) * (z + p) # Carlson, eqs 2.17 - 2.25 m = m3 = 1.0 S = Fsum() D = -_xyzp(x, y, z, -p) A = fsum_(x, y, z, 2 * p) * 0.2 T = [A, x, y, z, p] Q = _Q(A, T, _tolRD) for _ in range(_TRIPS): if Q < abs(m * T[0]): # max 7 trips break _, s, T = _rsT(T) d = _xyzp(*s) e = D / (m3 * d**2) S.fadd_(_RC(1, 1 + e) / (m * d)) m *= 4 m3 *= 64 else: raise EllipticError('no %s convergence' % ('RJ', )) S *= 6 m *= T[0] # An x = (A - x) / m y = (A - y) / m z = (A - z) / m xyz = x * y * z p = -(x + y + z) * 0.5 p2 = p**2 e2 = fsum_(x * y, x * z, y * z, -3 * p2) return _Hf(S.fsum(), m * sqrt(T[0]), e2, fsum_(xyz, 2 * p * e2, 4 * p * p2), fsum_(xyz * 2, p * e2, 3 * p * p2) * p, p2 * xyz)
def _tanf(self, txi): # called from .Ellipsoid.auxAuthalic '''(INTERNAL) Function M{tan-phi from tan-xi}. ''' tol = _tol(_TOL, txi) e2 = self.datum.ellipsoid.e2 qx = self._qx ta = txi Ta = Fsum(ta) for self._iteration in range(1, _NUMIT): # max 2, mean 1.99 # dtxi/dta = (scxi / sca)^3 * 2 * (1 - e^2) / (qZ * (1 - e^2 * sa^2)^2) ta2 = ta**2 sca2 = 1 + ta2 txia = self._txif(ta) s3qx = sqrt3(sca2 / (1 + txia**2)) * qx ta, d = Ta.fsum2_((txi - txia) * s3qx * (1 - e2 * ta2 / sca2)**2) if abs(d) < tol: return ta raise AlbersError(iteration=_NUMIT, txt=_no_convergence_fmt_ % (tol, ))
def _RG_(x, y): '''Symmetric integral of the second kind C{_RG}. @return: C{_RG(x, y)}. @see: U{C{_RG} definition<https://DLMF.NIST.gov/19.16.E3>} and U{Carlson<https://ArXiv.org/pdf/math/9409227.pdf>}. ''' # Carlson, eqs 2.36 - 2.39 a, b = sqrt(x), sqrt(y) if a < b: a, b = b, a m = 0.25 S = Fsum(m * (a + b)**2) for _ in range(_TRIPS): # max 4 trips if abs(a - b) <= (_TolRG0 * a): return S.fsum() * PI_2 / (a + b) b, a = sqrt(a * b), (a + b) * 0.5 m *= 2 S -= m * (a - b)**2 raise _convergenceError(_RG_, x, y)
def _Horner(e0, e1, e2, e3, e4, e5): '''(INTERNAL) Horner form for C{_RD} and C{_RJ} below. ''' # Polynomial is <https://DLMF.NIST.gov/19.36.E2> # (1 - 3*E2/14 + E3/6 + 9*E2**2/88 - 3*E4/22 - 9*E2*E3/52 + 3*E5/26 # - E2**3/16 + 3*E3**2/40 + 3*E2*E4/20 + 45*E2**2*E3/272 # - 9*(E3*E4+E2*E5)/68) # converted to Horner form ... H = Fsum(471240, -540540 * e2) * e5 H += Fsum(612612 * e2, -540540 * e3, -556920) * e4 H += Fsum(306306 * e3, 675675 * e2**2, -706860 * e2, 680680) * e3 H += Fsum(417690 * e2, -255255 * e2**2, -875160) * e2 e = 4084080 * e1 return H.fsum_(4084080, e * e0) / e
def toLatLon(self, LatLon=None, datum=Datums.WGS84): '''Convert this OSGR coordinate to an (ellipsoidal) geodetic point. While OS grid references are based on the OSGB36 datum, the I{Ordnance Survey} have deprecated the use of OSGB36 for lat-/longitude coordinates (in favour of WGS84). Hence, this method returns WGS84 by default with OSGB36 as an option, U{see<https://www.OrdnanceSurvey.co.UK/blog/2014/12/2>}. I{Note formulation implemented here due to Thomas, Redfearn, etc. is as published by OS, but is inferior to Krüger as used by e.g. Karney 2011.} @kwarg LatLon: Optional ellipsoidal class to return the geodetic point (C{LatLon}) or C{None}. @kwarg datum: Optional datum to convert to (L{Datum}, L{Ellipsoid}, L{Ellipsoid2}, L{Ellipsoid2} or L{a_f2Tuple}). @return: The geodetic point (B{C{LatLon}}) or a L{LatLonDatum3Tuple}C{(lat, lon, datum)} if B{C{LatLon}} is C{None}. @raise OSGRError: No convergence. @raise TypeError: If B{C{LatLon}} is not ellipsoidal or B{C{datum}} is invalid or conversion failed. @example: >>> from pygeodesy import ellipsoidalVincenty as eV >>> g = Osgr(651409.903, 313177.270) >>> p = g.toLatLon(eV.LatLon) # 52°39′28.723″N, 001°42′57.787″E >>> # to obtain (historical) OSGB36 lat-/longitude point >>> p = g.toLatLon(eV.LatLon, datum=Datums.OSGB36) # 52°39′27.253″N, 001°43′04.518″E ''' if self._latlon: return self._latlon3(LatLon, datum) E = self.datum.ellipsoid # _Datums_OSGB36.ellipsoid, Airy130 a_F0 = E.a * _F0 b_F0 = E.b * _F0 e, n = self.easting, self.northing n_N0 = n - _N0 a, m = _A0, n_N0 sa = Fsum(a) for self._iteration in range(1, _TRIPS): a = sa.fsum_(m / a_F0) m = n_N0 - b_F0 * _M(E.Mabcd, a) # meridional arc if abs(m) < _10um: break else: t = _dot_(_item_ps(self.classname, self.toStr(prec=-3)), self.toLatLon.__name__) raise OSGRError(_no_convergence_, txt=t) sa, ca = sincos2(a) s = E.e2s2(sa) # r, v = E.roc2_(sa, _F0) v = a_F0 / sqrt(s) # nu r = v * E.e12 / s # rho = a_F0 * E.e12 / pow(s, 1.5) == a_F0 * E.e12 / (s * sqrt(s)) vr = v / r # == s / E.e12 x2 = vr - 1 # η2 ta = tan(a) v3, v5, v7 = fpowers(v, 7, 3) # PYCHOK false! ta2, ta4, ta6 = fpowers(ta**2, 3) # PYCHOK false! tar = ta / r V4 = (a, tar / (2 * v), tar / (24 * v3) * fdot( (1, 3, -9), 5 + x2, ta2, ta2 * x2), tar / (720 * v5) * fdot( (61, 90, 45), 1, ta2, ta4)) csa = 1.0 / ca X5 = (_B0, csa / v, csa / (6 * v3) * fsum_(vr, ta2, ta2), csa / (120 * v5) * fdot( (5, 28, 24), 1, ta2, ta4), csa / (5040 * v7) * fdot( (61, 662, 1320, 720), 1, ta2, ta4, ta6)) d, d2, d3, d4, d5, d6, d7 = fpowers(e - _E0, 7) # PYCHOK false! a = fdot(V4, 1, -d2, d4, -d6) b = fdot(X5, 1, d, -d3, d5, -d7) r = _LLEB(degrees90(a), degrees180(b), datum=self.datum, name=self.name) r._iteration = self._iteration # only ellipsoidal LatLon self._latlon = r return self._latlon3(LatLon, datum)
def toLatLon(self, LatLon=None, datum=Datums.WGS84): '''Convert this OSGR coordinate to an (ellipsoidal) geodetic point. I{Note formulation implemented here due to Thomas, Redfearn, etc. is as published by OS, but is inferior to Krüger as used by e.g. Karney 2011.} @keyword LatLon: Optional ellipsoidal (sub-)class to return the point (C{LatLon}) or C{None}. @keyword datum: Optional datum to use (C{Datum}). @return: The geodetic point (B{C{LatLon}}) or a L{LatLonDatum3Tuple}C{(lat, lon, datum)} if B{C{LatLon}} is C{None}. @raise TypeError: If B{C{LatLon}} is not ellipsoidal or if B{C{datum}} conversion failed. @example: >>> from pygeodesy import ellipsoidalVincenty as eV >>> g = Osgr(651409.903, 313177.270) >>> p = g.toLatLon(eV.LatLon) # 52°39′28.723″N, 001°42′57.787″E >>> # to obtain (historical) OSGB36 lat-/longitude point >>> p = g.toLatLon(eV.LatLon, datum=Datums.OSGB36) # 52°39′27.253″N, 001°43′04.518″E ''' if self._latlon: return self._latlon3(LatLon, datum) E = _OSGB36.ellipsoid # Airy130 a_F0 = E.a * _F0 b_F0 = E.b * _F0 e, n = self._easting, self._northing n_N0 = n - _N0 a, M = _A0, 0 sa = Fsum(a) while True: t = n_N0 - M if t < _10um: break a = sa.fsum_(t / a_F0) M = b_F0 * _M(E.Mabcd, a) sa, ca = sincos2(a) s = E.e2s2(sa) # v, r = E.roc2_(sa, _F0) v = a_F0 / sqrt(s) # nu r = v * E.e12 / s # rho vr = v / r # == s / E.e12 x2 = vr - 1 # η2 ta = tan(a) v3, v5, v7 = fpowers(v, 7, 3) # PYCHOK false! ta2, ta4, ta6 = fpowers(ta**2, 3) # PYCHOK false! tar = ta / r V4 = (a, tar / ( 2 * v), tar / ( 24 * v3) * fdot((1, 3, -9), 5 + x2, ta2, ta2 * x2), tar / (720 * v5) * fdot((61, 90, 45), 1, ta2, ta4)) csa = 1.0 / ca X5 = (_B0, csa / v, csa / ( 6 * v3) * fsum_(vr, ta, ta), csa / ( 120 * v5) * fdot((5, 28, 24), 1, ta2, ta4), csa / (5040 * v7) * fdot((61, 662, 1320, 720), ta, ta2, ta4, ta6)) d, d2, d3, d4, d5, d6, d7 = fpowers(e - _E0, 7) # PYCHOK false! a = fdot(V4, 1, -d2, d4, -d6) b = fdot(X5, 1, d, -d3, d5, -d7) self._latlon = _LLEB(degrees90(a), degrees180(b), datum=_OSGB36, name=self.name) return self._latlon3(LatLon, datum)
def toLatLon(self, LatLon=None, eps=EPS, unfalse=True, **LatLon_kwds): '''Convert this UTM coordinate to an (ellipsoidal) geodetic point. @kwarg LatLon: Optional, ellipsoidal class to return the geodetic point (C{LatLon}) or C{None}. @kwarg eps: Optional convergence limit, L{EPS} or above (C{float}). @kwarg unfalse: Unfalse B{C{easting}} and B{C{northing}} if falsed (C{bool}). @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword arguments, ignored if B{C{LatLon=None}}. @return: This UTM coordinate (B{C{LatLon}}) or if B{C{LatLon}} is C{None}, a L{LatLonDatum5Tuple}C{(lat, lon, datum, convergence, scale)}. @raise TypeError: If B{C{LatLon}} is not ellipsoidal. @raise UTMError: Invalid meridional radius or H-value. @example: >>> u = Utm(31, 'N', 448251.795, 5411932.678) >>> from pygeodesy import ellipsoidalVincenty as eV >>> ll = u.toLatLon(eV.LatLon) # 48°51′29.52″N, 002°17′40.20″E ''' if eps < EPS: eps = EPS # less doesn't converge if self._latlon and self._latlon_args == (eps, unfalse): return self._latlon5(LatLon) E = self.datum.ellipsoid # XXX vs LatLon.datum.ellipsoid x, y = self.to2en(falsed=not unfalse) # from Karney 2011 Eq 15-22, 36 A0 = self.scale0 * E.A if A0 < EPS: raise self._Error(meridional=A0) x /= A0 # η eta y /= A0 # ξ ksi K = _Kseries(E.BetaKs, x, y) # Krüger series y = -K.ys(-y) # ξ' x = -K.xs(-x) # η' shx = sinh(x) sy, cy = sincos2(y) H = hypot(shx, cy) if H < EPS: raise self._Error(H=H) T = t0 = sy / H # τʹ S = Fsum(T) q = 1.0 / E.e12 P = 7 # -/+ toggle trips d = 1.0 + eps while abs(d) > eps and P > 0: p = -d # previous d, toggled h = hypot1(T) s = sinh(E.e * atanh(E.e * T / h)) t = T * hypot1(s) - s * h d = (t0 - t) / hypot1(t) * ((q + T**2) / h) T, d = S.fsum2_(d) # τi, (τi - τi-1) if d == p: # catch -/+ toggling of d P -= 1 # else: # P = 0 a = atan(T) # lat b = atan2(shx, cy) if unfalse and self.falsed: b += radians(_cmlon(self.zone)) ll = _LLEB(degrees90(a), degrees180(b), datum=self.datum, name=self.name) # convergence: Karney 2011 Eq 26, 27 p = -K.ps(-1) q = K.qs(0) ll._convergence = degrees(atan(tan(y) * tanh(x)) + atan2(q, p)) # scale: Karney 2011 Eq 28 ll._scale = E.e2s(sin(a)) * hypot1(T) * H * (A0 / E.a / hypot(p, q)) self._latlon_to(ll, eps, unfalse) return self._latlon5(LatLon, **LatLon_kwds)
def __init__(self, sa1, ca1, sa2, ca2, k, datum, name): '''(INTERNAL) New C{AlbersEqualArea...} instance. ''' if datum not in (None, self._datum): self._datum = _ellipsoidal_datum(datum, name=name) if name: self.name = name E = self.datum.ellipsoid b_a = E.b_a # fm = 1 - E.f e2 = E.e2 e12 = E.e12 # e2m = 1 - E.e2 self._qZ = qZ = _1_0 + e12 * self._atanhee(1) self._qZa2 = qZ * E.a2 self._qx = qZ / (_2_0 * e12) c = min(ca1, ca2) if c < 0: raise AlbersError(clat1=ca1, clat2=ca2) polar = c < _EPS__2 # == 0 # determine hemisphere of tangent latitude if sa1 < 0: # and sa2 < 0: self._sign = -1 # internally, tangent latitude positive sa1, sa2 = -sa1, neg(sa2) if sa1 > sa2: # make phi1 < phi2 sa1, sa2 = sa2, sa1 ca1, ca2 = ca2, ca1 if sa1 < 0: # or sa2 < 0: raise AlbersError(slat1=sa1, slat2=sa2) # avoid singularities at poles ca1, ca2 = max(_EPS__2, ca1), max(_EPS__2, ca2) ta1, ta2 = sa1 / ca1, sa2 / ca2 par1 = abs(ta1 - ta2) < _EPS__4 # ta1 == ta2 if par1 or polar: C, ta0 = _1_0, ta2 else: s1_qZ, C = self._s1_qZ_C2(ca1, sa1, ta1, ca2, sa2, ta2) ta0 = (ta2 + ta1) * _0_5 Ta0 = Fsum(ta0) tol = _tol(_TOL0, ta0) for self._iteration in range(1, _NUMIT0): ta02 = ta0**2 sca02 = ta02 + _1_0 sca0 = sqrt(sca02) sa0 = ta0 / sca0 sa01 = sa0 + _1_0 sa02 = sa0**2 # sa0m = 1 - sa0 = 1 / (sec(a0) * (tan(a0) + sec(a0))) sa0m = _1_0 / (sca0 * (ta0 + sca0)) # scb0^2 * sa0 g = (_1_0 + (b_a * ta0)**2) * sa0 dg = e12 * sca02 * (_1_0 + 2 * ta02) + e2 D = sa0m * (_1_0 - e2 * (_1_0 + sa01 * 2 * sa0)) / (e12 * sa01) # dD/dsa0 dD = -2 * (_1_0 - e2 * sa02 * (_3_0 + 2 * sa0)) / (e12 * sa01**2) sa02_ = _1_0 - e2 * sa02 sa0m_ = sa0m / (_1_0 - e2 * sa0) BA = sa0m_ * (self._atanhx1(e2 * sa0m_**2) * e12 - e2 * sa0m) \ - sa0m**2 * e2 * (2 + (_1_0 + e2) * sa0) / (e12 * sa02_) # == B + A dAB = 2 * e2 * (2 - e2 * (_1_0 + sa02)) / (e12 * sa02_**2 * sca02) u_du = fsum_(s1_qZ * g, -D, g * BA) \ / fsum_(s1_qZ * dg, -dD, dg * BA, g * dAB) # == u/du ta0, d = Ta0.fsum2_(-u_du * (sca0 * sca02)) if abs(d) < tol: break else: raise AlbersError(iteration=_NUMIT0, txt=_no_(Fmt.convergence(tol))) self._txi0 = txi0 = self._txif(ta0) self._scxi0 = hypot1(txi0) self._sxi0 = sxi0 = txi0 / self._scxi0 self._m02 = m02 = _1_0 / (_1_0 + (b_a * ta0)**2) self._n0 = n0 = ta0 / hypot1(ta0) if polar: self._polar = True self._nrho0 = self._m0 = _0_0 else: self._m0 = sqrt(m02) # == nrho0 / E.a self._nrho0 = E.a * self._m0 # == E.a * sqrt(m02) self._k0_(_1_0 if par1 else (k * sqrt(C / (m02 + n0 * qZ * sxi0)))) self._lat0 = _Lat(lat0=self._sign * atand(ta0))