Пример #1
0
    def crossingParallels(self, other, lat, wrap=False):
        '''Return the pair of meridians at which a great circle defined
           by this and an other point crosses the given latitude.

           @param other: The other point defining great circle (L{LatLon}).
           @param lat: Latitude at the crossing (C{degrees}).
           @keyword wrap: Wrap and unroll longitudes (C{bool}).

           @return: 2-Tuple C{(lon1, lon2)}, both in C{degrees180} or
                    C{None} if the great circle doesn't reach B{C{lat}}.
        '''
        self.others(other)

        a1, b1 = self.to2ab()
        a2, b2 = other.to2ab()

        a = radians(lat)
        db, b2 = unrollPI(b1, b2, wrap=wrap)

        sa,  ca,  sa1, ca1, \
        sa2, ca2, sdb, cdb = sincos2(a, a1, a2, db)

        x = sa1 * ca2 * ca * sdb
        y = sa1 * ca2 * ca * cdb - ca1 * sa2 * ca
        z = ca1 * ca2 * sa * sdb

        h = hypot(x, y)
        if h < EPS or abs(z) > h:
            return None  # great circle doesn't reach latitude

        m = atan2(-y, x) + b1  # longitude at max latitude
        d = acos1(z / h)  # delta longitude to intersections
        return degrees180(m - d), degrees180(m + d)
Пример #2
0
    def maxLat(self, bearing):
        '''Return the maximum latitude reached when travelling
           on a great circle on given bearing from this point
           based on Clairaut's formula.

           The maximum latitude is independent of longitude
           and the same for all points on a given latitude.

           Negate the result for the minimum latitude (on the
           Southern hemisphere).

           @param bearing: Initial bearing (compass C{degrees360}).

           @return: Maximum latitude (C{degrees90}).

           @JSname: I{maxLatitude}.
        '''
        a, _ = self.to2ab()
        m = acos1(abs(sin(radians(bearing)) * cos(a)))
        return degrees90(m)
Пример #3
0
    def maxLat(self, bearing):
        '''Return the maximum latitude reached when travelling
           on a great circle on given bearing from this point
           based on Clairaut's formula.

           The maximum latitude is independent of longitude
           and the same for all points on a given latitude.

           Negate the result for the minimum latitude (on the
           Southern hemisphere).

           @arg bearing: Initial bearing (compass C{degrees360}).

           @return: Maximum latitude (C{degrees90}).

           @raise ValueError: Invalid B{C{bearing}}.

           @JSname: I{maxLatitude}.
        '''
        m = acos1(abs(sin(Bearing_(bearing)) * cos(self.phi)))
        return degrees90(m)
Пример #4
0
    def alongTrackDistanceTo(self, start, end, radius=R_M, wrap=False):
        '''Compute the (signed) distance from the start to the closest
           point on the great circle path defined by a start and an
           end point.

           That is, if a perpendicular is drawn from this point to the
           great circle path, the along-track distance is the distance
           from the start point to the point where the perpendicular
           crosses the path.

           @arg start: Start point of great circle path (L{LatLon}).
           @arg end: End point of great circle path (L{LatLon}).
           @kwarg radius: Mean earth radius (C{meter}).
           @kwarg wrap: Wrap and unroll longitudes (C{bool}).

           @return: Distance along the great circle path (C{meter},
                    same units as B{C{radius}}), positive if after the
                    B{C{start}} toward the B{C{end}} point of the path or
                    negative if before the B{C{start}} point.

           @raise TypeError: The B{C{start}} or B{C{end}} point is not L{LatLon}.

           @raise ValueError: Invalid B{C{radius}}.

           @example:

           >>> p = LatLon(53.2611, -0.7972)

           >>> s = LatLon(53.3206, -1.7297)
           >>> e = LatLon(53.1887, 0.1334)
           >>> d = p.alongTrackDistanceTo(s, e)  # 62331.58
        '''
        r, x, b = self._trackDistanceTo3(start, end, radius, wrap)
        cx = cos(x)
        if abs(cx) > EPS:
            return copysign(acos1(cos(r) / cx), cos(b)) * radius
        else:
            return 0.0
Пример #5
0
def intersection(start1, end1, start2, end2,
                 height=None, wrap=False, LatLon=LatLon):
    '''Compute the intersection point of two paths both defined
       by two points or a start point and bearing from North.

       @param start1: Start point of the first path (L{LatLon}).
       @param end1: End point ofthe first path (L{LatLon}) or
                    the initial bearing at the first start point
                    (compass C{degrees360}).
       @param start2: Start point of the second path (L{LatLon}).
       @param end2: End point of the second path (L{LatLon}) or
                    the initial bearing at the second start point
                    (compass C{degrees360}).
       @keyword height: Optional height for the intersection point,
                        overriding the mean height (C{meter}).
       @keyword wrap: Wrap and unroll longitudes (C{bool}).
       @keyword LatLon: Optional (sub-)class to return the intersection
                        point (L{LatLon}) or C{None}.

       @return: The intersection point (B{C{LatLon}}) or a
                L{LatLon3Tuple}C{(lat, lon, height)} if B{C{LatLon}}
                is C{None}.  An alternate intersection point might
                be the L{antipode} to the returned result.

       @raise TypeError: A B{C{start}} or B{C{end}} point not L{LatLon}.

       @raise ValueError: Intersection is ambiguous or infinite or
                          the paths are parallel, coincident or null.

       @example:

       >>> p = LatLon(51.8853, 0.2545)
       >>> s = LatLon(49.0034, 2.5735)
       >>> i = intersection(p, 108.547, s, 32.435)  # '50.9078°N, 004.5084°E'
    '''
    _Trll.others(start1, name='start1')
    _Trll.others(start2, name='start2')

    hs = [start1.height, start2. height]

    a1, b1 = start1.to2ab()
    a2, b2 = start2.to2ab()

    db, b2 = unrollPI(b1, b2, wrap=wrap)
    r12 = haversine_(a2, a1, db)
    if abs(r12) < EPS:  # [nearly] coincident points
        a, b = map1(degrees, favg(a1, a2), favg(b1, b2))

    # see <https://www.EdWilliams.org/avform.htm#Intersection>
    elif isscalar(end1) and isscalar(end2):  # both bearings
        sa1, ca1, sa2, ca2, sr12, cr12 = sincos2(a1, a2, r12)

        x1, x2 = (sr12 * ca1), (sr12 * ca2)
        if abs(x1) < EPS or abs(x2) < EPS:
            raise ValueError('intersection %s: %r vs %r' % ('parallel',
                             (start1, end1), (start2, end2)))

        # handle domain error for equivalent longitudes,
        # see also functions asin_safe and acos_safe at
        # <https://www.EdWilliams.org/avform.htm#Math>
        t1, t2 = map1(acos1, (sa2 - sa1 * cr12) / x1,
                             (sa1 - sa2 * cr12) / x2)
        if sin(db) > 0:
            t12, t21 = t1, PI2 - t2
        else:
            t12, t21 = PI2 - t1, t2

        t13, t23 = map1(radiansPI2, end1, end2)
        x1, x2 = map1(wrapPI, t13 - t12,  # angle 2-1-3
                              t21 - t23)  # angle 1-2-3
        sx1, cx1, sx2, cx2 = sincos2(x1, x2)
        if sx1 == 0 and sx2 == 0:  # max(abs(sx1), abs(sx2)) < EPS
            raise ValueError('intersection %s: %r vs %r' % ('infinite',
                             (start1, end1), (start2, end2)))
        sx3 = sx1 * sx2
#       if sx3 < 0:
#           raise ValueError('intersection %s: %r vs %r' % ('ambiguous',
#                            (start1, end1), (start2, end2)))
        x3 = acos1(cr12 * sx3 - cx2 * cx1)
        r13 = atan2(sr12 * sx3, cx2 + cx1 * cos(x3))

        a, b = _destination2(a1, b1, r13, t13)
        # choose antipode for opposing bearings
        if _xb(a1, b1, end1, a, b, wrap) < 0 or \
           _xb(a2, b2, end2, a, b, wrap) < 0:
            a, b = antipode(a, b)

    else:  # end point(s) or bearing(s)
        x1, d1 = _x3d2(start1, end1, wrap, '1', hs)
        x2, d2 = _x3d2(start2, end2, wrap, '2', hs)
        x = x1.cross(x2)
        if x.length < EPS:  # [nearly] colinear or parallel paths
            raise ValueError('intersection %s: %r vs %r' % ('colinear',
                             (start1, end1), (start2, end2)))
        a, b = x.to2ll()
        # choose intersection similar to sphericalNvector
        d1 = _xdot(d1, a1, b1, a, b, wrap)
        d2 = _xdot(d2, a2, b2, a, b, wrap)
        if (d1 < 0 and d2 > 0) or (d1 > 0 and d2 < 0):
            a, b = antipode(a, b)

    h = fmean(hs) if height is None else height
    r = LatLon3Tuple(a, b, h) if LatLon is None else \
              LatLon(a, b, height=h)
    return _xnamed(r, intersection.__name__)
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
def intersection(start1,
                 end1,
                 start2,
                 end2,
                 height=None,
                 wrap=False,
                 LatLon=LatLon,
                 **LatLon_kwds):
    '''Compute the intersection point of two paths both defined
       by two points or a start point and bearing from North.

       @arg start1: Start point of the first path (L{LatLon}).
       @arg end1: End point ofthe first path (L{LatLon}) or
                  the initial bearing at the first start point
                  (compass C{degrees360}).
       @arg start2: Start point of the second path (L{LatLon}).
       @arg end2: End point of the second path (L{LatLon}) or
                  the initial bearing at the second start point
                  (compass C{degrees360}).
       @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
                      point (L{LatLon}) or C{None}.
       @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
                           arguments, ignored if B{C{LatLon=None}}.

       @return: The intersection point (B{C{LatLon}}) or a
                L{LatLon3Tuple}C{(lat, lon, height)} if B{C{LatLon}}
                is C{None}.  An alternate intersection point might
                be the L{antipode} to the returned result.

       @raise IntersectionError: Intersection is ambiguous or infinite
                                 or the paths are coincident, colinear
                                 or parallel.

       @raise TypeError: A B{C{start}} or B{C{end}} point not L{LatLon}.

       @raise ValueError: Invalid B{C{height}}.

       @example:

       >>> p = LatLon(51.8853, 0.2545)
       >>> s = LatLon(49.0034, 2.5735)
       >>> i = intersection(p, 108.547, s, 32.435)  # '50.9078°N, 004.5084°E'
    '''
    _Trll.others(start1, name=_start1_)
    _Trll.others(start2, name=_start2_)

    hs = [start1.height, start2.height]

    a1, b1 = start1.philam
    a2, b2 = start2.philam

    db, b2 = unrollPI(b1, b2, wrap=wrap)
    r12 = vincentys_(a2, a1, db)
    if abs(r12) < EPS:  # [nearly] coincident points
        a, b = favg(a1, a2), favg(b1, b2)

    # see <https://www.EdWilliams.org/avform.htm#Intersection>
    elif isscalar(end1) and isscalar(end2):  # both bearings
        sa1, ca1, sa2, ca2, sr12, cr12 = sincos2(a1, a2, r12)

        x1, x2 = (sr12 * ca1), (sr12 * ca2)
        if abs(x1) < EPS or abs(x2) < EPS:
            raise IntersectionError(start1=start1,
                                    end1=end1,
                                    start2=start2,
                                    end2=end2,
                                    txt='parallel')

        # handle domain error for equivalent longitudes,
        # see also functions asin_safe and acos_safe at
        # <https://www.EdWilliams.org/avform.htm#Math>
        t1, t2 = map1(acos1, (sa2 - sa1 * cr12) / x1, (sa1 - sa2 * cr12) / x2)
        if sin(db) > 0:
            t12, t21 = t1, PI2 - t2
        else:
            t12, t21 = PI2 - t1, t2

        t13, t23 = map1(radiansPI2, end1, end2)
        x1, x2 = map1(
            wrapPI,
            t13 - t12,  # angle 2-1-3
            t21 - t23)  # angle 1-2-3
        sx1, cx1, sx2, cx2 = sincos2(x1, x2)
        if sx1 == 0 and sx2 == 0:  # max(abs(sx1), abs(sx2)) < EPS
            raise IntersectionError(start1=start1,
                                    end1=end1,
                                    start2=start2,
                                    end2=end2,
                                    txt='infinite')
        sx3 = sx1 * sx2
        #       if sx3 < 0:
        #           raise IntersectionError(start1=start1, end1=end1,
        #                                   start2=start2, end2=end2, txt=_ambiguous_)
        x3 = acos1(cr12 * sx3 - cx2 * cx1)
        r13 = atan2(sr12 * sx3, cx2 + cx1 * cos(x3))

        a, b = _destination2(a1, b1, r13, t13)
        # choose antipode for opposing bearings
        if _xb(a1, b1, end1, a, b, wrap) < 0 or \
           _xb(a2, b2, end2, a, b, wrap) < 0:
            a, b = antipode_(a, b)  # PYCHOK PhiLam2Tuple

    else:  # end point(s) or bearing(s)
        x1, d1 = _x3d2(start1, end1, wrap, _1_, hs)
        x2, d2 = _x3d2(start2, end2, wrap, _2_, hs)
        x = x1.cross(x2)
        if x.length < EPS:  # [nearly] colinear or parallel paths
            raise IntersectionError(start1=start1,
                                    end1=end1,
                                    start2=start2,
                                    end2=end2,
                                    txt=_colinear_)
        a, b = x.philam
        # choose intersection similar to sphericalNvector
        d1 = _xdot(d1, a1, b1, a, b, wrap)
        if d1:
            d2 = _xdot(d2, a2, b2, a, b, wrap)
            if (d2 < 0 and d1 > 0) or (d2 > 0 and d1 < 0):
                a, b = antipode_(a, b)  # PYCHOK PhiLam2Tuple

    h = fmean(hs) if height is None else Height(height)
    return _latlon3(degrees90(a), degrees180(b), h, intersection, LatLon,
                    **LatLon_kwds)