Status quo mapping with ggplot2’s geom_sf()

ggplot2 is a beloved graphical system because of it’s elegance and flexibility. Usually a graph requires the user to specify three elements (and ggplot2 can take care of the rest):

  1. data - which dataframe will be the basis of the graphic
  2. aesthetic mapping - should variables be represented by x position, y position, color, linewidth etc, and
  3. geom, the mark that should take on these aesthetics.

ggplot2 added more geographic viz capabilities with the addition of it’s geom_sf_*() functions. However, geographic mapping with geom_sf may feel a little out-of-step with ggplot2’s other plot builds. Let’s consider creating a choropleth of characteristic that varies with geographic area. Here’s example syntax.

library(tidyverse)

brazil_info_to_map <- tribble(~state, ~info,
                       "Rondônia", 1,
                       "Amapá", 2,
                       "Amazônas", 3 )

## some geographic shape data
br_regiones <- geobr::read_state(year = 2020)

# get 
brazil_info_to_map |>
  full_join(br_regiones, 
            by = join_by(state == name_state)) |> 
  ggplot() + 
  aes(geometry = geom,
      fill = info) +
  geom_sf() 

A first step is to find shape data for the areas of interest, ideally in a data frame with one column identifying the geographic area, and another list-column often named geometry with the geographic information included. Then the actual data of interest is joined to the geography data frame via the identifying column. Then this data can be input into ggplot(). To render a map, geom_sf() can be added with no aesthetic mapping required, because under the hood, this layer is directed to look for a column named geometry. But to make the map a choropleth, aesthetic mapping instruction aes(fill = my_var) would be required.

Whereas so many geoms require at least one positional aesthetic (x and y) to be declared in plot syntax, sf weirdly doesn’t - as it’s all managed with precomputation and under-the-hood column discovery.

A new way: geography-specific layers (when aesthetic mapping is really about mapping)

This talk will show the ggregions workflow that’s more similar to the ‘classic’ ggplot2 build by requiring that the users define regions of interest. This package provides a new aesthetic, region and layer, geom_region().

library(ggregions)

geobr::read_state(year = 2020) |>
  select(state_name = name_state, 
         state_abbr = abbrev_state, 
         state_code = code_state,
         geometry = geom) |>
set_regions()


tribble(~state, ~info,
        "Rondônia", 1,
        "Amapá", 2,
        "Amazônas", 3 ) |> 
ggplot() + 
  stamp_region() + 
  aes(region = state, 
      fill = info) +
  geom_region() + 
  stamp_region_border(keep = "Rondônia", color = "red") + 
  stamp_region_label(aes(label = after_stat(state_abbr)))