19

First post here, I hope I'm observing website etiquette. I couldn't find and answer on the site and I previously posted this to a ggplot2 specific group, but no solutions as yet.

Basically I am trying to overlay two rasters using ggplot2 and require the top one to be semi-transparent. I have a hillShade raster which is computed from an elevation data raster, and I wish to overlay the elevation raster onto the hillshade raster so the resulting plot doesn't look 'flat'. You can see what I mean in the reproducible R code below.

Using base graphics I can achieve the desired result and I have included an example in code below to make it clear what I mean, but I need to do this in ggplot2.

I can't get it to work in ggplot2. Combining the rasters makes the colours go funny (I can plot each one ok by itself). Can anyone help or point me in the right direction. Self contained, reproducible code example included below. (Sorry for the length, but I thought better to be clear).

#   Load relevant libraries
library(ggplot2)
library(raster)


#   Download sample raster data of Ghana from my Dropbox
oldwd <- getwd()
tmp <- tempdir()
setwd(tmp)
url1 <- "http://dl.dropbox.com/s/xp4xsrjn3vb5mn5/GHA_HS.asc"
url2 <- "http://dl.dropbox.com/s/gh7gzou9711n5q7/GHA_DEM.asc"
f1 <- file.path(tmp,"GHA_HS.asc")
f2 <- file.path(tmp,"GHA_DEM.asc")
download.file(url1,f1)  #File is ~ 5,655Kb
download.file(url2,f2)  #File is ~ 2,645Kb


#   Create rasters from downloaded files
hs <-  raster(f1)
dem <- raster(f2)


#   Plot with base graphics to show desired output
plot(hs,col=grey(1:100/100),legend=F)
plot(dem,col=rainbow(100),alpha=0.4,add=T,legend=F)


#   Convert rasters TO dataframes for plotting with ggplot
hdf <- rasterToPoints(hs); hdf <- data.frame(hdf)
colnames(hdf) <- c("X","Y","Hill")
ddf <- rasterToPoints(dem); ddf <- data.frame(ddf)
colnames(ddf) <- c("X","Y","DEM")


#   Create vectors for colour breaks
b.hs <- seq(min(hdf$Hill),max(hdf$Hill),length.out=100)
b.dem <- seq(min(ddf$DEM),max(ddf$DEM),length.out=100)


#   Plot DEM layer with ggplot()
p1 <- ggplot()+
    layer(geom="raster",data=ddf,mapping=aes(X,Y,fill=DEM))+
    scale_fill_gradientn(name="Altitude",colours = rainbow(100),breaks=b.dem)+
    scale_x_continuous(name=expression(paste("Longitude (",degree,")")),limits=c(-4,2),expand=c(0,0))+
    scale_y_continuous(name=expression(paste("Latitude (",degree,")")),limits=c(4,12),expand=c(0,0))+
    coord_equal()
print(p1)


#   Plot hillShade layer with ggplot()
p2 <- ggplot()+
    layer(geom="raster",data=hdf,mapping=aes(X,Y,fill=Hill))+
    scale_fill_gradientn(colours=grey(1:100/100),breaks=b.hs,guide="none")+
    scale_x_continuous(name=expression(paste("Longitude (",degree,")")),limits=c(-4,2),expand=c(0,0))+
    scale_y_continuous(name=expression(paste("Latitude (",degree,")")),limits=c(4,12),expand=c(0,0))+
    coord_equal()
print(p2)


#   Try to plot both together with transparency on the DEM layer
p3 <- ggplot(hdf)+
    geom_raster(aes(X,Y,fill=Hill))+
    scale_fill_gradientn(colours=grey(1:100/100),breaks=b.hs,guide="none")+
    scale_x_continuous(name=expression(paste("Longitude (",degree,")")),limits=c(-4,2),expand=c(0,0))+
    scale_y_continuous(name=expression(paste("Latitude (",degree,")")),limits=c(4,12),expand=c(0,0))+
    geom_raster(data=ddf,aes(X,Y,fill=DEM),alpha=I(0.4))+
    scale_fill_gradientn(name="Altitude",colours = rainbow(100),breaks=b.dem)+
    coord_equal()
 print(p3)


#   Cleanup downloaded files and return to previous wd
unlink(tmp,recursive=T)
setwd(oldwd)

My questions are as follows:

Q1: How can I make the layers of p3 look like they do when plotted with base graphics in the example above?

Q2: How can I more sensibly specify colour scales so I don't have a ridiculous legend on the RHS?

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
Simon O'Hanlon
  • 58,647
  • 14
  • 142
  • 184
  • I'm sure there would be some way to use `annotation_raster` - but my attempts have been fruitless thus far. The reason (as I see it) for your combined plot failing is the two calls to `scale_fill_gradientn` – mnel Jun 25 '12 at 01:28
  • that's the crux of it. it seems a `ggplot` can only have one `fill` aesthetic and one `colour` aesthetic. you can get something that sort of works by making the DEM layer a `geom_point` with `colour=DEM` and then using `scale_colour_gradientn` instead of `scale_fill_gradientn`, but it doesn't look as good as the base graphics way. – Matthew Plourde Jun 25 '12 at 03:45
  • Thanks for looking at my problem, both of you! @mplourde - I do think that ggplot2 can have one fill aesthetic per layer, but I think where there is alpha transparency it combines the colours using addidtive colour mixing where layers overlap. Somehow this mixing is different in ggplot2 than base graphics.I've been thinking about it and I think what I need to do is manually calculate the colours from the relevant greyscale value in the hillShade raster (I think this is chrominance in the hcl colour model) and the the hue from the DEM raster (I think this would be hue). – Simon O'Hanlon Jun 25 '12 at 07:59

2 Answers2

15

Q1: You can't have different fill scales on different layers. One workaround is to use the fill aesthetic for the DEM and the alpha aesthetic for hillshade. Unfortunately, geom_raster doesn't seem to use the alpha aesthetic the way I expected. You can get the same effect with geom_tile, it just takes longer:

ggplot(hdf) +
  geom_raster(data=ddf,aes(X,Y,fill=DEM)) +
  scale_fill_gradientn(name="Altitude",colours = rainbow(100),breaks=b.dem) +
  geom_tile(aes(X,Y,alpha=Hill), fill = "grey20") +
  scale_alpha(range = c(0, 0.5)) +
  scale_x_continuous(name=expression(paste("Longitude (",degree,")")),
    limits=c(-4,2),expand=c(0,0)) +
  scale_y_continuous(name=expression(paste("Latitude (",degree,")")),
    limits=c(4,12),expand=c(0,0)) +
  coord_equal() 

Q2: Check out ?guide_colorbar. It doesn't work very nicely with your 100 colour breaks, but with fewer it's pretty good.

ggplot(hdf)+
  geom_raster(data=ddf,aes(X,Y,fill=DEM))+
  scale_fill_gradientn(name="Altitude",colours = rainbow(20))+
  guides(fill = guide_colorbar()) +
  geom_tile(aes(X,Y,alpha=Hill), fill = "grey20") +
  scale_alpha(range = c(0, 0.5)) +
  scale_x_continuous(name=expression(paste("Longitude (",degree,")")),
    limits=c(-4,2),expand=c(0,0)) +
  scale_y_continuous(name=expression(paste("Latitude (",degree,")")),
    limits=c(4,12),expand=c(0,0)) +
  coord_equal() 

DEM plus hill shading and colorbar legend

5

The alpha in raster will be supported in the next version, so you can draw by:

ggplot(NULL, aes(X, Y)) + 
  geom_raster(data = ddf, aes(fill = DEM)) + 
  geom_raster(data = hdf, aes(alpha = Hill)) +
  scale_fill_gradientn(name="Altitude",colours = rainbow(20))+
  guides(fill = guide_colorbar()) +
  scale_alpha(range = c(0, 0.5), guide = "none") +
  scale_x_continuous(name=expression(paste("Longitude (",degree,")")), limits=c(-4,2),expand=c(0,0)) +
  scale_y_continuous(name=expression(paste("Latitude (",degree,")")), limits=c(4,12),expand=c(0,0)) +
  coord_equal()

anyway, very beautiful plot.

If you want to use this immediately, try installing from github:

library(devtools)
install_github("ggplot2", "kohske", "fix/geom-raster-alpha")

Note that geom_tile and geom_raster look different in some devices. Perhaps raster is better for your purpose.

enter image description here

kohske
  • 65,572
  • 8
  • 165
  • 155
  • Thanks! With this fix I can now plot exactly as I need to. Thank-you so much. Cheers, Simon. – Simon O'Hanlon Jun 26 '12 at 06:01
  • Anyone have any idea how you might adjust the colors in the legend to match the colors in the map now that it's masked with a grey? – Dominik Dec 04 '13 at 04:51
  • Had the problem of file-output (PDF) with"blurred" raster when using geom_raster() instead of geom_tile(). The latter fixed the issue. Thx for the comment. – Shadow Aug 12 '14 at 07:16