wilkelab / cowplot

cowplot: Streamlined Plot Theme and Plot Annotations for ggplot2
https://wilkelab.org/cowplot/
704 stars 84 forks source link

Allow for annotations to be relative to the axes? #103

Closed kendonB closed 6 years ago

kendonB commented 6 years ago

draw_label currently requires the user to use ggdraw before annotating a plot, which seems to mess up desirable features like alignment of plots in a plot_grid call.

Could an annotate_label() function implement something like this answer on SO?

Examples: We can currently do this in cowplot but the axes don't align as they normally do:

library(tidyverse)
library(cowplot)
p <- ggplot(mpg, aes(displ, hwy)) +
  geom_point()
p1 <- p
p2 <- p + 
  theme(axis.title.x = element_blank())

p1_1 <- p1 %>% 
  ggdraw() + 
  draw_label("Annotation\nhere", x = 0.5, y = 0.7, size = 20, hjust = 0)

p2_1 <- p2 %>% 
  ggdraw() + 
  draw_label("Annotation\nhere", x = 0.5, y = 0.7, size = 20, hjust = 0)
plot_grid(p1_1, p2_1, nrow = 1, align = "h")

The SO solution almost gets there but the postitioning seems to set the right of the annotation object instead of the left of the annotation object like draw_label does.


annotate_textp <- function(label, x, y, facets=NULL, hjust=0, vjust=0, color='black', alpha=NA,
                           family=thm$text$family, size=thm$text$size, fontface=1, lineheight=1.0,
                           box_just=ifelse(c(x,y)<0.5,0,1), margin=unit(size/2, 'pt'), thm=theme_get()) {
  x <- scales::squish_infinite(x)
  y <- scales::squish_infinite(y)
  data <- if (is.null(facets)) data.frame(x=NA) else data.frame(x=NA, facets)

  tg <- grid::textGrob(
    label, x=0, y=0, hjust=hjust, vjust=vjust,
    gp=grid::gpar(col=alpha(color, alpha), fontsize=size, fontfamily=family, fontface=fontface, lineheight=lineheight)
  )
  ts <- grid::unit.c(grid::grobWidth(tg), grid::grobHeight(tg))
  vp <- grid::viewport(x=x, y=y, width=ts[1], height=ts[2], just=box_just)
  tg <- grid::editGrob(tg, x=ts[1]*hjust, y=ts[2]*vjust, vp=vp)
  inner <- grid::grobTree(tg, vp=grid::viewport(width=unit(1, 'npc')-margin*2, height=unit(1, 'npc')-margin*2))

  layer(
    data = NULL,
    stat = StatIdentity,
    position = PositionIdentity,
    geom = GeomCustomAnn,
    inherit.aes = TRUE,
    params = list(
      grob=grid::grobTree(inner), 
      xmin=-Inf, 
      xmax=Inf, 
      ymin=-Inf, 
      ymax=Inf
    )
  )
}

p1_2 <- p1 + 
  annotate_textp(label = "Annotation\nhere", 
                 x = 0.5, y = 0.7, size = 20, hjust = 0)

p2_2 <- p2 + 
  annotate_textp("Annotation\nhere", x = 0.5, y = 0.7, size = 20, hjust = 0)

plot_grid(p1_2, p2_2, nrow = 1, align = "h")

Created on 2018-07-12 by the reprex package (v0.2.0.9000).

clauswilke commented 6 years ago

Improved annotation support will have to happen in ggplot2 directly. Any alternative has to be hackish, for technical reasons. Therefore, I'm going to close this issue.

However, for your particular problem, note that you can annotate the output from plot_grid(), since it uses ggdraw() internally. This may be a simpler solution.

library(ggplot2)
library(cowplot)
p <- ggplot(mpg, aes(displ, hwy)) +
  geom_point()
p1 <- p
p2 <- p + 
  theme(axis.title.x = element_blank())

plot_grid(p1, p2, nrow = 1, align = "h") +
  draw_label("Annotation\nhere", x = 0.25, y = 0.7, size = 20, hjust = 0) +
  draw_label("Annotation\nhere", x = 0.75, y = 0.7, size = 20, hjust = 0)
screen shot 2018-07-12 at 11 42 50 am