H3-Boundary
Boundary cells and boundary polygons for Uber's H3 grid.
An H3 cell at one resolution contains thousands — or billions — of cells at finer resolutions. H3-Boundary works with the ones on its edge: it lists them, traces their outline, and builds polygons that safely contain them. Cost scales with the boundary, never with the interior.
A resolution-5 cell traced at resolution 8: 78 boundary cells computed, 265 interior cells never generated.
pip install h3-boundary
Quick start
Start from any H3 cell. Here we index downtown San Francisco at resolution 6 — a district-sized cell of about 36 km² — using latlng_to_cell from h3-py, which H3-Boundary already depends on.
import h3
import h3_boundary as h3b
cell = h3.latlng_to_cell(lat=37.7759, lng=-122.4180, res=6)
# 1. Its boundary cells: the descendants at resolution 10 (block-sized cells)
# that lie on its edge
edge = h3b.children_on_boundary_faces(cell, target_res=10)
len(edge) # 240, out of 2,401 descendants
# 2. Its exact outline — the shape those descendants actually fill
outline = h3b.cell_boundary_from_children(cell, target_res=10)
# 3. That same outline, grown by a safety margin
safe = h3b.get_buffered_boundary_polygon(cell, intermediate_res=10)
safe["properties"]["buffer_meters"] # 75.9 — the margin added
Both polygons are GeoJSON Features, and both trace the boundary at resolution 10 — the difference is what they are for. The outline is the exact shape: draw it. On its own it is not safe to filter with, because cells finer than resolution 10 still poke slightly past it; safe pushes the edges out by one resolution-10 edge length (75.9 m), after which nothing can fall outside at any resolution. That is also why its parameter is called intermediate_res — there, tracing is only an intermediate step.
Why not just use the hexagon H3 draws for the cell? Because that hexagon is not where the descendants sit — they straddle it, as the figure above shows. Filtering fine-resolution data with it silently loses the cells along the edge (about 7% of them).
Working at scale
Interiors explode; boundaries stay manageable. Boundary size has a closed form — 3**(depth + 1) - 3, where depth is how many resolution levels you descend — so it is known before computing anything. A resolution-2 cell has close to two billion descendants at resolution 13, but only 531,438 of them lie on its boundary, and you never need to build even those to use them:
res, target = 2, 13 # a country-sized cell, traced with ~44 m² cells
big = h3.latlng_to_cell(lat=37.7759, lng=-122.4180, res=res)
depth = target - res # 11 levels of subdivision between the two
total = 3 ** (depth + 1) - 3 # 531,438 boundary cells, known without counting
ids = h3b.boundary_cell_ids(big, target_res=target) # all of them, uint64, ~4 ms
mid = h3b.boundary_cell_at(big, target_res=target, n=total // 2) # any one, in microseconds
h3b.boundary_rank(big, mid) # the inverse — also a membership test
h3b.boundary_range(big, target_res=target, start=0, stop=100) # any slice — stream it, or shard it
Disjoint slices reassemble into exactly the full boundary, so parallel workers need no coordination.
Output formats
- Cells: hex strings (
children_on_boundary_faces) or NumPyuint64arrays (boundary_cell_ids) — ready for h3-py, dataframes, or database joins. - Polygons: GeoJSON Features — ready for folium, Leaflet, or PostGIS.
The package ships as a source distribution: it compiles a C++ extension during install when a toolchain is available (cmake, C++17, Boost headers) and falls back to pure Python otherwise. Same results either way, verified by a parity test suite.
Documentation
khoshkhah.github.io/h3-boundary — concepts, algorithm comparisons, benchmarks, and the full API.
Three runnable notebooks live in notebook/: boundary tracing on a map, working with half-million-cell boundaries, and the buffering modes compared.
Development
git clone https://github.com/Khoshkhah/h3-boundary.git
cd h3-boundary
conda env create -f environment.yml && conda activate h3-boundary
pip install -e .
pytest tests/python -v
Contributions welcome — see CONTRIBUTING.md.
License
MIT — see LICENSE. Built on Uber H3, Boost.Geometry and pybind11.
Release files for h3-boundary 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| h3_boundary-0.1.0.tar.gz | 51.6 kB | Details |
Release files / h3_boundary-0.1.0.tar.gz
| Download URL | h3_boundary-0.1.0.tar.gz |
|---|---|
| Size | 51.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3ee65e578fb2959f2d7c8bdaa00e316323578fc241f043217d3ad3fb46f057bc
|
|
BLAKE2b-256 checksum How to use checksums |
74efc7956c41392e28a90f09fdc80ee9161d8672f7dad38dd09f3d439803bd0d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.2
|