def upsZoneBand5(lat, lon, strict=True): '''Return the UTM/UPS zone number, (polar) Band letter, pole and clipped lat- and longitude for a given location. @param lat: Latitude in degrees (C{scalar} or C{str}). @param lon: Longitude in degrees (C{scalar} or C{str}). @keyword strict: Restrict B{C{lat}} to UPS ranges (C{bool}). @return: A L{UtmUpsLatLon5Tuple}C{(zone, band, hemipole, lat, lon)} where C{hemipole} is the C{'N'|'S'} pole, the UPS projection top/center. @raise RangeError: If B{C{strict}} and B{C{lat}} in the UTM and not the UPS range or if B{C{lat}} or B{C{lon}} outside the valid range and L{rangerrors} set to C{True}. @raise ValueError: Invalid B{C{lat}} or B{C{lon}}. ''' z, lat, lon = _to3zll(*parseDMS2(lat, lon)) if lat < _UPS_LAT_MIN: # includes 30' overlap z, B, p = _UPS_ZONE, _Band(lat, lon), 'S' elif lat > _UPS_LAT_MAX: # includes 30' overlap z, B, p = _UPS_ZONE, _Band(lat, lon), 'N' elif strict: x = '%s [%s, %s]' % ('range', _UPS_LAT_MIN, _UPS_LAT_MAX) raise RangeError('%s inside UTM %s: %s' % ('lat', x, degDMS(lat))) else: B, p = '', _hemi(lat) return UtmUpsLatLon5Tuple(z, B, p, lat, lon)
def latlon(self, latlonh): '''Set the lat- and longitude and optionally the height. @param 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). ''' _TypeError(list, tuple, latlonh=latlonh) if len(latlonh) == 3: h = scalar(latlonh[2], None, name='latlonh') elif len(latlonh) != 2: raise ValueError('%s invalid: %r' % ('latlonh', latlonh)) else: h = self._height lat, lon = parseDMS2(latlonh[0], latlonh[1]) self._update(lat != self._lat or lon != self._lon or h != self._height) self._lat, self._lon, self._height = lat, lon, h
def __init__(self, lat, lon, height=0, name=''): '''New C{LatLon}. @param lat: Latitude (C{degrees} or DMS C{str} with N or S suffix). @param lon: Longitude (C{degrees} or DMS C{str} with E or W suffix). @keyword height: Optional height (C{meter} above or below the earth surface). @keyword 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 ValueError: Invalid B{C{lat}} or B{C{lon}}. @example: >>> p = LatLon(50.06632, -5.71475) >>> q = LatLon('50°03′59″N', """005°42'53"W""") ''' self._lat, self._lon = parseDMS2(lat, lon) # PYCHOK LatLon2Tuple if height: # elevation self._height = scalar(height, None, name='height') if name: self.name = name
def upsZoneBand5(lat, lon, strict=True): '''Return the UTM/UPS zone number, (polar) Band letter, pole and clipped lat- and longitude for a given location. @arg lat: Latitude in degrees (C{scalar} or C{str}). @arg lon: Longitude in degrees (C{scalar} or C{str}). @kwarg strict: Restrict B{C{lat}} to UPS ranges (C{bool}). @return: A L{UtmUpsLatLon5Tuple}C{(zone, band, hemipole, lat, lon)} where C{hemipole} is the C{'N'|'S'} pole, the UPS projection top/center. @raise RangeError: If B{C{strict}} and B{C{lat}} in the UTM and not the UPS range or if B{C{lat}} or B{C{lon}} outside the valid range and L{rangerrors} set to C{True}. @raise ValueError: Invalid B{C{lat}} or B{C{lon}}. ''' z, lat, lon = _to3zll(*parseDMS2(lat, lon)) if lat < _UPS_LAT_MIN: # includes 30' overlap z, B, p = _UPS_ZONE, _Band(lat, lon), _S_ elif lat > _UPS_LAT_MAX: # includes 30' overlap z, B, p = _UPS_ZONE, _Band(lat, lon), _N_ elif strict: t = ' '.join((_inside_, _UTM_, _range_, '[%s,' % (_UPS_LAT_MIN,), '%s]' % (_UPS_LAT_MAX,))) raise RangeError(lat=degDMS(lat), txt=t) else: B, p = NN, _hemi(lat) return UtmUpsLatLon5Tuple(z, B, p, lat, lon, Error=UPSError)
def toWm(latlon, lon=None, radius=R_MA, Wm=Wm, name='', **Wm_kwds): '''Convert a lat-/longitude point to a WM coordinate. @arg latlon: Latitude (C{degrees}) or an (ellipsoidal or spherical) geodetic C{LatLon} point. @kwarg lon: Optional longitude (C{degrees} or C{None}). @kwarg radius: Optional earth radius (C{meter}). @kwarg Wm: Optional class to return the WM coordinate (L{Wm}) or C{None}. @kwarg name: Optional name (C{str}). @kwarg Wm_kwds: Optional, additional B{C{Wm}} keyword arguments, ignored if B{C{Wm=None}}. @return: The WM coordinate (B{C{Wm}}) or an L{EasNorRadius3Tuple}C{(easting, northing, radius)} if B{C{Wm}} is C{None}. @raise ValueError: If B{C{lon}} value is missing, if B{C{latlon}} is not scalar, if B{C{latlon}} is beyond the valid WM range and L{rangerrors} is set to C{True} or if B{C{radius}} is invalid. @example: >>> p = LatLon(48.8582, 2.2945) # 448251.8 5411932.7 >>> w = toWm(p) # 448252 5411933 >>> p = LatLon(13.4125, 103.8667) # 377302.4 1483034.8 >>> w = toWm(p) # 377302 1483035 ''' e, r = None, Radius(radius) try: lat, lon = latlon.lat, latlon.lon if isinstance(latlon, _LLEB): r = latlon.datum.ellipsoid.a e = latlon.datum.ellipsoid.e if not name: # use latlon.name name = nameof(latlon) lat = clipDegrees(lat, _LatLimit) except AttributeError: lat, lon = parseDMS2(latlon, lon, clipLat=_LatLimit) s = sin(radians(lat)) y = atanh(s) # == log(tan((90 + lat) / 2)) == log(tanPI_2_2(radians(lat))) if e: y -= e * atanh(e * s) e, n = r * radians(lon), r * y if Wm is None: r = EasNorRadius3Tuple(e, n, r) else: kwds = _xkwds(Wm_kwds, radius=r) r = Wm(e, n, **kwds) return _xnamed(r, name)
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))
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 = latlon.lat, latlon.lon if not isinstance(latlon, _LLEB): raise TypeError('%s not %s: %r' % ('latlon', 'ellipsoidal', 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))
def utmZoneBand5(lat, lon, cmoff=False): '''Return the UTM zone number, Band letter, hemisphere and (clipped) lat- and longitude for a given location. @arg lat: Latitude in degrees (C{scalar} or C{str}). @arg lon: Longitude in degrees (C{scalar} or C{str}). @kwarg cmoff: Offset longitude from the zone's central meridian (C{bool}). @return: A L{UtmUpsLatLon5Tuple}C{(zone, band, hemipole, lat, lon)} where C{hemipole} is the C{'N'|'S'} UTM hemisphere. @raise RangeError: If B{C{lat}} outside the valid UTM bands or if B{C{lat}} or B{C{lon}} outside the valid range and L{rangerrors} set to C{True}. @raise ValueError: Invalid B{C{lat}} or B{C{lon}}. ''' lat, lon = parseDMS2(lat, lon) z, B, lat, lon = _to3zBll(lat, lon, cmoff=cmoff) return UtmUpsLatLon5Tuple(z, B, _hemi(lat), lat, lon)
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)
def _2fll(lat, lon, *unused): '''(INTERNAL) Convert lat, lon. ''' return parseDMS2(lat, lon)
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 B{C{Osgr}} name (C{str}). @return: The OSGR coordinate (B{C{Osgr}}) or an L{EasNor2Tuple}C{(easting, northing)} if B{C{Osgr}} is C{None}. @raise TypeError: Non-ellipsoidal B{C{latlon}} or B{C{datum}} conversion failed. @raise OSGRError: Invalid B{C{latlon}} or B{C{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 OSGRError('%s not %s: %r' % ('lon', None, lon)) elif not name: # use latlon.name name = nameof(latlon) 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, r = 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) 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) if lon is None and isinstance(latlon, _LLEB): r._latlon = latlon # XXX weakref(latlon)? return _xnamed(r, name)
def _2fllh(lat, lon, height=None): '''(INTERNAL) Convert lat, lon, height. ''' return parseDMS2(lat, lon) + (height, )
def _2fll(lat, lon, *unused): '''(INTERNAL) Convert lat, lon to 2-tuple of floats. ''' return parseDMS2(lat, lon)