terra icon indicating copy to clipboard operation
terra copied to clipboard

plotRGB: Consistent colors to compare several SpatRasters

Open aloboa opened this issue 3 years ago • 10 comments

I have a set of raster bricks that I want to display using plotRGB() The problem is that plotRGB(x, stretch="lin") performs ad-hoc stretching to each object, which is good in some cases but not if you want to visually compare the plots. Is there a way to enforce one given scale to be applied in all plotRGB() displays?

aloboa avatar Sep 19 '22 10:09 aloboa

Can argument zlim be used here?

library(terra)
b <- rast(system.file("ex/logo.tif", package="terra"))   
plotRGB(b, zlim=c(100,255))

rhijmans avatar Sep 19 '22 20:09 rhijmans

If zlim refers to the input object and the output range is always the same, yes. In that case, please clarify in the doc: zlim: "numeric vector of length 2. Range of values to plot (optional)" should be "Range of input values to plot (optional)" Is the max in zlim the same as scale? Also, this is an RGB plot, thus 3 bands are involved. Perhaps a 3x2 matrix would be better? Finally, for the user, it would be a lot simpler to just extract the scaling from one plotRGB() and apply it to another.

aloboa avatar Sep 20 '22 06:09 aloboa

This is a little real example:

https://www.dropbox.com/s/p9tzrak01w3vowh/a.rds?dl=0

a <- readRDS("a.rda")
close.screen(all=TRUE)
split.screen(c(4,1))
#[1] 1 2 3 4
screen(1)
plotRGB(a)
#Error in grDevices::rgb(RGB[, 1], RGB[, 2], RGB[, 3], alpha = alpha, maxColorValue = scale) : 
#  color intensity 1.00587, not in [0,1]
screen(2)
plotRGB(a, zlim=c(0,4096))
#Error in grDevices::rgb(RGB[, 1], RGB[, 2], RGB[, 3], alpha = alpha, maxColorValue = scale) : 
#  color intensity 1.00587, not in [0,1]
screen(3)
plotRGB(a, stretch="lin")
screen(4)
plotRGB(a, zlim=c(0,4096), stretch="lin")
close.screen(all=TRUE)

I was not expecting the first 2 errors and was expecting screen 3 and 4 to be different (as the max of a bands are <<4096). I have images that do go up to 4096, and those should be displayed as much brighter.

aloboa avatar Sep 20 '22 06:09 aloboa

The example does not work. You can save/read a SpatRaster with saveRDS/readRDS if you want, but not with save//load

rhijmans avatar Sep 20 '22 07:09 rhijmans

Sorry about that. Here: https://www.dropbox.com/s/p9tzrak01w3vowh/a.rds?dl=0

> a <- rast(readRDS("a.rds"))
> global(a,range,na.rm=TRUE)
            X1       X2
B170  950.0561 1865.897
B14   903.6075 1768.794
B86  1214.1776 2435.019

aloboa avatar Sep 20 '22 08:09 aloboa

These are different issues. If your data are not within [0, 255] you need to provide argument scale. For example

plotRGB(a, scale=2500)

That should probably solve your problem.

You could also use the stretch method before using plotRGB.

rhijmans avatar Sep 21 '22 22:09 rhijmans

I certainly use stretch() a lot and can achieve what I need by generating previous stretched 3-band versions of the images, but this is complicated for fast visualizaitions. My suggestion here is a faster method that would mimic what can be done in QGIS (and other RSGIS software): given 4 multi-spectral images a1, a2, a3, a4, you display a stretched color composite of a1 (or even just a stretched grey scale display of one given band), copy the style and paste it to the display of a2, a3 and a4 at once. I suggest being able to do so as easily in terra. For the grey-scale display, a solution is to create an intermediate object compiling the band of interest from each multi-spectral image and use tidyterra::geom_spatraster() with facet_wrap(), which ensures that the same scale is being applied. But again, we have to create an intermediate object.

aloboa avatar Sep 22 '22 17:09 aloboa

What about setting the scale as in plotRGB(a, scale=2500)

rhijmans avatar Sep 22 '22 21:09 rhijmans

And it could be really helpful if you made an example (with code, not files) of two SpatRasters that have different ranges, but for which you would like to colors to match.

rhijmans avatar Sep 22 '22 21:09 rhijmans

All I suggest is something like: plotRGB(a1, r=7, g=5, b=2, stretch="lin") #assume this is a nice display estilo <- plotRGB(a1, r=7, g=5, b=2, stretch="lin")$style plotRGB(a2, style=estilo) plotRGB(a3, style=estilo) plotRGB(a4, style=estilo)

which can be done using stretch, but not as easily, and visualization is meant to be quick and easy. Then, for a final figure, a more complicated procedure can be followed.

aloboa avatar Sep 23 '22 06:09 aloboa

I understood what you were suggesting; but there might be better ways,

rhijmans avatar Sep 24 '22 04:09 rhijmans

When argument zlim is used with stretch=lim, the stretching now accounts for zlim. So you can simply set the limits you want/need to get consistent coloring, even with stretching different original value ranges.

library(terra)
a <- d <- b <- rast(system.file("ex/logo.tif", package="terra"))   
d[d>250 | d < 100]=NA
a[a>150]=NA

par(mfrow=c(1,3))
plotRGB(b, stretch="lin", zlim=c(50,200))
plotRGB(d, stretch="lin", zlim=c(50,200))
plotRGB(a, stretch="lin", zlim=c(50,200))

tripleR

rhijmans avatar Sep 27 '22 02:09 rhijmans

The problem is that we do not necessarily know a priori that zlim=c(50,200) is the appropriate range. I know this can be found, but a procedure such as the one I suggest (extracting it from a useful visualization) would be much easier and faster, much better for data exploration, and equivalent to what is done in GIS packages. Anyway, I think my point is clear and you obviously have the right to close this if you prefer investing your time on other issues.

aloboa avatar Sep 27 '22 07:09 aloboa

My approach is not because of time constraints. I do not want to add functionality now that I will remove later. I think that plot(RGB) should return an object that could recreate the plot. From that, you could possibly extract what you are after. This will take some work and time. So for now, something, like what I suggest would have to do.

rhijmans avatar Sep 28 '22 02:09 rhijmans

@aloboa, for data visualization, I suggest using dedicated packages like "ggplot2" or "tmap" (or something else) -- they are much more advanced; you can save plots as objects and copy their attributes.

t-wojciech avatar Sep 28 '22 18:09 t-wojciech

@t-wojciech I often use ggplot-based graphics, in particular, for this case, tidyterra geom_spatraster() and geom_spatraster_rgb(). The point here is a fast and simple visualization, which is the goal of terra::plotRGB(). My suggestion is making plotRGB() not only fast and simple, but also consistent across a set of raster objects in an equally simple manner. I really think my point is clear enough now and that this issue does not need further discussion.

aloboa avatar Sep 29 '22 07:09 aloboa