Search code examples
rggplot2r-sf

Replace plot points with polygons


I am trying to create a plot where instead of using points to represent data, I use the outline of administrative boundaries.

The below code illustrates where I am up to. Just a simple ordered scatter plot. I am now stuck, trying to replace the points with the polygon shapes for each administrative boundary (nz_map). Because the polygons in nz_map have their own geometries associated with them, I'm not sure how to position them at the locations of the points in plot space.

Any advice would be much appreciated.

# load packages
library(tidyverse)
library(sf)
library(rnaturalearth)

# map of nz with administrative boundary
nz_map <- ne_states(country  = "new zealand", returnclass = "sf")

# get regions of interest and calculate area
nz_map <- nz_map %>%
  select(name, region) %>%
  filter(region %in% c("South Island", "North Island")) %>%
  mutate(area = as.vector(st_area(.)/1e6))
  

# convert to points to illustrate plotting
nz_map_pnts <- st_centroid(nz_map)

# plot
ggplot() +
  geom_point(data = nz_map_pnts, aes(reorder(name, area), y = area)) +
  labs(x = "Region",
       y = "Area") +
  theme_classic()+
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

enter image description here


Solution

  • One option would be to use lapply and annotation_custom to add a map for each region. To this end I first create a blank plot in cartesian coordinates where I map the names on x and the areas on y. Then use e.g. lapply to loop over the regions to add a map for the region at your desired coordinates:

    library(ggplot2)
    library(sf)
    
    nz_map_split <- nz_map |>
      transform(name = reorder(name, area)) |>
      split(~name)
    
    dat <- data.frame(
      name = nz_map$name,
      area = nz_map$area
    ) |> 
      transform(name = reorder(name, area))
    
    ggplot(dat, aes(name, area)) +
      geom_blank() +
      lapply(
        nz_map_split,
        \(.data) {
          #browser()
          x <- as.numeric(.data$name)
          y <- .data$area
          annotation_custom(
            ggplotGrob(ggplot(.data) +
              geom_sf() +
              theme_void()),
            xmin = x - .5, xmax = x + .5,
            ymin = y - 2000, ymax = y + 2000
          )
        }
      ) +
      labs(
        x = "Region", y = "Area"
      ) +
      theme_classic() +
      theme(
        axis.text.x = element_text(
          angle = 90, vjust = 0.5, hjust = 1
        )
      )
    

    enter image description here