introduction

In this lab, we’ll explore the basics of map-making in R using the tmap package. The following materials are modified from Chapter 9 of Geocomputation with R by Rovin Lovelace

prerequisites

prerequisites

Let’s load all necessary packages.

#install.packages("sf")
#install.packages("terra")
#install.packages("spData")
#install.packages("spDataLarge", repos = "https://geocompr.r-universe.dev")
#remotes::install_github("r-tmap/tmap@v4")

library(sf)
library(terra)
library(dplyr)
library(spData)
library(spDataLarge)
library(tmap)    # for static and interactive maps

Let’s also read in some data from the spDataLarge package to work with later.

nz_elev = rast(system.file("raster/nz_elev.tif", package = "spDataLarge"))

map-making basics

Let’s start with a pre-loaded spatial object representing the states of New Zealand

nz
## Simple feature collection with 16 features and 6 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 1090144 ymin: 4748537 xmax: 2089533 ymax: 6191874
## Projected CRS: NZGD2000 / New Zealand Transverse Mercator 2000
## First 10 features:
##                 Name Island Land_area Population Median_income Sex_ratio
## 1          Northland  North 12500.561     175500         23400 0.9424532
## 2           Auckland  North  4941.573    1657200         29600 0.9442858
## 3            Waikato  North 23900.036     460100         27900 0.9520500
## 4      Bay of Plenty  North 12071.145     299900         26200 0.9280391
## 5           Gisborne  North  8385.827      48500         24400 0.9349734
## 6        Hawke's Bay  North 14137.524     164000         26100 0.9238375
## 7           Taranaki  North  7254.480     118000         29100 0.9569363
## 8  Manawatu-Wanganui  North 22220.608     234500         25000 0.9387734
## 9         Wellington  North  8048.553     513900         32700 0.9335524
## 10        West Coast  South 23245.456      32400         26900 1.0139072
##                              geom
## 1  MULTIPOLYGON (((1745493 600...
## 2  MULTIPOLYGON (((1803822 590...
## 3  MULTIPOLYGON (((1860345 585...
## 4  MULTIPOLYGON (((2049387 583...
## 5  MULTIPOLYGON (((2024489 567...
## 6  MULTIPOLYGON (((2024489 567...
## 7  MULTIPOLYGON (((1740438 571...
## 8  MULTIPOLYGON (((1866732 566...
## 9  MULTIPOLYGON (((1881590 548...
## 10 MULTIPOLYGON (((1557042 531...

the first element is always “tm_shape”

tm_shape(nz) +
  tm_fill()

now let’s plot just the boundaries

tm_shape(nz) +
  tm_borders()

and the shapes and boundaries together

tm_shape(nz) +
  tm_fill() +
  tm_borders()

map objects

map_nz <- tm_shape(nz) +
  tm_polygons()
class(map_nz)
## [1] "tmap"

In this case, we’re adding a layer with information on elevation and this layer to have 70% transparency.

map_nz1 <- map_nz +
  tm_shape(nz_elev) +
  tm_raster(col_alpha = 0.7)

map_nz1 
## SpatRaster object downsampled to 1141 by 877 cells.

we can add points designating high points in the country

map_nz2 <- map_nz1 +
  tm_shape(nz_height) +
  tm_dots()

map_nz2
## SpatRaster object downsampled to 1141 by 877 cells.

aesthetic basics

Let’s start by changing some fixed aesthetics…First, let’s change the color used to fill the NZ shapes.

tm_shape(nz) +
  tm_polygons(fill = "red")

now change the color of the boundaries

tm_shape(nz) + 
  tm_polygons(col = "blue")

and the width of the boundary lines

tm_shape(nz) + 
  tm_polygons(lwd = 3)

and the line type of the boundary lines

tm_shape(nz) + 
  tm_polygons(lty = 2)

all together now!

tm_shape(nz) + 
  tm_polygons(fill = "red", 
              fill_alpha = 0.3,
              col = "blue", 
              lwd = 3, 
              lty = 2)

Now let’s change the colors based on a value. We noticed that the New Zealand dataset has a column with each state’s land area

nz
## Simple feature collection with 16 features and 6 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 1090144 ymin: 4748537 xmax: 2089533 ymax: 6191874
## Projected CRS: NZGD2000 / New Zealand Transverse Mercator 2000
## First 10 features:
##                 Name Island Land_area Population Median_income Sex_ratio
## 1          Northland  North 12500.561     175500         23400 0.9424532
## 2           Auckland  North  4941.573    1657200         29600 0.9442858
## 3            Waikato  North 23900.036     460100         27900 0.9520500
## 4      Bay of Plenty  North 12071.145     299900         26200 0.9280391
## 5           Gisborne  North  8385.827      48500         24400 0.9349734
## 6        Hawke's Bay  North 14137.524     164000         26100 0.9238375
## 7           Taranaki  North  7254.480     118000         29100 0.9569363
## 8  Manawatu-Wanganui  North 22220.608     234500         25000 0.9387734
## 9         Wellington  North  8048.553     513900         32700 0.9335524
## 10        West Coast  South 23245.456      32400         26900 1.0139072
##                              geom
## 1  MULTIPOLYGON (((1745493 600...
## 2  MULTIPOLYGON (((1803822 590...
## 3  MULTIPOLYGON (((1860345 585...
## 4  MULTIPOLYGON (((2049387 583...
## 5  MULTIPOLYGON (((2024489 567...
## 6  MULTIPOLYGON (((2024489 567...
## 7  MULTIPOLYGON (((1740438 571...
## 8  MULTIPOLYGON (((1866732 566...
## 9  MULTIPOLYGON (((1881590 548...
## 10 MULTIPOLYGON (((1557042 531...

Let’s try to plot the Land_area column. We might think that the following works, but it doesn’t!

#tm_shape(nz) +
#  tm_fill(col = nz$Land_area)

Instead, tmap is expecting a character string naming the attribute associated with the geometry

tm_shape(nz) +
  tm_fill(fill = "Land_area")

We can also add an argument that updates the title of the legend

tm_shape(nz) +
  tm_fill(fill = "Land_area", title = "Area")
## Deprecated tmap v3 code detected. Code translated to v4

We can even make it more precise using the “expression” function

tm_shape(nz) +
  tm_fill(fill = "Land_area", title = expression("Area (km"^2*")")) +
  tm_borders()
## Deprecated tmap v3 code detected. Code translated to v4

color settings

Notice how the following maps display the same data, but look quite different

tm_shape(nz) + 
  tm_polygons(fill = "Median_income", style = "pretty")
## Deprecated tmap v3 code detected. Code translated to v4

tm_shape(nz) + 
  tm_polygons(fill = "Median_income", style = "equal")
## Deprecated tmap v3 code detected. Code translated to v4

tm_shape(nz) + 
  tm_polygons(fill = "Median_income", style = "quantile")
## Deprecated tmap v3 code detected. Code translated to v4

tm_shape(nz) + 
  tm_polygons(fill = "Median_income", style = "jenks")
## Deprecated tmap v3 code detected. Code translated to v4

We can also define custom bins

breaks = c(0, 3, 4, 5) * 10000
tm_shape(nz) + 
  tm_polygons(fill = "Median_income", breaks = breaks)
## Deprecated tmap v3 code detected. Code translated to v4

map_nz +
  tm_shape(nz_elev) +
  tm_raster(col_alpha = 0.7) +
  tm_scale_continuous()
## SpatRaster object downsampled to 1141 by 877 cells.

map_nz +
  tm_shape(nz) +
  tm_polygons(fill = "Island")

map layout

To clearly give readers the context of our map, we can include a compass and scale bar

map_nz +
  tm_compass(type = "4star", position = c("left", "top")) +
  tm_scale_bar(breaks = c(0, 100, 200), text.size = 1)
## Warning: As of version 4.0, tm_scale_bar has been renamed to tm_scalebar and is
## therefore deprecated

Instead of using a compass and scale bar, we could add latitude/longitudes graticules

map_nz +
  tm_graticules()

We can also update the background color

map_nz +
  tm_graticules() +
  tm_layout(bg.color = "lightblue")

faceted and animated maps

urb_1970_2030 <- urban_agglomerations %>% 
  filter(year %in% c(1970, 1990, 2010, 2030))

tm_shape(world) +
  tm_polygons() +
  tm_shape(urb_1970_2030) +
  tm_symbols(col = "black", border.col = "white", size = "population_millions") +
  tm_facets(by = "year", nrow = 2, free.coords = FALSE)
## Deprecated tmap v3 code detected. Code translated to v4

interactive maps

tmap_mode("view")
## tmap mode set to 'view'
map_nz

To go back to regular plotting, we just need enter plotting mode

tmap_mode("plot")
## tmap mode set to 'plot'