rpngterra

Reducing margins when exporting terra plot to png


I would like to export a terra plot to png and I need the exported figure to be almost perfectly clipped around the raster that is being plotted and the related legend.

However, if I run a default export with png(), the resulting figure has wide margins. At the same time, I do not see how I could calculate the useful aspect ratio to avoid such extra margins (as in this approach), considering that the relevant area on which the aspect ratio would have to be calculated includes also the legend, so that it cannot be straightforwardly derived from the aspect ratio of the raster.

Notice that I am using terra development version 1.8-36. Please find attached a minimal working example and the related figure, where hand-made arrow highlights the excessive left margin.

library(terra)
r <- rast(system.file("ex/elev.tif", package="terra"))
x <- scale_linear(r, 0, 30)
bks <- c(0,0.4,0.8,1.3,2,4,8,15,30)
colors <- c('#3182BD', '#9ECAE1', '#DEEBF7', '#FEE5D9', '#FCAE91',
            '#FB6A4A', '#DE2D26', '#A50F15')
png("file_name.png", type="cairo-png")
plot(x, col=colors, box=F, axes=F, breaks=bks, reverse=TRUE,
     plg=list(
       title = "Title",
       title.cex = 1.2,
       cex = 1.2
     ))
dev.off()

enter image description here


Solution

  • There are two considerations: (1) remove the whitespace around the map with arguments "mar" and "buffer", and perhaps with trim(x); (2) match the relative dimensions of png to that of the plot.

    Minimal example data

    library(terra)
    #terra 1.8.38
    r <- rast(system.file("ex/elev.tif", package="terra"))
    
    1. Below, note the use of "mar" and of "buffer=FALSE" (this is the default for raster data, but not for vector data, so I show it here for others who may have a similar question). I am using background="light gray" so that you can see where the map area begins and ends. I moved the legend inside the map area to save space. If you want to keep it where it is, you could use, e.g., mar=c(0,0,0,4). Do not use "asp=NA" because then you distort your map.
    plot(r, mar=c(0,0,0,4), buffer=FALSE, breaks=5, box=F, axes=F, background="light gray", 
             plg=list(x=6.25, y=50.18, title="Title", title.cex=1.5))  
    

    If the raster data have outer rows and columns with missing values, you can remove these with

    r <- trim(r)
    
    1. To get the right PNG dimensions, set the height make the width relative to that, and find the right parameter (in this example about 1.4) with some trial and error. You generally also need to increase the "pointsize" to make sure things are legible. It is generally easier to change "pointsize" than to tinker with multiple cex arguments.
    w <- 600
    png("plot.png", width=w, height=w*1.45, pointsize = 24) 
    plot(r, mar=c(0,0,0,0), buffer=FALSE, breaks=5, box=F, axes=F, background="light gray", 
             plg=list(x=6.25, y=50.18, title="Title", title.cex=1.5))  
    dev.off()
    

    enter image description here


    To keep the legend outside the map:

    w <- 600
    png("plot.png", width=w, height=w*1.15, pointsize = 24) 
    par(bg="light blue") # for illustration purposes
    plot(r, mar=c(0,0,0,5), buffer=FALSE, breaks=5, box=F, axes=F, background="light gray", 
             plg=list(title="Title", title.cex=1.5))  
    dev.off()