Search code examples
rggplot2r-sfpatchwork

plot multiple country maps with fixed size and layout


I am trying to plot a a series of maps (NZ, Australia and Argentina) in a grid, with some parts of the grid being empty. I am using ggplot to create the individual maps and then patchwork to sow together the grid.

I expect the result to be a 3x3 grid with 3x NZ maps on the top row, 2x aus + an empty plot on the second row and 1x Argentina + 2x empty plots on the 3rd row.

My problem is the plots do not maintain a 3x3 grid and the countries are very different sizes. An example:

library(tidyverse)
library(sf)
library(rnaturalearth)
library(patchwork)


# basic country maps
nz <- ne_countries(country = 'new zealand', type = 'countries', returnclass = "sf")
aus <- ne_countries(country = 'australia', type = 'countries', returnclass = "sf")
arge <- ne_countries(country = 'argentina', type ='countries', returnclass = "sf")

# plot nz
p1 <- ggplot() + 
  geom_sf(data = nz) +
  theme_void()

# plot aus
p2 <- ggplot() + 
  geom_sf(data = aus) +
  theme_void()

# plot arg
p3 <- ggplot() + 
  geom_sf(data = arge) +
  theme_void()


# combine plots
p1 + p1 + plot_spacer() /
  p2 + plot_spacer() + p2 /
  plot_spacer() + plot_spacer() + p3

enter image description here

Whereas the desired output would be like this (with the maps all a similar size):

enter image description here

Any pointers would be much appreciated!


Solution

  • This is a bit of a hack which I use quite often when I have to export maps. The basic idea is to not plot or export the map directly but instead add it to an empty background plot first via patchwork::inset_element.

    For the first plot I dropped theme_void from the background plot which shows that all subplots are actually of the same size independent what the size of the map is:

    library(tidyverse)
    library(sf)
    library(rnaturalearth)
    library(patchwork)
    
    plist <- dplyr::lst(p1, p2, p3)
    
    plist <- lapply(plist, function(x) {
      ggplot() +
        geom_blank() +
        #theme_void() +
        inset_element(x, left = 0, right = 1, top = 1, bottom = 0)
    })
    
    # combine plots
    (plist$p1 + plist$p1 + plist$p1) /
      (plist$p2 + plot_spacer() + plist$p2) /
      (plot_spacer() + plot_spacer() + plist$p3)
    

    And here is the final plot using theme_void for the background plots too:

    plist <- dplyr::lst(p1, p2, p3)
    
    plist <- lapply(plist, function(x) {
      ggplot() +
        geom_blank() +
        theme_void() +
        inset_element(x, left = 0, right = 1, top = 1, bottom = 0)
    })
    
    # combine plots
    (plist$p1 + plist$p1 + plist$p1) /
      (plist$p2 + plot_spacer() + plist$p2) /
      (plot_spacer() + plot_spacer() + plist$p3)