Skip to main content

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.

Overview

BoundCells is a pair of dicts keyed by H3 resolution:

  • area — cells whose interior lies inside the polygon.
    • The area layer is compacted: wherever all 7 children at a resolution are present, they collapse up to the parent.
    • A single covering naturally spans multiple resolutions (chunky cells in the interior, smaller cells near the edge).
  • border — cells that overlap the polygon boundary, materialised at every coarser resolution down to min_cell_resolution.

The border layer is what makes containment lookups cheap. To check whether an arbitrary cell falls inside the region, cell_in_bound_cells first tests the cell against the border layer at its own resolution, then walks its ancestors from fine to coarse against the compacted area cells, returning true on the first hit — no need to materialise every leaf cell of the polygon. A query cell is "inside" if it — or one of its H3 ancestors computed via cell.parent() — is an area cell.

BoundCells

Install

The package is built locally with maturin:

uv sync
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, min_cell_resolution=4)

print(bc)
# BoundCells(area_resolutions=[...], border_resolutions=[...])

for res, cells in bc.area.items():
    print(f"area   res {res}: {len(cells)} cells")
for res, cells in bc.border.items():
    print(f"border 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, min_cell_resolution=None, tolerance=None) -> BoundCells
    • exterior, 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.
    • min_cell_resolution — floor for the border layer (default 4).
    • 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 to 0 — 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 ~tolerance of the boundary can flip. Larger values are faster but shift more such edge cells.
  • cell_in_bound_cells(cell: str, bound_cells: BoundCells) -> bool — membership test by H3 cell id.
  • BoundCells — frozen class:
    • .area, .border — dict[str, list[str]] keyed by stringified 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 inside bound_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 by filter).

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 empty u64 regions for a uniform-resolution column, so they don't weaken pruning either way.
  • Matching dtype — dtype must match the column: pl.UInt64 (default) for an integer column, or pl.Utf8 for 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.

Dev visualiser

A small MapLibre app for drawing polygons and visualising the covering. It runs the Rust core entirely in the browser via WebAssembly — no backend — so it is a fully static page.

Draw a polygon (Draw polygon, click vertices, double-click to finish) and the area and border layers render colour-coded by resolution. Leave start_res blank to let the library auto-pick from the polygon's area, or set it to override. Renders are capped at 50,000 cells; reduce the polygon size or raise start_res if you trip the limit.

Running it locally

The page needs the WASM module built into dev/pkg/, and must be served over http(s) (ES-module + .wasm loading doesn't work from a file:// URL). Both are handled by:

just serve            # builds dev/pkg, then serves http://127.0.0.1:5050/

Or run the steps by hand (requires wasm-pack):

wasm-pack build --target web --out-dir dev/pkg --no-default-features --features wasm
python -m http.server -d dev 5050

The browser build compiles the same polygon_to_bound_cells core under the crate's wasm Cargo feature (PyO3 is behind the default python feature and excluded here).

Release files for h3_bound_cells 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for h3_bound_cells 0.3.1
File Size Uploaded
h3_bound_cells-0.3.1.tar.gz 5.4 MB Details

Built distributions (wheels)

Table of built distributions (wheels) for h3_bound_cells 0.3.1
File
h3_bound_cells-0.3.1-cp39-abi3-win_arm64.whl CPython 3.9 abi3 Windows ARM64 Details
h3_bound_cells-0.3.1-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details
h3_bound_cells-0.3.1-cp39-abi3-win32.whl CPython 3.9 abi3 Windows x86-32 Details
h3_bound_cells-0.3.1-cp39-abi3-musllinux_1_2_x86_64.whl CPython 3.9 abi3 Linux musl 1.2+ x86-64 Details
h3_bound_cells-0.3.1-cp39-abi3-musllinux_1_2_i686.whl CPython 3.9 abi3 Linux musl 1.2+ x86-32 Details
h3_bound_cells-0.3.1-cp39-abi3-musllinux_1_2_aarch64.whl CPython 3.9 abi3 Linux musl 1.2+ ARM64 Details
h3_bound_cells-0.3.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.9 abi3 Linux glibc 2.17+ x86-64 Details
h3_bound_cells-0.3.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.9 abi3 Linux glibc 2.17+ ARM64 Details
h3_bound_cells-0.3.1-cp39-abi3-manylinux_2_12_i686.manylinux2010_i686.whl CPython 3.9 abi3 Linux glibc 2.12+ x86-32 Details
h3_bound_cells-0.3.1-cp39-abi3-macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64 Details

Total release size: 54.4 MB

Release files / h3_bound_cells-0.3.1.tar.gz

Download URL h3_bound_cells-0.3.1.tar.gz
Size 5.4 MB
Tags Source
SHA-256 checksum
How to use checksums
35d2cedb89b22a50c2104292b29e5ec23b6fb4be3549d2f5bc4a1ca1ed0bd3a2
BLAKE2b-256 checksum
How to use checksums
2ded0a91c25a5513f3d285e68cc5ed30806dc477d2115a0c127a7106e2cc1d4d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.3.1-cp39-abi3-win_arm64.whl

Download URL h3_bound_cells-0.3.1-cp39-abi3-win_arm64.whl
Size 4.4 MB
Tags CPython 3.9 Windows ARM64 abi3
SHA-256 checksum
How to use checksums
3a3d7e802ba5829e055fb1f8540c51a8b1c1327f4dfd2fa08cf9f70df1fa88a8
BLAKE2b-256 checksum
How to use checksums
504af9d9b7393880e21ea459973a2055a1f3e69630a1dc02b5744cca9e14e8cf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.3.1-cp39-abi3-win_amd64.whl

Download URL h3_bound_cells-0.3.1-cp39-abi3-win_amd64.whl
Size 5.0 MB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
d1277e2b01614ecb25e78f2c25d4c876db45ef07448fc90cfe5ed9b17c8f8487
BLAKE2b-256 checksum
How to use checksums
b7107bb5508c76841e8e374c4a60f87a5d51f0d72afc5f33a0f196c601254fe3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.3.1-cp39-abi3-win32.whl

Download URL h3_bound_cells-0.3.1-cp39-abi3-win32.whl
Size 4.4 MB
Tags CPython 3.9 Windows x86-32 abi3
SHA-256 checksum
How to use checksums
7dd042e50e48b6bc4a7d1de07eb62117b54f7229bc3023598c1aa71139611fa9
BLAKE2b-256 checksum
How to use checksums
1e328f397801bef086ba62e5a24c0542ac748e8cc0db26729e668aaf274ccb07
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.3.1-cp39-abi3-musllinux_1_2_x86_64.whl

Download URL h3_bound_cells-0.3.1-cp39-abi3-musllinux_1_2_x86_64.whl
Size 5.2 MB
Tags CPython 3.9 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
e982b12396a89a39f871dfbc687f29ddf537020ae9474cebcf692ff183279b99
BLAKE2b-256 checksum
How to use checksums
86470e4bc58fbf1a73629624fc1294960af39c9b71c4bf81a8aec996fad0c602
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.3.1-cp39-abi3-musllinux_1_2_i686.whl

Download URL h3_bound_cells-0.3.1-cp39-abi3-musllinux_1_2_i686.whl
Size 5.5 MB
Tags CPython 3.9 Linux musl 1.2+ x86-32 abi3
SHA-256 checksum
How to use checksums
c199efb606d8c003d3de3f113775475ba080cce0c6351cec2962d76c303d94a8
BLAKE2b-256 checksum
How to use checksums
281fe3f31b5fc14f203f369ca444f45204703b913a9bf64ea04509ac3b332ff4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.3.1-cp39-abi3-musllinux_1_2_aarch64.whl

Download URL h3_bound_cells-0.3.1-cp39-abi3-musllinux_1_2_aarch64.whl
Size 4.9 MB
Tags CPython 3.9 Linux musl 1.2+ ARM64 abi3
SHA-256 checksum
How to use checksums
b33072642d6775d66a8cbcc1aea51644b3773ff5bd61152e7c45af3ad8686eb6
BLAKE2b-256 checksum
How to use checksums
dc9b6f8a3704eb5b476c0fb5c0cd073d0e82d02949125d77e1938c51addab4f0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.3.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL h3_bound_cells-0.3.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 5.0 MB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
6a497960353b8aa3a35b5ff55c17dd73f3d36223f80ec3ba890fcd86eed204fc
BLAKE2b-256 checksum
How to use checksums
00e8cc726187e0f4df5d79c514948703341afc515620f8543f36b6a7d6cb0c58
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.3.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL h3_bound_cells-0.3.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 4.7 MB
Tags CPython 3.9 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
a5565e102b6e212da35d4701ca53a5070b95ac3629a43d225328459961759420
BLAKE2b-256 checksum
How to use checksums
91a727742162b5872699dd03c438195c2e72915ad61969007a5df6105a9833a7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.3.1-cp39-abi3-manylinux_2_12_i686.manylinux2010_i686.whl

Download URL h3_bound_cells-0.3.1-cp39-abi3-manylinux_2_12_i686.manylinux2010_i686.whl
Size 5.4 MB
Tags CPython 3.9 Linux glibc 2.12+ x86-32 abi3
SHA-256 checksum
How to use checksums
7a47b61fa6ac2a9315a728f712cae08dc8cc9172d60b73a8dc3ebbc805dd28f6
BLAKE2b-256 checksum
How to use checksums
7a2ac4a864a9e1dbd4fbfb1f8435e7ff7343cea94bbb05676af8efc1ec49daae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.3.1-cp39-abi3-macosx_11_0_arm64.whl

Download URL h3_bound_cells-0.3.1-cp39-abi3-macosx_11_0_arm64.whl
Size 4.5 MB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
d5c7d655a7c49e3ea91eabd6dc8facd5cbb736118f5a0f32881e3523efade37e
BLAKE2b-256 checksum
How to use checksums
a0f3cac5608aaed9ae1584f3be1ce4eda758e44fd6973da1f9fb1f41f408b01d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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 history Release notifications | RSS feed

0.5.3

11 release files

0.5.2

11 release files

0.5.1

11 release files

0.5.0

11 release files

0.4.1

11 release files

This release

0.3.1 This release

11 release files

0.3.0

11 release files

0.2.2

11 release files

0.2.1

11 release files

0.2.0

11 release files

0.1.5

92 release files

0.1.4

92 release files

0.1.3

92 release files

0.1.2

92 release files

0.1.0

92 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page