March 17, 2017

Mapping Durham's roads and waterways

When I first saw Ben Fry’s All Streets, I thought, “WOW, I need to learn how to make that!” And, a few years later Nathan Yau created a nice tutorial explaining how one could do so in R1. On a recent Thursday night, I felt lethargic from the recent Daylight Savings “Spring Forward” and the chill outside. Focus came to me slowly, though, and I decided I wanted to make a map for my home area: Durham.

Since you probably can’t see Nathan’s great tutorial, I’ll give you the gist: take US Census shapefile data, import into R with the maptools package, and use R’s built-in graphics functions to draw a map.

Finding data turned out to be somewhat of a hassle. The US Census FTP site kept timing out. I guess after work hours, there’s no guarantee. Fortunately, their website frontend was slightly more reliable (for some of these files, frustratingly, I ended up coming back the next day). The real trick to downloading data is figuring out what data to download!

A consultation of the State FIPS codes, revealed North Carolina to be magical number 37. Consulting the NC FIPS Codes reference, revealed Durham County to be magical number 063. Thusly equipped with the magical combination 37063, I could deduce that the proper Shapefile of roads for Durham County, NC is tl_2016_37063_roads.zip.

With just a smidgeon of R, I got started:

library(maptools)
quartz()

roads <- readShapeLines('./tl_2016_37063_roads.shp')
par(mar=c(0,0,0,0))
plot(roads)

Nathan’s post focused mostly on how one might style and combine data to end up with state and national maps. Fortunately, since I am more interested in county level, I don’t have to worry about combining Shapefile data. But I wanted to include the county’s shape as an outline, I thought. I also wanted to make Interstate and State roads more prominent than a neighborhood street.

I found the county shapes on the same website. To figure out what sort of road each line was, I had to decipher the roads dataframe:

> summary(roads$RTTYP)
   I    M    O    S    U NA's
   7 6034   15  430   47  514

The roads zip file came with an XML file tl_2016_37063_roads.shp.xml, which serves as a data dictionary for the Shapefile. I is Interstate, S is State Road, etcetera. Armed with this little knowledge I could bring a little style to my map by varying the line width lwd and coloring the lines:

library(maptools)
quartz()

roads <- readShapeLines('./tl_2016_37063_roads.shp')
counties <- readShapePoly('./tl_2016_us_county.shp')
durham <- subset(counties, STATEFP == 37 & COUNTYFP == '063')

par(mar=c(0,0,0,0), bg='#e0e0e0')
plot(durham, col='#ffffff', border=F)
# Interstate and Highways:
lines(subset(roads, RTTYP == "U"), col="#737373", lwd=1)
lines(subset(roads, RTTYP == "I"), col="#737373", lwd=1)
# State roads:
lines(subset(roads, RTTYP == "S"), col="#737373", lwd=0.8)
# Everything else:
lines(subset(roads, !(RTTYP %in% c('S','U','I'))), col="#737373", lwd=0.5)
Second attempt

I felt pretty good. I stepped back to admire my map and wondered what all the little gaps are. Why aren’t there roads in the South West? And, hey, it kinda looks weird not having Falls Lake in the East. I decided to add water to my maps.

Adding multiple polygon data sets to the map tripped me up a little bit, but in all my final effort was not overly complicated: add more Shapefiles for waterways, and water areas, and add legends. Here is the final script

Stylistically I wanted to keep with the aesthetic of All Streets and create a map with a minimal number of colors, and minimal number of explanatory graphing (like, the county border). Let the data speak for itself. I couldn’t decide whether to let the ground be white, or the water be white. So, here are both:


1 Sorry, members only! If you are interested in making beautiful visualizations in R, I highly recommend obtaining a membership.

© Dane Summers 2023