Ejemplo n.º 1
0
def equirectangular(lat1, lon1, lat2, lon2, radius=R_M, **options):
    '''Compute the distance between two points using
       the U{Equirectangular Approximation / Projection
       <https://www.Movable-Type.co.UK/scripts/latlong.html#equirectangular>}.

       @arg lat1: Start latitude (C{degrees}).
       @arg lon1: Start longitude (C{degrees}).
       @arg lat2: End latitude (C{degrees}).
       @arg lon2: End longitude (C{degrees}).
       @kwarg radius: Mean earth radius (C{meter}).
       @kwarg options: Optional keyword arguments for function
                       L{equirectangular_}.

       @return: Distance (C{meter}, same units as B{C{radius}}).

       @see: Function L{equirectangular_} for more details, the
             available B{C{options}}, errors, restrictions and other,
             approximate or accurate distance functions.
    '''
    _, dy, dx, _ = equirectangular_(Lat(lat1, name='lat1'),
                                    Lon(lon1, name='lon1'),
                                    Lat(lat2, name='lat2'),
                                    Lon(lon2, name='lon2'),
                                    **options)  # PYCHOK Distance4Tuple
    return degrees2m(hypot(dx, dy), radius=radius)
Ejemplo n.º 2
0
def elevation2(lat, lon, timeout=2.0):
    '''Get the geoid elevation at an C{NAD83} to C{NAVD88} location.

       @arg lat: Latitude (C{degrees}).
       @arg lon: Longitude (C{degrees}).
       @kwarg timeout: Optional, query timeout (seconds).

       @return: An L{Elevation2Tuple}C{(elevation, data_source)}
                or (C{None, "error"}) in case of errors.

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

       @note: The returned C{elevation} is C{None} if B{C{lat}} or B{C{lon}}
              is invalid or outside the C{Conterminous US (CONUS)},
              if conversion failed or if the query timed out.  The
              C{error} is the C{HTTP-, IO-, SSL-, Type-, URL-} or
              C{ValueError} as a string (C{str}).

       @see: U{USGS National Map<https://NationalMap.gov/epqs>},
             the U{FAQ<https://www.USGS.gov/faqs/what-are-projection-
             horizontal-and-vertical-datum-units-and-resolution-3dep-standard-dems>},
             U{geoid.py<https://Gist.GitHub.com/pyRobShrk>}, module
             L{geoids}, classes L{GeoidG2012B}, L{GeoidKarney} and
             L{GeoidPGM}.
    '''
    try:
        x = _qURL(
            'https://NED.USGS.gov/epqs/pqs.php',  # https://NationalMap.gov/epqs/pqs.php
            (
                'x=%.6F' % (Lat(lon), ),
                'y=%.6F' % (Lat(lat), ),
                'units=Meters',  # Feet
                'output=xml'),
            timeout=Scalar(timeout, name='timeout'))
        if x[:6] == '<?xml ':
            e = _xml('Elevation', x)
            try:
                e = float(e)
                if -1000000 < e < 1000000:
                    return Elevation2Tuple(e, _xml('Data_Source', x))
                e = 'non-CONUS %.2F' % (e, )
            except ValueError:
                pass
        else:
            e = 'no XML "%s"' % (clips(x, limit=128, white=' '), )
    except (HTTPError, IOError, TypeError, ValueError) as x:
        e = repr(x)
    return Elevation2Tuple(None, _error(elevation2, lat, lon, e))
Ejemplo n.º 3
0
    def __init__(self, lat, lon, height=0, name=NN):
        '''New C{LatLon}.

           @arg lat: Latitude (C{degrees} or DMS C{str} with N or S suffix).
           @arg lon: Longitude (C{degrees} or DMS C{str} with E or W suffix).
           @kwarg height: Optional height (C{meter} above or below the earth surface).
           @kwarg name: Optional name (C{str}).

           @return: New instance (C{LatLon}).

           @raise RangeError: Value of B{C{lat}} or B{C{lon}} outside the valid
                              range and C{rangerrors} set to C{True}.

           @raise UnitError: Invalid B{C{lat}}, B{C{lon}} or B{C{height}}.

           @example:

           >>> p = LatLon(50.06632, -5.71475)
           >>> q = LatLon('50°03′59″N', """005°42'53"W""")
        '''
        self._lat = Lat(lat)  # parseDMS2(lat, lon)
        self._lon = Lon(lon)  # PYCHOK LatLon2Tuple
        if height:  # elevation
            self._height = Height(height)
        if name:
            self.name = name
Ejemplo n.º 4
0
    def latlon(self, latlonh):
        '''Set the lat- and longitude and optionally the height.

           @arg latlonh: New lat-, longitude and height (2- or
                        3-tuple of C{degrees} and C{meter}).

           @raise TypeError: Height of B{C{latlonh}} not C{scalar} or
                             B{C{latlonh}} not C{list} or C{tuple}.

           @raise ValueError: Invalid B{C{latlonh}} or M{len(latlonh)}.

           @see: Function L{parse3llh} to parse a B{C{latlonh}} string
                 into a 3-tuple (lat, lon, h).
        '''
        _xinstanceof(list, tuple, latlonh=latlonh)

        if len(latlonh) == 3:
            h = Height(latlonh[2], name=Fmt.SQUARE(latlonh=2))
        elif len(latlonh) != 2:
            raise _ValueError(latlonh=latlonh)
        else:
            h = self._height

        lat = Lat(latlonh[0])  # parseDMS2(latlonh[0], latlonh[1])
        lon = Lon(latlonh[1])
        self._update(lat != self._lat or
                     lon != self._lon or h != self._height)
        self._lat, self._lon, self._height = lat, lon, h
Ejemplo n.º 5
0
    def latlon2(self, datum=None):
        '''Convert this WM coordinate to a lat- and longitude.

           @kwarg datum: Optional, ellipsoidal datum (L{Datum},
                         L{Ellipsoid}, L{Ellipsoid2} or
                         L{a_f2Tuple}) or C{None}.

           @return: A L{LatLon2Tuple}C{(lat, lon)}.

           @raise TypeError: Invalid or non-ellipsoidal B{C{datum}}.

           @see: Method C{toLatLon}.
        '''
        r = self.radius
        x = self._x / r
        y = 2 * atan(exp(self._y / r)) - PI_2
        if datum is not None:
            E = _ellipsoidal_datum(datum, name=self.name).ellipsoid
            if not E.isEllipsoidal:
                raise _IsnotError(_ellipsoidal_, datum=datum)
            # <https://Earth-Info.NGA.mil/GandG/wgs84/web_mercator/
            #        %28U%29%20NGA_SIG_0011_1.0.0_WEBMERC.pdf>
            y = y / r
            if E.e:
                y -= E.e * atanh(E.e * tanh(y))  # == E.es_atanh(tanh(y))
            y *= E.a
            x *= E.a / r

        r = LatLon2Tuple(Lat(degrees90(y)), Lon(degrees180(x)))
        return self._xnamed(r)
Ejemplo n.º 6
0
def _to3zBll(lat, lon, cmoff=True):
    '''(INTERNAL) Return zone, Band and lat- and (central) longitude in degrees.

       @arg lat: Latitude (C{degrees}).
       @arg lon: Longitude (C{degrees}).
       @kwarg cmoff: Offset B{C{lon}} from zone's central meridian.

       @return: 4-Tuple (zone, Band, lat, lon).
    '''
    z, lat, lon = _to3zll(lat, lon)  # in .utmupsBase

    if _UTM_LAT_MIN > lat or lat >= _UTM_LAT_MAX:  # [-80, 84) like Veness
        t = ' '.join((_outside_, _UTM_, _range_, '[%s,' % (_UTM_LAT_MIN,),
                                                  '%s)' % (_UTM_LAT_MAX,)))
        raise RangeError(lat=degDMS(lat), txt=t)
    B = _Bands[int(lat + 80) >> 3]

    x = lon - _cmlon(z)  # z before Norway/Svaldbard
    if abs(x) > _UTM_ZONE_OFF_MAX:
        t = ' '.join((_outside_, _UTM_, _zone_, str(z), 'by', degDMS(x, prec=6)))
        raise RangeError(lon=degDMS(lon), txt=t)

    if B == 'X':  # and 0 <= int(lon) < 42: z = int(lon + 183) // 6 + 1
        x = {32: 9, 34: 21, 36: 33}.get(z, None)
        if x:  # Svalbard
            z += 1 if lon >= x else -1
    elif B == 'V' and z == 31 and lon >= 3:
        z += 1  # SouthWestern Norway

    if cmoff:  # lon off central meridian
        lon -= _cmlon(z)  # z after Norway/Svaldbard
    return Zone(z), Band(B), Lat(lat), Lon(lon)
Ejemplo n.º 7
0
    def lat(self, lat):
        '''Set the latitude.

           @arg lat: New latitude (C{str[N|S]} or C{degrees}).

           @raise ValueError: Invalid B{C{lat}}.
        '''
        lat = Lat(lat)  # parseDMS(lat, suffix=_NS_, clip=90)
        self._update(lat != self._lat)
        self._lat = lat
Ejemplo n.º 8
0
def decode3(garef, center=True):
    '''Decode a C{garef} to lat-, longitude and precision.

       @arg garef: To be decoded (L{Garef} or C{str}).
       @kwarg center: If C{True} the center, otherwise the south-west,
                      lower-left corner (C{bool}).

       @return: A L{LatLonPrec3Tuple}C{(lat, lon, precision)}.

       @raise GARSError: Invalid B{C{garef}}, INValid, non-alphanumeric
                         or bad length B{C{garef}}.
    '''
    def _Error(i):
        return GARSError(garef=_item_sq(repr(garef), i))

    def _ll(chars, g, i, j, lo, hi):
        ll, b = 0, len(chars)
        for i in range(i, j):
            d = chars.find(g[i])
            if d < 0:
                raise _Error(i)
            ll = ll * b + d
        if ll < lo or ll > hi:
            raise _Error(j)
        return ll

    def _ll2(lon, lat, g, i, m):
        d = _Digits.find(g[i])
        if d < 1 or d > m * m:
            raise _Error(i)
        d, r = divmod(d - 1, m)
        lon = lon * m + r
        lat = lat * m + (m - 1 - d)
        return lon, lat

    g, precision = _2garstr2(garef)

    lon = _ll(_Digits, g, 0, _LonLen, 1, 720) + _LonOrig_M1_1
    lat = _ll(_Letters, g, _LonLen, _MinLen, 0, 359) + _LatOrig_M1
    if precision > 0:
        lon, lat = _ll2(lon, lat, g, _MinLen, _M2)
        if precision > 1:
            lon, lat = _ll2(lon, lat, g, _MinLen + 1, _M3)

    if center:  # ll = (ll * 2 + 1) / 2
        lon += 0.5
        lat += 0.5

    r = _Resolutions[precision]  # == 1.0 / unit
    r = LatLonPrec3Tuple(Lat(lat * r, Error=GARSError),
                         Lon(lon * r, Error=GARSError), precision)
    return _xnamed(r, nameof(garef))
Ejemplo n.º 9
0
def decode3(georef, center=True):
    '''Decode a C{georef} to lat-, longitude and precision.

       @arg georef: To be decoded (L{Georef} or C{str}).
       @kwarg center: If C{True} the center, otherwise the south-west,
                      lower-left corner (C{bool}).

       @return: A L{LatLonPrec3Tuple}C{(lat, lon, precision)}.

       @raise WGRSError: Invalid B{C{georef}}, INValid, non-alphanumeric
                         or odd length B{C{georef}}.
    '''
    def _digit(ll, g, i, m):
        d = _Digits.find(g[i])
        if d < 0 or d >= m:
            raise _Error(i)
        return ll * m + d

    def _Error(i):
        return WGRSError(Fmt.SQUARE(georef=i), georef)

    def _index(chars, g, i):
        k = chars.find(g[i])
        if k < 0:
            raise _Error(i)
        return k

    g, precision = _2geostr2(georef)
    lon = _index(_LonTile, g, 0) + _LonOrig_Tile
    lat = _index(_LatTile, g, 1) + _LatOrig_Tile

    u = _1_0
    if precision > 0:
        lon = lon * _Tile + _index(_DegChar, g, 2)
        lat = lat * _Tile + _index(_DegChar, g, 3)
        m, p = 6, precision - 1
        for i in range(_BaseLen, _BaseLen + p):
            lon = _digit(lon, g, i, m)
            lat = _digit(lat, g, i + p, m)
            u *= m
            m = _Base
        u *= _Tile

    if center:
        lon = lon * _2_0 + _1_0
        lat = lat * _2_0 + _1_0
        u *= _2_0
    u = _Tile / u
    r = LatLonPrec3Tuple(Lat(lat * u, Error=WGRSError),
                         Lon(lon * u, Error=WGRSError), precision)
    return _xnamed(r, nameof(georef))
Ejemplo n.º 10
0
    def reset(self, lat0, lon0):
        '''Set or reset the center point of this Cassini-Soldner projection.

           @arg lat0: Center point latitude (C{degrees90}).
           @arg lon0: Center point longitude (C{degrees180}).
        '''
        g, M = self.datum.ellipsoid._geodesic_Math2

        self._meridian = m = g.Line(Lat(lat0, name=_lat0_),
                                    Lon(lon0, name=_lon0_), 0.0,
                                    g.STANDARD | g.DISTANCE_IN)
        self._latlon0 = LatLon2Tuple(m.lat1, m.lon1)
        s, c = M.sincosd(m.lat1)  # == self.lat0 == self.LatitudeOrigin()
        self._sb0, self._cb0 = M.norm(s * (1.0 - g.f), c)
Ejemplo n.º 11
0
def geoidHeight2(lat, lon, model=0, timeout=2.0):
    '''Get the C{NAVD88} geoid height at an C{NAD83} location.

       @arg lat: Latitude (C{degrees}).
       @arg lon: Longitude (C{degrees}).
       @kwarg model: Optional, geoid model ID (C{int}).
       @kwarg timeout: Optional, query timeout (seconds).

       @return: An L{GeoidHeight2Tuple}C{(height, model_name)}
                or C{(None, "error"}) in case of errors.

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

       @note: The returned C{height} is C{None} if B{C{lat}} or B{C{lon}} is
              invalid or outside the C{Conterminous US (CONUS)}, if the
              B{C{model}} was invalid, if conversion failed or if the
              query timed out.  The C{error} is the C{HTTP-, IO-, SSL-,
              Type-, URL-} or C{ValueError} as a string (C{str}).

       @see: U{NOAA National Geodetic Survey
             <https://www.NGS.NOAA.gov/INFO/geodesy.shtml>},
             U{Geoid<https://www.NGS.NOAA.gov/web_services/geoid.shtml>},
             U{USGS10mElev.py<https://Gist.GitHub.com/pyRobShrk>}, module
             L{geoids}, classes L{GeoidG2012B}, L{GeoidKarney} and
             L{GeoidPGM}.
    '''
    try:
        j = _qURL('https://Geodesy.NOAA.gov/api/geoid/ght',
                  ('lat=%.6F' % (Lat(lat), ), 'lon=%.6F' %
                   (Lon(lon), ), 'model=%s' % (model, ) if model else ''),
                  timeout=Scalar(timeout, name='timeout'))  # PYCHOK 5
        if j[:1] == '{' and j[-1:] == '}' and j.find('"error":') > 0:
            d = _json(j)
            if isinstance(d.get('error', 'N/A'), float):
                h = d.get('geoidHeight', None)
                if h is not None:
                    m = _str(d.get('geoidModel', 'N/A'))
                    return GeoidHeight2Tuple(h, m)
            e = 'geoidHeight'
        else:
            e = 'JSON'
        e = 'no %s "%s"' % (e, clips(j, limit=256, white=' '))
    except (HTTPError, IOError, TypeError, ValueError) as x:
        e = repr(x)
    e = _error(geoidHeight2, lat, lon, e)
    return GeoidHeight2Tuple(None, e)
Ejemplo n.º 12
0
    def rescale0(self, lat, scale0=_K0):
        '''Set the central scale factor for this UPS projection.

           @arg lat: Northern latitude (C{degrees}).
           @arg scale0: UPS k0 scale at B{C{lat}} latitude (C{scalar}).

           @raise RangeError: If B{C{lat}} outside the valid range
                              and L{rangerrors} set to C{True}.

           @raise UPSError: Invalid B{C{scale}}.
        '''
        s0 = Scalar_(scale0, Error=UPSError, name='scale0', low=EPS)  # <= 1.003 or 1.0016?
        u  = toUps8(abs(Lat(lat)), 0, datum=self.datum, Ups=_UpsK1)
        k  = s0 / u.scale
        if self.scale0 != k:
            self._band = NN  # force re-compute
            self._latlon = self._epsg = self._mgrs = self._utm = None
            self._scale0 = Scalar(k)
Ejemplo n.º 13
0
from pygeodesy.lazily import _ALL_LAZY
from pygeodesy.named import _NamedBase, _NamedTuple, nameof, _xnamed
from pygeodesy.namedTuples import LatLon2Tuple, PhiLam2Tuple
from pygeodesy.streprs import Fmt, strs, _xzipairs
from pygeodesy.units import Easting, Lam_, Lat, Lon, Northing, Phi_, \
                            Radius, Radius_
from pygeodesy.utily import degrees90, degrees180

from math import atan, atanh, exp, radians, sin, tanh

__all__ = _ALL_LAZY.webmercator
__version__ = '20.11.04'

# _FalseEasting  = 0   # false Easting (C{meter})
# _FalseNorthing = 0   # false Northing (C{meter})
_LatLimit = Lat(limit=85.051129)  # latitudinal limit (C{degrees})
# _LonOrigin     = 0   # longitude of natural origin (C{degrees})


class EasNorRadius3Tuple(_NamedTuple):
    '''3-Tuple C{(easting, northing, radius)}, all in C{meter}.
    '''
    _Names_ = (_easting_, _northing_, _radius_)
    _Units_ = (Easting, Northing, Radius)


class WebMercatorError(_ValueError):
    '''Web Mercator (WM) parser or L{Wm} issue.
    '''
    pass
Ejemplo n.º 14
0
def _2fllh(lat, lon, height=None):
    '''(INTERNAL) Convert lat, lon, height.
    '''
    # lat, lon = parseDMS2(lat, lon)
    return (Lat(lat, Error=WGRSError), Lon(lon, Error=WGRSError), height)
Ejemplo n.º 15
0
def _2fll(lat, lon, *unused):
    '''(INTERNAL) Convert lat, lon to 2-tuple of floats.
    '''
    # lat, lon = parseDMS2(lat, lon)
    return (Lat(lat, Error=GeohashError),
            Lon(lon, Error=GeohashError))
Ejemplo n.º 16
0
 def __new__(cls, z, B, h, lat, lon, Error=None):
     if Error is not None:
         lat = Lat(lat, Error=Error)
         lon = Lon(lon, Error=Error)
     return _NamedTuple.__new__(cls, z, B, h, lat, lon)
Ejemplo n.º 17
0
 def __new__(cls, lat, lon, d, c, s):
     return _NamedTuple.__new__(cls, Lat(lat), Lon(lon), d,
                                Scalar(c, name=_convergence_),
                                Scalar(s, name=_scale_))
Ejemplo n.º 18
0
def _2fll(lat, lon, *unused):
    '''(INTERNAL) Convert lat, lon.
    '''
    # lat, lon = parseDMS2(lat, lon)
    return (Lat(lat, Error=GARSError), Lon(lon, Error=GARSError))
Ejemplo n.º 19
0
 def __new__(cls, lat, lon, azi, rk):
     return _NamedTuple.__new__(cls, Lat(lat, Error=CSSError),
                                Lon(lon, Error=CSSError),
                                Bearing(azi, Error=CSSError),
                                Scalar(rk, Error=CSSError))