28  Mapping II

Many online services generate tile maps. Generally a small number of maps are available for free and then further maps require a subscription. For causal use the free service tier is almost always sufficient. To keep track of your usage you sometimes need to sign up for an API key. The first example below is unusual because you can use these maps without any account. Tools and services for this sort of mapping change frequently, so this lesson shows how to use two different services.

28.1 Tiled maps with leaflet

In the previous lesson we made maps to show geographical data using map outlines from R packages. In this lesson we will look at making maps that build on detailed basemaps from online mapping systems. Google maps is a well known service; OpenStreetMap is a community-built free alternative. We will use the leaflet package.

Let’s make a map and place a marker at the location of the Mathematics and Statistics Department. You can pan and zoom this map from within Rstudio and in the knitted output.

m <- leaflet() |>
  addTiles() |>  # Add default OpenStreetMap map tiles
  addMarkers(lng=-63.5932, lat=44.63697, popup="Math & Stats, Dalhousie U")
m  # Print the map

If you want to connect a name to a location, you can use the getbb function from osmdata (OpenStreetMap data). This gives the bounding box (upper left and lower right latitude and longitude coordinates):

getbb('halifax')
        min       max
x -63.72266 -63.54275
y  44.58117  44.71121

Let’s use the city database we used in the MDS lesson to label the 61 cities we selected in that lesson.

cities <- read_csv("static/selected_cities.csv", show_col_types = FALSE)
m2 <- leaflet(data = cities) |>
  addTiles() |>
  addCircleMarkers(~ lng, ~lat, label = ~city)
m2

There are several different basemaps, or tiles, that you can use. A fun one to look at – because it changes frequently is a radar map for the USA from Iowa State.

m3 <- leaflet(data = cities) |>
  addTiles() |>
  addCircleMarkers(~ lng, ~lat, label = ~city) %>%
  addWMSTiles("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi",
    layers = "nexrad-n0r-900913",
    options = WMSTileOptions(format = "image/png", transparent = TRUE),
    attribution = "Weather data © 2012 IEM Nexrad") |>
  setView(-93.65, 42.0285, zoom = 4)
m3

It’s common to see “chloropleth” maps of the USA in which geographic regions are coloured to show a quantitative or categorical variable, such as population density or election results. These maps are less common in Canada due to two main factors: the geographic concentration of the population in cities means that area maps are even less informative than for the USA and the latitude range of Canada makes for some severe challenges when plotting whole-country chloropleth maps on commonly used projections. Nevertheless, it is possible. Here’s a demonstration with some random numbers for “data”.

library(geojsonio)
pal <- colorNumeric("viridis", NULL)  # make a viridis palette
canada <- geojson_read("https://gist.githubusercontent.com/mikelotis/2156d7c170d10d2c77cb79424fe2137d/raw/7a13748ed7ea5ba64876c77c53b6cb64dd5c3ab0/canada-province.geojson", what="sp")
my_values = runif(13, 0, 10) # random numbers between 0 and 10
leaflet() |>
  addTiles() |>
  addPolygons(data = canada, fillColor = ~ pal(my_values)) |>
  addLegend(pal = pal, values = my_values, opacity = 1.0)

28.2 Tiled map with ggmap

In this section we will use tile maps from Google. To use google maps, you need to have an API key (which may cost money). We will use the ggmap package to download and draw the map.

There are two steps. First download the tiles needed for your map by specifying

  • the bounding box in degrees latitude and longitude, and
  • the zoom level of the map.

Always start with a small zoom level – if you make it too big you will download a lot of unnecessary data, which takes time and imposes a burden on the service.

mymap_terrain <- get_googlemap(bbox = c(left = -130, bottom = 41, right = -50, top = 60),
                                zoom=4, maptype = "terrain")
ggmap(mymap_terrain)  

This is a regular ggplot, so you can add lines and points to a map easily, using latitude and longitude as coordinates. Placing text on a map in a readable and attractive way can be quite a challenge.

my_points <- tibble(lat = c(43+57/60, 49+53/60),
                    lon = c(-59-55/60, -97-9/60),
                    label = c("Sable Is.", "Winnipeg")
)
ggmap(mymap_terrain)  +
   geom_point(data =my_points, color="brown") + 
   geom_text(data = my_points, aes(label=label))

You can also zoom in on urban areas.

mymap_halifax <- get_googlemap(bbox = c(left = -63.6-0.1, bottom = 44.60, right = -63.6+0.1, top = 44.75), 
                              zoom=12, maptype = "toner-lite")
ggmap(mymap_halifax)  

28.3 Packages used

In addition to tidyverse, I’ve used the following packages

  • leaflet for making interactive (zoomable, draggable) maps
  • ggmap
  • osmdata for geolocation function getbb
  • geojsonio

28.4 Further reading

  • Healy’s chapter 7 on mapping
  • Wilke’s chapter 15 on geospatial data
  • An article describing how ggmap works
  • A ggmap tutorial, showing how to use Google map services including geocoding (turning an address into a location and the reverse) and routing.
  • Another well-developed approach to mapping: mapview