8.4 Interactive maps

While static and animated maps can enliven geographic datasets, interactive maps can take them to a new level.Interactivity can take many forms, the most common and useful of which is the ability to pan around and zoom into any part of a geographic dataset overlaid on a ‘web map’ to show context.Less advanced interactivity levels include popups which appear when you click on different features, a kind of interactive label.More advanced levels of interactivity include the ability to tilt and rotate maps, as demonstrated in the mapdeck example below, and the provision of “dynamically linked” sub-plots which automatically update when the user pans and zooms (Pezanowski et al. 2018).

The most important type of interactivity, however, is the display of geographic data on interactive or ‘slippy’ web maps.The release of the leaflet package in 2015 revolutionized interactive web map creation from within R and a number of packages have built on these foundations adding new features (e.g., leaflet.extras) and making the creation of web maps as simple as creating static maps (e.g., mapview and tmap).This section illustrates each approach in the opposite order.We will explore how to make slippy maps with tmap (the syntax of which we have already learned), mapview and finally leaflet (which provides low-level control over interactive maps).

A unique feature of tmap mentioned in Section 8.2 is its ability to create static and interactive maps using the same code.Maps can be viewed interactively at any point by switching to view mode, using the command tmap_mode("view").This is demonstrated in the code below, which creates an interactive map of New Zealand based on the tmap object map_nz, created in Section 8.2.2, and illustrated in Figure 8.18:

  1. tmap_mode("view")
  2. map_nz

Figure 8.18: Interactive map of New Zealand created with tmap in view mode. Interactive version available online at: geocompr.robinlovelace.net.

Now that the interactive mode has been ‘turned on’, all maps produced with tmap will launch (another way to create interactive maps is with the tmap_leaflet function).Notable features of this interactive mode include the ability to specify the basemap with tm_basemap() (or tmap_options()) as demonstrated below (result not shown):

  1. map_nz + tm_basemap(server = "OpenTopoMap")

An impressive and little-known feature of tmap’s view mode is that it also works with faceted plots.The argument sync in tm_facets() can be used in this case to produce multiple maps with synchronized zoom and pan settings, as illustrated in Figure 8.19, which was produced by the following code:

  1. world_coffee = left_join(world, coffee_data, by = "name_long")
  2. facets = c("coffee_production_2016", "coffee_production_2017")
  3. tm_shape(world_coffee) + tm_polygons(facets) +
  4. tm_facets(nrow = 1, sync = TRUE)

Faceted interactive maps of global coffee production in 2016 and 2017 in sync, demonstrating tmap's view mode in action.
Figure 8.19: Faceted interactive maps of global coffee production in 2016 and 2017 in sync, demonstrating tmap’s view mode in action.

Switch tmap back to plotting mode with the same function:

  1. tmap_mode("plot")
  2. #> tmap mode set to plotting

If you are not proficient with tmap, the quickest way to create interactive maps may be with mapview.The following ‘one liner’ is a reliable way to interactively explore a wide range of geographic data formats:

  1. mapview::mapview(nz)

Illustration of mapview in action.
Figure 8.20: Illustration of mapview in action.

mapview has a concise syntax yet is powerful. By default, it provides some standard GIS functionality such as mouse position information, attribute queries (via pop-ups), scale bar, and zoom-to-layer buttons.It offers advanced controls including the ability to ‘burst’ datasets into multiple layers and the addition of multiple layers with + followed by the name of a geographic object.Additionally, it provides automatic coloring of attributes (via argument zcol).In essence, it can be considered a data-driven leaflet API (see below for more information about leaflet).Given that mapview always expects a spatial object (sf, Spatial, Raster) as its first argument, it works well at the end of piped expressions.Consider the following example where sf is used to intersect lines and polygons and then is visualised with mapview (Figure 8.21).

  1. trails %>%
  2. st_transform(st_crs(franconia)) %>%
  3. st_intersection(franconia[franconia$district == "Oberfranken", ]) %>%
  4. st_collection_extract("LINE") %>%
  5. mapview(color = "red", lwd = 3, layer.name = "trails") +
  6. mapview(franconia, zcol = "district", burst = TRUE) +
  7. breweries

Using mapview at the end of a sf-based pipe expression.
Figure 8.21: Using mapview at the end of a sf-based pipe expression.

One important thing to keep in mind is that mapview layers are added via the + operator (similar to ggplot2 or tmap). This is a frequent gotcha) in piped workflows where the main binding operator is %>%.For further information on mapview, see the package’s website at: r-spatial.github.io/mapview/.

There are other ways to create interactive maps with R.The googleway package, for example, provides an interactive mapping interface that is flexible and extensible(see the googleway-vignette for details).Another approach by the same author is mapdeck, which provides access to Uber’s Deck.gl framework.Its use of WebGL enables it to interactively visualize large datasets (up to millions of points).The package uses Mapbox access tokens, which you must register for before using the package.

Note that the following block assumes the access token is stored in your R environment as MAPBOX=your_unique_key.This can be added with edit_r_environ() from the usethis package.

A unique feature of mapdeck is its provision of interactive ‘2.5d’ perspectives, illustrated in Figure 8.22.This means you can can pan, zoom and rotate around the maps, and view the data ‘extruded’ from the map.Figure 8.22, generated by the following code chunk, visualizes road traffic crashes in the UK, with bar height respresenting casualties per area.

  1. library(mapdeck)
  2. set_token(Sys.getenv("MAPBOX"))
  3. df = read.csv("https://git.io/geocompr-mapdeck")
  4. ms = mapdeck_style("dark")
  5. mapdeck(style = ms, pitch = 45, location = c(0, 52), zoom = 4) %>%
  6. add_grid(data = df, lat = "lat", lon = "lng", cell_size = 1000,
  7. elevation_scale = 50, layer_id = "grid_layer",
  8. colour_range = viridisLite::plasma(5))

Map generated by mapdeck, representing road traffic casualties across the UK. Height of 1 km cells represents number of crashes.
Figure 8.22: Map generated by mapdeck, representing road traffic casualties across the UK. Height of 1 km cells represents number of crashes.

In the browser you can zoom and drag, in addition to rotating and tilting the map when pressing Cmd/Ctrl.Multiple layers can be added with the %>% operator, as demonstrated in the mapdeck vignette.

Mapdeck also supports sf objects, as can be seen by replacing the add_grid() function call in the preceding code chunk with add_polygon(data = lnd, layer_id = "polygon_layer"), to add polygons representing London to an interactive tilted map.

Last but not least is leaflet which is the most mature and widely used interactive mapping package in R.leaflet provides a relatively low-level interface to the Leaflet JavaScript library and many of its arguments can be understood by reading the documentation of the original JavaScript library (see leafletjs.com).

Leaflet maps are created with leaflet(), the result of which is a leaflet map object which can be piped to other leaflet functions.This allows multiple map layers and control settings to be added interactively, as demonstrated in the code below which generates Figure 8.23 (see rstudio.github.io/leaflet/ for details).

  1. pal = colorNumeric("RdYlBu", domain = cycle_hire$nbikes)
  2. leaflet(data = cycle_hire) %>%
  3. addProviderTiles(providers$Stamen.TonerLite) %>%
  4. addCircles(col = ~pal(nbikes), opacity = 0.9) %>%
  5. addPolygons(data = lnd, fill = FALSE) %>%
  6. addLegend(pal = pal, values = ~nbikes) %>%
  7. setView(lng = -0.1, 51.5, zoom = 12) %>%
  8. addMiniMap()

Figure 8.23: The leaflet package in action, showing cycle hire points in London.