def _intersects2(c1, rad1, c2, rad2, radius=R_M, # in .ellipsoidalBase._intersects2 height=None, wrap=True, too_d=None, LatLon=LatLon, **LatLon_kwds): # (INTERNAL) Intersect two spherical circles, see L{intersections2} # above, separated to allow callers to embellish any exceptions def _dest3(bearing, h): a, b = _destination2(a1, b1, r1, bearing) return _latlon3(degrees90(a), degrees180(b), h, intersections2, LatLon, **LatLon_kwds) r1, r2, f = _rads3(rad1, rad2, radius) if f: # swapped c1, c2 = c2, c1 # PYCHOK swap a1, b1 = c1.philam a2, b2 = c2.philam db, b2 = unrollPI(b1, b2, wrap=wrap) d = vincentys_(a2, a1, db) # radians if d < max(r1 - r2, EPS): raise ValueError(_near_concentric_) x = fsum_(r1, r2, -d) # overlap if x > EPS: sd, cd, sr1, cr1, _, cr2 = sincos2(d, r1, r2) x = sd * sr1 if abs(x) < EPS: raise ValueError(_invalid_) x = acos1((cr2 - cd * cr1) / x) # 0 <= x <= PI elif x < 0: t = (d * radius) if too_d is None else too_d raise ValueError(_too_distant_fmt_ % (t,)) if height is None: # "radical height" f = _radical2(d, r1, r2).ratio h = Height(favg(c1.height, c2.height, f=f)) else: h = Height(height) b = bearing_(a1, b1, a2, b2, final=False, wrap=wrap) if x < _EPS_I2: # externally ... r = _dest3(b, h) elif x > _PI_EPS_I2: # internally ... r = _dest3(b + PI, h) else: return _dest3(b + x, h), _dest3(b - x, h) return r, r # ... abutting circles
def initialBearingTo(self, other, wrap=False, raiser=False): '''Compute the initial bearing (forward azimuth) from this to an other point. @param other: The other point (spherical L{LatLon}). @keyword wrap: Wrap and unroll longitudes (C{bool}). @keyword raiser: Optionally, raise L{CrossError} (C{bool}), use B{C{raiser}}=C{True} for behavior like C{sphericalNvector.LatLon.initialBearingTo}. @return: Initial bearing (compass C{degrees360}). @raise CrossError: If this and the B{C{other}} point coincide, provided B{C{raiser}} is C{True} and L{crosserrors} is C{True}. @raise TypeError: The B{C{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() # XXX behavior like sphericalNvector.LatLon.initialBearingTo if raiser and crosserrors() and max(abs(a2 - a1), abs(b2 - b1)) < EPS: raise CrossError('%s %s: %r' % ('coincident', 'points', other)) return degrees(bearing_(a1, b1, a2, b2, final=False, wrap=wrap))
def _xb(a1, b1, end, a, b, wrap): # difference between the bearing to (a, b) and the given # bearing is negative if both are in opposite directions r = bearing_(a1, b1, radians(a), radians(b), wrap=wrap) return PI_2 - abs(wrapPI(r - radians(end)))
def intersections2( center1, rad1, center2, rad2, radius=R_M, # MCCABE 13 height=None, wrap=False, LatLon=LatLon, **LatLon_kwds): '''Compute the intersection points of two circles each defined by a center point and radius. @arg center1: Center of the first circle (L{LatLon}). @arg rad1: Radius of the second circle (C{meter} or C{radians}, see B{C{radius}}). @arg center2: Center of the second circle (L{LatLon}). @arg rad2: Radius of the second circle (C{meter} or C{radians}, see B{C{radius}}). @kwarg radius: Mean earth radius (C{meter} or C{None} if both B{C{rad1}} and B{C{rad2}} are given in C{radians}). @kwarg height: Optional height for the intersection point, overriding the mean height (C{meter}). @kwarg wrap: Wrap and unroll longitudes (C{bool}). @kwarg LatLon: Optional class to return the intersection points (L{LatLon}) or C{None}. @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword arguments, ignored if B{C{LatLon=None}}. @return: 2-Tuple of the intersection points, each a B{C{LatLon}} instance or L{LatLon3Tuple}C{(lat, lon, height)} if B{C{LatLon}} is C{None}. The intersection points are the same instance for abutting circles. @raise IntersectionError: Concentric, antipodal, invalid or non-intersecting circles. @raise TypeError: If B{C{center1}} or B{C{center2}} not L{LatLon}. @raise ValueError: Invalid B{C{rad1}}, B{C{rad2}}, B{C{radius}} or B{C{height}}. @note: Courtesy U{Samuel Čavoj<https://GitHub.com/mrJean1/PyGeodesy/issues/41>}. @see: This U{Answer<https://StackOverflow.com/questions/53324667/ find-intersection-coordinates-of-two-circles-on-earth/53331953>}. ''' def _destination1(bearing): a, b = _destination2(a1, b1, r1, bearing) return _latlon3(degrees90(a), degrees180(b), h, intersections2, LatLon, **LatLon_kwds) _Trll.others(center1, name='center1') _Trll.others(center2, name='center2') a1, b1 = center1.philam a2, b2 = center2.philam r1, r2, x = _rads3(rad1, rad2, radius) if x: a1, b1, a2, b2 = a2, b2, a1, b1 db, _ = unrollPI(b1, b2, wrap=wrap) d = vincentys_(a2, a1, db) # radians if d < max(r1 - r2, EPS): raise IntersectionError(center1=center1, rad1=rad1, center2=center2, rad2=rad2, txt=_near_concentric_) x = fsum_(r1, r2, -d) if x > EPS: try: sd, cd, s1, c1, _, c2 = sincos2(d, r1, r2) x = sd * s1 if abs(x) < EPS: raise ValueError x = acos1((c2 - cd * c1) / x) except ValueError: raise IntersectionError(center1=center1, rad1=rad1, center2=center2, rad2=rad2) elif x < 0: raise IntersectionError(center1=center1, rad1=rad1, center2=center2, rad2=rad2, txt=_too_distant_) b = bearing_(a1, b1, a2, b2, final=False, wrap=wrap) if height is None: h = fmean((center1.height, center2.height)) else: Height(height) if abs(x) > EPS: return _destination1(b + x), _destination1(b - x) else: # abutting circles x = _destination1(b) return x, x