Search code examples
rr-rasterterra

Plotting terra raster with white color set for 0 values


I have a raster in terra that has values that ranges around 0. I want my negative values to be blue and positive values to be red. This question has already been ask here and here, however the answer is for the raster package and do not apply for the terra package has there is no breakpoints argument.

Here is what I tried so far:

# preparing the session
library(terra)
library(magrittr)

# preparing the raster, I'm making sure my data go through 0 but are not centered at 0
set.seed(1234)
rr <- rast(matrix(rnorm(400, 1.5, 1), nrow=20, ncol=20))

Even if my values are not centered at 0, I want my color intensity to be centered at 0, to do so, I find the range that I need :

the_range <- range(values(rr)) %>% abs %>% max %>% multiply_by(c(-1,1))

Then I create my color palette:

the_palette_fc <- leaflet::colorNumeric(palette = "RdBu", domain = the_range, reverse = T)

If I set, for example, 31 color classes to my map and plot it normally with terra I get:

plot(rr, col=the_palette_fc(seq(the_range[1], the_range[2], length.out=31)))

enter image description here

You can see that the white color was not fixed to the zero value. Thats is probably because terra::plot calculate it's own range which is different from mine.

Reading around online, I found a possible option which is to play with the coltab argument. I tried it 2 ways:

coltab(rr) <- data.frame(
  val=seq(the_range[1], the_range[2], length.out=31),
  col=the_palette_fc(seq(the_range[1], the_range[2], length.out=31)))

plot(rr)

enter image description here

and:

coltab(rr) <- data.frame(
  val=values(rr),
  col=the_palette_fc(values(rr)))

plot(rr)

enter image description here

But the result do not seem to give the wanted results. Also, there is no legend automatically added which is also necessary. Maybe the coltab argument should be only used for categorical values?

So is there a way in terra to fined tune the colors palette so white is fix for 0 values?


Solution

  • To set 0 as the midpoint, you can set the domain of your palette to c(-max(rr[]), max(rr[])):

    the_palette_fc <- leaflet::colorNumeric(palette = "RdBu", 
                                            domain = c(-max(rr[]), max(rr[])),
                                            reverse = TRUE)
    
    the_colors <- the_palette_fc(seq(min(rr[]), max(rr[]), length.out = 50))
    
    plot(rr, col = the_colors)
    

    enter image description here

    If you want the scale to go from darkest blue to darkest red with a transition of white at 0, it is a bit more involved, but you could do:

    the_palette_fc <- leaflet::colorNumeric(palette = "RdBu", 
                                            domain = c(-max(rr[]), max(rr[])),
                                            reverse = TRUE)
    
    breakpoints <- seq(min(rr[]), max(rr[]), length.out = 50)
    
    the_colors <- the_palette_fc(c(seq(-max(rr[]), 0, length = sum(breakpoints < 0)),
                                  seq(0, max(rr[]), length = sum(breakpoints > 0))))
    
    plot(rr, col = the_colors)
    

    enter image description here