Create maps in R in 10 (fairly) easy steps

Use the R programming language to turn location-based data into interactive maps

1 2 3 Page 3
Page 3 of 3

Step 8: Add palettes for a multi-layer map

Let's look at the GOP results in South Carolina among the top three candidates. I won't go over the data wrangling on this, except to say that I downloaded results from the South Carolina State Election Commission as well as Census Bureau data for education levels by county. If you download the project files, you'll see the initial data as well as the R code I used to add candidate vote percentages and join all that data to the South Carolina shapefile. That creates a geospatial object scmap to map.

There's so much data for a multi-candidate race that it's a little more complicated to choose what to color beyond "who won." I decided to go with one map layer to show the winner in each county, one layer each for the top three candidates (Trump, Rubio and Cruz) and a final layer showing percent of adult population with at least a bachelor's degree. (Why education level? Some news reports out of South Carolina said that seemed to correlate with levels of Trump's support; mapping that could help show if there's a pattern.)

In making my color palettes, I decided to use the same numerical scale for all three candidates. If I scaled color intensity for each candidate's minimum and maximum, a candidate with 10% to 18% would have a map with the same color intensities as one who had 45% to 52% — giving a wrong impression of the losing candidate's strength. So, first I calculated the minimum and maximum for the combined Trump/Rubio/Cruz county results:

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

Now I can create a palette for each candidate using different colors but the same intensity range.

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

I'll also add palettes for the winner and education layers:

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

Finally, I'll create a basic pop-up showing the county name, who won, the percentage for each candidate and percent of population with a college degree:

scpopup <- paste0("County: ", scmap@data$County,
"Winner: ", scmap@data$winner,
"Trump: ", percent(scmap$Donald.J.TrumpPct),
"Rubio: ", percent(scmap$Marco.RubioPct),
"Cruz: ", percent(scmap$Ted.CruzPct),
"Pct w college ed: ", scmap$PctCollegeDegree, "% vs state-wide avg of 25%")

Finally, before mapping, I know that I'm going to need to add the same projection that I needed for the New Hampshire map. This code will add that projection to the scmap object:

scmap <- sf::st_transform(scmap, "+proj=longlat +datum=WGS84")

This code shows a basic map of winners by county. Note that because only Trump and Rubio won counties in South Carolina, we can set up the legend to show only their colors and names:

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"))

And here's the result:

Another basic interactive map, this one with data on more than two candidates. Click any county to see results for the top three candidates and the percentage of the population with a college degree.

Step 9: Add map layers and controls

A multi-layer map with layer controls starts off the same as our previous map, with one addition: A group name. In this case, each layer will be its own group, but it's also possible to turn multiple layers on and off together.

The next step is to add additional polygon layers for each candidate and a final layer for college education, along with a layer control to wrap up the code. This time, we'll store the map in a variable and then display it:

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", "#e41a1c"), 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(scmap$PctCollegeDegree),
              group="College degs"
  ) %>%

  addLayersControl(
      baseGroups=c("Winners", "Trump", "Rubio", "Cruz", "College degs"),
      position = "bottomleft",
      options = layersControlOptions(collapsed = FALSE)
      ) 

And now display the map with:

scGOPmap

Interactive map with multiple layers. Click on the radio buttons at the bottom left to change which layer displays.

addLayersControl can have two types of groups: baseGroups, like used above, which allow only one layer to be viewed at a time; and overlayGroups, where multiple layers can be viewed at once and each turned off individually.

Step 10: Save your interactive map

If you're familiar with RMarkdown or Shiny, a Leaflet map can be embedded in an RMarkdown document or Shiny web application. If you'd like to use this map as an HTML page on a website or elsewhere, save a Leaflet map with the htmlwidget package's saveWidget() function:

# install.packages("htmlwidgets")
library("htmlwidgets")
saveWidget(widget=scGOPmap, file="scGOPprimary.html")

You can also save the map with external resources such as jQuery and the Leaflet JavaScript code in a separate directory by using the selfcontained=FALSE argument and choosing the subdirectory for the dependency files:

# install.packages("htmlwidgets")
save(widget=scGOPmap2, file="scGOPprimary_withdependencies.html", selfcontained=FALSE, libdir = "js")

Extra: Add address search to an R map

Thanks to the leaflet.extras, it's easy to add address search to a map with a single line of code. For a state-level map, drilling down to town or street name is probably enough, and you can do that by adding

%>% addSearchOSM()

to the end of the map code.

That will add a magnifying glass to the map, where you can click and enter a place where you want the map to zoom. However, it's not quite as elegant as searching on Google Maps. One limitation is that the OSM (Open Street Map) search-data source can't accept a street address such as 492 Old Connecticut Path, Framingham, MA — you need to enter a street name, town name, or place name such as "Old Connecticut Path, Framingham" or "Framingham High School" — but not a home or business complete address. Then, you need to wait for a drop-down list to select an available choice from available OSM data. Finally, on a long street or a large city, the map may not zoom to the spot you'd like to see.

If you need address-level searching, leaflet.extras has also implemented Google Maps and Bing searching. Using either of those services requires an API key. For the Google search, you need to get a Google Maps Geocoding API key. I also enabled the Google Maps JavaScript API in the Google developers console.

leaflet.extras' addSearchGoogle() function looks for a geocoding API key in a system environment variable called GOOGLE_MAP_GEOCODING_KEY, which you set with

Sys.setenv(GOOGLE_MAP_GEOCODING_KEY = "YourKeyHere")

Then, instead of adding %>% addSearchOSM() to the map code, add

%>% addSearchGoogle()

There's more information at package author Bhaskar V. Karambelkar's Leaflet search sample code page.

This should get you started on creating your own choropleth maps with R. To see how to create maps with latitude/longitude point markers, see Useful new R packages for data visualization and analysis.

This story was originally published in March 2016 and was updated in October 2017.

Next: Learn R for beginners with our PDF

Related:

Copyright © 2017 IDG Communications, Inc.

1 2 3 Page 3
Page 3 of 3
It’s time to break the ChatGPT habit
Shop Tech Products at Amazon