-
Notifications
You must be signed in to change notification settings - Fork 7
/
mapboxer.Rmd
321 lines (250 loc) · 11.4 KB
/
mapboxer.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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
---
title: "Get started with mapboxer: Mapbox GL JS for R"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{mapboxer}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
eval = FALSE
)
```
The goal of mapboxer is to make it easy to create interactive maps using [Mapbox GL JS](https://docs.mapbox.com/mapbox-gl-js/api) within R. Visualizations can be used at the R console, embedded in R Markdown documents or Shiny apps.
This guide covers the basic usage.
## Overview
* [Map](https://docs.mapbox.com/mapbox-gl-js/api/map/): The map is the main component of your visualization to which you then add other components like layers, controls or sources. Maps are created with `mapboxer()`.
* [Sources](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/): Sources state which type of data should be displayed on the map. R objects can be converted to Mapbox sources with `as_mapbox_source()`. With `add_source()` or as first parameter of `mapboxer()` sources can be added to the map so that they can be used accross layers.
* [Layers](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/): A layer's style define how a source is displayed on the map. Furthermore, you can apply filters to the data of a source. With `add_layer()` you can add any type of layer to the map but in most cases it is easier to use one of the shortcuts like `add_circle_layer()`.
* [Controls](https://docs.mapbox.com/mapbox-gl-js/api/markers/): Controls are used to interact with the map. Besides the standard controls like `NavigationControl` included in Mapbox GL JS, mapboxer provides additional controls. For example, `add_filter_control()` can be used to filter your data on the fly without having to set up a Shiny app.
* [Expressions](https://docs.mapbox.com/help/tutorials/mapbox-gl-js-expressions/): Expressions are pretty powerful. Among other things, they can be used for data-driven-styling or to filter your data.
* [Shiny Bindings](https://shiny.rstudio.com/): With `renderMapboxer()` and `mapboxerOutput()` you can integrate your visualizations in Shiny apps. Furthermore, you can use `mapboxer_proxy()` and `update_mapboxer()` to update an already rendered widget. Observe the `input$<widget_id>_onclick` event to get the properties for a clicked feature.
## Quickstart
```{r quickstart}
# Load the library
library(mapboxer)
# Create a source
motor_vehicle_collisions_nyc %>%
dplyr::mutate(color = ifelse(injured > 0, "red", "yellow")) %>%
as_mapbox_source(lng = "lng", lat = "lat") %>%
# Setup a map with the default source above
mapboxer(
center = c(-73.9165, 40.7114),
zoom = 10
) %>%
# Add a navigation control
add_navigation_control() %>%
# Add a layer styling the data of the default source
add_circle_layer(
circle_color = c("get", "color"),
circle_radius = 3,
# Use a mustache template to add popups to the layer
popup = "Number of persons injured: {{injured}}"
)
```
![](pix/motor-vehicle-collisions-nyc.png)
## Map
With `mapboxer()` you create a map object. This is the main component of your vizualization. To add components like layers or controls or to modify your map you use the `add_*` and `set_*` functions.
The options to configure your map are set via the `...` parameter that allows you to pass on any option described in the [Map API Reference](https://docs.mapbox.com/mapbox-gl-js/api/map/):
```r
mapboxer(
style = basemaps$Carto$dark_matter,
center = c(-73.9165, 40.7114),
zoom = 9,
minZoom = 8
)
```
With the optional `source` parameter you can add a default source to the map that will be used by the layers if no source is provided. Therefore, it is easy to integrate `mapboxer()` into your workflow:
```{r, eval = FALSE}
motor_vehicle_collisions_nyc %>%
dplyr::filter(killed > 0) %>%
as_mapbox_source() %>%
mapboxer(
center = c(-73.9165, 40.7114),
zoom = 9
) %>%
add_circle_layer(circle_color = "red")
```
As you can see above mapboxer is designed to use the widely used piping style provided by [magrittr](https://magrittr.tidyverse.org/).
## Basemaps
The `style` parameter passed to `mapboxer()` sets the style of the basemap. By default mapboxer uses a [Carto vector style](https://github.com/CartoDB/basemap-styles). It is also possible to use raster tiles or a background color as basemap. Therefore, you can use the helpers `basemap_raster_style()` or `basemap_background_style()`:
```r
mapboxer(style = basemap_raster_style())
```
To use styles from [Mapbox](https://www.mapbox.com/maps) it is recommened that your store your API token in an environment variable called `MAPBOX_API_TOKEN`. If not set globally you can store it as follows:
```r
Sys.setenv(MAPBOX_API_TOKEN = "<yourSuperSecretToken>")
mapboxer(style = basemaps$Mapbox$satellite_v9)
```
## Sources
Sources state which data the map should display. To show the data on the map you need
to bind a source to a layer which contains the styling details like color or width.
This makes it possible to style the same source in different ways.
The easiest way to create a source from an R data object is to use `as_mapbox_source()`.
Supported structures are [sf](https://r-spatial.github.io/sf/index.html)-objects and data frames that contain longitudes and latitudes:
```{r, eval = FALSE}
mvc_sf <- motor_vehicle_collisions_nyc %>%
sf::st_as_sf(coords = c("lng", "lat"), crs = 4326)
mvc_source_from_sf <- mvc_sf %>%
as_mapbox_source()
mvc_source_from_df <- motor_vehicle_collisions_nyc %>%
as_mapbox_source(lng = "lng", lat = "lat")
```
With the `...` parameter you can pass additional options to the source:
```r
mvc_cluster <- motor_vehicle_collisions_nyc %>%
as_mapbox_source(
lat = "lat",
lng = "lng",
cluster = TRUE,
clusterMaxZoom = 14,
clusterRadius = 50
)
```
See the [Sources API Reference](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/) for available options for the used source type. Sources are either passed to the `add_*_layer` functions or as first parameter to `mapboxer()` setting it as default source. With `add_source()` you can add a source to the map that you refer to in the layer definition by its ID.
## Layers
Layers style the data of the source to which they refer. Optionally you can filter features. Each layer must have a unique ID. If you use the generic function `add_layer()`, the type of the layer is specified by the `type` property. See the [Layers API Reference](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/) for available types. In most cases it is convenient to use one of the `add_*_layer()` functions:
```{r, eval = FALSE}
mapboxer(
center = c(-73.9165, 40.7114),
zoom = 9
) %>%
add_circle_layer(
source = as_mapbox_source(motor_vehicle_collisions_nyc),
circle_color = "red",
circle_radius = 5
)
```
## Popups and tooltips
Usually popups are added to a layer with the `popup` parameter of the `add_*_layer()` functions. Optionally you can also use `add_popups()`. The popup text (HTML) is specified by a [mustache](https://github.com/janl/mustache.js) template in which the tags refer to the properties of the layer's data object. If your data contains the properties `name` and `population`, it could look like this:
```r
popup_template <- "Name: {{name}}</br>Population: {{population}}"
```
With `add_tooltips()` you can add tooltips to a layer in the same way.
## Controls
Controls are used to interact with the user. They are displayed as overlays on top of the map. Options of the standard controls described in the [Markers and Controls API Reference](https://docs.mapbox.com/mapbox-gl-js/api/markers/) are provided with the
`...` parameter. The position is set with the `pos` parameter, one of `top-left`, `top-right`, `bottom-right`, `bottom-left`:
```{r, eval = FALSE}
mapboxer() %>%
add_navigation_control(
pos = "top-left",
# Option passed to the 'NavigationControl'
showCompass = FALSE
) %>%
add_scale_control(
pos = "bottom-left",
# Option passed to the 'ScaleControl'
unit = "nautical"
) %>%
add_text_control(
pos = "top-right",
text = "mapboxer"
)
```
## Expressions
The value of any layout property, paint property (data-driven-styling) or filter may be specified as an expression. Expressions in Mapbox GL JS use a Lisp-like [syntax](https://docs.mapbox.com/help/tutorials/mapbox-gl-js-expressions/#syntax) represented as JSON arrays:
```javascript
[expression_name, argument_0, argument_1, ...]
```
Therefore, in R you must use the `list` structure:
```r
list(expression_name, argument_0, argument_1, ...)
```
If all elements are of the same type you can also use a vector:
```r
expr_get_property <- c("get", "<data-property>")`
```
A simple expression is to use a data property to style your data:
```{r, eval = FALSE}
map <- motor_vehicle_collisions_nyc %>%
dplyr::mutate(
color = ifelse(injured > 0, "red", "yellow")
) %>%
as_mapbox_source() %>%
mapboxer(
center = c(-73.9165, 40.7114),
zoom = 9
)
map %>%
add_circle_layer(
# Expression to get the color from the data's color property
circle_color = c("get", "color")
)
```
You can get the same result for the `circle_color` without modifying the data but using
expressions only:
```{r, eval = FALSE}
map %>%
add_circle_layer(
circle_color = list(
"case",
# 'red' if 'injured > 0'
list(">", c("get", "injured"), 0), "red",
# Defaults to 'yellow'
"yellow"
)
)
```
A filter could look like this:
```{r, eval = FALSE}
map %>%
add_circle_layer(
circle_color = c("get", "color"),
# Expression to display only data where 'injured > 1'
filter = list(">", "injured", 1)
)
```
See also `add_filter_control()` to modify filter expressions on the fly to update your map without the need of a Shiny app, [Get started with expressions](https://docs.mapbox.com/help/tutorials/mapbox-gl-js-expressions/) for a tutorial and the [Expressions Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/) for details.
## Shiny Bindings
Use `mapboxerOutput()` and `renderMapboxer()` to integrate mapboxer in a [Shiny](https://shiny.rstudio.com/) app:
```{r, eval = FALSE}
library(shiny)
library(mapboxer)
view <- fluidPage(
h1("mapboxer"),
mapboxerOutput("map")
)
backend <- function(input, output) {
output$map <- renderMapboxer({
mapboxer(center = c(9.5, 51.3), zoom = 10) %>%
add_navigation_control() %>%
add_marker(lng = 9.5, lat = 51.3, popup = "mapboxer")
})
}
if (interactive()) shinyApp(view, backend)
```
With `mapboxer_proxy()` and `update_mapboxer()` you can update your already rendered map:
```{r, eval = FALSE}
LAYER_ID <- "crashes"
START_VALUE <- 4
view <- basicPage(
sliderInput("slider", "Number of persons injured:",
min = 0, max = 7, step = 1, value = START_VALUE),
mapboxerOutput("map")
)
backend <- function(input, output) {
output$map <- renderMapboxer({
mapboxer(
center = c(-73.9165, 40.7114),
zoom = 9
) %>%
add_circle_layer(
source = as_mapbox_source(motor_vehicle_collisions_nyc),
circle_color = "red",
popup = "{{injured}}",
filter = list("==", "injured", START_VALUE),
id = LAYER_ID
)
})
observeEvent(input$slider, {
mapboxer_proxy("map") %>%
set_filter(LAYER_ID, list("==", "injured", input$slider)) %>%
update_mapboxer()
})
}
if (interactive()) shinyApp(view, backend)
```
Observe the `input$<widget_id>_onclick` event to get the properties for a clicked feature.