I've been trying to map the 435 US Congressional Districts (CD) using the R usmap package. I'm not committed to using usmap but it produces a much more user-friendly graphic than ggplot2 and I've not identified any suitable alternatives.
A bit of background to those who don't follow US politics, each of the 435 CDs sends one US Congressman to Washington where they vote on various laws, etc. CDs are created under the US Constitution and are different than state counties which usually have different boundaries.
It can be interesting to visualise how the Congresspeople have voted or are expected to vote on particular issues. You could also look at things such as educational level, median income, etc.
Here is the code for a basic 'county' map using usmap:
plot_usmap(regions = "counties")
Usmap does a great job presenting the boundaries of states and counties but doesn't appear to provide an in-built way to plot CDs or other shapefiles.
You can see an example below of what I'm trying to achieve. This graph was taken from a report produced by the US-based news service Axios. The layout is very similar to that generated by usmap so it is possible that this package.
The CD shapefile is available from the tigris package:
districts <- tigris::congressional_districts(cb = TRUE, resolution = '500k', year = 2023)
Any thoughts on how to use usmap to plot CDs or shapefiles in general?
IMHO, while usmap
allows for easy mapping of US state and county maps, for your use case you might be better off creating your map from "scratch" using tigris
and ggplot2
. To this end see also the book by the author of the tigris
package.
library(ggplot2)
library(tigris)
#> To enable caching of data, set `options(tigris_use_cache = TRUE)`
#> in your R script or .Rprofile.
set.seed(123)
districts <- tigris::congressional_districts(cb = TRUE, resolution = "500k", year = 2023)
states <- tigris::states(cb = TRUE, resolution = "500k", year = 2023)
# Shift and rescale Alaska, Hawaii, and Puerto Rico
districts_shifted <- tigris::shift_geometry(districts)
states_shifted <- tigris::shift_geometry(states)
# Get bounding box
bbox <- sf::st_bbox(states_shifted)
# Add a column of fake random categories to be mapped on "fill"
districts_shifted$fill <- sample(
c(NA, letters[1:5]), nrow(districts_shifted),
replace = TRUE
)
ggplot() +
geom_sf(
data = districts_shifted, aes(fill = fill),
linewidth = 0
) +
geom_sf(
data = states_shifted, fill = NA,
color = "white", linewidth = .5
) +
scale_fill_brewer(
labels = \(x) {
x[is.na(x)] <- "None"
x
},
breaks = \(x) {
c(x[is.na(x)], x[!is.na(x)])
},
palette = "Blues",
na.value = "grey80"
) +
# Set the limits to reduce white space
coord_sf(
xlim = min(abs(bbox[c(1,3)])) * c(-1, 1),
ylim = min(abs(bbox[c(2,4)])) * c(-1, 1)
) +
theme_void() +
theme(
legend.position = "top",
plot.margin = unit(rep(5.5, 4), "pt")
) +
guides(fill = guide_legend(
nrow = 1
)) +
labs(fill = NULL)