Control raster legend height - r

I would like to control the height of the colorbar to the extent of the plot window.
For example, if I use:
if (dev.cur() == 1) x11(width=10,height=9)
par(mfrow=c(2,1))
How can I increase the height of the colorbar to be same height as the plots window? e.g.
plot(r, legend.only=TRUE, legend.width=1.4, legend.shrink=1,
col=colorRampPalette(c("darkred", "red3", "orange2", "orange",
"yellow", "lightskyblue","steelblue3",
"royalblue3", "darkblue"))(12),
breaks=brks, axis.args=list(at=seq(80, 205, by=10),
labels=seq(80, 205, by=10), cex.axis=0.9),
legend.args=list(text='Precipitation (mm)', side=4, font=2,
line=2.3, cex=0.8))
Many thanks
AZ.

If I understand correctly, you want to plot a full-height legend beside two plots whose layout is defined by par(mfrow=c(2, 1).
One way to achieve this is to generate the two plots, then set par(new=FALSE) and plot the raster again with legend.only=TRUE.
library(raster)
r <- raster(matrix(runif(100), ncol=10))
# Set layout; ensure appropriate space at right for legend
par(mfrow=c(2, 1), mar=c(2, 3, 1, 3))
# Plot raster
plot(r, legend=FALSE, zlim=c(0, 1), las=1)
# Plot second object
plot(runif(10), runif(10), pch=21, cex=2, las=1,
bg=rev(terrain.colors(10)[sample(10, 10, replace=T)]))
# Revert to c(1, 1) layout and adjust legend margins
par(mfrow=c(1, 1), mar=c(2, 0, 1, 0), new=FALSE)
# Plot legend
plot(r, legend.only=TRUE, legend.shrink=1, legend.width=2, zlim=c(0, 1),
axis.args=list(at=pretty(0:1), labels=pretty(0:1)),
legend.args=list(text='Whatever', side=4, font=2, line=2.3))
If you are plotting multiple raster objects with matching extents and resolution, you might consider rasterVis::levelplot, which has a RasterStack method:
library(rasterVis)
s <- stack(replicate(2, raster(matrix(runif(100), nc=10))))
levelplot(s, layout=c(1, 2), names.attr=c('One', 'Two'),
at=seq(0, 1, length.out=100),
par.strip.text=list(font=2, cex=1.2))
# Plotting titles for vertical colorkeys is a little fiddly...
grid::grid.text('Precipitation (mm)', rot=90, y=unit(0.5, "npc"),
x=unit(0.95, "npc"))
You can suppress panel labels with par.strip.text=list(cex=0), and specify a colour ramp with col.regions:
levelplot(s, layout=c(1, 2),
col.regions=colorRampPalette(c('darkred', 'red3', 'orange2', 'orange',
'yellow', 'lightskyblue', 'steelblue3',
'royalblue3', 'darkblue')),
at=seq(0, 1, length.out=100), par.strip.text=list(cex=0),
scales=list(alternating=FALSE))
# Also demonstrating how to adjust fontface and size for legend title
grid::grid.text('Precipitation (mm)', y=unit(0.5, "npc"),
rot=90, x=unit(0.95, "npc"), gp=gpar(fontsize=14, font=2))

Related

Add border to rasterImage

Here is a gradient color legend I created using rasterImage:
colfunc <- colorRampPalette(c("red", "blue"))
legend_image <- as.raster(matrix(colfunc(20), ncol=1))
plot.new()
rasterImage(legend_image, 0.9, 0, 1, 1)
lbsq <- seq.int(0, 1, l=5)
axis(4, at=lbsq, pos=1, labels=F, col=0, col.ticks=1, tck=-.05)
mtext(lbsq, 4, -.2, at=lbsq, las=2, cex=.6)
I wish to add a thin black border surrounding the color legend. I tried to add lty = 1 in rasterImage, which did not work. My question is how to add a black border to the resultant image and adjust its color and width.
You could slightly increase the margins and use box().
colfunc <- colorRampPalette(c("red", "blue"))
legend_image <- as.raster(matrix(colfunc(20), ncol=1))
plot.new()
rasterImage(legend_image, -.1, -.1, 1.1, 1.1)
box(lwd=3)
You could also play with the pars.
png('test.png', 600, 400)
plot.new()
par(new=TRUE, mar=c(5.8, 40, 4, 4))
rasterImage(legend_image, .9, 0, 1.1, 1.1)
box(lwd=1)
par(mar=c(5.4, 4, 3.9, 4.8)+.1, new=TRUE)
plot(1:10)
dev.off()
Note, that this is rather unstable while working with the plotting preview window, definitely use png() or pdf() instead as shown.
Using rect(), the following adds a black border.
colfunc <- colorRampPalette(c("red", "blue"))
legend_image <- as.raster(matrix(colfunc(20), ncol=1))
plot.new()
rect(par("usr")[1], par("usr")[3], par("usr")[2], par("usr")[4], col = "black")
rasterImage(legend_image, 0, 0, 1, 1)

par(xpd=NA) does not work when axes=F in R plot

I want to plot an image in between two plots on one device. Importantly, the image has to be in the exact center, thus overlapping both plots. Please see the example, where I used the R logo as image:
# png and grid are both for plotting the image
library("png")
library("grid")
Rlogo <- readPNG("Rlogo.png")
par(mfrow=c(1,2), xpd=NA) # two columns, xpd should permit plotting outside of margin
barplot(-(1:10), horiz=T, border=NA, axes=FALSE)
grid.draw(rasterGrob(Rlogo, x=unit(0.5, "npc"), y = unit(0.5, "npc"), width=unit(0.1, "npc"), height=unit(0.1, "npc")))
barplot(1:10, horiz=T, border=NA, axes=F)
results in a cut R logo:
maintaining the looks, i.e. without axes, a workaround is to add an axis with col="white":
par(mfrow=c(1,2), xpd=NA)
barplot(-(1:10), horiz=T, border=NA, axes=FALSE)
axis(1, labels=F, col = "white")
# axis(1, labels=F, tick=F) # does not help
grid.draw(rasterGrob(Rlogo, x=unit(0.5, "npc"), y = unit(0.5, "npc"), width=unit(0.1, "npc"), height=unit(0.1, "npc")))
barplot(1:10, horiz=T, border=NA, axes=F)
which returns what I want
but doesn't seem like a good solution to me.
Why is the logo cut in the first plot? What do axes have to do with margins / size of device / xpd?
Do you have other ideas or solutions how to achieve the logo (uncut) in the center without axes in the plots?
It seems likely that the "traditional and grid graphics are fighting each other for control of the clipping region," from a list email from Paul Murrell. He suggests adding grid.clip() before the grid command, as follows. The xpd is no longer needed.
library("png")
library("grid")
Rlogo <- readPNG("Rlogo.png")
par(mfrow=c(1,2))
barplot(-(1:10), horiz=T, border=NA, axes=FALSE,)
grid.clip()
grid.draw(rasterGrob(Rlogo, x=unit(0.5, "npc"), y = unit(0.5, "npc"),
width=unit(0.1, "npc"), height=unit(0.1, "npc")))
barplot(1:10, horiz=T, border=FALSE, axes=F)
I can't really explain how adding the axis changes the behavior though.
I suggest my favorite classical method (without grid).
library("png")
Rlogo <- readPNG("Rlogo.png")
par(mfrow=c(1,2), mar=c(2,2,2,2))
barplot(-(1:10), horiz=T, border=NA, axes=FALSE)
barplot(1:10, horiz=T, border=NA, axes=F)
par(mfrow=c(1,1), new=T, mar=c(2,2,2,2))
plot(0,0, type="n", axes=F, ann=F, asp=1) # flat mar and asp=1 are important to keep size ratio.
a <- 0.3 # now the center is c(0, 0)
rasterImage(Rlogo, - a / 2, - a * 7 / 8 / 2 , a / 2, a * 7 / 8 /2 ) # R logo is 800 x 700
# xleft, ybottom, xright, ytop

Eliminating dead space from a plot consisting of two pie charts

I have this plot which I want to save as PDF.
pdf(file="pie_charts.pdf", width=8, height=5, onefile=F)
layout(matrix(c(1,2,3,3), ncol=2, byrow=TRUE), heights=c(4, 1))
par(mar=c(0,0,0,0), xpd=TRUE)
pie(c(1,9),col=c("black","white"))
pie(c(1,3),col=c("black","white"))
plot.new()
legend(x="center", ncol=2,legend=c("Black","Whtie"),fill=c("black","white"), bty = "n",cex=1.3)
dev.off()
And this is what I am getting
It looks quite good but I want to eliminate as much empty space as possible between the individual pie charts as well as between them and the legend. Any suggestions?
With layout(), I think you may be a bit limited with changing margins to squeeze the pie charts together.
This isn't an elegant solution but it works. I went in to the pie() function and modified the xlim arguments. This was my only change.
In other words, pie has this inside its function:
xlim <- ylim <- c(-1, 1)
Change the xlim to shift the pie charts left or right.
I made mypieleft() and mypieright().
mypieleft<-function(blah blah){
[untouched code from pie]
# xlim <- ylim <- c(-1, 1)
xlim <- c(-1.20, 0.80)
ylim <- c(-1, 1)
[untouched code from pie]
}
and
mypieright<-function(blah blah){
[untouched code from pie]
# xlim <- ylim <- c(-1, 1)
xlim <- c(-0.75, 1.25)
ylim <- c(-1, 1)
[untouched code from pie]
}
Then change your code slightly:
layout(matrix(c(1,2,3,3), ncol=2, byrow=TRUE), heights=c(4, 1))
par(oma=c(0,0,0,0), xpd=TRUE)
mypieleft(c(1,9),col=c("black","white"))
mypieright(c(1,3),col=c("black","white"))
plot.new()
legend(x="center", ncol=2,legend=c("Black","Whtie"),fill=c("black","white"), bty = "n",cex=1.3)
I get this image.
Just increase the radius of the pies:
layout(matrix(c(1, 2, 3, 3), ncol=2, byrow=TRUE), heights=c(4, 1))
par(mar=c(0, 1, 0, 0)) # increase left margin to accommodate text
pie(c(1, 9), col=c("black","white"), radius=1)
par(mar=c(0, 0, 0, 1)) # increase right margin to accommodate text
pie(c(1, 3), col=c("black", "white"), radius=1)
plot.new()
legend(x="center", ncol=2, legend=c("Black", "White"),
fill=c("black", "white"), bty="n", cex=1.3)
See the radius arg at ?pie.

Barplot -adding percentages

So i have this numeric variables which reflect percentages
data1.pct<-19
data2.pct<-5
data3.pct<-76
class1.pct<-35
class2.pct<-18
class3.pct<-47
Now i am using this code to generate barplot
CairoPDF(paste('data1/', data, '_plot1.pdf', sep=''), family='sans', pointsize=9, width=6, height=3.25)
par(mar=(c(4, 4, 1, 13) + 0.1), mgp=c(3, 2, 0), xpd=TRUE)
barplot(cbind(
c(data1.pct, data2.pct, data3.pct),
c(class1.pct, class2.pct, class3.pct)), col=c("firebrick3", "dodgerblue3", "mistyrose1"), ylim=c(0,100), space=c(0,1)
)
legend("topright", inset=c(-0.55, 0), legend=c("not attend", "refused", "attend"), col=c("mistyrose1", "dodgerblue3", "firebrick3"), lty=1, lwd=2, bty='n')
dev.off()
and the result is
I would like to add corresponding percentages inside barplot, that is numbers/percentages in my variables. So My output should be:
I would like to use barplot funcion to do this and NOT ggplot2
I have tried adding percentages with
text(mydata, 0, round(data1.pct), 1),cex=1,pos=3) but this is not right.
To get the y-values for the text, you can use cumsum along with tail and head to get the midpoints of each bar section.
par(mar=(c(4, 4, 1, 13) + 0.1), mgp=c(3, 2, 0), xpd=TRUE)
## Make the matrix for barplot
mat <- cbind(c(data1.pct, data2.pct, data3.pct), c(class1.pct, class2.pct, class3.pct))
## Get the y-values for text
ys <- apply(mat, 2, function(x) c(x[1]/2, head(cumsum(x),-1) + tail(x,-1)/2))
## Make barplot, store x data
xs <- barplot(mat, col=c("firebrick3", "dodgerblue3", "mistyrose1"), ylim=c(0,100), space=c(0,1))
## Add text
text(rep(xs, each=nrow(ys)), c(ys), labels=c(mat))
legend("topright", inset=c(-0.55, 0), legend=c("not attend", "refused", "attend"), col=c("mistyrose1", "dodgerblue3", "firebrick3"), lty=1, lwd=2, bty='n')

Gradient legend in base

Earlier I asked about creating a gradient of n values in base graphics (LINK). Now I'd like to create a gradient legend that goes with it. My ideal would be something like ggplot2's gradient legends:
Here's some code similar to what I'm working with:
colfunc <- colorRampPalette(c("red", "blue"))
plot(1:20, 1:20, pch = 19, cex=2, col = colfunc(20))
Here is an example of how to build a legend from first principles using rasterImage from grDevices and layout to split the screen
layout(matrix(1:2,ncol=2), width = c(2,1),height = c(1,1))
plot(1:20, 1:20, pch = 19, cex=2, col = colfunc(20))
legend_image <- as.raster(matrix(colfunc(20), ncol=1))
plot(c(0,2),c(0,1),type = 'n', axes = F,xlab = '', ylab = '', main = 'legend title')
text(x=1.5, y = seq(0,1,l=5), labels = seq(0,1,l=5))
rasterImage(legend_image, 0, 0, 1,1)
Late to the party, but here is a base version presenting a legend using discrete cutoffs. Thought it might be useful for future searchers.
layout(matrix(1:2,nrow=1),widths=c(0.8,0.2))
colfunc <- colorRampPalette(c("white","black"))
par(mar=c(5.1,4.1,4.1,2.1))
plot(1:10,ann=FALSE,type="n")
grid()
points(1:10,col=colfunc(10),pch=19,cex=1.5)
xl <- 1
yb <- 1
xr <- 1.5
yt <- 2
par(mar=c(5.1,0.5,4.1,0.5))
plot(NA,type="n",ann=FALSE,xlim=c(1,2),ylim=c(1,2),xaxt="n",yaxt="n",bty="n")
rect(
xl,
head(seq(yb,yt,(yt-yb)/10),-1),
xr,
tail(seq(yb,yt,(yt-yb)/10),-1),
col=colfunc(10)
)
mtext(1:10,side=2,at=tail(seq(yb,yt,(yt-yb)/10),-1)-0.05,las=2,cex=0.7)
And an example image:
The following creates a gradient color bar with three pinpoints without any plot beforehand and no alien package is needed. Hope it is useful:
plot.new()
lgd_ = rep(NA, 11)
lgd_[c(1,6,11)] = c(1,6,11)
legend(x = 0.5, y = 0.5,
legend = lgd_,
fill = colorRampPalette(colors = c('black','red3','grey96'))(11),
border = NA,
y.intersp = 0.5,
cex = 2, text.font = 2)
As a refinement of #mnel's great answer, inspired from another great answer of #Josh O'Brien, here comes a way to display the gradient legend inside the plot.
colfunc <- colorRampPalette(c("red", "blue"))
legend_image <- as.raster(matrix(colfunc(20), ncol=1))
## layer 1, base plot
plot(1:20, 1:20, pch=19, cex=2, col=colfunc(20), main='
Awesome gradient legend inside')
## layer 2, legend inside
op <- par( ## set and store par
fig=c(grconvertX(c(0, 10), from="user", to="ndc"), ## set figure region
grconvertY(c(4, 20.5), from="user", to="ndc")),
mar=c(1, 1, 1, 9.5), ## set margins
new=TRUE) ## set new for overplot w/ next plot
plot(c(0, 2), c(0, 1), type='n', axes=F, xlab='', ylab='') ## ini plot2
rasterImage(legend_image, 0, 0, 1, 1) ## the gradient
lbsq <- seq.int(0, 1, l=5) ## seq. for labels
axis(4, at=lbsq, pos=1, labels=F, col=0, col.ticks=1, tck=-.1) ## axis ticks
mtext(sq, 4, -.5, at=lbsq, las=2, cex=.6) ## tick labels
mtext('diff', 3, -.125, cex=.6, adj=.1, font=2) ## title
par(op) ## reset par

Resources