H3: Uber's Hierarchical Hexagonal Geospatial Index
H3 is an open-source hierarchical hexagonal geospatial indexing system developed by Uber and released under Apache 2.0 in 2018. Unlike classical square grids (geohashes, Google's S2, MGRS), H3 covers the Earth with hexagons: each cell has six neighbours at the same distance, which is critical for "distance to nearest" analysis in mobility, delivery and geomarketing. H3 has 16 resolution levels: from resolution 0 with cells of roughly 4,250,000 km² (continental scale) down to resolution 15 with cells of about 0.9 m² (a single room). A cell ID is a compact 64-bit integer that any relational or NoSQL database can index. In osm2cdr, H3 isn't a standalone "map-to-file" export format — it's an aggregation grid: we can count POI density, building density or road density across cells of a chosen resolution and return the result as GeoJSON/GeoParquet with an h3_index column.

History: surge pricing and the birth of H3 at Uber
By the mid-2010s Uber faced a concrete engineering problem: divide cities into "zones" for surge pricing and ride analytics, such that the zones were geometrically uniform, had predictable neighbours, and supported fast rollup across levels (block → district → city). Off-the-shelf solutions didn't fit: square geohashes like dr5ru4 suffer from neighbours sharing different prefixes (a boundary effect), and a square has different distances along its diagonal vs its side. Google's S2 is closer in spirit but relies on a cube-sphere projection and doesn't play well with mainstream geo-databases.
In 2018 Uber Engineering published H3 — a hierarchical grid of hexagons purpose-built for mobility analytics. Apache 2.0 licence, C reference implementation, bindings for Python (h3-py), JavaScript (h3-js), Java, Go, Rust. From day one Uber used H3 for surge pricing zones (res 7-8 cells covering a city centre, dynamically repricing based on supply/demand), for ride heatmaps, for matchmaking between drivers and riders.
By 2020-2021 H3 had spread far beyond Uber: Foursquare picked it up for POI clustering, AirBnb for hex-zone price predictions, DoorDash and Lyft for mobility, climate and epidemiology groups for aggregating observations into equal-area cells. PostgreSQL got the h3-pg extension, BigQuery added native H3 functions, Apache Sedona/Spark gained H3 UDFs. Today H3 is the de facto industrial standard for hexagonal grids in geo-analytics.
Inside H3
The key insight about H3 is that it's not a single "file format" — it's an identifier system: every cell on Earth gets a unique 64-bit integer, and from that ID you can deterministically compute the cell's centre, its boundary polygon in WGS84, its parent cell at a coarser resolution, and its seven child cells at a finer one.
There are 16 resolutions, 0 to 15. Resolution 0 covers the entire planet with just 122 cells, each around 4,250,000 km² ("continental"). Resolution 5 has about 2.5 million cells, each ~250 km² (a metro district). Resolution 9 is the most popular for urban analytics, cells ~0.1 km² (city block). Resolution 15 is the finest at ~0.9 m² (a room). Each step down divides every cell into roughly 7 children (exactly 7 on average — perfect 7× subdivision of a hex grid is impossible, with tiny distortions at the pentagonal pole cells).
A 64-bit cell ID is laid out as: 4 bits for mode (cell / unidirectional edge / vertex), 3 reserved bits, 4 bits for resolution (0-15), 7 bits for the base cell (one of 122 base cells at resolution 0), and then 3 bits per each of the 15 potential refinement levels (0..6 for which of the seven children was picked, 7 for "unused").
Useful API operations: geo_to_cell(lat, lon, res) returns the cell for a point; cell_to_boundary(cell_id) returns the boundary polygon; cell_to_parent(cell_id, target_res) and cell_to_children(cell_id, target_res) navigate the hierarchy; grid_disk(cell_id, k) returns all cells within k hex-steps (k-ring); grid_distance(a, b) is the distance in hex steps; polygon_to_cells(geometry, res) fills a polygon with cells at the requested resolution.
Out of the box H3 is an "index", not a "file". For storage and interchange, cells are typically written as:
- an h3 column in GeoParquet (compact, columnar, fast joins on BigQuery/Athena),
- a property on a GeoJSON feature with a cell_to_boundary polygon (handy for visualisation in Mapbox/MapLibre),
- a binary column in PostgreSQL via the h3-pg extension.
Use cases
Surge pricing and mobility analytics. Uber's original use case: tile the city in res 7-8 cells, aggregate demand (ride requests per minute window) and supply (free drivers) per cell, reprice dynamically. Lyft and DoorDash followed the same template: drivers, couriers, riders are matched and routed at the hex-cell level rather than at raw point coordinates.
Retail trade area and geomarketing. A coffee chain wants to know which parts of a city are already covered by existing locations and where the "white spots" are. Each existing store maps to an H3 cell at res 8, a k-ring of radius 1-2 (5-10 minute walking distance) is built around it, and the cells inside are marked "covered". Uncovered cells become candidates for new locations.
Environmental modelling and climate data. Satellite observations (NO₂, surface temperature, NDVI) historically ship in inconvenient raster projections with mismatched grid sizes. Repackaging into H3 res 6-7 (~36 km² and ~5 km² respectively) yields equal-area cells convenient for time-series comparison and population-data overlay. ESA Copernicus and NASA publish derived H3-aggregated products.

Fleet and delivery optimisation. Car-sharing services and logistics companies (Wolt, Glovo) use H3 to forecast demand per cell, balance vehicle/courier distribution, and cluster pickup/dropoff zones.
OSM data analytics. This is our home turf. How many cafés per square kilometre in Kazan? Where is residential density highest in Berlin? How does POI density decay from the city centre to the outskirts? An H3 cell at res 8 (~0.7 km²) is a great scale for such questions, and we can compute these aggregates straight from local PostGIS.
Workflow in OSM2CDR
In our system H3 isn't "map-to-file export" — it's an aggregation grid you can lay over any OSM dataset. The pipeline:
Step 1: bbox and layer selection. The user draws a bbox on the map (as for any export), then picks a data type — POI of a given category (amenity=cafe, shop=*, tourism=*), buildings (building=*), or the road graph (highway=*). Standard filtering via our local_osm_loader.py.
Step 2: resolution selection. Default depends on bbox size: city block → res 10 (~15,000 m²), district → res 9 (~100,000 m²), whole city → res 8 (~0.7 km²), region → res 6 (~36 km²). The user can override anywhere in the 0-15 range.
Step 3: aggregation into H3. Via h3-py (h3.geo_to_cells(geometry, res)) every OSM feature is mapped to its H3 cell. We group by h3_index and compute the aggregate: count(*) for POIs, sum(building_area) for buildings, sum(road_length_m) for roads. In parallel, via the h3-pg PostGIS extension, the same calculation can run entirely in SQL without shipping geometries — for large bboxes that's tens of times faster.
Step 4: package as GeoJSON or GeoParquet. Each output H3 cell becomes a feature: the boundary polygon (h3.cell_to_boundary) and properties {h3_index, resolution, count, density_per_km2}. For small exports we return GeoJSON (readable by everything — MapLibre/Mapbox/QGIS); for large ones GeoParquet with an h3 column (compact, analytics-ready on BigQuery/DuckDB/Athena).
Step 5: visualisation (optional). If the user wants a picture right away, we render a choropleth (colour by count) and serve PNG/SVG. On the client side the same GeoJSON drops nicely into MVT for an interactive hex map with zoom-dependent detail.
H3 complements our stack precisely as an aggregation layer: source OSM data stays vector (points, lines, polygons), and H3 provides a uniform grid for statistics — convenient for a data scientist in Jupyter, for a Grafana dashboard, and for ML model training alike.

Conclusion
H3 is a clean piece of engineering: a hexagonal grid with equidistant neighbours, a 7× hierarchical refinement factor, a compact 64-bit ID, and ready-made support across every modern geo-database and analytics platform. For any project that needs a "default grid" — surge pricing, POI density, climate aggregation, retail trade area — H3 has become the industry standard in just a few years. On osm2cdr you can build H3 aggregates over any OSM layers and download them as GeoJSON or GeoParquet on the /formats/h3-index/ page: pick a bbox, the data type, a resolution — and get ready hexagonal statistics for your dashboard, ML pipeline or research notebook.
Related
- GeoParquet — columnar format for geo-analytics
- Mapbox Vector Tiles (MVT) — binary tile format
- GeoJSON — universal interchange format for geo-data
Sources
- Uber Engineering blog: H3 announcement (2018) — eng.uber.com/h3
- H3 documentation — h3geo.org
- h3-py (Python bindings) — github.com/uber/h3-py
- h3-pg (PostgreSQL extension) — github.com/zachasme/h3-pg
- H3 in BigQuery — cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions