Precinct-level analysis

Pull the slider left or right to switch between showing the borders of Rutherford County’s voting precincts and the borders of Rutherford County’s five Tennessee House districts. Among precincts, darker-shading indicates precincts with larger counts of minority voters. Click or tap a district or precinct for details.

Demographic data come from the 2020 Census. District and precinct borders come from the Tennessee Comptroller. The map was produced using R and various packages including the geomander.

# Required packages

if (!require("tidyverse")) install.packages("tidyverse")
if (!require("mapview")) install.packages("mapview")
if (!require("sf")) install.packages("sf")
if (!require("leaflet")) install.packages("leaflet")
if (!require("leaflet.extras2")) install.packages("leaflet.extras2")
if (!require("geomander")) install.packages("geomander")

library(tidyverse)
library(mapview)
library(sf)
library(leaflet)
library(leafpop)
library(geomander)

# Data gathering

# Read TN 2024 precinct shapefile

my_sf <- read_sf("Voting_Precincts_5_31_24.shp")
RC_sf <- my_sf %>% 
  filter(COUNTY == 149)

# Create "sync" file for matching matches_id field
# to precinct label field

sync <- RC_sf
sync <- sync %>%
  st_drop_geometry(sync) %>% 
  mutate(matches_id = row_number()) %>%
  mutate(Precinct = NEWVOTINGP) %>% 
  select(matches_id, Precinct)

block <- create_block_table(state = 'TN', county = '149', year = 2020)  
matches <- geo_match(from = block, to = RC_sf)
prec <- block2prec(block_table = block, matches = matches)
precgeo <- block2prec(block_table = block, matches = matches, geometry = TRUE)
precgeo <- merge(precgeo,sync,
                 by="matches_id",
                 all.x = TRUE)

precgeo <- precgeo %>% 
  mutate(Pct_Nonwhite = round(1-(precgeo$vap_white/precgeo$vap),2)) %>% 
  mutate(Voters = vap) %>%
  mutate(Nonwhite = vap - vap_white) %>% 
  select(Precinct,
         Voters,
         Nonwhite,
         Pct_Nonwhite,
         geometry)
# Reading TN House districts map from
# https://tn-tnmap.opendata.arcgis.com/search?tags=boundaries

TNHouseDistricts <- st_read("TN_House_Districts.shp")
RCHoueDistricts <- TNHouseDistricts %>% 
  filter(DISTRICT == "13"|
           DISTRICT == "49"|
           DISTRICT == "37"|
           DISTRICT == "34"|
           DISTRICT == "48")

# Mapmaking

mypalette = colorRampPalette(c('lightgray', 'black'))

Precincts <- mapview(
  precgeo,
  zcol = "Nonwhite",
  map.types = ("OpenStreetMap"),
  legend = "FALSE",
  col.regions = mypalette,
  at = seq(0, 7000, 1),
  alpha.regions = .5,
  layer.name = "Precincts",
  popup = popupTable(
    precgeo,
    feature.id = FALSE,
    row.numbers = FALSE,
    zcol = c("Precinct", "Voters", "Nonwhite", "Pct_Nonwhite")
  )
)

Combined <-   mapview(
  precgeo,
  zcol = "Nonwhite",
  map.types = ("OpenStreetMap"),
  legend = "FALSE",
  col.regions = mypalette,
  at = seq(0, 7000, 7),
  alpha.regions = .5,
  layer.name = "Nonwhite",
  popup = popupTable(
    precgeo,
    feature.id = FALSE,
    row.numbers = FALSE,
    zcol = c("Precinct", "Voters", "Nonwhite", "Pct_Nonwhite")
  )
) + mapview(
  RCHoueDistricts,
  zcol = "DISTRICT",
  map.types = ("OpenStreetMap"),
  layer.name = "District",
  popup = popupTable(
    RCHoueDistricts,
    feature.id = FALSE,
    row.numbers = FALSE,
    zcol = c("DISTRICT", "NAME", "POPULATION")
  )
) 

Combined | Precincts

Block-level analysis

Same as above, but with block-level data in place of precinct-level data.

block2 <- block %>% 
  mutate(Minorities = vap - vap_white) %>% 
  mutate(Voters = vap) %>% 
  mutate(Block = block)
block2 <- block2 %>% 
  select(Block,
         Voters,
         Minorities,
         geometry)

mypalette = colorRampPalette(c('lightgray', 'black'))

Blocks <- mapview(
  block2,
  zcol = "Minorities",
  map.types = ("OpenStreetMap"),
  legend = "FALSE",
  col.regions = mypalette,
  at = seq(0, 600, 3),
  alpha.regions = .7,
  layer.name = "Minorities",
  popup = popupTable(
    block2,
    feature.id = FALSE,
    row.numbers = FALSE,
    zcol = c("Voters", "Minorities")
  )
)

mypalette = colorRampPalette(c('lightgray', 'black'))

Combined <- mapview(
  block2,
  zcol = "Minorities",
  map.types = ("OpenStreetMap"),
  legend = "FALSE",
  col.regions = mypalette,
  at = seq(0, 600, 3),
  alpha.regions = .7,
  layer.name = "Minorities",
  popup = popupTable(
    block2,
    feature.id = FALSE,
    row.numbers = FALSE,
    zcol = c("Block", "Voters", "Minorities")
  )
) + mapview(
  RCHoueDistricts,
  zcol = "DISTRICT",
  map.types = ("OpenStreetMap"),
  layer.name = "District",
  popup = popupTable(
    RCHoueDistricts,
    feature.id = FALSE,
    row.numbers = FALSE,
    zcol = c("DISTRICT", "NAME", "POPULATION")
  )
) 

Combined | Blocks

Nonwhites by Tennessee House district

Rutherford County’s Tennessee House Districts. Click or tap a district to view details on voter counts and percentages.

# Reading TN House districts map from
# https://tn-tnmap.opendata.arcgis.com/search?tags=boundaries

TNHouseDistricts <- st_read("TN_House_Districts.shp")
RCHoueDistricts <- TNHouseDistricts %>% 
  filter(DISTRICT == "13"|
           DISTRICT == "49"|
           DISTRICT == "37"|
           DISTRICT == "34"|
           DISTRICT == "48")

# Create "sync" file for matching matches_id field
# to distict label field

sync <- RCHoueDistricts
sync <- sync %>%
  st_drop_geometry(sync) %>% 
  mutate(matches_id = row_number()) %>%
  mutate(Dist = DISTRICT) %>%
  mutate(Rep = NAME) %>% 
  select(matches_id, Dist, Rep)

block <- create_block_table(state = 'TN', county = '149', year = 2020)  
matches <- geo_match(from = block, to = RCHoueDistricts)
distgeo <- block2prec(block_table = block, matches = matches, geometry = TRUE)

distgeo <- merge(distgeo,sync,
                 by="matches_id",
                 all.x = TRUE)

distgeo <- distgeo %>% 
  mutate(Pct_Nonwhite = round(1-(distgeo$vap_white/distgeo$vap),2)) %>% 
  mutate(Voters = vap) %>%
  mutate(Nonwhite = vap - vap_white) %>% 
  select(Dist,
         Rep,
         Voters,
         Nonwhite,
         Pct_Nonwhite,
         geometry)

DistMap <- mapview(
  distgeo,
  zcol = "Dist",
  map.types = ("OpenStreetMap"),
  layer.name = "District",
  popup = popupTable(
    distgeo,
    feature.id = FALSE,
    row.numbers = FALSE,
    zcol = c("Dist",
             "Rep",
             "Voters",
             "Nonwhite",
             "Pct_Nonwhite")))

DistMap

Before redistricting vs. after redistricting, Tennessee State House districts

Drag the slider left to see the Tennessee House district borders before redistricting. Drag the slider left to see The borders as they are now.

# Data gathering

# Reading TN House districts map from
# https://tn-tnmap.opendata.arcgis.com/search?tags=boundaries

TNHouseDistricts <- st_read("TN_House_Districts.shp")
RCHoueDistricts <- TNHouseDistricts %>% 
  filter(DISTRICT == "13"|
           DISTRICT == "49"|
           DISTRICT == "37"|
           DISTRICT == "34"|
           DISTRICT == "48")

# Create "sync" file for matching matches_id field
# to distict label field

sync <- RCHoueDistricts
sync <- sync %>%
  st_drop_geometry(sync) %>% 
  mutate(matches_id = row_number()) %>%
  mutate(Dist = DISTRICT) %>%
  mutate(Rep = NAME) %>% 
  select(matches_id, Dist, Rep)

block <- create_block_table(state = 'TN', county = '149', year = 2020)  
matches <- geo_match(from = block, to = RCHoueDistricts)
distgeo <- block2prec(block_table = block, matches = matches, geometry = TRUE)

distgeo <- merge(distgeo,sync,
                 by="matches_id",
                 all.x = TRUE)

distgeo <- distgeo %>%
  mutate(Period = "Now") %>% 
  mutate(Pct_Nonwhite = round(1-(distgeo$vap_white/distgeo$vap),2)) %>% 
  mutate(Voters = vap) %>%
  mutate(Nonwhite = vap - vap_white) %>% 
  select(Period,
         Dist,
         Rep,
         Voters,
         Nonwhite,
         Pct_Nonwhite,
         geometry)

DistMap <- mapview(
  distgeo,
  zcol = "Dist",
  map.types = ("OpenStreetMap"),
  layer.name = "Now",
  popup = popupTable(
    distgeo,
    feature.id = FALSE,
    row.numbers = FALSE,
    zcol = c("Period",
             "Dist",
             "Rep",
             "Voters",
             "Nonwhite",
             "Pct_Nonwhite")))

#######################

# Reading 2018 State House Districts file
# From U.S. Census Bureau
# https://www.census.gov/geographies/mapping-files/time-series/geo/carto-boundary-file.html

TNHouse2018 <- st_read("cb_2018_47_sldl_500k.shp")

# Filtering for Rutherford districts

RCHouse2018 <- TNHouse2018 %>% 
  filter(SLDLST == "049"|
           SLDLST == "037"|
           SLDLST == "034"|
           SLDLST == "048")

# Create "sync" file for matching matches_id field
# to distict label field

sync <- RCHouse2018
sync <- sync %>%
  st_drop_geometry(sync) %>% 
  mutate(matches_id = row_number()) %>%
  mutate(Dist = SLDLST) %>%
  select(matches_id, Dist)

# Pair 2020 data with 2018 districts

block <- create_block_table(state = 'TN', county = '149', year = 2020)  
matches <- geo_match(from = block, to = RCHouse2018)
distgeo <- block2prec(block_table = block, matches = matches, geometry = TRUE)

# Adding district labels from sync file

distgeo <- merge(distgeo,sync,
                 by="matches_id",
                 all.x = TRUE)

# Calculating minority voter fields
# and dropping unneeded columns

distgeo <- distgeo %>%
  mutate(Period = "Before") %>% 
  mutate(Pct_Nonwhite = round(1-(distgeo$vap_white/distgeo$vap),2)) %>% 
  mutate(Voters = vap) %>%
  mutate(Nonwhite = vap - vap_white) %>% 
  select(Period,
         Dist,
         Voters,
         Nonwhite,
         Pct_Nonwhite,
         geometry)

# Standardizing District number format

distgeo <- distgeo %>% 
  mutate(Dist = case_when(Dist == "037" ~ "37",
                          Dist == "034" ~ "34",
                          Dist == "048" ~ "48",
                          Dist == "049" ~ "49"))

# Mapping

HouseDistricts2018 <- mapview(
  distgeo,
  zcol = "Dist",
  map.types = ("OpenStreetMap"),
  layer.name = "Before",
  popup = popupTable(
    distgeo,
    feature.id = FALSE,
    row.numbers = FALSE,
    zcol = c("Period", "Dist", "Voters", "Nonwhite", "Pct_Nonwhite")
  )
)

DistMap | HouseDistricts2018

Before redistricting vs. after redistricting, Tennessee State Senate districts

Drag the slider left to see the Tennessee State Senate district borders before redistricting. Drag the slider left to see The borders as they are now.

# Data gathering

# Reading TN Senate districts map from
# https://tn-tnmap.opendata.arcgis.com/search?tags=boundaries

TNSenateDistricts <- st_read("TN_Senate_Districts.shp")
RCSenateDistricts <- TNSenateDistricts %>% 
  filter(DISTRICT == "13"|
           DISTRICT == "14")

# Create "sync" file for matching matches_id field
# to distict label field

sync <- RCSenateDistricts
sync <- sync %>%
  st_drop_geometry(sync) %>% 
  mutate(matches_id = row_number()) %>%
  mutate(Dist = DISTRICT) %>%
  mutate(Rep = NAME) %>% 
  select(matches_id, Dist, Rep)

block <- create_block_table(state = 'TN', county = '149', year = 2020)  
matches <- geo_match(from = block, to = RCSenateDistricts)
distgeo <- block2prec(block_table = block, matches = matches, geometry = TRUE)

distgeo <- merge(distgeo,sync,
                 by="matches_id",
                 all.x = TRUE)

distgeo <- distgeo %>%
  mutate(Period = "Now") %>% 
  mutate(Pct_Nonwhite = round(1-(distgeo$vap_white/distgeo$vap),2)) %>% 
  mutate(Voters = vap) %>%
  mutate(Nonwhite = vap - vap_white) %>% 
  select(Period,
         Dist,
         Rep,
         Voters,
         Nonwhite,
         Pct_Nonwhite,
         geometry)

DistMap <- mapview(
  distgeo,
  zcol = "Dist",
  map.types = ("OpenStreetMap"),
  layer.name = "Now",
  popup = popupTable(
    distgeo,
    feature.id = FALSE,
    row.numbers = FALSE,
    zcol = c("Period",
             "Dist",
             "Rep",
             "Voters",
             "Nonwhite",
             "Pct_Nonwhite")))

DistMap

#######################

# Reading 2018 State Senate Districts file
# From U.S. Census Bureau
# https://www.census.gov/geographies/mapping-files/time-series/geo/carto-boundary-file.html

TNSenate2018 <- st_read("cb_2018_47_sldu_500k.shp")

# Filtering for Rutherford districts

RCSenate2018 <- TNSenate2018 %>% 
  filter(SLDUST == "013"|
           SLDUST == "014")

# Create "sync" file for matching matches_id field
# to distict label field

sync <- RCSenate2018
sync <- sync %>%
  st_drop_geometry(sync) %>% 
  mutate(matches_id = row_number()) %>%
  mutate(Dist = SLDUST) %>%
  select(matches_id, Dist)

# Pair 2020 data with 2018 districts

block <- create_block_table(state = 'TN', county = '149', year = 2020)  
matches <- geo_match(from = block, to = RCSenate2018)
distgeo <- block2prec(block_table = block, matches = matches, geometry = TRUE)

# Adding district labels from sync file

distgeo <- merge(distgeo,sync,
                 by="matches_id",
                 all.x = TRUE)

# Calculating minority voter fields
# and dropping unneeded columns

distgeo <- distgeo %>%
  mutate(Period = "Before") %>% 
  mutate(Pct_Nonwhite = round(1-(distgeo$vap_white/distgeo$vap),2)) %>% 
  mutate(Voters = vap) %>%
  mutate(Nonwhite = vap - vap_white) %>% 
  select(Period,
         Dist,
         Voters,
         Nonwhite,
         Pct_Nonwhite,
         geometry)

# Standardizing District number format

distgeo <- distgeo %>% 
  mutate(Dist = case_when(Dist == "013" ~ "13",
                          Dist == "014" ~ "14"))

# Mapping

SenDistricts2018 <- mapview(
  distgeo,
  zcol = "Dist",
  map.types = ("OpenStreetMap"),
  layer.name = "Before",
  popup = popupTable(
    distgeo,
    feature.id = FALSE,
    row.numbers = FALSE,
    zcol = c("Period", "Dist", "Voters", "Nonwhite", "Pct_Nonwhite")
  )
)

SenDistricts2018

DistMap | SenDistricts2018