def _distances(self, x, y): # (x, y) degrees g = self._geodesic for ll in self._lls: # see .ellipsoidalKarney.LatLon._inverse _, lon = unroll180(x, ll.lon, wrap=self._wrap) # g.LONG_UNROLL # XXX g.DISTANCE needed for 's12', distance in meters? yield abs(g.Inverse(y, x, ll.lat, lon)['a12'])
def cosineAndoyerLambert(lat1, lon1, lat2, lon2, datum=Datums.WGS84, wrap=False): '''Compute the distance between two (ellipsoidal) points using the U{Andoyer-Lambert correction<https://navlib.net/wp-content/uploads/ 2013/10/admiralty-manual-of-navigation-vol-1-1964-english501c.pdf>} of the U{Law of Cosines<https://www.Movable-Type.co.UK/scripts/latlong.html#cosine-law>} fromula. @arg lat1: Start latitude (C{degrees}). @arg lon1: Start longitude (C{degrees}). @arg lat2: End latitude (C{degrees}). @arg lon2: End longitude (C{degrees}). @kwarg datum: Ellipsoidal datum to use (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: Distance (C{meter}, same units as the B{C{datum}}'s ellipsoid axes). @raise TypeError: Invalid B{C{datum}}. @see: Functions L{cosineAndoyerLambert_}, L{cosineForsytheAndoyerLambert}, L{cosineLaw}, L{equirectangular}, L{euclidean}, L{flatLocal}/L{hubeny}, L{flatPolar}, L{haversine}, L{thomas} and L{vincentys} and method L{Ellipsoid.distance2}. ''' d, _ = unroll180(lon1, lon2, wrap=wrap) r = cosineAndoyerLambert_(Phi_(lat2=lat2), Phi_(lat1=lat1), radians(d), datum=datum) return r * datum.ellipsoid.a
def flatLocal(lat1, lon1, lat2, lon2, datum=Datums.WGS84, wrap=False): '''Compute the distance between two (ellipsoidal) points using the U{ellipsoidal Earth to plane projection<https://WikiPedia.org/ wiki/Geographical_distance#Ellipsoidal_Earth_projected_to_a_plane>} aka U{Hubeny<https://www.OVG.AT/de/vgi/files/pdf/3781/>} formula. @arg lat1: Start latitude (C{degrees}). @arg lon1: Start longitude (C{degrees}). @arg lat2: End latitude (C{degrees}). @arg lon2: End longitude (C{degrees}). @kwarg datum: Ellipsoidal datum to use (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: Distance (C{meter}, same units as the B{C{datum}}'s ellipsoid axes). @raise TypeError: Invalid B{C{datum}}. @note: The meridional and prime_vertical radii of curvature are taken and scaled at the mean of both latitude. @see: Functions L{flatLocal_}/L{hubeny_}, L{cosineLaw}, L{flatPolar}, L{cosineAndoyerLambert}, L{cosineForsytheAndoyerLambert}, L{equirectangular}, L{euclidean}, L{haversine}, L{thomas}, L{vincentys}, method L{Ellipsoid.distance2} and U{local, flat earth approximation <https://www.EdWilliams.org/avform.htm#flat>}. ''' d, _ = unroll180(lon1, lon2, wrap=wrap) return flatLocal_(Phi_(lat2=lat2), Phi_(lat1=lat1), radians(d), datum=datum)
def vincentys(lat1, lon1, lat2, lon2, radius=R_M, wrap=False): '''Compute the distance between two (spherical) points using U{Vincenty's<https://WikiPedia.org/wiki/Great-circle_distance>} spherical formula. @arg lat1: Start latitude (C{degrees}). @arg lon1: Start longitude (C{degrees}). @arg lat2: End latitude (C{degrees}). @arg lon2: End longitude (C{degrees}). @kwarg radius: Mean earth radius (C{meter}). @kwarg wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: Distance (C{meter}, same units as B{C{radius}}). @see: Functions L{vincentys_}, L{cosineLaw}, L{equirectangular}, L{euclidean}, L{flatLocal}, L{flatPolar}, L{haversine} and methods L{Ellipsoid.distance2}, C{LatLon.distanceTo*} and C{LatLon.equirectangularTo}. @note: See note at function L{vincentys_}. ''' r = Radius(radius) if r: d, _ = unroll180(lon1, lon2, wrap=wrap) r *= vincentys_(Phi_(lat2, name='lat2'), Phi_(lat1, name='lat1'), radians(d)) return r
def thomas(lat1, lon1, lat2, lon2, datum=Datums.WGS84, wrap=False): '''Compute the distance between two (ellipsoidal) points using U{Thomas'<https://apps.DTIC.mil/dtic/tr/fulltext/u2/703541.pdf>} formula. @arg lat1: Start latitude (C{degrees}). @arg lon1: Start longitude (C{degrees}). @arg lat2: End latitude (C{degrees}). @arg lon2: End longitude (C{degrees}). @kwarg datum: Ellipsoidal datum to use (L{Datum}). @kwarg wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: Distance (C{meter}, same units as the B{C{datum}}'s ellipsoid axes). @raise TypeError: Invalid B{C{datum}}. @see: Functions L{thomas_}, L{cosineAndoyerLambert}, L{cosineForsytheAndoyerLambert}, L{cosineLaw}, L{equirectangular}, L{euclidean}, L{flatLocal}/L{hubeny}, L{flatPolar}, L{haversine}, L{vincentys} and method L{Ellipsoid.distance2}. ''' d, _ = unroll180(lon1, lon2, wrap=wrap) r = thomas_(Phi_(lat2, name=_lat2_), Phi_(lat1, name=_lat1_), radians(d), datum=datum) return r * datum.ellipsoid.a
def cosineLaw(lat1, lon1, lat2, lon2, radius=R_M, wrap=False): '''Compute the distance between two points using the U{spherical Law of Cosines <https://www.Movable-Type.co.UK/scripts/latlong.html#cosine-law>} fromula. @arg lat1: Start latitude (C{degrees}). @arg lon1: Start longitude (C{degrees}). @arg lat2: End latitude (C{degrees}). @arg lon2: End longitude (C{degrees}). @kwarg radius: Mean earth radius (C{meter}). @kwarg wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: Distance (C{meter}, same units as B{C{radius}}). @raise TypeError: Invalid B{C{datum}}. @see: Functions L{cosineLaw_}, L{equirectangular}, L{euclidean}, L{flatLocal}, L{flatPolar}, L{haversine}, L{vincentys} and method L{Ellipsoid.distance2}. @note: See note at function L{vincentys_}. ''' r = Radius(radius) if r: d, _ = unroll180(lon1, lon2, wrap=wrap) r *= cosineLaw_(Phi_(lat2, name='lat2'), Phi_(lat1, name='lat1'), radians(d)) return r
def compassAngle(lat1, lon1, lat2, lon2, adjust=True, wrap=False): '''Return the angle from North for the direction vector M{(lon2 - lon1, lat2 - lat1)} between two points. Suitable only for short, non-near-polar vectors up to a few hundred Km or Miles. Use function L{bearing} for longer vectors. @arg lat1: From latitude (C{degrees}). @arg lon1: From longitude (C{degrees}). @arg lat2: To latitude (C{degrees}). @arg lon2: To longitude (C{degrees}). @kwarg adjust: Adjust the longitudinal delta by the cosine of the mean latitude (C{bool}). @kwarg wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: Compass angle from North (C{degrees360}). @note: Courtesy Martin Schultz. @see: U{Local, flat earth approximation <https://www.EdWilliams.org/avform.htm#flat>}. ''' d_lon, _ = unroll180(lon1, lon2, wrap=wrap) if adjust: # scale delta lon d_lon *= _scaled(lat1, lat2) return degrees360(atan2(d_lon, lat2 - lat1))
def euclidean(lat1, lon1, lat2, lon2, radius=R_M, adjust=True, wrap=False): '''Approximate the C{Euclidian} distance between two (spherical) points. @arg lat1: Start latitude (C{degrees}). @arg lon1: Start longitude (C{degrees}). @arg lat2: End latitude (C{degrees}). @arg lon2: End longitude (C{degrees}). @kwarg radius: Mean earth radius (C{meter}). @kwarg adjust: Adjust the longitudinal delta by the cosine of the mean latitude (C{bool}). @kwarg wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: Distance (C{meter}, same units as B{C{radius}}). @see: U{Distance between two (spherical) points <https://www.EdWilliams.org/avform.htm#Dist>}, functions L{euclidean_}, L{cosineLaw}, L{equirectangular}, L{flatLocal}, L{flatPolar}, L{haversine}, L{vincentys} and methods L{Ellipsoid.distance2}, C{LatLon.distanceTo*} and C{LatLon.equirectangularTo}. ''' r = Radius(radius) if r: d, _ = unroll180(lon1, lon2, wrap=wrap) r *= euclidean_(Phi_(lat2, name='lat2'), Phi_(lat1, name='lat1'), radians(d), adjust=adjust) return r
def haversine(lat1, lon1, lat2, lon2, radius=R_M, wrap=False): '''Compute the distance between two (spherical) points using the U{Haversine<https://www.Movable-Type.co.UK/scripts/latlong.html>} formula. @param lat1: Start latitude (C{degrees}). @param lon1: Start longitude (C{degrees}). @param lat2: End latitude (C{degrees}). @param lon2: End longitude (C{degrees}). @keyword radius: Optional, mean earth radius (C{meter}). @keyword wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: Distance (C{meter}, same units as B{C{radius}}). @see: U{Distance between two (spherical) points <https://www.EdWilliams.org/avform.htm#Dist>}, functions L{equirectangular}, L{euclidean} and L{vincentys} and methods L{Ellipsoid.distance2}, C{LatLon.distanceTo*} and C{LatLon.equirectangularTo}. @note: See note under L{vincentys_}. ''' d, _ = unroll180(lon1, lon2, wrap=wrap) r = haversine_(radians(lat2), radians(lat1), radians(d)) return r * float(radius)
def flatLocal(lat1, lon1, lat2, lon2, datum=Datums.WGS84, wrap=False): '''Compute the distance between two (ellipsoidal) points using the U{ellipsoidal Earth to plane projection <https://WikiPedia.org/wiki/Geographical_distance#Ellipsoidal_Earth_projected_to_a_plane>} fromula. @arg lat1: Start latitude (C{degrees}). @arg lon1: Start longitude (C{degrees}). @arg lat2: End latitude (C{degrees}). @arg lon2: End longitude (C{degrees}). @kwarg datum: Optional, (ellipsoidal) datum to use (L{Datum}). @kwarg wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: Distance (C{meter}, same units as the B{C{datum}}'s ellipsoid axes). @raise TypeError: Invalid B{C{datum}}. @note: The meridional and prime_vertical radii of curvature are taken and scaled at the mean latitude. @see: Functions L{flatLocal_}, L{cosineLaw}, L{flatPolar}, L{equirectangular}, L{euclidean}, L{haversine}, L{vincentys}, method L{Ellipsoid.distance2} and U{local, flat earth approximation <https://www.edwilliams.org/avform.htm#flat>}. ''' d, _ = unroll180(lon1, lon2, wrap=wrap) return flatLocal_(Phi_(lat2, name='lat2'), Phi_(lat1, name='lat1'), radians(d), datum=datum)
def flatPolar(lat1, lon1, lat2, lon2, radius=R_M, wrap=False): '''Compute the distance between two (spherical) points using the U{polar coordinate flat-Earth <https://WikiPedia.org/wiki/Geographical_distance#Polar_coordinate_flat-Earth_formula>} formula. @arg lat1: Start latitude (C{degrees}). @arg lon1: Start longitude (C{degrees}). @arg lat2: End latitude (C{degrees}). @arg lon2: End longitude (C{degrees}). @kwarg radius: Mean earth radius (C{meter}). @kwarg wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: Distance (C{meter}, same units as B{C{radius}}). @see: Functions L{flatPolar_}, L{cosineLaw}, L{flatLocal}, L{equirectangular}, L{euclidean}, L{haversine} and L{vincentys}. ''' r = Radius(radius) if r: d, _ = unroll180(lon1, lon2, wrap=wrap) r *= flatPolar_(Phi_(lat2, name='lat2'), Phi_(lat1, name='lat1'), radians(d)) return r
def haversine(lat1, lon1, lat2, lon2, radius=R_M, wrap=False): '''Compute the distance between two (spherical) points using the U{Haversine<https://www.Movable-Type.co.UK/scripts/latlong.html>} formula. @arg lat1: Start latitude (C{degrees}). @arg lon1: Start longitude (C{degrees}). @arg lat2: End latitude (C{degrees}). @arg lon2: End longitude (C{degrees}). @kwarg radius: Mean earth radius (C{meter}). @kwarg wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: Distance (C{meter}, same units as B{C{radius}}). @see: U{Distance between two (spherical) points <https://www.EdWilliams.org/avform.htm#Dist>}, functions L{cosineLaw}, L{equirectangular}, L{euclidean}, L{flatLocal}, L{flatPolar}, L{vincentys} and methods L{Ellipsoid.distance2}, C{LatLon.distanceTo*} and C{LatLon.equirectangularTo}. @note: See note at function L{vincentys_}. ''' r = Radius(radius) if r: d, _ = unroll180(lon1, lon2, wrap=wrap) r *= haversine_(Phi_(lat2, name='lat2'), Phi_(lat1, name='lat1'), radians(d)) return r
def distance(self, p1, p2): '''Return the non-negative I{angular} distance in C{degrees}. ''' # see .ellipsoidalKarney.LatLon._inverse _, lon2 = unroll180(p1.lon, p2.lon, wrap=self._wrap) # g.LONG_UNROLL # XXX g.DISTANCE needed for 's12', distance in meters? return abs(self._Inverse(p1.lat, p1.lon, p2.lat, lon2)['a12'])
def cosineForsytheAndoyerLambert(lat1, lon1, lat2, lon2, datum=Datums.WGS84, wrap=False): '''Compute the distance between two (ellipsoidal) points using the U{Forsythe-Andoyer-Lambert correction<https://www2.UNB.CA/gge/Pubs/TR77.pdf>} of the U{Law of Cosines<https://www.Movable-Type.co.UK/scripts/latlong.html#cosine-law>} formula. @arg lat1: Start latitude (C{degrees}). @arg lon1: Start longitude (C{degrees}). @arg lat2: End latitude (C{degrees}). @arg lon2: End longitude (C{degrees}). @kwarg datum: Ellipsoidal datum to use (L{Datum}). @kwarg wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: Distance (C{meter}, same units as the B{C{datum}}'s ellipsoid axes). @raise TypeError: Invalid B{C{datum}}. @see: Functions L{cosineForsytheAndoyerLambert_}, L{cosineAndoyerLambert}, L{cosineLaw}, L{equirectangular}, L{euclidean}, L{flatLocal}/L{hubeny}, L{flatPolar}, L{haversine}, L{thomas} and L{vincentys} and method L{Ellipsoid.distance2}. ''' d, _ = unroll180(lon1, lon2, wrap=wrap) r = cosineForsytheAndoyerLambert_(Phi_(lat2, name=_lat2_), Phi_(lat1, name=_lat1_), radians(d), datum=datum) return r * datum.ellipsoid.a
def equirectangular_(lat1, lon1, lat2, lon2, adjust=True, limit=45, wrap=False): '''Compute the distance between two points using the U{Equirectangular Approximation / Projection <https://www.Movable-Type.co.UK/scripts/latlong.html#equirectangular>}. This approximation is valid for short distance of several hundred Km or Miles, see the B{C{limit}} keyword argument and the L{LimitError}. @arg lat1: Start latitude (C{degrees}). @arg lon1: Start longitude (C{degrees}). @arg lat2: End latitude (C{degrees}). @arg lon2: End longitude (C{degrees}). @kwarg adjust: Adjust the wrapped, unrolled longitudinal delta by the cosine of the mean latitude (C{bool}). @kwarg limit: Optional limit for lat- and longitudinal deltas (C{degrees}) or C{None} or C{0} for unlimited. @kwarg wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: A L{Distance4Tuple}C{(distance2, delta_lat, delta_lon, unroll_lon2)}. @raise LimitError: If the lat- and/or longitudinal delta exceeds the B{C{-limit..+limit}} range and L{limiterrors} set to C{True}. @see: U{Local, flat earth approximation <https://www.EdWilliams.org/avform.htm#flat>}, functions L{equirectangular}, L{cosineAndoyerLambert}, L{cosineForsytheAndoyerLambert}, L{cosineLaw}, L{euclidean}, L{flatLocal}/L{hubeny}, L{flatPolar}, L{haversine}, L{thomas} and L{vincentys} and methods L{Ellipsoid.distance2}, C{LatLon.distanceTo*} and C{LatLon.equirectangularTo}. ''' d_lat = lat2 - lat1 d_lon, ulon2 = unroll180(lon1, lon2, wrap=wrap) if limit and _limiterrors \ and max(abs(d_lat), abs(d_lon)) > limit > 0: t = unstr(equirectangular_.__name__, lat1, lon1, lat2, lon2, limit=limit) raise LimitError('delta exceeds limit', txt=t) if adjust: # scale delta lon d_lon *= _scale_deg(lat1, lat2) d2 = hypot2(d_lat, d_lon) # degrees squared! return Distance4Tuple(d2, d_lat, d_lon, ulon2 - lon2)
def Inverse1(self, lat1, lon1, lat2, lon2, wrap=False): '''Return the non-negative I{angular} distance in C{degrees}. ''' # see .FrechetKarney.distance, .HausdorffKarney._distance # and .HeightIDWkarney._distances _, lon2 = unroll180(lon1, lon2, wrap=wrap) # self.LONG_UNROLL d = self.Inverse(lat1, lon1, lat2, lon2) # XXX self.DISTANCE needed for 'a12'? return abs(d.a12)
def equirectangular_(lat1, lon1, lat2, lon2, adjust=True, limit=45, wrap=False): '''Compute the distance between two points using the U{Equirectangular Approximation / Projection <https://www.Movable-Type.co.UK/scripts/latlong.html>}. This approximation is valid for short distance of several hundred Km or Miles, see the B{C{limit}} keyword argument and the L{LimitError}. @param lat1: Start latitude (C{degrees}). @param lon1: Start longitude (C{degrees}). @param lat2: End latitude (C{degrees}). @param lon2: End longitude (C{degrees}). @keyword adjust: Adjust the wrapped, unrolled longitudinal delta by the cosine of the mean latitude (C{bool}). @keyword limit: Optional limit for lat- and longitudinal deltas (C{degrees}) or C{None} or C{0} for unlimited. @keyword wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: A L{Distance4Tuple}C{(distance2, delta_lat, delta_lon, unroll_lon2)}. @raise LimitError: If the lat- and/or longitudinal delta exceeds the B{C{-limit..+limit}} range and L{limiterrors} set to C{True}. @see: U{Local, flat earth approximation <https://www.EdWilliams.org/avform.htm#flat>}, functions L{equirectangular}, L{euclidean}, L{haversine} and L{vincentys} and methods L{Ellipsoid.distance2}, C{LatLon.distanceTo*} and C{LatLon.equirectangularTo}. ''' d_lat = lat2 - lat1 d_lon, ulon2 = unroll180(lon1, lon2, wrap=wrap) if limit and _limiterrors \ and max(abs(d_lat), abs(d_lon)) > limit > 0: t = fStr((lat1, lon1, lat2, lon2), prec=4) raise LimitError('%s(%s, limit=%s) delta exceeds limit' % ('equirectangular_', t, fStr(limit, prec=2))) if adjust: # scale delta lon d_lon *= _scaled(lat1, lat2) d2 = d_lat**2 + d_lon**2 # degrees squared! return Distance4Tuple(d2, d_lat, d_lon, ulon2 - lon2)
def _inverse(self, other, wrap): '''(INTERNAL) Karney's C{Inverse} method. @return: A L{Distance3Tuple}C{(distance, initial, final)}. @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. ''' g = self.ellipsoids(other).geodesic _, lon = unroll180(self.lon, other.lon, wrap=wrap) return g.Inverse3(self.lat, self.lon, other.lat, lon)
def _inverse(self, other, azis, wrap): '''(INTERNAL) Inverse Karney 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. ''' g = self.ellipsoids(other).geodesic m = g.DISTANCE if azis: m |= g.AZIMUTH _, lon = unroll180(self.lon, other.lon, wrap=wrap) r = g.Inverse(self.lat, self.lon, other.lat, lon, m) t = r['s12'] if azis: # forward and reverse azimuth t = t, wrap360(r['azi1']), wrap360(r['azi2']) return t
def euclidean(lat1, lon1, lat2, lon2, radius=R_M, adjust=True, wrap=False): '''Approximate the C{Euclidian} distance between two (spherical) points. @param lat1: Start latitude (C{degrees}). @param lon1: Start longitude (C{degrees}). @param lat2: End latitude (C{degrees}). @param lon2: End longitude (C{degrees}). @keyword radius: Optional, mean earth radius (C{meter}). @keyword adjust: Adjust the longitudinal delta by the cosine of the mean latitude (C{bool}). @keyword wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: Distance (C{meter}, same units as B{C{radius}}). @see: U{Distance between two (spherical) points <https://www.EdWilliams.org/avform.htm#Dist>}, functions L{equirectangular}, L{haversine} and L{vincentys} and methods L{Ellipsoid.distance2}, C{LatLon.distanceTo*} and C{LatLon.equirectangularTo}. ''' d, _ = unroll180(lon1, lon2, wrap=wrap) r = euclidean_(radians(lat2), radians(lat1), radians(d), adjust=adjust) return r * float(radius)
def vincentys(lat1, lon1, lat2, lon2, radius=R_M, wrap=False): '''Compute the distance between two (spherical) points using U{Vincenty's<https://WikiPedia.org/wiki/Great-circle_distance>} spherical formula. @param lat1: Start latitude (C{degrees}). @param lon1: Start longitude (C{degrees}). @param lat2: End latitude (C{degrees}). @param lon2: End longitude (C{degrees}). @keyword radius: Optional, mean earth radius (C{meter}). @keyword wrap: Wrap and L{unroll180} longitudes (C{bool}). @return: Distance (C{meter}, same units as B{C{radius}}). @see: Functions L{equirectangular}, L{euclidean} and L{haversine} and methods L{Ellipsoid.distance2}, C{LatLon.distanceTo*} and C{LatLon.equirectangularTo}. @note: See note under L{vincentys_}. ''' d, _ = unroll180(lon1, lon2, wrap=wrap) r = vincentys_(radians(lat2), radians(lat1), radians(d)) return r * float(radius)
def _unrollon(p1, p2): # unroll180 == .karney._unroll2 # wrap, unroll and replace longitude if different _, lon = unroll180(p1.lon, p2.lon, wrap=True) if abs(lon - p2.lon) > EPS: p2 = p2.classof(p2.lat, lon, p2.height, datum=p2.datum) return p2
def _intersects2( c1, r1, c2, r2, height=None, wrap=True, # MCCABE 16 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.formy import _euclidean, _radical2 from pygeodesy.sphericalTrigonometry import _intersects2 as _si2, LatLon as _LLS from pygeodesy.utily import m2degrees, unroll180 from pygeodesy.vector3d import _intersects2 as _vi2 def _latlon4(t, h, n): if LatLon is None: r = LatLon4Tuple(t.lat, t.lon, h, t.datum) else: kwds = _xkwds(LatLon_kwds, datum=t.datum, height=h) r = LatLon(t.lat, t.lon, **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 > (E.b * PI): raise ValueError(_exceed_PI_radians_) if wrap: # unroll180 == .karney._unroll2 _, lon2 = unroll180(c1.lon, c2.lon, wrap=True) if lon2 != c2.lon: c2 = c2.classof(c2.lat, lon2, c2.height, datum=c2.datum) # 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_distant_fmt_ % (m, )) f = _radical2(m, r1, r2).ratio # "radical lat" r = E.rocMean(favg(c1.lat, c2.lat, f=f)) e = max(m2degrees(tol, radius=r), EPS) # get the azimuthal equidistant projection if equidistant is None: from pygeodesy.azimuthal import equidistant as A else: A = equidistant # preferably EquidistantKarney A = A(0, 0, datum=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 above ts, ta = [], None for t in ((t1, ) if t1 is t2 else (t1, t2)): p = None # force d == p False for i in range(_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) t2 = A.reverse(v2.x, v2.y) # consider only the closer intersection d1 = _euclidean(t1.lat - t.lat, t1.lon - t.lon) d2 = _euclidean(t2.lat - t.lat, t2.lon - t.lon) # break if below tolerance or if unchanged t, d = (t1, d1) if d1 < d2 else (t2, d2) if d < e or d == p: t._iteration = i + 1 # _NamedTuple._iteration ts.append(t) if v1 is v2: # abutting ta = t break p = d else: raise ValueError(_no_convergence_fmt_ % (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
def _inverse(self, other, azis, wrap): '''(INTERNAL) Inverse 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 and/or if this and the B{C{other}} point are coincident or near-antipodal. ''' 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 self._iteration in range(1, self._iterations + 1): ll_ = ll sll, cll = sincos2(ll) ss = hypot(c2 * sll, c1s2 - s1c2 * cll) if ss < EPS: # coincident or antipodal, ... if self.isantipodeTo(other, eps=self._epsilon): t = '%r %sto %r' % (self, _antipodal_, other) raise VincentyError(_ambiguous_, txt=t) # return zeros like Karney, but unlike Veness return Distance3Tuple(0.0, 0, 0) 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 # # omitted and applied only after failure to converge below, see footnote # # under Inverse at <https://WikiPedia.org/wiki/Vincenty's_formulae> # # <https://GitHub.com/ChrisVeness/geodesy/blob/master/latlon-vincenty.js> # elif abs(ll) > PI and self.isantipodeTo(other, eps=self._epsilon): # raise VincentyError('%s, %r %sto %r' % ('ambiguous', self, # _antipodal_, other)) else: t = _antipodal_ if self.isantipodeTo(other, eps=self._epsilon) else NN t = _SPACE_(repr(self), NN(t, _to_), repr(other)) raise VincentyError(_no_(_convergence_), txt=t) 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 sll, cll = sincos2(ll) f = atan2b(c2 * sll, c1s2 - s1c2 * cll) r = atan2b(c1 * sll, -s1c2 + c1s2 * cll) else: f = r = 0 return Distance3Tuple(d, f, r)
def intersections2(lat1, lon1, radius1, lat2, lon2, radius2, datum=None, wrap=True): '''Conveniently compute the intersections of two circles each defined by (geodetic/-centric) center point and a radius, using either ... 1) L{vector3d.intersections2} for small distances or if no B{C{datum}} is specified, or ... 2) L{sphericalTrigonometry.intersections2} for a spherical B{C{datum}} or if B{C{datum}} is a C{scalar} representing the earth radius, or ... 3) L{ellipsoidalKarney.intersections2} for an ellipsoidal B{C{datum}} and if I{Karney}'s U{geographiclib<https://PyPI.org/project/geographiclib/>} is installed, or ... 4) L{ellipsoidalVincenty.intersections2} if B{C{datum}} is ellipsoidal otherwise. @arg lat1: Latitude of the first circle center (C{degrees}). @arg lon1: Longitude of the first circle center (C{degrees}). @arg radius1: Radius of the first circle (C{meter}, conventionally). @arg lat2: Latitude of the second circle center (C{degrees}). @arg lon2: Longitude of the second circle center (C{degrees}). @arg radius2: Radius of the second circle (C{meter}, same units as B{C{radius1}}). @kwarg datum: Optional ellipsoidal or spherical datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2}, L{a_f2Tuple} or C{scalar} earth radius in C{meter}, same units as B{C{radius1}} and B{C{radius2}}) or C{None}. @kwarg wrap: Wrap and unroll longitudes (C{bool}). @return: 2-Tuple of the intersection points, each a L{LatLon2Tuple}C{(lat, lon)}. For abutting circles, the intersection points are the same instance. @raise IntersectionError: Concentric, antipodal, invalid or non-intersecting circles or no convergence. @raise TypeError: Invalid B{C{datum}}. @raise UnitError: Invalid B{C{lat1}}, B{C{lon1}}, B{C{radius1}} B{C{lat2}}, B{C{lon2}} or B{C{radius2}}. ''' if datum is None or euclidean(lat1, lon1, lat1, lon2, radius=R_M, adjust=True, wrap=wrap) < _D_I2_: import pygeodesy.vector3d as m def _V2T(x, y, _, **unused): # _ == z unused return _xnamed(LatLon2Tuple(y, x), intersections2.__name__) r1 = m2degrees(Radius_(radius1=radius1), radius=R_M, lat=lat1) r2 = m2degrees(Radius_(radius2=radius2), radius=R_M, lat=lat2) _, lon2 = unroll180(lon1, lon2, wrap=wrap) t = m.intersections2(m.Vector3d(lon1, lat1, 0), r1, m.Vector3d(lon2, lat2, 0), r2, sphere=False, Vector=_V2T) else: def _LL2T(lat, lon, **unused): return _xnamed(LatLon2Tuple(lat, lon), intersections2.__name__) d = _spherical_datum(datum, name=intersections2.__name__) if d.isSpherical: import pygeodesy.sphericalTrigonometry as m elif d.isEllipsoidal: try: if d.ellipsoid.geodesic: pass import pygeodesy.ellipsoidalKarney as m except ImportError: import pygeodesy.ellipsoidalVincenty as m else: raise _AssertionError(datum=d) t = m.intersections2(m.LatLon(lat1, lon1, datum=d), radius1, m.LatLon(lat2, lon2, datum=d), radius2, wrap=wrap, LatLon=_LL2T, height=0) return t