def _intersects2(center1, r1, center2, r2, sphere=True, too_d=None, # in .ellipsoidalBase._intersections2 Vector=None, **Vector_kwds): # (INTERNAL) Intersect two spheres or circles, see L{intersections2} # above, separated to allow callers to embellish any exceptions def _V3(x, y, z): v = Vector3d(x, y, z) n = intersections2.__name__ return _V_n(v, n, Vector, Vector_kwds) def _xV3(c1, u, x, y): xy1 = x, y, _1_0 # transform to original space return _V3(fdot(xy1, u.x, -u.y, c1.x), fdot(xy1, u.y, u.x, c1.y), _0_0) c1 = _otherV3d(sphere=sphere, center1=center1) c2 = _otherV3d(sphere=sphere, center2=center2) if r1 < r2: # r1, r2 == R, r c1, c2 = c2, c1 r1, r2 = r2, r1 m = c2.minus(c1) d = m.length if d < max(r2 - r1, EPS): raise ValueError(_near_concentric_) o = fsum_(-d, r1, r2) # overlap == -(d - (r1 + r2)) # compute intersections with c1 at (0, 0) and c2 at (d, 0), like # <https://MathWorld.Wolfram.com/Circle-CircleIntersection.html> if o > EPS: # overlapping, r1, r2 == R, r x = _radical2(d, r1, r2).xline y = _1_0 - (x / r1)**2 if y > EPS: y = r1 * sqrt(y) # y == a / 2 elif y < 0: raise ValueError(_invalid_) else: # abutting y = _0_0 elif o < 0: t = d if too_d is None else too_d raise ValueError(_too_(Fmt.distant(t))) else: # abutting x, y = r1, _0_0 u = m.unit() if sphere: # sphere center and radius c = c1 if x < EPS else ( c2 if x > EPS1 else c1.plus(u.times(x))) t = _V3(c.x, c.y, c.z), Radius(y) elif y > 0: # intersecting circles t = _xV3(c1, u, x, y), _xV3(c1, u, x, -y) else: # abutting circles t = _xV3(c1, u, x, 0) t = t, t return t
def _txt(c1, r1, c2, r2): # check for concentric or too distant spheres d = c1.minus(c2).length if d < abs(r1 - r2): t = _near_concentric_ elif d > (r1 + r2): t = _too_(Fmt.distant(d)) else: return NN return _SPACE_(c1.name, 'and', c2.name, t)
def _intersects2( c1, r1, c2, r2, height=None, wrap=True, # MCCABE 17 equidistant=None, tol=_TOL_M, LatLon=None, **LatLon_kwds): # (INTERNAL) Intersect two spherical circles, see L{_intersections2} # above, separated to allow callers to embellish any exceptions from pygeodesy.sphericalTrigonometry import _intersects2 as _si2, LatLon as _LLS from pygeodesy.vector3d import _intersects2 as _vi2 def _latlon4(t, h, n): r = _LatLon4Tuple(t.lat, t.lon, h, t.datum, LatLon, LatLon_kwds) r._iteration = t.iteration # ._iteration for tests return _xnamed(r, n) if r1 < r2: c1, c2 = c2, c1 r1, r2 = r2, r1 E = c1.ellipsoids(c2) if r1 > (min(E.b, E.a) * PI): raise ValueError(_exceed_PI_radians_) if wrap: # unroll180 == .karney._unroll2 c2 = _unrollon(c1, c2) # distance between centers and radii are # measured along the ellipsoid's surface m = c1.distanceTo(c2, wrap=False) # meter if m < max(r1 - r2, EPS): raise ValueError(_near_concentric_) if fsum_(r1, r2, -m) < 0: raise ValueError(_too_(Fmt.distant(m))) f = _radical2(m, r1, r2).ratio # "radical fraction" r = E.rocMean(favg(c1.lat, c2.lat, f=f)) e = max(m2degrees(tol, radius=r), EPS) # get the azimuthal equidistant projection A = _Equidistant2(equidistant, c1.datum) # gu-/estimate initial intersections, spherically ... t1, t2 = _si2(_LLS(c1.lat, c1.lon, height=c1.height), r1, _LLS(c2.lat, c2.lon, height=c2.height), r2, radius=r, height=height, wrap=False, too_d=m) h, n = t1.height, t1.name # ... and then iterate like Karney suggests to find # tri-points of median lines, @see: references under # method LatLonEllipsoidalBase.intersections2 above ts, ta = [], None for t in ((t1, ) if t1 is t2 else (t1, t2)): p = None # force first d == p to False for i in range(1, _TRIPS): A.reset(t.lat, t.lon) # gu-/estimate as origin # convert centers to projection space t1 = A.forward(c1.lat, c1.lon) t2 = A.forward(c2.lat, c2.lon) # compute intersections in projection space v1, v2 = _vi2( t1, r1, # XXX * t1.scale?, t2, r2, # XXX * t2.scale?, sphere=False, too_d=m) # convert intersections back to geodetic t1 = A.reverse(v1.x, v1.y) d1 = euclid(t1.lat - t.lat, t1.lon - t.lon) if v1 is v2: # abutting t, d = t1, d1 else: t2 = A.reverse(v2.x, v2.y) d2 = euclid(t2.lat - t.lat, t2.lon - t.lon) # consider only the closer intersection t, d = (t1, d1) if d1 < d2 else (t2, d2) # break if below tolerance or if unchanged if d < e or d == p: t._iteration = i # _NamedTuple._iteration ts.append(t) if v1 is v2: # abutting ta = t break p = d else: raise ValueError(_no_(Fmt.convergence(tol))) if ta: # abutting circles r = _latlon4(ta, h, n) elif len(ts) == 2: return _latlon4(ts[0], h, n), _latlon4(ts[1], h, n) elif len(ts) == 1: # XXX assume abutting r = _latlon4(ts[0], h, n) else: raise _AssertionError(ts=ts) return r, r