2
votes

I am trying to overlay a multi-panel plot (using base graphics in R) over a single panel plot of a map. The multi-panel plot looks like this:

multi-panel plot

...and I wish to overlay this on a map (plotted using the maps package).

My code looks like this:

# Blank plot for map
plot(1, 1, type="n", xlab="", ylab="", axes=F,
     xlim=c(-39,-35.5),ylim=c(-55,-54))
# Plot map
map("worldHires", regions="Falkland Islands:South Georgia",col="#BFBFBF",
  fill=F, add=T, bg="#7F7F7F", lwd=0.05) 
# Start overplotting
par(new=T)
par(mfrow=c(2,3))
par(mar=c(0,0,0,0),oma=c(5,7,1,0))
hist(~yy$l.class[yy$strat=="SR"], breaks=classes, main="",xlab="",ylab="",
  col=cols[1], border=cols[1], probability=T, ylim=c(0,0.18),
  bg=rgb(1,1,1,0))

However, once I plot this first histogram, I get the following:

plot fail

As you can see, the first histogram is instead plotted into the last panel of the multi-panel layout. As I attempt to then plot further histograms, a new device is opened.

I assume this is something to do with my par() calls after plotting the map? Any advice?

2

2 Answers

2
votes

Without a reproducible question, I'll show one way to do it with generic data. You'll have to adapt it to your work.

Multi-plot can be done using par(mfrow=c(2,3)), and though it is simple, it is the most restrictive. Other options include layout(...) (which won't work for this, I think) and par(fig=...) (which is what I use here). A reasonable starting reference is here.

I'll start with your map (NB: please include required libraries with your code):

library(maps)
library(mapdata)
par(fig=c(0,1,0,1))                     # force full-device plot
plot(1, 1, type="n", xlab="", ylab="", axes=F,
     xlim=c(-39,-35.5),ylim=c(-55,-54))
map("worldHires", regions="Falkland Islands:South Georgia",col="#BFBFBF",
    fill=F, add=T, bg="#7F7F7F", lwd=0.05)

Next I'll set up a matrix to be used for defining the plot region, mimicking the arrangement you defined using par(mfrow=c(2,3)):

xs <- seq(0, 1, len=4)
ys <- seq(0, 1, len=3)
m <- merge(cbind(head(xs, n=-1), tail(xs, n=-1)),
           cbind(head(ys, n=-1), tail(ys, n=-1)),
           by=NULL)
##        V1.x      V2.x V1.y V2.y
## 1 0.0000000 0.3333333  0.0  0.5
## 2 0.3333333 0.6666667  0.0  0.5
## 3 0.6666667 1.0000000  0.0  0.5
## 4 0.0000000 0.3333333  0.5  1.0
## 5 0.3333333 0.6666667  0.5  1.0
## 6 0.6666667 1.0000000  0.5  1.0

par(fig=...) takes the left and right (x) and bottom and top (y) percentages. The first row of m says that the next plot will include from 0 to 33% horizontally and 0 to 50% vertically of the screen (i.e., the bottom left corner). The first plot call does not strictly need a par(fig=...) call, but I like to have it there so that I reset the plot layout when I redo the plot. It should either omit new=TRUE or use new=FALSE to be explicit (arguably a good thing at times like this).

Next I'll just throw in some charts. This part is my contrived part, but it shows how it's being used. The programmatic definition of m and its use below is not completely necessary; you can easily define each par(fig=...) call manually. Regardless, the use of a simple 2x3 grid of plots is also unnecessary and this method allows for placing the histograms in meaningful locations on the map. (This can obviously be done programmatically but is completely up to you and your data.)

columns <- c('mpg', 'hp', 'drat', 'wt', 'qsec')
for (i in 1:5) {
     par(fig=unlist(m[i,]), new=TRUE)
     col <- names(mtcars)[i]
     hist(mtcars[[col]], col=1+i, main=col)
}

enter image description here

2
votes

The most straightforward solution is to manually set par(mfg = c(1,1)). This tells R to place the next (sub)plot into the first slot of an mfrow-induced layout on the current page.1

So, for example, you can just do something like this:

library(maps)
library(mapdata)

## Set up the background map
plot.new()
par(fig = c(0,1,0,1))
plot.window(xlim=c(-39,-35.5),ylim=c(-55,-54))
map("worldHires", regions="Falkland Islands:South Georgia", col="#BFBFBF", add=TRUE)

## Set up a grid of subplots areas
par(mfrow = c(2, 3), mfg = c(1, 1))          ## **Key is to set mfg=c(1,1)**

## Fill grid with subplots
hist(mtcars[["mpg"]],  main="mpg",  col=2)
hist(mtcars[["hp"]],   main="hp",   col=3)
hist(mtcars[["cyl"]],  main="cyl",  col=4)
plot.new()                                   ## Skip plotting into this cell
hist(mtcars[["disp"]], main="dip",  col=5)
hist(mtcars[["drat"]], main="drat", col=6)

enter image description here

1 You need to set par(mfg=...) this explicitly because, for an m-by-n layout, R's default behavior is to set par(mfg=c(m,n)); that sets things up so that the next plot will go into the first slot of a layout on the next page -- not what you want in this case.