-
Notifications
You must be signed in to change notification settings - Fork 0
/
README.Rmd
226 lines (156 loc) · 5.22 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
---
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%"
)
```
# h3lib
## What is `h3lib`?
`h3lib` is an R pacakge. But it's also **not** an R package.
There aren't any R functions you can call, nor can you import this package as-is into your own.
## So, what _is_ `h3lib`?
`h3lib` exposes [Uber's H3](https://h3geo.org/) source files (written in C) through Callable routines.
It's these routines that you can then use in your own package.
## How do I call these routines?
Firstly, take a look at `/src/init.c`. You'll see a whole load of `R_RegisterCCallable()` calls:
```
// Indexing
R_RegisterCCallable("h3lib", "latLngToCell", (DL_FUNC) &latLngToCell);
R_RegisterCCallable("h3lib", "cellToLatLng", (DL_FUNC) &cellToLatLng);
R_RegisterCCallable("h3lib", "cellToBoundary", (DL_FUNC) &cellToBoundary);
```
These are essentially saying
> Here are some C functions I'd like you to register and make available to other packages
Now look in `/inst/include/h3libapi.h`. Here you'll see all the function definitions for these registered routines
```
// Indexing
inline H3Error latLngToCell(const LatLng *g, int res, H3Index *out) {
H3Error(*fun)(const LatLng*, int, H3Index*) =
(H3Error(*)(const LatLng*, int, H3Index*)) R_GetCCallable("h3lib","latLngToCell");
return fun(g, res, out);
}
```
The registered routines in `init.c` and the definitions in `h3libapi.h` should match. If there's something missing, please let us know!
## That explains which routines _can_ be called, but you haven't said _how_ to call them?
Yes, I'm getting to that.
To demonstrate how to actually **call** these routines, let's use the [h3r](https://github.com/symbolixau/h3r) package as an example.
Here are the steps to call the `latLngToCell` routine defined in `h3lib`
### DESCRIPTION
In the `DESCRIPTION` file you need to link to {h3lib}
```
Depends:
h3lib
LinkingTo:
h3lib
```
### src/init.c
If you look in the `/src/` directory you'll see various `.c` files.
The `init.c` also contains some `R_RegisterCCallable()` (because it too is exposing some of its C code to the wider R ecosystem).
But it also defines which Routines it wants to **import**
```
H3Error (*latLngToCell);
H3Error (*cellToLatLng);
H3Error (*cellToBoundary);
... etc
```
### src/h3rIndexing.c
Then in a .c file, you need to include the `h3libapi.h` file, and that's it!
(I've highlighted the bit of code that calls the `latLngToCell` routine)
```
#include "h3libapi.h"
SEXP h3rLatLngToCell(SEXP lat, SEXP lon, SEXP res) {
R_xlen_t n = Rf_xlength(lat);
R_xlen_t i;
SEXP cells = PROTECT(Rf_allocVector(STRSXP, n));
LatLng latLng;
H3Index h3Index;
// char str[17];
for( i = 0; i < n; i++ ) {
int ires = INTEGER(res)[i];
sexpToLatLng(&latLng, lat, lon, i);
h3error(latLngToCell(&latLng, ires, &h3Index), i); // <-- LOOK HERE; this is calling h3lib latLngToCell
// h3ToString(h3Index, str, 17);
SET_STRING_ELT(cells, i, h3ToSexpString(h3Index));
}
UNPROTECT(1);
return cells;
}
```
## Working Example
Let's build our own example that uses `cellToLatLng` to build `{sf}` polygons.
For this we can link to both `{h3lib}` and `{sfheaders}`
```r
library(Rcpp)
cppFunction(
depends = c(
"h3lib"
, "geometries" ## <- required because sfheaders depends on it
, "sfheaders" ## <- for building sf objects through C++ code
)
, includes = c(
'#include "h3libapi.h"'
, '#include "sfheaders/sfg/polygon/sfg_polygon.hpp"'
)
, code = '
SEXP cellToPolygon(SEXP h3) {
R_xlen_t n = Rf_xlength(h3);
R_xlen_t i, j;
Rcpp::List poylgons(n); // for storing the sfg_polygons
for(i = 0; i < n; ++i) {
H3Index idx;
CellBoundary cb;
// convert the SEXP cell (stringVector) to H3Index
int e1 = stringToH3(CHAR(STRING_ELT(h3, i)), &idx);
// TODO: handle the error-code `e1`
// Convert H3Index to CellBoundary object
int e2 = cellToBoundary(idx, &cb);
// TODO: handle the rror-code `e2`;
// Convert the CellBoundary to sfg_polygon
// where sfheaders::sfg_polygon accepts a matrix or data-frame
Rcpp::NumericMatrix latLng(cb.numVerts, 2);
for(j = 0; j < cb.numVerts; ++j) {
latLng(j, 0) = radsToDegs(cb.verts[i].lng);
latLng(j, 1) = radsToDegs(cb.verts[j].lat);
}
poylgons[i] = sfheaders::sfg::sfg_polygon(latLng, "XY");
}
return poylgons;
}
'
)
cellToPolygon(c("8cbe63562a54bff","8cbe635631103ff"))
# [[1]]
# [[1]]
# [,1] [,2]
# [1,] 144.9833 -37.82030
# [2,] 144.9833 -37.82019
# [3,] 144.9833 -37.82012
# [4,] 144.9833 -37.82016
# [5,] 144.9833 -37.82026
# [6,] 144.9833 -37.82033
# [7,] 144.9833 -37.82030
#
# attr(,"class")
# [1] "XY" "POLYGON" "sfg"
#
# [[2]]
# [[1]]
# [,1] [,2]
# [1,] 144.9675 -37.81851
# [2,] 144.9675 -37.81840
# [3,] 144.9675 -37.81833
# [4,] 144.9675 -37.81837
# [5,] 144.9675 -37.81847
# [6,] 144.9675 -37.81854
# [7,] 144.9675 -37.81851
#
# attr(,"class")
# [1] "XY" "POLYGON" "sfg"
```