H3 Bound Cells
https://pypi.org/project/h3_bound_cells/
Convert geographic polygons into multi-resolution H3 cell coverings, with fast point-in-region lookups.
Rust core is built on h3o and exposed to Python via PyO3 / Maturin.
Generation uses h3-compactfill which is an extension to H3 which implements a short-circuited polygonToCells + compact Algorithm.
Overview
BoundCells holds a Compacted Polyfill of a target Polygon, keyed by H3 Resolution.
Generation - Fairly standard polyfill+compact step which converts a Polygon into contained H3 Cells. Start Resolution - This is the finest (highest) Resolution of the Cells, this is needed at cell edges to get a closer match to the polygon. Containment - Walks a datasets H3 Cells (checking each from fine->coarse) against the BoundCells for containment predicate. Prefilter allows for Parquet Predicate Pushdown which can Prune Row Groups by checking against Cell Index Ranges.
Install
The package is built locally with maturin: uv run maturin develop --release
Usage
import h3
from h3_bound_cells import polygon_to_bound_cells, cell_in_bound_cells, BoundCells
# Rectangle around central London. (lat, lng) pairs; the ring need not be closed.
exterior = [
(51.50, -0.13),
(51.52, -0.13),
(51.52, -0.08),
(51.50, -0.08),
]
bc = polygon_to_bound_cells(exterior, start_res=9)
print(bc)
# BoundCells(cells={8: 9, 9: 5, ...})
for res, cells in bc.cells.items():
print(f"res {res}: {len(cells)} cells")
# Point-in-region check: Trafalgar Square at H3 resolution 11.
cell = h3.latlng_to_cell(51.5074, -0.1278, 11)
assert cell_in_bound_cells(cell, bc)
# JSON-friendly round trip
restored = BoundCells.from_dict(bc.to_dict())
API
polygon_to_bound_cells(exterior, holes=None, start_res=None, containment_mode=None, tolerance=None) -> BoundCellsexterior,holes— lists of(lat, lng)tuples. Note: this is(latitude, longitude), matching the H3 convention (e.g.h3.latlng_to_cell) — the reverse of GeoJSON/shapely/WKT, which use(longitude, latitude). Swap the order when feeding in GeoJSON coordinates.start_res— H3 resolution to tile at. When omitted, it is auto-picked from the polygon's planar area.containment_mode— aBCContainmentModeselecting which cells the fill keeps (defaultBCContainmentMode.ContainsCentroid). SeeBCContainmentModebelow.tolerance— Douglas–Peucker simplification distance in degrees, applied to the polygon before tiling. Tiling cost is linear in vertex count, so simplifying high-fidelity boundaries (e.g. OS/OSM data with metre-scale vertices) is a large speed-up. Defaults to0— no simplification, an exact covering identical to the raw geometry. Pass a small positive value (e.g.1e-5, ≈1 m, for a ~5× speed-up on detailed boundaries) to opt in. Note that any non-zero tolerance is effectively-lossless rather than provably identical: a cell whose centroid lies within ~toleranceof the boundary can flip. Larger values are faster but shift more such edge cells.
cells_to_bound_cells(cells) -> BoundCells- Build a covering directly from an existing set of H3 cells, skipping polygon tiling.
cellsis an iterable of H3 cells as hex strings or u64 ints. - Input must be at a single resolution (mixed resolutions raise
ValueError); duplicates are de-duplicated. An empty input yields an empty covering.
- Build a covering directly from an existing set of H3 cells, skipping polygon tiling.
cell_in_bound_cells(cell: str, bound_cells: BoundCells) -> bool— membership test by H3 cell id.BCContainmentMode— enum choosing which cells a polygon fill keeps (mirrors h3o'sContainmentMode):ContainsCentroid(default) — cell kept when its centroid is inside. Fast, approximate interior.ContainsBoundary— cell kept only when fully inside. Strict interior — no false positives, but omits edge cells only partly inside.IntersectsBoundary— cell kept when it touches the polygon. Produces a superset covering that extends past the edge.Covers— likeIntersectsBoundary, also handling polygons smaller than a cell. Also a superset.- ⚠️ The overlap modes (
IntersectsBoundary,Covers) make the covering a conservative superset, socell_in_bound_cellsthen means "overlaps the region" and can report cells lying outside the polygon as inside. Use them when you want a covering, not exact point-in-region.
BoundCells— frozen class:.cells—dict[str, list[str]]keyed by resolution..to_dict()/BoundCells.from_dict(d)— JSON-friendly round trip.BoundCells.merge([bc1, bc2, ...])— union of multiple results..cells_at_resolution(res)— flatten/expand the covering to a single H3 resolution (parents map up, coarser cells expand to their children).
Polars integration (optional)
Filter a Polars DataFrame/LazyFrame down to the rows whose H3 cell falls inside a covering.
Install the optional polars extra:
pip install h3_bound_cells[polars]
Importing h3_bound_cells registers a bound_cells namespace on Polars expressions (only registered when polars is installed):
import polars as pl
import h3_bound_cells # registers the bound_cells namespace
bc = h3_bound_cells.polygon_to_bound_cells(exterior, start_res=9)
df = pl.DataFrame({"cell": [...]}) # H3 cells as hex strings or u64 ints
# Filter to rows inside the covering:
df.filter(pl.col("cell").bound_cells.is_in(bc))
# Or use the boolean result as a column:
df.with_columns(inside=pl.col("cell").bound_cells.is_in(bc))
pl.col(cell_column).bound_cells.is_in(bound_cells)— a boolean expression, true where the cell lies insidebound_cells.- Use it anywhere an expression is accepted (
filter,select,with_columns, boolean combinations, …). - Works with both eager and lazy frames. Cells may be hex strings or u64 ints; a null cell maps to
false(dropped byfilter).
Predicate Pushdown (Parquet)
is_in is a native plugin, so the query optimiser can't see through it to prune a scan on its own.
To fix that, is_in ANDs a pure H3-index range predicate in front of the exact check (prefilter=True, on by default).
Because an H3 index sorts identically as a u64 and as its 15-char hex string, Polars can push that range test into a Parquet SCAN and skip whole row groups via their min/max statistics — often a large speed-up on big scans, with an identical result. The exact plugin check still runs, so correctness is unchanged.
This is ONLY recommended in Lazy Execution as it provides purely IO-bound wins, with a collected DataFrame there is no scan to prune so this then just becomes and additional filter to run.
lf = pl.scan_parquet("atlas.parquet") # sorted by the H3 column
# UInt64 cell column (default dtype):
lf.filter(pl.col("h3").bound_cells.is_in(bc))
# Canonical 15-char hex-string cell column:
lf.filter(pl.col("h3point").bound_cells.is_in(bc, dtype=pl.Utf8))
For the prefilter to be correct and effective:
- Resolution — by default the range predicate covers every resolution, so it is correct whatever
resolution the column is at. If you know it, pass
child_res=<res>(e.g.child_res=15) for the leanest predicate; the extra ranges of the default sit in emptyu64regions for a uniform-resolution column, so they don't weaken pruning either way. - Matching dtype —
dtypemust match the column:pl.UInt64(default) for an integer column, orpl.Utf8for a canonical 15-char lowercase-hex string column. - Sorted column — row-group skipping is dramatic only when the column is sorted (tight per-group min/max). On unsorted data the prefilter still trims per-row plugin cost but skips no row groups.
Pass prefilter=False for the exact-only behaviour.
Release files for h3_bound_cells 0.5.3
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_bound_cells-0.5.3.tar.gz | 72.8 kB | Details |
Built distributions (wheels)
Total release size: 47.1 MB
Release files / h3_bound_cells-0.5.3.tar.gz
| Download URL | h3_bound_cells-0.5.3.tar.gz |
|---|---|
| Size | 72.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ff7a3ff0ab1e9475b300747cab06c968665084e4606323d3293fedd458687390
|
|
BLAKE2b-256 checksum How to use checksums |
f5702df8512b94696b3acbb8fc7dd97fc46dac6e6e9f5414e79468a99e8b2617
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / h3_bound_cells-0.5.3-cp39-abi3-win_arm64.whl
| Download URL | h3_bound_cells-0.5.3-cp39-abi3-win_arm64.whl |
|---|---|
| Size | 4.2 MB |
| Tags | CPython 3.9 Windows ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
63631eea8c30f69ccf304529e8038a3b7de70b8ec36677fff78ca8c378263fc6
|
|
BLAKE2b-256 checksum How to use checksums |
06232e3d35b49f75e428203ca69c4824a12ee24be26b080247c8e2aeef40ac4b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / h3_bound_cells-0.5.3-cp39-abi3-win_amd64.whl
| Download URL | h3_bound_cells-0.5.3-cp39-abi3-win_amd64.whl |
|---|---|
| Size | 4.7 MB |
| Tags | CPython 3.9 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
efb0df8bbb1d5fd98a4c08ebd2f050abe7de5a001e1ae9850dc6b6da9c7a7d4b
|
|
BLAKE2b-256 checksum How to use checksums |
17b960657dc2fb5fe57ea071efa5ecbc4c8cb1d7874b76ab7ed0f34a9e24be24
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / h3_bound_cells-0.5.3-cp39-abi3-win32.whl
| Download URL | h3_bound_cells-0.5.3-cp39-abi3-win32.whl |
|---|---|
| Size | 4.2 MB |
| Tags | CPython 3.9 Windows x86-32 abi3 |
|
SHA-256 checksum How to use checksums |
ce069f412f6c92201cbd8fd36f13b8bfc0d527723291a459b38c4676622faaef
|
|
BLAKE2b-256 checksum How to use checksums |
3ecc7c88f75817b6b2d42632ff96cad6b953d69ca5606a644bfe94b30d4e9ba3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / h3_bound_cells-0.5.3-cp39-abi3-musllinux_1_2_x86_64.whl
| Download URL | h3_bound_cells-0.5.3-cp39-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 5.0 MB |
| Tags | CPython 3.9 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
d29db23e68018a1c88890d24fc83401c3ffd15b656bc1c2c8f540b79947f11ec
|
|
BLAKE2b-256 checksum How to use checksums |
5a068e544265a35b71098462333a0b555ecedacd439b9c384a6e6c868bccb091
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / h3_bound_cells-0.5.3-cp39-abi3-musllinux_1_2_i686.whl
| Download URL | h3_bound_cells-0.5.3-cp39-abi3-musllinux_1_2_i686.whl |
|---|---|
| Size | 5.3 MB |
| Tags | CPython 3.9 Linux musl 1.2+ x86-32 abi3 |
|
SHA-256 checksum How to use checksums |
7baef31933b7721403f19ff55c988e8ea70eb0cedcbf939b39a77fd17df4e6e9
|
|
BLAKE2b-256 checksum How to use checksums |
f77fbd03b560f59b9ffb54c5afd3b548547cf6609cadfac9bde72a1bc639b491
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / h3_bound_cells-0.5.3-cp39-abi3-musllinux_1_2_aarch64.whl
| Download URL | h3_bound_cells-0.5.3-cp39-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 4.7 MB |
| Tags | CPython 3.9 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
76364067620e2db20a6d371753982edf8d70cc816ca38c8b4febf6bb0a9637b9
|
|
BLAKE2b-256 checksum How to use checksums |
37531d4fa0321908698fbcc7422e7388461c3b3f6159f9f86dd0af0fce8c2fd3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / h3_bound_cells-0.5.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | h3_bound_cells-0.5.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 4.8 MB |
| Tags | CPython 3.9 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
5a6365180c7ade5677406c4957b30c9c91883294f5733b20b566980dee754461
|
|
BLAKE2b-256 checksum How to use checksums |
0d0b7e9080ba4ef5dae0f506a78572542c45c16a10dc49daaa772430b7acc354
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / h3_bound_cells-0.5.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | h3_bound_cells-0.5.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 4.5 MB |
| Tags | CPython 3.9 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
8db55d265c78a4eb3c7cc30749dac1b6911736ec4a2e4c934bbb03e93d6e5856
|
|
BLAKE2b-256 checksum How to use checksums |
f48dc52d3767029882850c19468562bef2b7bdd89ce04b58c9a2b3541b45c2f2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / h3_bound_cells-0.5.3-cp39-abi3-manylinux_2_12_i686.manylinux2010_i686.whl
| Download URL | h3_bound_cells-0.5.3-cp39-abi3-manylinux_2_12_i686.manylinux2010_i686.whl |
|---|---|
| Size | 5.3 MB |
| Tags | CPython 3.9 Linux glibc 2.12+ x86-32 abi3 |
|
SHA-256 checksum How to use checksums |
983fc8682e4c7c8d5dab4283409e0cf0d86faffaa0547805f7a04ff407dffb31
|
|
BLAKE2b-256 checksum How to use checksums |
75f87fc13985e704e4b33cf99305edf857027a8b6e62ccdb8b952610e181c4ca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / h3_bound_cells-0.5.3-cp39-abi3-macosx_11_0_arm64.whl
| Download URL | h3_bound_cells-0.5.3-cp39-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 4.3 MB |
| Tags | CPython 3.9 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
65779436c0a79e9139f21cb92b6670b9c239d764b5457eb660dd91a95ac949cb
|
|
BLAKE2b-256 checksum How to use checksums |
7844cc40b28cb998c143b145c77f609e53a1dbac9c79ee12a9ffdff5a20437d4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|