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.
Metadata
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 | Provenance | Actions |
|---|---|---|---|---|
| h3_boundary-0.1.0.tar.gz | 51.6 kB | Without provenance | Details for h3_boundary-0.1.0.tar.gz |
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
|
Security for h3-boundary 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.