1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
|
# Note: the alphabet in geohash differs from the common base32
# alphabet described in IETF's RFC 4648
# (http://tools.ietf.org/html/rfc4648)
from typing import Tuple
base32 = "0123456789bcdefghjkmnpqrstuvwxyz"
decodemap = {base32[i]: i for i in range(len(base32))}
def geo_decode(geohash: str) -> Tuple[float, float, float, float]:
"""
Decode the geohash to its exact values, including the error margins of the result. Returns four float values:
latitude, longitude, the plus/minus error for latitude (as a positive number) and the plus/minus error for longitude
(as a positive number).
"""
lat_interval, lon_interval = (-90.0, 90.0), (-180.0, 180.0)
lat_err, lon_err = 90.0, 180.0
is_longitude = True
for c in geohash:
cd = decodemap[c]
for mask in [16, 8, 4, 2, 1]:
if is_longitude: # adds longitude info
lon_err /= 2
if cd & mask:
lon_interval = (
(lon_interval[0] + lon_interval[1]) / 2,
lon_interval[1],
)
else:
lon_interval = (
lon_interval[0],
(lon_interval[0] + lon_interval[1]) / 2,
)
else: # adds latitude info
lat_err /= 2
if cd & mask:
lat_interval = (
(lat_interval[0] + lat_interval[1]) / 2,
lat_interval[1],
)
else:
lat_interval = (
lat_interval[0],
(lat_interval[0] + lat_interval[1]) / 2,
)
is_longitude = not is_longitude
lat = (lat_interval[0] + lat_interval[1]) / 2
lon = (lon_interval[0] + lon_interval[1]) / 2
return lat, lon, lat_err, lon_err
def geo_encode(latitude: float, longitude: float, precision: int = 12) -> str:
"""
Encode a position given in float arguments latitude, longitude to a geohash which will have the character count
precision.
"""
lat_interval, lon_interval = (-90.0, 90.0), (-180.0, 180.0)
geohash, bits = [], [16, 8, 4, 2, 1] # type: ignore
bit, ch = 0, 0
is_longitude = True
def next_interval(curr: float, interval: Tuple[float, float], ch: int) -> Tuple[Tuple[float, float], int]:
mid = (interval[0] + interval[1]) / 2
if curr > mid:
ch |= bits[bit]
return (mid, interval[1]), ch
else:
return (interval[0], mid), ch
while len(geohash) < precision:
if is_longitude:
lon_interval, ch = next_interval(longitude, lon_interval, ch)
else:
lat_interval, ch = next_interval(latitude, lat_interval, ch)
is_longitude = not is_longitude
if bit < 4:
bit += 1
else:
geohash += base32[ch]
bit = 0
ch = 0
return "".join(geohash)
|