How can I flexibly position an inset
using 'ggpp' (or 'ggpmisc') without changing the width and height of the inset itself?
library(tidyverse)
library(sf)
library(ggpp)
#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", linewidth = 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:
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", linewidth = 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:
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
The apaprently unexpected positioning happens when the aspect ratio of the inset and of the viewport differ. The justification is applied to the viewport as a whole. The justification of the inset within the viewport is not controlled by geom_plot()
. The way to solve this problem is to adjust the aspect ratio of the viewport in the call to geom_plot()
.
#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", linewidth = 1.5) +
geom_plot(data = insets_nc_tibble,
aes(x = I(x), y = I(y), label = plot),
vp.width = 0.55, vp.height = 0.35)
Created on 2025-04-05 with reprex v2.1.1
This behavior of 'ggplot2' is most likely intentional as latitude and longitude would be otherwise distorted. what taking can be seen by printing the ggplot and then running grid::showViewport()
. Using fixed coordinates the inset plot cannot stretch asymmetrically to fill the available space in the viewport in both x and y dimensions.