Generate Figure Wide Axes for Figure with Multiple Plots (grid) - r

I have an analysis in which I varying parameters of a model, and would like to generate a well-labeled figure showing the effects of various combinations of settings. I can generate the actual plots without issue, and arrange them in a grid. Now I would like to add axes which refer not to the data but to the parameter values that I am varying. Here is a simple example of what I am trying to do.
library(lattice)
library(gridExtra)
dat <- rnorm(1000, 0, 1)
# these are my parameters
scales <- c(0.1, 0.5, 1, 10, 15)
adds <- c(1, 2, 3, 4, 5)
# generate data to be plotted
revData <- list()
ptr <- 1
for (ii in 1:length(scales)){
for (jj in 1:length(adds)){
revData[[ptr]] <- (dat * scales[[ii]]) + adds[[jj]]
ptr <- ptr + 1
}
}
# generating plots
plotList <- lapply(revData, function(xx){
xyplot(xx ~ 1:1000)
})
names(plotList) <- sprintf("N%s", 1:length(plotList))
plotList <- c(plotList, list(ncol=length(scales)))
do.call(grid.arrange, plotList)
This produces the following figure:
What I would like to do now is generate X and Y axes for this set of plots, in which the X-axis refers to the scales variable and has the values listed, and similarly a Y-axis which corresponds to the adds value. How do I do this?

Another answer using grid package. First create a grid of viewports, then at each iteration of creating the data just plot it. This maybe gives a bit more control over spacing.
library(lattice)
library(grid)
# Data
dat <- rnorm(1000, 0, 1)
# these are my parameters
scales <- c(0.1, 0.5, 1, 10, 15)
adds <- c(1, 2, 3, 4, 5)
Create grid layout - this lets you look at the plot grid: 6 by 6 with the first column and last row used for the additional parameter labels.
grid.show.layout(grid.layout(6, 6,
widths = unit(c(2, rep(1,5)), c("lines", rep("null",5))) ,
heights = unit(c(rep(1,5),2), c(rep("null",5), "lines"))))
Start plot
grid.newpage()
pushViewport(viewport(layout=grid.layout(6, 6,
widths = unit(c(2, rep(1,5)), c("lines", rep("null",5))) ,
heights = unit(c(rep(1,5),2), c(rep("null",5), "lines")))))
# generate data and plot
for (ii in 1:length(scales)) {
for (jj in 1:length(adds)) {
revData <- (dat * scales[ii]) + adds[jj]
# plot (top to bottom / left to right)
pushViewport(viewport(layout.pos.row=jj, layout.pos.col=ii+1))
plot(xyplot(revData ~ 1:1000 , xlab=NULL , ylab=NULL) , newpage=FALSE)
upViewport(1)
}
}
add "scales" labels to x-axis
pushViewport(viewport(layout.pos.row=6, layout.pos.col=2:6 ))
grid.text(scales, x=seq(0.1,0.9 ,length=5), y=0.8 , hjust=0.2,
gp=gpar(col="red" , fontsize=14))
grid.text("Scales" , x=0.5 , y=0.3 , gp=gpar(fontsize=20))
grid.lines(y=1 , gp=gpar(col="red"))
upViewport(1)
add "adds" labels to y-axis
pushViewport(viewport(layout.pos.row=1:5, layout.pos.col=1 ))
grid.text(rev(adds),hjust=0.4 , x=0.8, y=seq(0.1,0.9 ,length=5), rot=90,
gp=gpar(col="red" , fontsize=14))
grid.text("Adds" , x=0.3 , y=0.5 , rot=90, gp=gpar(fontsize=20))
grid.lines(x=1 , gp=gpar(col="red"))
EDIT
I added axis titles using grid.text and tweaking the x & y slightly. A better way would be to add another row and column to the grid and use this for the labels. An example grid is below - you will need to tweak another couple of things in the code but is not difficult.
grid.show.layout(grid.layout(7,7,
widths = unit(c(2, 2,rep(1,5)), c("lines", "lines",rep("null",5))) ,
heights = unit(c(rep(1,5),2,2), c(rep("null",5), "lines","lines"))))
You could use the first column and last row for axis titles, the second column and second last row for axis labels and line, leaving the rest of the grid for the plot.

Not sure this is what you are looking for, but you can add a textGrob underneath and to the left of the plot to indicate what value of "scales" or "adds" each plot represents. The sub and left arguments of grid.arrange can be used.
do.call(grid.arrange,
c(plotList ,
list(
sub = textGrob(paste0("scales_",scales),
x=unit(seq(0.05,0.85,length=5), "npc"),
hjust=0, vjust=0 ,
gp=gpar(col="red", fontsize=14) ),
left = textGrob(paste0("adds_",rev(adds)),
y=unit(seq(0.05,0.85,length=5), "npc"),
hjust=0, vjust=1 , rot = 90,
gp=gpar(col="red", fontsize=14) ))))
You will need to play about with the x and y coordinates.
This produces the plot:

Related

How do I add multiple subplots into a multirow figure in R?

i need to overlay multiple subplots onto a single plot which is already contained inside a multirow figure (see image)
the reason why i need subplots instead of screen layout is because the figure will be possibly multicolumn, also (a 5 by 3 plot, in total)
there are packages which assist in doing subplots, but they break when you use multirow figures, and sequential subplots, except the first one, are rendered next to the overall figure border, not relative to the current row/column plot borders
i understand large packages such as ggplot2 allow this relatively easily, but base R plots are highly preferable
UPD:
the minimum reproducible example depicting the problem is here:
require(Hmisc)
COL.1 <- c('red','orange','yellow'); COL.2 <- c('blue','green','turquoise')
SUBPLOT.FUN <- function(COL) {plot(rnorm(100), type='l', col=COL)}
PLOT.FUN <- function(i) {
plot(rnorm(100),ylim=c(-1,1))
subplot(SUBPLOT.FUN(COL.1[i]), 100,1, vadj=1,hadj=1,pars=list(mfg=c(1,i)))
subplot(SUBPLOT.FUN(COL.2[i]), 100,-1,vadj=0,hadj=1,pars=list(mfg=c(1,i)))
}
plot.new(); par(mfrow=c(1,3))
for (i in 1:3) {
PLOT.FUN(i)
}
which looks like that:
while what is required is shown on the first image (meaning, EACH of the three plots must contain 3 subplots in their respective locations (along the right border, arranged vertically))
N.B. either the figure is multirow or multicolumn (as depicted) does not matter
Something like this? Inspired in this R-bloggers post.
# reproducible test data
set.seed(2022)
x <- rnorm(1000)
y <- rbinom(1000, 1, 0.5)
z <- rbinom(1000, 4, 0.5)
# save default values and prepare
# to create custom plot areas
old_par <- par(fig = c(0,1,0,1))
# set x axis limits based on data
h <- hist(x, plot = FALSE)
xlim <- c(h$breaks[1] - 0.5, h$breaks[length(h$breaks)] + 2)
hist(x, xlim = xlim)
# x = c(0.6, 1) right part of plot
# y = c(0.5, 1) top part of plot
par(fig = c(0.6, 1, 0.5, 1), new = TRUE)
boxplot(x ~ y)
# x = c(0.6, 1) right part of plot
# y = c(0.1, 0.6) bottom part of plot
par(fig = c(0.6, 1, 0.1, 0.6), new = TRUE)
boxplot(x ~ z)
# put default values back
par(old_par)
Created on 2022-08-18 by the reprex package (v2.0.1)

Left-aligning ggplot when saved while using a fixed aspect ratio

I'm building a custom ggplot theme to standardize the look & feel of graphs I produce. The goal is more complex than this minimal example, so I'm looking for a general solution. I have a few key goals:
I want all graphs to export at the same size (3000 pixels wide, 1500 pixels high).
I want to control the aspect ratio of the plot panel itself.
I want to use textGrobs to include figure numbers.
I want the image to be left-aligned
The challenge I'm facing is that when combining these two constraints, the image that gets saved centers the ggplot graph within the window, which makes sense as a default, but looks bad in this case.
I'm hoping there's a general solution to left-align the ggplot panel when I export. Ideally, this will also work similarly for faceted graphs.
It seems that something should be possible using one of or some combination of the gridExtra, gtable, cowplot, and egg packages, but after experimenting for a few hours I'm at a bit of a loss. Does anybody know how I can accomplish this? My code is included below.
This is the image that gets produced. As you can see, the caption is left-aligned at the bottom, but the ggplot itself is horizontally centered. I want the ggplot graph left-aligned as well.
Graph output: https://i.stack.imgur.com/5EM2c.png
library(ggplot2)
# Generate dummy data
x <- paste0("var", seq(1,10))
y <- LETTERS[1:10]
data <- expand.grid(X=x, Y=y)
data$Z <- runif(100, -2, 2)
# Generate heatmap with fixed aspect ratio
p1 <- ggplot(data, aes(X, Y, fill= Z)) +
geom_tile() +
labs(title = 'A Heatmap Graph') +
theme(aspect.ratio = 1)
# A text grob for the footer
figure_number_grob <- grid::textGrob('Figure 10',
x = 0.004,
hjust = 0,
gp = grid::gpar(fontsize = 10,
col = '#01A184'))
plot_grid <- ggpubr::ggarrange(p1,
figure_number_grob,
ncol = 1,
nrow = 2,
heights = c(1,
0.05))
# save it
png(filename = '~/test.png', width = 3000, height = 1500, res = 300, type = 'cairo')
print(plot_grid)
dev.off()
I was able to find a solution to this that works for my needs, though it does feel a bit hacky.
Here's the core idea:
Generate the plot without a fixed aspect ratio.
Split the legend from the plot as its own component
Use GridExtra's arrangeGrob to combine the plot, a spacer, the legend, and another spacer horizontally
Set the width of the plot to some fraction of npc (normal parent coordinates), in this case 0.5. This means that the plot will take up 50% of the horizontal space of the output file.
Note that this is not exactly the same as setting a fixed aspect ratio for the plot. If you know the size of the output file, it's close to the same thing, but the size of axis text & axis titles will affect the output aspect ratio for the panel itself, so while it gets you close, it's not ideal if you need a truly fixed aspect ratio
Set the width of the spacers to the remaining portion of the npc (in this case, 0.5 again), minus the width of the legend to horizontally center the legend in the remaining space.
Here's my code:
library(ggplot2)
# Generate dummy data
x <- paste0("var", seq(1,10))
y <- LETTERS[1:10]
data <- expand.grid(X=x, Y=y)
data$Z <- runif(100, -2, 2)
# Generate heatmap WITHOUT fixed aspect ratio. I address this below
p1 <- ggplot(data, aes(X, Y, fill= Z)) +
geom_tile() +
labs(title = 'A Heatmap Graph')
# Extract the legend from our plot
legend = gtable::gtable_filter(ggplotGrob(p1), "guide-box")
plot_output <- gridExtra::arrangeGrob(
p1 + theme(legend.position="none"), # Remove legend from base plot
grid::rectGrob(gp=grid::gpar(col=NA)), # Add a spacer
legend, # Add the legend back
grid::rectGrob(gp=grid::gpar(col=NA)), # Add a spacer
nrow=1, # Format plots in 1 row
widths=grid::unit.c(unit(0.5, "npc"), # Plot takes up half of width
(unit(0.5, "npc") - legend$width) * 0.5, # Spacer width
legend$width, # Legend width
(unit(0.5, "npc") - legend$width) * 0.5)) # Spacer width
# A text grob for the footer
figure_number_grob <- grid::textGrob('Figure 10',
x = 0.004,
hjust = 0,
gp = grid::gpar(fontsize = 10,
col = '#01A184'))
plot_grid <- ggpubr::ggarrange(plot_output,
figure_number_grob,
ncol = 1,
nrow = 2,
heights = c(1,
0.05))
# save it
png(filename = '~/test.png', width = 3000, height = 1500, res = 300, type = 'cairo')
print(plot_grid)
dev.off()
And here's the output image: https://i.stack.imgur.com/rgzFy.png

Extend axis limits without plotting (in order to align two plots by x-unit)

I am trying to combine two ggplot objects with patchwork - two plots with different subsets of data, but the same x variable (and therefore same unit). I would like to align the plots according to the x values - Each x unit should have the same physical width in the final plot.
This is very easy when actually plotting the entire width of the larger data set (see plot below) - but I struggle to plot only parts of the data and keeping the same alignment.
library(ggplot2)
library(patchwork)
library(dplyr)
p1 <-
ggplot(mtcars, aes(mpg)) +
geom_density(trim = TRUE) +
scale_x_continuous(limits = c(10,35))
p2 <-
ggplot(filter(mtcars, mpg < 20), aes(mpg)) +
geom_histogram(binwidth = 1, boundary = 1) +
scale_x_continuous(limits = c(10,35))
p1/p2
Created on 2019-08-07 by the reprex package (v0.3.0)
The desired output
That's photoshopped
adding coord_cartesian(xlim = c(10,(20 or 35)), clip = 'off'), and/or changing scale_x limits to c(0,(20 or 35)) doesn't work.
patchwork also won't let me set the widths of both plots when they are in two rows, which makes sense in a way. So I could create an empty plot for the second row and set the widths for those, but this seems a terrible hack and I feel there must be a much easier solution.
I am not restricted to patchwork, but any solution allowing to use it would be very welcome.
I modified the align_plots function from the cowplot package for this, so that its plot_grid function can now support adjustments to the dimensions of each plot.
(The main reason I went with cowplot rather than patchwork is that I haven't had much tinkering experience with the latter, and overloading common operators like + makes me slightly nervous.)
Demonstration of results
# x / y axis range of p1 / p2 have been changed for illustration purpose
p1 <- ggplot(mtcars, aes(mpg, 1 + stat(count))) +
geom_density(trim = TRUE) +
scale_x_continuous(limits = c(10,35)) +
coord_cartesian(ylim = c(1, 3.5))
p2 <- ggplot(filter(mtcars, mpg >= 15 & mpg < 30), aes(mpg)) +
geom_histogram(binwidth = 1, boundary = 1)
plot_grid(p1, p2, ncol = 1, align = "v") # plots in 1 column, x-axes aligned
plot_grid(p1, p2, nrow = 1, align = "h") # plots in 1 row, y-axes aligned
Plots in 1 column (x-axes aligned for 15-28 range):
Plots in 1 row (y-axes aligned for 1 - 3.5 range):
Caveats
This hack assumes the plots that the user intends to align (either horizontally or vertically) have reasonably similar axes of comparable magnitude. I haven't tested it on more extreme cases.
This hack expects simple non-faceted plots in Cartesian coordinates. I'm not sure what one could expect from aligning faceted plots. Similarly, I'm not considering polar coordinates (what's there to align?) or map projections (haven't looked into this, but they feel rather complicated).
This hack expects the gtable cell containing the plot panel to be in the 7th row / 5th column of the gtable object, which is based on my understanding of how ggplot objects are typically converted to gtables, and may not survive changes to the underlying code.
Code
Modified version of cowplot::align_plots:
align_plots_modified <- function (..., plotlist = NULL, align = c("none", "h", "v", "hv"),
axis = c("none", "l", "r", "t", "b", "lr", "tb", "tblr"),
greedy = TRUE) {
plots <- c(list(...), plotlist)
num_plots <- length(plots)
grobs <- lapply(plots, function(x) {
if (!is.null(x)) as_gtable(x)
else NULL
})
halign <- switch(align[1], h = TRUE, vh = TRUE, hv = TRUE, FALSE)
valign <- switch(align[1], v = TRUE, vh = TRUE, hv = TRUE, FALSE)
vcomplex_align <- hcomplex_align <- FALSE
if (valign) {
# modification: get x-axis value range associated with each plot, create union of
# value ranges across all plots, & calculate the proportional width of each plot
# (with white space on either side) required in order for the plots to align
plot.x.range <- lapply(plots, function(x) ggplot_build(x)$layout$panel_params[[1]]$x.range)
full.range <- range(plot.x.range)
plot.x.range <- lapply(plot.x.range,
function(x) c(diff(c(full.range[1], x[1]))/ diff(full.range),
diff(x)/ diff(full.range),
diff(c(x[2], full.range[2]))/ diff(full.range)))
num_widths <- unique(lapply(grobs, function(x) {
length(x$widths)
}))
num_widths[num_widths == 0] <- NULL
if (length(num_widths) > 1 || length(grep("l|r", axis[1])) > 0) {
vcomplex_align = TRUE
warning("Method not implemented for faceted plots. Placing unaligned.")
valign <- FALSE
}
else {
max_widths <- list(do.call(grid::unit.pmax,
lapply(grobs, function(x) {x$widths})))
}
}
if (halign) {
# modification: get y-axis value range associated with each plot, create union of
# value ranges across all plots, & calculate the proportional width of each plot
# (with white space on either side) required in order for the plots to align
plot.y.range <- lapply(plots, function(x) ggplot_build(x)$layout$panel_params[[1]]$y.range)
full.range <- range(plot.y.range)
plot.y.range <- lapply(plot.y.range,
function(x) c(diff(c(full.range[1], x[1]))/ diff(full.range),
diff(x)/ diff(full.range),
diff(c(x[2], full.range[2]))/ diff(full.range)))
num_heights <- unique(lapply(grobs, function(x) {
length(x$heights)
}))
num_heights[num_heights == 0] <- NULL
if (length(num_heights) > 1 || length(grep("t|b", axis[1])) > 0) {
hcomplex_align = TRUE
warning("Method not implemented for faceted plots. Placing unaligned.")
halign <- FALSE
}
else {
max_heights <- list(do.call(grid::unit.pmax,
lapply(grobs, function(x) {x$heights})))
}
}
for (i in 1:num_plots) {
if (!is.null(grobs[[i]])) {
if (valign) {
grobs[[i]]$widths <- max_widths[[1]]
# modification: change panel cell's width to a proportion of unit(1, "null"),
# then add whitespace to the left / right of the plot's existing gtable
grobs[[i]]$widths[[5]] <- unit(plot.x.range[[i]][2], "null")
grobs[[i]] <- gtable::gtable_add_cols(grobs[[i]],
widths = unit(plot.x.range[[i]][1], "null"),
pos = 0)
grobs[[i]] <- gtable::gtable_add_cols(grobs[[i]],
widths = unit(plot.x.range[[i]][3], "null"),
pos = -1)
}
if (halign) {
grobs[[i]]$heights <- max_heights[[1]]
# modification: change panel cell's height to a proportion of unit(1, "null"),
# then add whitespace to the bottom / top of the plot's existing gtable
grobs[[i]]$heights[[7]] <- unit(plot.y.range[[i]][2], "null")
grobs[[i]] <- gtable::gtable_add_rows(grobs[[i]],
heights = unit(plot.y.range[[i]][1], "null"),
pos = -1)
grobs[[i]] <- gtable::gtable_add_rows(grobs[[i]],
heights = unit(plot.y.range[[i]][3], "null"),
pos = 0)
}
}
}
grobs
}
Utilising the above modified function with cowplot package's plot_grid:
# To start using (in current R session only; effect will not carry over to subsequent session)
trace(cowplot::plot_grid, edit = TRUE)
# In the pop-up window, change `grobs <- align_plots(...)` (at around line 27) to
# `grobs <- align_plots_modified(...)`
# To stop using
untrace(cowplot::plot_grid)
(Alternatively, we can define a modified version of plot_grid function that uses align_plots_modified instead of cowplot::align_plots. Results would be the same either way.)
Here is an option with grid.arrange that does not use a blank plot, but requires a manual of adjustment of:
plot margin
x axis expansion
number of decimal places in y axis labels
library(ggplot2)
library(dplyr)
library(gridExtra)
p1 <-
ggplot(mtcars, aes(mpg)) +
geom_density(trim = TRUE) +
scale_x_continuous(limits = c(10,35), breaks=seq(10,35,5), expand = expand_scale(add=c(0,0)))
p2 <-
ggplot(filter(mtcars, mpg < 20), aes(mpg)) +
geom_histogram(binwidth = 1, boundary = 1) +
scale_x_continuous(limits = c(10,20), breaks=seq(10,20,5), expand = expand_scale(add=c(0,0))) +
scale_y_continuous(labels = scales::number_format(accuracy = 0.01)) +
theme(plot.margin = unit(c(0,1,0,0), "cm"))
grid.arrange(p1, p2,
layout_matrix = rbind(c(1, 1), c(2, NA))
)
Should make this plot:

Keeping plotted heights fixed when laying out a list ggplot together

I'm creating a list of ggplot heatmaps, which have the same number of rows but different number of columns and different lengths of their x-axis tick labels:
plot.list <- vector(mode="list",length(3))
n.cols <- c(600,30,300)
x.labs <- c("medium","this is a long label","sh")
library(ggplot2)
for(i in 1:3){
set.seed(1)
df <- reshape2::melt(matrix(rnorm(100*n.cols[i]),100,n.cols[i],dimnames = list(paste0("G",1:100),paste0("S",1:n.cols[i]))))
plot.list[[i]] <- ggplot(data=df,mapping=aes(x=Var2,y=Var1,fill=value))+
geom_tile()+theme_minimal()+scale_fill_gradient2(name="Scaled Value",low="darkblue",mid="gray",high="darkred")+
scale_x_discrete(name=NULL,breaks=unique(df$Var2)[floor(length(unique(df$Var2))/2)],labels=x.labs[i])+
scale_y_discrete(name=NULL)+
theme(legend.position=NULL,axis.title.x=element_blank(),axis.text.x=element_text(angle=90,hjust=1,vjust=0.5))
if(i != 1) plot.list[[i]] <- plot.list[[i]]+theme(axis.text.y=element_blank())
if(i != 3) plot.list[[i]] <- plot.list[[i]]+theme(legend.position = "none")
}
I then want to combine them together horizontally with a very small margin separating them, and have their widths be relative to the numbers of columns.
Trying to achieve this using gridExtra's arrangeGrob:
gridExtra::arrangeGrob(grobs=plot.list,ncol=length(plot.list),widths=n.cols,padding=0.01)
Or with cowplot's plot_grid:
cowplot::plot_grid(plotlist=plot.list,align="v",axis="tb",ncol=length(plot.list),rel_widths=n.cols)
Gives me:
So my questions are:
How to get them to have the same height and have the x-axis labels extend downwards to varying lengths?
Shrink the spaces between them? I tried reducing the padding value but see no change
Note that I know that using facet_grid might be the obvious way to create this in the first place, but I specifically need to first create the list of plots and only then combine them.
Both egg:ggarrange and cowplot::plot_grid() can accomplish this.
As far as answering 1, try out:
library(egg)
plot1 <- plot.list[[1]]
plot2 <- plot.list[[2]]
plot3 <- plot.list[[3]]
ggarrange(plot1, plot2, plot3, ncol = 3, widths = c(600,30,300)) #originally had the 20,3,10, but I don't think it scales right.
As far as 2, you can set you plot.margins beforehand and arrange like before.
plot1 <- plot.list[[1]] + theme(plot.margin = margin(1,0,1,1)) # order is top, right, bottom, left. Go negative if you want them to touch.
plot2 <- plot.list[[2]] + theme(plot.margin = margin(1,0,1,0))
plot3 <- plot.list[[3]] + theme(plot.margin = margin(1,1,1,0))
ggarrange(plot1, plot2, plot3, ncol = 3, widths = c(600,30,300))
plot_grid will give you the same image as below.
cowplot::plot_grid(plot1, plot2, plot3, ncol = 3, axis = "b", align = "h", rel_widths = c(600,30,300))

How to display the same legend for all raster layers in a rasterStack

I've looked through all SO but haven't been able to find an answer to this specific question:
I have a rasterStack with several layers whose values span over a quite large range. Knowing the values of each layer and the chosen colour scale I believe I have managed to plot all the rasters with the same colour scale, but I face three problems now:
I can't be sure that the values are being plotted with the same colour scale, although it seems like it
I can't manage to plot the same scale and scale labels for all layers.
One of my cells in ras3, ras3[3,9], doesn't get coloured, but it is not NA!
Example script follows:
library(raster)
# generate example rasters
set.seed(123)
ras1 <- raster(ncol = 10, nrow= 10)
values(ras1) <- runif(100, 1, 10)
ras2 <- raster(ncol = 10, nrow = 10)
values(ras2) <- runif(100, 5, 50)
ras3 <- raster(ncol = 10, nrow = 10)
values(ras3) <- runif(100, 10, 100)
# stack them
rasStack <- stack(ras1, ras2, ras3)
# plot normally to check the values
plot(rasStack)
# obtain max and min values
maxv <- max(maxValue(rasStack))+1
minv <- min(minValue(rasStack))
# set the breaks between min and max values
brks <- seq(minv,maxv,by=0.1)
nbrks <- length(brks)-1
r.range <- c(minv, maxv)
# generate palette
colfunc<-colorRampPalette(c("springgreen", "royalblue", "yellow", "red"))
# plot in a loop with a common legend, using legend.only = T
for(i in seq_len(nlayers(rasStack))){
tmp <- rasStack[[i]]
plot(tmp, breaks=brks,col=colfunc(nbrks), legend = F, zlim=c(minv,maxv),
main = names(tmp))
plot(tmp, legend.only=TRUE, col=colfunc(nbrks),
legend.width=1, legend.shrink=0.75,
axis.args=list(at=seq(minv, maxv, 5),
labels=round(seq(r.range[1], r.range[2], 5), 2),
cex.axis=0.6),
legend.args=list(text='value', side=4, font=2, line=2.5, cex=0.8))}
# check that the blank cell has a valid value
ras3[3, 9]
> 99.01704
Any help will be greatly appreciated!
EDIT: as per ycw's answer I've edited the code and now problem no. 3 has disappeared!
I just fixed this problem so I'll post the solution in case someone else stumbles with this.
I might be a bit of a workaround, and it certainly is not very elegant, but it works:
First of all we add up all three raster layers in a new one
rasTot <- ras1 + ras2 + ras3
Now we run the loop from the previous code but in the plot with the legend.onlycall we use this total raster.
for(i in seq_len(nlayers(rasStack))){
tmp <- rasStack[[i]]
plot(tmp, breaks=brks,col=colfunc(nbrks), legend = F, zlim=c(minv,maxv),
main = names(tmp))
plot(rasTot, legend.only=TRUE, col=colfunc(nbrks),
legend.width=1, legend.shrink=0.75,
legend.args=list(text='value', side=4, font=2, line=2.5, cex=0.8))
}
I also edited out some of the legend label specifications, as the defaults are OK.
The last break number should be larger than the maximum value of your data (maxv) so that the cell with the maximum can be colored based on the last color category. Otherwise, the cell will be blank.
I modified your code by changing maxv <- max(maxValue(rasStack)) + 1 but did not change other parts. The result looks good.

Resources