19 Leaflet Maps

library(tidyverse)
library(leaflet)
library(sf)

Another interesting approach to make maps is based on the R package "leaflet".

The code in this chapter requires the following packages:

library(tidyverse)      # for syntactic manipulation of tables
library(leaflet)        # for drawing basic geographical maps
library(sf)             # provides classes and functions for vector data

and the following table for storms in 1975:

storms75 <- filter(storms, year == 1975)

19.1 Introduction

Leaflet is one of the most popular open-source JavaScript libraries for web-based interactive maps. The good news for R users is that we also have the homonym R package "leaflet" that allows us to create maps directly from the R console, from RStudio, in Shiny applications, and in source dynamic documents that produce HTML output (e.g. Rmd, qmd files).

Extensive documentation for "leaflet" can be found in RStudio’s Leaflet for R: https://rstudio.github.io/leaflet/basemaps.html

19.1.1 Basic World Map

In analogous way to what we’ve done in the last two chapters, let’s start with a basic map of the world. With "leaflet" this is straightforward and it is actually the default map produced by the main function leaflet() with the layer addTiles()

leaflet() |>
  addTiles()

As you can tell, the way you create a Leaflet map requires 2 basic steps:

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

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

The displayed map is an HTML (web-based_ interactive map. Notice that, by default, addTiles() uses a map from OpenStreetMap which is is a free, open geographic database that provides geospatial data for anybody to use and share.

Zooming

We can zoom in to a certain location of the World by adding another layer function: setView() with arguments lng and lat for the desired location, and a zoom value. For example, we can get a map centered in Miami, Florida:

# view centered at Miami, FL
# value "zoom = 3" found by trail and error
leaflet() |>
  addTiles() |>
  setView(lng = -80.19, lat = 25.76, zoom = 3)


Since we are using Miami, FL as the point of reference to center our map, we can also use an addMarkers() layer with the same set of coordinates to include a map marker that you can click on and see the text displayed in the argument popup:

# view centered at Miami, FL
leaflet() |>
  addTiles() |>
  setView(lng = -80.19, lat = 25.76, zoom = 3) |>
  addMarkers(lng = -80.19, lat = 25.76, popup="Miami")


If you prefer, you can set the view to a different set of coordinates so that the map is somewhat centered in the middle of the North Atlantic ocean. In addition, you can use addProviderTiles() to change the tile from other map providers. Here’s an example with provider "CartoDB"

# zoom-in in the middle of North Atlantic ocean
leaflet() |>
  setView(lng = -50, lat = 30, zoom = 3) |>
  addTiles() |>
  addProviderTiles(provider = "CartoDB")


And there’s another example with one of my favorite tile providers, "NASAGIBS.ViirsEarthAtNight2012", which gives you a night view of the Earth:

# zoom-in in the middle of North Atlantic ocean
leaflet() |>
  setView(lng = -50, lat = 30, zoom = 3) |>
  addTiles() |>
  addProviderTiles(provider = "NASAGIBS.ViirsEarthAtNight2012")


To summarize, the creation of Leaflet maps involve the following steps:

  1. start with leaflet().

  2. Add layers with functions such as addTiles(), addMarkers(), addPolygons(), etc to modify the map widget.

  3. Repeat step 2 as desired.

  4. Print the map widget to display it.

19.2 Plotting Storms

How do we add the position of storms? By forming a pipeline where we pipe storms to leaflet(), and adding circle markers with addCircleMarkers() (the equivalent of geom_point() in "ggplot2")

Consider all tropical cyclones back in 1975

First map centered in Miami, Florida

# all tropycal cyclones in 1975
storms75 |>
  leaflet() |>
  setView(lng = -50, lat = 30, zoom = 2) |>
  addProviderTiles(provider = 'CartoDB') |>
  addCircleMarkers(
    lng = ~long, 
    lat = ~lat,
    radius = 1)

What if we just want to plot the hurricanes? First we get their names, stored in a character vector:

# vector of hurricane names
hurr_names75 <- storms75 |>
  filter(wind >= 64) |>
  distinct(name) |>
  pull(name)

hurr_names75
## [1] "Blanche"  "Caroline" "Doris"    "Eloise"   "Faye"     "Gladys"

We can then filter hurricanes:

hurricanes75 <- storms75 |>
  filter(name %in% hurr_names75)

And then we pass them to leaflet()

# all hurricanes in 1975
hurricanes75 |>
  leaflet() |>
  setView(lng = -50, lat = 30, zoom = 2) |>
  addProviderTiles(provider = 'CartoDB') |>
  addCircleMarkers(
    lng = ~long, 
    lat = ~lat,
    radius = 1)


19.2.1 Customizing color palette

As we did with ggplot(), we can also color the storms in order to distinguish them:

# If you want to set your own colors manually:
count_storms75 = storms75 |>
  distinct(name) |>
  nrow()

pal <- colorFactor(
  palette = rainbow(n = count_storms75),
  domain = storms75$name
)
storms75 |>
  leaflet() |>
  addProviderTiles('CartoDB') |>
  setView(lng = -80.19, lat = 25.76, zoom = 3) |>
  addCircleMarkers(
    lng = ~long, 
    lat = ~lat,
    radius = 2, 
    color = ~pal(name))


19.2.2 Adding a Legend

colores <- rainbow(n = count_storms75)


storms75 |>
  leaflet() |>
  addProviderTiles('CartoDB') |>
  setView(lng = -80.19, lat = 25.76, zoom = 3) |>
  addCircleMarkers(
    lng = ~long, 
    lat = ~lat,
    radius = 2, 
    color = ~pal(name)) |>
  addLegend(
    position = "bottomleft",
    color = ~colores,
    labels = ~unique(storms75$name),
    title = "Storms in 1975",
    opacity = 1,
    group = "circles"
  )