htmlwidgets - leaflet #
The Bigger Picture #
In this document we learn how to create interactive charts with leaflet. Simply put, we are learning how to transform tidy data into visually clear graphs. In the overall context of the workflow, this falls into the category of transforming our data into data visualisation.
What is Leaflet? #
library("leaflet")
- An htmlwidget used to make interactive maps
- The package is bound to the Leaflet library in JavaScript
- Can create:
- Geoplots
- Choropleths
- Geolines maps
- Like the Tidyverse, leaflet heavily depends on the pipeline (
%>%
) operator to create charts
Creating basic geoplots #
The example data used is from the ACORN-SAT dataset. The dataset spans 112 Australian stations recording temperature measurements for over 100 years. We will use data from the year 2000.
station_data %>%
sample_n(5)
## Number year average.temp Station.name Latitude Longitude Elevation Start
## 1 36007 2000 23.0 Barcaldine -23.55 145.29 266 1962
## 2 7045 2000 21.5 Meekatharra -26.61 118.54 517 1926
## 3 53115 2000 19.3 Moree -29.49 149.85 213 1912
## 4 9518 2000 17.5 Cape Leeuwin -34.37 115.14 13 1910
## 5 14015 2000 27.3 Darwin -12.42 130.89 30 1910
- We begin by piping our data into the
leaflet()
function to begin any leaflet chart
station_data %>%
leaflet()
- We may then pipe this data into a variety of “functions” to customise the graph we create
- Since we first want a geoplot, we use
addTiles()
to get a default map
plot <- station_data %>%
leaflet() %>%
addTiles()
widgetframe::frameWidget(plot)
Notice how we can move around and zoom in – Leaflet is very interactive!
- We could also use
addProviderTiles()
to use a custom map - Maps available from https://rstudio.github.io/leaflet/basemaps.html
plot <- station_data %>%
leaflet() %>%
addProviderTiles(providers$Esri.NatGeoWorldMap)
widgetframe::frameWidget(plot)
- We may now pipe into
addMarkers()
to place our geographical data
station_data %>%
leaflet() %>%
addTiles() %>%
addMarkers(lng = ~Longitude,
lat = ~Latitude)
If we have numeric latitude and longitude columns in our data, addMarkers()
will automatically use these. If your columns aren’t automatically detected, try using lat = ~as.numeric(Latitude)
. The error may be caused by numeric data stored as a character variable!
Geoplot options #
- We can display data if we hover over our points, or if we click on them
- Hover data is given by the
label
argument ofaddMarkers()
- Click data is given by the
popup
argument ofaddMarkets()
- For each, we can display multiple variables or lines of text by adding arguments to
paste0()
orpaste()
- If we type this special string “
,” our following text begins on a new line
- If we type this special string “
station_data %>%
leaflet() %>%
addTiles() %>%
addMarkers(label = ~Station.name,
popup = ~paste0("Altitude: ",
Elevation,
"<br/>",
"<br/>",
"Isn't Leaflet neat?"),
lng = ~Longitude,
lat = ~Latitude)
Click on the markers!
A note on Leaflet formatting #
Notice in the previous section, our arguments used a tilde (“~
”) before we called on our column names. This even occured when we called them within a formula.
station_data %>%
leaflet() %>%
addTiles() %>%
addMarkers(label = ~Station.name,
popup = ~paste0("Altitude: ", Elevation),
lng = ~Longitude,
lat = ~Latitude)
We must do this, as this is the format Leaflet demands. It’s how Leaflet detects column names and converts them to lists of variables.
Creating circle marker geoplots #
- We simply use
addCircleMarkers()
instead of justaddMarkers()
- We also gain access to a number of additional options, some of which are shown here
Radius changes the size of circle plots according to a variable. Here we’d like the size to represent temperature.
station_data %>%
leaflet() %>%
addTiles() %>%
addCircleMarkers(label = ~paste("The", Station.name, "circle!"),
radius = ~average.temp,
lng = ~Longitude,
lat = ~Latitude)
We can also change the colour of the circle markers according to a variable, although this requires some setup.
- First we define a palette with the
colorNumeric()
function - The
palette
argument specifies the colours we move on a gradient between - The
domain
argument is the list of numeric values we map to the gradient
pal <- colorNumeric(
palette = "YlOrRd",
domain = station_data$average.temp)
- We can then use this in the
color
argument ofaddCircleMarkers()
station_data %>%
leaflet() %>%
addTiles() %>%
addCircleMarkers(label = ~paste("The", Station.name, "circle!"),
color = ~pal(average.temp),
lng = ~Longitude,
lat = ~Latitude)
- R comes with several pre-built palettes such as “YlOrRd” and “Blues” to help
colorNumeric()
- Take note that using the
colorRampPalette()
function, it is possible to create custom palettes - In this example, the
palette
argument ofcolorNumeric()
is defined using this specialcolorRampPalette()
call
pal <- colorNumeric(
palette = colorRampPalette(c("green", "purple"))(length(station_data$average.temp)),
domain = station_data$average.temp)
Leaflet Choropleths #
A choropleth is a map-based chart in which regions are shaded with colours to reflect some variable. Creating choropleths with Leaflet requires us to manipulate ‘shapefiles.’ These are files which contain information about points, lines, polygons (etc) necessary to visually depict shapes, such as countries of the world. Before we can create a choropleth, we must learn how to prepare these shape files
There are two types of shapefiles
- ESRI shapefiles - the older standard for shapefiles
- To use them we must have (at least) one of all of the below:
- A
.dbf
file - A
.shp
file - A
.shx
file - GeoJson shapefiles - a newer type
- To use them we only require one
.json
file
A good sources of global shapefiles are NaturalEarthData.com and Johan’s repository
Preparing shapefiles #
- We require the library “
sf
” - We call upon
read_sf()
to read an entire directory of shapefiles and save the result - For our example we will use an Australian shapefile released by the Australian Government
library("sf")
## Linking to GEOS 3.8.1, GDAL 3.1.4, PROJ 6.3.1
shapefile_map <- read_sf(dsn = "shapefiles")
# Note: for file path, do not include a '/' at the end
shapefile_map
## Simple feature collection with 8 features and 2 fields
## geometry type: MULTIPOLYGON
## dimension: XY
## bbox: xmin: 112.9211 ymin: -43.74037 xmax: 159.1053 ymax: -9.142319
## CRS: NA
## # A tibble: 8 x 3
## STE COUNT geometry
## <int> <dbl> <MULTIPOLYGON>
## 1 1 11619 (((151.0689 -33.82025, 151.0697 -33.81847, 151.0705 -33.82025, 15…
## 2 2 7889 (((146.3024 -39.15425, 146.3018 -39.15395, 146.3024 -39.1532, 146…
## 3 3 6373 (((153.3613 -27.63769, 153.3603 -27.63577, 153.3635 -27.63518, 15…
## 4 4 3152 (((137.5385 -34.01007, 137.5391 -34.01418, 137.5375 -34.01343, 13…
## 5 5 3481 (((115.2278 -33.59031, 115.2281 -33.59122, 115.2291 -33.59122, 11…
## 6 6 1089 (((146.5695 -41.17678, 146.5697 -41.17737, 146.5691 -41.17769, 14…
## 7 7 389 (((130.8684 -12.35691, 130.8692 -12.35513, 130.8699 -12.35691, 13…
## 8 8 492 (((149.2214 -35.34117, 149.2198 -35.34141, 149.2189 -35.34153, 14…
- Note that our
shapefile_map
variable has a column called “geometry”- Each value in “geometry” is a list of lattitudes and longitudes
- These geographical points map out a shape, hence our “shapefiles”
- After reading the files in, we simply pipe them into the
addPolygons()
Leaflet function to view our map
shapefile_map %>%
leaflet() %>%
addPolygons()
Creating Leaflet choropleths and Legends #
We have our shapes - we will now mutate our shape data so that they are named by state.
shapefile_map$State <- c("NSW", "VIC", "QLD", "SA", "WA", "TAS", "NT", "ACT")
- Of course we want to colour our chart
- We first create a palette
What colours do we use?
- Color Brewer is a website with many palettes
- We will use the one called “Set1”
How do we create our palette?
-Several functions exist
colorNumeric()
for continuous numeric variable colouringcolorQuantile()
for colouring by quantile of a numeric variablecolorBin()
for colouring by bins of a numeric variablecolorFactor()
for colouring by a categorical variable
Right now we have a map but no data. Let us colour by “State,” a categorical variable.
palette <- colorFactor("Set1", domain = shapefile_map$State)
shapefile_map %>%
leaflet() %>%
addTiles() %>%
addPolygons(color = ~palette(State)) %>%
addLegend(pal = palette, values = ~State, title = "State: ")
- Note that color is an argument of addPolygons()
- Note that the palette object is a function we are applying to the “State” column
Here we have introduced the Leaflet addLegend()
function
- The
pal
argument specifies the palette we use for the legend - The
values
argument specifies the variable our legend explains - The
title
argument titles the legend
Let us add some actual data to our choropleth. We will use the 2013-2014 subset of water usage data from the Australian Environmental-Economic Accounts (2016)
- To see how this data was processed, see
building_state_data.R
- We can add extra data simply by merging with respect to the “State” variable
load("tidy_EnvAcc_data/water_data.rdata")
water_data
## State DistributedReuseSupply(GL) Consumption(GL) Expenditure($m)
## 1 ACT 49 53 288
## 2 NSW 6211 7508 4192
## 3 NT 59 167 157
## 4 QLD 2579 4145 3877
## 5 SA 380 1077 1162
## 6 TAS 99 390 245
## 7 VIC 3238 3988 4554
## 8 WA 643 1317 1597
## HouseholdPhysical(GL) HouseholdMonetary($m) HouseholdPrice($/KL)
## 1 31 93 3.00
## 2 565 1589 2.81
## 3 37 60 1.62
## 4 370 1109 3.00
## 5 125 531 4.25
## 6 38 118 3.11
## 7 366 1240 3.39
## 8 339 557 1.64
water_data_map <- shapefile_map %>%
merge(water_data, by = "State")
We may now make a new palette and colour it. Let us do this by the average price of water.
- While we’re at it, let’s display the average price by hovering our mouse over the state
- Additionally let’s make it so that if we click, we can see per household water consumption and expenditure
palette <- colorNumeric("Blues", domain = water_data_map$`HouseholdPrice($/KL)`)
water_data_map %>%
leaflet() %>%
addTiles() %>%
addPolygons(color = ~palette(water_data_map$`HouseholdPrice($/KL)`),
label = ~paste("Price:",
water_data_map$`HouseholdPrice($/KL)`),
popup = ~paste("Average household consumption (KL):",
water_data_map$`HouseholdPhysical(GL)`,
"<br/>",
"Household expenditure ($m):",
water_data_map$`HouseholdMonetary($m)`)) %>%
addLegend(pal = palette,
values = ~water_data_map$`HouseholdPrice($/KL)`,
title = "Average price of water ($/KL): ")
Just like with addMarkers()
, the label
and popup
arguments still work!
Geoline maps #
- These are another great feature of Leaflet that requires some preparation
- We can create maps of arcs across the surface of the earth using the
gcIntermediate()
function - We specify our “to” and from points as latitudes and longitudes
- For our example we will use the ACORN-SAT stations and plot the distance between the most eastern and most western stations
To begin, we need the “geosphere” and “sp” packages. We also need two data.frames, each containing only a set of latitudes and longitudes. For our example, we filter out only the most eastern and most western coordinates.
library("geosphere")
library("sp")
load("tidy_ACORN-SAT_data/station_data.rdata")
east_to_west <- station_data %>%
filter(year == 2000)
start_loc <- east_to_west %>%
filter(Longitude == max(Longitude)) %>%
select(Longitude, Latitude)
end_loc <- east_to_west %>%
filter(Longitude == min(Longitude)) %>%
select(Longitude, Latitude)
- We then use
gcIntermediate()
to create our lines- This is useful, but isn’t attached to our original data
- We thus pipe the result into
SpatialLinesDataFrame()
specifying our data to create one integrated object - Finally we pipe this into
st_as_sf()
to format the object as an sf
distance <- gcIntermediate(p1 = start_loc,
p2 = end_loc,
n = 50,
addStartEnd = TRUE,
sp = TRUE) %>%
SpatialLinesDataFrame(data = east_to_west) %>%
st_as_sf()
- Now we can use
addPolylines()
specifying our object to plot the lines - In our example this is only one line, but multiple lines are just as achievable
east_to_west %>%
leaflet() %>%
addTiles() %>%
addCircleMarkers(label = ~Station.name) %>%
addPolylines(data = distance)