# Libraries
library(tidyverse)
library(compare)
library(sf)
library(leaflet)
library(leaflet.extras)
library(readr)
#files
data_file <- "~/Downloads/mobility_data.csv"
cz_center_file <- "~/Downloads/cz_centroids.rds"
geom_file <- "~/Downloads/geom_data.rds"
# Albers projection for 48 contiguous US states
US_ALBERS <-
"+proj=aea +lat_1=29.5 +lat_2=45.5 +lat_0=37.5 +lon_0=-96 +x_0=0 +y_0=0"
# GPS long-lat projection
WGS84 <-
"+proj=longlat +datum=WGS84 +no_defs"
# Colors
COLORS <- c(
"#FFFFE1",
"#FFFFC4",
"#FFFAAC",
"#FFD858",
"#FFD94F",
"#FEB93A",
"#FF7B00",
"#D64700",
"#BA1B00",
"#990200"
)
NA_COLOR <- "#f2f2f2"
#codes for alaska and hawaii
CZS_AK_HI <- c(
"34101",
"34102",
"34103",
"34104",
"34105",
"34106",
"34107",
"34108",
"34109",
"34110",
"34111",
"34112",
"34113",
"34114",
"34115",
"34701",
"34702",
"34703",
"35600"
)
DESCRIPTION <-
c(stringr::str_c('<p style="font-weight: normal">',
'This is <b style="font-weight: bold">very low</b>',
' compared with</br>the rest of the country.</p>'),
stringr::str_c('<p style="font-weight: normal">',
'This is <b style="font-weight: bold">below average</b>',
'</br>compared with the rest of the</br>country.</p>'),
stringr::str_c('<p style="font-weight: normal">',
'This is <b style="font-weight: bold">about average</b>',
'</br>compared with the rest of the</br>country.</p>'),
stringr::str_c('<p style="font-weight: normal">',
'This is <b style="font-weight: bold">above average</b>',
'</br>compared with the rest of the</br>country.</p>'),
stringr::str_c('<p style="font-weight: normal">',
'This is <b style="font-weight: bold">very high</b>',
' compared</br>with the rest of the country.</p>'))
CZ_MOB_DOMAIN <- c(34, 42.11, 45.07, 48, 60.8)
Store data in a tibble:
#read in col names
names <- read_csv(file = data_file, skip = 15, n_max = 0)
#read in data
data <- read_csv(file = data_file, skip = 17,
col_names = attributes(names)$names) %>%
slice(2:n()) %>%
dplyr::select(cz = `Commuting Zone`,
cz_name = `CZ Name`,
intercept = `Income Age 26 Intercept`,
slope = `Income Age 26 Slope`)
Calculate absolute upward mobility measurement, as defined in Chetty et al. (2014):
data <-
data %>%
mutate(mobility = intercept + .25*slope)
Compute mean absolute mobility per commuting zone:
data <-
data %>%
group_by(cz, cz_name) %>%
summarise(mean_mobility = mean(mobility*100, na.rm = TRUE)) %>%
ungroup()
Read in geometry data:
geom_data <- read_rds(geom_file)
geom <-
geom_data$q3 %>%
st_transform(crs = WGS84) %>%
mutate(cz = as.integer(cz_1990)) %>%
dplyr::select(cz, geometry)
Join mobility data to geometry data:
geom2 <-
geom %>%
left_join(data, by = "cz") %>%
st_transform(crs = WGS84)
Get state lines data:
state_lines <-
geom_data$q5 %>%
st_transform(crs = WGS84)
Separate state name from commuting zone name:
data <-
data %>%
separate(cz_name, into = c("cz_name", "state"), sep = ", ")
The following function formats the name and mobility measure for a hover-over label.
hover_format <- function(czname, mobility) {
if (is.na(mobility)) {
t <- '<span style="font-weight: normal">No data.</span>'
} else {
t <-
stringr::str_c('<p style="font-weight: normal">',
'In the ',
'<b style="font-weight: bold">', czname, '</b>',
' area, absolute upward',
'</br>',
' mobility is ',
'<b style="font-weight: bold">',
htmltools::htmlEscape(format(mobility, digit = 3)),
'.</p>',
DESCRIPTION[findInterval(mobility, CZ_MOB_DOMAIN) + 1])
}
htmltools::HTML(t)
}
Read in the centroid data (geometry for the center a given commuting zone):
cz_center <- read_rds(cz_center_file)
Color scale for the map:
color_scale <- function(x) {
scale_fill_gradientn(colours = COLORS,
values = seq(max(data$mean_mobility, na.rm = TRUE),
min(data$mean_mobility, na.rm = TRUE)) %>%
scales::rescale(),
na.value = NA_COLOR,
breaks = 26:52)$palette(scales::rescale(x)) %>%
stringr::str_replace_na(replacement = "#F3F3F3")
}
Labeling function for each commuting zone.
label_format <- function(czname) {
mob <- data %>%
filter(cz_name == czname) %>%
.$mean_mobility
t <-
stringr::str_c('<p>',
'<b style="font-weight: bold">', czname, '</b>',
'</br>',
'<b style="font-weight: normal">',
htmltools::htmlEscape(format(mob, digit = 3)), '</b>',
'</p>')
htmltools::HTML(t)
}
Plot:
leaf_crs <- leafletCRS(crsClass = "L.Proj.CRS",
code = "EPSG:42303",
proj4def = US_ALBERS,
resolutions = c(5500, 5500 / 5.5))
leaf_options <- leafletOptions(crs = leaf_crs, maxZoom = 2)
leaflet(options = leaf_options,
width = "1000px",
height = "650px") %>%
addPolygons(data = geom2,
group = "polygons",
fillOpacity = 1,
fillColor = ~color_scale(mean_mobility),
color = "white",
weight = 0.1,
opacity = 1,
highlightOptions = highlightOptions(color = "black",
weight = 1,
opacity = 1,
bringToFront = TRUE,
sendToBack = TRUE),
label = ~map2(cz_name, mean_mobility, hover_format),
labelOptions =
labelOptions(direction = "down",
opacity = 1,
textOnly = TRUE,
offset = c(0, 25),
style = list('border' = '1px solid #BBBBBB',
'border-radius' = '0px',
'font-size' = '10px',
'background-color' = '#FFFFFF',
'line-height' = '110%'))) %>%
addLabelOnlyMarkers(data = cz_center,
group = "labels",
label = ~map(czname, label_format),
labelOptions =
labelOptions(direction = "bottom",
noHide = 'T',
textOnly = T,
textsize = '10px',
style = list(
'text-align' = 'center',
'line-height' = '105%',
'font-size' = '9pt'
)
)) %>%
addPolylines(data = state_lines,
group = "state_lines",
weight = 1,
color = "gray") %>%
groupOptions(group = "labels", zoomLevels = 1) %>%
addLegend(color = color_scale(seq(26, 52, 3)),
labels = as.character(seq(26, 52, 3)),
opacity = 1,
title = "Absolute upward mobility",
position = "bottomright") %>%
setMapWidgetStyle(list(background = "white")) %>%
setMaxBounds(-131, 20, -68, 48.42)