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 --dev
uv run maturin develop --release

The dev dependency group (declared in pyproject.toml) also pulls in flask, h3, polars, and pytest, used by the visualisation server, the tests, and the optional Polars integration below.

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) -> 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).
  • 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 server

A small Flask + MapLibre app for drawing polygons and visualising the output:

just serve
# or:
uv run --dev dev/server.py

Then open http://127.0.0.1:5050/. Draw a polygon, hit Compute Bound Cells, and the area and border layers render colour-coded by resolution. Renders are capped at 50,000 cells; reduce start_res or shrink the polygon if you trip the limit. The page also accepts a pre-computed {"area": {...}, "border": {...}} blob via the Render Cells panel for offline inspection of saved output.

Release files for h3_bound_cells 0.2.0

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.2.0
File Size Uploaded
h3_bound_cells-0.2.0.tar.gz 5.5 MB Details

Built distributions (wheels)

Table of built distributions (wheels) for h3_bound_cells 0.2.0
File
h3_bound_cells-0.2.0-cp39-abi3-win_arm64.whl CPython 3.9 abi3 Windows ARM64 Details
h3_bound_cells-0.2.0-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details
h3_bound_cells-0.2.0-cp39-abi3-win32.whl CPython 3.9 abi3 Windows x86-32 Details
h3_bound_cells-0.2.0-cp39-abi3-musllinux_1_2_x86_64.whl CPython 3.9 abi3 Linux musl 1.2+ x86-64 Details
h3_bound_cells-0.2.0-cp39-abi3-musllinux_1_2_i686.whl CPython 3.9 abi3 Linux musl 1.2+ x86-32 Details
h3_bound_cells-0.2.0-cp39-abi3-musllinux_1_2_aarch64.whl CPython 3.9 abi3 Linux musl 1.2+ ARM64 Details
h3_bound_cells-0.2.0-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.2.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl CPython 3.9 abi3 Linux glibc 2.17+ x86-32 Details
h3_bound_cells-0.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.9 abi3 Linux glibc 2.17+ ARM64 Details
h3_bound_cells-0.2.0-cp39-abi3-macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64 Details

Total release size: 57.5 MB

Release files / h3_bound_cells-0.2.0.tar.gz

Download URL h3_bound_cells-0.2.0.tar.gz
Size 5.5 MB
Tags Source
SHA-256 checksum
How to use checksums
3a75502bb11d64b5ef4d063fd69f1036e574f74be8e916b54e1da030e349f2e1
BLAKE2b-256 checksum
How to use checksums
38c661ac1f8c3d0a83b346e5c7888047cccd1110f4baa9691b1819f54acff56c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","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.2.0-cp39-abi3-win_arm64.whl

Download URL h3_bound_cells-0.2.0-cp39-abi3-win_arm64.whl
Size 4.2 MB
Tags CPython 3.9 Windows ARM64 abi3
SHA-256 checksum
How to use checksums
065530d16404d094babda2be89846d3bc0fbf698a777bf3171c2f3801508bbfe
BLAKE2b-256 checksum
How to use checksums
f594a1c43ff00e365f62887bd65ddaff06c45de1d540f73ed9d37645e97e368d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","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.2.0-cp39-abi3-win_amd64.whl

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

Download URL h3_bound_cells-0.2.0-cp39-abi3-win32.whl
Size 4.1 MB
Tags CPython 3.9 Windows x86-32 abi3
SHA-256 checksum
How to use checksums
e1234b33f4551bfb2851bac6ea11764dd0093455432201d62fce7d7f97e83c8a
BLAKE2b-256 checksum
How to use checksums
5e8ceb248fa4bd65b57232038286a3564eff7c512f909a1bc48b1a566f6b092b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","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.2.0-cp39-abi3-musllinux_1_2_x86_64.whl

Download URL h3_bound_cells-0.2.0-cp39-abi3-musllinux_1_2_x86_64.whl
Size 5.8 MB
Tags CPython 3.9 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
5bfcc8cc3c01ad2e6ed774a6b90cd2e5da0112cd0f5264aefe5daa20d9c9c2f8
BLAKE2b-256 checksum
How to use checksums
42784d6785c44a43f47c59e0e3498d20497838459fa367aa6fec95eda406dcd0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","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.2.0-cp39-abi3-musllinux_1_2_i686.whl

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

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

Download URL h3_bound_cells-0.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 5.5 MB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
32cc6ad63e0aa7070ecce70d76c6b0eba990e260ec30b96499572c196fd27e48
BLAKE2b-256 checksum
How to use checksums
a070fa4b9824986f727eb6518281360da800c7332857307f6f296c844de46869
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","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.2.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl

Download URL h3_bound_cells-0.2.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl
Size 6.2 MB
Tags CPython 3.9 Linux glibc 2.17+ x86-32 abi3
SHA-256 checksum
How to use checksums
9064da50e6e4b6f71a9477625cc764af2c7201f4b01b3e08ffafa14ff13ac8e5
BLAKE2b-256 checksum
How to use checksums
d69c85093ad21eaff346b50b3ba7b39a3c8defb62d086e58b6aa7ec8af11dc94
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","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.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL h3_bound_cells-0.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 5.4 MB
Tags CPython 3.9 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
50d992ac02418c0aac1676f07ab5c8f5bcb3137b41750fad45333da318c25f8d
BLAKE2b-256 checksum
How to use checksums
28db46ae126ea18f53a303ca7e1761a033f20ad10c2f1c54d6012d70ddd88b4d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","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.2.0-cp39-abi3-macosx_11_0_arm64.whl

Download URL h3_bound_cells-0.2.0-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
024d9220d8a3956f7e5c92d95a0ced035891f55385e7db658b02fa74214eca8e
BLAKE2b-256 checksum
How to use checksums
39f0e89a226b4f15437e9733ebd29f37dbfed5cc71303a39596510703a9fc7bc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","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

0.3.1

11 release files

0.3.0

11 release files

0.2.2

11 release files

0.2.1

11 release files

This release

0.2.0 This release

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