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 -*-
encode, decode and inspect I{geohashes}.
Transcribed 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>}.
@newfield example: Example, Examples '''
_NamedStr, Neighbors8Dict
# all public contants, classes and functions 'decode', 'decode_error', 'distance1', 'distance2', 'distance3', 'encode', 'neighbors', 'precision', 'resolution2', 'sizes')
N=('prxz', 'bcfguvyz'), S=('028b', '0145hjnp'), E=('bcfguvyz', 'prxz'), W=('0145hjnp', '028b'))
N=('p0r21436x8zb9dcf5h7kjnmqesgutwvy', 'bc01fg45238967deuvhjyznpkmstqrwx'), S=('14365h7k9dcfesgujnmqp0r2twvyx8zb', '238967debc01fg45kmstqrwxuvhjyznp'), E=('bc01fg45238967deuvhjyznpkmstqrwx', 'p0r21436x8zb9dcf5h7kjnmqesgutwvy'), W=('238967debc01fg45kmstqrwxuvhjyznp', '14365h7k9dcfesgujnmqp0r2twvyx8zb'))
# lat-, longitudinal and radial cell size (in meter) (20032e3, 20000e3, 11292815.096), # 0 ( 5003e3, 5000e3, 2821794.075), # 1 ( 650e3, 1225e3, 503442.397), # 2 ( 156e3, 156e3, 88013.575), # 3 ( 19500, 39100, 15578.683), # 4 ( 4890, 4890, 2758.887), # 5 ( 610, 1220, 486.710), # 6 ( 153, 153, 86.321), # 7 ( 19.1, 38.2, 15.239), # 8 ( 4.77, 4.77, 2.691), # 9 ( 0.596, 1.19, 0.475), # 10 ( 0.149, 0.149, 0.084), # 11 ( 0.0186, 0.0372, 0.015)) # 12 _MaxPrec
# Geohash-specific base32 map # ... and the inverse map
'''(INTERNAL) return SW and NE bounds. ''' Bounds2Tuple(LatLon(s, w, **kwds), LatLon(n, e, **kwds)) # PYCHOK inconsistent
'''(INTERNAL) Convert lat, lon to 2-tuple of floats. '''
'''(INTERNAL) Check or create a Geohash instance. ''' except (TypeError, ValueError): raise _IsNotError(Geohash.__name__, str.__name__, 'LatLon', geohash=geohash)
'''(INTERNAL) Check a geohash string. ''' raise ValueError raise ValueError except (AttributeError, TypeError, ValueError): raise GeohashError('%s: %r[%s]' % (Geohash.__name__, geohash, len(geohash)))
'''Geohash encode, decode or other L{Geohash} issue. '''
'''Geohash class, a L{_NamedStr}. '''
# 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}.
@param cll: Cell or location (L{Geohash} or C{str}, C{LatLon} or C{str}). @keyword precision: Optional, the desired geohash length (C{int} 1..12), see function L{geohash.encode} for examples. @keyword name: Optional name (C{str}).
@return: New L{Geohash}.
@raise TypeError: Invalid B{C{cll}}.
@raise GeohashError: INValid or non-alphanumeric B{C{cll}}. '''
else:
else: # assume LatLon except AttributeError: raise TypeError('%s: %r' % (Geohash.__name__, cll))
self.name = name
def ab(self): '''Get the lat- and longitude of (the approximate center of) this geohash as a 2-tuple (lat, lon) in C{radians}. '''
'''Determine the adjacent cell in given compass direction.
@param direction: Compass direction ('N', 'S', 'E' or 'W').
@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('%s invalid: %s' % ('direction', direction))
raise GeohashError('%s invalid: %s' % ('geohash', self))
# check for edge-cases which don't share common prefix
# append letter for direction to parent
'''Return the lower-left SW and upper-right NE bounds of this geohash cell.
@keyword LatLon: Optional (sub-)class to return I{bounds} (C{LatLon}) or C{None}. @keyword kwds: Optional keyword arguments for I{LatLon}.
@return: A L{Bounds2Tuple}C{(latlonSW, latlonNE)} as B{C{LatLon}} or a L{Bounds4Tuple}C{(latS, lonW, latN, lonE)} if B{C{LatLon}} is C{None}. '''
'''DEPRECATED, use method C{distance1To}. '''
'''Estimate the distance between this and an other geohash (from the cell sizes).
@param other: The other geohash (L{Geohash}).
@return: Approximate distance (C{meter}).
@raise TypeError: The B{C{other}} is not a L{Geohash}, C{LatLon} or C{str}. '''
break
'''DEPRECATED, use method C{distance2To}. '''
'''Compute the distance between this and an other geohash using the U{Equirectangular Approximation / Projection <https://www.Movable-Type.co.UK/scripts/latlong.html>}.
@param other: The other geohash (L{Geohash}). @keyword radius: Optional, mean earth radius (C{meter}) or C{None}. @keyword adjust: Adjust the wrapped, unrolled longitudinal delta by the cosine of the mean latitude (C{bool}). @keyword wrap: Wrap and unroll longitudes (C{bool}).
@return: Approximate distance (C{meter}, same units as I{radius}) or distance squared (C{degrees} squared) if I{radius} is C{None} or 0.
@raise TypeError: The I{other} is not a L{Geohash}, C{LatLon} or C{str}.
@see: U{Local, flat earth approximation <https://www.EdWilliams.org/avform.htm#flat>}, functions '''
adjust=adjust, limit=None, wrap=wrap) else: return equirectangular_(a1, b1, a2, b2, adjust=adjust, limit=None, wrap=wrap)[0]
'''DEPRECATED, use method C{distance3To}. '''
'''Compute the great-circle distance between this and an other geohash using the U{Haversine <https://www.Movable-Type.co.UK/scripts/latlong.html>} formula.
@param other: The other geohash (L{Geohash}). @keyword radius: Optional, mean earth radius (C{meter}). @keyword wrap: Wrap and unroll longitudes (C{bool}).
@return: Great-circle distance (C{meter}, same units as I{radius}).
@raise TypeError: The I{other} is not a L{Geohash}, C{LatLon} or C{str}. '''
def latlon(self): '''Get the lat- and longitude of (the approximate center of) this geohash as a 2-tuple (lat, lon) in C{degrees}.
B{Example:}
>>> geohash.Geohash('geek').latlon # 65.478515625, -17.75390625 >>> geohash.decode('geek') # '65.48', '-17.75' ''' # B{Example:} not @example: since that causes Epydoc error
def neighbors(self): '''Get all 8 adjacent cells as a L{Neighbors8Dict}C{(N, NE, E, SE, S, SW, W, NW)} of L{Geohash}es.
B{JSname:} I{neighbours}. ''' S=self.S, SW=self.SW, W=self.W, NW=self.NW)
def precision(self): '''Get this geohash's precision (C{int}). '''
def sizes(self): '''Get the lat- and longitudinal size of this cell as a L{LatLon2Tuple}C{(lat, lon)} with the latitudinal height and longitudinal width in (C{meter}). '''
'''Return (the approximate center of) this geohash cell as an instance of the supplied C{LatLon} class.
@param LatLon: Class to use (C{LatLon}). @keyword kwds: Optional keyword arguments for B{C{LatLon}}.
@return: This geohash location (B{C{LatLon}}).
@raise GeohashError: B{C{LatLon}} invalid.
@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 ''' raise GeohashError('%s invalid: %r' % ('LatLon', LatLon))
def N(self): '''Get the cell North of this (L{Geohash}). '''
def S(self): '''Get the cell South of this (L{Geohash}). '''
def E(self): '''Get the cell East of this (L{Geohash}). '''
def W(self): '''Get the cell West of this (L{Geohash}). '''
def NE(self): '''Get the cell NorthEast of this (L{Geohash}). '''
def NW(self): '''Get the cell NorthWest of this (L{Geohash}). '''
def SE(self): '''Get the cell SouthEast of this (L{Geohash}). '''
def SW(self): '''Get the cell SouthWest of this (L{Geohash}). '''
'''Returns the lower-left SW and upper-right NE corners of a geohash.
@param geohash: To be bound (L{Geohash}). @keyword LatLon: Optional (sub-)class to return the bounds (C{LatLon}) or C{None}. @keyword kwds: Optional keyword arguments for B{C{LatLon}}.
@return: A L{Bounds2Tuple}C{(latlonSW, latlonNE)} as B{C{LatLon}} or a L{Bounds4Tuple}C{(latS, lonW, latN, lonE)} if B{C{LatLon}} is C{None}.
@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.bounds('u120fxw') # 52.20428467, 0.11810303, # 52.20565796, 0.11947632 >>> geohash.decode('u120fxw') # '52.205', '0.1188' ''' raise GeohashError('%s invalid: %s' % ('geohash', geohash))
except KeyError: raise GeohashError('%s invalid: %s' % ('geohash', geohash))
else: else: # latitude else:
'''Decode a geohash to lat-/longitude of the (approximate centre of) geohash cell, to reasonable precision.
@param geohash: To be decoded (L{Geohash}).
@return: 2-Tuple C{"(latStr, lonStr)"} in (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 # ⌊2-log10(Δ°)⌋ decimal places, strip trailing zeros
'''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 '''
'''Estimate the distance between two geohash (from the cell sizes).
@param geohash1: First geohash (L{Geohash}). @param geohash2: Second geohash (L{Geohash}).
@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.distance1('u120fxwsh', 'u120fxws0') # 15.239 '''
'''Approximate the distance between two geohashes (with Pythagoras' theorem).
@param geohash1: First geohash (L{Geohash}). @param geohash2: Second geohash (L{Geohash}). @keyword radius: Optional, 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.distance2('u120fxwsh', 'u120fxws0') # 19.0879 '''
'''Compute the great-circle distance between two geohashes (using the Haversine formula).
@param geohash1: First geohash (L{Geohash}). @param geohash2: Second geohash (L{Geohash}). @keyword radius: Optional, 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.distance3('u120fxwsh', 'u120fxws0') # 11.6978 '''
'''Encode a lat-/longitude as a C{geohash}, either to the specified precision or if not provided, to an automatically evaluated precision.
@param lat: Latitude (C{degrees}). @param lon: Longitude (C{degrees}). @keyword 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' '''
raise ValueError except (TypeError, ValueError): raise GeohashError('%s invalid: %r' % ('precision', precision)) else: # Infer precision by refining geohash until # it matches precision of supplied lat/lon. abs(lon - ll[1]) < EPS: p = _MaxPrec
else: else: # bisect latitude else:
# 5 bits gives a character: # append it and start over
'''Return the L{Geohash}es for all 8 adjacent cells.
@param 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}.
@JSname: I{neighbours}. '''
'''Determine the L{Geohash} precisions to meet a given (geographic) resolutions.
@param res1: The required, primary (longitudinal) resolution (C{degrees}). @keyword res2: Optional, required, secondary (latitudinal resolution (C{degrees}).
@return: The L{Geohash} precision or length (C{int} 1..12).
@see: C++ class U{Geohash <https://GeographicLib.SourceForge.io/html/classGeographicLib_1_1Geohash.html>}. '''
'''Determine the (geographic) resolutions of given L{Geohash} precisions.
@param prec1: The given primary (longitudinal) precision (C{int} 1..12). @keyword prec2: Optional, secondary (latitudinal) precision (C{int} 1..12).
@return: 2-Tuple (C{res1, res2}) with the (geographic) resolutions (C{degrees}) where C{res2} is C{res1} if no I{prec2} is given.
@see: C++ class U{Geohash <https://GeographicLib.SourceForge.io/html/classGeographicLib_1_1Geohash.html>}. '''
res2 = res1
'''Return the lat- and longitudinal size of this L{Geohash} cell.
@param 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}. '''
# **) MIT License # # Copyright (C) 2016-2020 -- 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. |