Search code examples
rimage-processingplotggplot2raster

Plotting multiple layers with geom_raster() or geom_tile or geom_rect()


I have a data.frame of b/w raster image data that I'm using as a background/base layer. It has three columns: x, y, z. Where x and y are the respective coordinates and z is a continuous value defining a 512 x 512 image. I set this background fill to a black-white gradient and plot using geom_raster() in ggplot2 and then save the image to a PDF file.

### MWE of base layer
# original 512x512 data is read in from a file in matrix format, so here's a mocked up example of same:
h = matrix(data = rep(c(1:512), 512), byrow = T, nrow = 512, ncol = 512)

# convert to data.frame with all the rows of z-data in first column
h = data.frame(z = as.vector(t(h)))

# create equally spaced 512x512 x and y pixel coordinate vectors
x = c(-255:256)
y = c(-255:256)

# add x and y coordinate vectors to the data.frame
h$x = rep(t(x), 512)
h$y = rep(t(-y), each=512)

# plot the data
ggplot() + 
  coord_fixed() +
  geom_raster(data=h, aes(x,y,fill=z)) +
  scale_fill_gradient(low="black", high="white", na.value="transparent")

# save to png   
ggsave("testbaseplot.png", dpi=300)

This gives the desired result in the saved file.

BasePlot


Next, on top of that base layer I plot one or more additional layers. These layers also have x, y, and r. Where r is either a continuous or discrete value depending on the region. For example, a layer defining region borders would label the discrete regions, whereas a layer defining topology in the region would define the topology.

Ideally, I'd like to use geom_raster() twice, once for each layer (base, overlay) and use a different aes(fill=) on each layer, with a different scale_fill_*() for each layer as well. However, attempting this results in errors:

# MWE of mock overlay = 4 square labeled regions
# create z-data for some example shapes
r = data.frame(r = as.vector(c(rep(rep(c('a','b'),each=100),100),
                               rep(rep(c('c','d'),each=100),100))))

# create x and y coordinates 
r$x = rep(c(-99:100),200)
r$y = rep(c(-99:100),each=200)

# plot base layer and 1 overlay layer, both as geom_raster
ggplot() + 
  coord_fixed() +
  geom_raster(data=h, aes(x,y,fill=z)) +
  scale_fill_gradient(low="black", high="white", na.value="transparent") + 
  geom_raster(data=r, aes(x,y,fill=r)) +
  scale_fill_manual(values=c("red","white","blue","yellow"))

Error Message:

Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale. Error: Continuous value supplied to discrete scale


I thought maybe I could do one geom_raster() with aes(fill=z) and one with the aes(colour=r) and scale_colour_manual but geom_raster() does not recognize the aes(colour=) option, so I used geom_tile(aes(colour=r)) instead:

# plot looks fine
ggplot() + 
  coord_fixed() +
  geom_raster(data=h, aes(x,y,fill=z)) +
  scale_fill_gradient(low="black", high="white", na.value="transparent") + 
  geom_tile(data=r, aes(x,y,colour=r), fill="transparent") +
  scale_colour_manual(values=c("red","white","blue","yellow"))

# saved file does not
ggsave("testlayerplot.png", dpi=300)

The plot looks fine in RStudio's previewer window, but when saved as a file (pdf, png, etc.), the tile layer has a grid of unwanted lines on it.
Layer Plot

I believe these lines are showing up because the geom_tile default fill is grey. Is this due to the file format? Due to the default grey fill of geom_tile ignoring the fill="transparent" option? Some other reason?


I feel like I am approaching this wrong. I am probably converting the original raster data in matrix format into x,y,z data.frame format unnecessarily... and because I understand data.frame format better.

First part: Can I use ggplot to plot one raster over another without getting the unwanted lines? If so, how? Can I also add an alpha=.5 transparency to the overlay layer?

Second part: Is there a way that I can plot the original raster matrix format without converting to x,y,z data.frame format first? If so, how? Can I set transparency on the overlay layer?


Solution

  • With a little work, you can use annotate to create the background without mapping, so the fill scales remains available for the center:

    h$z <- (h$z - min(h$z)) / diff(range(h$z))
    ggplot() + 
      coord_fixed() +
      annotate(
        geom = 'raster', 
        x = h$x, 
        y = h$y, 
        fill = scales::colour_ramp(c("black", "white"))(h$z)
      ) +
      geom_raster(aes(x, y, fill = r), data = r) +
      scale_fill_manual(values=c("red", "white", "blue", "yellow"))
    

    enter image description here