def elevation(self): '''Get the elevation, tilt of this NED vector in degrees from horizontal, i.e. tangent to ellipsoid surface (C{degrees90}). ''' if self._elevation is None: self._elevation = -degrees90(asin(self.down / self.length)) return self._elevation
def to3lld(self, datum=None): '''Convert this L{Lcc} to a geodetic lat- and longitude. @keyword datum: Optional datum to use, otherwise use this I{Lcc}'s conic.datum (C{Datum}). @return: 3-Tuple (lat, lon, datum) in (C{degrees90}, {degrees180}, I{datum}). @raise TypeError: If I{datum} is not ellipsoidal. ''' c = self.conic if datum: c = c.toDatum(datum) e = self.easting - c._E0 n = c._r0 - self.northing + c._N0 r_ = copysign(hypot(e, n), c._n) t_ = pow(r_ / c._aF, c._n_) x = c._xdef(t_) # XXX c._lon0 while True: p, x = x, c._xdef(t_ * c._pdef(x)) if abs(x - p) < 1e-9: # XXX EPS too small? break # x, y == lon, lat a = degrees90(x) b = degrees180((atan(e / n) + c._opt3) * c._n_ + c._lon0) return a, b, c.datum
def to2ll(self, datum=None): '''Convert this WM coordinate to a geodetic lat- and longitude. @keyword datum: Optional datum (C{Datum}). @return: 2-Tuple (lat, lon) in (C{degrees90}, C{degrees180}). @raise TypeError: Non-ellipsoidal I{datum}. @raise ValueError: Invalid I{radius}. ''' r = self.radius x = self._x / r y = 2 * atan(exp(self._y / r)) - PI_2 if datum: E = datum.ellipsoid if not E.isEllipsoidal: raise TypeError('%s not %s: %r' % ('datum', 'ellipsoidal', datum)) # <http://Earth-Info.NGA.mil/GandG/wgs84/web_mercator/ # %28U%29%20NGA_SIG_0011_1.0.0_WEBMERC.pdf> y = y / r if E.e: y -= E.e * atanh(E.e * tanh(y)) y *= E.a x *= E.a / r return degrees90(y), degrees180(x)
def intermediateTo(self, other, fraction, height=None, wrap=False): '''Locate the point at given fraction between this and an other point. @param other: The other point (L{LatLon}). @param fraction: Fraction between both points (float, 0.0 = this point, 1.0 = the other point). @keyword height: Optional height, overriding the fractional height (C{meter}). @keyword wrap: Wrap and unroll longitudes (C{bool}). @return: Intermediate point (L{LatLon}). @raise TypeError: The I{other} point is not L{LatLon}. @example: >>> p1 = LatLon(52.205, 0.119) >>> p2 = LatLon(48.857, 2.351) >>> p = p1.intermediateTo(p2, 0.25) # 51.3721°N, 000.7073°E @JSname: I{intermediatePointTo}. ''' self.others(other) a1, b1 = self.to2ab() a2, b2 = other.to2ab() db, b2 = unrollPI(b1, b2, wrap=wrap) r = haversine_(a2, a1, db) sr = sin(r) if abs(sr) > EPS: cb1, cb2, ca1, ca2 = map1(cos, b1, b2, a1, a2) sb1, sb2, sa1, sa2 = map1(sin, b1, b2, a1, a2) A = sin((1 - fraction) * r) / sr B = sin(fraction * r) / sr x = A * ca1 * cb1 + B * ca2 * cb2 y = A * ca1 * sb1 + B * ca2 * sb2 z = A * sa1 + B * sa2 a = atan2(z, hypot(x, y)) b = atan2(y, x) else: # points too close a = favg(a1, a2, f=fraction) b = favg(b1, b2, f=fraction) if height is None: h = self._havg(other, f=fraction) else: h = height return self.classof(degrees90(a), degrees180(b), height=h)
def to3llh(self, datum=Datums.WGS84): '''Convert this (geocentric) Cartesian (x/y/z) point to (ellipsoidal, geodetic) lat-, longitude and height on the given datum. Uses Bowring’s (1985) formulation for μm precision in concise form: U{'The accuracy of geodetic latitude and height equations' <http://www.ResearchGate.net/publication/ 233668213_The_Accuracy_of_Geodetic_Latitude_and_Height_Equations>}, B. R. Bowring, Survey Review, Vol 28, 218, Oct 1985. See also Ralph M. Toms U{'An Efficient Algorithm for Geocentric to Geodetic Coordinate Conversion'<http://www.OSTI.gov/scitech/biblio/110235>}, Sept 1995 and U{'An Improved Algorithm for Geocentric to Geodetic Coordinate Conversion'<http://www.OSTI.gov/scitech/servlets/purl/231228>}, Apr 1996, from Lawrence Livermore National Laboratory. @keyword datum: Optional datum to use (L{Datum}). @return: 3-Tuple (lat, lon, heigth) in (C{degrees90}, C{degrees180}, C{meter}). ''' E = datum.ellipsoid x, y, z = self.to3xyz() p = hypot(x, y) # distance from minor axis r = hypot(p, z) # polar radius if min(p, r) > EPS: # parametric latitude (Bowring eqn 17, replaced) t = (E.b * z) / (E.a * p) * (1 + E.e22 * E.b / r) c = 1 / hypot1(t) s = t * c # geodetic latitude (Bowring eqn 18) a = atan2(z + E.e22 * E.b * s**3, p - E.e2 * E.a * c**3) b = atan2(y, x) # ... and longitude # height above ellipsoid (Bowring eqn 7) ca, sa = cos(a), sin(a) # r = E.a / E.e2s(sa) # length of normal terminated by minor axis # h = p * ca + z * sa - (E.a * E.a / r) h = fsum_(p * ca, z * sa, -E.a * E.e2s(sa)) a, b = degrees90(a), degrees180(b) # see <http://GIS.StackExchange.com/questions/28446> elif p > EPS: # latitude arbitrarily zero a, b, h = 0.0, degrees180(atan2(y, x)), p - E.a else: # polar latitude, longitude arbitrarily zero a, b, h = copysign(90.0, z), 0.0, abs(z) - E.b return a, b, h
def _destination2(a, b, r, t): '''(INTERNAL) Computes destination lat- and longitude. @param a: Latitude (C{radians}). @param b: Longitude (C{radians}). @param r: Angular distance (C{radians}). @param t: Bearing (compass C{radians}). @return: 2-Tuple (lat, lon) of (C{degrees90}, C{degrees180}). ''' a, b = _destination2_(a, b, r, t) return degrees90(a), degrees180(b)
def to2ll(self): '''Convert this vector to (geodetic) lat- and longitude. @return: 2-Tuple (lat, lon) in (C{degrees90}, C{degrees180}). @example: >>> v = Vector3d(0.500, 0.500, 0.707) >>> a, b = v.to2ll() # 44.99567, 45.0 ''' a, b = self.to2ab() return degrees90(a), degrees180(b)
def _direct(self, distance, bearing, llr, height=None): '''(INTERNAL) Direct 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. ''' E = self.ellipsoid() c1, s1, t1 = _r3(self.lat, E.f) i = radians(bearing) # initial bearing (forward azimuth) si, ci = sincos2(i) s12 = atan2(t1, ci) * 2 sa = c1 * si c2a = 1 - sa**2 if c2a < EPS: c2a = 0 A, B = 1, 0 else: # e22 == (a / b)**2 - 1 A, B = _p2(c2a * E.e22) s = d = distance / (E.b * A) for _ in range(self._iterations): ss, cs = sincos2(s) c2sm = cos(s12 + s) s_, s = s, d + _ds(B, cs, ss, c2sm) if abs(s - s_) < self._epsilon: break else: raise VincentyError('%s, %r' % ('no convergence', self)) t = s1 * ss - c1 * cs * ci # final bearing (reverse azimuth +/- 180) r = degrees360(atan2(sa, -t)) if llr: # destination latitude in [-90, 90) a = degrees90(atan2(s1 * cs + c1 * ss * ci, (1 - E.f) * hypot(sa, t))) # destination longitude in [-180, 180) b = degrees180(atan2(ss * si, c1 * cs - s1 * ss * ci) - _dl(E.f, c2a, sa, s, cs, ss, c2sm) + radians(self.lon)) h = self.height if height is None else height r = self.classof(a, b, height=h, datum=self.datum), r return r
def to2ll(self): '''Convert this vector to (geodetic) lat- and longitude. @return: A L{LatLon2Tuple}C{(lat, lon)}. @example: >>> v = Vector3d(0.500, 0.500, 0.707) >>> a, b = v.to2ll() # 44.99567, 45.0 ''' a, b = self.to2ab() r = LatLon2Tuple(degrees90(a), degrees180(b)) return self._xnamed(r)
def centroidOf(points, wrap=True, LatLon=None): '''Determine the centroid of a polygon. @param points: The polygon points (C{LatLon}[]). @keyword wrap: Wrap lat-, wrap and unroll longitudes (C{bool}). @keyword LatLon: Optional (sub-)class to return the centroid (L{LatLon}) or C{None}. @return: Centroid location (I{LatLon}) or as 2-tuple (C{lat, lon}) in C{degrees} if I{LatLon} is C{None}. @raise TypeError: Some I{points} are not C{LatLon}. @raise ValueError: Insufficient number of I{points} or I{points} enclose a pole or zero area. @see: U{Centroid<http://WikiPedia.org/wiki/Centroid#Of_a_polygon>} and U{Calculating The Area And Centroid Of A Polygon <http://www.Seas.UPenn.edu/~sys502/extra_materials/ Polygon%20Area%20and%20Centroid.pdf>}. ''' # setting radius=1 converts degrees to radians pts = LatLon2psxy(points, closed=True, radius=1, wrap=wrap) n = len(pts) A, X, Y = Fsum(), Fsum(), Fsum() x1, y1, _ = pts[n-1] for i in range(n): x2, y2, _ = pts[i] if wrap and i < (n - 1): _, x2 = unrollPI(x1, x2, wrap=True) t = x1 * y2 - x2 * y1 A += t X += t * (x1 + x2) Y += t * (y1 + y2) # XXX more elaborately: # t1, t2 = x1 * y2, -(x2 * y1) # A.fadd_(t1, t2) # X.fadd_(t1 * x1, t1 * x2, t2 * x1, t2 * x2) # Y.fadd_(t1 * y1, t1 * y2, t2 * y1, t2 * y2) x1, y1 = x2, y2 A = A.fsum() * 3.0 # 6.0 / 2.0 if abs(A) < EPS: raise ValueError('polar or zero area: %r' % (pts,)) Y, X = degrees90(Y.fsum() / A), degrees180(X.fsum() / A) return (Y, X) if LatLon is None else LatLon(Y, X)
def toLatLon(self, LatLon=None, unfalse=True): '''Convert this UPS coordinate to an (ellipsoidal) geodetic point. @keyword LatLon: Optional, ellipsoidal (sub-)class to return the point (C{LatLon}) or C{None}. @keyword unfalse: Unfalse I{easting} and I{northing} if falsed (C{bool}). @return: This UPS coordinate as (I{LatLon}) or 5-tuple (C{lat, lon, datum, convergence, scale}) if I{LatLon} is C{None}. @raise TypeError: If I{LatLon} is not ellipsoidal. @raise UPSError: Invalid meridional radius or H-value. ''' if self._latlon: return self._latlon5(LatLon) E = self.datum.ellipsoid # XXX vs LatLon.datum.ellipsoid f = self.falsed if unfalse else 0 x = self.easting - f y = self.northing - f r = hypot(x, y) t = (r / (2 * self.scale0 * E.a / E.es_c)) if r > 0 else EPS**2 t = E.es_tauf((1 / t - t) * 0.5) if self.pole == 'N': a, b, c = atan(t), atan2(x, -y), 1 else: a, b, c = -atan(t), atan2(x, y), -1 a, b = degrees90(a), degrees180(b) if not self._band: self._band = _Band(a, b) if not self._hemisphere: self._hemisphere = _hemi(a) ll = _LLEB(a, b, datum=self._datum, name=self.name) ll._convergence = b * c # gamma ll._scale = _scale(E, r, t) if r > 0 else self.scale0 self._latlon = ll return self._latlon5(LatLon)
def rhumbMidpointTo(self, other, height=None): '''Return the (loxodromic) midpoint between this and an other point. @param other: The other point (spherical LatLon). @keyword height: Optional height, overriding the mean height (C{meter}). @return: The midpoint (spherical C{LatLon}). @raise TypeError: The I{other} point is not spherical. @example: >>> p = LatLon(51.127, 1.338) >>> q = LatLon(50.964, 1.853) >>> m = p.rhumb_midpointTo(q) >>> m.toStr() # '51.0455°N, 001.5957°E' ''' self.others(other) # see <https://MathForum.org/library/drmath/view/51822.html> a1, b1 = self.to2ab() a2, b2 = other.to2ab() if abs(b2 - b1) > PI: b1 += PI2 # crossing anti-meridian a3 = favg(a1, a2) b3 = favg(b1, b2) f1 = tanPI_2_2(a1) if abs(f1) > EPS: f2 = tanPI_2_2(a2) f = f2 / f1 if abs(f) > EPS: f = log(f) if abs(f) > EPS: f3 = tanPI_2_2(a3) b3 = fsum_(b1 * log(f2), -b2 * log(f1), (b2 - b1) * log(f3)) / f h = self._havg(other) if height is None else height return self.classof(degrees90(a3), degrees180(b3), height=h)
def _destination2(a, b, r, t): '''(INTERNAL) Computes destination lat-/longitude. @param a: Latitude (C{radians}). @param b: Longitude (C{radians}). @param r: Angular distance (C{radians}). @param t: Bearing (compass C{radians}). @return: 2-Tuple (lat, lon) of (C{degrees90}, C{degrees180}). ''' # see <http://www.EdWilliams.org/avform.htm#LL> ca, cr, ct = map1(cos, a, r, t) sa, sr, st = map1(sin, a, r, t) a = asin(ct * sr * ca + cr * sa) d = atan2(st * sr * ca, cr - sa * sin(a)) # note, use addition, not subtraction of d (since # in EdWilliams.org/avform.htm W is + and E is -) return degrees90(a), degrees180(b + d)
def rhumbDestination(self, distance, bearing, radius=R_M, height=None): '''Return the destination point having travelled along a rhumb (loxodrome) line from this point the given distance on the given bearing. @param distance: Distance travelled (C{meter}, same units as I{radius}). @param bearing: Bearing from this point (compass C{degrees360}). @keyword radius: Optional, mean earth radius (C{meter}). @keyword height: Optional height, overriding the default height (C{meter}, same unit as I{radius}). @return: The destination point (spherical C{LatLon}). @example: >>> p = LatLon(51.127, 1.338) >>> q = p.rhumbDestination(40300, 116.7) # 50.9642°N, 001.8530°E @JSname: I{rhumbDestinationPoint} ''' a1, b1 = self.to2ab() r = float(distance) / float(radius) # angular distance in radians sb, cb = sincos2d(bearing) da = r * cb a2 = a1 + da # normalize latitude if past pole if a2 > PI_2: a2 = PI - a2 elif a2 < -PI_2: a2 = -PI - a2 dp = log(tanPI_2_2(a2) / tanPI_2_2(a1)) # E-W course becomes ill-conditioned with 0/0 q = (da / dp) if abs(dp) > EPS else cos(a1) b2 = (b1 + r * sb / q) if abs(q) > EPS else b1 h = self.height if height is None else height return self.classof(degrees90(a2), degrees180(b2), height=h)
def maxLat(self, bearing): '''Return the maximum latitude reached when travelling on a great circle on given bearing from this point based on Clairaut's formula. The maximum latitude is independent of longitude and the same for all points on a given latitude. Negate the result for the minimum latitude (on the Southern hemisphere). @param bearing: Initial bearing (compass C{degrees360}). @return: Maximum latitude (C{degrees90}). @JSname: I{maxLatitude}. ''' a, _ = self.to2ab() m = acos1(abs(sin(radians(bearing)) * cos(a))) return degrees90(m)
def midpointTo(self, other, height=None, wrap=False): '''Find the midpoint between this and an other point. @param other: The other point (L{LatLon}). @keyword height: Optional height for midpoint, overriding the mean height (C{meter}). @keyword wrap: Wrap and unroll longitudes (C{bool}). @return: Midpoint (L{LatLon}). @raise TypeError: The I{other} point is not L{LatLon}. @example: >>> p1 = LatLon(52.205, 0.119) >>> p2 = LatLon(48.857, 2.351) >>> m = p1.midpointTo(p2) # '50.5363°N, 001.2746°E' ''' self.others(other) # see <http://MathForum.org/library/drmath/view/51822.html> a1, b1 = self.to2ab() a2, b2 = other.to2ab() db, b2 = unrollPI(b1, b2, wrap=wrap) ca1, ca2, cdb = map1(cos, a1, a2, db) sa1, sa2, sdb = map1(sin, a1, a2, db) x = ca2 * cdb + ca1 y = ca2 * sdb a = atan2(sa1 + sa2, hypot(x, y)) b = atan2(y, x) + b1 h = self._havg(other) if height is None else height return self.classof(degrees90(a), degrees180(b), height=h)
def par2(self): '''Get the 2nd standard parallel (C{degrees90}). ''' return degrees90(self._par2)
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 (I{LatLon}) or 3-tuple (C{degrees90}, C{degrees180}, I{datum}) if I{LatLon} is C{None}. @raise TypeError: If I{LatLon} is not ellipsoidal or if I{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 = 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): '''Convert this UTM coordinate to an (ellipsoidal) geodetic point. @keyword LatLon: Optional, ellipsoidal (sub-)class to use for the point (C{LatLon}) or C{None}. @return: This UTM coordinate as (I{LatLon}) or 5-tuple (lat, lon, datum, convergence, scale) if I{LatLon} is C{None}. @raise TypeError: If I{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 self._latlon: return self._latlon5(LatLon) E = self._datum.ellipsoid # XXX vs LatLon.datum.ellipsoid x = self._easting - _FalseEasting # relative to central meridian y = self._northing if self._hemi == 'S': # relative to equator y -= _FalseNorthing # from Karney 2011 Eq 15-22, 36 A0 = _K0 * E.A if A0 < EPS: raise UTMError('%s invalid: %r' % ('meridional', E.A)) x /= A0 # η eta y /= A0 # ξ ksi Ks = _Kseries(E.BetaKs, x, y) # Krüger series y = -Ks.ys(-y) # ξ' x = -Ks.xs(-x) # η' shx = sinh(x) cy, sy = cos(y), sin(y) H = hypot(shx, cy) if H < EPS: raise UTMError('%s invalid: %r' % ('H', H)) T = t0 = sy / H # τʹ q = 1.0 / E.e12 d = 1 sd = Fsum(T) # toggles on +/-1.12e-16 eg. 31 N 400000 5000000 while abs(d) > EPS: # 1e-12 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 = sd.fsum_(d) # τi a = atan(T) # lat b = atan2(shx, cy) + radians(_cmlon(self._zone)) ll = _LLEB(degrees90(a), degrees180(b), datum=self._datum, name=self.name) # convergence: Karney 2011 Eq 26, 27 p = -Ks.ps(-1) q = Ks.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 = ll return self._latlon5(LatLon)
def toLatLon(self, LatLon=None, eps=EPS, unfalse=True): '''Convert this UTM coordinate to an (ellipsoidal) geodetic point. @keyword LatLon: Optional, ellipsoidal (sub-)class to return the point (C{LatLon}) or C{None}. @keyword eps: Optional convergence limit, L{EPS} or above (C{float}). @keyword unfalse: Unfalse I{easting} and I{northing} if falsed (C{bool}). @return: This UTM coordinate as (I{LatLon}) or 5-tuple (lat, lon, datum, convergence, scale) if I{LatLon} is C{None}. @raise TypeError: If I{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_eps == eps: return self._latlon5(LatLon) E = self._datum.ellipsoid # XXX vs LatLon.datum.ellipsoid x = self._easting y = self._northing if unfalse and self._falsed: x -= _FalseEasting # relative to central meridian if self._hemisphere == 'S': # relative to equator y -= _FalseNorthing # from Karney 2011 Eq 15-22, 36 A0 = _K0 * E.A if A0 < EPS: raise UTMError('%s invalid: %r' % ('meridional', E.A)) x /= A0 # η eta y /= A0 # ξ ksi Ks = _Kseries(E.BetaKs, x, y) # Krüger series y = -Ks.ys(-y) # ξ' x = -Ks.xs(-x) # η' shx = sinh(x) sy, cy = sincos2(y) H = hypot(shx, cy) if H < EPS: raise UTMError('%s invalid: %r' % ('H', H)) d = 1.0 + eps q = 1.0 / E.e12 T = t0 = sy / H # τʹ sd = Fsum(T) while abs(d) > eps: 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 = sd.fsum2_(d) # τi, (τi - τi-1) a = atan(T) # lat b = atan2(shx, cy) + radians(_cmlon(self._zone)) ll = _LLEB(degrees90(a), degrees180(b), datum=self._datum, name=self.name) # convergence: Karney 2011 Eq 26, 27 p = -Ks.ps(-1) q = Ks.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, self._latlon_eps = ll, eps return self._latlon5(LatLon)
def toLatLon(self, LatLon=None, eps=EPS, unfalse=True): '''Convert this UTM coordinate to an (ellipsoidal) geodetic point. @keyword LatLon: Optional, ellipsoidal (sub-)class to return the point (C{LatLon}) or C{None}. @keyword eps: Optional convergence limit, L{EPS} or above (C{float}). @keyword unfalse: Unfalse B{C{easting}} and B{C{northing}} if falsed (C{bool}). @return: This UTM coordinate as (B{C{LatLon}}) or a L{LatLonDatum5Tuple}C{(lat, lon, datum, convergence, scale)} if B{C{LatLon}} is C{None}. @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('%s invalid: %r' % ('meridional', E.A)) 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('%s invalid: %r' % ('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)
def par1(self): '''Get the 1st standard parallel (C{degrees90}). ''' return degrees90(self._par1)
def lat0(self): '''Get the origin latitude (C{degrees90}). ''' return degrees90(self._lat0)