3

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.stack.imgur.com/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

Community
  • 1
  • 1
Dave Braze
  • 441
  • 3
  • 14
  • 1
    To make it easier for people to try out solutions, please make your example reproducible by adding a PNG file which we have access to, e.g. `img <- readPNG(system.file("img", "Rlogo.png", package="png"))` – Henrik Jan 15 '16 at 06:17
  • Ok. added some bits toward making the example more reproducible, with a nod to http://stackoverflow.com/questions/17918330/how-to-directly-read-an-image-file-from-a-url-address-in-r – Dave Braze Jan 15 '16 at 11:04
  • In terms of y-scale range: your second y scale overwrites the first one (you should get a message about this in the console), so your second graph doesn't have the defined y-axis limits like your first graph does. You'd need to add `limits = c(ysize, 0)` to one of your y scales (you can get rid of the other one). – aosmith Jan 15 '16 at 15:55
  • @aosmith, that's true. But if I comment out the call to scale_y_reverse() in the "reversed y" code block, I end up with an output in which the background bitmap transposed (flipped in both x and y), the x label and xtics are at the top of the plot and _inside_ the plotting region, the y label is correctly placed but there are no ytics at all, and the points don't show up at all. I suspect that latter may be because the y range is displaced somehow. All of this is rather unexpected, at least to me. – Dave Braze Jan 15 '16 at 17:39
  • Did you change your limits to what I suggested? Note the reversal... – aosmith Jan 15 '16 at 17:43
  • Ack! I had not. But following your suggestion carefully gives me the output I want. Thank you! I'll post an answer. – Dave Braze Jan 15 '16 at 17:53

1 Answers1

2

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

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

myurl <- "https://i.stack.imgur.com/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....

Dave Braze
  • 441
  • 3
  • 14