Highlights

August 17, 2025 Β· View on GitHub

This is a Polars extension that adds support for the H3 discrete global grid system, so you can index points and geometries to hexagons directly in Polars. All credits goes to the h3o for doing the heavy lifting.

MIT License PyPI Latest Release

Highlights

  • πŸš€ Blazing Fast: Built entirely in Rust, offering vectorized, multi-core H3 operations within Polars. Ideal for high-performance data processing.

  • 🌍 H3 Feature Parity: Comprehensive support for H3 functions, covering almost everything the standard H3 library provides, excluding geometric functions.

  • πŸ“‹ Fully Tested & Used in Production: Thoroughly tested against the standard H3 library.

  • πŸ” Data Type Agnostic: Supports string and integer H3 indexes natively, eliminating format conversion hassles.

Get started

You can get started by installing it with pip (or uv):

pip install polars-h3

You can use the extension as a drop-in replacement for the standard H3 functions.

import polars_h3 as plh3
import polars as pl

df = (
    pl.DataFrame(
        {
            "lat": [37.7749],
            "long": [-122.4194],
        }
    )
    .with_columns(
        plh3.latlng_to_cell(
            "lat",
            "long",
            resolution=7,
            return_dtype=pl.Utf8,
        ).alias("h3_cell")
    )
)

print(df)
shape: (1, 3)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ lat     ┆ long      ┆ h3_cell         β”‚
β”‚ ---     ┆ ---       ┆ ---             β”‚
β”‚ f64     ┆ f64       ┆ str             β”‚
β•žβ•β•β•β•β•β•β•β•β•β•ͺ═══════════β•ͺ═════════════════║
β”‚ 37.7749 ┆ -122.4194 ┆ 872830828ffffff β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Check out the quickstart notebook for more examples.

🌟 You can also find the advanced notebooks here.

Implemented functions

This extension implements most of the H3 API. The full list of functions is below - you can find full docs here.

⚠️ Performance Note: When possible, prefer using pl.UInt64 for H3 indices instead of the pl.Utf8 representation. String representations require casting operations which impact performance. Working directly with the native 64-bit integer format provides better computational efficiency.

We are unable to support the functions that work with geometries.

Full list of functions

βœ… = Supported 🚧 = Pending πŸ›‘ = Not supported

FunctionDescriptionSupported
latlng_to_cellConvert latitude/longitude coordinate to cell IDβœ…
cell_to_latConvert cell ID to latitudeβœ…
cell_to_lngConvert cell ID to longitudeβœ…
cell_to_latlngConvert cell ID to latitude/longitudeβœ…
get_resolutionGet resolution number of cell IDβœ…
str_to_intConvert pl.Utf8 cell ID to pl.UInt64βœ…
int_to_strConvert pl.UInt64 or pl.Int64 cell ID to pl.Utf8βœ…
is_valid_cellTrue if this is a valid cell IDβœ…
is_res_class_iiiTrue if the cell's resolution is class IIIβœ…
is_pentagonTrue if the cell is a pentagonβœ…
get_icosahedron_facesList of icosahedron face IDs the cell is onβœ…
cell_to_parentGet coarser cell for a cellβœ…
cell_to_childrenGet finer cells for a cellβœ…
cell_to_center_childProvides the center child (finer) cell contained by cell at resolution childRes.βœ…
cell_to_child_posPosition of the child cell within the ordered list of all children of its parent at the specified resolutionβœ…
child_pos_to_cellChild cell at a given position within the ordered list of children for a specified parent/resolutionβœ…
compact_cellsCompacts a collection of H3 cells (all same resolution) by replacing child cells with their parent if all children existβœ…
uncompact_cellsUncompacts a set of H3 cells to the resolution resβœ…
grid_ringProduces the "hollow ring" of cells which are exactly grid distance k from the origin cellβœ…
grid_diskProduces the "filled-in disk" of cells at most grid distance k from the origin cellβœ…
grid_path_cellsFind a grid path to connect two cellsβœ…
grid_distanceFind the grid distance between two cellsβœ…
cell_to_local_ijConvert a cell ID to a local I,J coordinate spaceβœ…
local_ij_to_cellConvert a local I,J coordinate to a cell IDβœ…
cell_to_boundaryConvert cell ID to its boundary lat/lng coordinatesβœ…
cell_to_vertexGet the vertex ID for a cell ID and vertex numberβœ…
cell_to_vertexesGet all vertex IDs for a cell ID (5 for pentagon, 6 for hex)βœ…
vertex_to_latlngConvert a vertex ID to latitude/longitude coordinatesβœ…
is_valid_vertexTrue if passed a valid vertex IDβœ…
is_valid_directed_edgeTrue if passed a valid directed edge IDβœ…
origin_to_directed_edgesGet all directed edge IDs for a cell IDβœ…
directed_edge_to_cellsConvert a directed edge ID to origin/destination cell IDsβœ…
get_directed_edge_originConvert a directed edge ID to origin cell IDβœ…
get_directed_edge_destinationConvert a directed edge ID to destination cell IDβœ…
cells_to_directed_edgeConvert an origin/destination pair to directed edge IDβœ…
are_neighbor_cellsTrue if the two cell IDs share an edgeβœ…
average_hexagon_areaGet average area of a hexagon cell at resolutionβœ…
cell_areaGet the area of a cell IDβœ…
average_hexagon_edge_lengthAverage hexagon edge length at resolutionβœ…
edge_lengthGet the length of a directed edge IDβœ…
get_num_cellsGet the number of cells at a resolutionβœ…
get_pentagonsGet all pentagons at a resolutionβœ…
great_circle_distanceCompute the great circle distance between two points (haversine)βœ…
cells_to_multi_polygon_wktConvert a set of cells to multipolygon WKTπŸ›‘
polygon_wkt_to_cellsConvert polygon WKT to a set of cellsπŸ›‘
directed_edge_to_boundary_wktConvert directed edge ID to linestring WKTπŸ›‘

Plotting

The library also comes with helper functions to plot hexes on a Folium map.

import polars_h3 as plh3
import polars as pl

hex_map = plh3.graphing.plot_hex_outlines(df, "h3_cell")
display(hex_map)

# or if you have a metric to plot

hex_map = plh3.graphing.plot_hex_fills(df, "h3_cell", "metric_col")
display(hex_map)

CleanShot 2024-12-08 at 00 26 22

Development

It's recommended to use uv to manage the extension's python dependencies. If you modify rust code, you will need to run uv run maturin develop --uv to see changes.

You can run test suite with uv run pytest. You can also run the docs locally with uv run mkdocs serve.

Benchmarking

If you're looking to benchmark the performance of the extension, build the release version with maturin develop --release --uv and then run uv run -m benchmarks.engine (assuming you have the benchmark dependencies installed). Benchmarking with the development version will lead to misleading results.

# 1 – (build) compile the optimized Rust extension
uv run maturin develop --release --uv

# 2 – (run) execute the benchmark CLI
uv run h3-bench \
  --libraries plh3 duckdb h3_py \   # which back-ends to test (or β€œall”)
  --functions latlng_to_cell cell_to_parent \  # which functions to time (or β€œall”)
  --iterations 3 \                  # repetitions per test
  --fast-factor 4 \                 # divide default row-counts to speed things up
  --output results.json             # optional: dump raw results