-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLeaflet_live tutorial.Rmd
366 lines (254 loc) · 16.6 KB
/
Leaflet_live tutorial.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
---
title: "Leaflet: Creating Interactive Maps in R"
author: "Florian Winkler and Jiayu Yang"
date: "November 4, 2021"
output:
html_document:
toc: true
toc_float: true
toc_depth: 2
theme: lumen
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
# Introduction
Who doesn't love maps?
Welcome to our live tutorial on creating interactive maps with [Leaflet](http://rstudio.github.io/leaflet/)!
Now that you've heard us explain *WHY*, *WHEN* and *HOW* to use Leaflet, let's try to create our first interactive map in R ourselves.
## Motivation
Are you ready to create this interactive map of COVID-19 cases across Europe in just 15 minutes? 😎
![](motivation.png "covid cases europe")
Yes, you're right. This is not interactive. Well, this is just a screenshot for inspiration (for now). Let's get started!
## Installation
To install the Leaflet package in R, run the familiar command to install packages at your R prompt:
```{r eval=FALSE}
install.packages("leaflet")
```
Once installed, you can use the Leaflet package at your R console, in R Markdown documents, and in *Shiny* applications ([Shiny](https://shiny.rstudio.com/) is an R package used for creating interactive web applications such as dashboards).
## Loading Packages
Next, we're loading all the packages that we are going to use throughout this tutorial.
```{r, message = FALSE}
library(leaflet)
library(tidyverse)
library(scales)
library(lubridate)
```
# Recall from Presentation
## Basic Syntax
You create a Leaflet map with these basic steps:
1. Create a map widget by calling `leaflet()`.
2. Add *layers* (i.e., features) by using layer functions (e.g. `addTiles`, `addMarkers`, `addPolygons`) to modify the map widget.
3. Repeat step 2 as desired.
4. Print the map widget to display it.
Here's a basic example. Guess where we are? Try clicking on the marker to confirm.
```{r}
first_map <- leaflet() %>% # Leaflet works with the pipe operator
addTiles() %>% # Draws on default OpenStreetMap map tiles
addMarkers(lng = 13.3893204, lat = 52.5128055, popup = 'Hertie School')
first_map
```
The result is an interactive map (also called a "*slippy*" map in the geospatial jargon): Try using the plus and minus buttons to zoom in and out, and click on the map to pan around the map.
## Basemaps
### Default (OpenStreetMap) Tiles
Leaflet supports basemaps (i.e., the underlying map you're working with) using [map tiles](https://www.mapbox.com/guides/how-web-maps-work/) (similar to Google Maps). The easiest way to add map tiles is by calling `addTiles()` with no arguments; by default, [OpenStreetMap](https://www.openstreetmap.org/) tiles are used.
### Third-Party Tiles
Alternatively, you can also use other basemaps for your projects. Many popular free third-party basemaps can be added using the `addProviderTiles()` function (see here for a collection of [basemaps](http://leaflet-extras.github.io/leaflet-providers/preview/index.html) to choose from). Let's try out "CartoDB.Voyager".
```{r}
second_map <- leaflet() %>%
addProviderTiles(providers$CartoDB.Voyager) %>% # Uses CartoDB.Voyager map tiles
addMarkers(lng = 13.3893204, lat = 52.5128055, popup = 'Hertie School')
second_map
```
The range of basemaps to choose from is long. Fancy a watercolor map for your next project?
```{r}
third_map <- leaflet() %>%
addProviderTiles(providers$Stamen.Watercolor) %>% # Uses Stamen.Watercolor map tiles
addMarkers(lng = 13.3893204, lat = 52.5128055, popup = 'Hertie School')
third_map
```
For now, we'll stick with the "CartoDB.Voyager" basemap (from our second map).
## Map Methods
You can manipulate the attributes of a map widget using a series of methods and options.
- `setView()` sets the center of the map view and the zoom level;
- `fitBounds()` fits the view into the rectangle `[lng1, lat1]` -- `[lng2, lat2]`;
- `clearBounds()` clears the bound, so that the view will be automatically determined by the range of latitude/longitude data in the map layers if provided.
To keep it simple for now, we're going to add an `options` argument in our `leaflet()` function that will set a minimum and maximum zoom level in our map. Take some time to play around with zoom levels to get a feeling for your map widget settings, if you like.
```{r}
second_map <- second_map %>%
leaflet(options = leafletOptions(minZoom = 13, maxZoom = 16)) %>% # Sets minimum and maximum zoom
addProviderTiles(providers$CartoDB.Voyager) %>%
addMarkers(lng = 13.3893204, lat = 52.5128055, popup = 'Hertie School')
second_map
```
# Our Data: COVID-19 in Europe
To show you a real-world example of how we can create and use Leaflet maps in R, we're going to visualize COVID-19 data from European countries. We compiled a small data set from the [COVID-19 Data Repository](https://github.com/CSSEGISandData/COVID-19) by the Johns Hopkins University (JHU) that can be downloaded from our workshop repository (credits to Riddhiman on the [R'tichoke blog](https://rtichoke.netlify.app/post/covid_visualisation_using_leaflet/) for providing a summarized version of the JHU data set).
```{r}
europe <- read.csv('leaflet_covid_data.csv')
glimpse(europe)
```
Note that we need *spatial* data to create maps that are meaningful and informative. In our example, we use latitude and longitude data for specific locations (note the "Latitude" and "Longitude" columns in the data set). However, spatial data can also exist in a variety of other formats and can contain more than just location-specific information. For a list of data formats that leaflet can work with, see [here](http://rstudio.github.io/leaflet/map_widget.html#the-data-object).
# Confirmed Cases Across Countries
Let's say we wanted to visualize how confirmed COVID-19 cases were distributed across European countries in September 2021 on a map. We can do so by adding circle markers that vary in the size of their radius according to each country's number of confirmed cases. Again, credits to Riddhiman on the [R'tichoke blog](https://rtichoke.netlify.app/post/covid_visualisation_using_leaflet/) for the inspiration (we're only focusing on Europe here though).
## Step 1: Add Circle Markers
We can use markers to call out points on a map. Marker locations are expressed in latitude/longitude coordinates, and can either appear as icons or as circles. We're going to use circle markers in order to add a radius argument later on.
The `popup` argument can be used to add a message to be displayed on click. Popups are small boxes containing HTML that point to a specific point on the map. We want popups to show selected COVID-19 data per country, such as confirmed cases, deaths, and fatality rates.
```{r}
cases_circles <- europe %>%
# Select data from September 2021 only
filter(mon_yr == "2021-09") %>%
# Call Leaflet function and add map tiles
leaflet(options = leafletOptions(minZoom = 3, maxZoom = 5)) %>%
addProviderTiles(providers$CartoDB.Voyager) %>%
# Add circle markers (same radius for now) with popups
addCircleMarkers(lng = ~Longitude, # Don't forget the "~" sign in layer feature arguments
lat = ~Latitude,
popup = ~paste0("<b>", country, "</b><br>",
"Confirmed Cases: <b>", confirmed, "</b><br>",
"Deaths: <b>", deaths, "</b><br>",
"Fatality Ratio: <b>", fatal_rate, "</b><br>"))
cases_circles
```
If you click on a circle, you will see that we have successfully created popups. However, the COVID-19 data is not presented in a very interpretable format as numbers are shown as raw numeric values (i.e., no thousands separator, no percentage values for fataility rate).
In the next step, we will refine our popup text by transforming our data using the *scales* package.
```{r}
europe <- europe %>%
mutate(confirmed_readable = scales::label_number_si(accuracy = 0.1)(confirmed),
deaths_readable = scales::label_number_si(accuracy = 0.1)(deaths),
fatal_readable = scales::percent(fatal_rate, accuracy = 0.1))
```
Now, let's try again to add circle markers with (more readable) popups!
```{r}
cases_circles <- europe %>%
# Select data from September 2021 only
filter(mon_yr == "2021-09") %>%
# Call Leaflet function and add map tiles
leaflet(options = leafletOptions(minZoom = 3, maxZoom = 5)) %>%
addProviderTiles(providers$CartoDB.Voyager) %>%
# Add circle markers (same radius for now) with popups
addCircleMarkers(lng = ~Longitude,
lat = ~Latitude,
popup = ~paste0("<b>", country, "</b><br>",
"Confirmed Cases: <b>", confirmed_readable, "</b><br>",
"Deaths: <b>", deaths_readable, "</b><br>",
"Fatality Ratio: <b>", fatal_readable, "</b><br>"))
cases_circles
```
## Step 2: Adjust Circle Radius
To create circle markers that vary in the size of their radius, we need to create a "radius" variable that scales confirmed cases per country.
```{r}
cases_circles <- europe %>%
# Select data from September 2021 only
filter(mon_yr == "2021-09") %>%
# Create a new variable for circle radius (arbitrary scaling function for dramatic effect)
mutate(rad = sqrt(confirmed/max(confirmed)) * 50) %>%
# Call Leaflet function and add map tiles
leaflet(options = leafletOptions(minZoom = 3, maxZoom = 5)) %>%
addProviderTiles(providers$CartoDB.Voyager) %>%
# Add circle markers that vary in the size of their radius according to COVID-19 cases
addCircleMarkers(lng = ~Longitude,
lat = ~Latitude,
popup = ~paste0("<b>", country, "</b><br>",
"Confirmed Cases: <b>", confirmed_readable, "</b><br>",
"Deaths: <b>", deaths_readable, "</b><br>",
"Fatality Ratio: <b>", fatal_readable, "</b><br>"),
radius = ~rad) # Adjust circle radius to "rad" value
cases_circles
```
## Step 3: Refine Design of Circle Markers
Using ***color*** arguments in the `addCircleMarkers()` function, let's specify colors and outlines for all circle markers. We set ***stroke*** to TRUE to include an outline, adjust ***weight*** to define the outline's weight, and set a ***color*** for the outline's color (black in this case). Next, we define the color of the circle filling with ***fillColor*** (grey in this case) and set ***fillOpacity*** to apply a transparency effect.
```{r}
cases_circles <- europe %>%
filter(mon_yr == "2021-09") %>%
mutate(rad = sqrt(confirmed/max(confirmed)) * 50) %>%
leaflet(options = leafletOptions(minZoom = 3, maxZoom = 5)) %>%
addProviderTiles(providers$CartoDB.Voyager) %>%
addCircleMarkers(lng = ~Longitude,
lat = ~Latitude,
popup = ~paste0("<b>", country, "</b><br>",
"Confirmed Cases: <b>", confirmed_readable, "</b><br>",
"Deaths: <b>", deaths_readable, "</b><br>",
"Fatality Ratio: <b>", fatal_readable, "</b><br>"),
radius = ~rad,
stroke = TRUE, # Include a circle outline
weight = 0.7, # Define the circle outline's weight
color = "#000000", # Define the circle outline's color
fillColor = "#525c63", # Define the color of the circle filling
fillOpacity = 0.3) # Apply transparency to the circle filling
cases_circles
```
Here you go! You just created an interactive map to visualize COVID data across Europe -- with only a few lines of code. Try clicking on one of the circle markers. What was the fatality rate in France in September 2021? Did Denmark or Hungary have more COVID cases in September?
***Unfortunately, our session has run out of time! If you want to take our map one step further at home, feel free to follow our instructions below.***
# Appendix: COVID-19 Cases over Time
Let's say we wanted to know how COVID-19 cases were not only distributed across countries, but also at different points in time.
Leaflet has an interesting feature that allows us to **add layers** to the same map and switch between them using the the `group` argument and the `addLayersControl()` function (remember the climate data map from our presentation earlier?).
## Step 1: Group Layers by Date
```{r}
cases_over_time_layers <- europe %>%
group_by(date) %>% # Group data by date (in this case by months)
filter(mon_yr != '2020-02') %>% # Select data from March 2020 onward
mutate(rad = sqrt(confirmed/max(confirmed)) * 60) %>%
leaflet(options = leafletOptions(minZoom = 3, maxZoom = 6, )) %>%
addProviderTiles(providers$CartoDB.Voyager) %>%
addCircleMarkers(lng = ~Longitude,
lat = ~Latitude,
radius = ~rad,
popup = ~paste0("<b>", country, "</b><br>",
"Confirmed Cases: <b>", confirmed_readable, "</b><br>",
"Deaths: <b>", deaths_readable, "</b><br>",
"Fatality Ratio: <b>", fatal_readable, "</b><br>"),
weight = 0.7,
stroke = T,
color = '#000000',
fillColor = '#525c63',
fillOpacity = 0.5,
group = ~date, # Create layers by date (in this case by months)
labelOptions = labelOptions(noHide = F))
cases_over_time_layers
```
The result looks messy. What happened? Our code above printed all layers on one single map layer.
To filter layers, we need to make use of Leaflet's layers control feature that allows us to show and hide map layers. Using the `addLayersControl()` function, we can switch between individual layers and hide all others.
## Step 2: Create Base Groups
As a necessary intermediate step, we first need to create a vector of base groups that include the names of all layers (essentially the "names" of the dates that we want to display as layer names).
```{r}
vec <- seq.Date(as.Date('2020-03-01'), to = as.Date('2021-09-01'), by = '1 month')
vec <- as.character(vec)
```
## Step 3: Add Layers Control
```{r}
cases_over_time_layers <- europe %>%
group_by(date) %>%
filter(mon_yr != '2020-02') %>%
mutate(rad = sqrt(confirmed/max(confirmed)) * 60) %>%
leaflet(options = leafletOptions(minZoom = 3, maxZoom = 6, )) %>%
addProviderTiles(providers$CartoDB.Voyager) %>%
addCircleMarkers(lng = ~Longitude,
lat = ~Latitude,
radius = ~rad,
popup = ~paste0("<b>", country, "</b><br>",
"Confirmed Cases: <b>", confirmed_readable, "</b><br>",
"Deaths: <b>", deaths_readable, "</b><br>",
"Fatality Ratio: <b>", fatal_readable, "</b><br>"),
weight = 0.7,
stroke = T,
color = '#000000',
fillColor = '#525c63',
fillOpacity = 0.5,
group = ~date,
labelOptions = labelOptions(noHide = F)) %>%
addLayersControl( # Layer control
baseGroups = vec, # baseGroups adds radio buttons to switch between layers
options = layersControlOptions(collapsed = FALSE))
cases_over_time_layers
```
Voilà! Now you can switch between individual layers (in our case: months) and hide all other layers.
Time for analysis: Which countries were most affected by COVID in early 2020? How has it spread across Europe since then?
# Further resources
If we sparked your interest in using Leaflet for R, make sure to check out the following resources:
- Clear and comprehensive [documentation](http://rstudio.github.io/leaflet/) for Leaflet in R (including code, examples and further links).
- Leaflet [cheat sheet](https://ugoproto.github.io/ugo_r_doc/pdf/leaflet-cheat-sheet.pdf)
- Hands-on [YouTube tutorial](https://www.youtube.com/playlist?list=PLmFi_ou2WwcEyPq7Y9DvzFRLlp9-XvFDb) for basic Leaflet features (e.g., markers, popups, labels and polylines)
- Collection of free [third-party basemaps](http://leaflet-extras.github.io/leaflet-providers/preview/index.html) (map tiles) for Leaflet
- Leaflet website for [JavaScript](https://leafletjs.com/) (for those of you interested in the more advanced side of things)
# Sources
This tutorial drew heavily on Riddhiman's article on the [R'tichoke blog](https://rtichoke.netlify.app/post/covid_visualisation_using_leaflet/) (for the general idea, code and data) as well as Leaflet's [documentation for R](http://rstudio.github.io/leaflet/) (for explanations of Leaflet features and code).