I would like to export a terra
plot to png and I need the exported figure to be almost perfectly clipped around the rast
er 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 rast
er 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
|
Show 2 more comments
2 Answers
Reset to default 2What 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"))
- 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)
- 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()
reverse
? Haven't seen it. An extra argument coming with{terra}
shipping with the developer version? – Friede Commented Mar 20 at 8:04terra
development version 1.8-36, which indeed includes the optionreverse
. I have specified this also in the main body of my question. – Laura Roich Commented Mar 20 at 8:11mar
-argument inpar()
or setasp=NA
inplot()
ortrim
/crop
x
. – Friede Commented Mar 20 at 8:59