1
votes

I would like to manually adjust the legend in my plot I'm making with ggplot as well as add two new items to the legend. Right now the legend appears as legend title - factor(CruiseID), followed by a red circle with the text 201905 and then a blue circle with the text 201906 even though I have the scale_fill_manual code in the code.
I would like to have no legend title, followed by red circle and text 2019 MAB Leg 1, blue circle and text 2019 MAB Leg 2, gray box and text Survey Domain white box with black outline and text Access Area

example data

data<-data.frame(CruiseID=c(rep(201905,5),rep(201906,5)),
                 beglat=c(36.66,36.66,37.07,37.01,37.03,37.033,37.08,37.09,37.07,37.077),
                 beglong=c(-74.75,-74.75,-74.73,
                  -74.731,-74.90,-74.90,-74.88,-74.88,
                  -74.72,-74.72))


ggplot() +
  geom_point(data = data, aes(x = beglong, y = beglat,colour=factor(CruiseID))) +
  scale_colour_manual(values = c("red", "blue"),drop=T)+
  xlim(-76,-71)+
  ylim(36,42)+
  ggtitle("2019 MAB Survey Stations") +
  labs(x = "Longitude", y = "Latitude") +
  scale_fill_manual(values = c("red", "blue","gray87","black"),
                    labels = c('Leg 1', 'MAB Leg 2','Survey Domain','Access Area'))+
  theme_bw()+
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        plot.title=element_text(size=14),
        text=element_text(size=12),
        axis.text.x=element_text(colour="black", size = 12),
        axis.text.y=element_text(colour="black", size = 12),
        legend.justification=c(.5,.5), 
        legend.background = element_rect(fill = "white", color = NA),
        legend.position=c(0.8, 0.3),legend.box="vertical", 
        legend.margin=margin())
2
+ scale_fill_manual(name = NULL, ...)? (for removing the legend title)r2evans
I have tried that and it doesn't do anything. It seems like the entire section of scale_fill_manual code is not being recognizeduser41509
scale_fill_manual cannot be recognized without an aesthetic mapping fill to a variable. In aes(.) you have color, not fill.Rui Barradas

2 Answers

2
votes

Here's a solution that uses ggnewscale::new_scale_color to create two scales for the color, one for the points and another one for the boxes. Additionally, I created an additional data.frame named df to plot 2 rectangles using geom_rect and create the legend for the boxes.

library(ggplot2)
library(ggnewscale)

# Create another data frame to plot the rectangles and its legend
df <- data.frame (xmin = c(0,0),
            xmax = c(0,0),
            ymin = c(0,0),
            ymax = c(0,0),
            fill = c("A","B"))

ggplot() +
  geom_point(data = data, aes(x = beglong, 
                              y = beglat, 
                              colour=factor(CruiseID))) +
  # Move the color scale for the points before setting a new scale color
  scale_color_manual(values = c("red", "blue"),
                      labels = c('2019 MAB Leg 1', '2019 MAB Leg 2'))+
  # Set new scale color
  new_scale_color() +
  # plot rectangles in 0,0; where they will not appear in the plot's area
  geom_rect(data = df,aes(xmin = xmin,
                          xmax = xmax,
                          ymin = ymin,
                          ymax = ymax,
                          fill = fill,
                          col = fill)) +
  # Manually set color for the boxes
  scale_color_manual(values = c("white","black"),
                     labels = c('Survey Domain', 'Access Area')) +
  # Manually set fill for the boxes, use same labels as color so the box legends are combined
  scale_fill_manual(values = c("gray87","white"),
                    labels = c('Survey Domain', 'Access Area'))+
  xlim(-76,-71)+
  ylim(36,42)+
  ggtitle("2019 MAB Survey Stations") +
  labs(x = "Longitude", y = "Latitude") +
  theme_bw()+
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        plot.title=element_text(size=14),
        text=element_text(size=12),
        axis.text.x=element_text(colour="black", size = 12),
        axis.text.y=element_text(colour="black", size = 12),
        legend.justification=c(.5,.5),
        # Remove legend title
        legend.title = element_blank(),
        legend.background = element_rect(fill = "white", color = NA),
        legend.position=c(0.8, 0.3),legend.box="vertical", 
        legend.margin=margin())

desired plot

0
votes

Up front, @RuiBarradas's comment about scale_fill_manual is both correct and misled from your code. I believe you included it with the expectation of adding your areas (survey and access) later. Perhaps you thought that it could have an effect on your color legend (or just confused them). Regardless, his point is correct: scale_fill_manual here does nothing unless you actually use fill= as an aesthetic somewhere in the plot. All of the naming and such would have to be done in scale_color_manual for your dots.

Further, you have two options for setting your labels. I believe you can use labels= in your scale calls to set them, and this can be a named vector of c("201905" = "MAB Leg 1", ...). Or you can use the labels initially in the aesthetic (as I do below). They both work, but the former requires a little more data-management without ggplot.

I suggest a slightly different data organization.

  • Since you don't want to show the 201905 (etc) numbers, yet you want to assign labels and colors on them, I'll create a labels frame that maps them together, and then I'll merge them in.

  • Since you have scale_fill_manual yet have assigned no fill= aesthetics, I'll create a fake areas frame that contains your two types of area. I'll assign them arbitrary CruiseID values so that we can still use the labels frame to identify them (and their fill as well).

labels <- data.frame(
  CruiseID = c(201905, 201906, -1, -2),
  # 'factor' to preserve the order
  label    = factor(c("MAB Leg 1", "MAB Leg 2", "Survey Domain", "Access Area")),
  color    = c("red", "blue", "#00000000", "black"),
  fill     = c("#00000000", "#00000000", "gray", "white")
)
areas <- data.frame(
  label = c(-1, -2),
  beglat = NA_real_, beglong = NA_real_)

Next, the values= value of the scale_*_manual functions can work with a named vector. I'll re-use labels for this, using a small trick to turn two columns into a named vector:

labels
#   CruiseID         label     color      fill
# 1   201905     MAB Leg 1       red #00000000
# 2   201906     MAB Leg 2      blue #00000000
# 3       -1 Survey Domain #00000000      gray
# 4       -2   Access Area     black     white
setNames(labels$color, labels$label)
#     MAB Leg 1     MAB Leg 2 Survey Domain   Access Area 
#         "red"        "blue"   "#00000000"       "black" 

For removing the legend titles, merely add name=NULL to the applicable scale_ calls.

The code:

ggplot() +
  geom_point(                                     # UPDATED
    data = merge(data, labels, by = "CruiseID", all.x = TRUE),
    aes(x = beglong, y = beglat, colour = label)) +
  scale_colour_manual(                            # UPDATED
    name = NULL,
    values = setNames(labels$color, labels$label),
    drop = TRUE) +
  geom_polygon(                                   # NEW, placeholder
    data = merge(areas, labels, by = "CruiseID", all.x = TRUE),
    aes(x = beglong, y = beglat, fill = label),
    na.rm = TRUE) +
  scale_fill_manual(                              # UPDATED
    name = NULL,
    values = setNames(labels$fill, labels$label),
    drop = TRUE) +
  xlim(-76,-71) +
  ylim(36,42) +
  ggtitle("2019 MAB Survey Stations") +
  labs(x = "Longitude", y = "Latitude") +
  theme_bw() +
  theme(
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    plot.title=element_text(size=14),
    text=element_text(size=12),
    axis.text.x=element_text(colour="black", size = 12),
    axis.text.y=element_text(colour="black", size = 12),
    legend.justification=c(.5,.5), 
    legend.background = element_rect(fill = "white", color = NA),
    legend.position=c(0.8, 0.3),legend.box="vertical", 
    legend.margin=margin(),
    legend.key = element_rect(colour = "black")   # NEW
  )

updated ggplot2 with boxed legends

Finally, without editing the grob (graphic object) and/or graphic-table manually, I don't think there's a way to box in the colors of one legend and not the other: the theme option legend.key allows us to box them in, but it applies to all legends. If you don't mind the boxes around the dots, you're good. If you can't have them, then you can remove the legend.key= option and change your "Access Area" from white to something different than the background color, so that it is visible.

labels <- data.frame(
  CruiseID = c(201905, 201906, -1, -2),
  # 'factor' to preserve the order
  label    = factor(c("MAB Leg 1", "MAB Leg 2", "Survey Domain", "Access Area")),
  color    = c("red", "blue", "#00000000", "black"),
  fill     = c("#00000000", "#00000000", "gray", "gray90")
)

ggplot() +
  geom_point(                                     # UPDATED
    data = merge(data, labels, by = "CruiseID", all.x = TRUE),
    aes(x = beglong, y = beglat, colour = label)) +
  scale_colour_manual(                            # UPDATED
    name = NULL,
    values = setNames(labels$color, labels$label),
    drop = TRUE) +
  geom_polygon(                                   # NEW, placeholder
    data = merge(areas, labels, by = "CruiseID", all.x = TRUE),
    aes(x = beglong, y = beglat, fill = label),
    na.rm = TRUE) +
  scale_fill_manual(                              # UPDATED
    name = NULL,
    values = setNames(labels$fill, labels$label),
    drop = TRUE) +
  xlim(-76,-71) +
  ylim(36,42) +
  ggtitle("2019 MAB Survey Stations") +
  labs(x = "Longitude", y = "Latitude") +
  theme_bw() +
  theme(
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    plot.title=element_text(size=14),
    text=element_text(size=12),
    axis.text.x=element_text(colour="black", size = 12),
    axis.text.y=element_text(colour="black", size = 12),
    legend.justification=c(.5,.5), 
    legend.background = element_rect(fill = "white", color = NA),
    legend.position=c(0.8, 0.3),legend.box="vertical", 
    legend.margin=margin()
  )

updated ggplot2, not-boxed legend