示例#1
0
    def __init__(self, points, tolerance, radius, shortest, indices,
                 **options):
        '''New C{Simplify} state.
        '''
        n, self.pts = len2(points)
        if n > 0:
            self.n = n
            self.r = {0: True, n - 1: True}  # dict to avoid duplicates

        if isNumpy2(points) or isTuple2(points):  # NOT self.pts
            self.subset = points.subset

        if indices:
            self.indices = True

        if radius:
            self.radius = float(radius)
        if self.radius < self.eps:
            raise _ValueError(radius=radius, txt=_too_(_small_))

        if options:
            self.options = options

        # tolerance converted to degrees squared
        self.s2 = degrees(tolerance / self.radius)**2
        if min(self.s2, tolerance) < self.eps:
            raise _ValueError(tolerance=tolerance, txt=_too_(_small_))
        self.s2e = self.s2 + 1  # sentinel

        # compute either the shortest or perpendicular distance
        self.d2i = self.d2iS if shortest else self.d2iP  # PYCHOK false
示例#2
0
def radical2(distance, radius1, radius2):
    '''Compute the I{radical ratio} and I{radical line} of two
       U{intersecting circles<https://MathWorld.Wolfram.com/
       Circle-CircleIntersection.html>}.

       The I{radical line} is perpendicular to the axis thru the
       centers of the circles at C{(0, 0)} and C{(B{distance}, 0)}.

       @arg distance: Distance between the circle centers (C{scalar}).
       @arg radius1: Radius of the first circle (C{scalar}).
       @arg radius2: Radius of the second circle (C{scalar}).

       @return: A L{Radical2Tuple}C{(ratio, xline)} where C{0.0 <=
                ratio <= 1.0} and C{xline} is along the B{C{distance}}.

       @raise IntersectionError: The B{C{distance}} exceeds the sum
                                 of B{C{radius1}} and B{C{radius2}}.

       @raise UnitError: Invalid B{C{distance}}, B{C{radius1}} or
                         B{C{radius2}}.
    '''
    d  = Distance_(distance)
    r1 = Radius_(radius1=radius1)
    r2 = Radius_(radius2=radius2)
    if d > (r1 + r2):
        raise IntersectionError(distance=d, radius1=r1, radius2=r2,
                                            txt=_too_(_distant_))
    return _radical2(d, r1, r2)
示例#3
0
    def intersections2(self, rad1, other, rad2, radius=R_M):
        '''Compute the intersection points of two circles each defined
           by a center point and a radius.

           @arg rad1: Radius of the this circle (C{meter} or C{radians},
                      see B{C{radius}}).
           @arg other: Center of the other circle (C{Cartesian}).
           @arg rad2: Radius of the other 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}).

           @return: 2-Tuple of the intersection points, each C{Cartesian}.
                    The intersection points are the same C{Cartesian}
                    instance for abutting circles.

           @raise IntersectionError: Concentric, antipodal, invalid or
                                     non-intersecting circles.

           @raise TypeError: If B{C{other}} is not C{Cartesian}.

           @raise ValueError: Invalid B{C{rad1}}, B{C{rad2}} or B{C{radius}}.

           @see: U{Java code<https://GIS.StackExchange.com/questions/48937/
                 calculating-intersection-of-two-circles>} and function
                 L{trilaterate3d2}.
        '''
        x1, x2 = self, self.others(other)
        r1, r2, x = _rads3(rad1, rad2, radius)
        if x:
            x1, x2 = x2, x1

        n, q = x1.cross(x2), x1.dot(x2)
        n2, q21 = n.length2, _1_0 - q**2
        if min(abs(q21), n2) < EPS:
            raise IntersectionError(center=self, other=other,
                                    txt=_near_concentric_)
        try:
            cr1, cr2 = cos(r1), cos(r2)
            a = (cr1 - q * cr2) / q21
            b = (cr2 - q * cr1) / q21
            x0 = x1.times(a).plus(x2.times(b))
        except ValueError:
            raise IntersectionError(center=self, rad1=rad1,
                                    other=other, rad2=rad2)
        x = _1_0 - x0.length2  # XXX x0.dot(x0)
        if x < EPS:
            raise IntersectionError(center=self, rad1=rad1,
                                    other=other, rad2=rad2, txt=_too_(_distant_))
        n = n.times(sqrt(x / n2))
        if n.length > EPS:
            x1, x2 = x0.plus(n), x0.minus(n)
        else:  # abutting circles
            x1 = x2 = x0

        for x in (x1, x2):
            x.datum = self.datum
            x.name  = self.intersections2.__name__
        return x1, x2
示例#4
0
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
示例#5
0
 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)
示例#6
0
def _pts2(points, closed, inull):
    '''(INTERNAL) Get the points to clip.
    '''
    if closed and inull:
        n, pts = len2(points)
        # only remove the final, closing point
        if n > 1 and _eq(pts[n - 1], pts[0]):
            n -= 1
            pts = pts[:n]
        if n < 2:
            raise PointsError(points=n, txt=_too_(_few_))
    else:
        n, pts = points2(points, closed=closed)
    return n, list(pts)
示例#7
0
def _h_x2(xs):
    '''(INTERNAL) Helper for L{hypot_} and L{hypot2_}.
    '''
    if xs:
        n, xs = len2(xs)
        if n > 0:
            h = float(max(abs(x) for x in xs))
            if h > 0:
                if n > 1:
                    X = Fsum(_1_0)
                    X.fadd((x / h)**2 for x in xs)
                    x2 = X.fsum_(-_1_0)
                else:
                    x2 = _1_0
            else:
                h = x2 = _0_0
            return h, x2
    raise _ValueError(xs=xs, txt=_too_(_few_))
示例#8
0
def points2(points, closed=True, base=None, Error=PointsError):
    '''Check a path or polygon represented by points.

       @arg points: The path or polygon points (C{LatLon}[])
       @kwarg closed: Optionally, consider the polygon closed,
                      ignoring any duplicate or closing final
                      B{C{points}} (C{bool}).
       @kwarg base: Optionally, check all B{C{points}} against
                    this base class, if C{None} don't check.
       @kwarg Error: Exception to raise (C{ValueError}).

       @return: A L{Points2Tuple}C{(number, points)} with the number
                of points and the points C{list} or C{tuple}.

       @raise PointsError: Insufficient number of B{C{points}}.

       @raise TypeError: Some B{C{points}} are not B{C{base}}
                         compatible.
    '''
    n, points = len2(points)

    if closed:
        # remove duplicate or closing final points
        while n > 1 and points[n-1] in (points[0], points[n-2]):
            n -= 1
        # XXX following line is unneeded if points
        # are always indexed as ... i in range(n)
        points = points[:n]  # XXX numpy.array slice is a view!

    if n < (3 if closed else 1):
        raise Error(points=n, txt=_too_(_few_))

    if base and not (isNumpy2(points) or isTuple2(points)):
        for i in range(n):
            base.others(points[i], name=Fmt.SQUARE(points=i))

    return Points2Tuple(n, points)
示例#9
0
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
示例#10
0
    def clip2(self, points, closed, inull):  # MCCABE 17, clip points
        np, pts = _pts2(points, closed, inull)
        pcs = _List(inull)  # clipped points

        ne = 0  # number of non-null clip edges
        no = ni = True  # all out- and inside?
        for e in self.clipedges():
            ne += 1  # non-null clip edge

            # clip points, closed always
            d2, p2 = self.dot2(pts[np - 1])
            for i in range(np):
                d1, p1 = d2, p2
                d2, p2 = self.dot2(pts[i])
                if d1 < 0:  # p1 inside, ...
                    # pcs.append(p1)
                    if d2 < 0:  # ... p2 inside
                        p = p2
                    else:  # ... p2 outside
                        p = self.intersect(p1, p2, e)
                        if d2 > 0:
                            no = False
                    pcs.append(p)
                elif d2 < 0:  # p1 out-, p2 inside
                    p = self.intersect(p1, p2, e)
                    pcs.append(p)
                    pcs.append(p2)
                    if d1 > 0:
                        no = ni = False
                elif d1 > 0:  # both out
                    ni = False

            if pcs:  # replace points
                pts[:] = pcs
                pcs[:] = []
                np = len(pts)

        if ne < 3:
            raise ClipError(self.name, ne, self._cs, txt=_too_(_few_))

        # ni is True iff all points are on or on the
        # right side (i.e. inside) of all clip edges,
        # no is True iff all points are on or at one
        # side (left or right) of each clip edge: if
        # none are inside (ni is False) and if all
        # are on the same side (no is True), then all
        # must be outside
        if no and not ni:
            np, pts = 0, []
        elif np > 1:
            p = pts[0]
            if closed:  # close clipped pts
                if _neq(pts[np - 1], p):
                    pts.append(p)
                    np += 1
            elif not inull:  # open clipped pts
                while np > 0 and _eq(pts[np - 1], p):
                    pts.pop()
                    np -= 1

        # assert len(pts) == np
        return np, pts