So far, we have only made static maps. What if we wanted to make maps that were more interactive? In this tutorial, I will provide an introduction to Leaflet, a package in R that will enable us to make our maps more dynamic and interactive.

#Load in the leaflet package and other required packages
#Install it if you haven't yet
library(leaflet)
## Warning: package 'leaflet' was built under R version 4.3.3
library(sf)     
## Linking to GEOS 3.11.2, GDAL 3.7.2, PROJ 9.3.0; sf_use_s2() is TRUE
library(dplyr)   
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(spData) 
## To access larger datasets in this package, install the spDataLarge
## package with: `install.packages('spDataLarge',
## repos='https://nowosad.github.io/drat/', type='source')`
library(urbnmapr)
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.3.3
counties <- get_urbn_map("counties", sf = TRUE)

#filter the data to get just NYS
counties <- counties %>% 
  filter(state_abbv == "NY") %>% 
  st_transform("EPSG:32116")
## old-style crs object detected; please recreate object with a recent sf::st_crs()
## Warning in CPL_crs_from_input(x): GDAL Message 1: CRS EPSG:2163 is deprecated.
## Its non-deprecated replacement EPSG:9311 will be used instead. To use the
## original CRS, set the OSR_USE_NON_DEPRECATED configuration option to NO.
#download some data to map!
setwd("C:/Users/melha/Downloads")
county_data <- read.csv("county_data.csv")

#get the data ready to merge
library(tidyr)
counties1 <- counties %>% 
  separate(county_name, c("county_name", "county"), sep = " County") %>% 
  select(-county)

#Now merge it!!
county_data_full <- counties1 %>%
  left_join(county_data,by = c("county_name" = "county_name"))

plot(county_data_full)
## Warning: plotting the first 9 out of 22 attributes; use max.plot = 22 to plot
## all

#let's make our first leaflet plot

#put our data in the correct projection
#You need to do this every time, with this exact projection, or leaflet won't work
counties.sf = st_transform(county_data_full, "EPSG:4326")

leaflet() %>%
  leaflet::addPolygons(data = counties.sf,
                       color = "green")

So, now we have a beginning plot. We can make this much more interesting! Let’s add some variables and make them interactive.

#generate text for the map pop-up
p_popup <- paste0("<strong>Inequality: </strong>", county_data_full$inequality)

#generate a color palette with five values
#read more about leaflet colors here: https://rstudio.github.io/leaflet/colors.html 
pal_fun <- colorQuantile("YlOrRd", NULL, n = 5)

#now plot it with these custom options
leaflet() %>%
  leaflet::addPolygons(data = counties.sf, 
          # remove polygon borders             
                       stroke = FALSE, 
          # set fill color with function from above and value
                       fillColor = ~pal_fun(inequality), 
                       fillOpacity = 0.8, 
                       smoothFactor = 0.5, 
          # add pop-up text
                       popup = p_popup)

Wait, there’s more! Let’s add a legend

leaflet() %>%
  leaflet::addPolygons(data = counties.sf, 
                       stroke = FALSE, 
                       fillColor = ~pal_fun(inequality), 
                       fillOpacity = 0.8, 
                       smoothFactor = 0.5, 
                       popup = p_popup) %>% 
  addLegend("bottomright",  # location
            pal=pal_fun,    # palette function
            values=counties.sf$inequality, 
            title = 'Inequality') # legend title

Next, let’s add a basemap to make it look really sharp

leaflet() %>%
  leaflet::addPolygons(data = counties.sf, 
                       stroke = FALSE, 
                       fillColor = ~pal_fun(inequality), 
                       fillOpacity = 1, 
                       smoothFactor = 0.5, 
                       popup = p_popup) %>% 
  addLegend("bottomright",  
            pal=pal_fun,    
            values=counties.sf$inequality, 
            title = 'Inequality') %>% 
  #we can do this with one line of code:
  addTiles()

We can add different base maps, but these will be third party options so we need a different function to do this. Here’s an example:

leaflet() %>%
  leaflet::addPolygons(data = counties.sf, 
                       stroke = FALSE, 
                       fillColor = ~pal_fun(inequality), 
                       fillOpacity = 1, 
                       smoothFactor = 0.5, 
                       popup = p_popup) %>% 
  addLegend("bottomright",  
            pal=pal_fun,    
            values=counties.sf$inequality, 
            title = 'Inequality') %>% 
  #we can do this with one line of code:
  addProviderTiles(providers$CartoDB.Positron)

To save your map as an html image: copy paste your map code into a regular R file (not a notebook!). Click on export > save as webpage, and create a name for your interactive map. You should be able to open your map in a browser now!

#Let's add more than one layer!

#Create pallette and pop-up for second layer
p_popup2 <- paste0("<strong>% Middle Class: </strong>", county_data_full$frac_middleclass)

pal_fun2 <- colorQuantile("Blues", NULL, n = 5)

leaflet() %>%
  leaflet::addPolygons(data = counties.sf, 
                       stroke = FALSE, 
                       fillColor = ~pal_fun(inequality), 
                       fillOpacity = 1, 
                       smoothFactor = 0.5, 
                       popup = p_popup,
                       group = "Inequality") %>% 
  leaflet::addPolygons(data = counties.sf, 
                       stroke = FALSE, 
                       fillColor = ~pal_fun2(frac_middleclass), 
                       fillOpacity = 1, 
                       smoothFactor = 0.5, 
                       popup = p_popup2,
                       group = "Middle Class") %>% 
  addLegend("bottomright",  
            pal=pal_fun,    
            values=counties.sf$inequality, 
            title = 'Inequality',
            group = "Inequality") %>% 
  addLegend("bottomright",  
            pal=pal_fun2,    
            values=counties.sf$frac_middleclass, 
            title = '% Middle Class',
            group = "Middle Class") %>% 
  #we can do this with one line of code:
  addProviderTiles(providers$CartoDB.Positron) %>% 
  addLayersControl(overlayGroups = c("Inequality", "Middle Class"),
                   options = layersControlOptions(collapsed = FALSE))

A note of caution on this: I wouldn’t recommend adding more than two layers to a map at a time, as the legends might start to look very messy and cluttered together. You can play around with it, but just keep this in mind as you do. The goal of using leaflet is to make our maps more interactive and visually effective, not to increase the complexity of the map so much as to diminish its usefulness. TLDR: Don’t get carried away!

Finally, let’s look at some other types of layers we can add. We’ve added color to our polygons, but let’s also add points to our map.

#Points need to be in a CRS that stores data in lat/long format
#We'll need to reproject all of our data in order to properly map it
#EPSG:4326 is the code for the WGS84 projection, which is a very standard option if you want your data to be stored in degrees (rather than meters)
#Create points! This code tranforms polygons into county centroids and preserves all of our other variables 
points <- st_point_on_surface(county_data_full)
## Warning: st_point_on_surface assumes attributes are constant over geometries
points <- points %>% st_transform("EPSG:4326")
county_wgs <- county_data_full %>% st_transform("EPSG:4326")

leaflet() %>%
  leaflet::addPolygons(data = county_wgs,
                       #customize county borders
                       stroke = T,
                       weight = 0.5,
                       #fill changes solid fill of counties
                       fillOpacity = 0.8,
                       #color changes border colors
                       color = "gray",
                       fillColor = "lightgray") %>% 
  leaflet::addCircleMarkers(data = points,
                            stroke = F,
                            fillColor = ~pal_fun2(frac_middleclass), 
                            popup = p_popup2,
                            group = "Middle Class",
                            fillOpacity = 1,
                            #This changes the size of the points
                            radius = ~ (frac_middleclass*10)) %>% 
  addLegend("bottomright",  
            pal=pal_fun2,    
            values=counties.sf$frac_middleclass, 
            title = '% Middle Class',
            group = "Middle Class") %>% 
  #we can do this with one line of code:
  addProviderTiles(providers$CartoDB.Positron)

Resources

Engel, C. A. (2019). Using Spatial Data with R. Retrieved from: https://cengel.github.io/R-spatial/mapping.html.

R Studio (2016). Leaflet for R. Retrieved from: https://rstudio.github.io/leaflet/.