1

I'm looking to manually inset several boxplots over a geom_sf map made in ggplot. I have found several examples of overlays/insets on geom_polygon but not geom_sf specifically, which seems to work differently. The example below produces a map p.map and boxplot p.box.

Example code here:

library(sf)
library(ggplot2)

nc <- st_read(system.file("shape/nc.shp", package="sf"))

p.map <- ggplot() +
  geom_sf(data = nc, aes(fill = AREA)) +
  coord_sf(crs = "+proj=aea +lat_1=50 +lat_2=70 +lat_0=40 +lon_0=-96 +x_0=0 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m no_defs") +
  theme_void()

p.box <- ggplot() +
  geom_boxplot(data = mtcars, aes(x = factor(cyl), y = hp, group = factor(cyl))) +
  theme_bw() +
  theme(panel.background = element_blank(),
        panel.grid = element_blank())

I wish to inset the boxplot on the map in a way to produce something like this (though with translucent background): enter image description here

Is there a way to do this with geom_sf layers?

Here is my sessionInfo()

R version 4.0.0 (2020-04-24)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.1252  LC_CTYPE=English_United States.1252    LC_MONETARY=English_United States.1252 LC_NUMERIC=C                          
[5] LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] rmapshaper_0.4.4 ggspatial_1.1.4  here_0.1         rgdal_1.5-16     ggplot2_3.3.0    sf_0.9-6         sp_1.4-1        

loaded via a namespace (and not attached):
 [1] tidyselect_1.1.0   purrr_0.3.4        lattice_0.20-41    V8_3.2.0           colorspace_1.4-1   vctrs_0.3.2        generics_0.0.2     yaml_2.2.1        
 [9] rlang_0.4.7        geojsonlint_0.4.0  e1071_1.7-3        pillar_1.4.3       foreign_0.8-78     glue_1.4.0         httpcode_0.3.0     withr_2.2.0       
[17] DBI_1.1.0          lifecycle_0.2.0    geojsonio_0.9.2    rgeos_0.5-5        munsell_0.5.0      gtable_0.3.0       labeling_0.3       maptools_0.9-9    
[25] curl_4.3           class_7.3-16       Rcpp_1.0.4.6       KernSmooth_2.23-16 scales_1.1.0       backports_1.1.6    classInt_0.4-3     jsonvalidate_1.1.0
[33] jsonlite_1.6.1     farver_2.0.3       digest_0.6.25      dplyr_1.0.1        grid_4.0.0         rprojroot_1.3-2    jqr_1.1.0          tools_4.0.0       
[41] magrittr_1.5       lazyeval_0.2.2     geojson_0.3.4      tibble_3.0.1       crul_1.0.0         crayon_1.3.4       pkgconfig_2.0.3    ellipsis_0.3.0    
[49] rstudioapi_0.11    R6_2.4.1           units_0.6-7        compiler_4.0.0   
Fragilaria
  • 159
  • 7

2 Answers2

1

So I've realized that I was using the wrong wording when searching for answers; I should have been searching for "inset" rather than "overlay". Just goes to show the importance of searching for the right things!

The example I used from here (It is possible to create inset graphs?) used viewports from the grid package that is called by ggplot.

For my own reference and anyone else that happens to stumble upon this question, here is some code that worked for me.

library(sf)
library(ggplot2)
library(scales)

nc <- st_read(system.file("shape/nc.shp", package="sf"))

# Setting up the map
p.map <- ggplot() +
  geom_sf(data = nc, aes(fill = AREA)) +
  coord_sf(crs = "+proj=aea +lat_1=50 +lat_2=70 +lat_0=40 +lon_0=-96 +x_0=0 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m no_defs") +
  theme_void()

# Setting up the boxplot
p.box <- ggplot() +
  geom_boxplot(data = mtcars, aes(x = factor(cyl), y = hp, group = factor(cyl))) +
  theme_bw() +
  theme(panel.background = element_blank(),
        panel.grid = element_blank(),
        plot.background = element_rect(fill = alpha("white", 0.4), colour = NA))

# Printing out the figure and inserting the inset using a viewport
png("inset_plot.png", width = 10, height = 8, units = "in", res = 200)
print(p.map)
# width, height, x, and y are relative values to figure dimensions
subvp <- grid::viewport(width = 0.2, height = 0.2, x = 0.5, y = 0.3)
print(p.box, vp = subvp)
dev.off()

This produces the following figure: enter image description here

Of course this will require a lot of manual tweaking so there may be better methods for this. I will update this answer as I experiment more with my own data.

Fragilaria
  • 159
  • 7
1

One option is to overlay both plots as Grobs onto a blank canvas. Something like this

ggplot() + annotation_custom(grob = ggplotGrob(p.map),
  xmin = 0, xmax = 1,
  ymin = 0, ymax = 1) 
         + annotation_custom(grob = ggplotGrob(p.box),
  xmin = 0.1, xmax = 0.4,
  ymin = 0.6, ymax = 0.8)

You will need to play about with the coordinates, etc.

enter image description here

Andrew Gustar
  • 17,295
  • 1
  • 22
  • 32