Center-align legend title and legend keys in ggplo

2019-02-13 23:44发布

问题:

I am having a hard time making the title of a legend center-aligned relative to the legend keys when the legend title is long. There is a question from a year ago that works for short titles, but it doesn't seem to work for long ones.

Example, first with a short legend title:

library(ggplot2)
ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width, color=Petal.Width)) + geom_point(size = 3) +
  scale_color_distiller(palette = "YlGn", type = "seq", direction = -1,
                        name = "A") +
  theme(legend.title.align = 0.5)

Everything is as expected, the legend title is centered above the legend key.

Now the same with a long legend title:

ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width, color=Petal.Width)) + geom_point(size = 3) +
  scale_color_distiller(palette = "YlGn", type = "seq", direction = -1,
                        name = "Long legend heading\nShould be centered") +
  theme(legend.title.align = 0.5)

We can see that the text is center aligned to itself but not relative to the legend key. I have tried modifying other theme options, such as legend.justification = "center", but none seem to move the key from its left-most position in the legend box.

A couple of comments:

  • I'm running the development version of ggplot2, v2.2.1.9000 from a few days ago.

  • I specifically need a solution for a continuous colorscale palette.

回答1:

I found a solution. It requires some digging into the grob tree, and it may not work if there are multiple legends, but otherwise this seems a reasonable solution until something better comes along.

library(ggplot2)
library(gtable)
library(grid)

p <- ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width, color=Petal.Width)) + 
  geom_point(size = 3) +
  scale_color_distiller(palette = "YlGn", type = "seq", direction = -1,
                        name = "Long legend heading\nShould be centered") +
  theme(legend.title.align = 0.5)

# extract legend
g <- ggplotGrob(p)
grobs <- g$grobs
legend_index <- which(sapply(grobs, function(x) x$name) == "guide-box")
legend <- grobs[[legend_index]]

# extract guides table
guides_index <- which(sapply(legend$grobs, function(x) x$name) == "layout")
guides <- legend$grobs[[guides_index]]

# add extra column for spacing
# guides$width[5] is the extra spacing from the end of the legend text
# to the end of the legend title. If we instead distribute it 50:50 on
# both sides, we get a centered legend
guides <- gtable_add_cols(guides, 0.5*guides$width[5], 1)
guides$widths[6] <- guides$widths[2]
title_index <- guides$layout$name == "title"
guides$layout$l[title_index] <- 2

# reconstruct legend and write back
legend$grobs[[guides_index]] <- guides
g$grobs[[legend_index]] <- legend

grid.newpage()
grid.draw(g)



回答2:

you'd have to change the source code. Currently it computes the widths for the title grob and the bar+labels, and left-justifies the bar+labels in the viewport (gtable). This is hard-coded.