h3r is an interface to {h3lib}, which is itself a wrapper around Uber’s H3 library. See their getting started guide for all the details.
The wrappers are all vectorised, meaning each input can take a vector, and / or return a vector.
e.g:
h3r::latLngToCell(
lat = c(-37.820197, -37.818476)
, lng = c(144.983324, 144.967354)
, resolution = c(1L, 14L)
)
# [1] "81be7ffffffffff" "8ebe6356311035f"
Most of the H3 API is included in this package as R functions. The only exceptions are
However, these should all be accessible through the C API
h3
class, or nice printing, or any fancy sugar-coating of what’s returned from the functions. I’ve kept the outputs as raw / primitive as possibleH3Index
/ uint64_t
type you need to use the C / C++ functions directlylatLngToCell
)data.frame
h3r::cellToLatLng(cell = c("8cbe63562a54bff","8cbe635631103ff"))
# lat lng
# 1 -37.82023 144.9832
# 2 -37.81844 144.9674
data.frames
h3r::cellToBoundary(cell = c("8cbe63562a54bff","8cbe635631103ff"))
# $`8cbe63562a54bff`
# lat lng
# 1 -37.82030 144.9833
# 2 -37.82019 144.9833
# 3 -37.82012 144.9832
# 4 -37.82016 144.9831
# 5 -37.82026 144.9831
# 6 -37.82033 144.9832
#
# $`8cbe635631103ff`
# lat lng
# 1 -37.81851 144.9675
# 2 -37.81840 144.9675
# 3 -37.81833 144.9674
# 4 -37.81837 144.9673
# 5 -37.81847 144.9673
# 6 -37.81854 144.9674
To use the source C / C++ code in your own package you should only need to include inst/include/h3rapi.h
In /inst/capi
you’ll find a demo package {h3rc}. This shows how to include / call the C code from {h3r} into another package.
The main components you need to address are
src/init.c
src/myCode.c
- i.e., your own C codeR/myRCode.R
- i.e., your own R codeDepend & Link to {h3r}
Depends:
h3r
LinkingTo:
h3r
Define the functions you want to import from {h3r}
SEXP (*h3rLatLngToCell)(SEXP,SEXP,SEXP);
void R_init_h3rc(DllInfo *info)
{
R_registerRoutines(info, NULL, callMethods, NULL, NULL);
R_useDynamicSymbols(info, FALSE);
/* Imports from h3r */
h3rLatLngToCell = (SEXP(*)(SEXP,SEXP,SEXP)) R_GetCCallable("h3r", "h3rLatLngToCell");
}
Include the “h3rapi.h” header, then you can call the functions you’ve registered in src/init.c
#include "h3rapi.h"
SEXP h3rcLatLngToCell(SEXP lat, SEXP lng, SEXP res) {
return h3rLatLngToCell(lat, lng, res);
}
Call the function you’ve defined in h3rc.c
from within an R function
Register your dynamic routines. If using Roxygen to build and document your pacakge you can specify
and it will be built and added to your NAMESPACE automatically
In /inst/cppapi
you’ve find a demo package {h3rcpp}. This shows how to include / call the C++ code from {h3r} into another package.
The main components you need to address are
src/myCode.cpp
- i.e., your own C++ codeR/myRCode.R
- i.e., your own R codeThe example package shows how to do this, and it’s very similar to the C
exmple above. So I’m not going to repeate it.
Instead I’m going to show you an example of how you might want to use it
Consider the output of cellToBoundary()
h3r::cellToBoundary(cell = c("8cbe63562a54bff","8cbe635631103ff"))
# $`8cbe63562a54bff`
# lat lng
# 1 -37.82030 144.9833
# 2 -37.82019 144.9833
# 3 -37.82012 144.9832
# 4 -37.82016 144.9831
# 5 -37.82026 144.9831
# 6 -37.82033 144.9832
#
# $`8cbe635631103ff`
# lat lng
# 1 -37.81851 144.9675
# 2 -37.81840 144.9675
# 3 -37.81833 144.9674
# 4 -37.81837 144.9673
# 5 -37.81847 144.9673
# 6 -37.81854 144.9674
This gives a list of 2 elements, and each element contains lat/lng coordinates.
You can use the C++ h3r::cellToBoundary()
in your own workflow, and using other R packages that allow you to link to the source code.
In this example I’m using {geometries}
and {sfheaders}
to convert to a valid {sf}
object.
The steps inside this function are:
h3r::cellToBoundary
- get the boundaries of each cellgeometries::collapse_list()
- makes a single list, with three vectorssfheaders::sf_polygon()
- convert the result into an sf
object
library(Rcpp)
library(sf) ## for the `sf.print` method
# Linking to GEOS 3.11.0, GDAL 3.5.3, PROJ 9.1.0; sf_use_s2() is TRUE
cppFunction(
depends = c("h3r", "geometries", "sfheaders") # you need `sfheaders` installed
, includes = c(
'#include "geometries/utils/lists/collapse.hpp"'
, '#include "sfheaders/sf/sf.hpp"'
, '#include "h3rapi.h"'
)
, code = '
SEXP sfBoundary(Rcpp::StringVector cells) {
R_xlen_t n = Rf_xlength(cells);
// convert to latLng boundaries
Rcpp::List boundaries = h3r::cellToBoundary(cells);
// need to account for any pentagons
Rcpp::IntegerVector n_pentagons = h3r::isPentagon(cells);
R_xlen_t n_pentagon = n_pentagons[0];
R_xlen_t row_count = (n_pentagon * 5) + ((n - n_pentagon) * 6);
// _collapse_ the boundaries to a list of three vectors
// col0: id
// col1: lat
// col2: lng
Rcpp::List geometries = geometries::utils::collapse_list(boundaries, row_count);
// the `sfheaders` api expects a data.frame or a matrix
Rcpp::DataFrame df = Rcpp::as< Rcpp::DataFrame >(geometries);
Rcpp::IntegerVector idCol = {0};
Rcpp::IntegerVector geometryCol = {1, 2};
return sfheaders::api::sf_polygon(df, geometryCol, idCol, R_NilValue, "XY", false, true);
}
'
)
sfBoundary(cell = c("8cbe63562a54bff","8cbe635631103ff"))
# Simple feature collection with 2 features and 1 field
# Geometry type: POLYGON
# Dimension: XY
# Bounding box: xmin: -37.82033 ymin: 144.9673 xmax: -37.81833 ymax: 144.9833
# CRS: NA
# id geometry
# 1 1 POLYGON ((-37.8203 144.9833...
# 2 2 POLYGON ((-37.81851 144.967...