I've been trying to follow this vignette on how to make a shared legend for multiple ggplot2. The given examples work perfectly as is, but in my case, I'm using tikzDevice to export a tikzpicture
environment. The main problem seems to be that the widths of the legend keys are not correctly captured by grid_plot
.
I came up with a minimal R code that reproduces the problem:
require(ggplot2)
require(grid)
require(gridExtra)
require(cowplot)
require(tikzDevice)
tikz(file = "./tmp.tex", width = 5.6, height = 2.2, standAlone = T )
mpg2 <- mpg
mpg2$cyl = as.factor(mpg2$cyl)
levels(mpg2$cyl) <- c("\\textbf{\\textsc{four}}",
"\\textbf{\\textsc{five}}",
"\\textbf{\\textsc{six}}",
"\\textbf{\\textsc{seven}}",
"\\textbf{\\textsc{eight}}")
plot.mpg <- ggplot(mpg2, aes(x=cty, colour=cyl, y = hwy)) +
geom_point() +
theme(legend.position='none')
legend <- get_legend(plot.mpg + theme(legend.position = "top"))
print(plot_grid(legend,
plot.mpg, nrow=2, ncol=1,align='h',
rel_heights = c(.1, 1)))
dev.off()
The generated PDF file (after compiling tmp.tex) looks like this:
As we can observe, first legend key (four) is only partially displayed and legend key (eight) is completely invisible. I tried changing tikz
command width to no avail.
Also, I suspect that the reason behind the problem is that grid_plot command incorrectly measures the length of the legend keys if they contain latex mark up. To show that this is the cause of the problem, consider changing the levels of the mpg2$cyl to the following:
levels(mpg2$cyl) <- c("four",
"five",
"six",
"seven",
"eight")
This should result in the following plot with a perfect legend:
Please note that the example above is just meant to reproduce the problem and is not what I'm trying to do. Instead, I have four plots that I'm trying to use a shared common legend for them.
Anyone please can tell me how to fix the legend problem when it contains latex mark up?
By the way, here is my sessionInfo()
:
R version 3.3.2 (2016-10-31)
Platform: x86_64-apple-darwin13.4.0 (64-bit)
Running under: OS X Yosemite 10.10.5
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
attached base packages:
[1] grid stats graphics grDevices utils datasets methods
[8] base
other attached packages:
[1] tikzDevice_0.10-1 dplyr_0.5.0 gdata_2.17.0 cowplot_0.7.0
[5] gridExtra_2.2.1 ggplot2_2.2.0
loaded via a namespace (and not attached):
[1] gtools_3.5.0 colorspace_1.2-6 DBI_0.5 RColorBrewer_1.1-2
[5] plyr_1.8.4 munsell_0.4.3 gtable_0.2.0 labeling_0.3
[9] Rcpp_0.12.6 scales_0.4.1 filehash_2.3 digest_0.6.10
[13] tools_3.3.2 magrittr_1.5 lazyeval_0.2.0 tibble_1.1
[17] assertthat_0.1 R6_2.1.3
Thank you all.
ggplot2 appears to calculate the string widths based on the raw string as opposed to box sizes that should be returned by TeX. I'm guessing it's a calculation done too early (ie not at drawing time) in the guides code.
As a workaround you could edit the relevant widths manually in the gtable, by calling getLatexStrWidth
explicitly. Note I also added a package in the preamble otherwise the default font doesn't show bold small caps.
require(ggplot2)
require(grid)
require(gridExtra)
require(tikzDevice)
setTikzDefaults(overwrite = TRUE)
preamble <- options("tikzLatexPackages")
options("tikzLatexPackages" = c(preamble$tikzLatexPackages, "\\usepackage{bold-extra}"))
tikz(file = "./tmp.tex", width = 5.6, height = 2.2, standAlone = TRUE )
mpg2 <- mpg
mpg2$cyl = as.factor(mpg2$cyl)
levels(mpg2$cyl) <- c("\\textbf{\\textsc{four}}",
"\\textbf{\\textsc{five}}",
"\\textbf{\\textsc{six}}",
"\\textbf{\\textsc{seven}}",
"\\textbf{\\textsc{eight}}")
p <- ggplot(mpg2, aes(x=cty, colour=cyl, y = hwy)) +
geom_point() +
theme(legend.position='none')
leg <- cowplot::get_legend(p + theme(legend.position = "top"))
ids <- grep("label",leg$grobs[[1]]$layout$name)
pos <- leg$grobs[[1]]$layout$l[ids]
wl <- sapply(leg$grobs[[1]][["grobs"]][ids], function(g) getLatexStrWidth(g[["label"]]))
leg$grobs[[1]][["widths"]][pos] <- unit(wl, "pt")
grid.arrange(p, top=leg)
dev.off()