-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathintro-rshiny-mapping.Rmd
397 lines (252 loc) · 10.4 KB
/
intro-rshiny-mapping.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
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
---
title: "Interactive mapping with RShiny"
subtitle: "*Engaging end-users with research*"
author: "Christina Buelow"
institute: "Made for RLadies Brisbane (May 2022)"
#date: "2022/05/26 (updated: `r Sys.Date()`)"
output:
xaringan::moon_reader:
lib_dir: libs
nature:
highlightStyle: github
highlightLines: true
countIncrementalSlides: false
---
class: left, top
## A journey in maps and apps...
1. The basics of programming for interactivity: *Reactivity*
--
2. R package options for mapping interactively in R, with pros and cons
--
3. Make an interactive map and share in a web app
--
.footnote[[*] This journey is thanks to the awesome book [Mastering Shiny](https://mastering-shiny.org/index.html)]
---
## Why interactive and why Shiny?
--
#### Open Science
- Interactive web apps are a great way to make our research **open**, **transparent**, and **engaging**
--
- Move beyond static papers
--
#### Shiny
- Performative apps without HTML, javascript, CSS...
--
- Range of users - academics to tech companies
--
- Need to be hosted on a web server
--
#### Server options:
- [shinyapps.io](https://www.shinyapps.io/): free or subscription options
- [See here for more info on Rshiny servers](https://shiny.rstudio.com/articles/shiny-server.html)
- There are other 'non-R' server options out there too
---
## App fundamentals
Interactivity requires *Reactivity*
What is **reactive programming**?
--
- Outputs *react* to inputs
--
- Means computations are only done when necessary (moving beyond **functions** to **reactions**)
--
- Reactive contexts are **lazy** and **cached**
--
- It's different from how we normally code our scripts:
- Commands vs. recipes
- Imperative vs. declarative
- Assertive vs. passive-aggressive
???
From Mastering Shiny:
In imperative programming, you issue a specific command and it’s carried out immediately. This is the style of programming you’re used to in your analysis scripts: you command R to load your data, transform it, visualise it, and save the results to disk.
In declarative programming, you express higher-level goals or describe important constraints, and rely on someone else to decide how and/or when to translate that into action. This is the style of programming you use in Shiny.
With imperative code you say “Make me a sandwich”8. With declarative code you say “Ensure there is a sandwich in the refrigerator whenever I look inside of it”. Imperative code is assertive; declarative code is passive-aggressive.
lazy and cached: minimal set of computations to update ouputs based on inputs
---
## Shiny
{shiny} provides a framework for **reactive programming**:
- User-interface (ui): how it looks. Takes *inputs* and displays *outputs*
- Server: has the recipe to turn *inputs* into *outputs*
--
*hint*: if you have {shiny} installed, just type `shinyapp` in your R script to insert boilerplate shiny app code and get started quickly
```{r}
library(shiny)
ui <- fluidPage(
)
server <- function(input, output, session) {
}
shinyApp(ui, server)
```
???
* Here pause to do this together in R. Show what fluidPage means using shiny cheat-sheet.
* ui and server can be stored as different objects in an app.R script and deployed with shinyApp function
* or, for more complex apps, split and save as separate ui.R and server.R scripts
* Discuss input (widget) vs. output (render) functions in R
* Run the example in the cheet sheet, just to show how a basic app works
---
## A simple histogram
```{r}
hist(rnorm(25))
```
---
## A reactive histogram
```{r}
library(shiny)
ui <- fluidPage(
numericInput(inputId = "n", "Sample size", value = 25),
plotOutput(outputId = "hist")
)
server <- function(input, output, session) {
output$hist <- renderPlot({
hist(rnorm(input$n))
})
}
shinyApp(ui, server)
```
--
.footnote[[ ] There's an additional reactive tool, *reactive expressions*. *Reactive expressions* can be used to eliminate redundant code in your app, thereby improving efficiency. Find more information [here](https://mastering-shiny.org/reactivity-intro.html). ]
???
One more important thing about input: it’s selective about who is allowed to read it. To read from an input, you must be in a reactive context created by a function like renderText() or reactive().
There’s one more important component that you’ll see in the reactive graph: the reactive expression. We’ll come back to reactive expressions in detail very shortly; for now think of them as a tool that reduces duplication in your reactive code by introducing additional nodes into the reactive graph.
---
## But what about maps?
My go-to for mapping in R these days is {tmap}
One of the reasons is that I can make either interactive or static, publication-quality maps with ease. Just set the 'mode'.
```{r, warning = F}
library(tmap)
data('World')
tmap_mode('view')
qtm(World, 'name') # qtm stands for 'quick thematic mapper'
```
---
## Share with shiny
If we're in a hurry, we can put our interactive {tmap} in a {shiny} app to share
```{r}
library(shiny)
library(tmap)
data('World')
ui <- fluidPage(
tmapOutput('map')
)
server <- function(input, output, session) {
output$map <- renderTmap({
qtm(World, 'name')})
}
shinyApp(ui, server)
```
---
## Add some reactivity
Let's add a widget to allow users to choose their country from a list
```{r}
library(shiny)
library(tmap)
library(dplyr)
data('World')
ui <- fluidPage(
tmapOutput('map'),
selectInput('var', 'Select country',
choices = c('Global', as.character(World$name)),
selected = 'Global')
)
server <- function(input, output, session) {
output$map <- renderTmap({
if(input$var == 'Global'){
qtm(World, 'name')
}else{
qtm(filter(World, name == input$var))
}
})
}
shinyApp(ui, server)
```
---
## Customising and scaling your interactive map
If you want to **customise** your map with more *features*, the [leaflet](https://rstudio.github.io/leaflet/) R package offers some nice functionality (there are others too)
--
{tmap} and {leaflet} both use **'scalable vector graphics'** (SVG) to visualise maps on the web
--
- This is fine for simple apps, but if you need to render large datasets or do complex computations they will be really slow
--
**Scaling-up** your map app might require graphics to be rendered in **'WebGL'** instead of SVG
--
- There are several R packages to try including [mapdeck](https://symbolixau.github.io/mapdeck/articles/mapdeck.html) and [rdeck](https://github.com/anthonynorth/rdeck)
--
- These usually require API tokens to access basemaps from [mapbox](https://www.mapbox.com/)
--
Should I go **SVG** or **WebGL**?
- Generally I've found there is a **feature vs. speed trade-off**
- Recommend start with **SVG** and if too slow then move to **WebGL**
???
What are my interactive web mapping options?
Interactive web visualisations:
Options for graphics on the web:
SVG (Scalable vector graphics) - simplest option, but not scalable, can’t handle large datasets. This is leaflet
WebGL - good if you have lots of data to render in browser (points/polygons), need to perform lots of data calculations, want to animate
WebGL can be used to visualize large, complex graphics with excellent performance. WebGL provides an API that allows you to write low-level code for creating rasterized images. This code is run on your computer's GPU instead of CPU, allowing large data to be processed in parallel.
WebGL = Performance!!
But complex b/c works with low level code
So pick what you need depending on use-case. Only go up the ladder when not performing.
—
Mapbox - base map provider (also Google Maps, ArcGIS, etc)
Leaflet vs leafgl vs mapveiew vs map deck vs rdeck
Map deck
- No feature click popsps
- Needs an API key
- Offers some cool functions
- Heat map, grid map column plots
- Can render large datasets
Mapdeck has more fine grained controlled than map view
Rdeck
In short it's a similar premise to {mapdeck} but implemented in a different way which comes with many benefits. It supports much more of http://deck.gl (almost all of it), it uses less data, has a ggppot2 scale-style approach to legends, built in layer selector...
Mapbox - base maps
API - Application programming interface
---
## Tips and tricks
Lots of complex polygons?
Use [rmapshaper](https://cran.r-project.org/web/packages/rmapshaper/vignettes/rmapshaper.html) to simplify, this can really **speed up** a slow map app
--
App still slow?
Use [profvis](https://rstudio.github.io/profvis/) to profile your script and find the bottleneck
--
Have you reached the **'cliff of complexity'**?
Check out [Engineering production grade shiny apps](https://engineering-shiny.org/successful-shiny-app.html)
---
## Leaflet example
Follow the R script 'leaflet-map.R' to build an interactive {leaflet} map
--
Next follow the R script 'leaflet-app.R' to turn the interactive {leaflet} map in to a share-able and dynamic {shiny} web app
---
class: center, middle, inverse
# Thank-you!
--
???
Notes to build on for another tutorial
- Modules
- Caching
-etc....
To reiterate the section on “Improving performance and scalability” in shiny from Cheng (2018a), you have a number of tools available to address performance:
The profvis package for profiling code.
Cache computations ahead-of-time.
Cache computations at run time.
Cache computations through chaining reactive expressions.
Leverage multiple R processes and/or servers.
Async programming with promises
# Setting up your UI
Layout function fluidPage - layout function, what goes where?
How do we let end-users interact? Inputs.
Widgets - e.g., selectInput, etc.
How does shiny react to end-user inputs?
Outputs. We define those in the UI too.
(Fancy ways to generate HTML)
# Bringing outputs to life
Reactive programming = interactive
`It’s like the difference between giving someone a recipe versus demanding that they go make you a sandwich.`
Give a recipe
When inputs change, app will react and outputs will change based on recipe.
Server is essentially a function with input, output, session arguments.
Server connects the inputs and outputs - fully functional app.
Provide a recipe for each shiny output with a specific ID.
Render functions wrap up the recipe.
# Duplicated code
Usually capture value using a variable, or capture computation with a function.
Shiny answer: reactive expressions
Assign reactive expression to a variable, and can call it like a function