Set various values needed, including names of files and FIPS codes for New Hampshire and South Carolina

#nhdatafiile <- "NHD2016.xlsx"
nhdatafilecsv <- "NHD2016.csv"
usshapefile <- "cb_2014_us_county_5m/cb_2014_us_county_5m.shp"
nhfipscode <- "33"
scdatafile <- "SCGOP2016.csv"
scfipscode <- "45"

Load tmap, tmaptools, and leaflet packages into your working session

library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ ggplot2 3.4.0     ✔ purrr   1.0.1
## ✔ tibble  3.2.1     ✔ dplyr   1.1.1
## ✔ tidyr   1.3.0     ✔ stringr 1.5.0
## ✔ readr   2.1.3     ✔ forcats 1.0.0
## Warning: package 'tibble' was built under R version 4.2.3
## Warning: package 'dplyr' was built under R version 4.2.3
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
library(tmap)
## Warning: package 'tmap' was built under R version 4.2.3
library(tmaptools)
## Warning: package 'tmaptools' was built under R version 4.2.3
library(leaflet)
## Warning: package 'leaflet' was built under R version 4.2.3
library(sf)
## Warning: package 'sf' was built under R version 4.2.3
## Linking to GEOS 3.9.3, GDAL 3.5.2, PROJ 8.2.1; sf_use_s2() is TRUE
library(leaflet.extras)
## Warning: package 'leaflet.extras' was built under R version 4.2.3
library(rio)
## Warning: package 'rio' was built under R version 4.2.3
library(sp)
## Warning: package 'sp' was built under R version 4.2.3

Step 1: Read in the NH election results file:

setwd("C:/Users/Upsta/OneDrive/R Programming/GIS")
nhdata <- import(nhdatafilecsv)

Eliminate columns for minor candidates and just use County, Clinton, and Sanders columns:

nhdata <- nhdata[, c("County", "Clinton", "Sanders")]

Step 2: Decide what data to map

Add columns for percents and margins:

nhdata$SandersMarginVotes <- nhdata$Sanders - nhdata$Clinton
nhdata$SandersPct <- (nhdata$Sanders) / (nhdata$Sanders + nhdata$Clinton)
nhdata$ClintonPct <- (nhdata$Clinton) / (nhdata$Sanders + nhdata$Clinton)
nhdata$SandersMarginPctgPoints <- nhdata$SandersPct - nhdata$ClintonPct

Step 3: Get Geographic data files

Read in the shapefile for US states and counties:

library(raster)
## Warning: package 'raster' was built under R version 4.2.3
## 
## Attaching package: 'raster'
## The following object is masked from 'package:dplyr':
## 
##     select
library(rgdal)
## Warning: package 'rgdal' was built under R version 4.2.3
## Please note that rgdal will be retired during 2023,
## plan transition to sf/stars/terra functions using GDAL and PROJ
## at your earliest convenience.
## See https://r-spatial.org/r/2022/04/12/evolution.html and https://github.com/r-spatial/evolution
## rgdal: version: 1.6-5, (SVN revision 1199)
## Geospatial Data Abstraction Library extensions to R successfully loaded
## Loaded GDAL runtime: GDAL 3.5.2, released 2022/09/02
## Path to GDAL shared files: C:/Users/Upsta/AppData/Local/R/win-library/4.2/rgdal/gdal
## GDAL binary built with GEOS: TRUE 
## Loaded PROJ runtime: Rel. 8.2.1, January 1st, 2022, [PJ_VERSION: 821]
## Path to PROJ shared files: C:/Users/Upsta/AppData/Local/R/win-library/4.2/rgdal/proj
## PROJ CDN enabled: FALSE
## Linking to sp version:1.6-0
## To mute warnings of possible GDAL/OSR exportToProj4() degradation,
## use options("rgdal_show_exportToProj4_warnings"="none") before loading sp or rgdal.
setwd("C:/Users/Upsta/OneDrive/R Programming/GIS")
usgeo <- shapefile("cb_2014_us_county_5m/cb_2014_us_county_5m.shp")
## Warning: [vect] Z coordinates ignored

Do a quick plot (qtm stands for quick thematic map) of the shapefile ands check its structure

tmap_options(check.and.fix = TRUE)
qtm(usgeo)
## Warning: The shape usgeo is invalid. See sf::st_is_valid

Subset just the NH data from the US shapefile

nhgeo <- usgeo[usgeo$STATEFP == nhfipscode,]

tmap test plot of the New Hampshire data

qtm(nhgeo)

Structure of the object

#str(nhgeo)
str(nhgeo$NAME)
##  chr [1:10] "Grafton" "Hillsborough" "Coos" "Belknap" "Rockingham" ...
str(nhdata$County)
##  chr [1:10] "Belknap" "Carroll" "Cheshire" "Coos" "Grafton" "Hillsborough" ...
nhgeo$NAME <- as.character(nhgeo$NAME)
nhgeo$NAME
##  [1] "Grafton"      "Hillsborough" "Coos"         "Belknap"      "Rockingham"  
##  [6] "Cheshire"     "Strafford"    "Merrimack"    "Carroll"      "Sullivan"

Order each data set by county name

nhgeo <- nhgeo[order(nhgeo$NAME),]
nhdata <- nhdata[order(nhdata$County),]
if (identical(nhgeo$NAME, nhdata$County)) {
  nhmap <- merge(nhgeo, nhdata, by.x = "NAME", by.y = "County")
} else {stop}

Step 4: Merge geo data with tresults data using the merge function

#str(nhmap)

Step 5: Create a static map with tmap’s qtm() function:

qtm(nhmap, "SandersMarginVotes")
## Some legend labels were too wide. These labels have been resized to 0.62, 0.62, 0.62, 0.57, 0.53. Increase legend.width (argument of tm_layout) to make the legend wider and therefore the labels larger.

qtm(nhmap, "SandersMarginPctgPoints")

For more control over look and feel, use the tm_shape() function:

tm_shape(nhmap) +
  tm_fill("SandersMarginPctgPoints", title ="Sanders Margin, Total Votes", palette = "PRGn") +
  tm_borders(alpha=.5) +
  tm_text("NAME", size=0.8)

Same code as above, but store the static map in a variable, and chnage the theme to “classic” style

nhstaticmap <- tm_shape(nhmap) +
  tm_fill("SandersMarginVotes", title ="Sanders Margin, Total Votes", palette = "PRGn") +
  tm_borders(alpha=.5) +
  tm_text("NAME", size=0.8) +
  tm_style("classic")

View the map

nhstaticmap
## Some legend labels were too wide. These labels have been resized to 0.62, 0.62, 0.62, 0.57, 0.53. Increase legend.width (argument of tm_layout) to make the legend wider and therefore the labels larger.

Save the map the a jpg file with the tmap’s tmap_save():

tmap_save(nhstaticmap, filename="nhdemprimary.jpg")
## Map saved to C:\Users\Upsta\OneDrive\R Programming\nhdemprimary.jpg
## Resolution: 1501.336 by 2937.385 pixels
## Size: 5.004452 by 9.791282 inches (300 dpi)

Part 6

Create a palette

clintonPalette <- colorNumeric(palette="Blues", domain=nhmap$ClintonPct)

and a pop-up window

library(scales)
## Warning: package 'scales' was built under R version 4.2.3
## 
## Attaching package: 'scales'
## The following object is masked from 'package:purrr':
## 
##     discard
## The following object is masked from 'package:readr':
## 
##     col_factor
nhpopup <- paste0("CountY: ", nhmap$NAME,
"<br /><br /> Sanders, ", percent(nhmap$SandersPct), " Clinton ", percent(nhmap$ClintonPct))

Step 7: Now generate the interactive map

nhmap_projected <- sp::spTransform(nhmap, "+proj=longlat +datum=WGS84")
leaflet(nhmap_projected) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addPolygons(stroke=FALSE,
              smoothFactor = 0.2,
              fillOpacity = .8,
              popup=nhpopup,
              color= ~clintonPalette(nhmap$ClintonPct))

South Carolina Data

setwd("C:/Users/Upsta/OneDrive/R Programming/GIS")
scdata <- rio::import(scdatafile)

South Carolina shapefile and Quick plot of scgeo SC geospatial object:

scgeo <- usgeo[usgeo@data$STATEFP=="45",]
qtm(scgeo)

Add a column with percent votes for each candidate. Candidates are in columns 2-7:

candidates <- colnames(scdata[2:7])
for(i in 2:7) {
  j = i + 7
  temp <- scdata[[i]] / scdata$Total
  scdata[[j]] <- temp
  colnames(scdata)[j] <- paste0(colnames(scdata)[i], "Pct")
}
winnner <- colnames(scdata[2:7])

Get winner in each precinct

for(i in 1:nrow(scdata)){
  scdata$winner[i] <- names(which.max(scdata[i,2:7]))
}

Import spreadsheet with percent of adult population holding at least a 4-yr college degree

setwd("C:/Users/Upsta/OneDrive/R Programming/GIS")
sced <- rio::import("SCdegree.xlsx")

Check if county names are in the same format in both files

str(scgeo$NAME)
##  chr [1:46] "Edgefield" "Lee" "Horry" "Allendale" "Marion" "Dorchester" ...
str(scdata$County)
##  chr [1:46] "Abbeville" "Aiken" "Allendale" "Anderson" "Bamberg" "Barnwell" ...
scgeo$NAME <- as.character(scgeo$NAME)
scgeo <- scgeo[order(scgeo$NAME),]
scdata <- scdata[order(scdata$County),]
if (identical(scgeo$NAME, scdata$County)) {
  scmap <- merge(scgeo, scdata, by.x = "NAME", by.y = "County")
} else {stop}

str(scgeo$NAME)
##  chr [1:46] "Abbeville" "Aiken" "Allendale" "Anderson" "Bamberg" "Barnwell" ...
str(scdata$County)
##  chr [1:46] "Abbeville" "Aiken" "Allendale" "Anderson" "Bamberg" "Barnwell" ...

Add the election results and rename county column

scmap <- merge(scgeo, scdata, by.x="NAME", by.y= "County")

Instead of just coloring the winner, let’s color by strength of win with multiple layers

minpct <- min(c(scdata$`Donald J TrumpPct`, scdata$`Marco RubioPct`, scdata$`Ted CruzPct`))
maxpct <- max(c(scdata$`Donald J TrumpPct`, scdata$`Marco RubioPct`, scdata$`Ted CruzPct`))

Create leaflet palettes for each layer of the map:

trumpPalette <- colorNumeric(palette = "Purples", domain=c(minpct, maxpct))
rubioPalette <- colorNumeric(palette = "Reds", domain = c(minpct, maxpct))
cruzPalette <- colorNumeric(palette = "Oranges", domain = c(minpct, maxpct))

winnerPalette <- colorFactor(palette=c("#984ea3","#e41a1c"), domain = scmap$winner)
edPalette <- colorNumeric(palette= "Blues", domain = scmap$PctCollegeDegree)

Create a pop-up:

scpopup <- paste0("<b>County: ", scmap$NAME, "<br />Winner: ", scmap$winner, "</b><br /><br 
/>Trump: ", percent(scmap$`Donald J TrumpPct`), "<br />Rubio: ", percent(scmap$`Marco RubioPct`), "<br />Cruz: ", percent(scmap$`Ted CruzPct`), "<br /><br />Pct w college ed: ", sced$PctCollegeDegree, "% vs state-wide avg of 25%")

Add the projection we know from the NH map we’ll need for the data on a Leaflet map:

scmap <- sp::spTransform(scmap, "+proj=longlat +datum=WGS84")

Basic interactive map shwoing winner in each county

leaflet(scmap) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addPolygons(stroke=TRUE,
              weight=1,
              smoothFactor = 0.2,
              fillOpacity = .75,
              popup=scpopup,
              color= ~winnerPalette(scmap$winner),
              group="Winners" ) %>%
  addLegend(position="bottomleft", colors=c("#984ea3", "#e41a1c"), labels=c("Trump", "Rubio"))

Put top 3 candidates in there own layers and add education layer, store in scGOPmap2 variable

scGOPmap <- leaflet(scmap) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addPolygons(stroke=TRUE,
              weight=1,
              smoothFactor =0.2,
              fillOpacity =.75,
              popup=scpopup,
              color= ~winnerPalette(scmap$winner),
              group="Winners"  ) %>%
  addLegend(position="bottomleft", colors=c("#984ea3", "#ea1a1c"), labels=c("Trump", "Rubio")) %>%
  
  addPolygons(stroke=TRUE,
              weight=1,
              smoothFactor =0.2,
              fillOpacity =.75,
              popup=scpopup,
              color= ~trumpPalette(scmap$`Donald J TrumpPct`),
              group= "Trump") %>%
  
  addPolygons(stroke=TRUE,
              weight=1,
              smoothFactor =0.2,
              fillOpacity =.75,
              popup=scpopup,
              color= ~rubioPalette(scmap$`Marco RubioPct`),
              group="Rubio") %>%
  
  addPolygons(stroke=TRUE,
              weight=1,
              smoothFactor =0.2,
              fillOpacity =.75,
              popup=scpopup,
              color= ~cruzPalette(scmap$`Ted CruzPct`),
              group="Cruz") %>%
  
  addPolygons(stroke=TRUE,
              weight=1,
              smoothFactor =0.2,
              fillOpacity =.75,
              popup=scpopup,
              color= ~edPalette(sced$PctCollegeDegree),
              group="College degs") %>%
  
  addLayersControl(
    baseGroups =c("Winners", "Trump", "Rubio", "Cruz", "College degs"),
    position = "bottomleft",
    options = layersControlOptions(collapsed = FALSE)
  )

scGOPmap