This is the supplementary notes and examples for an introduction to leaflet.

It is based on data and examples seen in class

Built on the excellent tutorial from RStudio


If not already installed, install leaflet:

install.packages("leaflet")

Then attach the library:

library(leaflet)

Bring in the other needed libraries:

library(sf)
library(tidyverse)
library(USAboundaries)

Basic Usage

Creating a Leaflet map requires a few basic steps (not dissimilar to ggplot):

  1. Initialize a map widget by calling leaflet().

  2. Add layers (i.e., features) to the map by using layer functions (e.g. addTiles, addMarkers, addPolygons,…)

  3. Print the map widget to display it.


Here’s a basic example:

leaflet() %>%
  addTiles() %>%
  addMarkers(lng=-119.8489, lat=34.4140, popup="UCSB")

  • By default, leaflet sets the view of the map to the range of latitude/longitude data in the map layers

  • You can adjust these if needed using:

    • setView(): sets the center of the map view and the zoom level;

    • fitBounds(): fits the view into the rectangle [lng1, lat1] – [lng2, lat2];

    • clearBounds() clears the bound


Basemaps

Default (OpenStreetMap) Tiles

The easiest way to add tiles is by calling addTiles() with no arguments; by default, OpenStreetMap tiles are used.

(leaflet() %>% 
  setView(lng=-119.8489, lat=34.4140, zoom = 12) %>% 
  addTiles())

Third-Party Tiles

  • Many third-party basemaps can be added using the addProviderTiles() function

  • As a convenience, leaflet provides a named list of all the third-party tile providers supported by the plugin: just type providers$ and choose from one of the options.

length(providers)
#> [1] 164
names(providers) %>% 
  head()
#> [1] "OpenStreetMap"        "OpenStreetMap.Mapnik"
#> [3] "OpenStreetMap.DE"     "OpenStreetMap.CH"    
#> [5] "OpenStreetMap.France" "OpenStreetMap.HOT"

Note that some tile set providers require you to register. You can pass access tokens/keys, and other options, to the tile provider by populating the options argument with the providerTileOptions() function.

Below are a few examples:

CartoDB

leaflet() %>% 
  setView(lng=-119.8489, lat=34.4140, zoom = 12) %>% 
  addProviderTiles(providers$CartoDB)

Stamen.Toner

leaflet() %>% 
  setView(lng=-119.8489, lat=34.4140, zoom = 12) %>% 
  addProviderTiles(providers$Stamen.Toner)

ESRI Imagery

leaflet() %>% 
  setView(lng=-119.8489, lat=34.4140, zoom = 12) %>% 
  addProviderTiles(providers$Esri.WorldImagery)

Stamen.TopOSMRelief

leaflet() %>% 
  setView(lng=-119.8489, lat=34.4140, zoom = 12) %>% 
  addProviderTiles(providers$Stamen.TopOSMRelief)

Combining Tile Layers

You can stack multiple tile layers if the front tiles have some level of opacity. Here we layer the Stamen.TonerLines with aerial imagery

leaflet() %>% 
  setView(lng=-119.8489, lat=34.4140, zoom = 12) %>% 
  addProviderTiles(providers$Esri.WorldImagery) %>% 
   addProviderTiles(providers$Stamen.TonerLines, options = providerTileOptions(opacity = .5)) 

Markers

Markers are one way to identify point information on a map:


Example Starbucks data:

(starbucks = read_csv('data/directory.csv') %>% 
  filter(City %in% c("Goleta", "Santa Barbara")) %>% 
  st_as_sf(coords = c("Longitude", "Latitude"), crs = 4326) %>% 
  select(store_name = `Store Name`, phone = `Phone Number`, address = `Street Address`, city = City, brand = Brand))
#> Simple feature collection with 11 features and 5 fields
#> geometry type:  POINT
#> dimension:      XY
#> bbox:           xmin: -119.87 ymin: 34.4 xmax: -119.65 ymax: 34.44
#> geographic CRS: WGS 84
#> # A tibble: 11 x 6
#>    store_name  phone  address   city  brand                 geometry
#>    <chr>       <chr>  <chr>     <chr> <chr>              <POINT [°]>
#>  1 Hollister … 805-9… 7030 Mar… Gole… Star…          (-119.87 34.43)
#>  2 Goleta      805-6… 173 N. F… Gole… Star…          (-119.83 34.44)
#>  3 Montecito   (805)… 1046 - A… Sant… Star…          (-119.65 34.42)
#>  4 State & Co… 805-8… 539 Stat… Sant… Star…           (-119.7 34.42)
#>  5 Ralph's - … 805-5… 100 W Ca… Sant… Star…           (-119.7 34.42)
#>  6 800 State … (805)… 800 Stat… Sant… Star…           (-119.7 34.42)
#>  7 W Carrillo… 805-8… 208 West… Sant… Star…           (-119.7 34.42)
#>  8 La Cumbre … 805-5… 3815 Sta… Sant… Star…          (-119.75 34.44)
#>  9 Five Point… (805)… 3957 Sta… Sant… Star…          (-119.75 34.44)
#> 10 State & Vi… 805-8… 1235 Sta… Sant… Star…          (-119.71 34.42)
#> 11 1990 Cliff… 805-8… 1990 Cli… Sant… Star…           (-119.72 34.4)

Markers

Markers are added using the addMarkers or addAwesomeMarkers

Their default appearance is a blue dropped pin.

As with most layer functions, - the popup argument adds a message to be displayed on click - the label argument display a text label either on hover

leaflet() %>% 
  addProviderTiles(providers$CartoDB) %>% 
  addMarkers(data = starbucks, popup = ~store_name, label = ~city)

Awesome Icons

Using the Font Awesome Icons seen in lab one, we can make markers with more specific coloring and icons

  • Here we define the icon as a green marker with a coffee icon from the fa library
  • For fun we can make the coffee cups spin…

We then use addAwesomeMarkers to spcifiy the icon we created using the icon argument:

icons = awesomeIcons(icon = 'coffee', markerColor = "green", library = 'fa', spin = TRUE)

leaflet(data = starbucks) %>% addProviderTiles(providers$CartoDB) %>% 
  addAwesomeMarkers(icon = icons, popup = ~store_name)

Custom Popups

You can use HTML, CSS, and Java Script to modify your pop-ups

For example, we can associate the name of the Starbucks locations with their google maps URL as an hyper reference (href):

starbucks = starbucks %>% 
  mutate(url = paste0('https://www.google.com/maps/place/',
                      gsub(" ", "+", address), "+",
                      gsub(" ", "+", city)))

pop = paste0('<a href=', starbucks$url, '>', starbucks$store_name, "</a>")
head(pop)
#> [1] "<a href=https://www.google.com/maps/place/7030+Marketplace+Drive+Goleta>Hollister & Storke-Goleta</a>"               
#> [2] "<a href=https://www.google.com/maps/place/173+N.+Fairview+Avenue+Goleta>Goleta</a>"                                  
#> [3] "<a href=https://www.google.com/maps/place/1046+-+A+Coast+Village+Road,+Paseo+Nuevo+Shops+Santa+Barbara>Montecito</a>"
#> [4] "<a href=https://www.google.com/maps/place/539+State+Street+Santa+Barbara>State & Cota, Santa Barbara</a>"            
#> [5] "<a href=https://www.google.com/maps/place/100+W+Carrillo+St+Santa+Barbara>Ralph's - Santa Barbara #680</a>"          
#> [6] "<a href=https://www.google.com/maps/place/800+State+Street+Santa+Barbara>800 State Street</a>"

We can then add our custom popup to our icons:

leaflet(data = starbucks) %>%
  addProviderTiles(providers$CartoDB) %>% 
  addAwesomeMarkers(icon = icons, 
                    label = ~address, popup = pop)

Circle Markers

Circle markers are much like regular circles (shapes), except their radius in onscreen pixels stays constant regardless of zoom level (z).

leaflet(data = starbucks) %>%
  addProviderTiles(providers$CartoDB) %>% 
  addCircleMarkers(label = ~address, popup = pop)

Marker Clustering

Sometimes while mapping many points, it is useful to cluster them. For example lets plot all starbucks in the world!

all_sb = read_csv('data/directory.csv') %>% 
  filter(!is.na(Latitude)) %>% 
  st_as_sf(coords = c("Longitude", "Latitude"), crs = 4326) 

leaflet(data = all_sb) %>% 
  addProviderTiles(providers$CartoDB) %>% 
  addMarkers(clusterOptions = markerClusterOptions())

Adding color ramps

  • Colors can be add by factor, numeric, bins, or quartiles using the built in leaflet functions
  • Each of these are defined by a palette, and a domain

The palette argument can be any of the following:

  • A character vector of RGB or named colors.
    • Examples: palette(), c(“#000000”, “#0000FF”, “#FFFFFF”), topo.colors(10)
  • The name of an RColorBrewer palette
    • Examples: “BuPu” or “Greens”.
  • The full name of a viridis palette:
    • Examples: “viridis”, “magma”, “inferno”, or “plasma”.
  • A function that receives a single value between 0 and 1 and returns a color.
    • Examples: colorRamp(c(“#000000”, “#FFFFFF”), interpolate = “spline”).

The domain is the values - named by variable - that the color palette should range over

# ?colorFactor

# Create a palette that maps factor levels to colors
pal <- colorFactor(c("darkgreen", "navy"), domain = c("Goleta", "Santa Barbara"))

leaflet(data = starbucks) %>% addProviderTiles(providers$CartoDB) %>% 
  addCircleMarkers(color = ~pal(city), fillOpacity = .5, stroke = FALSE)

Shapes (Polylines, Polygons, Circles)

Getting some data …

(covid = readr::read_csv('https://raw.githubusercontent.com/nytimes/covid-19-data/master/live/us-states.csv') %>% 
  filter(date == max(date)) %>% 
  right_join(USAboundaries::us_states(), by = c("state" = "name")) %>% 
  filter(!stusps %in% c("AK","PR", "HI")) %>% 
  st_as_sf())
#> Simple feature collection with 49 features and 20 fields
#> geometry type:  MULTIPOLYGON
#> dimension:      XY
#> bbox:           xmin: -124.7258 ymin: 24.49813 xmax: -66.9499 ymax: 49.38436
#> geographic CRS: WGS 84
#> # A tibble: 49 x 21
#>    date       state              fips   cases deaths confirmed_cases
#>    <date>     <chr>              <chr>  <dbl>  <dbl>           <dbl>
#>  1 2021-03-17 Alabama            01    5.09e5  10337          396375
#>  2 2021-03-17 Arizona            04    8.34e5  16574              NA
#>  3 2021-03-17 Arkansas           05    3.27e5   5493          257449
#>  4 2021-03-17 California         06    3.63e6  56719         3606156
#>  5 2021-03-17 Colorado           08    4.47e5   6129              NA
#>  6 2021-03-17 Connecticut        09    2.94e5   7799              NA
#>  7 2021-03-17 Delaware           10    9.08e4   1514              NA
#>  8 2021-03-17 District of Colum… 11    4.27e4   1042           42730
#>  9 2021-03-17 Florida            12    1.98e6  32448               0
#> 10 2021-03-17 Georgia            13    1.01e6  17735          812675
#> # … with 39 more rows, and 15 more variables:
#> #   confirmed_deaths <dbl>, probable_cases <dbl>,
#> #   probable_deaths <dbl>, statefp <chr>, statens <chr>,
#> #   affgeoid <chr>, geoid <chr>, stusps <chr>, lsad <chr>,
#> #   aland <dbl>, awater <dbl>, state_name <chr>, state_abbr <chr>,
#> #   jurisdiction_type <chr>, geometry <MULTIPOLYGON [°]>

Polygons

Adding those cases counts to polygons over a YlOrRd color ramp

leaflet() %>% 
  addProviderTiles(providers$CartoDB) %>% 
  addPolygons(data = covid, 
              fillColor  = ~colorQuantile("YlOrRd", confirmed_cases)(confirmed_cases),
              color = NA,
              label = ~state_name)

Circles

leaflet() %>% 
  addProviderTiles(providers$CartoDB.DarkMatter) %>% 
  addCircles(data = st_centroid(covid), 
             fillColor  = ~colorQuantile("YlOrRd", cases)(cases),
             color = NA,
             fillOpacity = .5,
             radius = ~cases/3,
             label = ~cases)

Web based data

Adding “Web data” to the map

# site ID
id = "11120000"

# base URL
base ="https://labs.waterdata.usgs.gov/api/nldi/linked-data/"

# Reading sf for URLs in line
leaflet() %>% 
  addProviderTiles(providers$CartoDB) %>% 
  addPolylines(data = read_sf(paste0(base,'nwissite/USGS-',id,'/navigate/UT'))) %>% 
  addPolygons(data =  read_sf(paste0(base,'nwissite/USGS-',id,'/basin')), 
              fillColor =  "transparent", color = "black")

WMS Tiles

WMS tiles can be added directly to a map. Here we use the NEXRAD rainfall information (refelctivity) from the Iowa Mesonet Program

(You may need to scroll out to find an)

conus = filter(us_states(), !stusps %in% c("AK", "PR", "HI"))

leaflet() %>% 
  addProviderTiles(providers$CartoDB) %>% 
  addPolygons(data = st_union(conus), fillColor = "transparent",
              color = "black", weight = 1) %>% 
  addWMSTiles(
    "http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi",
    layers = "nexrad-n0r-900913",
    options = WMSTileOptions(format = "image/png", transparent = TRUE)
  )

Layer Controls

Uses Leaflet’s built-in layers control you can choose one of several base layers, and any number of overlay layers to view.

By defining groups, you have the ability to toogle layers, and overlays on/off.

leaflet() %>% 
  addProviderTiles(providers$CartoDB, group = "Grayscale") %>% 
  addProviderTiles(providers$Esri.WorldTerrain, group = "Terrain") %>% 
  addPolylines(data = read_sf(paste0(base,'nwissite/USGS-',id,'/navigate/UT'))) %>% 
  addPolygons(data =  read_sf(paste0(base,'nwissite/USGS-',id,'/basin')), fillColor =  "transparent", color = "black", group = "basin") %>% 
  addWMSTiles("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", layers = "nexrad-n0r-900913",
    options = WMSTileOptions(format = "image/png", transparent = TRUE)) %>% 
  addLayersControl(overlayGroups = c("basin"), baseGroups = c("Terrain", "Grayscale"))

‘Function-ize’

You can wrap your mapping code in functions to allow reusability

watershed_map = function(gage_id){
leaflet() %>% 
  addProviderTiles(providers$CartoDB) %>% 
  addPolylines(data = read_sf(paste0(base,'nwissite/USGS-',gage_id,'/navigate/UT'))) %>% 
  addPolygons(data =  read_sf(paste0(base,'nwissite/USGS-',gage_id,'/basin')), 
              fillColor =  "transparent", color = "black", group = "basin") %>% 
  addWMSTiles("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", layers = "nexrad-n0r-900913",
    options = WMSTileOptions(format = "image/png", transparent = TRUE))
}
watershed_map("07382000")

Adding Details

  • Measures, graticules, and inset maps
watershed_map("07382000") %>% 
  addMeasure() %>% 
  addGraticule() %>% 
  addMiniMap()

leafem (new library)

  • Home buttons and Mouse Coordinates
  • Support for raster and stars objects (to come)
watershed_map("07382000") %>% 
  addMeasure() %>% 
  addGraticule() %>% 
  leafem::addHomeButton(group = "basin") %>% 
  leafem::addMouseCoordinates() 

leafpop (new library)

  • Popup Tables
leaflet(data = starbucks) %>% 
  addProviderTiles(providers$CartoDB) %>% 
  addCircleMarkers(
    color = ~pal(city), 
    fillOpacity = .5,
    stroke = FALSE, 
    popup = leafpop::popupTable(starbucks)
  )

Making that table nicer…

leaflet(data = starbucks) %>% addProviderTiles(providers$CartoDB) %>% 
  addCircleMarkers(
    color = ~pal(city), fillOpacity = .5,
    stroke = FALSE, 
    popup = leafpop::popupTable(st_drop_geometry(starbucks[,1:5]), feature.id = FALSE, row.numbers = FALSE)
  )

Mapview

  • easy, but less control
  • Support for raster and stars objects (to come in our class)
  • Implements many of the leafem and leafpop functionalities
library(mapview)
mapview(starbucks)