Alan Leal

Teaching

A short guide to maps in R

November 2023.

This guide shows how to draw static maps in R for theses, papers, and policy briefs. Interactive maps, drawn with libraries such as leaflet or plotly, are outside its scope.

Maps in economics require the same elements as in cartography: besides the map itself, a title, a scale, a legend, and a north arrow. Variables may be continuous or categorical, and their values determine whether a map uses a continuous color scale, a diverging scale, or another design. Color scales should remain readable for people with color vision deficiency.

All layers come from BH Map, maintained by Belo Horizonte City Hall, which holds the copyright; the use here is academic. The site offers point, line, and polygon shapefiles on many subjects. The guide uses cultural facilities (points), subway lines (lines), and neighborhood population in 2010 (polygons). The municipal boundary comes from the geobr package, which downloads shapefiles of Brazilian municipalities and states directly into R.

Points

The maps use geom_sf from ggplot2. The scale bar and north arrow come from ggsn.

ggplot() + geom_sf(data = bh) +
  geom_sf(data = cultural_facilities, aes()) +
  labs(x = "Longitude", y = "Latitude",
       title = "Cultural facilities in Belo Horizonte, MG") +
  ggsn::scalebar(data = bh,
                 location = "bottomright",
                 dist = 2,
                 dist_unit = "km",
                 transform = TRUE,
                 model = "GRS80",
                 st.size = 2) +
  ggsn::north(data = bh,
              location = "topleft",
              symbol = 12,
              scale = 0.15) +
  theme_bw()
  1. Map. geom_sf draws the layers. Point layers need no aesthetic mapping. labs sets the title, axis labels, and legend title.
  2. Scale bar. The ggsn scale bar can be repositioned with location and resized; dist usually needs adjusting for maps at different scales.
  3. North arrow. ggsn offers 14 symbols; this map uses symbol 12. Its position can also be changed.

Lines

The subway map follows the same structure and allows the same customization.

ggplot() + geom_sf(data = bh) +
  geom_sf(data = metro, aes()) +
  labs(x = "Longitude", y = "Latitude",
       title = "Subway lines in Belo Horizonte, MG") +
  ggsn::scalebar(data = bh,
                 location = "bottomright",
                 dist = 2,
                 dist_unit = "km",
                 transform = TRUE,
                 model = "GRS80",
                 st.size = 2) +
  ggsn::north(data = bh,
              location = "topleft",
              symbol = 12,
              scale = 0.15) +
  theme_bw()

Polygons

The fill aesthetic colors each polygon by the value of a variable, here residents per household in each neighborhood. scale_fill_viridis replaces the default blue scale; RColorBrewer offers further palettes.

ggplot() + geom_sf(data = bh) +
  geom_sf(data = pop_bh, aes(fill = HAB_DOM)) +
  labs(x = "Longitude", y = "Latitude",
       title = "Population by neighborhood in Belo Horizonte, MG",
       fill = "Residents per household") +
  scale_fill_viridis(direction = -1, na.value = "white") +
  ggsn::scalebar(data = bh,
                 location = "bottomright",
                 dist = 2,
                 dist_unit = "km",
                 transform = TRUE,
                 model = "GRS80",
                 st.size = 2) +
  ggsn::north(data = bh,
              location = "topleft",
              symbol = 12,
              scale = 0.15) +
  theme_bw()

Combining layers

Layers are drawn in the order they are declared. For the subway lines to appear above the neighborhood polygons, the metro layer must come after the population layer; otherwise the polygons cover the lines.

ggplot() + geom_sf(data = bh) +
  geom_sf(data = pop_bh, aes(fill = HAB_DOM)) +
  labs(x = "Longitude", y = "Latitude",
       title = "Population by neighborhood and subway lines in Belo Horizonte, MG",
       fill = "Residents per household") +
  scale_fill_viridis(direction = -1, na.value = "white") +
  ggsn::scalebar(data = bh,
                 location = "bottomright",
                 dist = 2,
                 dist_unit = "km",
                 transform = TRUE,
                 model = "GRS80",
                 st.size = 2) +
  ggsn::north(data = bh,
              location = "topleft",
              symbol = 12,
              scale = 0.15) +
  geom_sf(data = metro, aes()) +
  theme_bw()

Color palettes

The viridis and RColorBrewer packages offer many palettes. The same map with two of them:

Viridis

ggplot() + geom_sf(data = bh) +
  geom_sf(data = pop_bh, aes(fill = HAB_DOM)) +
  labs(x = "Longitude", y = "Latitude",
       title = "Population by neighborhood in Belo Horizonte, MG",
       fill = "Residents per household") +
  scale_fill_viridis(direction = -1, na.value = "white") +
  ggsn::scalebar(data = bh,
                 location = "bottomright",
                 dist = 2,
                 dist_unit = "km",
                 transform = TRUE,
                 model = "GRS80",
                 st.size = 2) +
  ggsn::north(data = bh,
              location = "topleft",
              symbol = 12,
              scale = 0.15) +
  theme_bw()

RColorBrewer, Spectral

n_colors <- 7
spectral_colors <- brewer.pal(n_colors, "Spectral")
ggplot() + geom_sf(data = bh) +
  geom_sf(data = pop_bh, aes(fill = HAB_DOM)) +
  labs(x = "Longitude", y = "Latitude",
       title = "Population by neighborhood in Belo Horizonte, MG",
       fill = "Residents per household") +
  scale_fill_gradientn(colours = spectral_colors,
                       name = "Residents per household") +
  ggsn::scalebar(data = bh,
                 location = "bottomright",
                 dist = 2,
                 dist_unit = "km",
                 transform = TRUE,
                 model = "GRS80",
                 st.size = 2) +
  ggsn::north(data = bh,
              location = "topleft",
              symbol = 12,
              scale = 0.15) +
  theme_bw()