1

How can I flexibly position an inset using ggpmisc without changing the width and height of the inset itself?

library(tidyverse)
library(sf)  
library(ggpmisc)

#data
nc <- st_read(system.file("gpkg/nc.gpkg", package = "sf"), quiet = TRUE) %>%
  st_transform(st_crs(4326)) %>%
  st_cast('POLYGON')

#create timeseries data
nc_2 <- rbind(nc %>% mutate(timepoint = 1), nc %>% mutate(timepoint = 2))
#create base plot
nc_2_base <- ggplot(data = nc_2) + 
  geom_sf(aes(fill = BIR74)) + 
  coord_sf(xlim = c(-80, -76), 
           ylim = c(32, 37), expand = FALSE)
#facet plot
nc_2_main <- nc_2_base + facet_wrap(~timepoint, dir = "h", ncol = 2) 

#extract number of timepoints
nmax_rep_nc <- length(unique(nc_2$timepoint))

#create insets
insets_nc <- lapply(seq_len(nmax_rep_nc), function(i) {
  nc_2_base + ggforce::facet_wrap_paginate(~ timepoint, nrow = 1, ncol = 1, page = i) +
    coord_sf(xlim = c(-79.5, -78.5), ylim = c(34.5, 35.5)) +
    theme(strip.background = element_blank(),
          strip.text = element_blank(),
          axis.title = element_blank(),
          plot.background = element_blank(),
          legend.position = "none")
})

To position the insets you need to create a tibble with x, y indicating the position you want. Here, I want them in the bottom left corner so specify x = 0.0 and y = 0 (x, y can be 0 - 1 from the vignette here) and I want the size of the insets to be 50% of the main plot (vp.width = 0.5, vp.height = 0.5):

insets_nc_tibble <- tibble(x = rep(0.0, nmax_rep_nc),
                           y = rep(0.0, nmax_rep_nc),
                           plot = insets_nc,
                           timepoint = unique(nc_2$timepoint))
#add inset to plot:
nc_2_main +
  geom_rect(xmin = -79.5, xmax = -78.5, ymin = 34.5, ymax = 35.5, 
            fill = NA, colour = "red", size = 1.5) +
  geom_plot_npc(data = insets_nc_tibble, 
                aes(npcx = x, npcy = y, label = plot,
                    vp.width = 0.5, vp.height = 0.5))

which produces this plot:

enter image description here

But the inset isn't correctly in the bottom left corner (0, 0) as I wanted. How can I keep the inset this size but move it so it is directly in the corner?

If I reduce the size of the inset it seems to help but this is completely trial and error and I don't want to reduce the size of the inset.

#reduce size
nc_2_main +
  geom_rect(xmin = -79.5, xmax = -78.5, ymin = 34.5, ymax = 35.5, 
            fill = NA, colour = "red", size = 1.5) +
  geom_plot_npc(data = insets_nc_tibble, 
                aes(npcx = x, npcy = y, label = plot,
                    vp.width = 0.5, vp.height = 0.25))

This produces this plot which is better positioning but not the correct size I want:

enter image description here

Note, you can also specify corner by string but this doesn't help:

#insets_nc_tibble <- tibble(x = rep("left", nmax_rep_nc),
#                           y = rep("bottom", nmax_rep_nc),
#                           plot = insets_nc,
#                           timepoint = unique(nc_2$timepoint))

This question is a follow up to my previous answer and others here.

I don't understand how changing the size, changes the position. I thought specifying x, y = 0, 0 means the bottom left corner of the inset should be set to 0, 0 but doesn't seem the case here?

Any ideas?

thanks

user63230
  • 4,095
  • 21
  • 43

1 Answers1

1

This looks like a bug. I will investigate why there is a shift of 0.5 degrees in the x axis.

Here is a temporary workaround using the non-noc version of the geom and shifting the x coordinates by -0.5 degrees:

insets_nc_tibble1 <- tibble(x = rep(-80, nmax_rep_nc),
                            y = rep(31.5, nmax_rep_nc),
                            plot = insets_nc,
                            timepoint = unique(nc_2$timepoint))

#add inset to plot:
nc_2_main +
  geom_rect(xmin = -79.5, xmax = -78.5, ymin = 34.5, ymax = 35.5, 
            fill = NA, colour = "red", size = 1.5) +
  geom_plot(data = insets_nc_tibble1, 
            aes(x = x, y = y, label = plot),
            vp.width = 0.5, vp.height = 0.5)

enter image description here

The reason is that the grid viewport for the rendered plot is larger than the plot itself. Whether this a feature or a bug in 'ggplot2' is difficult to say as lat and lot would be otherwise distorted. Can be seen by printing the ggplot and then running grid::showViewport(). This seems to be the result of using fixed coordinates so that the inset plot cannot stretch to fill the available space in the viewport.

Pedro J. Aphalo
  • 5,796
  • 1
  • 22
  • 23
  • thanks, this looks good. Interested to hear about the bug is you find one. – user63230 Feb 08 '21 at 12:14
  • 1
    @user63230 I have not updated the test cases of 'ggpmisc' to include cases with `geom_sf()`. Other cases work as expected. I am guessing that the layer added with `geom_sf()` in the inset plot causes the problem. – Pedro J. Aphalo Feb 09 '21 at 19:15
  • 1
    @user63230 I updated my answer. There is no bug I can find in 'ggpmisc'. The grob returned by `ggplotGrob()` or the one printed is within a larger viewport than the plot. `geom_plot()` uses the size of the grob, even if it is in part invisible. You were correct in that playing with the size of the viewport helps, additional tweaking so that the inset would fill the viewport without changing the coordinates would be needed which is far from ideal. Automatic resizing of the viewport might be doable, but I do not think easy. – Pedro J. Aphalo Feb 10 '21 at 20:40