示例#1
0
    def forward(self, latlonh, lon=None, height=0, M=False):
        '''Convert from geodetic C{(lat, lon, height)} to geocentric C{(x, y, z)}.

           @param latlonh: Either a C{LatLon}, an L{Ecef9Tuple} or C{scalar}
                           latitude in C{degrees}.
           @keyword lon: Optional C{scalar} longitude in C{degrees} for C{scalar}
                         B{C{latlonh}}.
           @keyword height: Optional height in C{meter}, vertically above (or
                            below) the surface of the ellipsoid.
           @keyword M: Optionally, return the rotation L{EcefMatrix} (C{bool}).

           @return: An L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M, datum)}
                    with geocentric C{(x, y, z)} coordinates for the given
                    geodetic ones C{(lat, lon, height)}, case C{C} 0,
                    L{EcefMatrix} C{M} and C{datum} if available.

           @raise EcefError: If B{C{latlonh}} not C{LatLon}, L{Ecef9Tuple} or
                             C{scalar} or B{C{lon}} not C{scalar} for C{scalar}
                             B{C{latlonh}} or C{abs(B{lat})} exceeds 90°.
        '''
        lat, lon, h, name = _llhn4(latlonh, lon, height, '')
        sa, ca, sb, cb = sincos2d(lat, lon)

        E = self.ellipsoid
        n = 1.0 / hypot(E.a * ca, E.b * sa)
        z = (n * E.b2 + h) * sa
        x = (n * E.a2 + h) * ca

        m = self._Matrix(sa, ca, sb, cb) if M else None
        r = Ecef9Tuple(x * cb, x * sb, z, lat, lon, h, 0, m, self.datum)
        return self._xnamed(r, name)
示例#2
0
    def destination(self, distance, bearing, radius=R_M, height=None):
        '''Locate the destination from this point after having
           travelled the given distance on the given bearing.

           @param distance: Distance travelled (C{meter}, same units
                            as B{C{radius}}).
           @param bearing: Bearing from this point (compass C{degrees360}).
           @keyword radius: Mean earth radius (C{meter}).
           @keyword height: Optional height at destination, overriding
                            the default height (C{meter}, same units as
                            B{C{radius}}).

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

           @raise Valuerror: Polar coincidence.

           @example:

           >>> p = LatLon(51.4778, -0.0015)
           >>> q = p.destination(7794, 300.7)
           >>> q.toStr()  # 51.513546°N, 000.098345°W

           @JSname: I{destinationPoint}.
        '''
        p = self.toNvector()

        e = NorthPole.cross(p, raiser='pole').unit()  # east vector at p
        n = p.cross(e)  # north vector at p

        s, c = sincos2d(bearing)
        q = n.times(c).plus(e.times(s))  # direction vector @ p

        s, c = sincos2(float(distance) / float(radius))  # angular distance in radians
        n = p.times(c).plus(q.times(s))
        return n.toLatLon(height=height, LatLon=self.classof)  # Nvector(n.x, n.y, n.z).toLatLon(...)
示例#3
0
    def greatCircle(self, bearing):
        '''Compute the n-vector normal to great circle obtained by
           heading on given compass bearing from this point as its
           n-vector.

           Direction of vector is such that initial bearing vector
           b = c × p.

           @param bearing: Initial compass bearing (C{degrees}).

           @return: N-vector representing great circle (L{Nvector}).

           @raise Valuerror: Polar coincidence.

           @example:

           >>> n = LatLon(53.3206, -1.7297).toNvector()
           >>> gc = n.greatCircle(96.0)  # [-0.794, 0.129, 0.594]
        '''
        s, c = sincos2d(bearing)

        e = NorthPole.cross(self, raiser='pole')  # easting
        n = self.cross(e, raiser='point')  # northing

        e = e.times(c / e.length)
        n = n.times(s / n.length)
        return n.minus(e)
示例#4
0
def toNed(distance, bearing, elevation, Ned=Ned, name=NN):
    '''Create an NED vector from distance, bearing and elevation
       (in local coordinate system).

       @arg distance: NED vector length (C{meter}).
       @arg bearing: NED vector bearing (compass C{degrees360}).
       @arg elevation: NED vector elevation from local coordinate
                       frame horizontal (C{degrees}).
       @kwarg Ned: Optional class to return the NED (L{Ned}) or
                   C{None}.
       @kwarg name: Optional name (C{str}).

       @return: An NED vector equivalent to this B{C{distance}},
                B{C{bearing}} and B{C{elevation}} (L{Ned}) or
                if B{C{Ned=None}}, an L{Ned3Tuple}C{(north, east,
                down)}.

       @raise ValueError: Invalid B{C{distance}}, B{C{bearing}}
                          or B{C{elevation}}.

       @JSname: I{fromDistanceBearingElevation}.
    '''
    d = Distance(distance)

    sb, cb, se, ce = sincos2d(Bearing(bearing),
                              Height(elevation, name=_elevation_))
    n = cb * d * ce
    e = sb * d * ce
    d *= se

    r = Ned3Tuple(n, e, -d) if Ned is None else \
              Ned(n, e, -d)
    return _xnamed(r, name)
示例#5
0
 def _gc(p, b):
     n = p.toNvector()
     de = NorthPole.cross(n, raiser='pole').unit()  # east vector @ n
     dn = n.cross(de)  # north vector @ n
     s, c = sincos2d(b)
     dest = de.times(s)
     dnct = dn.times(c)
     d = dnct.plus(dest)  # direction vector @ n
     return n.cross(d)  # great circle point + bearing
示例#6
0
    def forward(self, lat, lon, lon0=0, name=NN):
        '''Convert a geodetic location to east- and northing.

           @arg lat: Latitude of the location (C{degrees}).
           @arg lon: Longitude of the location (C{degrees}).
           @kwarg lon0: Optional central meridian longitude (C{degrees}).
           @kwarg name: Optional name for the location (C{str}).

           @return: An L{Albers7Tuple}C{(x, y, lat, lon, gamma, scale, datum)}.

           @note: The origin latitude is returned by C{property lat0}.  No
                  false easting or northing is added.  The value of B{C{lat}}
                  should be in the range C{[-90..90] degrees}.  The returned
                  values C{x} and C{y} will be large but finite for points
                  projecting to infinity, i.e. one or both of the poles.
        '''
        E = self.datum.ellipsoid
        s = self._sign

        k0 = self._k0
        n0 = self._n0
        nrho0 = self._nrho0
        txi0 = self._txi0

        sa, ca = sincos2d(_Lat_(lat) * s)
        ca = max(_EPSX, ca)
        ta = sa / ca

        _, sxi, txi = self._cstxif3(ta)
        dq = self._qZ * _Dsn(txi, txi0, sxi, self._sxi0) * (txi - txi0)
        drho = -E.a * dq / (sqrt(self._m02 - n0 * dq) + self._m0)

        lon = _Lon_(lon)
        if lon0:
            lon, _ = _diff182(_Lon_(lon0, name=_lon0_), lon)
        b = radians(lon)

        th = self._k02n0 * b
        sth, cth = sincos2(th)  # XXX sin, cos
        if n0:
            x = sth / n0
            y = (1 - cth if cth < 0 else sth**2 / (1 + cth)) / n0
        else:
            x = self._k02 * b
            y = 0
        t = nrho0 + n0 * drho
        x = t * x / k0
        y = s * (nrho0 * y - drho * cth) / k0

        g = degrees360(s * th)
        if t:
            k0 *= t * hypot1(E.b_a * ta) / E.a
        t = Albers7Tuple(x, y, lat, lon, g, k0, self.datum)
        return _xnamed(t, name or self.name)
示例#7
0
    def fEd(self, deg):
        '''The incomplete integral of the second kind with
           the argument given in degrees.

           @param deg: Angle (C{degrees}).

           @return: E(π deg/180, k).
        '''
        n = ceil(deg / 360.0 - 0.5)
        sn, cn = sincos2d(deg - n * 360.0)
        return self.fE(sn, cn, self.fDelta(sn, cn)) + 4 * self.cE * n
示例#8
0
    def reset(self, lat0, lon0):
        '''Set or reset the center point of this azimuthal projection.

           @arg lat0: Center point latitude (C{degrees90}).
           @arg lon0: Center point longitude (C{degrees180}).

           @raise AzimuthalError: Invalid B{C{lat0}} or B{C{lon0}}.
        '''
        self._latlon0 = LatLon2Tuple(Lat_(lat0=lat0, Error=AzimuthalError),
                                     Lon_(lon0=lon0, Error=AzimuthalError))
        self._sc0 = tuple(sincos2d(self.lat0))
        self._radius = None
示例#9
0
    def fEd(self, deg):
        '''The incomplete integral of the second kind with
           the argument given in degrees.

           @arg deg: Angle (C{degrees}).

           @return: E(π B{C{deg}}/180, k) (C{float}).

           @raise EllipticError: No convergence.
        '''
        n = ceil(deg / 360.0 - 0.5)
        sn, cn = sincos2d(deg - n * 360.0)
        return self.fE(sn, cn, self.fDelta(sn, cn)) + 4 * self.cE * n
示例#10
0
    def __init__(self, lat, k0=1, datum=Datums.WGS84, name=NN):
        '''New L{AlbersEqualArea} projection.

           @arg lat: Standard parallel (C{degrees}).
           @kwarg k0: Azimuthal scale on the standard parallel (C{scalar}).
           @kwarg datum: Optional datum or ellipsoid (L{Datum}, L{Ellipsoid},
                         L{Ellipsoid2} or L{a_f2Tuple}).
           @kwarg name: Optional name for the projection (C{str}).

           @raise AlbertError: Invalid B{C{lat}}, B{C{k0}} or no convergence.
        '''
        self._lat1 = self._lat2 = lat = _Lat(lat1=lat)
        args = tuple(sincos2d(lat)) * 2 + (_Ks(k0=k0), datum, name)
        _AlbersBase.__init__(self, *args)
示例#11
0
    def __init__(self, lat1, lat2, k1=1, datum=Datums.WGS84, name=NN):
        '''New L{AlbersEqualArea2} projection.

           @arg lat1: First standard parallel (C{degrees}).
           @arg lat2: Second standard parallel (C{degrees}).
           @kwarg k1: Azimuthal scale on the standard parallels (C{scalar}).
           @kwarg datum: Optional datum or ellipsoid (L{Datum}, L{Ellipsoid},
                         L{Ellipsoid2} or L{a_f2Tuple}).
           @kwarg name: Optional name for the projection (C{str}).

           @raise AlbertError: Invalid B{C{lat1}}m B{C{lat2}}, B{C{k1}}
                               or no convergence.
        '''
        self._lat1, self._lat2 = lats = _Lat(lat1=lat1), _Lat(lat2=lat2)
        args = tuple(sincos2d(*lats)) + (_Ks(k1=k1), datum, name)
        _AlbersBase.__init__(self, *args)
示例#12
0
    def _forward(self, lat, lon, name, _k_t):
        '''(INTERNAL) Azimuthal (spherical) forward C{lat, lon} to C{x, y}.
        '''
        sa, ca, sb, cb = sincos2d(Lat_(lat), Lon_(lon) - self.lon0)
        s0, c0 = self._sc0

        k, t = _k_t(s0 * sa + c0 * ca * cb)
        if t:
            r = k * self.radius
            x = r * ca * sb
            y = r * (c0 * sa - s0 * ca * cb)
            z = atan2b(x, y)  # (x, y) for azimuth from true North
        else:  # 0 or 180
            x = y = z = 0

        t = Azimuthal7Tuple(x, y, lat, lon, z, k, self.datum)
        return self._xnamed(t, name=name)
示例#13
0
    def rhumbDestination(self, distance, bearing, radius=R_M, height=None):
        '''Return the destination point having travelled along a rhumb
           (loxodrome) line from this point the given distance on the
           given bearing.

           @param distance: Distance travelled (C{meter}, same units as
                            I{radius}).
           @param bearing: Bearing from this point (compass C{degrees360}).
           @keyword radius: Optional, mean earth radius (C{meter}).
           @keyword height: Optional height, overriding the default
                            height (C{meter}, same unit as I{radius}).

           @return: The destination point (spherical C{LatLon}).

           @example:

           >>> p = LatLon(51.127, 1.338)
           >>> q = p.rhumbDestination(40300, 116.7)  # 50.9642°N, 001.8530°E

           @JSname: I{rhumbDestinationPoint}
        '''
        a1, b1 = self.to2ab()

        r = float(distance) / float(radius)  # angular distance in radians

        sb, cb = sincos2d(bearing)

        da = r * cb
        a2 = a1 + da
        # normalize latitude if past pole
        if a2 > PI_2:
            a2 = PI - a2
        elif a2 < -PI_2:
            a2 = -PI - a2

        dp = log(tanPI_2_2(a2) / tanPI_2_2(a1))
        # E-W course becomes ill-conditioned with 0/0
        q = (da / dp) if abs(dp) > EPS else cos(a1)
        b2 = (b1 + r * sb / q) if abs(q) > EPS else b1

        h = self.height if height is None else height
        return self.classof(degrees90(a2), degrees180(b2), height=h)
示例#14
0
    def forward(self, latlonh, lon=None, height=0, M=False):
        '''Convert from geodetic C{(lat, lon, height)} to geocentric C{(x, y, z)}.

           @arg latlonh: Either a C{LatLon}, an L{Ecef9Tuple} or C{scalar}
                         latitude in C{degrees}.
           @kwarg lon: Optional C{scalar} longitude in C{degrees} for C{scalar}
                       B{C{latlonh}}.
           @kwarg height: Optional height in C{meter}, vertically above (or
                          below) the surface of the ellipsoid.
           @kwarg M: Optionally, return the rotation L{EcefMatrix} (C{bool}).

           @return: An L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M, datum)}
                    with geocentric C{(x, y, z)} coordinates for the given
                    geodetic ones C{(lat, lon, height)}, case C{C} 0, optional
                    L{EcefMatrix} C{M} and C{datum} if available.

           @raise EcefError: If B{C{latlonh}} not C{LatLon}, L{Ecef9Tuple} or
                             C{scalar} or B{C{lon}} not C{scalar} for C{scalar}
                             B{C{latlonh}} or C{abs(lat)} exceeds 90°.

           @note: Let C{v} be a unit vector located at C{(lat, lon, h)}.  We can
                  express C{v} as column vectors in one of two ways, C{v1} in east,
                  north, up coordinates (where the components are relative
                  to a local coordinate system at C{C(lat0, lon0, h0)}) or as
                  C{v0} in geocentric C{x, y, z} coordinates.  Then, M{v0 =
                  M ⋅ v1} where C{M} is the rotation matrix.
        '''
        lat, lon, h, name = _llhn4(latlonh, lon, height, '')
        sa, ca, sb, cb = sincos2d(lat, lon)

        n = self.a / self.ellipsoid.e2s(sa)  # ... / sqrt(1 - self.e2 * sa**2)
        z = (self.e2m * n + h) * sa
        x = (n + h) * ca

        m = self._Matrix(sa, ca, sb, cb) if M else None
        r = Ecef9Tuple(x * cb, x * sb, z, lat, lon, h, 0, m, self.datum)
        return self._xnamed(r, name)
示例#15
0
    def forward(self, lat, lon, name=NN, raiser=True):
        '''Convert an (ellipsoidal) geodetic location to azimuthal gnomonic east- and northing.

           @arg lat: Latitude of the location (C{degrees90}).
           @arg lon: Longitude of the location (C{degrees180}).
           @kwarg name: Optional name for the location (C{str}).
           @kwarg raiser: Do or don't throw an error (C{bool}) if
                          the location lies over the horizon.

           @return: An L{Azimuthal7Tuple}C{(x, y, lat, lon, azimuth, scale, datum)}
                    with C{x} and C{y} in C{meter} and C{lat} and C{lon} in C{degrees}
                    and C{azimuth} clockwise from true North.  The C{scale} of the
                    projection is C{1 / reciprocal**2} in I{radial} direction and
                    C{1 / reciprocal} in the direction perpendicular to this.  Both
                    C{x} and C{y} will be C{NAN} if the geodetic location lies over
                    the horizon and B{C{raiser}} is C{False}.

           @raise AzimuthalError: Invalid B{C{lat}}, B{C{lon}} or the geodetic location
                                  lies over the horizon and B{C{raiser}} is C{True}.
        '''
        self._iteration = 0

        r = self.geodesic.Inverse(self.lat0, self.lon0, Lat_(lat), Lon_(lon),
                                  self._mask)
        if r.M21 < EPS:
            if raiser:
                raise AzimuthalError(lat=lat, lon=lon, txt=_over_horizon_)
            x = y = NAN
        else:
            q = r.m12 / r.M21  # .M12
            x, y = sincos2d(r.azi1)
            x *= q
            y *= q

        t = self._7Tuple(x, y, r, r.M21)
        t._iteraton = self._iteration  # = 0
        return self._xnamed(t, name=name)
示例#16
0
    def forward(self, lat, lon, name=NN):
        '''Convert an (ellipsoidal) geodetic location to azimuthal equidistant east- and northing.

           @arg lat: Latitude of the location (C{degrees90}).
           @arg lon: Longitude of the location (C{degrees180}).
           @kwarg name: Optional name for the location (C{str}).

           @return: An L{Azimuthal7Tuple}C{(x, y, lat, lon, azimuth, scale, datum)}
                    with C{x} and C{y} in C{meter} and C{lat} and C{lon} in
                    C{degrees}.  The C{scale} of the projection is C{1} in I{radial}
                    direction, C{azimuth} clockwise from true North and is C{1 /
                    reciprocal} in the direction perpendicular to this.

           @see: Method L{EquidistantKarney.reverse}.  A call to C{.forward}
                 followed by a call to C{.reverse} will return the original
                 C{lat, lon} to within roundoff.

            @raise AzimuthalError: Invalid B{C{lat}} or B{C{lon}}.
       '''
        r = self.geodesic.Inverse(self.lat0, self.lon0, Lat_(lat), Lon_(lon),
                                  self._mask)
        x, y = sincos2d(r.azi1)
        t = self._7Tuple(x * r.s12, y * r.s12, r)
        return self._xnamed(t, name=name)
示例#17
0
def toUps8(latlon, lon=None, datum=None, Ups=Ups, pole='',
                             falsed=True, strict=True, name=''):
    '''Convert a lat-/longitude point to a UPS coordinate.

       @param latlon: Latitude (C{degrees}) or an (ellipsoidal)
                      geodetic C{LatLon} point.
       @keyword lon: Optional longitude (C{degrees}) or C{None}
                     if B{C{latlon}} is a C{LatLon}.
       @keyword datum: Optional datum for this UPS coordinate,
                       overriding B{C{latlon}}'s datum (C{Datum}).
       @keyword Ups: Optional (sub-)class to return the UPS
                     coordinate (L{Ups}) or C{None}.
       @keyword pole: Optional top/center of (stereographic) projection
                      (C{str}, C{'N[orth]'} or C{'S[outh]'}).
       @keyword falsed: False both easting and northing (C{bool}).
       @keyword strict: Restrict B{C{lat}} to UPS ranges (C{bool}).
       @keyword name: Optional B{C{Ups}} name (C{str}).

       @return: The UPS coordinate (B{C{Ups}}) or a
                L{UtmUps8Tuple}C{(zone, hemipole, easting, northing,
                band, datum, convergence, scale)} if B{C{Ups}} is
                C{None}.  The C{hemipole} is the C{'N'|'S'} pole,
                the UPS projection top/center.

       @raise RangeError: If B{C{strict}} and B{C{lat}} outside the
                          valid UPS bands or if B{C{lat}} or B{C{lon}}
                          outside the valid range and L{rangerrors}
                          set to C{True}.

       @raise TypeError: If B{C{latlon}} is not ellipsoidal.

       @raise ValueError: If B{C{lon}} value is missing or if B{C{latlon}}
                          is invalid.

       @see: Karney's C++ class U{UPS
             <https://GeographicLib.SourceForge.io/html/classGeographicLib_1_1UPS.html>}.
    '''
    lat, lon, d, name = _to4lldn(latlon, lon, datum, name)
    z, B, p, lat, lon = upsZoneBand5(lat, lon, strict=strict)  # PYCHOK UtmUpsLatLon5Tuple

    E = d.ellipsoid

    p = str(pole or p)[:1].upper()
    N = p == 'N'  # is north

    a = lat if N else -lat
    A = abs(a - 90) < _TOL  # at pole

    t = tan(radians(a))
    T = E.es_taupf(t)
    r = hypot1(T) + abs(T)
    if T >= 0:
        r = 0 if A else 1 / r

    k0 = getattr(Ups, '_scale0', _K0)  # Ups is class or None
    r *= 2 * k0 * E.a / E.es_c

    k = k0 if A else _scale(E, r, t)
    c = lon  # [-180, 180) from .upsZoneBand5
    x, y = sincos2d(c)
    x *= r
    y *= r
    if N:
        y = -y
    else:
        c = -c

    if falsed:
        x += _Falsing
        y += _Falsing

    if Ups is None:
        r = UtmUps8Tuple(z, p, x, y, B, d, c, k)
    else:
        if z != _UPS_ZONE and not strict:
            z = _UPS_ZONE  # ignore UTM zone
        r = Ups(z, p, x, y, band=B, datum=d, falsed=falsed,
                                    convergence=c, scale=k)
        r._hemisphere = _hemi(lat)
        if isinstance(latlon, _LLEB) and d is latlon.datum:
            r._latlon_to(latlon, falsed)  # XXX weakref(latlon)?
    return _xnamed(r, name)