It’s a good feeling when a great friend who is smarter than you offers to write a blog post, for your blog, that’s better than anything you’ve written so far. Friends, colleagues, people who’ve not yet realized they are at the wrong site: please allow me to introduce to you the awe-inspiring Dr. Keegan Hines. He got his PhD in neuroscience from the University of Texas at Austin in 2014 and is now a data scientist doing some super-secret-James-Bond-machine-learning work for a DoD contractor near D.C. When he is not breathing life into spatially-centered instructional R blogs, he is part of an improv comedy troop, does some consulting work, and serves a mean campfire omelet. Without further adieu…
Microbreweries and Interactive Maps With Leaflet
As a guiding example, lets return to a previous blog post where Paul visualized the locations of microbreweries in Virginia . In that post, Paul introduced Plotly, a super cool company that allows you to create and deploy interactive graphics on their web-based service. Here, we’re going to do this all ourselves, with help from a new R package called leaflet. So let’s jump right in.
Here’s some boiler-plate stuff. We need to install the package manually from github and then load it.
devtools::install_github("rstudio/leaflet") library(leaflet) library(ggmap)
So first thing, let’s grab some location that we might want to put on a map. I’ll use a function from the ggmap package.
somePlace <-ggmap::geocode("Washington,DC") somePlace
So we have a dataframe (with one row) and lat/lon coordinates for some arbitrary point in Washington, DC. We’re going to use functions from the leaflet package to generate a map around this point.
leaflet(somePlace) %<% addTiles() %>% addMarkers()
Now we have this draggable, zoomable, interactive map with a single line of R!
A little explanation of what we just did. In case it’s unfamiliar, I’ll first point out that we’re using the forward pipe %>% thing. The forward pipe was introduced in the magrittr package and has now been adopted in lots of places. The idea is that we can pass the output of a function as the input to the next function. This allows us to write code that reads left to right and is more aligned with our logic. For example:
# this is nested and confusing, we have to read it inside-out sqrt(sum(c(1,2,3))) # this is sequential and awesome, it reads left to right c(1,2,3) %>% sum() %>% sqrt()
So back to leaflet. The first funciton we use is called leaflet() and this returns a base leaflet object, sort of the starting point for everything we might do. We passed our data frame as an argument to leaflet(), and so any later functions that might require data will look to this data frame.
We then sent the output of leaflet() to another function, addTiles(). This is because the output of leaflet() doesn’t have enough visual information to actually create a map – we haven’t provided enough detail yet about what we want. The function addTiles() updates the leaflet object by providing the visual look and feel through different “tiles”. In fact, there’s many different styles of map we could make, just by choosing different tiles. Here’s some examples:
leaflet(somePlace) %>% addProviderTiles("Stamen.Watercolor") %>% addMarkers()
leaflet(somePlace) %>% addProviderTiles("Stamen.Toner") %>% addMarkers()
The full list of available tiles is here.
And so the third function in this simple example is addMarkers(). This function’s purpose is pretty obvious and results in the big blue marker thing on the map. What it does is look through the provided data frame for any columns that are similar to “lat” or “lon” and then plots them. And it’ll do so for every row in the data frame, so it’s effortless to put lots of points on a map, as we’ll see below. There are also a few other other functions that are similar and plot slightly different things. You might be able to guess what addCircles() or addPolyLines() are capable of, but as an example:
leaflet(somePlace) %>% addProviderTiles("Stamen.Toner") %>% addCircles(radius=400,color='firebrick')
So let’s move on to our more interesting example – the breweries. I’ve scraped a list of microbreweries in Virginia and gotten their names, websites, addresses and so. Since I want lat/lon info as well, I’ve used ggmap::geocode() to estimate those as well. The result is a dataframe called ‘breweries’ that has 106 rows and looks like this:
> names(breweries)  "Name" "Address" "Phone" "Website" "lat" "lng" > head(breweries[,c(1,4:6)]) Name Website lat lng 1 Wolf Hills Brewing Co www.wolfhillsbrewing.com 36.71231 -81.96560 2 Blue Mountain Brewery www.bluemountainbrewery.com 37.96898 -78.83499 3 Quattro Goomba Brewery www.goombabrewery.com 38.98597 -77.61748 4 Hops Grill & Brewery www.hopsonline.com 38.83758 -77.05116 5 Port City Brewing Co www.portcitybrewing.com 38.80800 -77.10137 6 Loose Shoe Brewing Co www.looseshoebrewing.com 37.56500 -79.06352
So let’s put em on a map.
leaflet(breweries) %>% addTiles() %>% addMarkers()
Pretty effortless I’d say! This is great except we don’t know which brewery is which, it’s just anonymous points on a map. We could try to add some text to a map, but remember our goal is to utilize web-based interactivity. So we’re going to take advantage of a click-based popup by inserting the Name column of the data frame.
leaflet(breweries) %>% addTiles() %>% addMarkers(popup=breweries$Name)
popup_style<-paste0("",breweries$Name,"") leaflet(breweries) %>% addTiles() %>% addMarkers(popup=breweries$Name)
Now we can easily zoom around and explore Virginia’s thriving craft-brew industry! You have to admit that’s a pretty awesome thing we were able to create with just a couple lines of R. And the interactivity allows us to encode a lot information (locations, names, and website of all the breweries) in a simple exerience that any viewer can explore at their own pace. As you might guess, this is just the beginning of what we can do with leaflet, and there’s a great guide at RStudio’s site.