Пример #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 (lon1, lon2) in (C{degrees180}) or C{None}
                    if the great circle doesn't reach the given I{lat}.
        '''
        self.others(other)

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

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

        ca, ca1, ca2, cdb = map1(cos, a, a1, a2, db)
        sa, sa1, sa2, sdb = map1(sin, 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 initialBearingTo(self, other, wrap=False):
        '''Compute the initial bearing (aka forward azimuth) from
           this to an other point.

           @param other: The other point (L{LatLon}).
           @keyword wrap: Wrap and unroll longitudes (bool).

           @return: Initial bearing (compass degrees).

           @raise TypeError: The I{other} point is not L{LatLon}.

           @example:

           >>> p1 = LatLon(52.205, 0.119)
           >>> p2 = LatLon(48.857, 2.351)
           >>> b = p1.initialBearingTo(p2)  # 156.2

           @JSname: I{bearingTo}.
        '''
        self.others(other)

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

        db, b2 = unrollPI(b1, b2, wrap=wrap)
        ca1, ca2, cdb = map1(cos, a1, a2, db)
        sa1, sa2, sdb = map1(sin, a1, a2, db)

        # see <http://mathforum.org/library/drmath/view/55417.html>
        x = ca1 * sa2 - sa1 * ca2 * cdb
        y = sdb * ca2

        return degrees360(atan2(y, x))
Пример #3
0
def bearing_(a1, b1, a2, b2, final=False, wrap=False):
    '''Compute the initial or final bearing (forward or reverse
       azimuth) between a (spherical) start and end point.

       @param a1: Start latitude (C{radians}).
       @param b1: Start longitude (C{radians}).
       @param a2: End latitude (C{radians}).
       @param b2: End longitude (C{radians}).
       @keyword final: Return final bearing if C{True}, initial
                       otherwise (C{bool}).
       @keyword wrap: Wrap and L{unrollPI} longitudes (C{bool}).

       @return: Initial or final bearing (compass C{radiansPI2}) or
                zero if start and end point coincide.
    '''
    if final:
        a1, b1, a2, b2 = a2, b2, a1, b1
        r = PI + PI2
    else:
        r = PI2

    db, _ = unrollPI(b1, b2, wrap=wrap)
    ca1, ca2, cdb = map1(cos, a1, a2, db)
    sa1, sa2, sdb = map1(sin, a1, a2, db)

    # see <http://MathForum.org/library/drmath/view/55417.html>
    x = ca1 * sa2 - sa1 * ca2 * cdb
    y = sdb * ca2

    return (atan2(y, x) + r) % PI2  # wrapPI2
Пример #4
0
def _x3d2(start, end, wrap, n):
    # see <http://www.EdWilliams.org/intersect.htm> (5) ff
    a1, b1 = start.to2ab()

    if isscalar(end):  # bearing, make a point
        a2, b2 = _destination2_(a1, b1, PI_4, radians(end))
    else:  # must be a point
        _Trll.others(end, name='end' + n)
        a2, b2 = end.to2ab()

    db, b2 = unrollPI(b1, b2, wrap=wrap)
    if max(abs(db), abs(a2 - a1)) < EPS:
        raise ValueError('intersection %s%s null: %r' % ('path', n,
                                                         (start, end)))

    # note, in EdWilliams.org/avform.htm W is + and E is -
    b21, b12 = db * 0.5, -(b1 + b2) * 0.5

    cb21, cb12 = map1(cos, b21, b12)
    sb21, sb12 = map1(sin, b21, b12)
    sa21, sa12 = map1(sin, a1 - a2, a1 + a2)

    x = Vector3d(sa21 * sb12 * cb21 - sa12 * cb12 * sb21,
                 sa21 * cb12 * cb21 + sa12 * sb12 * sb21,
                 cos(a1) * cos(a2) * sin(db),
                 ll=start)
    return x.unit(), (db, (a2 - a1))  # negated d
Пример #5
0
    def greatCircle(self, bearing):
        '''Compute the vector normal to great circle obtained by heading
           on the given initial bearing from this point.

           Direction of vector is such that initial bearing vector
           b = c × n, where n is an n-vector representing this point.

           @param bearing: Bearing from this point (compass C{degrees360}).

           @return: Vector representing great circle (L{Vector3d}).

           @example:

           >>> p = LatLon(53.3206, -1.7297)
           >>> g = p.greatCircle(96.0)
           >>> g.toStr()  # (-0.794, 0.129, 0.594)
        '''
        a, b = self.to2ab()
        t = radians(bearing)

        ca, cb, ct = map1(cos, a, b, t)
        sa, sb, st = map1(sin, a, b, t)

        return Vector3d(sb * ct - cb * sa * st, -cb * ct - sb * sa * st,
                        ca * st)  # XXX .unit()?
Пример #6
0
    def intermediateTo(self, other, fraction, height=None, wrap=False):
        '''Locate the point at given fraction between this and an
           other point.

           @param other: The other point (L{LatLon}).
           @param fraction: Fraction between both points (float, 0.0 =
                            this point, 1.0 = the other point).
           @keyword height: Optional height, overriding the fractional
                            height (C{meter}).
           @keyword wrap: Wrap and unroll longitudes (C{bool}).

           @return: Intermediate point (L{LatLon}).

           @raise TypeError: The I{other} point is not L{LatLon}.

           @example:

           >>> p1 = LatLon(52.205, 0.119)
           >>> p2 = LatLon(48.857, 2.351)
           >>> p = p1.intermediateTo(p2, 0.25)  # 51.3721°N, 000.7073°E

           @JSname: I{intermediatePointTo}.
        '''
        self.others(other)

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

        db, b2 = unrollPI(b1, b2, wrap=wrap)
        r = haversine_(a2, a1, db)
        sr = sin(r)
        if abs(sr) > EPS:
            cb1, cb2, ca1, ca2 = map1(cos, b1, b2, a1, a2)
            sb1, sb2, sa1, sa2 = map1(sin, b1, b2, a1, a2)

            A = sin((1 - fraction) * r) / sr
            B = sin(fraction * r) / sr

            x = A * ca1 * cb1 + B * ca2 * cb2
            y = A * ca1 * sb1 + B * ca2 * sb2
            z = A * sa1 + B * sa2

            a = atan2(z, hypot(x, y))
            b = atan2(y, x)

        else:  # points too close
            a = favg(a1, a2, f=fraction)
            b = favg(b1, b2, f=fraction)

        if height is None:
            h = self._havg(other, f=fraction)
        else:
            h = height
        return self.classof(degrees90(a), degrees180(b), height=h)
Пример #7
0
    def cross(self, other, raiser=None):
        '''Compute the cross product of this and an other vector.

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

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

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

           @raise TypeError: Incompatible I{other} C{type}.

           @raise ValueError: Coincident or colinear to I{other}.
        '''
        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('%s %s: %r' % (t, raiser, r))

        return self.classof(x, y, z)
Пример #8
0
    def __init__(self, x, y, radius=R_MA, name=''):
        '''New Web Mercator (WM) coordinate.

           @param x: Easting from central meridian (C{meter}).
           @param y: Northing from equator (C{meter}).
           @keyword radius: Optional earth radius (C{meter}).
           @keyword name: Optional name (C{str}).

           @raise ValueError: Invalid I{x}, I{y} or I{radius}.

           @example:

           >>> import pygeodesy
           >>> w = pygeodesy.Wm(448251, 5411932)
        '''
        if name:
            self.name = name

        try:
            self._x, self._y, r = map1(float, x, y, radius)
        except (TypeError, ValueError):
            raise ValueError('%s invalid: %r' % (Wm.__name__, (x, y, radius)))

        if r < EPS:  # check radius
            t = '%s.%s' % (classname(self), 'radius')
            raise ValueError('%s invalid: %r' % (t, r))
        self._radius = r
Пример #9
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)
Пример #10
0
def toOsgr(latlon, lon=None, datum=Datums.WGS84, Osgr=Osgr):
    '''Convert a lat-/longitude point to an OSGR coordinate.

       @param latlon: Latitude (degrees) or an (ellipsoidal)
                      geodetic I{LatLon} point.
       @keyword lon: Optional longitude in degrees (scalar or None).
       @keyword datum: Optional datum to convert (I{Datum}).
       @keyword Osgr: Optional Osgr class to use for the
                      OSGR coordinate (L{Osgr}).

       @return: The OSGR coordinate (L{Osgr}).

       @raise TypeError: If I{latlon} is not ellipsoidal or if
                         I{datum} conversion failed.

       @raise ValueError: Invalid I{latlon} or I{lon}.

       @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, _eLLb):
        # XXX fix failing _eLLb.convertDatum()
        latlon = _eLLb(*parseDMS2(latlon, lon), datum=datum)
    elif lon is not None:
        raise ValueError('%s not %s: %r' % ('lon', None, lon))

    E = _OSGB36.ellipsoid

    ll = _ll2datum(latlon, _OSGB36, 'latlon')
    a, b = map1(radians, ll.lat, ll.lon)

    ca, sa, ta = cos(a), sin(a), tan(a)

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

    x2 = r - 1  # η2

    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)

    return Osgr(e, n)
Пример #11
0
    def to2ab(self):
        '''Return this point's lat- and longitude in radians.

           @return: 2-Tuple (lat, lon) in (C{radiansPI_2}, C{radiansPI}).
        '''
        if not self._ab:
            self._ab = map1(radians, self.lat, self.lon)
        return self._ab
Пример #12
0
 def _exs(n, points):  # iterate over spherical edge excess
     a1, b1 = points[n - 1].to2ab()
     ta1 = tan_2(a1)
     for i in range(n):
         a2, b2 = points[i].to2ab()
         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
Пример #13
0
    def to2ab(self):
        '''Return this point's lat- and longitude in C{radians}.

           @return: A L{PhiLam2Tuple}C{(phi, lambda)}.
        '''
        if not self._ab:
            a, b = map1(radians, self.lat, self.lon)
            self._ab = self._xnamed(PhiLam2Tuple(a, b))
        return self._ab
Пример #14
0
def _destination2_(a, b, r, t):
    '''(INTERNAL) Computes destination lat- and longitude.

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

       @return: 2-Tuple (lat, lon) of (radians, radians).
    '''
    # see <http://www.EdWilliams.org/avform.htm#LL>
    ca, cr, ct = map1(cos, a, r, t)
    sa, sr, st = map1(sin, a, r, t)

    a = asin(ct * sr * ca + cr * sa)
    d = atan2(st * sr * ca, cr - sa * sin(a))
    # note, in EdWilliams.org/avform.htm W is + and E is -
    return a, b + d
Пример #15
0
def _destination2(a, b, r, t):
    '''(INTERNAL) Computes destination lat-/longitude.

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

       @return: 2-Tuple (lat, lon) of (C{degrees90}, C{degrees180}).
    '''
    # see <http://www.EdWilliams.org/avform.htm#LL>
    ca, cr, ct = map1(cos, a, r, t)
    sa, sr, st = map1(sin, a, r, t)

    a = asin(ct * sr * ca + cr * sa)
    d = atan2(st * sr * ca, cr - sa * sin(a))
    # note, use addition, not subtraction of d (since
    # in EdWilliams.org/avform.htm W is + and E is -)
    return degrees90(a), degrees180(b + d)
Пример #16
0
    def midpointTo(self, other, height=None, wrap=False):
        '''Find the midpoint between this and an other point.

           @param other: The other point (L{LatLon}).
           @keyword height: Optional height for midpoint, overriding
                            the mean height (meter).
           @keyword wrap: Wrap and unroll longitudes (bool).

           @return: Midpoint (L{LatLon}).

           @raise TypeError: The I{other} point is not L{LatLon}.

           @example:

           >>> p1 = LatLon(52.205, 0.119)
           >>> p2 = LatLon(48.857, 2.351)
           >>> m = p1.midpointTo(p2)  # '50.5363°N, 001.2746°E'
        '''
        self.others(other)

        # see <http://mathforum.org/library/drmath/view/51822.html>
        a1, b1 = self.to2ab()
        a2, b2 = other.to2ab()

        db, b2 = unrollPI(b1, b2, wrap=wrap)

        ca1, ca2, cdb = map1(cos, a1, a2, db)
        sa1, sa2, sdb = map1(sin, a1, a2, db)

        x = ca2 * cdb + ca1
        y = ca2 * sdb

        a = atan2(sa1 + sa2, hypot(x, y))
        b = atan2(y, x) + b1

        if height is None:
            h = self._havg(other)
        else:
            h = height
        return self.classof(degrees90(a), degrees180(b), height=h)
Пример #17
0
def bearing(lat1, lon1, lat2, lon2, final=False, wrap=False):
    '''Compute the initial or final bearing (forward or reverse
       azimuth) between a (spherical) start and end point.

       @param lat1: Start latitude (C{degrees}).
       @param lon1: Start longitude (C{degrees}).
       @param lat2: End latitude (C{degrees}).
       @param lon2: End longitude (C{degrees}).
       @keyword final: Return final or initial bearing (C{bool}).
       @keyword wrap: Wrap and L{unroll180} longitudes (C{bool}).

       @return: Initial or final bearing (compass C{degrees360}) or
                zero if start and end point coincide.
    '''
    a1, b1, a2, b2 = map1(radians, lat1, lon1, lat2, lon2)
    return degrees(bearing_(a1, b1, a2, b2, final=final, wrap=wrap))
Пример #18
0
def bearing(lat1, lon1, lat2, lon2, **options):
    '''Compute the initial or final bearing (forward or reverse
       azimuth) between a (spherical) start and end point.

       @param lat1: Start latitude (C{degrees}).
       @param lon1: Start longitude (C{degrees}).
       @param lat2: End latitude (C{degrees}).
       @param lon2: End longitude (C{degrees}).
       @keyword options: Optional keyword arguments for function
                         L{bearing_}.

       @return: Initial or final bearing (compass C{degrees360}) or
                zero if start and end point coincide.
    '''
    ab4 = map1(radians, lat1, lon1, lat2, lon2)
    return degrees(bearing_(*ab4, **options))
Пример #19
0
    def __init__(self, x, y, radius=R_MA):
        '''New Web Mercator (WM) coordinate.

           @param x: Easting from central meridian (meter).
           @param y: Northing from equator (meter).
           @keyword radius: Optional earth radius (meter).

           @raise ValueError: Invalid I{x}, I{y} or I{radius}.

           @example:

           >>> import pygeodesy
           >>> w = pygeodesy.Wm(448251, 5411932)
        '''
        try:
            self._x, self._y, self._radius = map1(float, x, y, radius)
        except (TypeError, ValueError):
            raise ValueError('%s invalid: %r' % (Wm.__name__, (x, y, radius)))
        _ = self.radius  # PYCHOK check radius
Пример #20
0
def areaOf(points, radius=R_M, wrap=True):
    '''Calculate the area of a (spherical) polygon (with great circle
       arcs joining the points).

       @param points: The polygon points (L{LatLon}[]).
       @keyword radius: Optional, mean earth radius (C{meter}).
       @keyword wrap: Wrap and unroll longitudes (C{bool}).

       @return: Polygon area (C{meter}, same units as I{radius}, squared).

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

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

       @note: The area is based on Karney's U{'Area of a spherical polygon'
              <http://osgeo-org.1560.x6.nabble.com/Area-of-a-spherical-polygon-td3841625.html>}.

       @see: L{pygeodesy.areaOf}, L{sphericalNvector.areaOf} and
             L{ellipsoidalKarney.areaOf}.

       @example:

       >>> b = LatLon(45, 1), LatLon(45, 2), LatLon(46, 2), LatLon(46, 1)
       >>> areaOf(b)  # 8666058750.718977

       >>> c = LatLon(0, 0), LatLon(1, 0), LatLon(0, 1)
       >>> areaOf(c)  # 6.18e9
    '''
    n, points = _Trll.points2(points, closed=True)

    # Area method due to Karney: for each edge of the polygon,
    #
    #                tan(Δλ/2) · (tan(φ1/2) + tan(φ2/2))
    #     tan(E/2) = ------------------------------------
    #                     1 + tan(φ1/2) · tan(φ2/2)
    #
    # where E is the spherical excess of the trapezium obtained by
    # extending the edge to the equator-circle vector for each edge

    if iterNumpy2(points):

        def _exs(n, points):  # iterate over spherical edge excess
            a1, b1 = points[n - 1].to2ab()
            ta1 = tan_2(a1)
            for i in range(n):
                a2, b2 = points[i].to2ab()
                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

        s = fsum(_exs(n, points)) * 2

    else:
        a1, b1 = points[n - 1].to2ab()
        s, ta1 = [], tan_2(a1)
        for i in range(n):
            a2, b2 = points[i].to2ab()
            db, b2 = unrollPI(b1, b2, wrap=wrap)
            ta2, tdb = map1(tan_2, a2, db)
            s.append(atan2(tdb * (ta1 + ta2), 1 + ta1 * ta2))
            ta1, b1 = ta2, b2

        s = fsum(s) * 2

    if isPoleEnclosedBy(points):
        s = abs(s) - PI2

    return abs(s * radius**2)
Пример #21
0
def intersection(start1, bearing1, start2, bearing2,
                 height=None, wrap=False, LatLon=LatLon):
    '''Compute the intersection point of two paths each defined
       by a start point and an initial bearing.

       @param start1: Start point of first path (L{LatLon}).
       @param bearing1: Initial bearing from start1 (compass C{degrees360}).
       @param start2: Start point of second path (L{LatLon}).
       @param bearing2: Initial bearing from start2 (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 for the intersection point
                        (L{LatLon}) or C{None}.

       @return: Intersection point (L{LatLon}) or 3-tuple (C{degrees90},
                C{degrees180}, height) if C{LatLon} is C{None}.

       @raise TypeError: Point I{start1} or I{start2} is not L{LatLon}.

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

       @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')

    # see <http://www.EdWilliams.org/avform.htm#Intersection>
    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))

    else:
        ca1, ca2, cr12 = map1(cos, a1, a2, r12)
        sa1, sa2, sr12 = map1(sin, 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, start2))

        # handle domain error for equivalent longitudes,
        # see also functions asin_safe and acos_safe at
        # <http://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, bearing1, bearing2)
        x1, x2 = map1(wrapPI, t13 - t12,  # angle 2-1-3
                              t21 - t23)  # angle 1-2-3

        sx1, sx2 = map1(sin, x1, x2)
        if sx1 == 0 and sx2 == 0:
            raise ValueError('intersection %s: %r vs %r' % ('infinite', start1, start2))
        sx3 = sx1 * sx2
        if sx3 < 0:
            raise ValueError('intersection %s: %r vs %r' % ('ambiguous', start1, start2))
        cx1, cx2 = map1(cos, x1, x2)

        x3 = acos1(cr12 * sx3 - cx2 * cx1)
        r13 = atan2(sr12 * sx3, cx2 + cx1 * cos(x3))

        a, b = _destination2(a1, b1, r13, t13)

    h = start1._havg(start2) if height is None else height
    return (a, b, h) if LatLon is None else LatLon(a, b, height=h)
Пример #22
0
def trilaterate(point1,
                distance1,
                point2,
                distance2,
                point3,
                distance3,
                radius=R_M,
                height=None,
                LatLon=LatLon,
                useZ=False):
    '''Locate a point at given distances from three other points.
       See also U{Trilateration<https://WikiPedia.org/wiki/Trilateration>}.

       @param point1: First point (L{LatLon}).
       @param distance1: Distance to the first point (C{meter}, same units
                         as B{C{radius}}).
       @param point2: Second point (L{LatLon}).
       @param distance2: Distance to the second point (C{meter}, same units
                         as B{C{radius}}).
       @param point3: Third point (L{LatLon}).
       @param distance3: Distance to the third point (C{meter}, same units
                         as B{C{radius}}).
       @keyword radius: Optional, mean earth radius (C{meter}).
       @keyword height: Optional height at the trilaterated point, overriding
                        the IDW height (C{meter}, same units as B{C{radius}}).
       @keyword LatLon: Optional (sub-)class to return the trilaterated
                        point (L{LatLon}).
       @keyword useZ: Include Z component iff non-NaN, non-zero (C{bool}).

       @return: Trilaterated point (B{C{LatLon}}).

       @raise TypeError: If B{C{point1}}, B{C{point2}} or B{C{point3}}
                         is not L{LatLon}.

       @raise ValueError: Invalid B{C{radius}}, some B{C{distances}} exceed
                          trilateration or some B{C{points}} coincide.
    '''
    def _nd2(p, d, name, *qs):
        # return Nvector and radial distance squared
        _Nvll.others(p, name=name)
        for q in qs:
            if p.isequalTo(q, EPS):
                raise ValueError('%s %s: %r' % ('coincident', 'points', p))
        return p.toNvector(), (float(d) / radius)**2

    if float(radius or 0) < EPS:
        raise ValueError('%s %s: %r' % ('radius', 'invalid', radius))

    n1, d12 = _nd2(point1, distance1, 'point1')
    n2, d22 = _nd2(point2, distance2, 'point2', point1)
    n3, d32 = _nd2(point3, distance3, 'point3', 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)

    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_(d12, -d22, d**2) / (2 * d)  # n1->intersection x- and ...
            y = fsum_(d12, -d32, i**2, j**2) / (2 * j) - (x * i / j
                                                          )  # ... y-component

            n = n1.plus(X.times(x)).plus(Y.times(y))  # .plus(Z.times(z))
            if useZ:  # include non-NaN, non-zero Z component
                z = fsum_(d12, -(x**2), -(y**2))
                if z > EPS:
                    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
            return n.toLatLon(
                height=h,
                LatLon=LatLon)  # Nvector(n.x, n.y, n.z).toLatLon(...)

    # no intersection, d < EPS_2 or j < EPS_2
    raise ValueError('no %s for %r, %r, %r at %r, %r, %r' %
                     ('trilaterate', point1, point2, point3, distance1,
                      distance2, distance3))
Пример #23
0
def toOsgr(latlon, lon=None, datum=Datums.WGS84, Osgr=Osgr, name=''):
    '''Convert a lat-/longitude point to an OSGR coordinate.

       @param latlon: Latitude (C{degrees}) or an (ellipsoidal)
                      geodetic C{LatLon} point.
       @keyword lon: Optional longitude in degrees (scalar or C{None}).
       @keyword datum: Optional datum to convert (C{Datum}).
       @keyword Osgr: Optional (sub-)class to return the OSGR
                      coordinate (L{Osgr}) or C{None}.
       @keyword name: Optional I{Osgr} name (C{str}).

       @return: The OSGR coordinate (L{Osgr}) or 2-tuple (easting,
                northing) if I{Osgr} is C{None}.

       @raise TypeError: Non-ellipsoidal I{latlon} or I{datum}
                         conversion failed.

       @raise ValueError: Invalid I{latlon} or I{lon}.

       @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 ValueError('%s not %s: %r' % ('lon', None, lon))
    elif not name:  # use latlon.name
        name = _nameof(latlon) or name  # PYCHOK no effect

    E = _OSGB36.ellipsoid

    ll = _ll2datum(latlon, _OSGB36, 'latlon')
    a, b = map1(radians, ll.lat, ll.lon)

    sa, ca = sincos2(a)

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

    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)

    return (e, n) if Osgr is None else _xnamed(Osgr(e, n), name)
Пример #24
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 for the intersection point
                        (L{LatLon}) or C{None}.

       @return: The intersection point (I{LatLon}) or 3-tuple
                (C{degrees90}, C{degrees180}, height) if I{LatLon}
                is C{None}.  An alternate intersection point might
                be the L{antipode} to the returned result.

       @raise TypeError: Start or end point(s) 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')

    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 <http://www.EdWilliams.org/avform.htm#Intersection>
    elif isscalar(end1) and isscalar(end2):  # both bearings
        ca1, ca2, cr12 = map1(cos, a1, a2, r12)
        sa1, sa2, sr12 = map1(sin, 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
        # <http://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, sx2 = map1(sin, 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)))
        cx1, cx2 = map1(cos, x1, x2)

        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')
        x2, d2 = _x3d2(start2, end2, wrap, '2')
        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 = start1._havg(start2) if height is None else height
    return (a, b, h) if LatLon is None else LatLon(a, b, height=h)
Пример #25
0
def isenclosedBy(point, points, wrap=False):  # MCCABE 15
    '''Determine whether a point is enclosed by a polygon.

       @param point: The point (C{LatLon} or 2-tuple (lat, lon)).
       @param points: The polygon points (C{LatLon}[]).
       @keyword wrap: Wrap lat-, wrap and unroll longitudes (C{bool}).

       @return: C{True} if I{point} is inside the polygon, C{False}
                otherwise.

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

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

       @see: L{sphericalNvector.LatLon.isEnclosedBy},
             L{sphericalTrigonometry.LatLon.isEnclosedBy} and
             U{MultiDop GeogContainPt<http://GitHub.com/NASA/MultiDop>}
             (U{Shapiro et al. 2009, JTECH
             <http://journals.AMetSoc.org/doi/abs/10.1175/2009JTECHA1256.1>}
             and U{Potvin et al. 2012, JTECH
             <http://journals.AMetSoc.org/doi/abs/10.1175/JTECH-D-11-00019.1>}).
    '''
    try:
        y0, x0 = point.lat, point.lon
    except AttributeError:
        try:
            y0, x0 = map1(float, *point[:2])
        except (IndexError, TypeError, ValueError):
            raise ValueError('%s invalid: %r' % ('point', point))

    pts = LatLon2psxy(points, closed=True, radius=None, wrap=wrap)
    n = len(pts)

    if wrap:
        x0, y0 = wrap180(x0), wrap90(y0)

        def _dxy(x1, i):
            x2, y2, _ = pts[i]
            dx, x2 = unroll180(x1, x2, wrap=i < (n - 1))
            return dx, x2, y2

    else:
        x0 = fmod(x0, 360.0)  # not x0 % 360

        def _dxy(x1, i):  # PYCHOK expected
            x, y, _ = pts[i]
            x %= 360.0
            if x < (x0 - 180):
                x += 360
            elif x >= (x0 + 180):
                x -= 360
            return (x - x1), x, y

    e = m = False
    s = Fsum()

    _, x1, y1 = _dxy(x0, n - 1)
    for i in range(n):
        dx, x2, y2 = _dxy(x1, i)
        # ignore duplicate and near-duplicate pts
        if max(abs(dx), abs(y2 - y1)) > EPS:
            # determine if polygon edge (x1, y1)..(x2, y2) straddles
            # point (lat, lon) or is on boundary, but do not count
            # edges on boundary as more than one crossing
            if abs(dx) < 180 and (x1 < x0 <= x2 or x2 < x0 <= x1):
                m = not m
                dy = (x0 - x1) * (y2 - y1) - (y0 - y1) * dx
                if (dy > 0 and dx >= 0) or (dy < 0 and dx <= 0):
                    e = not e

            s.fadd(sin(radians(y2)))
            x1, y1 = x2, y2

    # An odd number of meridian crossings means, the polygon
    # contains a pole.  Assume it is the pole on the hemisphere
    # containing the polygon mean point and if the polygon does
    # contain the North Pole, flip the result.
    if m and s.fsum() > 0:
        e = not e
    return e
Пример #26
0
def _T(*fs):  # tuple of single floats
    return map1(_F, *fs)