Week 8

Fancy Maps

This week, we’ll make our maps zoom-able and interactive with the leaflet package.

Sample Code

Here’s a demonstration, plotting the county-level 2020 presidential election results for each county in Georgia.1 First, load all the packages we need:

library(tidyverse)
library(ggthemes) # for the theme_map() layer
library(sf) # functions for working with spatial data
library(tigris) # for map data from the US Census
library(leaflet) # for interactive maps

Next, we’ll get our map data from the tigris package. The format of this data is called a Simple Feature, or sf object. It is a more compact way of representing GIS data, and is quickly becoming the standard way to do things.

county_map <- counties('Georgia')

head(county_map)
Simple feature collection with 6 features and 17 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -85.46221 ymin: 31.01044 xmax: -81.73169 ymax: 34.58748
Geodetic CRS:  NAD83
    STATEFP COUNTYFP COUNTYNS GEOID     NAME        NAMELSAD LSAD
16       13      189 00348794 13189 McDuffie McDuffie County   06
53       13      025 00351605 13025 Brantley Brantley County   06
54       13      171 00326713 13171    Lamar    Lamar County   06
83       13      115 00353665 13115    Floyd    Floyd County   06
102      13      273 00352238 13273  Terrell  Terrell County   06
103      13      063 01672399 13063  Clayton  Clayton County   06
    CLASSFP MTFCC CSAFP CBSAFP METDIVFP FUNCSTAT      ALAND   AWATER
16       H1 G4020  <NA>  12260     <NA>        A  666590014 23114032
53       H1 G4020  <NA>  15260     <NA>        A 1147972258 10291563
54       H1 G4020   122  12060     <NA>        A  475264404  6044329
83       H1 G4020   122  40660     <NA>        A 1320404595 22414013
102      H1 G4020  <NA>  10500     <NA>        A  869695791  4951325
103      H1 G4020   122  12060     <NA>        A  366879097  6962586
       INTPTLAT     INTPTLON                       geometry
16  +33.4806126 -082.4795333 MULTIPOLYGON (((-82.44998 3...
53  +31.1973339 -081.9829779 MULTIPOLYGON (((-81.91012 3...
54  +33.0744405 -084.1466893 MULTIPOLYGON (((-84.24837 3...
83  +34.2636918 -085.2136851 MULTIPOLYGON (((-85.24134 3...
102 +31.7771909 -084.4394464 MULTIPOLYGON (((-84.56317 3...
103 +33.5426863 -084.3555727 MULTIPOLYGON (((-84.45856 3...

Notice that this may take a little while, because it’s downloading the shape data from the Census. Next we’ll merge that map data with the county-level election results.

# load data from project folder
load('../data/county-results-2020-for-map.RData')

# we're going to merge by fips code, so 
# make sure that the fips code in each dataset
# is named the same thing and is in the same format
county_map <- mutate(county_map, 
                     fips = as.numeric(GEOID))

counties_2020 <- mutate(counties_2020,
                        fips = as.numeric(county_fips))

# join the map data and the election results data together
d <- left_join(county_map, counties_2020, by = 'fips')

We can plot sf objects with ggplot using the geom_sf() layer.

# define the hex color codes for Democratic Blue and Republican Red
party_colors <- c("#CB454A", 'gray', "#2E74C0") 

ggplot(data = d,
       mapping = aes(fill = percent_biden)) +
  geom_sf() +
  scale_fill_gradient2(low = party_colors[1],
                       mid = party_colors[2],
                       high = party_colors[3],
                       midpoint = 50) +
  theme_map() +
  theme(legend.position = 'bottom') +
  labs(fill = 'Biden Two-Party Vote Share')

And we can make an interactive map with leaflet(). See here for instructions on modifying the color palette, and here for the complete list of base maps you can add.

# create the color scheme
pal <- colorNumeric(
  palette = party_colors,
  domain = d$percent_biden)

p <- leaflet(data = d) %>% 
  # add the county polygons, with fill color based on Biden vote share and label with county name
  addPolygons(fillColor = ~pal(percent_biden),
              weight = 1,
              opacity = 1,
              color = 'white',
              fillOpacity = 0.7,
              label = d$NAME) %>% 
  # add a basemap
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>% 
  # add a legend
  addLegend(pal = pal, values = ~percent_biden,
            opacity = 0.7, title = NULL,
            position = 'bottomright')

p

Reading Assignments

Next week, we discuss the challenges and opportunities presented by data that is collected across multiple time periods. To prepare, please read and annotate Chapter 16 of R For Data Science.

Team Project

Revise your map from last week based on our in-class feedback. Submit both a still image of your map from ggplot() and create an interactive version of the map with leaflet() to show off in class next week.


  1. All the code is available on the repository at R/week-08/interactive-map-election-results-2020.R.↩︎