{h3o}
is a lightweight R package for interacting with Uber’s H3
Geospatial Indexing system. The R package
uses extendr to wrap the eponymous h3o
Rust crate, which offers a pure Rust
implementation of H3, so no linking to Uber’s H3 C library. The package
is also intended to work with the
{sf}
package for geometric
operations and as a bonus represents the H3 class as
{vctrs}
, so they work seamlessly
within a tidyverse workflow.
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("JosiahParry/h3o")
H3 vectors can be created from POINT
geometry columns (sfc
objects)
defined by sf.
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
)
pnts |> mutate(h3 = h3_from_points(geometry, 5))
#> Simple feature collection with 100 features and 1 field
#> Geometry type: POINT
#> Dimension: XY
#> Bounding box: xmin: -4.687389 ymin: 40.09688 xmax: 9.911612 ymax: 49.97784
#> Geodetic CRS: WGS 84
#> First 10 features:
#> geometry h3
#> 1 POINT (4.504552 45.26616) 851f93d7fffffff
#> 2 POINT (1.816329 40.94935) 853946abfffffff
#> 3 POINT (-2.147328 46.43598) 85184183fffffff
#> 4 POINT (3.260273 41.34728) 853940a3fffffff
#> 5 POINT (-1.50323 46.38411) 85186a53fffffff
#> 6 POINT (0.1499664 42.05342) 85397567fffffff
#> 7 POINT (4.900316 41.40955) 85394cdbfffffff
#> 8 POINT (-1.195541 46.94178) 85186e6bfffffff
#> 9 POINT (9.911612 49.19627) 851faa37fffffff
#> 10 POINT (-3.434653 45.27516) 85184ecffffffff
H3 vectors also have an st_as_sfc()
method which allows conversion of
H3 cell indexes into sf POLYGON
s.
# 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")
H3Edge
vectors representing the boundaries of H3 cells can be created
with h3_edges()
, h3_shared_edge_pairwise()
, and
h3_shared_edge_sparse()
.
cell_edges <- h3_edges(h3s[1:3])
cell_edges
#> [[1]]
#> <H3Edge[6]>
#> [1] 1141f93dffffffff 1241f93dffffffff 1341f93dffffffff 1441f93dffffffff
#> [5] 1541f93dffffffff 1641f93dffffffff
#>
#> [[2]]
#> <H3Edge[6]>
#> [1] 1143946bffffffff 1243946bffffffff 1343946bffffffff 1443946bffffffff
#> [5] 1543946bffffffff 1643946bffffffff
#>
#> [[3]]
#> <H3Edge[6]>
#> [1] 11418419ffffffff 12418419ffffffff 13418419ffffffff 14418419ffffffff
#> [5] 15418419ffffffff 16418419ffffffff
We’ve created a list of each cell’s edges. We can flatten them using
flatten_edges()
.
cell_edges <- flatten_edges(cell_edges)
cell_edges
#> <H3Edge[18]>
#> [1] 1141f93dffffffff 1241f93dffffffff 1341f93dffffffff 1441f93dffffffff
#> [5] 1541f93dffffffff 1641f93dffffffff 1143946bffffffff 1243946bffffffff
#> [9] 1343946bffffffff 1443946bffffffff 1543946bffffffff 1643946bffffffff
#> [13] 11418419ffffffff 12418419ffffffff 13418419ffffffff 14418419ffffffff
#> [17] 15418419ffffffff 16418419ffffffff
These can be cast to sfc objects using st_as_sfc()
.
st_as_sfc(cell_edges)
#> Geometry set for 18 features
#> Geometry type: LINESTRING
#> Dimension: XY
#> Bounding box: xmin: -2.518472 ymin: 40.84516 xmax: 4.818449 ymax: 46.60887
#> Geodetic CRS: WGS 84
#> First 5 geometries:
#> LINESTRING (4.748771 44.94225, 4.818449 45.17055)
#> LINESTRING (4.201051 44.99631, 4.440499 44.85539)
#> LINESTRING (4.440499 44.85539, 4.748771 44.94225)
#> LINESTRING (4.578042 45.31175, 4.268915 45.22437)
#> LINESTRING (4.818449 45.17055, 4.578042 45.31175)
Additionally, you can get the vertexes of H3 cell indexes using
h3_to_vertexes()
which returns an sfc_MULTIPOINT
.
h3_to_vertexes(h3s)
#> Geometry set for 100 features
#> Geometry type: MULTIPOINT
#> Dimension: XY
#> Bounding box: xmin: -5.049869 ymin: 39.94716 xmax: 10.37721 ymax: 50.35027
#> Geodetic CRS: WGS 84
#> First 5 geometries:
#> MULTIPOINT ((4.268915 45.22437), (4.201051 44.9...
#> MULTIPOINT ((1.563896 41.22113), (1.508041 40.9...
#> MULTIPOINT ((-2.164091 46.60887), (-2.470648 46...
#> MULTIPOINT ((2.941338 41.43962), (2.880845 41.2...
#> MULTIPOINT ((-1.602187 46.52355), (-1.907502 46...
Since h3o is written in Rust, it is very fast.
h3_strs <- as.character(h3s)
bench::mark(
h3o = st_as_sfc(h3s),
h3jsr = h3jsr::cell_to_polygon(h3_strs)
)
#> # A tibble: 2 × 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 h3o 432.3µs 477.7µs 1972. 9.85KB 14.6
#> 2 h3jsr 8.19ms 8.83ms 110. 2.69MB 89.4
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
)
#> # A tibble: 2 × 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 h3o 4.96ms 5.29ms 185. 21.4KB 11.2
#> 2 h3jsr 28.79ms 29.5ms 33.8 748.7KB 5.19
bench::mark(
h3o = h3_from_points(pnts$geometry, 3),
h3jsr = h3jsr::point_to_cell(pnts$geometry, 3),
check = FALSE
)
#> # A tibble: 2 × 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 h3o 105.2µs 124.5µs 7114. 848B 11.7
#> 2 h3jsr 2.57ms 2.96ms 329. 975KB 8.49
bench::mark(
h3o = h3_edges(h3s),
h3jsr = h3jsr::get_udedges(h3_strs),
check = FALSE
)
#> # A tibble: 2 × 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 h3o 351.7µs 509.5µs 1674. 848B 13.1
#> 2 h3jsr 1.72ms 2.88ms 363. 67.9KB 16.2
# 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
)
#> # A tibble: 2 × 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 h3o 14.3µs 22µs 40992. 7.86KB 16.4
#> 2 h3jsr 646.1µs 741µs 1250. 19.82KB 15.4