In the last section we made interactive line charts. This section will focus on building interactive maps. To play with the new material these things, open a test script. At the top of it attach the source your helpers file and load your data as covered in the last section:

source('./helpers.R')
covid19 = read_covid19()
today   = today_centroids(counties, covid19)

Leaflet is one of the most popular open-source JavaScript libraries for interactive maps. It’s used by websites like The New York Times, The Washington Post, GitHub and Flickr, as well as GIS systems like OpenStreetMap, Mapbox, and CartoDB. The leaflet R package makes it easy to integrate and control Leaflet maps in R. An exhaustive list of functionality and examples can be found here.

Our goal for this section is to make a function, called basemap that will plot our county centroids (today) - with sizes based on case count - on a tiled slippy map.

We will create a second function - zoom_to_county - that zooms to a specific county based on a FIP code. The following code will get you a basic example that you can customize and tweak.

Web Tiles

Lets start by initializing a basic leaflet map. We start by giving our today data as the data input to the the generic leaflet call. We pass that map object to the addProviderTiles call. In this example we add the CartoDB.DarkMatter tileset but you can pick any tile set by name from the extensive leaflet provides found here. Finally we pass the map object with defined tiles to the addScaleBar function and ask for a scale bar in the bottom left of the map. The results look like:

leaflet(data = today) %>%
# Add Tile sets from linked providers:
  addProviderTiles('CartoDB.DarkMatter') %>%
# Add a scale bar to a define position
  addScaleBar("bottomleft")

Note that our objects are not mapped! This is because we have not told leaflet how to represent spatial input yet. To do so we will pass our map object to addCircleMarkers. The effect will the that circleMarkers will be added to - or layered on - the existing map:

leaflet(data = today) %>%
  addProviderTiles(providers$CartoDB.DarkMatter) %>%
  addScaleBar("bottomleft") %>%
  addCircleMarkers()     

Great! They are now mapped but are pretty ugly. Lets make our map more pleasing by defining a color pallete, picking a better basemap, and adding aesthetic parameters. You can try any of the following pallete names here or one of: “viridis”, “magma”, “inferno”, or “plasma”.

# define a unique color pallete using the name you select
pal <- colorNumeric("inferno", domain = today$size, n = 100)
  
map = leaflet(data = today) %>%
  addProviderTiles('CartoDB.Positron') %>%
  addScaleBar("bottomleft") %>%
  addCircleMarkers(
    fillColor = ~pal(size), # the cicle inside color
    color = 'transparent',  # the circle border color
    fillOpacity = 0.5,      # The opacity of circle inside color
    radius = ~size*2,       # the circle size
    layerId = ~fips,        # the circle ID (needed for Shiny!)
    label   = ~name         # how should the markers be labeled?
  )

map

You job is to make this map your own by modifying the basemap, color pallete, and design parameters in addCircleMarkers. The only thing that CANNOT be modified is the layerID. You will see why latter on.

Map Modifiers

Here we are going to get a bit complicated 😄 but again if you are not feeling it, just focus on exposing yourself to the material and absorbing as much as you personally want to.

What we want to do it define a function that will “zoom” to a county county based on a FIP code. In other words, if a user asks about LA county, we want to zoom to LA county and show the county boundaries.

To do this we will make a new function that requires a leaflet map, county boundaries, and a FIP code we will call this function zoom_to_county

zoom_to_county = function(map, counties, FIP){
  # Filter the counties to the input FIP code
  shp = filter(counties, fips == FIP) 
  # Build a buffered bounding box to center the map on:
  bounds = shp %>% 
    # make bounding box
    st_bbox() %>% 
    # Make spatial
    st_as_sfc() %>% 
    # Buffer to .1 degree
    st_buffer(.1) %>% 
    # make new bounding box
    st_bbox() %>% 
    # extract coordinates as vector
    as.vector()
  # Clear all current shapes (remember county centroids are currently markers!)
    clearShapes(map) %>% 
  # Add the county shape making the outline color red and the fill an opaque white
    addPolygons(data = shp,
                color = "red",
                fillColor  = "white",
                fillOpacity = .2) %>% 
  # Fly the leaflet map to the buffered boundary
    flyToBounds(bounds[1], bounds[2], bounds[3], bounds[4])
}

Go ahead a copy the above code. Don’t worry about changing any of it except for the way you want your Polygon to show up on the map. These are the parameters in the addPolygon method (e.g. should the color be red and the fillColor white??)

Once completed, run the function to save it and then, you can pass your map object to this new function to zoom your map!

## zoom to LA
zoom_to_county(map, counties, 6037)

Lets add these new functions to our helpers.R. They might look something like this but should make use of your personal touches and aesthetics! Note that we added a legend that you can take, ignore, or modify!

basemap = function(today){
  pal  <- colorNumeric("inferno", domain = today$size, n = 50)  # markers
  pal2 <- colorNumeric("inferno", domain = today$cases, n = 50) # legend 
  
  leaflet(data = today) %>%
    addProviderTiles('CartoDB.Positron') %>%
    addScaleBar("bottomleft") %>%
    addCircleMarkers(
      fillColor   = ~pal(size), 
      color       = 'transparent', 
      fillOpacity = 0.5,
      radius      = ~size*2,
      layerId     = ~fips,
      label       = ~name ) %>% 
      addLegend("bottomright", pal = pal2, values = ~cases,
    title = paste("COVID Cases\n", max(today$date)),
    opacity = 1)
}

zoom_to_county = function(map, counties, FIP){
  
  shp = filter(counties, fips == FIP) 
  bounds = shp %>% 
    st_bbox() %>% 
    st_as_sfc() %>% 
    st_buffer(.1) %>% 
    st_bbox() %>% 
    as.vector()
  
  clearShapes(map) %>% 
    addPolygons(data = shp,
                color = "red", 
                fillColor  = "white", 
                fillOpacity = .2) %>% 
    flyToBounds(bounds[1], bounds[2], bounds[3], bounds[4])
}

Testing

basemap(today)

Conclusion

Fantastic, now we have a function for making a map and zooming to counties. Lets move to the next section where we will make interactive tables of web-scraped Wikipedia data using the DT package. Great job making it to here!!