Marius Hofert
2012-Oct-19 10:18 UTC
[R] grid(Base): How to avoid "Figure region too small and/or viewport too large" by specifying 'relative' units?
Dear grid-expeRts, The goal: I would like to construct a plot (matrix) with grid and gridBase, which consists of four "sub-plots". The sub-plots should have a square plotting region as one would force with par(pty="s") in base graphics. The problem: I don't get a square plotting region, not even by specifying pty="s" in par(). Indeed, if you display the grid layout by commenting in "grid.show.layout(gl)" below, you'll see that the grid layout is fine, but the plot does not seem to respect the layout measurements. The initial quick-and-dirty hack: Use absolute measurements, i.e., specify all units in inches. This worked perfectly fine for me initially. However, when the device width and height are not set accordingly (and one can never know what others specify here -- indeed the problem arose this way), this produces errors of type "Error in gridPLT() : Figure region too small and/or viewport too large". I also found this issue in this post (https://stat.ethz.ch/pipermail/r-help/2008-December/181993.html), but I am wondering what's the correct approach towards this problem / how can one specify "relative" units but guarantee that the grid layout is respected when plotting is done? [We use this plot in a quite complicated setup and the quick solution to specify inches and a large enough width/height for the device fails.] Cheers, Marius require(grid) require(gridBase) ## setup strg <- LETTERS[1:2] # row variables t <- c(0.2, 0.8) # column variables ## plot variables (spaces) pspc <- c(3,3) spc <- c(0.3, 0.3) axlabspc <- c(1.2, 0.75) labspc <- c(0.3, 0.3) ## save plot settings par. <- par(no.readonly=TRUE) ## set up the grid layout nx <- 2 # number of sub-plots per column nx. <- 5 # number of grid rectangles per column ny <- 2 # number of sub-plots per row ny. <- 5 # number of grid rectangles per row plot.new() # start (empty) new page with 'graphics' gl <- grid.layout(nx., ny., # "2 x 2" grid together with spaces => 5 x 5 grid widths=c(axlabspc[1], rep(c(pspc[1], spc[1]), nx-1), pspc[1], labspc[1]), heights=c(labspc[2], rep(c(pspc[2], spc[2]), ny-1), pspc[2], axlabspc[2])) ## grid.show.layout(gl) # display the layout; use this to see that the sub-plots do not match the layout specification pushViewport(viewport(layout=gl)) # use this layout in a viewport ## generate dummy data to plot in the four sub-plots n <- 10 x <- array(NA, dim=c(nx, ny, n)) for(i in 1:2) for(j in 1:2) x[i,j,] <- rep(i,n)+rep(j,n)+runif(n) ## go through the "panels" set.seed(1) for(i in 1:nx) { # rows i. <- 2*i # column index in layout (for jumping over gaps) yran <- range(x[i,,]) # for forcing the same y-axis within a row for(j in 1:ny) { # columns j. <- 2*j # row index in layout (for jumping over gaps) pushViewport(viewport(layout.pos.row=i., layout.pos.col=j.)) ## plot par(plt=gridPLT()) # start a 'graphics' plot par(new=TRUE) # always do this before each new 'graphics' plot plot(range(1:n), yran, type="n", ann=FALSE, axes=FALSE) # set up coordinate axes points(1:n, x[i,j,], type="b") # actual plot grid.rect() ## axes if(i==nx) axis(1) # x axis if(j==1) axis(2) # y axes upViewport() ## column labels if(i==1){ pushViewport(viewport(layout.pos.row=1, layout.pos.col=j.)) grid.rect() grid.text(t[j], x=0.5, y=0.5) upViewport() } ## row labels if(j==2){ pushViewport(viewport(layout.pos.row=i., layout.pos.col=nx.)) grid.rect() grid.text(strg[i], x=0.5, y=0.5, rot=-90) upViewport() } } } ## x axis label pushViewport(viewport(layout.pos.row=ny., layout.pos.col=2:(ny.-1))) grid.text(expression(gamma), y=unit(0.5, "null")) upViewport() ## y axis label pushViewport(viewport(layout.pos.row=2:(nx.-1), layout.pos.col=1)) grid.text(expression(f[gamma]), rot=90, x=unit(0.5, "null")) upViewport() ## restore plot settings par(par.)
Marius Hofert
2012-Oct-20 06:10 UTC
[R] grid(Base): How to avoid "Figure region too small and/or viewport too large" by specifying 'relative' units?
In the meanwhile, I found a more minimal example which shows the problem (just change 'inch' to TRUE to see the difference): require(grid) inch <- FALSE # TRUE d <- if(inch) 5 else 1 pspc <- d*c(0.3, 0.3) # width, height of panels spc <- d*c(0.05, 0.05) # width, height of space axlabspc <- d*c(0.1, 0.1) # width y label, height x label labspc <- d*c(0.05, 0.05) # width label boxes, height label boxes par. <- par(no.readonly=TRUE) gl <- grid.layout(5, 5, default.units=if(inch) "inches" else "npc", widths=c(axlabspc[1], pspc[1], spc[1], pspc[1], labspc[1]), heights=c(labspc[2], pspc[2], spc[2], pspc[2], axlabspc[2])) grid.show.layout(gl) pushViewport(viewport(layout=gl)) for(i in 1:2) { for(j in 1:2) { pushViewport(viewport(layout.pos.row=2*i, layout.pos.col=2*j, name="foo")) grid.rect() upViewport() } } par(par.)