rggplot2terrarastervis

Non-linear diverging color scale for raster plot


I have a SpatRast object that contains a positive variable with a lower bound at 0. However, values below 1.3 indicate spatial dispersion, whereas values above 1.3 indicate spatial concentration. Therefore, I would like to map this raster using a color scale that diverges at 1.3. The values would need to be mapped in two distinct ways, that is continuously and in intervals; however, for both of these representations, the resulting legend would need to show 0 at the bottom and the maximum value at the top.

Despite my various attempts, I cannot manage to obtain what I want. Here I report a minimal example and the resulting figure using an interval representation, in which however I am unable to display the legend in reverse order. I would then also need to produce the corresponding representation treating the variable as continuous.

I am not bound to any specific library. With terra it seems impossible to reverse the legend order and it also seems impossible to obtain a non-linear customization of the color scale when the variable is to be plotted continuously. But I have tried also with rasterVis and ggplot2, and in each of these cases I have stumbled upon other obstacles. Also, in my example I obtain the color palette with RColorBrewer, but again I am not specifically bound to this library.

Can anybody help me? Thanks.

library(RColorBrewer)
bks <- c(0,0.4,0.8,1.3,2,4,8,15,30)
cold <- rev(RColorBrewer::brewer.pal(3, "Blues"))
warm <- RColorBrewer::brewer.pal(((length(bks)-1)-length(cold)), "Reds")
colors <- c(cold, warm)
terra::plot(x,
     breaks = bks, 
     col = colors,
     box=F,
     axes=F)

enter image description here


Solution

  • Example data:

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

    With terra > 1.8-36, you can reverse the interval legend like this:

    plot(x, col=colors, box=F, axes=F, breaks=bks, reverse=TRUE)
    # or 
    plot(classify(x, bks), col=colors, box=F, axes=F, reverse=TRUE)
    

    enter image description here

    With prior versions, you can achieve this by creating a categorical raster and use argument "sort" like this:

    b <- classify(x, bks)
    ord <- rev(levels(b)[[1]][,2])
    terra::plot(b, col = colors, box=F, axes=F, sort=ord)
    

    For the continuous legend, you can make two color palettes, and then create colors with each, proportionally to the range of values before and after the break

    crp1 <- colorRampPalette(colors[1:3])
    crp2 <- colorRampPalette(colors[4:8])
    cols <- c(crp1(10), crp2(10 * (30-1.3)/1.3))
    plot(x, col=cols, plg=list(at=c(0,1.3, 5, 10, 20, 30), digits=1), 
             range=c(0,30), axes=FALSE)
    

    enter image description here