rasterVis - setting the bottom plots in the middle with levelplot - r

I am using the awesome rasterVis to create a panel with maps that have the same extent (i.e. same spatial coverage) but that show different features (i.e. each with its own legend).
This is what it looks like so far:
library(raster)
library(rasterVis)
library(RColorBrewer)
library(gridExtra)
# make-up data
r <- raster(system.file("external/test.grd", package="raster"))
s <- stack(r, r*2, r*3, r*4, r*5)
names(s) <- paste0("Field ",seq(1,5))
# pre-allocate list
l <- vector("list", length=nlayers(s))
# define theme for plots
my.theme <- rasterTheme(region=brewer.pal(11,'RdYlGn'))
# loop over stack layers to fill list
for (n in (1:nlayers(s))){
l[[n]] <- levelplot(s[[n]], margin=F, main=names(s[[n]]), par.settings=my.theme)
}
# plot combined maps
grid.arrange(l[[1]], l[[2]], l[[3]], l[[4]], l[[5]], ncol=3)
Note that the default positioning for the maps is:
a b c
d e
However, I would like to have a finer control on the positioning. Specifically, I would like to "center" the bottom two facets in order to distribute the void space more evenly on the sides of the plot.
In other words, the placement I am looking for would look like:
a b c
d e
How can I achieve this? I looked up the documentation for (grid.arrange), but couldn't find any option the would solve my problem.
Thanks in advance for any hints.

Here's an approach with cowplot::plot_grid inspired by this answer
library(cowplot)
top <- plot_grid(l[[1]], l[[2]], l[[3]],
ncol=3)
bottom <- plot_grid(NULL, l[[4]], NULL, l[[5]], NULL,
ncol=6, rel_widths=c(0.12,0.34,0.12,0.34,0.06))
plot_grid(top, bottom,
ncol=1, rel_heights=c(1,1))
You can modify the rel_widths in the bottom plot_grid call to get it just like you like.

You can provide a layout with 6 columns and use NA for the blanks,
library(gridExtra)
p <- ggplot()
grid.arrange(grobs = list(p,p,p,p,p),
layout_matrix = rbind(c(1,1,2,2,3,3), c(NA,4,4,5,5,NA)))

Related

Join Multiple R Rastervis levelplots

I have two rasterstacks, each with a common legend, that I want to put on a single plot with a space in between stacks. I want the raster images to be the same size within each block and across blocks. Is there an easy way to do this using Rastervis::levelplot. I believe the best way is too add a legend to grid.arrange object, as in this MWE
library(raster)
library(rasterVis)
s <- stack(
raster( matrix(runif(9),3,3)),
raster( matrix(runif(9),3,3)) )
gridExtra::grid.arrange(
levelplot(s, colorkey=FALSE, at=seq(0,1,length.out=11)),
levelplot(s, at=seq(0,1,length.out=11)),
ncol=2)
An even simpler solution would be to do this from within a single leveplot call
I think you are looking to do something like this:
levObj <- levelplot(s)
comb_levObj <- c(levObj, levObj, layout = c(4, 1), merge.legends = F)
print(comb_levObj)
But, a simpler solution would be:
s <- stack(s,s)
levelplot(s)

Arrange odd number of plots using rasterVis and gridExtra

I am trying to plot a panel with seven rasters using the levelplot function of the rasterVis package, combined with gridExtra's grid.arrange.
I almost get what I need by using the following code:
# load required packages
library(rasterVis)
library(gridExtra)
# load sample raster
f <- system.file("external/test.grd", package="raster")
r <- raster(f)
# create plots
p1 <- levelplot(r, xlab=NULL, ylab=NULL, margin=FALSE)
p2 <- levelplot(r*2, xlab=NULL, ylab=NULL, margin=FALSE,colorkey=FALSE)
# put plots in list
p.list <- list(p1,p2,p2,p2,p2,p2,p2)
# create layout
lay <- rbind(c(1,1,1),
c(2,3,4),
c(5,6,7))
# arrange plots
grid.arrange(grobs=p.list, layout_matrix=lay)
which yields this figure:
However, there are some things I still need to improve:
How to decrease the blank space between the plots in the bottom rows?
How to add a single, combined legend for the six bottom rasters, preferentially put to the bottom of the figure?
Is this possible to achieve using rasterVis and gridExtra? Is there any other approach that can be used?
The white space is a combination of lattice margin settings, but also the plots having a fixed aspect ratio (they can't be too close unless the device itself has a compatible aspect ratio).
With regards the legend, you could use draw.colorkey(), but from what I can tell you need to manually ensure that the colours match by passing them explicitly to both plots and the key.
# load required packages
library(rasterVis)
library(gridExtra)
# load sample raster
f <- system.file("external/test.grd", package="raster")
r <- raster(f)
my_theme <- rasterTheme(region = blues9)
# create plots
p1 <- levelplot(r, xlab=NULL, ylab=NULL, margin=FALSE, par.settings = my_theme)
leg <- p1$legend$right$args$key
p1$legend <- list()
p2 <- levelplot(r*2, xlab=NULL, ylab=NULL, margin=FALSE,colorkey=FALSE, par.settings = my_theme)
# put plots in list
p.list <- list(p1,p2,p2,p2,p2,p2,p2)
# create layout
lay <- rbind(c(NA,1,NA),
c(2,3,4),
c(5,6,7),
c(8,8,8))
leg$col <- my_theme$regions$col
legGrob <- draw.colorkey(key = leg, vp = grid::viewport(height=0.5))
# arrange plots
grid.arrange(grobs=c(p.list, list(legGrob)), layout_matrix=lay,
vp = grid::viewport(width=0.7,height=1))
(needless to say, facetting seems the better option by a wide margin)

Using a pheatmap in arrangeGrob

I'm attempting to plate two plots in the same .jpg using arrangeGrob().
I've only just started learning about grids and grobs and I think I know what the problem is: pheatmap is a grid object and containing grob objects, not allowing me to put it in arrangeGrob. Is this true?
Would I somehow need to put the qplot in a grid and the pheatmap in a grid and then put those grids in a new grid?
library(grid)
library(gridExtra)
library(pheatmap)
library(ggplot2)
hmdat=rbind(c(1,2,3),
c(3,4,5),
c(5,6,7))
hm=pheatmap(hmdat)
qp=qplot(1,1)
lm=rbind(c(1,2,2),
c(1,2,2))
jpeg("plots.jpg")
arrangeGrob(qp,hm, layout_matrix=lm)
dev.off()
The above code snippet runs just fine when using
arrangeGrob(qp,qp, layout_matrix=lm)
I'm not sure if you wanted to have 6 figures or you wanted to have two figures one with twice as wide as the other one (I tried to do minimum code change):
library("grid")
library("gridExtra")
library("pheatmap")
library("ggplot2")
hmdat=rbind(c(1,2,3),
c(3,4,5),
c(5,6,7))
hm <- pheatmap::pheatmap(hmdat)
qp <- qplot(1,1)
lm <- rbind(c(1,2,2),
c(1,2,2))
grid.arrange(grobs = list(qp,hm[[4]]), layout_matrix = lm)
which will give you:
The same way you can have multiple pheatmaps side-by-side:
library("grid")
library("gridExtra")
library("pheatmap")
hmdat <- rbind(c(1,2,3),
c(3,4,5),
c(5,6,7))
hm <- pheatmap::pheatmap(hmdat)
lm <- rbind(c(1,2),
c(3,3))
grid.arrange(grobs = list(hm[[4]],
hm[[4]],
hm[[4]]),
layout_matrix = lm)
As #hrbrmstr mentioned in the comment, you should use the 4th item in the pheatmap object. Also remember to provide grobs as list to the grid.arrange

How to plot additional raster with spplot?

I want to plot SpatialPolygonsDataFrame as a semi-transparent main object (with legend on the right), but I want to plot yet additional raster (hillshade) as a background - just to make nicer map. I would need something like:
spplot(polygons, sp.layout = list(list("raster", myRaster)))
but looking at ?spplot, it doesn't seem to be possible to specify the raster in sp.layout. I can't specify the raster as the main object, because the main object are the polygons dataFrame, whose value scale I want to plot in the legend on the right side.
How is it possible to plot an additional raster in spplot?
Here's one way to do it. There's probably a neater way to achieve it without plotting the polygon object twice, though...
library(sp)
library(rasterVis)
r <- raster(nrow=18, ncol=36)
r[] <- runif(ncell(r)) * 10
r[r > 8] <- NA
pol <- rasterToPolygons(r, function(x) x > 6)
spplot(pol) + levelplot(r) + spplot(pol)
Or alternatively:
library(latticeExtra)
spplot(pol) + spplot(r) + spplot(pol)
EDIT
As per the comment by #OscarPerpiñán, a better way to do this is:
spplot(pol) + as.layer(spplot(r), under = TRUE)

Concentric Circles like a grid, centered at origin

I would like to include a sequence of concentric circles as a grid in a plot of points. The goal is to give the viewer an idea of which points in the plot have approximately the same magnitude.
I created a hack to do this:
add_circle_grid <- function(g,ncirc = 10){
gb <- ggplot_build(g)
xl <- gb$panel$ranges[[1]]$x.range
yl <- gb$panel$ranges[[1]]$y.range
rmax = sqrt(max(xl)^2+max(yl)^2)
theta=seq(from=0,by=.01,to=2*pi)
for(n in 1:ncirc){
r <- n*rmax/ncirc
circle <- data.frame(x=r*sin(theta),y=r*cos(theta))
g<- g+geom_path(data=circle,aes(x=x,y=y),alpha=.2)
}
return(g+xlim(xl)+ylim(yl))
}
xy<-data.frame(x=rnorm(100),y=rnorm(100))
ggplot(xy,aes(x,y))+geom_point()
ggg<-add_circle_grid(ggplot(xy,aes(x,y))+geom_point())
print(ggg)
But I was wondering if there is a more ggplot way to do this. I also considered using polar coordinates but it does not allow me to set x- and y-limits in the same way.
Finally, I wouldn't mind little text labels indicating the radius of each circle.
EDIT
Perhaps this is asking too much but there are two other things that I would like.
The axis limits should stay the same (which can be done via ggplot_build)
Can this work with facets? As far as I can tell you would need to somehow figure out the facets if I want to add the circles dynamically.
set.seed(1)
xy <- data.frame(x=rnorm(100),y=rnorm(100))
rmax = sqrt(max(xy$x)^2+max(xy$y)^2)
theta=seq(from=0,by=.01,to=2*pi)
ncirc=10
dat.circ = do.call(rbind,
lapply(seq_len(ncirc),function(n){
r <- n*rmax/ncirc
data.frame(x=r*sin(theta),y=r*cos(theta),r=round(r,2))
}))
rr <- unique(dat.circ$r)
dat.text=data.frame(x=rr*cos(30),y=rr*sin(30),label=rr)
library(ggplot2)
ggplot(xy,aes(x,y))+
geom_point() +
geom_path(data=dat.circ,alpha=.2,aes(group=factor(r))) +
geom_text(data=dat.text,aes(label=rr),vjust=-1)
How about this with ggplot2 and grid:
require(ggplot2)
require(grid)
x<-(runif(100)-0.5)*4
y<-(runif(100)-0.5)*4
circ_rads<-seq(0.25,2,0.25)
qplot(x,y)+
lapply(circ_rads,FUN=function(x)annotation_custom(circleGrob(gp=gpar(fill="transparent",color="black")),-x,x,-x,x))+
geom_text(aes(x=0,y=circ_rads+0.1,label=circ_rads)) + coord_fixed(ratio = 1)

Resources