Search code examples
rggplot2annotationscoordinatesscale

ggplot2 custom annotation containing bitmap, with reversed y scale


In ggplot, I want to use a custom annotation to set up a bitmap (like the one at the very end of this post) as background to a plot. Other points are that

  1. The background image should fill the entire plot
  2. the data plotted over the image are points whose positions are in pixel units relative to the background image.
  3. Ideally, I'd like the y scale reversed, running from minimum value at top to maximum at bottom. The reason I want the y scale reversed is that the data to be plotted over the image has the origin in the upper left corner. I know I could reflect the data, but I'd rather not.
  4. I'm using R v3.2.3 and ggplot v2.0.0.

Without reversing the y scale, things seem straightforward enough. This works (as suggested here, for example):

require(ggplot2) ## packages png and grid are also needed.

myurl <- "https://i.sstatic.net/pbmsi.png"
tmp <- tempfile()
download.file(myurl,tmp,mode="wb")
bg <- png::readPNG(tmp)
file.remove(tmp) # cleanup

ysize <- dim(bg)[1]
xsize <- dim(bg)[2]
bg <- grid::rasterGrob(bg)

D <- data.frame(x=seq(10, (xsize-10), length.out=10),
              y=seq(10, (ysize-10), length.out=10))

## upright y scale  
p <- ggplot(data=D, aes(x=x, y=y)) + geom_blank()
p <- p + annotation_custom(bg,                      
                         xmin=0, ymin=0,
                         xmax=xsize, ymax=ysize)
p <- p + coord_equal()
p <- p + scale_x_continuous(limits=c(0,xsize), 
                          expand=c(0,0))
p <- p + scale_y_continuous(limits=c(0, ysize), 
                          expand=c(0,0))
p <- p + geom_point(size=5, color="blue", alpha=.6)
p

Figure 1

The problem is that I'm having no end of trouble finding a solution that gets the reversed y scale: The closest I've been able to get is with the following, which is definitely not what I would expect to need. Note the doubly reversed y scale and the negative ymin in annotation_custom(). I've tried many variations and can't seem to come up with anything more reasonable.

# reversed y  
p <- ggplot(data=D, aes(x=x, y=y)) + geom_blank()
p <- p + annotation_custom(bg,                      
                         xmin=0, ymin=-ysize,
                         xmax=xsize, ymax=0)
p <- p + coord_equal()
p <- p + scale_x_continuous(limits=c(0,xsize), 
                          expand=c(0,0))
p <- p + scale_y_continuous(limits=c(0, ysize), 
                          expand=c(0,0), 
                          trans="reverse")
p <- p + scale_y_reverse(expand=c(0,0))
p <- p + geom_point(size=5, color="blue", alpha=.6)
p

Figure 2 Also note that the range of the y scale seems off (at least, slightly diminished relative to the previous plot).

Am I asking too much of ggplot, or maybe just doing something dumb? Any advice?

Figure 3


Solution

  • So here's the solution (thanks to aosmith):

    require(ggplot2) ## packages png and grid are also needed.
    
    myurl <- "https://i.sstatic.net/pbmsi.png"
    tmp <- tempfile()
    download.file(myurl,tmp,mode="wb")
    bg <- png::readPNG(tmp)
    file.remove(tmp) # cleanup
    
    ysize <- dim(bg)[1]
    xsize <- dim(bg)[2]
    bg <- grid::rasterGrob(bg)
    
    D <- data.frame(x=seq(10, (xsize-10), length.out=10),
              y=seq(10, (ysize-10), length.out=10))
    
    # reversed y  
    p <- ggplot(data=D, aes(x=x, y=y)) + geom_blank()
    p <- p + annotation_custom(bg,                      
                         xmin=0, ymin=-ysize,
                         xmax=xsize, ymax=0)
    p <- p + coord_equal()
    p <- p + scale_x_continuous(limits=c(0,xsize), 
                          expand=c(0,0))
    p <- p + scale_y_continuous(limits=c(0, ysize), 
                          expand=c(0,0), 
                          trans="reverse")
    p <- p + geom_point(size=5, color="blue", alpha=.6)
    p
    

    And here's the result:

    enter image description here

    Seems like I should have been able to find that myself, but, you know....