def __new__(cls, cll, precision=3, name=NN): '''New L{Georef} from an other L{Georef} instance or georef C{str} or from a C{LatLon} instance or lat-/longitude C{str}. @arg cll: Cell or location (L{Georef} or C{str}, C{LatLon} or C{str}). @kwarg precision: Optional, the desired georef resolution and length (C{int} 0..11), see function L{wgrs.encode} for more details. @kwarg name: Optional name (C{str}). @return: New L{Georef}. @raise RangeError: Invalid B{C{cll}} lat- or longitude. @raise TypeError: Invalid B{C{cll}}. @raise WGRSError: INValid or non-alphanumeric B{C{cll}}. ''' h = None if isinstance(cll, Georef): g, p = _2geostr2(str(cll)) self = Str.__new__(cls, g) self._latlon = LatLon2Tuple(*cll._latlon) self._precision = p # cll._precision if cll._name: self._name = cll._name elif isstr(cll): if ',' in cll: lat, lon, h = _2fllh(*parse3llh(cll, height=None)) g = encode(lat, lon, precision=precision, height=h) # PYCHOK false self = Str.__new__(cls, g) self._latlon = LatLon2Tuple(lat, lon) else: self = Str.__new__(cls, cll.upper()) self._decode() else: # assume LatLon try: lat, lon, h = _2fllh(cll.lat, cll.lon) h = getattr(cll, _height_, h) except AttributeError: raise _xStrError(Georef, cll=cll) # Error=WGRSError g = encode(lat, lon, precision=precision, height=h) # PYCHOK false self = Str.__new__(cls, g) self._latlon = LatLon2Tuple(lat, lon) if h not in (None, _Missing): self._height = Height(h) if self._precision is None: self._precision = _2Precision(precision) if name: self.name = name return self
def __new__(cls, cll, precision=3, name=''): '''New L{Georef} from an other L{Georef} instance or georef C{str} or from a C{LatLon} instance or lat-/longitude C{str}. @param cll: Cell or location (L{Georef} or C{str}, C{LatLon} or C{str}). @keyword precision: Optional, the desired georef resolution and length (C{int} 0..11), see function L{wgrs.encode} for more details. @keyword name: Optional name (C{str}). @return: New L{Georef}. @raise RangeError: Invalid B{C{cll}} lat- or longitude. @raise TypeError: Invalid B{C{cll}}. @raise WGRSError: INValid or non-alphanumeric B{C{cll}}. ''' if isinstance(cll, Georef): g, p = _2geostr2(str(cll)) self = str.__new__(cls, g) self._latlon = LatLon2Tuple(*cll._latlon) self._name = cll._name self._precision = p # cll._precision elif isinstance(cll, _Strs): if ',' in cll: lat, lon, h = _2fllh(*parse3llh(cll, height=None)) g = encode(lat, lon, precision=precision, height=h) # PYCHOK false self = str.__new__(cls, g) self._latlon = LatLon2Tuple(lat, lon) self._precision = precision if h not in (None, _MISSING): self._height = h else: self = str.__new__(cls, cll.upper()) self._decode() else: # assume LatLon try: lat, lon, h = _2fllh(cll.lat, cll.lon) h = getattr(cll, 'height', h) except AttributeError: raise _IsNotError('valid', **{Georef.__name__: cll}) g = encode(lat, lon, precision=precision, height=h) # PYCHOK false self = str.__new__(cls, g) self._latlon = LatLon2Tuple(lat, lon) self._precision = precision if h not in (None, _MISSING): self._height = h if name: self.name = name return self
def __new__(cls, cll, precision=1, name=NN): '''New L{Garef} from an other L{Garef} instance or garef C{str} or from a C{LatLon} instance or lat-/longitude C{str}. @arg cll: Cell or location (L{Garef} or C{str}, C{LatLon} or C{str}). @kwarg precision: Optional, the desired garef resolution and length (C{int} 0..2), see function L{gars.encode} for more details. @kwarg name: Optional name (C{str}). @return: New L{Garef}. @raise RangeError: Invalid B{C{cll}} lat- or longitude. @raise TypeError: Invalid B{C{cll}}. @raise GARSError: INValid or non-alphanumeric B{C{cll}}. ''' if isinstance(cll, Garef): g, p = _2garstr2(str(cll)) self = Str.__new__(cls, g) self._latlon = LatLon2Tuple(*cll._latlon) self._name = cll._name self._precision = p # cll._precision elif isstr(cll): if ',' in cll: lat, lon = _2fll(*parse3llh(cll)) cll = encode(lat, lon, precision=precision) # PYCHOK false self = Str.__new__(cls, cll) self._latlon = LatLon2Tuple(lat, lon) else: self = Str.__new__(cls, cll.upper()) self._decode() else: # assume LatLon try: lat, lon = _2fll(cll.lat, cll.lon) except AttributeError: raise _xStrError(Garef, cll=cll) # Error=GARSError cll = encode(lat, lon, precision=precision) # PYCHOK false self = Str.__new__(cls, cll) self._latlon = LatLon2Tuple(lat, lon) if self._precision is None: self._precision = _2Precision(precision) if name: self.name = name return self
def to2ll(self, datum=None): '''Convert this WM coordinate to a geodetic lat- and longitude. @keyword datum: Optional datum (C{Datum}). @return: A L{LatLon2Tuple}C{(lat, lon)}. @raise TypeError: Non-ellipsoidal B{C{datum}}. ''' r = self.radius x = self._x / r y = 2 * atan(exp(self._y / r)) - PI_2 if datum: E = datum.ellipsoid if not E.isEllipsoidal: raise TypeError('%s not %s: %r' % ('datum', 'ellipsoidal', 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)) y *= E.a x *= E.a / r return self._xnamed(LatLon2Tuple(degrees90(y), degrees180(x)))
def reverse(self, easting, northing, LatLon=None, **LatLon_kwds): '''Convert a Cassini-Soldner location to (ellipsoidal) geodetic lat- and longitude. @arg easting: Easting of the location (C{meter}). @arg northing: Northing of the location (C{meter}). @kwarg LatLon: Optional, ellipsoidal class to return the geodetic location as (C{LatLon}) or C{None}. @kwarg LatLon_kwds: Optional (C{LatLon}) keyword arguments, ignored if B{C{LatLon=None}}. @return: Geodetic location B{C{LatLon}} or if B{C{LatLon}} is C{None}, a L{LatLon2Tuple}C{(lat, lon)}. @raise CSSError: Ellipsoidal mismatch of B{C{LatLon}} and this projection. @raise TypeError: Invalid B{C{LatLon}} or B{C{LatLon_kwds}}. @see: Method L{CassiniSoldner.reverse4}, L{CassiniSoldner.forward} and L{CassiniSoldner.forward4}. ''' r = self.reverse4(easting, northing) if LatLon is None: r = LatLon2Tuple(r.lat, r.lon) # PYCHOK expected else: _xsubclassof(_LLEB, LatLon=LatLon) kwds = _xkwds(LatLon_kwds, datum=self.datum) r = LatLon(r.lat, r.lon, **kwds) # PYCHOK expected self._datumatch(r) return self._xnamed(r)
def latlon2(self, datum=None): '''Convert this WM coordinate to a lat- and longitude. @kwarg datum: Optional ellipsoidal datum (C{Datum}). @return: A L{LatLon2Tuple}C{(lat, lon)}. @raise TypeError: 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: _xinstanceof(Datum, datum=datum) E = datum.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)
def _decode(self): # cache all decoded attrs lat, lon, p, h, r = decode5(self) # PYCHOK LatLonPrec5Tuple if self._latlon is None: self._latlon = LatLon2Tuple(lat, lon) if self._precision is None: self._precision = p if self._height is _Missing: self._height = h if self._radius is _Missing: self._radius = r
def latlon2(self, ndigits=0): '''Return this point's lat- and longitude, rounded. @keyword ndigits: Number of decimal digits (C{int}). @return: A L{LatLon2Tuple}C{(lat, lon)}, both rounded away from zero. @see: Built-in function C{round}. ''' r = LatLon2Tuple(round(self.lat, ndigits), round(self.lon, ndigits)) return self._xnamed(r)
def reset(self, lat0, lon0): '''Set the center point of this projection. @param lat0: Latitude of center point (C{degrees90}). @param lon0: Longitude of center point (C{degrees180}). ''' g, M = self.datum.ellipsoid._geodesic2 self._meridian = m = g.Line(lat0, 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)
def antipode(lat, lon): '''Return the antipode, the point diametrically opposite to a given point in C{degrees}. @arg lat: Latitude (C{degrees}). @arg lon: Longitude (C{degrees}). @return: A L{LatLon2Tuple}C{(lat, lon)}. @see: U{Geosphere<https://CRAN.R-Project.org/web/packages/geosphere/geosphere.pdf>}. ''' return LatLon2Tuple(-wrap90(lat), wrap180(lon + 180))
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, name=_lat0_, Error=AzimuthalError), Lon_(lon0, name=_lon0_, Error=AzimuthalError)) self._sc0 = tuple(sincos2d(self.lat0)) self._radius = None
def nearestOn2(point, points, **closed_radius_LatLon_options): # PYCHOK no cover '''DEPRECATED, use function L{sphericalTrigonometry.nearestOn3}. @return: ... 2-tuple C{(closest, distance)} of the C{closest} point (L{LatLon}) on the polygon and the C{distance} between the C{closest} and the given B{C{point}}. The C{closest} is a B{C{LatLon}} or a L{LatLon2Tuple}C{(lat, lon)} if B{C{LatLon}} is C{None} ... ''' ll, d, _ = nearestOn3(point, points, **closed_radius_LatLon_options) if _xkwds_get(closed_radius_LatLon_options, LatLon=LatLon) is None: ll = LatLon2Tuple(ll.lat, ll.lon) return ll, d
def to2ll(self): '''Convert this vector to (geodetic) lat- and longitude in C{degrees}. @return: A L{LatLon2Tuple}C{(lat, lon)}. @example: >>> v = Vector3d(0.500, 0.500, 0.707) >>> a, b = v.to2ll() # 44.99567, 45.0 ''' a, b = self.to2ab() r = LatLon2Tuple(degrees90(a), degrees180(b)) return self._xnamed(r)
def _fractional(points, fi): '''(INTERANL) Compute point as fractional index. ''' i = int(fi) p = points[i] f = fi - float(i) if f > EPS: if f < EPS1: q = points[i + 1] p = LatLon2Tuple(favg(p.lat, q.lat, f=f), favg(p.lon, q.lon, f=f)) else: p = points[i + 1] return p
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)
def toCss(latlon, cs0=_CassiniSoldner0, height=None, Css=Css, name=''): '''Convert an (ellipsoidal) geodetic point to a Cassini-Soldner location. @param latlon: Ellipsoidal point (C{LatLon}). @keyword cs0: Optional, the Cassini-Soldner projection to use (L{CassiniSoldner}). @keyword height: Optional height for the point, overriding the default height (C{meter}). @keyword Css: Optional (sub-)class to return the location (L{Css}) or C{None}. @keyword name: Optional B{C{Css}} name (C{str}). @return: The Cassini-Soldner location (B{C{Css}}) or an L{EasNor3Tuple}C{(easting, northing, height)} if B{C{Css}} is C{None}. @raise CSSError: Mismatch of this and the B{C{latlon}} ellipsoid. @raise ImportError: Package U{GeographicLib<https://PyPI.org/ project/geographiclib>} missing. @raise TypeError: If B{C{latlon}} is not ellipsoidal. ''' if not isinstance(latlon, _LLEB): raise _IsNotError(_LLEB.__name__, latlon=latlon) cs = _CassiniSoldner(cs0) C, E = cs.datum.ellipsoid, latlon.datum.ellipsoid if C != E: raise CSSError('%s mistmatch: %r vs %r' % ('ellipsoidal', C, E)) c = cs.forward4(latlon.lat, latlon.lon) h = latlon.height if height is None else height if Css is None: r = EasNor3Tuple(c.easting, c.northing, h) else: r = Css(c.easting, c.northing, h=h, cs0=cs) r._latlon = LatLon2Tuple(latlon.lat, latlon.lon) r._azi, r._rk = c.azimuth, c.reciprocal return _xnamed(r, name or nameof(latlon))
def reverse(self, easting, northing, LatLon=None): '''Convert a Cassini-Soldner location to (ellipsoidal) geodetic lat- and longitude. @param easting: Easting of the location (C{meter}). @param northing: Northing of the location (C{meter}). @keyword LatLon: Optional, ellipsoidal (sub-)class to return the location as (C{LatLon}) or C{None}. @return: Geodetic location B{C{LatLon}} or a L{LatLon2Tuple}C{(lat, lon)} if B{C{LatLon}} is C{None}. @raise TypeError: If B{C{LatLon}} is not ellipsoidal. ''' r = LatLon2Tuple(*self.reverse4(easting, northing)[:2]) if issubclassof(LatLon, _LLEB): r = LatLon(r.lat, r.lon, datum=self.datum) # PYCHOK expected elif LatLon is not None: raise _IsNotError(_LLEB.__name__, LatLon=LatLon) return self._xnamed(r)
def parseDMS2(strLat, strLon, sep=S_SEP, clipLat=90, clipLon=180): '''Parse lat- and longitude representions in degrees. @arg strLat: Latitude in any of several forms (C{str} or C{degrees}). @arg strLon: Longitude in any of several forms (C{str} or C{degrees}). @kwarg sep: Optional separator between deg°, min′ and sec″ (''). @kwarg clipLat: Keep latitude in B{C{-clipLat..+clipLat}} range (C{degrees}). @kwarg clipLon: Keep longitude in B{C{-clipLon..+clipLon}} range (C{degrees}). @return: A L{LatLon2Tuple}C{(lat, lon)} in C{degrees}. @raise ParseError: Invalid B{C{strLat}} or B{C{strLon}}. @raise RangeError: Value of B{C{strLat}} or B{C{strLon}} outside the valid range and L{rangerrors} set to C{True}. @note: See the B{Notes} at function L{parseDMS}. @see: Functions L{parseDDDMMSS}, L{parseDMS} and L{parse3llh}. ''' return LatLon2Tuple(parseDMS(strLat, suffix=_NS_, sep=sep, clip=clipLat), parseDMS(strLon, suffix=_EW_, sep=sep, clip=clipLon))
def toCss(latlon, cs0=_CassiniSoldner0, height=None, Css=Css, name=NN): '''Convert an (ellipsoidal) geodetic point to a Cassini-Soldner location. @arg latlon: Ellipsoidal point (C{LatLon} or L{LatLon4Tuple}). @kwarg cs0: Optional, the Cassini-Soldner projection to use (L{CassiniSoldner}). @kwarg height: Optional height for the point, overriding the default height (C{meter}). @kwarg Css: Optional class to return the location (L{Css}) or C{None}. @kwarg name: Optional B{C{Css}} name (C{str}). @return: The Cassini-Soldner location (B{C{Css}}) or an L{EasNor3Tuple}C{(easting, northing, height)} if B{C{Css}} is C{None}. @raise CSSError: Ellipsoidal mismatch of B{C{latlon}} and B{C{cs0}}. @raise ImportError: Package U{GeographicLib<https://PyPI.org/ project/geographiclib>} missing. @raise TypeError: If B{C{latlon}} is not ellipsoidal. ''' _xinstanceof(_LLEB, LatLon4Tuple, latlon=latlon) cs = _CassiniSoldner(cs0) cs._datumatch(latlon) c = cs.forward4(latlon.lat, latlon.lon) h = latlon.height if height is None else Height(height) if Css is None: r = EasNor3Tuple(c.easting, c.northing, h) else: r = Css(c.easting, c.northing, h=h, cs0=cs) r._latlon = LatLon2Tuple(latlon.lat, latlon.lon) r._azi, r._rk = c.azimuth, c.reciprocal return _xnamed(r, name or nameof(latlon))
def decode_error(geohash): '''Return the relative lat-/longitude decoding errors for this geohash. @param geohash: To be decoded (L{Geohash}). @return: A L{LatLon2Tuple}C{(lat, lon)} with the lat- and longitudinal errors in (C{degrees}). @raise TypeError: The B{C{geohash}} is not a L{Geohash}, C{LatLon} or C{str}. @raise GeohashError: Invalid or null B{C{geohash}}. @example: >>> geohash.decode_error('u120fxw') # 0.00068665, 0.00068665 >>> geohash.decode_error('fur') # 0.703125, 0.703125 >>> geohash.decode_error('fu') # 2.8125, 5.625 >>> geohash.decode_error('f') # 22.5, 22.5 ''' s, w, n, e = bounds(geohash) return LatLon2Tuple((n - s) * 0.5, (e - w) * 0.5)