Skip to content

h3 — H3 index

polars-hash registers this expression on pl.Expr as .h3. H3 is the hierarchical hexagonal grid from Uber. Refer to the H3 website.

H3 divides the earth into hexagons. A geohash uses rectangles. Each hexagonal cell has six neighbors, and the distance to each neighbor is the same. H3 is therefore better than geohash for aggregation and area analysis. Geohash keeps the more simple property that a prefix gives a rectangular area.

All the examples on this page use this data:

import polars as pl
import polars_hash as plh

df = pl.DataFrame(
    {"coord": [{"longitude": -120.6623, "latitude": 35.3003}]},
    schema={
        "coord": pl.Struct(
            [pl.Field("longitude", pl.Float64), pl.Field("latitude", pl.Float64)]
        ),
    },
)
Expression Input Output
from_coords(len) Struct Utf8

from_coords(len)

Encodes a coordinate struct to an H3 cell index. The output is the standard lowercase hex string.

df.select(plh.col("coord").h3.from_coords(5))  # type: ignore
┌─────────────────┐
│ coord           │
│ ---             │
│ str             │
╞═════════════════╡
│ 8529adc7fffffff │
└─────────────────┘

Parameters:

Parameter Type Default Description
len int 12 The H3 resolution, from 1 to 15. A column name (str) or a pl.Expr also operates correctly at run time and gives a different resolution for each row. The type hint does not show this yet.

Input: a Struct with a latitude field and a longitude field. Both fields must be Float32 or Float64. polars-hash casts Float32 to Float64. It finds the fields by name, thus the order of the fields is not important.

Returns: Utf8, a hex cell index with 15 characters. The value contains the resolution. Two indexes with different resolutions are therefore always different.

Resolutions. The approximate average length of a hexagon edge:

len Approximate edge length
1 483 km
3 69 km
5 9.9 km
7 1.4 km
9 200 m
11 29 m
13 4.1 m
15 0.6 m

Errors. Polars raises all of these as ComputeError when it collects the data:

Condition Message
len is less than 1 or more than 15 expected resolution between 1 and 15, got 16
The latitude is outside −90 to 90, the longitude is outside −180 to 180, or a value is NaN or inf invalid coordinate range: latitude 91, longitude -120.6623
len is null Length may not be null
latitude or longitude is not a float Latitude input needs to be float

A null latitude or a null longitude does not cause an error. That row gives null. A float len does not cause an error either. polars-hash casts it to Int64, thus 5.9 gives a resolution of 5.

Resolution 0 is not permitted

H3 has the resolutions 0 to 15. But from_coords accepts only 1 to 15. A len of 0 raises an error. Resolution 0 has 122 base cells and is not a usual level for aggregation.

Different resolution for each row. Give a column name or an expression:

df = pl.DataFrame({"latitude": [35.3003], "longitude": [-120.6623], "n": [5]})
df = df.with_columns(coord=pl.struct(["latitude", "longitude"]))

df.select(plh.col("coord").h3.from_coords("n"))  # type: ignore

All integer data types are permitted, signed and unsigned. polars-hash casts the value to Int64.

This namespace encodes only

The h3 namespace has no expression to decode a cell index or to find the neighbors of a cell. For these operations, use the h3 Python package on the output column. The geohash namespace has both operations. Refer to geohash.to_coords() and geohash.neighbors().