Mapping Voters
The Data
Robert Husseman @RHusseman on Twitter is running to represent Oregon District 21 in the Oregon House of Representatives. He has a platform that focuses on housing and livability in the state by making sensible allocations of public resources to public problems. He has an MBA; he was my student and his head and heart are very much in the right place to serve Oregon well.
His campaign has a voter list for the Democratic voters in Oregon 21. He shared access to this. Let me load them up from a saved Excel spreadsheet derived from a Google sheet.
Geocoding
I ran across a package called tidygeocoder
to do the heavy lifting. Here is what I have to work with.
library(tidygeocoder)
names(RHCamp)
## [1] "VOTER_ID" "FIRST_NAME" "MIDDLE_NAME"
## [4] "LAST_NAME" "NAME_SUFFIX" "BIRTH_DATE"
## [7] "CONFIDENTIAL" "EFF_REGN_DATE" "STATUS"
## [10] "PARTY_CODE" "PHONE_NUM" "UNLISTED"
## [13] "COUNTY" "RES_ADDRESS_1" "RES_ADDRESS_2"
## [16] "HOUSE_NUM" "HOUSE_SUFFIX" "PRE_DIRECTION"
## [19] "STREET_NAME" "STREET_TYPE" "POST_DIRECTION"
## [22] "UNIT_TYPE" "UNIT_NUM" "ADDR_NON_STD"
## [25] "CITY" "STATE" "ZIP_CODE"
## [28] "ZIP_PLUS_FOUR" "EFF_ADDRESS_1" "EFF_ADDRESS_2"
## [31] "EFF_ADDRESS_3" "EFF_ADDRESS_4" "EFF_CITY"
## [34] "EFF_STATE" "EFF_ZIP_CODE" "EFF_ZIP_PLUS_FOUR"
## [37] "ABSENTEE_TYPE" "PRECINCT_NAME" "PRECINCT"
## [40] "SPLIT"
A Great Thing about R
There is a vignette explaining the usage.
My use case is pretty easy though there is a lot of data. I want to try this with just the addresses. I have loaded the tidyverse
and now I want to peel off just the data that I need in order to geocode it, and then process it with the Census geocoder. This results in 9443 queries.
GeoMe <- RHCamp %>% select(VOTER_ID, RES_ADDRESS_1, CITY, STATE) %>% geocode(street = RES_ADDRESS_1, city=CITY, state=STATE, method="census")
NewData <- GeoMe %>% left_join(., RHCamp)
save(NewData, file="~/Desktop/NewDataRH.RData")
This takes quite a long time to run – 667.1 seconds.
When it is complete, I will want to figure out how to map them in a way that is best fit for assisting a campaign. This seems like a good use for leaflet
. Rather than taking 10 minutes or so to compile each time, I saved them off. I am loading them from a local file in the first line.
Leaflet Mapping
load("~/Desktop/NewDataRH.RData")
The leaflet
package uses very powerful mapping tools that will be perfect for this use case because the maps have easy zoom features.
library(leaflet)
NewData %>% mutate(labMy = paste0(FIRST_NAME,LAST_NAME,RES_ADDRESS_1,sep="\n")) %>%
leaflet(data = .) %>%
addTiles() %>%
addCircleMarkers(~long, ~lat, label = ~labMy, fillOpacity = 0.1, stroke=FALSE, radius=1.6)
## Warning in validateCoords(lng, lat, funcName): Data contains 641 rows with
## either missing or invalid lat/lon values and will be ignored
That works but it isn’t quite right. Turns out that I will have to mess with html to get the labels quite right.
So, of course, I went to stackoverflow
.
Building Better Labels
I found a post on how to do this.
The key piece of code seems to be:
labs <- lapply(seq(nrow(NewData)), function(i) {
paste0( '<p>', NewData[i,"RES_ADDRESS_1"],'</p>' )
})
NewData %>%
leaflet(data = .) %>%
addTiles() %>%
addCircleMarkers(~long, ~lat, label = lapply(labs, htmltools::HTML), fillOpacity = 0.15, stroke=FALSE, radius=1.6)
## Warning in validateCoords(lng, lat, funcName): Data contains 641 rows with
## either missing or invalid lat/lon values and will be ignored