Set plot margin of png plot device using par - r

I've created a choropleth of Brazil. When saving the plot in .png, the upper and the lower part of the plot are lost (covered). Here are the lines to save the plot.
plot.new()
par(omi=c(0,0,0,0), mgp=c(0,0,0),mar=c(0,0,0,0) , family = "D")
par(mfrow=c(1,1),cex=1,cex.lab = 0.75,cex.main=0.2,cex.axis=0.2)
png(filename = "map_cons_g.png", width = 6,height = 6, units = "in", res = 600)
plot(c(-75,-35),c(0,-30),type="n",axes=FALSE,xlab="",ylab="",asp=1.2)
plot(Brazil,col=cols[Brazil$Cons.g_ri],add=TRUE,border="black",lwd=0.5)
dev.off()
For saving the plot without losing the upper and the lower part of the map, I must change the coordinates to add white space at the bottom and at the top (i.e. replace c(0,-30) by c(5,-33)):
plot.new()
par(omi=c(0,0,0,0), mgp=c(0,0,0),mar=c(0,0,0,0) , family = "D")
par(mfrow=c(1,1),cex=1,cex.lab = 0.75,cex.main=0.2,cex.axis=0.2)
png(filename = "map_cons_g.png", width = 6,height = 6, units = "in", res = 600)
plot(c(-75,-35),c(5,-33),type="n",axes=FALSE,xlab="",ylab="",asp=1.2)
plot(Brazil,col=cols[Brazil$Cons.g_ri],add=TRUE,border="black",lwd=0.5)
dev.off()
This works in the sense that I can see the full map but the map then does not use all the available area in the figure. It seems that there are some margin in the upper and the lower part of the figure when saving the plot. I've never had that problem with other types of plot.
Sorry, I don't have enough "reputation" to post images to show you how the maps look like.
Any idea of how to fix this?
Edit:
The comments below got me searching more into the problem and I finally found a fix. I apologize as I now realized that I did not understand the source of the problem and thus did not explain as best as I could have,
It seems that png resets the outer margin of the plot. Thus, even though I had set omi=c(0,0,0,0), those were not the value used by the png command in saving the plot. The solution was to set the plot parameters after calling png so save the figure.
plot.new()
png(filename = "map_cons_g.png", width = 6,height = 6, units = "in", res = 600)
par(omi=c(0,0,0,0), mgp=c(0,0,0),mar=c(0,0,0,0) , family = "D")
par(mfrow=c(1,1),cex=1,cex.lab = 0.75,cex.main=0.2,cex.axis=0.2)
plot(c(-75,-35),c(5,-33),type="n",axes=FALSE,xlab="",ylab="",asp=1.2)
plot(Brazil,col=cols[Brazil$Cons.g_ri],add=TRUE,border="black",lwd=0.5)
dev.off()

From Details in ?par:
Each device has its own set of graphical parameters.
Thus, even though I had set the outer margin of the plot in par (omi = c(0,0,0,0)), those value were overwritten by the parameters in png when saving the plot.
The solution was to set the margin parameters in par after calling png
plot.new()
# first open png device...
png(filename = "map_cons_g.png", width = 6,height = 6, units = "in", res = 600)
# ...then set par
par(omi = c(0,0,0,0), mgp = c(0,0,0), mar = c(0,0,0,0), family = "D")
par(mfrow = c(1, 1), cex = 1, cex.lab = 0.75, cex.main = 0.2, cex.axis = 0.2)
plot(c(-75, -35), c(5, -33), type = "n", axes = FALSE, xlab = "", ylab = "", asp = 1.2)
plot(Brazil, col = cols[Brazil$Cons.g_ri], add = TRUE, border = "black", lwd = 0.5)
dev.off()

Related

size of legend is too big and cex is not useful

I am plotting with r, but the legend is too big. when I reduce the cex, the text shrinks but the legend box still has huge empty spaces. the legend code is:
legend('topleft',adj = c(0, 0.6), pch=c(4,1), c("non smokers", "smokers"), cex=.75,horiz = TRUE,pt.cex = 1)
how can I fix this?thx
First specify the plot size using windows or pdf or png and it will be fine. Here's an example using png
graphics.off()
png(filename = "test.png", width = 7, height = 7, units = "in", res = 75)
plot(1:10, 1:10)
legend('topleft', pch=c(4,1), c("non smokers", "smokers"), horiz = TRUE)
dev.off()

high-quality png without changing the multiple-plot appearance in R?

To produce a high-quality png file from a plot, I usually increase the value of the res = argument of the png(). But in the following case, I have a complex 7x4 plotting platform, and changing the res = changes the actual appearance of the plot (e.g., plot frames look much thicker etc.).
I am wondering how I could raise the quality of the following plot while preserving its original appearance (i.e., as shown in the graphical device)?
P.S: I'm just trying to achieve a high-quality version of what I see in my graphical device.
png("Plot.png")
par(mfcol = c(7, 4), mar = rep(.1, 4), oma = rep(7, 4))
invisible(lapply(1:28, plot, t = "n", xaxt = "n", yaxt = "n"))
dev.off()
Does this look better?
png("Plot.png", res = 600, width = 8, height = 7, units = "in")
par(mai = c(0.5,0.5,0.5,0.5))
par(mfcol = c(7, 4), mar = rep(.1, 4), oma = rep(7, 4))
invisible(lapply(1:28, function(x){
plot(rnorm(20), rnorm(20), axes = FALSE, col = sample(1:7, 1), ann = FALSE)
box()
}))
dev.off()
Depending on what you're trying to accomplish, you can also increase width and height parameters within png. As you increase res, if you increase the width and height the relative layout will stay the same, but you'll end up with more pixels so for a given physical dimension it will be higher resolution.

How to change the legend box width when plotting in R

I use the following script to generate a legend in R. But the legend box is too small... how do I increase the box width?
legend("topleft", lty = 1, legend = c("Sub_metering_1","Sub_metering_2","Sub_metering_3"),col = c("black","red","blue"))
You are probably resizing your graph after you plot it and the legend. If that is the case, and you want to keep the box, one option would be to plot the graph, resize it, and then generate the legend. Perhaps a better option would be to size the window to the desired width to start with:
# on Windows, you can use the `windows` function. elsewhere, try quartz or X11
windows(height = 7, width = 3.5)
plot(hp ~ mpg, data = mtcars)
leg <- legend("topleft", lty = 1,
legend = c("Sub_metering_1","Sub_metering_2","Sub_metering_3"),
col = c("black","red","blue"),
#plot = FALSE,
#bty = "n")
)
You can also define exactly where you want the box to fall by providing a pair of x and y coordinates to the legend function. Those values would represent the upper left and bottom right corners of the box. The legend function will actually generate the coordinates for the upper-left hand corner of the box along with the width and height. By default it returns them invisibly, but you can assign them to an object, and If you use the plot = FALSE, option to legend you can capture those coordinates and modify them as you wish without actually plotting the legend.
windows(height = 7, width = 3.5)
plot(hp ~ mpg, data = mtcars)
legend(x = c(9.46, 31), y = c(346.32, 298),
legend = c("Sub_metering_1","Sub_metering_2","Sub_metering_3"),
col = c("black","red","blue"),
lty = 1)
The legend function will actually generate the coordinates for the upper-left hand corner of the box (that's where I got 9.46 and 346.62) along with the width and height of the box. By default it returns them invisibly, but you can assign them to an object, and if you use the plot = FALSE, option to legend you can capture those coordinates and modify them as you wish without actually plotting the legend.
plot(hp ~ mpg, data = mtcars)
leg <- legend("topleft", lty = 1,
legend = c("Sub_metering_1","Sub_metering_2","Sub_metering_3"),
col = c("black","red","blue"),
plot = FALSE)
# adjust as desired
leftx <- leg$rect$left
rightx <- (leg$rect$left + leg$rect$w) * 1.2
topy <- leg$rect$top
bottomy <- (leg$rect$top - leg$rect$h) * 1
# use the new coordinates to define custom
legend(x = c(leftx, rightx), y = c(topy, bottomy), lty = 1,
legend = c("Sub_metering_1","Sub_metering_2","Sub_metering_3"),
col = c("black","red","blue"))
Part of the legend width is determined by the longest width of the labels you use, which is calculated via strwidth. Below an easy example how to halve or double the size by using legend(..., text.width = ...).
plot(1)
text = c("Sub_metering_1","Sub_metering_2","Sub_metering_3")
legend("topleft"
,lty = 1
,legend = text
,col = c("black","red","blue")
)
strwidth(text)
# [1] 0.1734099 0.1734099 0.1734099
# half the length
legend("bottomleft"
,lty = 1
,legend = text
,text.width = strwidth(text)[1]/2
,col = c("black","red","blue")
)
# double the length
legend("center"
,lty = 1
,legend = text
,text.width = strwidth(text)[1]*2
,col = c("black","red","blue")
)

How to change the legend title and position in a lattice plot

I'm using lsmip from lsmeans to plot my model,
library(lsmeans)
PhWs1 <- lsmip(GausNugget1, Photoperiod:Ws ~ Month,
ylab = "Observed log(number of leaves)", xlab = "Month",
main = "Interaction between Photoperiod and Water stress over the months (3 photoperiods)",
par.settings = list(fontsize = list(text = 15, points = 10)))
but I was not able to get a suggestion on the internet on how to handle the legend position, size, title, etc.
I used trellis.par.get() to see the parameters but I could not find the one related to my issue. As you can see from the graph, the legend should be "Photoperiod*Ws" but Ws is not visible.
I see two possibly complementing alternatives to approach this issue. The first would be to create a fully customized legend and pass it on to the key argument of xyplot (which lsmip is heavily based on). Here is an example taken from ?lsmip to clarify my point.
## default trellis point theme
trellis_points <- trellis.par.get("superpose.symbol")
## create customized key
key <- list(title = "Some legend title", # legend title
cex.title = 1.2,
x = .7, y = .9, # legend position
points = list(col = trellis_points$col[1:2], # points
pch = trellis_points$pch[1:2],
cex = 1.5),
text = list(c("A", "B"), cex = .9)) # text
## create results and extract lattice plot
d <- lsmip(warp.lm, wool ~ tension, plotit = FALSE,
main = "Some figure title", key = key)
p <- attr(d, "lattice")
p
As you can see, setting up a customized legend let's you modify all the different components of the legend - including labels, text and symbol sizes, legend spacing, etc. Have a deeper look at the key argument described in ?xyplot which describes the various modification options in detail.
Now, if you have a long legend title and you do not want to include the legend inside the plot area, you could also define separate viewports, thus allowing the legend to occupy more space at the right margin. Note the use of update to remove the initially created legend from p and the subsequent assembly of the single figure components using grid functionality.
## remove legend from figure
p <- update(p, legend = NULL)
## assemble figure incl. legend
library(grid)
png("plot.png", width = 14, height = 10, units = "cm", res = 300)
grid.newpage()
## add figure without legend
vp0 <- viewport(x = 0, y = 0, width = .75, height = 1,
just = c("left", "bottom"))
pushViewport(vp0)
print(p, newpage = FALSE)
## add legend
upViewport(0)
vp1 <- viewport(x = .7, y = 0, width = .3, height = 1,
just = c("left", "bottom"))
pushViewport(vp1)
draw.key(key, draw = TRUE)
dev.off()

How to change line width of map.scale from package maps?

I'm using map.scale() from package maps to create a scale in a map. The following code creates an example of what I'm looking for:
library(maps)
png("example.png", width = 6000, height = 6000)
map(database = "world", regions = 'Brazil',fill = T)
map.scale(-43, -30,
ratio = F,
cex = 9,
lheight= 1,
pos =1,
offset = 1)
dev.off()
As I'm creating a high resolution png image, the line width of the scale produced is not appropriate. You can check it by zooming in the lower right corner of the map.
Is there any parameter that I'm missing to increase the width of the scale?
The reason you can't modify the line width (it's the lwd argument you should be looking for not line height here) is that it isn't applied in the map.scale() function and passed on to the lines() call for that part of the scale. We can, however, programmatically add this functionality to the function by doing something like this:
library(maps)
t <- capture.output(map.scale)
## Strip trailing text:
t <- t[-length(t)]
## Find lines() call and add in a line width argument:
t <- gsub("(lines\\(linexy)", "\\1, lwd=lwd", t)
t <- gsub("(\\.\\.\\.)", "\\1, lwd=10", t)
t <- paste(t, collapse="\n")
eval( parse(text=paste0( "map.scale.new <- ", t )) )
## Use our new map.scale.new() function with lwd= parameter:
png("example.png", width = 6000, height = 6000)
map(database = "world", regions = 'Brazil',fill = T)
map.scale.new(
-43, -30,
ratio = F,
cex = 9,
lwd= 10,
pos =1,
offset = 1
)
dev.off()
You can modify the lwd= to now suit your fancy.

Resources