-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathREADME.Rmd
251 lines (159 loc) · 5.93 KB
/
README.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
---
output: github_document
editor_options:
chunk_output_type: console
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#",
fig.path = "man/figures/README-",
out.width = "100%"
)
```
# h3r
**h3r** is an interface to [{h3lib}](https://github.com/SymbolixAU/h3lib), which is itself a wrapper around Uber's [H3](https://h3geo.org/) library. See their [getting started](https://h3geo.org/docs/) guide for all the details.
The wrappers are all vectorised, meaning each input can take a vector, and / or return a vector.
e.g:
```{r}
h3r::latLngToCell(
lat = c(-37.820197, -37.818476)
, lng = c(144.983324, 144.967354)
, resolution = c(1L, 14L)
)
```
Most of the [H3 API](https://h3geo.org/docs/api/indexing/) is included in this package as R functions. The only exceptions are
- stringToH3
- h3ToString
- gridDisksUnsafe
- cellToChildrenSize
- uncompactCellsSize
- maxPolygonToCellsSize
- cellsToLinkedMultipolygon
- destroyLInkedMultiPolygon
However, these should all be accessible through the C API
## Design Choices
- There is no `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 possible
- All H3Indexes are returned as the String representation. If you want the `H3Index` / `uint64_t` type you need to use the C / C++ functions directly
- We're using the same code conventions as per the H3 API
- CamelCase function names (e.g. `latLngToCell`)
- lat/lng, rather than lat/lon or lon/lat
- Functions which return a single coordinate pair for each input return a `data.frame`
```{r}
h3r::cellToLatLng(cell = c("8cbe63562a54bff","8cbe635631103ff"))
```
- Functions which return a multiple coordinates for each input return a list of `data.frames`
```{r}
h3r::cellToBoundary(cell = c("8cbe63562a54bff","8cbe635631103ff"))
```
## API
To use the source C / C++ code in your own package you should only need to include `inst/include/h3rapi.h`
## C API
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
1. DESCRIPTION
2. `src/init.c`
3. `src/myCode.c` - i.e., your own C code
4. `R/myRCode.R` - i.e., your own R code
5. Register the dynamic library
---
### DESCRIPTION
Depend & Link to {h3r}
```
Depends:
h3r
LinkingTo:
h3r
```
### src/init.c
Define the functions you want to import from {h3r}
```c
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");
}
```
### src/h3rc.c
Include the "h3rapi.h" header, then you can call the functions you've registered in `src/init.c`
```c
#include "h3rapi.h"
SEXP h3rcLatLngToCell(SEXP lat, SEXP lng, SEXP res) {
return h3rLatLngToCell(lat, lng, res);
}
```
### R/h3rr.R
Call the function you've defined in `h3rc.c` from within an R function
```r
#' @export
ll2Cell <- function(lat, lon, res) {
.Call(h3rcLatLngToCell, lat, lon, res)
}
```
### R/<somewhere.R>
Register your dynamic routines. If using Roxygen to build and document your pacakge you can specify
```r
#' @useDynLib h3rc, .registration = TRUE
NULL
```
and it will be built and added to your NAMESPACE automatically
## C++ API
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
1. DESCRIPTION
3. `src/myCode.cpp` - i.e., your own C++ code
4. `R/myRCode.R` - i.e., your own R code
5. Register the dynamic library
The 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()`
```{r}
h3r::cellToBoundary(cell = c("8cbe63562a54bff","8cbe635631103ff"))
```
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:
1. `h3r::cellToBoundary` - get the boundaries of each cell
2. `geometries::collapse_list()` - makes a single list, with three vectors
- id
- lat
- lng
3. `sfheaders::sf_polygon()` - convert the result into an `sf` object
```{r}
library(Rcpp)
library(sf) ## for the `sf.print` method
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"))
```