Fix legend title when using superscript in more than two lines - R ggplot2

喜你入骨 提交于 2019-12-05 19:43:29

After some other tries, I did come up with the following solution for the legend title.

name = expression(atop("",
                       atop(textstyle("Projected fruit"),
                            atop(textstyle("productivity in"),
                                 atop(textstyle("fallows in 40 yrs"),
                                      atop(textstyle("(fruits ha"^-1*")"))))))),

I used textstyle() to plot all text with the same size, otherwise it would be plotted smaller every time atop() was called. Atop() creates a space between the first and second line, that is why the first line of the code is atop("", so the first line will be a blank.

This is the final code with the map below.

library(sf) #sf = simple feature
library(ggplot2)
library(dplyr)

PAECM_fallows <-read.csv("spatial_dist_fallows.csv")
PAECM_fallows_sp <- st_as_sf(PAECM_fallows,coords = c("X", "Y"),crs = "+proj=longlat +datum=WGS84 +no_defs")

custom_bins_fruit = c(0,60,120,180,240,1400)
PAECM_fallows_fruit <- PAECM_fallows_sp %>% 
  mutate(prod_cat_fallow = cut(prod_40, breaks= custom_bins_fruit),
     age_cat_fallow = cut(age, breaks = c(11,17,22,29,60)))


prod_map_PAECM_fruit_legend_test<-ggplot()+
  geom_sf(data = PAECM_fallows_fruit,aes(size = prod_cat_fallow), shape = 18, show.legend = "point")+
  scale_size_manual(values= c(2,3,4,5,6),
              name = expression(atop("",
                                     atop(textstyle("Projected fruit"),
                                          atop(textstyle("productivity in"),
                                               atop(textstyle("fallows in 40 yrs"),
                                                    atop(textstyle("(fruits ha"^-1*")"))))))),
              breaks= c(NA,"(0,60]","(60,120]","(120,180]","(180,240]","(240,1.4e+03]"),
              labels= c("NA","\u2264 60","60 - 120","120 - 180","180 - 240","> 240"),
              guide = guide_legend(override.aes = list(linetype = "blank", shape = 18, fill = NA)))+
  ggplot2::theme_minimal()+
  ggplot2::theme(legend.text.align=0.5,
             legend.title.align = 0.5,
             plot.background = element_blank(),
             panel.grid = element_line(colour = "white"),
             panel.background = element_rect(fill = "grey87", color = "white"))+#,
  coord_sf(xlim = c(-68.45,-68.2), ylim = c(-11.05,-10.8))
prod_map_PAECM_fruit_legend_test

Alternatively, you could use the annotation functions cowplot::draw_label() or ggplot2::annotation_custom(). I think that the explanations about these approaches given in ggplot2 two-line label with expression are helpful here as well.

1) Solution with cowplot::draw_label()

library(ggplot2)
library(cowplot)
#> Warning: package 'cowplot' was built under R version 3.5.2
#> 
#> Attaching package: 'cowplot'
#> The following object is masked from 'package:ggplot2':
#> 
#>     ggsave

# If needed, revert to default theme (cowplot modifies the theme); 
theme_set(theme_grey())

# Build a simple plot as example
p <- ggplot(mtcars, aes(x = wt, y = mpg, size = factor(gear))) + 
  geom_point() +
  labs(size = element_blank()) + # remove default legend title
  # Make enough space for the custom legend title by tweaking the right margin
  theme(legend.margin = margin(t = 0, r = 26, b = 0, l = 0, unit = "mm"))
  # Adjust further theme elements if needed, like text size, font, etc

# The lines of text and expression that constitute your custom legend title
lines <- list(
  "Projected fruit",
  "productivity in",
  "fallows in 40 yrs",
  expression("(fruits ha" ^-1 ~ ")")
)

# Using relative coordinates ranging from 0 to 1 (relative to the entire canvas).
# There is some guesswork with the coordinates until we get them right.
min_y <- 0.6
step <- 0.04 # dictates the line spacing; need to play with it until you get it right
ys <- seq(from = min_y + step * 4, to = min_y, by = -step)
x <- 0.87

# Add the annotations that will actually constitute the legend title.
gg <- ggdraw(p)
#> Warning: Using size for a discrete variable is not advised.
# Neglect the warning in this example.
for (i in 1:4){
  gg <- gg + draw_label(lines[[i]], x = x, y = ys[i])
}
gg

Note that, cowplot::draw_label() can also be used in combination with setting the clipping off, coord_cartesian(clip = "off"), which allows plotting anywhere on the canvas (see next example with ggplot2::annotation_custom()). In such a case, we do not use the relative coordinates anymore, but the ones from the plot/data (the absolute coordinates).


2) Solution with ggplot2::annotation_custom()

Note that, cowplot::draw_label() uses ggplot2::annotation_custom() under the hood, so it is more or less the same annotation technique, but bit more verbose. We need to set clipping off. This time we do not use the relative coordinates anymore, but the ones from the plot/data (the absolute coordinates).

Building upon the p plot example from above:

min_y <- 24
step <- 1 # dictates the line spacing; need to play with it until you get it right
ys <- seq(from = min_y + step * 4, to = min_y, by = -step)
x <- 6.2

# set clipping off - allows plotting anywhere on the canvas
pp <- p + coord_cartesian(clip = "off")
for (i in 1:4){
  pp <- pp + annotation_custom(grid::textGrob(lines[[i]]), 
                               xmin = x, xmax = x, ymin = ys[i], ymax = ys[i])
}
pp
#> Warning: Using size for a discrete variable is not advised.

Created on 2019-01-15 by the reprex package (v0.2.1)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!