最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

r - Reducing margins when exporting terra plot to png - Stack Overflow

programmeradmin3浏览0评论

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()

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()

Share Improve this question edited Mar 20 at 9:48 Friede 9,0902 gold badges9 silver badges29 bronze badges asked Mar 20 at 7:23 Laura RoichLaura Roich 876 bronze badges 7
  • What is reverse? Haven't seen it. An extra argument coming with {terra} shipping with the developer version? – Friede Commented Mar 20 at 8:04
  • 1 I have specified that I am using terra development version 1.8-36, which indeed includes the option reverse. I have specified this also in the main body of my question. – Laura Roich Commented Mar 20 at 8:11
  • 1 Collecting previous (now deleted) comments. Use the mar-argument in par() or set asp=NA in plot() or trim/crop x. – Friede Commented Mar 20 at 8:59
  • 1 I sincerely do not understand why my question has been voted down. The question is well defined, fully reproducible, and self-contained. Moreover, it cites a related post explaining why it cannot serve as an immediate solution, thus showing that some research has been done. Finally, some of the tentative answers that have been proposed (and deleted in the meanwhile) turned out to fall short under some respects, thus proving that the question is not trivial. Under these premises, why should this type of question be regarded as "not showing any research effort, unclear, or not useful"? – Laura Roich Commented Mar 20 at 9:23
  • 1 I voted it up as it turned out to be far more tricky than I thought it would be and your questions is well defined. I posted an answer that outlines two possible solutions - can you give feedback on how this falls short ? – Tim G Commented Mar 20 at 9:29
 |  Show 2 more comments

2 Answers 2

Reset to default 2

What seems to work is setting the width manually and positioning the legend with x="topright" so that it is not cut off by the width. To reduce the margins even further, you can also set mar=c(0, 0, 0, 0) - see plot.terra.

library(terra)

# Load and scale raster
r <- rast(system.file("ex/elev.tif", package="terra"))
x <- scale_linear(r, 0, 30)

# Define breaks and colors
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("plot.png", 
    width=600, 
    height=800, 
    type="cairo-png") 

plot(x, 
     col=colors, 
     box=FALSE, 
     axes=FALSE, 
     breaks=bks, 
     mar=c(0, 0, 0, 0),
     plg=list(
       title = "Title",
       title.cex = 1.2,
       cex = 1.2,
       x="topright"
     ))

dev.off()

will give

Option 2 using ASP

As per Friede's comment, Plot.terra sets the aspect ratio automatically.

if (is.null(list(...)$asp)) {
        asp <- ifelse(is.lonlat(x, perhaps=TRUE, warn=FALSE), 1/cos((mean(as.vector(ext(x))[3:4]) * pi)/180), 1)
        graphics::filled.contour(x=X, y=Y, z=Z, asp=asp, ...)
    } else {
        graphics::filled.contour(x=X, y=Y, z=Z,...)
    }

You can remove this by adding ASP = NA but you have to keep in mind that this will destroy the proper map scaling. So while it will stretch the map to fill out the space more, it is no longer a proper map (source: Robert Hijmans).

## ASP NA

png("plot_asp_na.png", type="cairo-png") 

plot(x, 
     col=colors, 
     box=FALSE, 
     axes=FALSE, 
     breaks=bks, 
     asp=NA, # as per Friede's comment
     mar=c(0, 0, 0, 0),
     plg=list(
       title = "Title",
       title.cex = 1.2,
       cex = 1.2,
       x="topright"
     ))

dev.off()

will give

Add - placing the legend

I think you asked that before, so I will outline how to place the legend. You can obtain the extend of the plot by using

ext(x)
SpatExtent : 5.74166666666667, 6.53333333333333, 49.4416666666667, 50.1916666666667 (xmin, xmax, ymin, ymax)

e <- ext(x)
xmin(e) # sames as e[1]
xmax(e) # same as e[2]

Inside these ranges, you can manually place your legend by adding x=6.37, y=50.15 to the plg list where x and y are the coordinates of the to left corner of the legend box.

plot(x, 
     col=colors, 
     box=FALSE, 
     axes=FALSE, 
     breaks=bks, 
     asp=NA, # as per Friede's comment
     mar=c(0, 0, 0, 0),
     plg=list(
       title = "Title",
       title.cex = 1.2,
       cex = 1.2,
       bty="o", # added for a box around the legend
       x=6.37, y=50.15
     ))

giving

Using this, we can allways place the legend right by doing

plot(x, 
     col=colors, 
     box=FALSE, 
     axes=FALSE, 
     breaks=bks, 
     asp=NA, # as per Friede's comment
     mar=c(0, 0, 0, 6),
     plg=list(
       title = "Title",
       title.cex = 1.2,
       cex = 1.2,
       bty="o", 
       x=xmax(ext(x)), y=(ymax(ext(x))+ymin(ext(x)))/2 + 0.1
     ))

giving

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()


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()
发布评论

评论列表(0)

  1. 暂无评论