4

I am plotting a map of my study area and I am having problems to edit the legend title. I need it to be "Projected fruit productivity in fallows in 40 yrs (fruits ha^-1) written in four lines. I could use bquote() to plot the -1 as a superscript. But it created an extra space that I cannot figure it out how to take it off. The extra space only appears when the title is divided into multiple lines. Also, expression(atop()) creates the superscript but once I tried to divide it into more than two lines it does not show lines three and four.

This is the Map with the extra space using bquote() Map with the extra space in the legend title

This is the Map with the four line title using expression(atop()) enter image description here

I did try different solutions found on the internet, including this post. But they all plot the fourth line with the extra space or only plot the first or second line.

Bellow is the code I am using. Any help is welcomed. The comments are different tries.

Data = spatial_dist_fallows.csv

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<-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 = "Projected fruit\nproductivity in\nfallows in 40 yrs \n(fruits ha^-1)",
                  name = bquote("Projected fruit\nproductivity in\nfallows in 40 yrs \n( fruits"*ha^-1*")"),
                  # name = expression(paste("Projected fruit productivity\nin fallows in 40 yrs\n"),bquote(paste("("*fruits~ha^-1*")"))),#(Fruits/ha)
                  name = expression(atop("Projected fruit",
                                     "productivity in",
                                     "fallows in 40 yrs",
                                     "( 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)))+
  # labs(size = expression(atop("Projected fruit\nproductivity in\nfallows in 40 yrs\n(fruits"*ha^-1*")", sep="")))+ #comment name line at the scale_size_manual
  # labs(size = bquote("Projected fruit productivity \nin fallows in 40 yrs \n( fruits"*ha^-1*")"))+ #comment name line at the scale_size_manual
  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

Extra question. Once I started to use the bquote I could not align the title text using theme(legend.title.align = 0.5), any other ideas?

2 Answers2

4

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

enter image description here

0

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)

Valentin_Ștefan
  • 6,130
  • 2
  • 45
  • 68