Making a Maine Map with purrr to Add Multiple Layers
R leaflet · maps · R
This week I went backcountry camping with Brooke at Cutler Public Land Preserve on Maine’s northern coast. Mainers call this area the bold coast for its dramatic cliffs, which was certainly the case at our campsite pictured below.
I’ve hiked Cutler before but forgot how awesome and awe-inspiring the landscape is. While we were hiking out we started talking about other places in Maine that we’ve visited and would like to go back to. It seems like whenever anyone comes to visit (which seems like every weekend in July and never in February…), we can’t remember all of the great places Maine has to offer us and our friends.
To help me remember our Maine favorites, I created a leaflet map that categorizes our favorite places to camp, eat, drink, learn, shop, and sight-see.
The markers are added in seperate layers using the purrr
package, so we don’t have to call addAwesomeMarkers
a bunch of times and to make it easier to add new categories and more favorites later. Each type of marker can be toggled on and off using the menu in the upper right corner and clicking on a marker reveals a popup with more information about the location.
The data for the map lives in a googlesheet and I wrote an R script to geocode new locations that are added to the sheet.
Geocoding Our Maine Favorites
After loading the libraries, we use the googlesheets
package to read the data into R. The sheet key used to locate the sheet in the gs_key
function is available in the sheet’s url.
# load libraries
library(googlesheets)
library(tidyverse)
library(ggmap)
# load maine places data from googlesheets
maine_sheet <- gs_key("1E9ARIwEj76Atp_kbYziGAWNAyLK82tO_Ad5IdRMjpWE",
lookup = FALSE,
visibility = "private")
# read maine place data into a dataframe
maine <- gs_read(maine_sheet)
Then we split the data into new and old locations (i.e., those that have coordinates and those that do not.).
# create df of locations without lat/lon
new_locations <-
maine %>%
filter(is.na(lon)) %>%
mutate(location = paste(address, city, "Maine", sep = ", ")) %>%
select(-lon, -lat)
# create df of locations with lat/lon
old_locations <-
maine %>%
filter(!is.na(lon))
Leaving the old locations alone, we geocode all of the new locations. To return a geocoded data frame in one fell swoop we use the map_df
function from the purrr
package to iterate the geocode
function from the ggmaps
package over each row in the new_locations
data frame.
# geocode new locations
new_locations <-
new_locations %>%
select(location) %>%
# geocode each location in new_locations
map_df(~geocode(., override_limit = TRUE)) %>%
# bind lon and lat onto new_locations
bind_cols(new_locations, .) %>%
# drop location variable created for geocoding
select(-location)
Then we reunite the new and old locations and write the new data back to the same googlesheet using the gs_edit_cells
function from the googlesheets
package.
# combine new and old locations
maine <-
new_locations %>%
bind_rows(old_locations)
# write data with new lat/lon to googlesheet
gs_edit_cells(ss = maine_sheet,
ws = "Maine",
input = maine,
anchor = "A1",
trim = TRUE,
col_names = TRUE)
Mapping Our Maine Favorites
We could use the same approach from the googlesheets
package to read our data into R to create a map, but I chose a simpler alternative - publishing the sheet to the web as a .csv and loading the data into R using the read_csv
function. The published .csv will update wheneve the googlesheet is changed so this approach should always use the most recent data. To publish a googlesheet to the web, click on the file menu and select publish to the web (note: this is different than publicly sharing your googlehseet.)
Then we can load our data just like any other .csv file.
# read data from googlesheet that is published to the web
maine <- read_csv("https://docs.google.com/spreadsheets/d/e/2PACX-1vRIi2n-sBlNHVgMTV3AwxvqeYq5yy-4pbv0rl9mi2xGTobwupj7AvfXaV13c9xqnRwuPWXxYFVqYh6B/pub?gid=0&single=true&output=csv")
Then we select a font awesome icon for each category of favorites included in the data. Because we use purrr
later to add the markers this is the only section of the code that would need to be updated if new categories are added to the data.
# make awesome icons
# use version 4 docs https://fontawesome.com/v4.7.0/
# if a new category is added this is the only code that needs to be updated
# adding the new marker to the icon list will automatically add it to the map
maine_icons <- awesomeIconList(
drink = makeAwesomeIcon(icon = "beer", library = "fa", markerColor = "beige"),
eat = makeAwesomeIcon(icon = "cutlery", library = "fa", markerColor = "lightred"),
camp = makeAwesomeIcon(icon = "fire", library = "fa", markerColor = "green"),
see = makeAwesomeIcon(icon = "eye", library = "fa", markerColor = "blue"),
learn = makeAwesomeIcon(icon = "graduation-cap", library = "fa", markerColor = "purple"),
shop = makeAwesomeIcon(icon = "shopping-cart", library = "fa", markerColor = "orange")
)
One more step before the heavy lifting, let’s add a popup variable that links to the webiste of each of the locations.
# make popup text with name, address, notes, and link to website
maine <-
maine %>%
mutate(popup = paste("<b><a href='", link,"'>", name,"</a></b>", "<br/>",
address, ', ', city, "<br/>", sep=''))
Now we’re ready to make our map. First, we set up our base map and store it in an object called m
.
m <-
leaflet() %>%
addTiles()
Then we add one layer to our map for each type of marker in maine_icons
, so as of now we are adding six layers (i.e., camp, drink, eat, learn, shop, and see). To do this without purrr
we would have to call addAwesomeMarkers
six times and would have to remember to add another call to addAwesomeMarkers
whenever we added a new type of marker to maine_icons
. With the walk
function from purrr
we can add all of these layers to m
in one go. It took me a little while to figure out how to use walk
to accomplish this, but I think the payoff going forward is clear as adding to the map will be much easier going forward.
# add one overlay layer for each marker in maine_icons using purrr
# use walk from per to return m after adding each layer
names(maine_icons) %>% # get names of markers in awesomeIconList
walk(function(x) # then walk through vector of names one at a time
m <<-
m %>% addAwesomeMarkers( # creating a new awesome marker layer
data = filter(maine, category == x), # for each category in the maine data
group = x,
icon = maine_icons[[x]],
popup = ~popup))
Finally, we add layer controls to m
and save our map.
# add layer controls to map
m <-
m %>%
addLayersControl(
overlayGroups = names(maine_icons),
options = layersControlOptions(collapsed = TRUE)
)
# save widget
saveWidget(m, "/rmarkdown-files/maine_map.html")
I hope you enjoy checking out a few of our favorite places in Maine and feel free to send suggestions my way.
The full geocoding and mapping R scripts are available in the github repo for my personal website.
My goal for next week is to set this up in a shiny app so that new locations added to the googlesheet are automatically geocoded without having to run the geocode_locations.R
script and to demonstrate how to deploy leaflet maps in shiny with a few enhanced user features.