`mapillary/s2geometry`: The 64-Bit Index That Makes the Earth Queryable

A deep dive into the spherical data structure behind fast geospatial search, local neighborhood lookups, and the surprisingly elegant math that replaces flat maps with a hierarchical planet.

10 min read View on GitHub More from mapillary

A globe unfolds into six cube faces, and one face subdivides into smaller square cells while a thin path traces locality across them. The image explains how S2 turns spherical geography into a hierarchical index without flattening the Earth.
S2 starts with a sphere, projects it onto cube faces, then subdivides those faces into an ordered hierarchy that keeps nearby places close in ID space.
Key Takeaways

The unusual thing about mapillary/s2geometry is not that it understands maps. It is that it makes the planet look like an index key. Once location becomes a 64-bit cell ID, you can ask database-style questions about geography: what is nearby, what contains what, what overlaps what, and what can be pruned before anyone does expensive math?

The Earth, Reduced to a 64-Bit Integer

That is the core idea behind S2. Instead of treating latitude and longitude as the final answer, the library maps the sphere onto six cube faces, then subdivides those faces into a recursive hierarchy of cells. The result is a compact identifier that preserves locality well enough to support fast search and range scans.

The payoff is bigger than convenience. A place on Earth becomes something you can sort, compare, store, and traverse with bit operations. That is a rare combination in geospatial systems, where precision and speed usually fight each other.

Why Flat Maps Fail at Planet Scale

SystemCell shapeHierarchyStrengthWeaknessBest fit
S2Square cells on cube facesStrict parent-child containmentStrong spherical indexing and robust containmentMore complex mental modelSpatial search, indexing, and geofencing
H3HexagonsHierarchical, but different containment trade-offsSmooth neighborhood behavior and flow analysisLess exact containment semanticsAggregation and movement analysis
GeohashRectangular gridSimple string encodingEasy to implement and explainDistortion and edge discontinuitiesBasic coarse indexing

Flat projections are convenient until they are not. Mercator-style thinking makes the poles weird, the dateline awkward, and distance comparisons harder than they should be. S2 avoids that entire category of bugs by working natively on the sphere, then using a hierarchy that is built for computation rather than cartography.

This is why the library feels more like a data structure than a map package. It is not trying to draw the world. It is trying to make the world queryable.

The Trick: Cube Faces, Then a Hilbert Curve

S2’s encoding path goes from sphere to face to recursive subdivision to ordered integer. The diagram makes locality, hierarchy, and bit layout visible in one view.

The design starts with a simple compromise: approximate the sphere with six cube faces. Each face is recursively subdivided, and a Hilbert curve orders the cells so that nearby places tend to stay nearby in the encoded space. That locality is the whole game.

The important detail is not just that the IDs are compact. It is that the ordering supports useful traversal. Nearby cells are not random neighbors in ID space, so range scans can approximate geographic neighborhoods without brute force searches.

A close-up of a single 64-bit S2 cell ID shown like a ledger tile, with bit positions, parent and child splits, and a narrow bridge connecting the integer to a tiny patch of spherical surface. The image explains how hierarchy and traversal live inside one integer.
A cell ID is not just an identifier. It is a compressed route through hierarchy, containment, and neighborhood structure.

Why S2CellId Is the Real Product

The headline object in this codebase is S2CellId. The rest of the library builds around it. It encodes face, position, and level in a single 64-bit integer, which means parent, child, and neighbor operations can be expressed with very cheap bit logic.

S2CellId id = S2CellId::FromPoint(point);
if (id.is_valid()) {
  S2CellId parent = id.parent(id.level() - 1);
  S2CellId next = id.next();
  S2Point center = id.ToPoint();
}

This is the implementation story in miniature. You do not build a heavy tree object just to ask where a cell belongs. You manipulate the encoded integer directly, and the hierarchy falls out of the bits.

That choice matters for performance and for ergonomics. When the hierarchy is encoded in the identifier itself, traversal becomes cheap, and the data model stays small enough to use everywhere.

S2Point and S2Cell: Precision Without the Geometry Tax

S2Point handles the 3D vector math. S2Cell handles the geometric work such as subdivision, bounds, and area. That split keeps the low-level index lightweight while giving higher-level operations a place to live.

The 3D approach is doing a lot of work here. By representing points as unit vectors on the sphere, the code avoids many of the singularities and special cases that plague latitude and longitude math. The result is less brittle geometry.

The library also spends real effort on speed. The implementation notes in s2cell.cc describe subdivision paths that are tuned to avoid unnecessary reconstruction, which is exactly the kind of detail that separates a good geometry system from a toy one.

How Queries Get Fast

Fast geometry is mostly about pruning. S2’s shape index and query APIs can rule out large regions before they do expensive edge tests. That is how the library supports spatial joins, nearest-edge queries, and containment checks at scale.

The pattern is familiar to database people. Use the index to get close, then do exact work on the narrowed candidate set. S2 makes that pattern natural for geography.

This is also why the Mapillary use case makes sense. Street-level imagery lives and dies by adjacency, neighborhood lookup, and spatial organization. S2 gives the product a way to treat camera captures like indexed entities instead of raw coordinates.

MapillaryJS needs a scalable way to index camera captures when determining adjacency. It uses discrete S2 geometry cells to solve that in the S2GeometryProvider.

Mapillary Documentation, Official Project Documentation · Geometry Provider | MapillaryJS

S2 vs H3 vs Geohash

The comparison is not about declaring a universal winner. It is about choosing the right geometry model for the job. S2 is strongest when strict containment and spherical correctness matter most.

SystemShapeHierarchyContainmentDistortionTypical advantage
S2Squares on cube facesVery strongExact parent-child nestingLow on the sphereRobust indexing and pruning
H3HexagonsStrong but differentLess strict than S2Low, but with hex trade-offsFlow, aggregation, smoothing
GeohashRectanglesSimpleCoarse and unevenHigh near poles and edgesQuick implementation

If you need a tidy string representation and can tolerate approximation, Geohash is fine. If you want hexagonal neighborhood behavior, H3 is compelling. If you want a spherical index that behaves like a serious containment system, S2 is the one that keeps showing up.

Why Mapillary Cares

A unique feature of the S2 library is that unlike traditional geographic information systems, which represent data as flat two-dimensional projections (similar to an atlas), the S2 library represents all data on a three-dimensional sphere (similar to a globe).

Google Research, Original Library Authors · S2 Geometry Library

That globe-first model is exactly why Mapillary would care. A street-level imagery platform needs adjacency, containment, and fast neighborhood lookup across huge datasets. S2 gives it a durable spatial language for those operations.

So the library is not just an implementation detail. It is infrastructure for the product’s spatial brain. Without something like S2, the system would spend far more time compensating for the geometry of the Earth.