/p/g1magld5ptgzaktqcexxq0pgq5lmdf0kz6re8hlv/x/daily/geohash/v0
gno.land/p/moul/x/daily/geohash/v0
Geohash: a latitude/longitude pair as a short base32 string with Encode,
Decode, DecodeBox, Neighbor, Neighbors, Box and Direction.
1import "gno.land/p/moul/x/daily/geohash/v0"
2
3h, _ := geohash.Encode(42.6, -5.6, 5) // "ezs42"
4b, _ := geohash.DecodeBox(h) // the cell, not a point
5n, _ := geohash.Neighbor(h, geohash.North) // "ezs48"
A geohash is a prefix code, and that is the whole point of storing one. Every character narrows the box by five bits, so a prefix is a valid geohash of the same place, zoomed out, and two places that share a long prefix are close together. An avl tree keyed by geohash therefore answers "everything near here" with a range scan over a prefix, storing no geometry and computing no distance.
A hash names a box, never a point. Decode returns the centre because the
centre is the best single point a box can give; DecodeBox returns the box
itself with HeightDeg and WidthDeg, which is what you want before claiming
two coordinates are "the same place".
Longitude wraps and latitude does not. The cell east of z is b: the
antimeridian is a line on a map, not an edge of the world. The classic
border/neighbour lookup-table implementations report nothing there, which is a
property of the tables rather than of the globe, so this package differs from
them on purpose and the tests pin both behaviours. A step past a pole is
genuinely impossible and returns ErrNoNeighbor; Neighbors returns a short
list there rather than padding it with empty strings a caller has to filter.
Deterministic by construction. Both world ranges are powers of two over
powers of two, so every box bound is exactly representable in a float64, the
one-cell step Neighbor takes lands on another centre rather than near one,
and nothing is rounded or iterated out of a map. Two identical calls cannot
disagree, which is the bar a Render has to clear.
The alphabet is the standard one and drops a, i, l and o, so a hash
survives being read aloud. Anything outside it is refused rather than skipped,
and so is a hash longer than MaxPrecision (12 characters, 60 bits).
Not included: distances, Encode from a radius, or the 3x3 prefix search a
proximity query would build on top. The box and its neighbours are the
primitives those need.
Live demo: r/moul/x/daily/geohashdemo
· render it at /r/moul/x/daily/geohashdemo/v0.
Part of moul/gno-contracts — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage.
🧪 Highly experimental — potentially vibe-coded. Not audited; may break, change, or be removed at any time. Do not use with anything of value. Full disclaimer: DISCLAIMER.