District One, in La’Vergne leads the subdivisions with 1,859 persons who match the criteria while District Thirteen, in Murfeesboro, seconds it with 1,844.
# ----------------------------------------------------------
# Step 1: Install required packages (if missing)
# ----------------------------------------------------------
if (!require("tidyverse"))
install.packages("tidyverse")
if (!require("tidycensus"))
install.packages("tidycensus")
if (!require("sf"))
install.packages("sf")
if (!require("leaflet"))
install.packages("leaflet")
if (!require("htmlwidgets"))
install.packages("htmlwidgets")
if (!require("plotly"))
install.packages("plotly") # For the interactive dot plot
# ----------------------------------------------------------
# Step 2: Load libraries
# ----------------------------------------------------------
library(tidyverse)
library(tidycensus)
library(sf)
library(leaflet)
library(htmlwidgets)
library(plotly)
# ----------------------------------------------------------
# Step 3: Transmit Census API key (uncomment and paste yours)
# ----------------------------------------------------------
census_api_key("904ea9b9c6b5f8f7d6b1d4846a59ee8cc3b48d1f")
# ----------------------------------------------------------
# Step 4: Fetch ACS codebooks (for variable lookup if needed)
# ----------------------------------------------------------
DetailedTables <- load_variables(2024, "acs5", cache = TRUE)
SubjectTables <- load_variables(2024, "acs5/subject", cache = TRUE)
ProfileTables <- load_variables(2024, "acs5/profile", cache = TRUE)
# ----------------------------------------------------------
# Step 5: Specify target variable(s)
# ----------------------------------------------------------
VariableList <- c(Estimate_ = "DP03_0108")
# ----------------------------------------------------------
# Step 6: Fetch ACS data (county subdivision, Tennessee)
# ----------------------------------------------------------
mydata <- get_acs(
geography = "county subdivision",
state = "TN",
variables = VariableList,
year = 2024,
survey = "acs5",
output = "wide",
geometry = TRUE
)
# ----------------------------------------------------------
# Step 7: Reformat the NAME field into Area / County / State
# ----------------------------------------------------------
mydata <- separate_wider_delim(
mydata,
NAME,
delim = ", ",
names = c("Area", "County", "State")
)
# ----------------------------------------------------------
# Step 8: Filter to Rutherford County
# ----------------------------------------------------------
filtereddata <- mydata %>%
filter(County %in% c("Rutherford County"))
# ----------------------------------------------------------
# Step 9: Prepare data for mapping (rename, as sf, CRS)
# ----------------------------------------------------------
mapdata <- filtereddata %>%
rename(
Estimate = Estimate_E,
Range = Estimate_M
) %>%
st_as_sf()
# Ensure CRS is WGS84 for Leaflet
mapdata <- st_transform(mapdata, 4326)
# ----------------------------------------------------------
# Step 10: Build color palette with quantile-based breaks
# ----------------------------------------------------------
qs <- quantile(mapdata$Estimate, probs = seq(0, 1, length.out = 6), na.rm = TRUE)
pal <- colorBin(
palette = "Blues", # Can specify other palettes here
domain = mapdata$Estimate,
bins = qs,
pretty = FALSE
)
# ----------------------------------------------------------
# Step 11: Build the plotly dot plot with error bars
# ----------------------------------------------------------
# Add point color from the same Leaflet palette and ordered y factor
filtereddata <- filtereddata %>%
mutate(
point_color = pal(Estimate_E),
y_ordered = reorder(Area, Estimate_E),
hover_text = dplyr::if_else(
!is.na(Area),
paste0("Area: ", Area),
Area
)
)
# Create the plotly scatter with horizontal error bars and thin gray borders
mygraph <- plot_ly(
data = filtereddata,
x = ~Estimate_E,
y = ~y_ordered,
type = "scatter",
mode = "markers",
marker = list(
color = ~point_color,
size = 8,
line = list(
color = "rgba(120,120,120,0.9)", # thin gray border for contrast
width = 0.5
)
),
error_x = list(
type = "data",
array = ~Estimate_M, # + side
arrayminus = ~Estimate_M, # - side
color = "rgba(0,0,0,0.65)",
thickness = 1
),
text = ~hover_text,
# Show District (from hover_text) and the X value with thousands separators
hovertemplate = "%{text}<br>%{x:,}<extra></extra>"
) %>%
layout(
title = list(text = "Estimates by area<br><sup>County subdivisions. Brackets show error margins.</sup>"),
xaxis = list(title = "ACS estimate"),
yaxis = list(title = "")
)
# display the plot
mygraph
# ----------------------------------------------------------
# Step 12: Create popup content for the map
# ----------------------------------------------------------
mapdata$popup <- paste0(
"<strong>", mapdata$Area, "</strong><br/>",
"<hr>",
"Estimate: ", format(mapdata$Estimate, big.mark = ","), "<br/>",
"Plus/Minus: ", format(mapdata$Range, big.mark = ",")
)
# ----------------------------------------------------------
# Step 13: Build the Leaflet map
# ----------------------------------------------------------
DivisionMap <- leaflet(mapdata) %>%
# Choose one basemap:
addProviderTiles(providers$CartoDB.Positron) %>%
# addProviderTiles(providers$Esri.WorldStreetMap, group = "Streets (Esri World Street Map)") %>%
# addProviderTiles(providers$Esri.WorldImagery, group = "Satellite (Esri World Imagery)") %>%
addPolygons(
fillColor = ~pal(Estimate),
fillOpacity = 0.5,
color = "black",
weight = 1,
popup = ~popup
) %>%
addLegend(
pal = pal,
values = ~Estimate,
title = "Estimate",
labFormat = labelFormat(big.mark = ",")
)
DivisionMap
# ----------------------------------------------------------
# Step 14: Export graph as a standalone HTML file
# ----------------------------------------------------------
# This creates a fully self-contained HTML file for the dot plot.
saveWidget(
widget = as_widget(mygraph),
file = "ACSGraph.html",
selfcontained = TRUE
)
# ----------------------------------------------------------
# Step 15: Export map as a standalone HTML file
# ----------------------------------------------------------
# This creates a fully self-contained HTML you can open or share.
# Adjust the path/filename as you like.
saveWidget(
widget = DivisionMap,
file = "ACSMap.html",
selfcontained = TRUE
)