grey scale map
aloboa opened this issue · comments
How can I display in grey scale?
> g1 <- ggplot() +
+ geom_spatraster(data = tile, aes(fill=cyl_tile_1))
> g1
> g1 + scale_fill_grey()
Error: Continuous value supplied to discrete scale
To extend a bit my question: I'm trying to display individual spectral bands of multispectral images. I need plots to be in consistent grey scale, that is, an image with range 0-100 should be half bright as another in the 100-200 range.
Hi,
So I see here two issues:
- First,
scale_fill_grey()
is a discrete scale, meant to be used with categorical variables. You would need to create a
equivalent version withscale_fill_gradientn()
. - It is not exactly clear to me the desired output on multispectral images, but I think this is tackled automatically by
ggplot2
if you usefacet_wrap()
.
See if this code helps:
library(tidyterra)
#> ── Attaching packages ─────────────────────────────────────── tidyterra 0.2.0 ──
#>
#> Suppress this startup message by setting Sys.setenv(tidyterra.quiet = TRUE)
#> ✔ tibble 3.1.7 ✔ dplyr 1.0.9
#> ✔ tidyr 1.2.0
library(ggplot2)
file <- system.file("extdata/cyl_tile.tif", package = "tidyterra")
tile <- terra::rast(file)
ggplot() +
geom_spatraster(data = tile, aes(fill = cyl_tile_1)) +
scale_fill_grey()
#> Error: Continuous value supplied to discrete scale
# Use a custom scale, scale_fill_grey is for discrete values
ggplot() +
geom_spatraster(data = tile, aes(fill = cyl_tile_1)) +
scale_fill_gradientn(colors = gray.colors(100,
start = 0.2,
end = .8, rev = TRUE
))
# Multiband
ggplot() +
geom_spatraster(data = tile) +
scale_fill_gradientn(colors = gray.colors(100,
start = 0.2,
end = .8, rev = TRUE
)) +
facet_wrap(~lyr)
# Now on multiband with different ranges
tile2 <- tile %>%
mutate(cyl_tile_2_mod = as.integer(100 * cyl_tile_2 / 250)) %>%
select(cyl_tile_1, cyl_tile_2_mod)
tile2
#> class : SpatRaster
#> dimensions : 212, 261, 2 (nrow, ncol, nlyr)
#> resolution : 2445.985, 2445.985 (x, y)
#> extent : -812067, -173664.9, 4852834, 5371383 (xmin, xmax, ymin, ymax)
#> coord. ref. : WGS 84 / Pseudo-Mercator (EPSG:3857)
#> sources : memory
#> memory
#> names : cyl_tile_1, cyl_tile_2_mod
#> min values : 35, 14
#> max values : 253, 100
ggplot() +
geom_spatraster(data = tile2) +
scale_fill_gradientn(colors = gray.colors(100,
start = 0.2,
end = .8, rev = TRUE
)) +
facet_wrap(~lyr)
Created on 2022-09-20 by the reprex package (v2.0.1)
Yes, many thanks, scale_fill_gradientn()
does the trick (with reverse=FALSE
in my case), although I'm not sure color scales are consistent across plots.
The idea of using facet_wrap()
is very good to ensure consistency. The problem is that I have to compare bands from one image to bands of another one, so I will have to select and build an intermediate multiband raster.
I have also problems with geom_spatraster_rgb()
, but this goes to another ticket.
Why not normalize data so you would have the same scale?
library(terra)
norm <- function(r){
mnv <- global(r, 'min',na.rm=TRUE)
mxv <- global(r, 'max',na.rm=TRUE)
r_n <- (r - mnv$min) / (mxv$max - mnv$min)
return(r_n)
}
That is what I often do with terra::stretch(),
but as the global range is known in this case (eg 0-4096), it would be easier to have a global scale.
It is not clear to me if geom_spatraster()
assumes that the range is within 0-255, and whether scale_fill_gradientn() distributes de 0-1 values (0.2-0.8 in the examples above) along the actual range of the data (data often do not actually span the 0-4096 range) or a custom range can be set.
So all this issue is more related with how to work with the ggplot2 system rather than a bug or issue on tidyterra. I would suggests to ask a question on https://stackoverflow.com/questions/tagged/ggplot2.
However, I think I can put some light on this. I assume scale_fill_gradientn()
distributes colors on the range of the values of the SpatRaster, but this can be adjusted with limits
. On your case I would try scale_fill_gradientn(..., limits = c(0,4096))
, that would re-escale the range of values of each SpatRaster to your desired range:
library(tidyterra)
#> ── Attaching packages ────────────────────────────────── tidyterra 0.2.0.9000 ──
#>
#> Suppress this startup message by setting Sys.setenv(tidyterra.quiet = TRUE)
#> ✔ tibble 3.1.8 ✔ dplyr 1.0.10
#> ✔ tidyr 1.2.1
library(ggplot2)
r <- terra::rast(system.file("extdata/cyl_temp.tif",
package = "tidyterra"
))
# Select two layers
r2 <- r %>%
mutate(tavg_05_mod = tavg_05 * 2) %>%
select(tavg_04, tavg_05_mod)
# Different ranges
r2
#> class : SpatRaster
#> dimensions : 89, 116, 2 (nrow, ncol, nlyr)
#> resolution : 3856.617, 3856.617 (x, y)
#> extent : 2893583, 3340950, 2019451, 2362690 (xmin, xmax, ymin, ymax)
#> coord. ref. : ETRS89-extended / LAEA Europe (EPSG:3035)
#> sources : memory
#> memory
#> names : tavg_04, tavg_05_mod
#> min values : 0.565614, 8.588204
#> max values : 13.283829, 33.481796
pal <- gray.colors(100, rev = FALSE)
ggplot() +
geom_spatraster(data = r2) +
facet_wrap(~lyr) +
scale_fill_gradientn(colours = pal, na.value = NA)
# Adjust limits
# Note the change on the legend and the colors of the plot
ggplot() +
geom_spatraster(data = r2) +
facet_wrap(~lyr) +
scale_fill_gradientn(
colours = pal, na.value = NA,
limits = c(0, 100)
)
# Individual plots with the same limits
ggplot() +
geom_spatraster(data = r2 %>% select(1)) +
facet_wrap(~lyr) +
scale_fill_gradientn(
colours = pal, na.value = NA,
limits = c(0, 100)
)
ggplot() +
geom_spatraster(data = r2 %>% select(2)) +
facet_wrap(~lyr) +
scale_fill_gradientn(
colours = pal, na.value = NA,
limits = c(0, 100)
)
Created on 2022-09-20 with reprex v2.0.2
I agree with closing, but please note that some of these examples would be very useful in the doc, where there is nothing of grey scale display. Also, I would personally prefer having a grey scale by default when using geom_spatraster() instead of the current shades of blue.
Many thanks for the excellent package and your kind assistance.
Thanks, regarding
Also, I would personally prefer having a grey scale by default when using geom_spatraster() instead of the current shades of blue.
This is to be tackle on ggplot2 not tidyterra. The default blue palette is the one in use for ggplot2:
library(ggplot2)
# Default palette on ggplot2
ggplot(faithfuld, aes(waiting, eruptions, fill = density)) +
geom_tile()
In the previous example tidyterra is not involved. But good news, as per ?scale_colour_continuous the default continuous palette con be changed with options(ggplot2.continuous.fill)
. See how we can change it to make a grey color palette as the default.
# Can be changed
# Get default
tmp <- getOption("ggplot2.continuous.fill") # store current setting
# Change the default
# Custom default palette function
scale_fill_cont_grey <- function(...) {
greycols <- grey.colors(2)
scale_fill_gradient(..., low = greycols[1], high = greycols[2], na.value = NA)
}
# Now make it by default
options(ggplot2.continuous.fill = scale_fill_cont_grey)
ggplot(faithfuld, aes(waiting, eruptions, fill = density)) +
geom_tile()
In the previous example, we didn't need to add scale_fill_gradient()
, so now after setting the option all your plots would have this new palette as the default palette.
Now see how this plays with tidyterra:
# Check with tidyterra
library(tidyterra)
#> ── Attaching packages ─────────────────────────────────────── tidyterra 0.2.0 ──
#>
#> Suppress this startup message by setting Sys.setenv(tidyterra.quiet = TRUE)
#> ✔ tibble 3.1.7 ✔ dplyr 1.0.9
#> ✔ tidyr 1.2.0
r <- terra::rast(system.file("extdata/cyl_temp.tif", package = "tidyterra"))
ggplot() +
geom_spatraster(data = r) +
facet_wrap(~lyr)
We got it! By last, restore the ggplot blue palette as the default palette:
# Restore the ggplot2 default option
options(ggplot2.continuous.fill = tmp)
# Now back to blue
ggplot() +
geom_spatraster(data = r) +
facet_wrap(~lyr)
Created on 2022-09-21 by the reprex package (v2.0.1)