Creating a zoomed inset in a ggplot

  1. Turn the ggplot object into a grob gtable object
  2. Render this original plot
  3. Copy the original plot and scale it and move it up and to the right
  4. Mask the view so we’re only seeing part of this zoomed plot
  5. Add some borders for the regions.

Issues:

  • the site and zoomed extents are a bit different.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Basic plot
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
p <- ggplot(mtcars) + 
  geom_point(aes(mpg, wt)) + 
  theme_bw()

p

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Convert plot to a gtable (collection of grobs),
# and create a copy of it
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orig <- ggplot2::ggplot_gtable(ggplot2::ggplot_build(p))
zoom <- ingrid::ig_copy(orig)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# zoom location
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
site <- ig_roundrect(
  x      = .npc(0.2), 
  y      = .npc(0.5), 
  width  = .npc(0.20), 
  height = .npc(0.15)
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Adjust the viewport to:
#   * Scale up the zoomed version
#   * mask it with the rounded rectangle
#   * move into the upper right of the plot
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
zoom$vp <- viewport(
  x      = .npc(1.16),
  y      = .npc(0.6),
  width  = .npc(2),
  height = .npc(2),
  mask   = site
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# copy the site and make it into a visible object with a border
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
site_border <- ig_copy(site)
site_border$gp <- gp_update(site_border$gp, lty = 2, col = 'blue', fill = 'transparent')

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create another border for the zoom view
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
zoom_border <- ig_roundrect(
  x      = .npc(0.58), 
  y      = .npc(0.6), 
  width  = .npc(0.20) * 2, 
  height = .npc(0.15) * 2,
  fill   = 'transparent',
  col    = 'red',
  lty    = 2
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Draw everything
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
grid.newpage()
grid.draw(orig)
grid.draw(zoom)
grid.draw(site_border)
grid.draw(zoom_border)