Coverage for pygeodesy/geohash.py : 96%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: utf-8 -*-
Classes L{Geohash} and L{GeohashError} and several functions to encode, decode and inspect I{geohashes}.
Transcoded from JavaScript originals by I{(C) Chris Veness 2011-2015} and published under the same MIT Licence**, see U{Geohashes <https://www.Movable-Type.co.UK/scripts/geohash.html>}.
See also U{Geohash<https://WikiPedia.org/wiki/Geohash>}, U{Geohash<https://GitHub.com/vinsci/geohash>}, U{PyGeohash<https://PyPI.org/project/pygeohash>} and U{Geohash-Javascript<https://GitHub.com/DaveTroy/geohash-js>}. '''
_360_0, _90_0, _N_90_0, _N_180_0 # PYCHOK used! equirectangular, euclidean, haversine, vincentys _S_, _SE_, _SW_, _W_ LatLon2Tuple, PhiLam2Tuple deprecated_property_RO, Property_RO _xStrError
'''(INTERNAL) Lazily defined constants. ''' E=(e, n), W=(w, s))
# Geohash-specific base32 map
'bc01fg45238967deuvhjyznpkmstqrwx', '14365h7k9dcfesgujnmqp0r2twvyx8zb', '238967debc01fg45kmstqrwxuvhjyznp')
# ... where radial = sqrt(latSize * lonWidth / PI) _floatuple( 5003e3, 5000e3, 2821794.075), # 1 _floatuple( 650e3, 1225e3, 503442.397), # 2 _floatuple( 156e3, 156e3, 88013.575), # 3 _floatuple( 19500, 39100, 15578.683), # 4 _floatuple( 4890, 4890, 2758.887), # 5 _floatuple( 610, 1220, 486.710), # 6 _floatuple( 153, 153, 86.321), # 7 _floatuple( 19.1, 38.2, 15.239), # 8 _floatuple( 4.77, 4.77, 2.691), # 9 _floatuple( 0.596, 1.19, 0.475), # 10 _floatuple( 0.149, 0.149, 0.084), # 11 _floatuple( 0.0186, 0.0372, 0.015)) # 12 _MaxPrec
'''(INTERNAL) Return SW and NE bounds. ''' else:
'''(INTERNAL) Return the C{bounds} center. ''' favg(bounds.lonE, bounds.lonW))
'''(INTERNAL) Convert lat, lon to 2-tuple of floats. ''' # lat, lon = parseDMS2(lat, lon) Lon(lon, Error=GeohashError))
'''(INTERNAL) Check or create a Geohash instance. ''' Geohash(geohash)
'''(INTERNAL) Check a geohash string. ''' raise ValueError raise ValueError except (AttributeError, TypeError, ValueError) as x: raise GeohashError(Geohash.__name__, geohash, txt=str(x))
'''Geohash class, a named C{str}. ''' # no str.__init__ in Python 3 '''New L{Geohash} from an other L{Geohash} instance or C{str} or from a C{LatLon} instance or C{str}.
@arg cll: Cell or location (L{Geohash}, C{LatLon} or C{str}). @kwarg precision: Optional, the desired geohash length (C{int} 1..12), see function L{geohash.encode} for some examples. @kwarg name: Optional name (C{str}).
@return: New L{Geohash}.
@raise GeohashError: INValid or non-alphanumeric B{C{cll}}.
@raise TypeError: Invalid B{C{cll}}. '''
else:
else: # assume LatLon except AttributeError: raise _xStrError(Geohash, cll=cll, Error=GeohashError)
'''DEPRECATED, use property C{philam}.''' return self.philam
'''Determine the adjacent cell in given compass direction.
@arg direction: Compass direction ('N', 'S', 'E' or 'W'). @kwarg name: Optional name (C{str}), otherwise the name of this cell plus C{.D}irection.
@return: Geohash of adjacent cell (L{Geohash}).
@raise GeohashError: Invalid geohash or B{C{direction}}. ''' # based on <https://GitHub.com/DaveTroy/geohash-js>
raise GeohashError(direction=direction)
raise GeohashError(geohash=self)
# check for edge-cases which don't share common prefix
n = _DOT_(n, D) # append letter for direction to parent
'''(INTERNAL) Cache for L{bounds}. '''
'''Return the lower-left SW and upper-right NE bounds of this geohash cell.
@kwarg LatLon: Optional class to return I{bounds} (C{LatLon}) or C{None}. @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword arguments, ignored if B{C{LatLon}} is C{None}.
@return: A L{Bounds2Tuple}C{(latlonSW, latlonNE)} of B{C{LatLon}}s or a L{Bounds4Tuple}C{(latS, lonW, latN, lonE)} if C{B{LatLon} is None}, ''' _2bounds(LatLon, LatLon_kwds, *r, name=self.name)
'''(INTERNAL) Helper for distances, see C{formy._distanceTo*}. '''
'''Estimate the distance between this and an other geohash based the cell sizes.
@arg other: The other geohash (L{Geohash}, C{LatLon} or C{str}).
@return: Approximate distance (C{meter}).
@raise TypeError: The B{C{other}} is not a L{Geohash}, C{LatLon} or C{str}. '''
def distance1To(self, other): # PYCHOK no cover '''DEPRECATED, use method L{distanceTo}.''' return self.distanceTo(other)
def distance2To(self, other, radius=R_M, adjust=False, wrap=False): # PYCHOK no cover '''DEPRECATED, use method L{equirectangularTo}.''' return self.equirectangularTo(other, radius=radius, adjust=adjust, wrap=wrap)
def distance3To(self, other, radius=R_M, wrap=False): # PYCHOK no cover '''DEPRECATED, use method L{haversineTo}.''' return self.haversineTo(other, radius=radius, wrap=wrap)
'''Approximate the distance between this and an other geohash using function L{pygeodesy.equirectangular}.
@arg other: The other geohash (L{Geohash}, C{LatLon} or C{str}). @kwarg radius: Mean earth radius, ellipsoid or datum (C{meter}, L{Ellipsoid}, L{Ellipsoid2}, L{Datum} or L{a_f2Tuple}) or C{None}. @kwarg adjust: Adjust the wrapped, unrolled longitudinal delta by the cosine of the mean latitude C{bool}). @kwarg wrap: Wrap and unroll longitudes (C{bool}).
@return: Distance (C{meter}, same units as B{C{radius}} or the ellipsoid or datum axes or C{radians I{squared}} if B{C{radius}} is C{None} or C{0}).
@raise TypeError: The B{C{other}} is not a L{Geohash}, C{LatLon} or C{str} or invalid B{C{radius}}.
@see: U{Local, flat earth approximation <https://www.EdWilliams.org/avform.htm#flat>}, functions ''' _equirectangular_(*lls, **kwds).distance2
'''Approximate the distance between this and an other geohash using function L{pygeodesy.euclidean}.
@arg other: The other geohash (L{Geohash}, C{LatLon} or C{str}). @kwarg radius: Mean earth radius, ellipsoid or datum (C{meter}, L{Ellipsoid}, L{Ellipsoid2}, L{Datum} or L{a_f2Tuple}). @kwarg adjust: Adjust the wrapped, unrolled longitudinal delta by the cosine of the mean latitude C{bool}). @kwarg wrap: Wrap and unroll longitudes (C{bool}).
@return: Distance (C{meter}, same units as B{C{radius}} or the ellipsoid or datum axes).
@raise TypeError: The B{C{other}} is not a L{Geohash}, C{LatLon} or C{str} or invalid B{C{radius}}. ''' return self._distanceTo(euclidean, other, radius=radius, adjust=adjust, wrap=wrap)
'''Compute the distance between this and an other geohash using the L{pygeodesy.haversine} formula.
@arg other: The other geohash (L{Geohash}, C{LatLon} or C{str}). @kwarg radius: Mean earth radius, ellipsoid or datum (C{meter}, L{Ellipsoid}, L{Ellipsoid2}, L{Datum} or L{a_f2Tuple}). @kwarg wrap: Wrap and unroll longitudes (C{bool}).
@return: Distance (C{meter}, same units as B{C{radius}} or the ellipsoid or datum axes).
@raise TypeError: The B{C{other}} is not a L{Geohash}, C{LatLon} or C{str} or invalid B{C{radius}}. '''
'''Get the lat- and longitude of (the approximate center of) this geohash as a L{LatLon2Tuple}C{(lat, lon)} in C{degrees}. '''
'''Get all 8 adjacent cells as a L{Neighbors8Dict}C{(N, NE, E, SE, S, SW, W, NW)} of L{Geohash}es. ''' S=self.S, SW=self.SW, W=self.W, NW=self.NW, name=self.name)
'''Get the lat- and longitude of (the approximate center of) this geohash as a L{PhiLam2Tuple}C{(phi, lam)} in C{radians}. '''
'''Get this geohash's precision (C{int}). '''
'''Get the lat- and longitudinal size of this cell as a L{LatLon2Tuple}C{(lat, lon)} in (C{meter}). '''
'''Return (the approximate center of) this geohash cell as an instance of the supplied C{LatLon} class.
@arg LatLon: Class to use (C{LatLon}) or C{None}. @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword arguments, ignored if C{B{LatLon} is None}.
@return: This geohash location (B{C{LatLon}}) or a L{LatLon2Tuple}C{(lat, lon)} if B{C{LatLon}} is C{None}.
@raise TypeError: Invalid B{C{LatLon}} or B{C{LatLon_kwds}}.
@example:
>>> from sphericalTrigonometry import LatLon >>> ll = Geohash('u120fxw').toLatLon(LatLon) >>> print(repr(ll)) # LatLon(52°12′17.9″N, 000°07′07.64″E) >>> print(ll) # 52.204971°N, 000.11879°E ''' *self.latlon, **LatLon_kwds), self.name)
'''Compute the distance between this and an other geohash using the L{pygeodesy.vincentys} formula.
@arg other: The other geohash (L{Geohash}, C{LatLon} or C{str}). @kwarg radius: Mean earth radius, ellipsoid or datum (C{meter}, L{Ellipsoid}, L{Ellipsoid2}, L{Datum} or L{a_f2Tuple}). @kwarg wrap: Wrap and unroll longitudes (C{bool}).
@return: Distance (C{meter}, same units as B{C{radius}} or the ellipsoid or datum axes).
@raise TypeError: The B{C{other}} is not a L{Geohash}, C{LatLon} or C{str} or invalid B{C{radius}}. ''' return self._distanceTo(vincentys, other, radius=radius, wrap=wrap)
'''Get the cell North of this (L{Geohash}). '''
'''Get the cell South of this (L{Geohash}). '''
'''Get the cell East of this (L{Geohash}). '''
'''Get the cell West of this (L{Geohash}). '''
'''Get the cell NorthEast of this (L{Geohash}). '''
'''Get the cell NorthWest of this (L{Geohash}). '''
'''Get the cell SouthEast of this (L{Geohash}). '''
'''Get the cell SouthWest of this (L{Geohash}). '''
'''Geohash encode, decode or other L{Geohash} issue. '''
'''8-Dict C{(N, NE, E, SE, S, SW, W, NW)} of L{Geohash}es, providing key I{and} attribute access to the items. '''
len(Neighbors8Dict._Keys_))) # XXX frozendict
'''Returns the lower-left SW and upper-right NE corners of a geohash.
@arg geohash: To be bound (L{Geohash}). @kwarg LatLon: Optional class to return the bounds (C{LatLon}) or C{None}. @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword arguments, ignored if C{B{LatLon} is None}.
@return: A L{Bounds2Tuple}C{(latlonSW, latlonNE)} of B{C{LatLon}}s or if B{C{LatLon}} is C{None}, a L{Bounds4Tuple}C{(latS, lonW, latN, lonE)}.
@raise TypeError: The B{C{geohash}} is not a L{Geohash}, C{LatLon} or C{str} or invalid B{C{LatLon}} or invalid B{C{LatLon_kwds}}.
@raise GeohashError: Invalid or C{null} B{C{geohash}}.
@example:
>>> geohash.bounds('u120fxw') # 52.20428467, 0.11810303, 52.20565796, 0.11947632 >>> geohash.decode('u120fxw') # '52.205', '0.1188' ''' raise GeohashError(geohash=geohash)
else: else: # latitude else: except KeyError: raise GeohashError(geohash=geohash)
name=nameof(geohash))
'''(INTERNAL) Return 3-tuple C{(bounds, height, width)}. '''
'''Decode a geohash to lat-/longitude of the (approximate centre of) geohash cell to reasonable precision.
@arg geohash: To be decoded (L{Geohash}).
@return: 2-Tuple C{(latStr, lonStr)}, both C{str}.
@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('u120fxw') # '52.205', '0.1188' >>> geohash.decode('sunny') # '23.708', '42.473' Saudi Arabia >>> geohash.decode('fur') # '69.6', '-45.7' Greenland >>> geohash.decode('reef') # '-24.87', '162.95' Coral Sea >>> geohash.decode('geek') # '65.48', '-17.75' Iceland '''
# round to near centre without excessive precision to # ⌊2-log10(Δ°)⌋ decimal places, strip trailing zeros fstr(lon, prec=int(2 - log10(w)))) # strs!
'''Decode a geohash to lat-/longitude of the (approximate centre of) geohash cell to reasonable precision.
@arg geohash: To be decoded (L{Geohash}). @kwarg LatLon: Optional class to return the location (C{LatLon}) or C{None}. @kwarg LatLon_kwds: Optional, addtional B{C{LatLon}} keyword arguments, ignored if C{B{LatLon} is None}.
@return: L{LatLon2Tuple}C{(lat, lon)}, both C{degrees} if C{B{LatLon} is None}, otherwise a B{C{LatLon}} instance.
@raise TypeError: The B{C{geohash}} is not a L{Geohash}, C{LatLon} or C{str}.
@raise GeohashError: Invalid or null B{C{geohash}}. '''
'''Return the relative lat-/longitude decoding errors for this geohash.
@arg 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 ''' w * _0_5) # Width error
'''Estimate the distance between two geohash (from the cell sizes).
@arg geohash1: First geohash (L{Geohash}, C{LatLon} or C{str}). @arg geohash2: Second geohash (L{Geohash}, C{LatLon} or C{str}).
@return: Approximate distance (C{meter}).
@raise TypeError: If B{C{geohash1}} or B{C{geohash2}} is not a L{Geohash}, C{LatLon} or C{str}.
@example:
>>> geohash.distance_('u120fxwsh', 'u120fxws0') # 15.239 '''
'''DEPRECATED, used L{geohash.distance_}.''' return distance_(geohash1, geohash2)
'''DEPRECATED, used L{geohash.equirectangular_}.''' return equirectangular_(geohash1, geohash2)
'''DEPRECATED, used L{geohash.haversine_}.''' return haversine_(geohash1, geohash2)
'''Encode a lat-/longitude as a C{geohash}, either to the specified precision or if not provided, to an automatically evaluated precision.
@arg lat: Latitude (C{degrees}). @arg lon: Longitude (C{degrees}). @kwarg precision: Optional, the desired geohash length (C{int} 1..12).
@return: The C{geohash} (C{str}).
@raise GeohashError: Invalid B{C{lat}}, B{C{lon}} or B{C{precision}}.
@example:
>>> geohash.encode(52.205, 0.119, 7) # 'u120fxw' >>> geohash.encode(52.205, 0.119, 12) # 'u120fxwshvkg' >>> geohash.encode(52.205, 0.1188, 12) # 'u120fxws0jre' >>> geohash.encode(52.205, 0.1188) # 'u120fxw' >>> geohash.encode( 0, 0) # 's00000000000' '''
# Infer precision by refining geohash until # it matches precision of supplied lat/lon. abs(lon - ll[1]) < EPS: else:
else: else: # bisect latitude else:
# 5 bits gives a character: # append it and start over
'''Approximate the distance between two geohashes using the L{pygeodesy.equirectangular} formula.
@arg geohash1: First geohash (L{Geohash}, C{LatLon} or C{str}). @arg geohash2: Second geohash (L{Geohash}, C{LatLon} or C{str}). @kwarg radius: Mean earth radius (C{meter}) or C{None}.
@return: Approximate distance (C{meter}, same units as B{C{radius}}).
@raise TypeError: If B{C{geohash1}} or B{C{geohash2}} is not a L{Geohash}, C{LatLon} or C{str}.
@example:
>>> geohash.equirectangular_('u120fxwsh', 'u120fxws0') # 19.0879 '''
'''Compute the great-circle distance between two geohashes using the L{pygeodesy.haversine} formula.
@arg geohash1: First geohash (L{Geohash}, C{LatLon} or C{str}). @arg geohash2: Second geohash (L{Geohash}, C{LatLon} or C{str}). @kwarg radius: Mean earth radius (C{meter}).
@return: Great-circle distance (C{meter}, same units as B{C{radius}}).
@raise TypeError: If B{C{geohash1}} or B{C{geohash2}} is not a L{Geohash}, C{LatLon} or C{str}.
@example:
>>> geohash.haversine_('u120fxwsh', 'u120fxws0') # 11.6978 '''
'''Return the L{Geohash}es for all 8 adjacent cells.
@arg geohash: Cell for which neighbors are requested (L{Geohash} or C{str}).
@return: A L{Neighbors8Dict}C{(N, NE, E, SE, S, SW, W, NW)} of L{Geohash}es.
@raise TypeError: The B{C{geohash}} is not a L{Geohash}, C{LatLon} or C{str}. '''
'''Determine the L{Geohash} precisions to meet a or both given (geographic) resolutions.
@arg res1: The required primary I{(longitudinal)} resolution (C{degrees}). @kwarg res2: Optional, required secondary I{(latitudinal)} resolution (C{degrees}).
@return: The L{Geohash} precision or length (C{int}, 1..12).
@raise GeohashError: Invalid B{C{res1}} or B{C{res2}}.
@see: C++ class U{Geohash <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Geohash.html>}. ''' t = r, r for p in range(1, _MaxPrec): if resolution2(p, None) <= t: return p
else:
'''2-Tuple C{(res1, res2)} with the primary I{(longitudinal)} and secondary I{(latitudinal)} resolution, both in C{degrees}. '''
'''Determine the (geographic) resolutions of given L{Geohash} precisions.
@arg prec1: The given primary I{(longitudinal)} precision (C{int} 1..12). @kwarg prec2: Optional, secondary I{(latitudinal)} precision (C{int} 1..12).
@return: L{Resolutions2Tuple}C{(res1, res2)} with the (geographic) resolutions C{degrees}, where C{res2} B{C{is}} C{res1} if no B{C{prec2}} is given.
@raise GeohashError: Invalid B{C{prec1}} or B{C{prec2}}.
@see: I{Karney}'s C++ class U{Geohash <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Geohash.html>}. '''
'''Return the lat- and longitudinal size of this L{Geohash} cell.
@arg geohash: Cell for which size are required (L{Geohash} or C{str}).
@return: A L{LatLon2Tuple}C{(lat, lon)} with the latitudinal height and longitudinal width in (C{meter}).
@raise TypeError: The B{C{geohash}} is not a L{Geohash}, C{LatLon} or C{str}. '''
decode, decode2, decode_error, distance_, encode, equirectangular_, haversine_, neighbors, precision, resolution2, sizes)
# **) MIT License # # Copyright (C) 2016-2022 -- mrJean1 at Gmail -- All Rights Reserved. # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. |