h3o is now on CRAN‼️

Release

R users now have access to a pure Rust implementation of Uber’s H3, a hexagonal geospatial grid and indexing system.

Authors

Blake Vernon

Josiah Parry

Published

September 9, 2025

The extendr-powered R package h3o provides access to a pure Rust implementation of Uber’s H3 Geospatial Indexing System. Originally developed by Josiah Parry, the R package has also become an official product of extendr, which means community support 🤝 and maintenance 🏗️ into the foreseeable future.

h3o at a glance 👀

The package provides functionality to interact with H3’s grid as vectors, which can be converted to and from sf geometries.

library(h3o)
library(dplyr)
library(sf)
library(tibble)

xy <- data.frame(
  x = runif(100, -5, 10),
  y = runif(100, 40, 50)
)

pnts <- st_as_sf(
  xy,
  coords = c("x", "y"),
  crs = 4326
)

mutate(pnts, h3 = h3_from_points(geometry, 5))
Simple feature collection with 100 features and 1 field
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -4.861231 ymin: 40.05397 xmax: 9.846006 ymax: 49.98446
Geodetic CRS:  WGS 84
First 10 features:
                     geometry              h3
1   POINT (3.169478 45.06344) 851f92d7fffffff
2    POINT (5.63995 44.91631) 851f9323fffffff
3   POINT (1.263684 42.55526) 85396207fffffff
4  POINT (0.3876174 41.06653) 853973b3fffffff
5  POINT (-1.192501 44.22128) 85184d7bfffffff
6    POINT (3.86172 41.56535) 8539419bfffffff
7  POINT (-1.565639 49.98446) 851866bbfffffff
8   POINT (3.704704 42.74759) 85396e47fffffff
9    POINT (2.804652 47.0575) 851fb267fffffff
10 POINT (-4.021278 47.52185) 851846affffffff

You can use the st_as_sfc() method to convert H3 hexagons to sf POLYGONs.

# replace geometry
h3_cells <- pnts |>
  mutate(
    h3 = h3_from_points(geometry, 4),
    geometry = st_as_sfc(h3)
  )

# plot the hexagons
plot(st_geometry(h3_cells))

H3 cell centroids can be returned using h3_to_points(). If sf is avilable, the results will be returned as an sfc (sf column) object. Otherwise it will return a list of sfg (sf geometries).

# fetch h3 column
h3s <- h3_cells[["h3"]]

# get there centers
h3_centers <- h3_to_points(h3s)

# plot the hexagons with the centers
plot(st_geometry(h3_cells))
plot(h3_centers, pch = 16, add = TRUE, col = "black")

H3 at light speed ⚡

Because it builds on a pure Rust implementation, h3o is also very very fast. Here are some benchmarks, which also serve to showcase h3o tools.

Creating polygons

h3_strs <- as.character(h3s)
bench::mark(
  h3o = st_as_sfc(h3s),
  h3jsr = h3jsr::cell_to_polygon(h3_strs),
  relative = TRUE
)
# A tibble: 2 × 6
  expression   min median `itr/sec` mem_alloc `gc/sec`
  <bch:expr> <dbl>  <dbl>     <dbl>     <dbl>    <dbl>
1 h3o          1      1        26.1        1      1   
2 h3jsr       27.0   25.6       1        268.     3.85

Converting polygons to H3 cells:

nc <- st_read(system.file("gpkg/nc.gpkg", package = "sf"), quiet = TRUE) |>
  st_transform(4326) |>
  st_geometry()

bench::mark(
  h3o = sfc_to_cells(nc, 5, "centroid"),
  h3jsr = h3jsr::polygon_to_cells(nc, 5),
  check = FALSE,
  relative = TRUE
)
# A tibble: 2 × 6
  expression   min median `itr/sec` mem_alloc `gc/sec`
  <bch:expr> <dbl>  <dbl>     <dbl>     <dbl>    <dbl>
1 h3o         1      1         7.79       1       5.84
2 h3jsr       8.96   7.82      1         33.0     1   

Converting points to cells

bench::mark(
  h3o = h3_from_points(pnts$geometry, 3),
  h3jsr = h3jsr::point_to_cell(pnts$geometry, 3),
  check = FALSE,
  relative = TRUE
)
# A tibble: 2 × 6
  expression   min median `itr/sec` mem_alloc `gc/sec`
  <bch:expr> <dbl>  <dbl>     <dbl>     <dbl>    <dbl>
1 h3o          1      1        16.8        1      1.02
2 h3jsr       16.7   19.4       1       1193.     1   

Retrieve edges

bench::mark(
  h3o = h3_edges(h3s),
  h3jsr = h3jsr::get_udedges(h3_strs),
  check = FALSE,
  relative = TRUE
)
# A tibble: 2 × 6
  expression   min median `itr/sec` mem_alloc `gc/sec`
  <bch:expr> <dbl>  <dbl>     <dbl>     <dbl>    <dbl>
1 h3o         1      1         2.95      1        1   
2 h3jsr       2.97   2.83      1         7.02     2.65

Get origins and destinations from edges.

# get edges for a single location
eds <- h3_edges(h3s[1])[[1]]
# strings for h3jsr
eds_str <- as.character(eds)

bench::mark(
  h3o = h3_edge_cells(eds),
  h3jsr = h3jsr::get_udends(eds_str),
  check = FALSE,
  relative = TRUE
)
# A tibble: 2 × 6
  expression   min median `itr/sec` mem_alloc `gc/sec`
  <bch:expr> <dbl>  <dbl>     <dbl>     <dbl>    <dbl>
1 h3o          1      1        24.6      1        1   
2 h3jsr       21.3   26.1       1        2.52     3.01

Installation 📦

You can install the release version of h3o from CRAN with:

install.packages("h3o")

Or you can install the development version from GitHub with:

# install.packages("pak")
pak::pak("extendr/h3o")

Learn more 🧑‍🎓

See the package documentation for more details: http://extendr.rs/h3o/.

If you encounter a bug or would like to request new features, head over to the GitHub repository: https://github.com/extendr/h3o.