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 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 self._iteration in range(1, _TRIPS): # GEOGRAPHICLIB_PANIC sn, cn, dn = self._sncndn3(phi) phi, e = Phi.fsum2_((x - self.fE(sn, cn, dn)) / dn) if abs(e) < _TolJAC: if n: phi = Phi.fsum_(n * PI) return phi raise _convergenceError(self.fEinv, x)
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 _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 fEinv(self, x): '''The inverse of the incomplete integral of the second kind. @param x: Argument (C{float}). @return: φ = 1 / E(B{C{x}}, k), such that E(φ, k) = B{C{x}}. ''' 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 EllipticError('no %s convergence' % ('fEinv', ))
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)