예제 #1
0
def _null_space2(numpy, A, eps):
    # (INTERNAL) Return the nullspace and rank of matrix A
    # @see: <https://SciPy-Cookbook.ReadTheDocs.io/items/RankNullspace.html>,
    # <https://NumPy.org/doc/stable/reference/generated/numpy.linalg.svd.html>,
    # <https://StackOverflow.com/questions/19820921>,
    # <https://StackOverflow.com/questions/2992947> and
    # <https://StackOverflow.com/questions/5889142>
    A = numpy.array(A)
    m = max(numpy.shape(A))
    if m != 4:  # for this usage
        raise _AssertionError(shape=m, txt=modulename(_null_space2, True))
    # if needed, square A, pad with zeros
    A = numpy.resize(A, m * m).reshape(m, m)
#   try:  # no numpy.linalg.null_space <https://docs.SciPy.org/doc/>
#       return scipy.linalg.null_space(A)  # XXX no scipy.linalg?
#   except AttributeError:
#       pass
    _, s, v = numpy.linalg.svd(A)
    t = max(eps, eps * s[0])  # tol, s[0] is largest singular
    r = numpy.sum(s > t)  # rank
    if r == 3:  # get null_space
        n = numpy.transpose(v[r:])
        s = numpy.shape(n)
        if s != (m, 1):  # bad null_space shape
            raise _AssertionError(shape=s, txt=modulename(_null_space2, True))
        e = float(numpy.max(numpy.abs(numpy.dot(A, n))))
        if e > t:  # residual not near-zero
            raise _AssertionError(eps=e, txt=modulename(_null_space2, True))
    else:  # coincident, colinear, concentric centers, ambiguous, etc.
        n = None
    # del A, s, vh  # release numpy
    return n, r
예제 #2
0
 def _update(self, updated, *attrs):
     '''(INTERNAL) Zap cached instance attributes.
     '''
     if updated and attrs:
         for a in attrs:  # zap attrs to None
             if getattr(self, a, None) is not None:
                 setattr(self, a, None)
             elif not hasattr(self, a):
                 raise _AssertionError('.%s invalid: %r' % (a, self))
예제 #3
0
파일: named.py 프로젝트: rbpdqdat/PyGeodesy
def _xother3(inst, other, name=_other_, up=1, **name_other):
    '''(INTERNAL) Get C{name} and C{up} for a named C{other}.
    '''
    if name_other:  # and not other and len(name_other) == 1
        name, other = _xkwds_popitem(name_other)
    elif other and len(other) == 1:
        other = other[0]
    else:
        raise _AssertionError(name, other, txt=classname(inst, prefixed=True))
    return other, name, up
예제 #4
0
def notOverloaded(inst, name, *args, **kwds):  # PYCHOK no cover
    '''Raise an C{AssertionError} for a method or property not overloaded.

       @arg inst: Instance (C{any}).
       @arg name: Method, property or name (C{str} or C{callable}).
       @arg args: Method or property positional arguments (any C{type}s).
       @arg kwds: Method or property keyword arguments (any C{type}s).
    '''
    t = _notError(inst, name, args, kwds)
    raise _AssertionError(t, txt=notOverloaded.__name__)
예제 #5
0
파일: named.py 프로젝트: rbpdqdat/PyGeodesy
 def _update(self, updated, *attrs):
     '''(INTERNAL) Zap cached instance attributes.
     '''
     if updated and attrs:
         for a in attrs:  # zap attrs to None
             if getattr(self, a, None) is not None:
                 setattr(self, a, None)
             elif not hasattr(self, a):
                 a = NN(_DOT_, a, _SPACE_, _invalid_)
                 raise _AssertionError(a, txt=repr(self))
예제 #6
0
    def unregister(self):
        '''Remove this instance from its C{_NamedEnum} registry.

           @raise _AssertionError: Mismatch of this and registered item.

           @raise NameError: This item is unregistered.
        '''
        enum = self._enum
        if enum and self.name and self.name in enum:
            item = enum.unregister(self.name)
            if item is not self:
                raise _AssertionError('%r vs %r' % (item, self))
예제 #7
0
def clipCS3(points, lowerleft, upperright, closed=False, inull=False):
    '''Clip a path against a rectangular clip box using the
       U{Cohen-Sutherland
       <https://WikiPedia.org/wiki/Cohen-Sutherland_algorithm>} algorithm.

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

       @return: Yield a L{ClipCS3Tuple}C{(start, end, index)} for each
                edge of the clipped path.

       @raise ClipError: The B{C{lowerleft}} and B{C{upperright}} corners
                         specify an invalid clip box.

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

    cs = _CS(lowerleft, upperright, name=clipCS3.__name__)
    n, pts = _points2(points, closed, inull)

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

        if not cs.edge(p1, p2):
            if inull:  # null edge
                if not c1:
                    yield ClipCS3Tuple(p1, p1, i)
                elif not c2:
                    yield ClipCS3Tuple(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 ClipCS3Tuple(p1, p2, i)
                break
            if c1 & c2:  # edge outside
                break
        else:  # PYCHOK no cover
            raise _AssertionError(_dot_(cs.name, 'for_else'))
예제 #8
0
파일: named.py 프로젝트: rbpdqdat/PyGeodesy
    def unregister(self):
        '''Remove this instance from its C{_NamedEnum} registry.

           @raise AssertionError: Mismatch of this and registered item.

           @raise NameError: This item is unregistered.
        '''
        enum = self._enum
        if enum and self.name and self.name in enum:
            item = enum.unregister(self.name)
            if item is not self:
                t = _SPACE_(repr(item), _vs_, repr(self))
                raise _AssertionError(t)
예제 #9
0
 def intersect(self, p1, p2, edge):  # compute intersection
     # of polygon edge p1 to p2 and the current clip edge,
     # where p1 and p2 are known to NOT be located on the
     # same side or on top of the current clip edge
     # <https://StackOverflow.com/questions/563198/
     #        how-do-you-detect-where-two-line-segments-intersect>
     fy = float(p2.lat - p1.lat)
     fx = float(p2.lon - p1.lon)
     fp = fy * self._dx - fx * self._dy
     if abs(fp) < EPS:  # PYCHOK no cover
         raise _AssertionError(_dot_(self.name, self.intersect.__name__))
     r = fsum_(self._xy, -p1.lat * self._dx, p1.lon * self._dy) / fp
     y = p1.lat + r * fy
     x = p1.lon + r * fx
     return _LLi_(y, x, p1.classof, edge)
예제 #10
0
def _allis2(llis, m=1, Error=HeightError):  # imported by .geoids
    # dtermine return type and convert lli C{LatLon}s to list
    if not isinstance(llis, tuple):  # llis are *args
        raise _AssertionError('type(%s): %r' % ('*llis', llis))

    n = len(llis)
    if n == 1:  # convert single lli to 1-item list
        llis = llis[0]
        try:
            n, llis = len2(llis)
            _as = _alist  # return list of interpolated heights
        except TypeError:  # single lli
            n, llis = 1, [llis]
            _as = _ascalar  # return single interpolated heights
    else:  # of 0, 2 or more llis
        _as = _atuple  # return tuple of interpolated heights

    if n < m:
        raise _insufficientError(m, Error=Error, llis=n)
    return _as, llis
예제 #11
0
def _ascalar(ais):  # imported by .geoids
    # return single float, not numpy.float64
    ais = list(ais)  # np.array, etc. to list
    if len(ais) != 1:
        raise _AssertionError('%s(%r): %s != 1' % (_len_, ais, len(ais)))
    return float(ais[0])  # remove np.<type>
예제 #12
0
                            points2 as _points2, _scale_rad, thomas_, vincentys_
from pygeodesy.interns import _datum_, _degrees_, _distanceTo_, _dot_, _item_sq, \
                              _meter_, NN, _points_, _radians_, _radians2_, _units_
from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _FOR_DOCS
from pygeodesy.named import LatLon2Tuple, _Named, _NamedTuple, \
                            notOverloaded, PhiLam2Tuple
from pygeodesy.utily import unrollPI

from collections import defaultdict
from math import radians

__all__ = _ALL_LAZY.frechet
__version__ = '20.07.08'

if not 0 < EPS < EPS1 < 1:
    raise _AssertionError('%s < %s: 0 < %.6g < %.6g < 1' %
                          ('EPS', 'EPS1', EPS, EPS1))


def _fraction(fraction, n):
    f = 1  # int, no fractional indices
    if fraction in (None, 1):
        pass
    elif not (isscalar(fraction) and EPS < fraction < EPS1 and
              (float(n) - fraction) < n):
        raise FrechetError(fraction=fraction)
    elif fraction < EPS1:
        f = float(fraction)
    return f


class FrechetError(PointsError):
예제 #13
0
 def nop4(self, b, p):  # PYCHOK no cover
     if p:  # should never get here
         raise _AssertionError(_dot_(self.name, self.nop4.__name__))
     return _CS._IN, self.nop4, b, p
예제 #14
0
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
예제 #15
0
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
예제 #16
0
                                     LatLonSphericalBase, _rads3
from pygeodesy.units import Bearing_, Height, Radius, Radius_, Scalar
from pygeodesy.utily import acos1, asin1, degrees90, degrees180, degrees2m, \
                            iterNumpy2, radiansPI2, sincos2, tan_2, \
                            unrollPI, wrapPI
from pygeodesy.vector3d import sumOf, Vector3d

from math import asin, atan2, copysign, cos, degrees, hypot, radians, sin

__all__ = _ALL_LAZY.sphericalTrigonometry
__version__ = '20.08.14'

_EPS_I2    = 4.0 * EPS
_PI_EPS_I2 = PI - _EPS_I2
if _PI_EPS_I2 >= PI:
    raise _AssertionError(_PI_EPS_I2=_PI_EPS_I2)


def _destination2(a, b, r, t):
    '''(INTERNAL) Destination phi- and longitude in C{radians}.

       @arg a: Latitude (C{radians}).
       @arg b: Longitude (C{radians}).
       @arg r: Angular distance (C{radians}).
       @arg t: Bearing (compass C{radians}).

       @return: 2-Tuple (phi, lam) of (C{radians}, C{radiansPI}).
    '''
    # see <https://www.EdWilliams.org/avform.htm#LL>
    sa, ca, sr, cr, st, ct = sincos2(a, r, t)