With minor adjustment, you can use the approach shown at change-geom-texts-default-a-legend-to-label-string-itself. This changes the key generating function, and so allows ggplot2
to calculate the positions to place the labels for you.
Th idea is to keep the original rectGrob
for the geom_bar
legend key and use an additional textGrob
to place the label on top.
library(ggplot2)
library(grid)
oldK <- GeomBar$draw_key # to save for later
# see other answer on how to tweak function based n manual colours
GeomBar$draw_key <- function (data, params, size,
var=unique(mtcars$vs),
cols=scales::hue_pal()(length(var))) {
# match labels to the fill colour
txt <- if(is.factor(var)) levels(var) else sort(var)
txt <- txt[match(data$fill, cols)]
# This is from the original GeomBar$draw_key -------------------
lwd <- min(data$size, min(size)/4)
rg <- rectGrob(width = unit(1, "npc") - unit(lwd, "mm"), height = unit(1,
"npc") - unit(lwd, "mm"), gp = gpar(col = data$colour,
fill = alpha(data$fill, data$alpha), lty = data$linetype,
lwd = lwd * .pt, linejoin = "mitre"))
# ---------------------------------------------------------------
# Add text label: lifted from other answer
# hard-coded text size multiplier -- got to be a better way!
tg <- textGrob(txt, 0.5, 0.5,
just="center",
gp = gpar(col = "black",
fontfamily = data$family,
fontface = data$fontface,
fontsize = 10*data$size * .pt))
# ---------------------------------------------------------------
grobTree(rg, tg) # output new key
}
You can then plot
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) +
geom_bar() +
theme(
legend.position = "top",
legend.direction = "horizontal",
legend.text = element_blank())
# reset key function to original
GeomBar$draw_key <- oldK

This should be fairly robust to resizing your plot.