Exemple #1
0
def clipCS3(points,
            lowerleft,
            upperright,
            closed=False,
            inull=False):  # MCCABE 25
    '''Clip a path against a rectangular clip box using the
       U{Cohen-Sutherland
       <http://WikiPedia.org/wiki/Cohen-Sutherland_algorithm>} algorithm.

       @param points: The points (C{LatLon}[]).
       @param lowerleft: Bottom-left corner of the clip box (C{LatLon}).
       @param upperright: Top-right corner of the clip box (C{LatLon}).
       @keyword closed: Optionally, close the path (C{bool}).
       @keyword inull: Optionally, include null edges if inside (C{bool}).

       @return: Yield a 3-tuple (start, end, index) for each edge
                of the clipped path with the start and end points
                C{LatLon} of the portion of the edge inside or on the
                clip box and the index C{int} of the edge in the
                original path.

       @raise ValueError: The I{lowerleft} corner is not below and/or
                          not to the left of the I{upperright} corner.
    '''

    cs = _CS(lowerleft, upperright)
    n, points = points2(points, closed=closed)

    i, m = _imdex2(closed, n)
    cmbp = cs.code4(points[i])
    for i in range(m, n):
        c1, m1, b1, p1 = cmbp
        c2, m2, b2, p2 = cmbp = cs.code4(points[i])
        if c1 & c2:  # edge outside
            continue

        if not cs.edge(p1, p2):
            if inull:  # null edge
                if not c1:
                    yield p1, p1, i
                elif not c2:
                    yield p2, p2, i
            continue

        for _ in range(5):
            if c1:  # clip p1
                c1, m1, b1, p1 = m1(b1, p1)
            elif c2:  # clip p2
                c2, m2, b2, p2 = m2(b2, p2)
            else:  # inside
                if inull or _neq(p1, p2):
                    yield p1, p2, i
                break
            if c1 & c2:  # edge outside
                break
        else:  # should never get here
            raise AssertionError('clipCS3.for_')
Exemple #2
0
 def __init__(self, corners):
     n = ''
     try:
         n, cs = len2(corners)
         if n == 2:  # make a box
             b, l, t, r = boundsOf(cs, wrap=False)
             cs = LL_(b, l), LL_(t, l), LL_(t, r), LL_(b, r)
         n, cs = points2(cs, closed=True)
         self._corners = cs = cs[:n]
         self._nc = n
         self._cw = 1 if isclockwise(cs, adjust=False, wrap=False) else -1
         if self._cw != isconvex_(cs, adjust=False, wrap=False):
             raise ValueError
     except ValueError:
         raise ValueError('invalid %s[%s]: %r' % ('corners', n, corners))
     self._clipped = self._points = []
Exemple #3
0
    def points2(self, points, closed=True):
        '''Check a polygon represented by points.

           @param points: The polygon points (C{LatLon}[])
           @keyword closed: Optionally, consider the polygon closed,
                            ignoring any duplicate or closing final
                            I{points} (C{bool}).

           @return: 2-Tuple (number, ...) of points (C{int}, C{list} or
                    C{tuple}).

           @raise TypeError: Some I{points} are not C{LatLon}.

           @raise ValueError: Insufficient number of I{points}.
        '''
        return points2(points, closed=closed, base=self)
def _geodesic(datum, points, closed, line, wrap):
    # Compute the area or perimeter of a polygon,
    # using the GeographicLib package, iff installed
    g = datum.ellipsoid.geodesic

    if not wrap:  # capability LONG_UNROLL can't be off
        raise ValueError('%s invalid: %s' % ('wrap', wrap))

    _, points = points2(points, closed=closed)  # base=LatLonEllipsoidalBase(0, 0)

    g = g.Polygon(line)

    # note, lon deltas are unrolled, by default
    for p in points:
        g.AddPoint(p.lat, p.lon)
    if line and closed:
        p = points[0]
        g.AddPoint(p.lat, p.lon)

    # g.Compute returns (number_of_points, perimeter, signed area)
    return g.Compute(False, True)[1 if line else 2]
Exemple #5
0
def ispolar(points, wrap=False):
    '''Check whether a polygon encloses a pole.

       @param points: The polygon points (C{LatLon}[]).
       @keyword wrap: Wrap and unroll longitudes (C{bool}).

       @return: C{True} if a pole is enclosed by the polygon,
                C{False} otherwise.

       @raise ValueError: Insufficient number of I{points}.

       @raise TypeError: Some I{points} are not C{LatLon} or don't
                         have C{bearingTo2}, C{initialBearingTo}
                         and C{finalBearingTo} methods.
    '''
    n, points = points2(points, closed=True)

    def _cds(n, points):  # iterate over course deltas
        p1 = points[n - 1]
        try:  # LatLon must have initial- and finalBearingTo
            b1, _ = p1.bearingTo2(points[0], wrap=wrap)
        except AttributeError:
            raise TypeError('invalid %s: %r' % ('points', p1))

        for i in range(n):
            p2 = points[i]
            if not p2.isequalTo(p1, EPS):
                b, b2 = p1.bearingTo2(p2, wrap=wrap)
                yield wrap180(b - b1)  # (b - b1 + 540) % 360 - 180
                yield wrap180(b2 - b)  # (b2 - b + 540) % 360 - 180
                p1, b1 = p2, b2

    # sum of course deltas around pole is 0° rather than normally ±360°
    # <http://blog.Element84.com/determining-if-a-spherical-polygon-contains-a-pole.html>
    s = fsum(_cds(n, points))

    # XXX fix (intermittant) edge crossing pole - eg (85,90), (85,0), (85,-90)
    return abs(s) < 90  # "zero-ish"
Exemple #6
0
    def __init__(self, latlons, closed=False, radius=None, wrap=True):
        '''Handle C{LatLon} points as pseudo-xy coordinates.

           @note: The C{LatLon} latitude is considered the pseudo-y
                  and longitude the pseudo-x coordinate.  Similarly,
                  2-tuples (x, y) are (longitude, latitude).

           @param latlons: Points C{list}, C{sequence}, C{set}, C{tuple},
                           etc. (I{LatLon[]}).
           @keyword closed: Optionally, close the polygon (C{bool}).
           @keyword radius: Optional, mean earth radius (C{meter}).
           @keyword wrap: Wrap lat- and longitudes (C{bool}).

           @raise TypeError: Some I{latlons} are not C{LatLon}.

           @raise ValueError: Insufficient number of I{latlons}.
        '''
        self._closed = closed
        self._len, self._array = points2(latlons, closed=closed)
        if radius:
            self._radius = radius
            self._deg2m = degrees2m(1.0, radius)
        self._wrap = wrap
Exemple #7
0
def nearestOn4(point, points, closed=False, wrap=False, **options):
    '''Locate the point on a path or polygon closest to an other point.

       If the given point is within the extent of a polygon edge,
       the closest point is on that edge, otherwise the closest
       point is the nearest of that edge's end points.

       Distances are approximated by function L{equirectangular_},
       subject to the supplied I{options}.

       @param point: The other, reference point (C{LatLon}).
       @param points: The path or polygon points (C{LatLon}[]).
       @keyword closed: Optionally, close the path or polygon (C{bool}).
       @keyword wrap: Wrap and L{unroll180} longitudes and longitudinal
                      delta (C{bool}) in function L{equirectangular_}.
       @keyword options: Other keyword arguments for function
                         L{equirectangular_}.

       @return: 4-Tuple (lat, lon, I{distance}, I{angle}) all in
                C{degrees}.  The I{distance} is the L{equirectangular_}
                distance between the closest and the reference I{point}
                in C{degrees}.  The I{angle} from the reference I{point}
                to the closest point is in compass C{degrees360}, like
                function L{compassAngle}.

       @raise LimitError: Lat- and/or longitudinal delta exceeds
                          I{limit}, see function L{equirectangular_}.

       @raise TypeError: Some I{points} are not C{LatLon}.

       @raise ValueError: Insufficient number of I{points}.

       @see: Function L{nearestOn3}.  Use function L{degrees2m} to
             convert C{degrees} to C{meter}.
    '''
    n, points = points2(points, closed=closed)

    def _d2yx(p2, p1, u, i):
        w = wrap if (not closed or i < (n - 1)) else False
        # equirectangular_ returns a 4-tuple (distance in
        # degrees squared, delta lat, delta lon, p2.lon
        # unroll/wrap); the previous p2.lon unroll/wrap
        # is also applied to the next edge's p1.lon
        return equirectangular_(p1.lat,
                                p1.lon + u,
                                p2.lat,
                                p2.lon,
                                wrap=w,
                                **options)

    # point (x, y) on axis rotated ccw by angle a:
    #   x' = y * sin(a) + x * cos(a)
    #   y' = y * cos(a) - x * sin(a)
    #
    # distance (w) along and perpendicular (h) to
    # a line thru point (dx, dy) and the origin:
    #   w = (y * dy + x * dx) / hypot(dx, dy)
    #   h = (y * dx - x * dy) / hypot(dx, dy)
    #
    # closest point on that line thru (dx, dy):
    #   xc = dx * w / hypot(dx, dy)
    #   yc = dy * w / hypot(dx, dy)
    # or
    #   xc = dx * f
    #   yc = dy * f
    # with
    #   f = w / hypot(dx, dy)
    # or
    #   f = (y * dy + x * dx) / (dx**2 + dy**2)

    i, m = _imdex2(closed, n)
    p2 = c = points[i]
    u2 = u = 0
    d, dy, dx, _ = _d2yx(p2, point, u2, i)
    for i in range(m, n):
        p1, u1, p2 = p2, u2, points[i]
        # iff wrapped, unroll lon1 (actually previous
        # lon2) like function unroll180/-PI would've
        d21, y21, x21, u2 = _d2yx(p2, p1, u1, i)
        if d21 > EPS:
            # distance point to p1, y01 and x01 inverted
            d2, y01, x01, _ = _d2yx(point, p1, u1, n)
            if d2 > EPS:
                w2 = y01 * y21 + x01 * x21
                if w2 > 0:
                    if w2 < d21:
                        # closest is between p1 and p2, use
                        # original delta's, not y21 and x21
                        f = w2 / d21
                        p1 = LatLon_(favg(p1.lat, p2.lat, f=f),
                                     favg(p1.lon, p2.lon + u2, f=f))
                        u1 = 0
                    else:  # p2 is closest
                        p1, u1 = p2, u2
                    d2, y01, x01, _ = _d2yx(point, p1, u1, n)
            if d2 < d:  # p1 is closer, y01 and x01 inverted
                c, u, d, dy, dx = p1, u1, d2, -y01, -x01
    return c.lat, c.lon + u, hypot(dx, dy), degrees360(atan2(dx, dy))
Exemple #8
0
 def points(self, points, closed=False, base=None):
     return points2(points, closed=closed, base=base)
Exemple #9
0
def polygon(points, closed=True, base=None):
    '''DEPRECATED, use function L{points2}.
    '''
    from utily import points2
    return points2(points, closed=closed, base=base)