I'm trying to define custom shapes to use in ggplot2 by passing grobs created with the grid package to geom_grob. So, for example, I might create the following two grobs (a square and a triangle):
library(grid)
library(ggplot2)
library(dplyr)
library(ggpp)
devratio <- dev.size()[2]/dev.size()[1]
square_ang <- seq(pi/4, 9*pi/4, length.out = 5)
square <- grid.polygon(
x = unit(0.5 + 0.2 * devratio * sin(square_ang), 'npc'),
y = unit(0.5 + 0.2 * cos(square_ang), 'npc'),
gp = gpar(lwd = 2, fill = 'blue', col = 'blue')
)
triangle_ang <- seq(0, 2*pi, length.out = 4)
triangle <- grid.polygon(
x = unit(0.5 + 0.18 * devratio * sin(triangle_ang), 'npc'),
y = unit(0.5 + 0.18 * cos(triangle_ang), 'npc'),
gp = gpar(lwd = 2, fill = 'blue', col = 'blue')
)
Within geom_grob there is an argument show.legend, but after setting that to TRUE nothing shows up.
ggplot(tibble(x = 1:6, y = 1:6,
shape = rep(list(square, triangle), each = 3))) +
geom_grob(aes(x, y, label = shape), show.legend = TRUE)
I thought maybe ggplot2 doesn't consider label to be an aesthetic, so I tried adding an artificial color specification. That got the legend to appear, but the legend key is blank.
ggplot(tibble(x = 1:6, y = 1:6,
shape = rep(list(square, triangle), each = 3),
col = rep(c('col1', 'col2'), each = 3))) +
geom_grob(aes(x, y, label = shape, color = col),
show.legend = TRUE)
Is it possible to have a legend showing the custom grobs?
At least for your shapes I think the easiest approach would be to go on with your second approach and fake a legend using the color
or ... aesthetic. The reason that no keys show up is that default draw_key
function of geom_grob
or GeomGrob
returns a nullGrob
, i.e. display nothing. But you can override that using the key_glyph
argument:
library(grid)
library(ggplot2)
library(ggpp)
library(dplyr)
library(gtable)
ggplot(tibble(
x = 1:6, y = 1:6,
shape = rep(list(square, triangle), each = 3),
col = rep(c("col1", "col2"), each = 3)
)) +
geom_grob(aes(x, y, label = shape, color = col),
show.legend = TRUE, key_glyph = "point"
) +
guides(
color = guide_legend(
override.aes = list(
shape = c(15, 17),
color = "blue",
size = 4
)
)
)
For more general shapes, a second option (and alternative to the approach by @M--) would be to manually add a legend using guide_custom
introduced in ggplot2 >= 3.5.0
where I use gtable
to setup the layout for the legend grobs:
square_text <- textGrob(
x = unit(0, "npc"),
label = "Square",
gp = gpar(fontsize = 8),
hjust = 0
)
triangle_text <- textGrob(
x = unit(0, "npc"),
label = "Triangle",
gp = gpar(fontsize = 8),
hjust = 0
)
legend_key_height <- 20
# Setup the gtable layout
legend <- gtable(
# Widths of columns.
widths = unit(
c(legend_key_height / devratio, 1.5), c("pt", "cm")
),
heights = unit(rep(legend_key_height, 2), "pt")
)
# Add the grobs
legend <- gtable_add_grob(
legend,
grobs = list(square, triangle, square_text, triangle_text),
t = c(1, 2, 1, 2),
l = c(1, 1, 2, 2),
clip = "off"
)
ggplot(tibble(
x = 1:6, y = 1:6,
shape = rep(list(square, triangle), each = 3)
)) +
geom_grob(aes(x, y, label = shape)) +
guides(
custom = guide_custom(
legend
)
) +
theme(
legend.box.spacing = unit(0, "pt")
)