def _inverse(self, other, azis): '''(INTERNAL) Inverse Vincenty method. @raise TypeError: The other point is not L{LatLon}. @raise ValueError: If this and the 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 or this and the other point coincide. ''' E = self.ellipsoids(other) c1, s1, _ = _r3(self.lat, E.f) c2, s2, _ = _r3(other.lat, E.f) c1c2, s1s2 = c1 * c2, s1 * s2 c1s2, s1c2 = c1 * s2, s1 * c2 ll = dl = radians(other.lon - self.lon) for _ in range(self._iterations): cll, sll, ll_ = cos(ll), sin(ll), ll ss = hypot(c2 * sll, c1s2 - s1c2 * cll) if ss < EPS: raise VincentyError('%r coincident with %r' % (self, other)) cs = s1s2 + c1c2 * cll s = atan2(ss, cs) sa = c1c2 * sll / ss c2a = 1 - (sa * sa) if abs(c2a) < EPS: c2a = 0 # equatorial line ll = dl + E.f * sa * s else: c2sm = cs - 2 * s1s2 / c2a ll = dl + _dl(E.f, c2a, sa, s, cs, ss, c2sm) if abs(ll - ll_) < self._epsilon: break else: raise VincentyError('no convergence %r to %r' % (self, other)) if c2a: # e22 == (a / b) ** 2 - 1 A, B = _p2(c2a, E.e22) s = A * (s - _ds(B, cs, ss, c2sm)) b = E.b # if self.height or other.height: # b += self._alter(other) d = b * s if azis: # forward and reverse azimuth cll, sll = cos(ll), sin(ll) f = degrees360(atan2(c2 * sll, c1s2 - s1c2 * cll)) r = degrees360(atan2(c1 * sll, -s1c2 + c1s2 * cll)) d = d, f, r return d
def initialBearingTo(self, other, **unused): '''Compute the initial bearing (aka forward azimuth) from this to an other point. @param other: The other point (L{LatLon}). @param unused: Optional keyword argument I{wrap} ignored. @return: Initial bearing (compass degrees). @raise TypeError: The I{other} point is not L{LatLon}. @raise Valuerror: Points or polar coincidence. @example: >>> p1 = LatLon(52.205, 0.119) >>> p2 = LatLon(48.857, 2.351) >>> b = p1.bearingTo(p2) # 156.2 @JSname: I{bearingTo}. ''' gc1 = self.greatCircleTo(other) gc2 = self.toNvector().cross(NorthPole, raiser='pole') # gc2 = self.greatCircleTo(NorthPole) return degrees360(gc1.angleTo(gc2, vSign=self.toNvector()))
def initialBearingTo(self, other, wrap=False): '''Compute the initial bearing (aka forward azimuth) from this to an other point. @param other: The other point (L{LatLon}). @keyword wrap: Wrap and unroll longitudes (bool). @return: Initial bearing (compass degrees). @raise TypeError: The I{other} point is not L{LatLon}. @example: >>> p1 = LatLon(52.205, 0.119) >>> p2 = LatLon(48.857, 2.351) >>> b = p1.initialBearingTo(p2) # 156.2 @JSname: I{bearingTo}. ''' self.others(other) 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) # see <http://mathforum.org/library/drmath/view/55417.html> x = ca1 * sa2 - sa1 * ca2 * cdb y = sdb * ca2 return degrees360(atan2(y, x))
def _direct(self, distance, bearing, llr, height=None): '''(INTERNAL) Direct Vincenty method. @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) ci, si = cos(i), sin(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): cs, ss, c2sm = cos(s), sin(s), cos(s12 + s) s_, s = s, d + _ds(B, cs, ss, c2sm) if abs(s - s_) < self._epsilon: break else: raise VincentyError('no convergence %r' % (self, )) t = s1 * ss - c1 * cs * ci # final bearing (reverse azimuth +/- 180) r = degrees360(atan2(sa, -t)) if llr: # destination latitude in [-270, 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 rhumbBearingTo(self, other): '''Return the initial bearing (forward azimuth) from this to an other point along a rhumb (loxodrome) line. @param other: The other point (spherical LatLon). @return: Initial bearing (compass degrees). @raise TypeError: The other point is not spherical. @example: >>> p = LatLon(51.127, 1.338) >>> q = LatLon(50.964, 1.853) >>> b = p.rhumbBearingTo(q) # 116.7 ''' _, db, dp = self._rhumb3(other) return degrees360(atan2(db, dp))
def compassAngle(lat0, lon0, lat1, lon1): '''Return the angle from North for the direction vector M{(lon1 - lon0, lat1 - lat0)} between two points. Suitable only for short, non-near-polar vectors up to a few hundred Km or Miles. Use I{LatLon} methods I{initialBearingTo} or I{forward azimuth} for larger distances. @param lat0: From latitude (degrees). @param lon0: From longitude (degrees). @param lat1: To latitude (degrees). @param lon1: To longitude (degrees). @return: Angle from North (degrees360). @note: Courtesy Martin Schultz. @see: U{Local, Flat Earth<http://www.edwilliams.org/avform.htm#flat>}. ''' return degrees360(atan2(lon1 - lon0, lat1 - lat0))
def bearingTo(self, other): '''Computes the initial bearing (aka forward azimuth) from this to an other point. @param other: The other point (L{LatLon}). @return: Initial bearing (compass degrees). @raise TypeError: The other point is not L{LatLon}. @example: >>> p1 = LatLon(52.205, 0.119) >>> p2 = LatLon(48.857, 2.351) >>> b = p1.bearingTo(p2) # 156.2 ''' gc1 = self.greatCircleTo(other) gc2 = self.toNvector().cross(NorthPole) # gc2 = self.greatCircleTo(NorthPole) return degrees360(gc1.angleTo(gc2, vSign=self.toNvector()))
def _inverse(self, other, azis, wrap): '''(INTERNAL) Inverse Vincenty method. @raise TypeError: The other point is not L{LatLon}. @raise ValueError: If this and the I{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 and/or if this and the I{other} point are near-antipodal or coincide. ''' E = self.ellipsoids(other) c1, s1, _ = _r3(self.lat, E.f) c2, s2, _ = _r3(other.lat, E.f) c1c2, s1c2 = c1 * c2, s1 * c2 c1s2, s1s2 = c1 * s2, s1 * s2 dl, _ = unroll180(self.lon, other.lon, wrap=wrap) ll = dl = radians(dl) for _ in range(self._iterations): cll, sll, ll_ = cos(ll), sin(ll), ll ss = hypot(c2 * sll, c1s2 - s1c2 * cll) if ss < EPS: raise VincentyError('%r coincides with %r' % (self, other)) cs = s1s2 + c1c2 * cll s = atan2(ss, cs) sa = c1c2 * sll / ss c2a = 1 - sa**2 if abs(c2a) < EPS: c2a = 0 # equatorial line ll = dl + E.f * sa * s else: c2sm = cs - 2 * s1s2 / c2a ll = dl + _dl(E.f, c2a, sa, s, cs, ss, c2sm) if abs(ll - ll_) < self._epsilon: break # <http://github.com/chrisveness/geodesy/blob/master/latlon-vincenty.js> # omitted and applied only after failure to converge, see footnote under # Inverse at <http://en.wikipedia.org/wiki/Vincenty's_formulae> # elif abs(ll) > PI and self.isantipode(other, eps=self._epsilon): # raise VincentyError('%r antipodal to %r' % (self, other)) else: t = 'antipodal ' if self.isantipode(other, eps=self._epsilon) else '' raise VincentyError('no convergence, %r %sto %r' % (self, t, other)) if c2a: # e22 == (a / b)**2 - 1 A, B = _p2(c2a * E.e22) s = A * (s - _ds(B, cs, ss, c2sm)) b = E.b # if self.height or other.height: # b += self._havg(other) d = b * s if azis: # forward and reverse azimuth cll, sll = cos(ll), sin(ll) f = degrees360(atan2(c2 * sll, c1s2 - s1c2 * cll)) r = degrees360(atan2(c1 * sll, -s1c2 + c1s2 * cll)) d = d, f, r return d
def bearing(self): '''Get bearing of this NED vector in compass degrees (degrees360). ''' if self._bearing is None: self._bearing = degrees360(atan2(self.east, self.north)) return self._bearing