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.2

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

Built distributions (wheels)

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

Total release size: 53.7 MB

Release files / h3_bound_cells-0.2.2.tar.gz

Download URL h3_bound_cells-0.2.2.tar.gz
Size 5.5 MB
Tags Source
SHA-256 checksum
How to use checksums
e0ccc5fa9b704d27ee95997188466d05e5fd54ac3400d8ed53138c2e4ee9d0d6
BLAKE2b-256 checksum
How to use checksums
068c6bf7f8e3ff6e76a5fe8bd02d47652f6a80276dfcde056dc563e187fa43a0
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.2.2-cp39-abi3-win_arm64.whl

Download URL h3_bound_cells-0.2.2-cp39-abi3-win_arm64.whl
Size 4.4 MB
Tags CPython 3.9 Windows ARM64 abi3
SHA-256 checksum
How to use checksums
3467c699de06366a1ef4a48c720ace1e4c34ca25fcff7f1738d20fe0f3aa4698
BLAKE2b-256 checksum
How to use checksums
372d2efa5d745d312f393dcfcc84b858fba86d4f7f3bd16b752d1f5726088344
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.2.2-cp39-abi3-win_amd64.whl

Download URL h3_bound_cells-0.2.2-cp39-abi3-win_amd64.whl
Size 4.9 MB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
71d52677abbe5cd05cfb3777fe76b2809eb97f2b065b2ff6ac1bcfa8d35a177b
BLAKE2b-256 checksum
How to use checksums
39dc946f96a91e8dc6025fc32ed0183acfa9ce48ba1070c8f0b984c695649801
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.2.2-cp39-abi3-win32.whl

Download URL h3_bound_cells-0.2.2-cp39-abi3-win32.whl
Size 4.3 MB
Tags CPython 3.9 Windows x86-32 abi3
SHA-256 checksum
How to use checksums
334c8a3fdea3ff03f3fb4bb9774f04e86ed08d4b657db87e1ec532325712bcfc
BLAKE2b-256 checksum
How to use checksums
df3d30d3a7b426a77cb072625ad03849ad6d546b2baa4c480b1a2ea92449b0cf
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.2.2-cp39-abi3-musllinux_1_2_x86_64.whl

Download URL h3_bound_cells-0.2.2-cp39-abi3-musllinux_1_2_x86_64.whl
Size 5.1 MB
Tags CPython 3.9 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
88d25245046cbe147a9e582efdb111756d2b6b29e44350718c31663ed0dc2f68
BLAKE2b-256 checksum
How to use checksums
433cb268ec6e425ae9ea0441fb84802d486a5d1418bab1fbf248cf091f87dc78
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.2.2-cp39-abi3-musllinux_1_2_i686.whl

Download URL h3_bound_cells-0.2.2-cp39-abi3-musllinux_1_2_i686.whl
Size 5.4 MB
Tags CPython 3.9 Linux musl 1.2+ x86-32 abi3
SHA-256 checksum
How to use checksums
6ba9a440d92e205f186f9e10059d399bfebce720ad72287833b1da7d4d66e857
BLAKE2b-256 checksum
How to use checksums
1b1ad1e0c948e5d4b8402fa2516cda9694dc374183f4106849c1f78a655cd50a
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.2.2-cp39-abi3-musllinux_1_2_aarch64.whl

Download URL h3_bound_cells-0.2.2-cp39-abi3-musllinux_1_2_aarch64.whl
Size 4.8 MB
Tags CPython 3.9 Linux musl 1.2+ ARM64 abi3
SHA-256 checksum
How to use checksums
17a9f4d9fd837344c8d7f71b6545b994a21d9564f147437cec1cfbdf50a6fc6c
BLAKE2b-256 checksum
How to use checksums
df399a1ba34ca37459e4c51860317a45874a3c815a0fbf0af8ebaa12aeeea5f1
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.2.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL h3_bound_cells-0.2.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 4.9 MB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
47550f226d7ef06eafb0d74300ba9bc4f78cfae33541c3bab15613ed0e036eae
BLAKE2b-256 checksum
How to use checksums
e1618ede6c84b56b22f0b8ad50393ad6c214a25d64bca49b548e4eb6f7cc4246
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.2.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL h3_bound_cells-0.2.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 4.6 MB
Tags CPython 3.9 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
64fd55f72348446d37c2251a6f318d6b2f527198b4557eeeb3b9e38688deaf04
BLAKE2b-256 checksum
How to use checksums
ade2ae577b93be935a08aa9c5aa3ec06042bf6effc02ccdab676876525ffd0f6
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.2.2-cp39-abi3-manylinux_2_12_i686.manylinux2010_i686.whl

Download URL h3_bound_cells-0.2.2-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
768126efc4f1ecf15fce62c51780886618d07568c5aaa49ec8ad2f0fe9507c7d
BLAKE2b-256 checksum
How to use checksums
2ac3d6246098126cc75941f09a2bf6eb9be21e475a9b1794f9b6513e78f8209d
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.2.2-cp39-abi3-macosx_11_0_arm64.whl

Download URL h3_bound_cells-0.2.2-cp39-abi3-macosx_11_0_arm64.whl
Size 4.4 MB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
0d3289f40bcd856f4714638cfbc391293fae68284f625b801a296da5cddcf22a
BLAKE2b-256 checksum
How to use checksums
c3a83b8c6216ca2ce3d4cc83d5134310a30415243b18795613ae5069f25cec89
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

0.3.1

11 release files

0.3.0

11 release files

This release

0.2.2 This release

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