def greatCircle(self, bearing): '''Compute the n-vector normal to great circle obtained by heading on given compass bearing from this point as its n-vector. Direction of vector is such that initial bearing vector b = c × p. @param bearing: Initial compass bearing (C{degrees}). @return: N-vector representing great circle (L{Nvector}). @raise Valuerror: Polar coincidence. @example: >>> n = LatLon(53.3206, -1.7297).toNvector() >>> gc = n.greatCircle(96.0) # [-0.794, 0.129, 0.594] ''' s, c = sincos2d(bearing) e = NorthPole.cross(self, raiser='pole') # easting n = self.cross(e, raiser='point') # northing e = e.times(c / e.length) n = n.times(s / n.length) return n.minus(e)
def _gc(p, b): n = p.toNvector() de = NorthPole.cross(n, raiser='pole').unit() # east vector @ n dn = n.cross(de) # north vector @ n s, c = sincos2d(b) dest = de.times(s) dnct = dn.times(c) d = dnct.plus(dest) # direction vector @ n return n.cross(d) # great circle point + bearing
def fEd(self, deg): '''The incomplete integral of the second kind with the argument given in degrees. @param deg: Angle (C{degrees}). @return: E(π deg/180, k). ''' n = ceil(deg / 360.0 - 0.5) sn, cn = sincos2d(deg - n * 360.0) return self.fE(sn, cn, self.fDelta(sn, cn)) + 4 * self.cE * n
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 destination(self, distance, bearing, radius=R_M, height=None): '''Locate the destination from this point after having travelled the given distance on the given bearing. @param distance: Distance travelled (C{meter}, same units as B{C{radius}}). @param bearing: Bearing from this point (compass C{degrees360}). @keyword radius: Optional, mean earth radius (C{meter}). @keyword height: Optional height at destination, overriding the default height (C{meter}, same units as B{C{radius}}). @return: Destination point (L{LatLon}). @raise Valuerror: Polar coincidence. @example: >>> p = LatLon(51.4778, -0.0015) >>> q = p.destination(7794, 300.7) >>> q.toStr() # 51.513546°N, 000.098345°W @JSname: I{destinationPoint}. ''' p = self.toNvector() e = NorthPole.cross(p, raiser='pole').unit() # east vector at p n = p.cross(e) # north vector at p s, c = sincos2d(bearing) q = n.times(c).plus(e.times(s)) # direction vector @ p s, c = sincos2(float(distance) / float(radius)) # angular distance in radians n = p.times(c).plus(q.times(s)) return n.toLatLon( height=height, LatLon=self.classof) # Nvector(n.x, n.y, n.z).toLatLon(...)
def toUps8(latlon, lon=None, datum=None, Ups=Ups, pole='', falsed=True, strict=True, name=''): '''Convert a lat-/longitude point to a UPS coordinate. @param latlon: Latitude (C{degrees}) or an (ellipsoidal) geodetic C{LatLon} point. @keyword lon: Optional longitude (C{degrees}) or C{None} if I{latlon} is a C{LatLon}. @keyword datum: Optional datum for this UPS coordinate, overriding I{latlon}'s datum (C{Datum}). @keyword Ups: Optional (sub-)class to return the UPS coordinate (L{Ups}) or C{None}. @keyword pole: Optional top/center of (stereographic) projection (C{str}, C{'N[orth]'} or C{'S[outh]'}). @keyword falsed: False both easting and northing (C{bool}). @keyword strict: Restrict I{lat} to UPS ranges (C{bool}). @keyword name: Optional I{Ups} name (C{str}). @return: The UPS coordinate (L{Ups}) or a 8-tuple (zone, hemisphere, easting, northing, band, datum, convergence, scale) if I{Ups} is C{None}. @raise RangeError: If I{strict} and I{lat} outside the valid UPS bands or if I{lat} or I{lon} outside the valid range and I{rangerrrors} set to C{True}. @raise TypeError: If I{latlon} is not ellipsoidal. @raise ValueError: If I{lon} value is missing or if I{latlon} is invalid. @see: Karney's C++ class U{UPS <http://GeographicLib.SourceForge.io/html/classGeographicLib_1_1UPS.html>}. ''' lat, lon, d, name = _to4lldn(latlon, lon, datum, name) z, B, p, lat, lon = upsZoneBand5(lat, lon, strict=strict) p = str(pole or p)[:1] N = p in 'Nn' E = d.ellipsoid A = abs(lat - 90) < _TOL t = tan(radians(lat if N else -lat)) T = E.es_taupf(t) r = hypot1(T) + abs(T) if T >= 0: r = 0 if A else 1 / r k0 = getattr(Ups, '_scale0', _K0) # Ups is class or None r *= 2 * k0 * E.a / E.es_c k = k0 if A else _scale(E, r, t) c = lon # [-180, 180) from .upsZoneBand5 x, y = sincos2d(c) x *= r y *= r if N: y = -y else: c = -c if falsed: x += _Falsing y += _Falsing if Ups is None: r = z, p, x, y, B, d, c, k else: if z != _UPS_ZONE and not strict: z = _UPS_ZONE # ignore UTM zone r = _xnamed(Ups(z, p, x, y, band=B, datum=d, convergence=c, scale=k, falsed=falsed), name) if hasattr(Ups, '_hemisphere'): r._hemisphere = _hemi(lat) return r
def toUps8(latlon, lon=None, datum=None, Ups=Ups, pole='', falsed=True, strict=True, name=''): '''Convert a lat-/longitude point to a UPS coordinate. @param latlon: Latitude (C{degrees}) or an (ellipsoidal) geodetic C{LatLon} point. @keyword lon: Optional longitude (C{degrees}) or C{None} if B{C{latlon}} is a C{LatLon}. @keyword datum: Optional datum for this UPS coordinate, overriding B{C{latlon}}'s datum (C{Datum}). @keyword Ups: Optional (sub-)class to return the UPS coordinate (L{Ups}) or C{None}. @keyword pole: Optional top/center of (stereographic) projection (C{str}, C{'N[orth]'} or C{'S[outh]'}). @keyword falsed: False both easting and northing (C{bool}). @keyword strict: Restrict B{C{lat}} to UPS ranges (C{bool}). @keyword name: Optional B{C{Ups}} name (C{str}). @return: The UPS coordinate (B{C{Ups}}) or a L{UtmUps8Tuple}C{(zone, hemipole, easting, northing, band, datum, convergence, scale)} if B{C{Ups}} is C{None}. The C{hemipole} is the C{'N'|'S'} pole, the UPS projection top/center. @raise RangeError: If B{C{strict}} and B{C{lat}} outside the valid UPS bands or if B{C{lat}} or B{C{lon}} outside the valid range and L{rangerrors} set to C{True}. @raise TypeError: If B{C{latlon}} is not ellipsoidal. @raise ValueError: If B{C{lon}} value is missing or if B{C{latlon}} is invalid. @see: Karney's C++ class U{UPS <https://GeographicLib.SourceForge.io/html/classGeographicLib_1_1UPS.html>}. ''' lat, lon, d, name = _to4lldn(latlon, lon, datum, name) z, B, p, lat, lon = upsZoneBand5( lat, lon, strict=strict) # PYCHOK UtmUpsLatLon5Tuple E = d.ellipsoid p = str(pole or p)[:1].upper() N = p == 'N' # is north a = lat if N else -lat A = abs(a - 90) < _TOL # at pole t = tan(radians(a)) T = E.es_taupf(t) r = hypot1(T) + abs(T) if T >= 0: r = 0 if A else 1 / r k0 = getattr(Ups, '_scale0', _K0) # Ups is class or None r *= 2 * k0 * E.a / E.es_c k = k0 if A else _scale(E, r, t) c = lon # [-180, 180) from .upsZoneBand5 x, y = sincos2d(c) x *= r y *= r if N: y = -y else: c = -c if falsed: x += _Falsing y += _Falsing if Ups is None: r = UtmUps8Tuple(z, p, x, y, B, d, c, k) else: if z != _UPS_ZONE and not strict: z = _UPS_ZONE # ignore UTM zone r = Ups(z, p, x, y, band=B, datum=d, falsed=falsed, convergence=c, scale=k) r._hemisphere = _hemi(lat) if isinstance(latlon, _LLEB) and d is latlon.datum: r._latlon_to(latlon, falsed) # XXX weakref(latlon)? return _xnamed(r, name)