Search code examples
rggplot2

Scale Legend Objects to Same Size and Alignment


How would I adjust the legend so the legend emblems and text are all scaled with in the same edge width/box? I would like them to align vertically and horizontally with each of the emblems to be similar sizes. I know I could manually adjust them various theme() options but I am assuming there must be a way to fix the general sizing of all legend items so they are the same overall size which makes them align properly.

Data

may_sonde_do <- structure(list(station_name = c("TMDL-R1", "TMDL-R2", "TMDL-R3", 
"TMDL-R4", "TMDL-R1", "TMDL-R2", "TMDL-R3", "TMDL-R4", "TMDL-R1", 
"TMDL-R2", "TMDL-R3", "TMDL-R4", "TMDL-R1", "TMDL-R2", "TMDL-R3", 
"TMDL-R4", "TMDL-R1", "TMDL-R2", "TMDL-R3", "TMDL-R4", "TMDL-R1", 
"TMDL-R2", "TMDL-R3", "TMDL-R4", "TMDL-R1", "TMDL-R2", "TMDL-R3", 
"TMDL-R4", "TMDL-R1", "TMDL-R2"), sample_date_time = c("2023-05-12 11:00:00", 
"2023-05-12 11:00:00", "2023-05-12 11:00:00", "2023-05-12 11:00:00", 
"2023-05-12 11:15:00", "2023-05-12 11:15:00", "2023-05-12 11:15:00", 
"2023-05-12 11:15:00", "2023-05-12 11:30:00", "2023-05-12 11:30:00", 
"2023-05-12 11:30:00", "2023-05-12 11:30:00", "2023-05-12 11:45:00", 
"2023-05-12 11:45:00", "2023-05-12 11:45:00", "2023-05-12 11:45:00", 
"2023-05-12 12:00:00", "2023-05-12 12:00:00", "2023-05-12 12:00:00", 
"2023-05-12 12:00:00", "2023-05-12 12:15:00", "2023-05-12 12:15:00", 
"2023-05-12 12:15:00", "2023-05-12 12:15:00", "2023-05-12 12:30:00", 
"2023-05-12 12:30:00", "2023-05-12 12:30:00", "2023-05-12 12:30:00", 
"2023-05-12 12:45:00", "2023-05-12 12:45:00"), parameter = c("Dissolved Oxygen", 
"Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", 
"Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", 
"Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", 
"Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", 
"Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", 
"Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", 
"Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", 
"Dissolved Oxygen"), result = c(8.42, 8.82, 8.94, 9.77, 8.42, 
8.74, 8.9, 9.39, 8.37, 8.68, 8.85, 8.73, 8.37, 8.6, 8.76, 8.44, 
8.33, 8.56, 8.7, 8.26, 8.31, 8.48, 8.63, 8.07, 8.31, 8.42, 8.57, 
7.85, 8.31, 8.34), units = c("mg/L", "mg/L", "mg/L", "mg/L", 
"mg/L", "mg/L", "mg/L", "mg/L", "mg/L", "mg/L", "mg/L", "mg/L", 
"mg/L", "mg/L", "mg/L", "mg/L", "mg/L", "mg/L", "mg/L", "mg/L", 
"mg/L", "mg/L", "mg/L", "mg/L", "mg/L", "mg/L", "mg/L", "mg/L", 
"mg/L", "mg/L"), comments = c(NA, NA, NA, NA, NA, NA, NA, NA, 
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
NA, NA, NA, NA, NA, NA), date = c("2023-05-12", "2023-05-12", 
"2023-05-12", "2023-05-12", "2023-05-12", "2023-05-12", "2023-05-12", 
"2023-05-12", "2023-05-12", "2023-05-12", "2023-05-12", "2023-05-12", 
"2023-05-12", "2023-05-12", "2023-05-12", "2023-05-12", "2023-05-12", 
"2023-05-12", "2023-05-12", "2023-05-12", "2023-05-12", "2023-05-12", 
"2023-05-12", "2023-05-12", "2023-05-12", "2023-05-12", "2023-05-12", 
"2023-05-12", "2023-05-12", "2023-05-12"), ym = c("2023-05", 
"2023-05", "2023-05", "2023-05", "2023-05", "2023-05", "2023-05", 
"2023-05", "2023-05", "2023-05", "2023-05", "2023-05", "2023-05", 
"2023-05", "2023-05", "2023-05", "2023-05", "2023-05", "2023-05", 
"2023-05", "2023-05", "2023-05", "2023-05", "2023-05", "2023-05", 
"2023-05", "2023-05", "2023-05", "2023-05", "2023-05")), row.names = c(NA, 
30L), class = "data.frame")

may_sonde_do_situ <- structure(list(station_name = c("Instant TMDL-R1", "Instant TMDL-R1", 
"Instant TMDL-R1", "Instant TMDL-R2", "Instant TMDL-R2", "Instant TMDL-R2", 
"Instant TMDL-R3", "Instant TMDL-R3", "Instant TMDL-R3", "Instant TMDL-R4", 
"Instant TMDL-R4", "Instant TMDL-R4"), sample_date_time = c("2023-05-12 06:17:00", 
"2023-05-19 04:42:00", "2023-05-26 04:55:00", "2023-05-12 05:26:00", 
"2023-05-19 04:11:00", "2023-05-26 04:20:00", "2023-05-12 04:30:00", 
"2023-05-19 03:33:00", "2023-05-26 03:25:00", "2023-05-12 03:36:00", 
"2023-05-19 02:40:00", "2023-05-26 02:45:00"), parameter = c("Dissolved Oxygen", 
"Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", 
"Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen", 
"Dissolved Oxygen", "Dissolved Oxygen", "Dissolved Oxygen"), 
    result = c(9.81, 8.46, 9.77, 9.68, 8.41, 10.62, 10.33, 8.81, 
    9.19, 9.57, 7.89, 8.86), units = c("mg/L", "mg/L", "mg/L", 
    "mg/L", "mg/L", "mg/L", "mg/L", "mg/L", "mg/L", "mg/L", "mg/L", 
    "mg/L"), date = c("2023-05-12", "2023-05-19", "2023-05-26", 
    "2023-05-12", "2023-05-19", "2023-05-26", "2023-05-12", "2023-05-19", 
    "2023-05-26", "2023-05-12", "2023-05-19", "2023-05-26"), 
    ym = c("2023-05", "2023-05", "2023-05", "2023-05", "2023-05", 
    "2023-05", "2023-05", "2023-05", "2023-05", "2023-05", "2023-05", 
    "2023-05")), row.names = c(NA, -12L), class = "data.frame")

Code

annotation_do_sonde <- data.frame(
   x = c("2023-05-22"),
   y = c(4),
   label = c("Numeric Target Minimum DO = 7 mg/L")
)



ggplot() +
    geom_point(data = may_sonde_do, aes(x = sample_date_time,
                                      y = result, color = station_name,
                                      group = station_name),
                 size = 0.8, alpha = 0.6) +
  geom_point(data = may_sonde_do_situ, aes(x = sample_date_time, y = result, fill = station_name,
                                           shape = station_name,
                                           group = station_name), size = 3) +
  geom_point(data = may_sonde_do_situ, aes(x = sample_date_time, y = result, 
                                           shape = station_name,
                                           group = station_name), size = 3,
                color  = 'black', alpha = 1) +

  scale_y_continuous(limits = c(0, 20), breaks = seq(0, 20, by = 2),
                     expand = c(0,0)) +
  scale_x_discrete(breaks = unique(may_sonde_do$date),
                   labels = unique(may_sonde_do$date),
                   expand = c(0.04,0.04)) +
  scale_shape_manual(values=22:26)  +
  geom_hline(yintercept = c(7), linetype = "solid", color = "black", size = 0.7) +
  scale_color_viridis(
    discrete = TRUE, name = NULL,
    guide = guide_legend(order = 2)
  ) +
  scale_fill_viridis_d() +
  labs(title = "May 2023 - Dissolved Oxygen", y = "Dissolved Oxygen (mg/L)") +  
  theme_classic() +
  theme(
    plot.title = element_text(hjust = 0.5),
    axis.title.x = element_blank(),
    legend.position = "bottom",
    legend.box = "vertical",
    legend.direction = "horizontal",
    panel.grid.major.y = element_line(size = .01, color = "grey60"),
    legend.margin = margin(),
    panel.grid.major.x = element_line(size=.01, color="grey60" ),
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.title=element_blank(),
    legend.text = element_text(size = 6.7)) +
    geom_text(data = annotation_do_sonde, aes(x=x, y=y, label=label), size = 3.5, size.unit = "mm")

Legend aligment is off


Solution

  • One option to achieve your desired result would be to use the override.aes argument of guide_legend to set the size for color legend symbols to be the same as the one used for the fill legend. Second, to align the symbols I use a small hack by adding a transparent string " Instant" to the labels of the color legend. This way the legend labels have the same length as the ones of the fill legend. To make the added string transparent I make use of ggtext::element_markdown. This is of course no general option but works for your use case.

    library(ggplot2)
    library(viridis)
    
    ggplot() +
      geom_point(
        data = may_sonde_do, aes(
          x = sample_date_time,
          y = result, color = station_name,
          group = station_name
        ),
        size = 0.8, alpha = 0.6
      ) +
      geom_point(data = may_sonde_do_situ, aes(
        x = sample_date_time, y = result, fill = station_name,
        shape = station_name,
        group = station_name
      ), size = 3) +
      geom_point(
        data = may_sonde_do_situ, aes(
          x = sample_date_time, y = result,
          shape = station_name,
          group = station_name
        ), size = 3,
        color = "black", alpha = 1
      ) +
      geom_hline(yintercept = c(7), linetype = "solid", color = "black", size = 0.7) +
      geom_text(
        data = annotation_do_sonde, aes(x = x, y = y, label = label),
        size = 3.5, size.unit = "mm"
      ) +
      scale_y_continuous(
        limits = c(0, 20), breaks = seq(0, 20, by = 2),
        expand = c(0, 0)
      ) +
      scale_x_discrete(
        breaks = unique(may_sonde_do$date),
        labels = unique(may_sonde_do$date),
        expand = c(0.04, 0.04)
      ) +
      scale_shape_manual(values = 22:26) +
      scale_color_viridis(
        labels = \(x) paste0(x, "<span style='color: transparent'> Instant</span>"),
        discrete = TRUE, name = NULL,
        guide = guide_legend(order = 2, override.aes = list(size = 3))
      ) +
      scale_fill_viridis_d() +
      labs(title = "May 2023 - Dissolved Oxygen", y = "Dissolved Oxygen (mg/L)") +
      theme_classic() +
      theme(
        plot.title = element_text(hjust = 0.5),
        axis.title.x = element_blank(),
        legend.position = "bottom",
        legend.box = "vertical",
        legend.direction = "horizontal",
        panel.grid.major.y = element_line(size = .01, color = "grey60"),
        legend.margin = margin(),
        panel.grid.major.x = element_line(size = .01, color = "grey60"),
        axis.text.x = element_text(angle = 45, hjust = 1),
        legend.title = element_blank(),
        legend.text = ggtext::element_markdown(size = 6.7)
      )
    

    enter image description here