Пример #1
0
def _xyhs3(atype, m, knots, off=True):
    # convert knot C{LatLon}s to tuples or C{NumPy} arrays and C{SciPy} sphericals
    xs, ys, hs = zip(*_xyhs(knots, off=off))  # PYCHOK unzip
    n = len(hs)
    if n < m:
        raise HeightError('insufficient %s: %s, need %s' % ('knots', n, m))
    return map1(atype, xs, ys, hs)
Пример #2
0
def _xyhs3(atype, m, knots, off=True):
    # convert knot C{LatLon}s to tuples or C{NumPy} arrays and C{SciPy} sphericals
    xs, ys, hs = zip(*_xyhs(knots, off=off, name=_knots_))  # PYCHOK unzip
    n = len(hs)
    if n < m:
        raise _insufficientError(m, knots=n)
    return map1(atype, xs, ys, hs)
Пример #3
0
    def cross(self, other, raiser=None):  # raiser=NN
        '''Compute the cross product of this and an other vector.

           @arg other: The other vector (L{Vector3d}).
           @kwarg raiser: Optional, L{CrossError} label if raised (C{str}).

           @return: Cross product (L{Vector3d}).

           @raise CrossError: Zero or near-zero cross product and both
                              B{C{raiser}} and L{crosserrors} set.

           @raise TypeError: Incompatible B{C{other}} C{type}.
        '''
        self.others(other)

        x = self.y * other.z - self.z * other.y
        y = self.z * other.x - self.x * other.z
        z = self.x * other.y - self.y * other.x

        if raiser and self.crosserrors and max(map1(abs, x, y, z)) < EPS:
            t = _coincident_ if self.isequalTo(other) else _colinear_
            r = getattr(other, '_fromll', None) or other
            raise CrossError(raiser, r, txt=t)

        return self.classof(x, y, z)
Пример #4
0
    def isequalTo(self, other, eps=None):
        '''Compare this point with an other point, I{ignoring} height.

           @arg other: The other point (C{LatLon}).
           @kwarg eps: Tolerance for equality (C{degrees}).

           @return: C{True} if both points are identical,
                    I{ignoring} height, C{False} otherwise.

           @raise TypeError: The B{C{other}} point is not C{LatLon}
                             or mismatch of the B{C{other}} and
                             this C{class} or C{type}.

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

           @see: Method L{isequalTo3}.

           @example:

           >>> p = LatLon(52.205, 0.119)
           >>> q = LatLon(52.205, 0.119)
           >>> e = p.isequalTo(q)  # True
        '''
        self.others(other)

        e = _0_0 if eps in (None, 0, _0_0) else Scalar_(eps, name='eps')
        if e > 0:
            return max(map1(abs, self.lat - other.lat,
                                 self.lon - other.lon)) < e
        else:
            return self.lat == other.lat and \
                   self.lon == other.lon
Пример #5
0
 def _exs(n, points):  # iterate over spherical edge excess
     a1, b1 = points[n - 1].philam
     ta1 = tan_2(a1)
     for i in range(n):
         a2, b2 = points[i].philam
         db, b2 = unrollPI(b1, b2, wrap=wrap)
         ta2, tdb = map1(tan_2, a2, db)
         yield atan2(tdb * (ta1 + ta2), 1 + ta1 * ta2)
         ta1, b1 = ta2, b2
Пример #6
0
def _to4lldn(latlon, lon, datum, name):
    '''(INTERNAL) Return 4-tuple (C{lat, lon, datum, name}).
    '''
    try:
        # if lon is not None:
        #     raise AttributeError
        lat, lon = map1(float, latlon.lat, latlon.lon)
        _xinstanceof(_LLEB, LatLonDatum5Tuple, latlon=latlon)
        d = datum or latlon.datum
    except AttributeError:
        lat, lon = parseDMS2(latlon, lon)
        d = datum or Datums.WGS84
    return lat, lon, d, (name or nameof(latlon))
Пример #7
0
def _xyzn4(xyz, y, z, Error=TypeError):  # imported by .ecef.py
    '''(INTERNAL) Get an C{(x, y, z, name)} 4-tuple.
    '''
    try:
        t = xyz.x, xyz.y, xyz.z
    except AttributeError:
        t = xyz, y, z
    try:
        x, y, z = map1(float, *t)
    except (TypeError, ValueError) as x:
        raise Error('%s invalid: %r, %s' % ('xyz, y or z', t, x))

    return x, y, z, getattr(xyz, 'name', '')
Пример #8
0
def _xyzn4(xyz, y, z, Error=_TypeError):  # imported by .ecef
    '''(INTERNAL) Get an C{(x, y, z, name)} 4-tuple.
    '''
    try:
        t = xyz.x, xyz.y, xyz.z
    except AttributeError:
        t = xyz, y, z
    try:
        x, y, z = map1(float, *t)
    except (TypeError, ValueError) as x:
        d = dict(zip(('xyz', _y_, _z_), t))
        raise Error(txt=str(x), **d)

    return x, y, z, getattr(xyz, _name_, NN)
Пример #9
0
def _llhn4(latlonh, lon, height, suffix):
    '''(INTERNAL) Get C{lat, lon, h, name} as C{4-tuple}.
    '''
    try:
        llh = latlonh.lat, latlonh.lon, getattr(latlonh, 'height',
                                                getattr(latlonh, 'h', height))
    except AttributeError:
        llh = latlonh, lon, height
    try:
        lat, lon, h = map1(float, *llh)
    except (TypeError, ValueError) as x:
        t = 'lat_, lon_ or height_'.replace('_', suffix)
        raise EcefError('%s invalid: %r, %s' % (t, llh, x))

    if abs(lat) > 90:
        raise EcefError('%s%s out of range: %.6g' % ('lat', suffix, lat))

    return lat, lon, h, getattr(latlonh, 'name', '')
Пример #10
0
def date2epoch(year, month, day):
    '''Return the reference frame C{epoch} for a calendar day.

       @arg year: Year of the date (C{scalar}).
       @arg month: Month in the B{C{year}} (C{scalar}, 1..12).
       @arg day: Day in the B{C{month}} (C{scalar}, 1..31).

       @return: Epoch, the fractional year (C{float}).

       @raise TRFError: Invalid B{C{year}}, B{C{month}} or B{C{day}}.

       @note: Any B{C{year}} is considered a leap year, i.e. having
              29 days in February.
    '''
    try:
        y, m, d = map1(int, year, month, day)
        if y > 0 and 1 <= m <= 12 and 1 <= d <= _mDays[m]:
            return y + float(sum(_mDays[:m]) + d) / 366.0
    except (ValueError, TypeError):
        pass
    raise TRFError('%s invalid: %s-%s-%s' % ('date', year, month, day))
Пример #11
0
def date2epoch(year, month, day):
    '''Return the reference frame C{epoch} for a calendar day.

       @arg year: Year of the date (C{scalar}).
       @arg month: Month in the B{C{year}} (C{scalar}, 1..12).
       @arg day: Day in the B{C{month}} (C{scalar}, 1..31).

       @return: Epoch, the fractional year (C{float}).

       @raise TRFError: Invalid B{C{year}}, B{C{month}} or B{C{day}}.

       @note: Any B{C{year}} is considered a leap year, i.e. having
              29 days in February.
    '''
    try:
        y, m, d = map1(int, year, month, day)
        if y > 0 and 1 <= m <= 12 and 1 <= d <= _mDays[m]:
            return Epoch(y + float(sum(_mDays[:m]) + d) / _366_0, low=0)

        t = NN  # _invalid_
    except (TRFError, TypeError, ValueError) as x:
        t = str(x)
    raise TRFError(year=year, month=month, day=day, txt=t)
Пример #12
0
def toOsgr(latlon,
           lon=None,
           datum=Datums.WGS84,
           Osgr=Osgr,
           name=NN,
           **Osgr_kwds):
    '''Convert a lat-/longitude point to an OSGR coordinate.

       @arg latlon: Latitude (C{degrees}) or an (ellipsoidal) geodetic
                    C{LatLon} point.
       @kwarg lon: Optional longitude in degrees (scalar or C{None}).
       @kwarg datum: Optional datum to convert B{C{lat, lon}} from
                     (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or
                     L{a_f2Tuple}).
       @kwarg Osgr: Optional class to return the OSGR coordinate
                    (L{Osgr}) or C{None}.
       @kwarg name: Optional B{C{Osgr}} name (C{str}).
       @kwarg Osgr_kwds: Optional, additional B{C{Osgr}} keyword
                         arguments, ignored if B{C{Osgr=None}}.

       @return: The OSGR coordinate (B{C{Osgr}}) or an
                L{EasNor2Tuple}C{(easting, northing)} if B{C{Osgr}}
                is C{None}.

       @raise OSGRError: Invalid B{C{latlon}} or B{C{lon}}.

       @raise TypeError: Non-ellipsoidal B{C{latlon}} or invalid
                         B{C{datum}} or conversion failed.

       @example:

       >>> p = LatLon(52.65798, 1.71605)
       >>> r = toOsgr(p)  # TG 51409 13177
       >>> # for conversion of (historical) OSGB36 lat-/longitude:
       >>> r = toOsgr(52.65757, 1.71791, datum=Datums.OSGB36)
    '''
    if not isinstance(latlon, _LLEB):
        # XXX fix failing _LLEB.convertDatum()
        latlon = _LLEB(*parseDMS2(latlon, lon), datum=datum)
    elif lon is not None:
        raise OSGRError(lon=lon, txt='not %s' % (None, ))
    elif not name:  # use latlon.name
        name = nameof(latlon)

    # if necessary, convert to OSGB36 first
    ll = _ll2datum(latlon, _Datums_OSGB36, _latlon_)
    try:
        a, b = ll.philam
    except AttributeError:
        a, b = map1(radians, ll.lat, ll.lon)
    sa, ca = sincos2(a)

    E = _Datums_OSGB36.ellipsoid

    s = E.e2s2(sa)  # r, v = E.roc2_(sa, _F0); r = v / r
    v = E.a * _F0 / sqrt(s)  # nu
    r = s / E.e12  # nu / rho == v / (v * E.e12 / s) == s / E.e12

    x2 = r - 1  # η2
    ta = tan(a)

    ca3, ca5 = fpowers(ca, 5, 3)  # PYCHOK false!
    ta2, ta4 = fpowers(ta, 4, 2)  # PYCHOK false!

    vsa = v * sa
    I4 = (E.b * _F0 * _M(E.Mabcd, a) + _N0, (vsa / 2) * ca,
          (vsa / 24) * ca3 * fsum_(5, -ta2, 9 * x2),
          (vsa / 720) * ca5 * fsum_(61, ta4, -58 * ta2))

    V4 = (_E0, (v * ca), (v / 6) * ca3 * (r - ta2), (v / 120) * ca5 * fdot(
        (-18, 1, 14, -58), ta2, 5 + ta4, x2, ta2 * x2))

    d, d2, d3, d4, d5, d6 = fpowers(b - _B0, 6)  # PYCHOK false!
    n = fdot(I4, 1, d2, d4, d6)
    e = fdot(V4, 1, d, d3, d5)

    if Osgr is None:
        r = _EasNor2Tuple(e, n)
    else:
        r = Osgr(e, n, datum=_Datums_OSGB36, **Osgr_kwds)
        if lon is None and isinstance(latlon, _LLEB):
            r._latlon = latlon  # XXX weakref(latlon)?
    return _xnamed(r, name)
Пример #13
0
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 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
                          or 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 = haversine_(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 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)  # 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 ValueError('intersection %s: %r vs %r' % ('colinear',
                                                            (start1, end1),
                                                            (start2, end2)))
        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)
Пример #14
0
def _trilaterate(point1,
                 distance1,
                 point2,
                 distance2,
                 point3,
                 distance3,
                 radius=R_M,
                 height=None,
                 useZ=False,
                 **LatLon_LatLon_kwds):
    # (INTERNAL) Locate a point at given distances from
    # three other points, see LatLon.triangulate above

    def _nd2(p, d, r, _i_, *qs):  # .toNvector and angular distance squared
        for q in qs:
            if p.isequalTo(q, EPS):
                raise _ValueError(points=p, txt=_coincident_)
        return p.toNvector(), (Scalar(d, name=_distance_ + _i_) / r)**2

    r = Radius_(radius)

    n1, r12 = _nd2(point1, distance1, r, _1_)
    n2, r22 = _nd2(point2, distance2, r, _2_, point1)
    n3, r32 = _nd2(point3, distance3, r, _3_, point1, point2)

    # the following uses x,y coordinate system with origin at n1, x axis n1->n2
    y = n3.minus(n1)
    x = n2.minus(n1)
    z = None

    d = x.length  # distance n1->n2
    if d > EPS_2:  # and y.length > EPS_2:
        X = x.unit()  # unit vector in x direction n1->n2
        i = X.dot(y)  # signed magnitude of x component of n1->n3
        Y = y.minus(X.times(i)).unit()  # unit vector in y direction
        j = Y.dot(y)  # signed magnitude of y component of n1->n3
        if abs(j) > EPS_2:
            # courtesy Carlos Freitas <https://GitHub.com/mrJean1/PyGeodesy/issues/33>
            x = fsum_(r12, -r22, d**2) / (2 * d)  # n1->intersection x- and ...
            y = fsum_(r12, -r32, i**2, j**2, -2 * x * i) / (
                2 * j)  # ... y-component
            # courtesy AleixDev <https://GitHub.com/mrJean1/PyGeodesy/issues/43>
            z = fsum_(max(r12, r22, r32), -(x**2),
                      -(y**2))  # XXX not just r12!
            if z > EPS:
                n = n1.plus(X.times(x)).plus(Y.times(y))
                if useZ:  # include Z component
                    Z = X.cross(Y)  # unit vector perpendicular to plane
                    n = n.plus(Z.times(sqrt(z)))
                if height is None:
                    h = fidw((point1.height, point2.height, point3.height),
                             map1(fabs, distance1, distance2, distance3))
                else:
                    h = Height(height)
                kwds = _xkwds(LatLon_LatLon_kwds, height=h)
                return n.toLatLon(**
                                  kwds)  # Nvector(n.x, n.y, n.z).toLatLon(...)

    # no intersection, d < EPS_2 or abs(j) < EPS_2 or z < EPS
    t = NN(_no_, _intersection_, _SPACE_)
    raise IntersectionError(point1=point1,
                            distance1=distance1,
                            point2=point2,
                            distance2=distance2,
                            point3=point3,
                            distance3=distance3,
                            txt=unstr(t, z=z, useZ=useZ))
Пример #15
0
def _T(*fs):
    '''(INTERNAL) Cache a tuple of single C{float}s.
    '''
    return map1(_F, *fs)
Пример #16
0
def utmupsValidate(coord, falsed=False, MGRS=False, Error=UTMUPSError):
    '''Check a UTM or UPS coordinate.

       @arg coord: The UTM or UPS coordinate (L{Utm}, L{Ups} or C{5+Tuple}).
       @kwarg falsed: C{5+Tuple} easting and northing are falsed (C{bool}).
       @kwarg MGRS: Increase easting and northing ranges (C{bool}).
       @kwarg Error: Optional error to raise, overriding the default
                     (L{UTMUPSError}).

       @return: C{None} if validation passed.

       @raise Error: Validation failed.

       @see: Function L{utmupsValidateOK}.
    '''
    def _en(en, lo, hi, ename):  # U, Error
        try:
            if lo <= float(en) <= hi:
                return
        except (TypeError, ValueError):
            pass
        t = _SPACE_.join(
            (_outside_, U, _range_, '[%.0F' % (lo, ), '%.0F]' % (hi, )))
        raise Error(ename, en, txt=t)

    if isinstance(coord, (Ups, Utm)):
        hemi = coord.hemisphere
        enMM = coord.falsed
    elif isinstance(coord, (UtmUps5Tuple, UtmUps8Tuple)):
        hemi = coord.hemipole
        enMM = falsed
    else:
        raise _IsnotError(Error=Error,
                          coord=coord,
                          *map1(modulename, Utm, Ups, UtmUps5Tuple,
                                UtmUps8Tuple))
    band = coord.band
    zone = coord.zone

    z, B, h = _to3zBhp(zone, band, hemipole=hemi)

    if z == _UPS_ZONE:  # UPS
        import pygeodesy.ups as u  # PYCHOK expected
        U, M = _UPS_, _UpsMinMax
    else:  # UTM
        import pygeodesy.utm as u  # PYCHOK expected
        U, M = _UTM_, _UtmMinMax

    if MGRS:
        U, s = _MGRS_, _MGRS_TILE
    else:
        s = 0

    i = _NS_.find(h)
    if i < 0 or z < _UTMUPS_ZONE_MIN \
             or z > _UTMUPS_ZONE_MAX \
             or B not in u._Bands:
        t = '%s(%s%s %s)' % (U, z, B, h)
        raise Error(coord=t, zone=zone, band=band, hemisphere=hemi)

    if enMM:
        _en(coord.easting, M.eMin[i] - s, M.eMax[i] + s,
            _easting_)  # PYCHOK .eMax .eMin
        _en(coord.northing, M.nMin[i] - s, M.nMax[i] + s,
            _northing_)  # PYCHOK .nMax .nMin