Phil Smith
Phil Smith

Reputation: 454

Overlay grid on coloured panel background and different coloured rectangle

I am drawing a chart using ggplot2 in R. I want to colour the panel background using one colour, and a rectangle within the panel using a different colour. I want the grid lines to overlay both the panel and the rectangle.

If I do not colour the panel background, I have a good solution thanks to AF7 and zx8754 here. But if I try to colour the panel background as well, it does not work.

Here is a reprex :

library(ggplot2)
t <- c(1,2,3)
a <- c(5,4,1)
b <- c(3,2,4)
df <- data.frame(t,a,b)

ggplot(df) +
  geom_rect(xmin=2,xmax=3,ymin=-Inf,ymax=Inf,fill="gray") +

  # The code works fine without this next line, but the panel colour is the default (white)
  # I want the background colour to be lightblue except for the rectangle
  theme(panel.background = element_rect(fill = "lightblue")) +

  # Changing NA to "lightblue" in the line below does not work either
  theme(panel.background = element_rect(fill = NA),panel.ontop = TRUE) +

  theme(panel.grid.minor=element_line(colour="hotpink",size=0.5)) +
  theme(panel.grid.major=element_line(colour="green",size=0.5)) +
  geom_line(aes(x=t,y=a),colour="red") +
  geom_line(aes(x=t,y=b),colour="blue")

Upvotes: 2

Views: 752

Answers (1)

Z.Lin
Z.Lin

Reputation: 29085

In ggplot, the panel background & grid layers are contained within the same grob. So either both stay below the geom layers (default panel.ontop = FALSE) or both go over them (panel.ontop = TRUE). Here are two possible workarounds for consideration:

  1. Use panel.ontop = TRUE, keep panel background transparent, and colour the entire plot background with your desired colour instead:
ggplot(df) +
  geom_rect(xmin=2,xmax=3,ymin=-Inf,ymax=Inf,fill="gray") +
  geom_line(aes(x=t,y=a),colour="red") +
  geom_line(aes(x=t,y=b),colour="blue")+

  theme(plot.background = element_rect(fill = "lightblue"), # change this line to plot.background
        panel.background = element_rect(fill = NA),
        panel.ontop = TRUE,
        panel.grid.minor=element_line(colour="hotpink",size=0.5),
        panel.grid.major=element_line(colour="green",size=0.5)) 

1st approach

  1. Leave default panel.ontop = FALSE, then hack the underlying grobs to move the grid lines to the top:
p <- ggplot(df) +
  geom_rect(xmin=2,xmax=3,ymin=-Inf,ymax=Inf,fill="gray") +
  geom_line(aes(x=t,y=a),colour="red") +
  geom_line(aes(x=t,y=b),colour="blue")+

  theme(panel.background = element_rect(fill = "lightblue"),
        panel.grid.minor=element_line(colour="hotpink",size=0.5),
        panel.grid.major=element_line(colour="green",size=0.5)) 

# convert from ggplot object to grob object
gp <- ggplotGrob(p) 

# make a copy of the grob that contains the panel background (first child) & panel grids (all subsequent children),
# then drop the panel background grob (i.e. only keep the grobs for grids)
panel.grid.grob <- gp$grobs[[6]]$children[[1]]
panel.grid.grob$children[[1]] <- zeroGrob()

# leave only the panel background grob in its original slot
gp$grobs[[6]]$children[[1]] <- gp$grobs[[6]]$children[[1]]$children[[1]]

# add back grid grobs on top of panel
gp <- gtable::gtable_add_grob(gp, panel.grid.grob, t = 7, l = 5)

# plot result
grid::grid.draw(gp)

2nd approach

The first approach is simpler to implement, but the panel background colour would 'spill out' over the entire plot, including the axes labels. You may find one or the other more useful, depending on your use case.

Upvotes: 4

Related Questions