S2 & H3
From One Machine to the Whole Planet
Look back at the three structures we've built. Geohash was a string — dead simple, and it shards across a thousand machines by prefix without a second thought. But it had two real flaws: its Z-order curve jumps (two points a stone's throw apart, on opposite sides of a seam, get codes with nothing in common), and its flat latitude/longitude grid stretches badly toward the poles, so a cell near the Arctic covers a wildly different area than one at the equator. Quadtrees and R-trees fixed the geometry — they adapt to the data — but they're trees of pointers that live on one machine; sharding them across a cluster is real work.
Planet-scale companies want both halves at once: a geohash's effortless distribution and geometry that doesn't fall apart. So Google and Uber each went back to the geohash's core idea — turn a location into a single number a B-tree can index — and rebuilt it properly. The recipe is the same for both: wrap the round Earth around a 3-D solid, lay a hierarchy of cells on its faces, and give every cell a 64-bit integer ID. A 64-bit ID is just a number — it drops into any database column, any key-value store, and shards as easily as a geohash — but now the cells sit on a sphere-aware grid with a locality-preserving order. Two flaws, fixed.
Where they part ways is the shape of the bet. Google's S2 tiles with squares and threads them with a smarter curve. Uber's H3 tiles with hexagons. Those two choices — a better curve versus a better shape — fall out of two different jobs, and between them they finish the spatial toolkit.

S2: A Hilbert Curve on a Cube
Google's S2 starts by fixing the geohash's projection problem. Instead of a flat lat/lng grid, it projects the sphere onto the six faces of a cube and grids each face. Unfolding a cube keeps cells far more uniform in size than a flat map that smears toward the poles — the first flaw, gone.
Each cube face is then a quadtree: divide it into four squares, each of those into four, and so on for 30 levels. Because it's a clean four-way split, the cells nest exactly — a level-18 cell sits perfectly inside its level-12 parent, which sits inside its face. The levels are your precision dial: a whole face at level 0, roughly 3 km at level 12, about 38 m at level 18, down to roughly 1 cm at level 30. Each cell gets a 64-bit ID: a 3-bit face number, then a pair of bits per level choosing which of the four children, then a marker bit — so the length of the meaningful prefix is the level, exactly like a geohash but as an integer.
Now the clever part, and the one that pays off Geohash's cliffhanger. To order the cells, S2 doesn't use the Z-order curve — it uses a Hilbert curve. Both are space-filling curves that thread a single line through the grid, but the Hilbert curve has a property the Z-order lacks: it never jumps. Every step from one ID to the next lands on a touching cell. Measure it and the difference is stark — and it grows with scale. Between consecutive codes the Hilbert curve moves at most 1 cell at any zoom level, while the Z-order curve's longest leap balloons with the grid: 7 cells on an 8×8, 63 on a 64×64, and unboundedly far on S2's real 2³⁰-per-side grid — nearby points flung to the far side of the map. That's the geohash seam, gone.
Why does that matter in practice? Because a region — "everything in this neighborhood" — becomes a set of contiguous ID ranges (an S2 covering), and each range is one fast B-tree scan. The smoother the curve, the fewer ranges you need: covering a small box took about 3.9 ranges with the Hilbert curve versus 6.3 with Z-order — fewer scans for the same area. And because the exact nesting makes every descendant of a cell fall in a contiguous run of IDs, the question "is this point inside that region?" collapses to an integer range check — no geometry math for the coarse pass, just low ≤ id ≤ high. That efficiency is why S2 is the choice when the job is indexing and geometry: Google Maps runs on it, MongoDB's 2dsphere and CockroachDB use it, and Foursquare rebuilt its entire venue database on S2 in 2018, replacing geohashing for large gains in query speed and reliability.

H3: Hexagons All the Way Down
Uber's H3 makes a different bet, and it's all about one deceptively simple question: when you step from a cell to a neighbor, how far did you go?
On a square grid, there's no single answer. Your four edge-neighbors are one step away, but your four corner-neighbors are about 1.41 steps away — 41% farther. Two different distances. A triangle grid is worse: three. But a hexagon has exactly six neighbors, and all six are the same distance away. One distance, always. That sounds like a small thing and it's enormous: it means "grow the search outward by one ring," "measure how demand flows from this cell to the next," and "smooth a heatmap across cells" are all uniform and deterministic — no awkward corners that are secretly farther. For a company whose whole business is modeling movement — riders, drivers, supply, demand, surge — that uniformity is exactly what you want. So H3 tiles the Earth with hexagons, and its cell IDs (64-bit again) are the sharding key Uber's surge-pricing system uses to bucket supply and demand, city by city.
Hexagons come with a bill, though, and H3 pays it honestly. You cannot tile a sphere with hexagons alone — it's a geometric impossibility. H3 wraps the Earth as an icosahedron (a 20-sided solid) and is forced to place exactly 12 pentagons at its corners, surrounded by hexagons — 122 cells at the coarsest resolution (12 pentagons + 110 hexagons), tucked over the oceans where they cause the least trouble. And unlike S2's exact squares, H3's levels only nest approximately: each parent hexagon splits into 7 children that rotate about 19° and don't line up perfectly with the parent's edges. So a child isn't cleanly "inside" one parent the way an S2 cell is. Sixteen resolutions span the range — a base cell over 1,000 km across at resolution 0, down to about 0.5 m at resolution 15.
The trade, in a sentence: hexagons buy uniform neighbors — perfect for flow — at the price of 12 pentagons and approximate nesting.

Same Idea, Two Bets
Line S2 and H3 up and the family resemblance is obvious — and so is the split.
What they share: both wrap the Earth on a 3-D solid, both build a hierarchy of cells, both hand every cell a 64-bit integer ID that indexes and shards as easily as a geohash, and both fix geohash's two flaws (the jumpy curve and the pole distortion). If you need a global spatial index that distributes across a cluster, either one beats a geohash and neither one is a tree stuck on a single machine.
Where they differ — the bet:
- S2 = squares + a Hilbert curve. Cells nest exactly, the curve preserves locality, and a region becomes a tidy set of ID ranges. That makes it superb for indexing, containment, and covering regions — "is this point inside that polygon," "index every venue," "range-scan this neighborhood." It's the database-and-geometry tool.
- H3 = hexagons. Neighbors are uniform, so it shines at movement, flow, and analytics — bucketing rides into cells, measuring supply and demand, drawing gradients, expanding search rings evenly. It's the marketplace-analytics tool.
It's not even either/or at the company level: Uber uses both — H3 to bucket supply and demand for surge, and S2 for its RideCheck safety system that geofences pickups and drop-offs. Same firm, different jobs, different grid. The rule of thumb is simple: reach for S2 when you're indexing and asking about geometry; reach for H3 when you're analyzing movement and flow.
Both bets are worth feeling directly. Toggle the curve — Z-order versus Hilbert — and watch which one keeps neighbors close; toggle the tiling — square versus hexagon — and watch the hexagon's neighbors line up at a single distance while the square's corners hang back.

The Spatial Toolkit Is Complete
Step back: with S2 and H3, the spatial section is finished, and the four structures line up as a single clean answer to the question we posed in Geospatial Search: The Problem — how do you find what's near, at scale?
- Geohash — a location becomes a string; near becomes shares a prefix. Dead simple, distributes anywhere, but a fixed grid with a jumpy curve. Reach for it when simplicity and distribution win.
- Quadtree — an adaptive grid that splits space where points cluster. Great for points and range queries in memory.
- R-tree — an adaptive tree of bounding boxes that groups the data. The one for shapes with extent, and what spatial databases ship (PostGIS's GiST).
- S2 / H3 — global, hierarchical grids with 64-bit IDs that keep the geohash's distribution and add real geometry. S2 (squares + Hilbert) for indexing and covering; H3 (hexagons) for movement and flow.
Every one of them is the same core move from the very first lesson of this section — turn "near" into a cheap lookup by imposing an order or a partition on space — just with different bargains: a string, an adaptive tree, or a global grid. You now have the whole toolkit and, more importantly, the judgment to pick.
And that closes the first half of §8. Everything so far has kept its answers exact — a spatial index finds precisely the points in your region. The rest of this section makes a startling different trade. What if you'd accept an answer that's occasionally wrong — "probably in the set," "about a million distinct users," "roughly this frequent" — in exchange for a structure hundreds of times smaller that runs at memory speed? That's the world of probabilistic sketches, and it opens with the most beloved of them all: the Bloom filter.