A {grid} Legend

Paul Murrell
gg1 <- baseplot + 
    geom_col(aes(count, country, fill=pet), position="dodge") +
    basetheme
print(gg1)

labels <- c(" dogs", " cats")
grid.legend <- function() {
    grid.segments(.2, .2, .2, .8, gp=gpar(col="grey"))
    grid.rect(.2, rep(.5, 2), .2, .2, hjust=0, vjust=1:0,
              gp=gpar(col=NA, fill=cols))
    grid.text(labels, .4, just="left",
              unit(.5, "npc") + unit(c(1, -1), "lines"),
              gp=gpar(fontsize=18))
}
grid.legend()

legendGrob <- function(width=.2, height=.2, fontsize=18) {
    line <- segmentsGrob(.2, .5 + height + .1,
                         .2, .5 - height - .1, 
                         gp=gpar(col="grey"))
    bars <- rectGrob(.2, rep(.5, 2), width, height, hjust=0, vjust=1:0,
                     gp=gpar(col=NA, fill=cols))
    labs <- textGrob(labels, .2 + width, just="left",
                     .5 + c(1, -1)*.5*height,
                     gp=gpar(fontsize=fontsize))
    grobTree(line, bars, labs)
}
grid.draw(legendGrob())

gg2 <- gg1 + theme(legend.position="none")
pushViewport(viewport(x=0, width=.8, just="left"))
print(gg2, newpage=FALSE)
popViewport()
pushViewport(viewport(x=1, width=.2, just="right"))
grid.legend()
popViewport()

legendViewport <- viewport(x=1, width=.2, just="left")
panelLegend <- grobTree(legendGrob(fontsize=12), 
                        vp=legendViewport)
gg3 <- gg2 + 
    coord_cartesian(clip="off") +
    grid_panel(panelLegend)
pushViewport(viewport(x=0, width=.8, just="left"))
print(gg3, newpage=FALSE)
popViewport()

legendFun <- function(data, coords) {
    panelViewport <- viewport(x=1, width=.2, just="left", y=coords$y[1])
    h <- abs(diff(coords$y)[2])*.9/2
    grobTree(legendGrob(height=h, fontsize=12), vp=panelViewport)
}
gg4 <- gg2 + 
    coord_cartesian(clip="off") +
    grid_panel(legendFun, aes(count, country))
pushViewport(viewport(x=0, width=.8, just="left"))
print(gg4, newpage=FALSE)
popViewport()

legendConvert <- function(data, coords) {
    panelViewport <- viewport(x=1, width=.2, just="left", y=coords$y[1])
    h <- abs(diff(coords$y)[2])*.9/2
    hin <- convertHeight(unit(h, "npc"), "in")
    w <- convertWidth(hin, "npc", valueOnly=TRUE)
    grobTree(legendGrob(width=w, height=h, fontsize=12), vp=panelViewport)
}
gg5 <- gg2 + 
    coord_cartesian(clip="off") +
    grid_panel(legendConvert, aes(count, country))
pushViewport(viewport(x=0, width=.8, just="left"))
print(gg5, newpage=FALSE)
popViewport()

makeContent.legend <- function(x) {
    h <- abs(diff(x$coords$y)[2])*.9/2
    hin <- convertHeight(unit(h, "npc"), "in")
    w <- convertWidth(hin, "npc", valueOnly=TRUE)
    setChildren(x,
                gList(grobTree(legendGrob(width=w, height=h, fontsize=12))))
}
legendDelayed <- function(data, coords) {
    panelViewport <- viewport(x=1, width=.2, just="left", y=coords$y[1])
    gTree(coords=coords, cl="legend", vp=panelViewport)
}
gg6 <- gg2 + 
    coord_cartesian(clip="off") +
    grid_panel(legendDelayed, aes(count, country))
pushViewport(viewport(x=0, width=.8, just="left"))
print(gg6, newpage=FALSE)
popViewport()

print(gg2, newpage=FALSE)
grid.ls()
layout

print(gg2, newpage=FALSE)
grid.force()
grid.ls()
layout
  background.1-13-16-1
    plot.background..zeroGrob.470
  panel.9-7-9-7
    panel-1.gTree.458
      grill.gTree.456
        panel.background..zeroGrob.454
        panel.grid.minor.x..zeroGrob.450
        panel.grid.major.y..zeroGrob.451
        panel.grid.major.x..polyline.453

print(gg2, newpage=FALSE)
grid.force()
barPath <- grid.grep("geom_rect", grep=TRUE, viewports=TRUE)
barPath
layout::panel.9-7-9-7::panel-1.gTree.507::geom_rect.rect.497 
attr(barPath, "vpPath")
[1] "layout::panel.9-7-9-7::GRID.VP.82"

print(gg2, newpage=FALSE)
grid.force()
barPath <- grid.grep("geom_rect", grep=TRUE, viewports=TRUE)
bars <- grid.get(barPath)
downViewport(attr(barPath, "vpPath"))
barHeight <- convertHeight(bars$height[1], "in")
barHeight
[1] 0.61327121682363inches
bottomGap <- convertY(bars$y[6], "in") - barHeight
bottomGap
[1] 0.20442373894121inches

labelGrob <- textGrob(labels, gp=gpar(fontsize=10))
gap <- unit(10, "mm")
legendWidth <- 3*gap + barHeight + grobWidth(labelGrob)
legend <- function(data, coords) {
    line <- segmentsGrob(2*gap, unit(.5, "npc") + barHeight + bottomGap,
                         2*gap, unit(.5, "npc") - barHeight - bottomGap, 
                         gp=gpar(col="grey", lwd=1.5))
    bars <- rectGrob(2*gap, rep(.5, 2), barHeight, barHeight, 
                     hjust=0, vjust=1:0,
                     gp=gpar(col=NA, fill=cols))
    labs <- textGrob(labels, 2*gap + barHeight, just="left",
                     unit(.5, "npc") + c(1, -1)*.5*barHeight,
                     gp=gpar(fontsize=10))
    vp <- viewport(x=1, width=legendWidth, y=coords$y[1], just="left")
    grobTree(line, bars, labs, vp=vp)
}
gg7 <- gg2 +
    theme(plot.margin=margin_part(r=0)) +
    grid_panel(legend, aes(count, country))
pushViewport(viewport(x=0, width=unit(1, "npc") - legendWidth, just="left"))
print(gg7, newpage=FALSE)
popViewport()