Store a geohash as an integer in Python¶
Retain precision alongside the integer, or enforce one precision for
the whole column. The value carries no length: "0" and "00"
both encode as zero. Integer storage preserves the geohash exactly with
its original precision; it does not improve coordinate accuracy. Unlike
tile conversion, it has no latitude loss or Web Mercator polar
restriction.
import pygeohash as pgh
value, precision = pgh.geohash_to_int("ezs42"), len("ezs42")
assert pgh.geohash_from_int(value, precision) == "ezs42"
The integer is 14672002. All 1–12-character geohashes fit in at most
60 bits, within a signed SQL BIGINT.
As verified on 2026-10-04, interop is in the development source and is absent from the published 3.5.1 release. To run these examples now, install this pinned source revision (a compiler is needed for the package’s C core):
pip install "pygeohash @ git+https://github.com/wdm0006/pygeohash.git@b0c06ed723a58bab372ac6fbfe8bda953448d637"
Exact storage contract¶
Each base32 character contributes five bits. For precision p the value
is in [0, 2**(5*p)). geohash_from_int requires precision 1–12
and rejects negative values and values that overflow that precision.
Input strings are case-insensitive; restored strings are lowercase:
import pygeohash as pgh
for code in ("EZS42", "00", "zzzzzzzzzzzz"):
assert pgh.geohash_from_int(pgh.geohash_to_int(code), len(code)) == code.lower()
assert pgh.geohash_from_int(0, 2) == "00"
No projection happens. Polar cells round-trip too. If you convert through map tiles or quadkeys first, latitude can shift and polar centres raise by default; integer storage cannot undo that loss.
Smaller keys where the precision justifies them¶
In PostgreSQL
18,
BIGINT uses eight bytes. Its character storage
rules
use one byte of overhead for short strings: a 12-character ASCII geohash
takes 13 bytes of character storage. Eight bytes is a smaller key
payload at that precision, but a five-character string takes six bytes
and can be smaller. Row alignment, index overhead and a per-row
precision field also matter.
ASSUMPTION: smaller payloads at longer precision may reduce index size and improve cache use. Measure your actual index and query plan; no database speedup or percentage reduction is claimed here. Fixed-precision integer keys give numeric range bounds without depending on text collation. String prefix indexes remain a reasonable option.
Turn a prefix into an integer range¶
Choose one stored precision, here six characters. A prefix of length m
and integer value k covers all suffixes in the half-open range
[k << (5*(p-m)), (k+1) << (5*(p-m))):
import pygeohash as pgh
precision = 6
prefix = "ezs"
suffix_bits = 5 * (precision - len(prefix))
k = pgh.geohash_to_int(prefix)
lower, upper = k << suffix_bits, (k + 1) << suffix_bits
assert lower <= pgh.geohash_to_int("ezs42e") < upper
assert not lower <= pgh.geohash_to_int("ezt000") < upper
For a PostgreSQL column deliberately fixed at precision 6:
CREATE TABLE locations (
geohash_value BIGINT NOT NULL
CHECK (geohash_value >= 0 AND geohash_value < 1073741824)
);
CREATE INDEX locations_geohash_idx ON locations (geohash_value);
SELECT geohash_value FROM locations
WHERE geohash_value >= 469499904 AND geohash_value < 469532672;
The range selects the ezs prefix at precision 6. Store the precision
in the schema contract. For mixed precisions, store it per row and
filter on it before using these bounds; identical integers can otherwise
represent different cells. Prefix retrieval selects encoded cells, not
an exact radius or nearest-neighbor result. Apply the appropriate
geometry filter for that application.
For choosing between storage and map grids, see Grid systems.