Plotting a raster with exponential or quantile color ramp diverging around zero
Asked Answered
A

1

3

I am using the R function levelplot() from the rasterVis package to plot a stack of three rasters with a single diverging color ramp. I would like to change the scale of a raster color ramp so that the map accentuates differences in lower values. This can be done by non-linear binning of the color breaks.

I'm using the code from a gist written by @jbaums (code included below). Any suggestions on how to adjust the color ramp in this code so that the breaks follow 2^x but the min and max values are preserved? It would seem that changing the sequences of s (below) would have the desired effect.

diverge0 <- function(p, ramp) {
  # p: a trellis object resulting from rasterVis::levelplot
  # ramp: the name of an RColorBrewer palette (as character), a character 
  #       vector of colour names to interpolate, or a colorRampPalette.
  require(RColorBrewer)
  require(rasterVis)
  if(length(ramp)==1 && is.character(ramp) && ramp %in% 
     row.names(brewer.pal.info)) {
    ramp <- suppressWarnings(colorRampPalette(brewer.pal(11, ramp)))
  } else if(length(ramp) > 1 && is.character(ramp) && all(ramp %in% colors())) {
    ramp <- colorRampPalette(ramp)
  } else if(!is.function(ramp)) 
    stop('ramp should be either the name of a RColorBrewer palette, ', 
         'a vector of colours to be interpolated, or a colorRampPalette.')
  rng <- range(p$legend[[1]]$args$key$at)
  s <- seq(-max(abs(rng)), max(abs(rng)), len=1001)
  i <- findInterval(rng[which.min(abs(rng))], s)
  zlim <- switch(which.min(abs(rng)), `1`=i:(1000+1), `2`=1:(i+1))
  p$legend[[1]]$args$key$at <- s[zlim]
  p$par.settings$regions$col <- ramp(1000)[zlim[-length(zlim)]]
  p
}

And here is some code that applies this function:

library (rasterVis)

ras1 <- raster(nrow=10,ncol=10) 
set.seed(1) 
ras1[] <- rchisq(df=10,n=10*10) 
ras2 <- ras1*(-1)/2 
s <- stack(ras1,ras2) 

p <- levelplot(s, par.settings=RdBuTheme())

diverge0(p, ramp='RdBu')
Aerophyte answered 14/3, 2019 at 21:12 Comment(2)
It's easier to help you if you include a simple reproducible example with sample input and desired output that can be used to test and verify possible solutions. Provide some sample code that actually calls this function that shows what you expect the output to be.Trainor
It sounds like you need to cut() your continuous field value into a vector of factors (bins). I've done this a fair amount with raster objects, then piped them into ggplot. For example: as.data.frame(my_raster, xy = TRUE) %>% ggplot(aes(x,y, fill = cut_bins)) + geom_raster() + scale_fill_manual() + theme_void(). Then in scale_fill_manual you specify the values, breaks, and labels for each bin. Complete control!Sporocyte
P
3

You can probably avoid diverge0 for this, instead defining your vector of breaks and letting levelplot take care of the rest.

library(rasterVis)

You can either define breaks manually:

b <- c(-10.289, -8, -4, -2, -1, 0, 1, 2, 4, 8, 16, 20.578)

or automate it:

rng <- range(cellStats(s, range))
lim <- ceiling(log(abs(rng), 2))
b <- sort(c(0, unique(unlist(mapply(function(x, y) y*2^(0:x), lim, sign(rng))))))
b[1] <- rng[1]
b[length(b)] <- rng[2]

Then pass the breaks to at, and the tick locations/labels to colorkey$labels:

p <- levelplot(s, par.settings=RdBuTheme(), at=b, 
               colorkey=list(height=0.8, labels=list(at=b, labels=round(b, 2))))
p

enter image description here

Pleiad answered 20/3, 2019 at 5:53 Comment(1)
Thanks @jbaums. This is similar to what I did... I realized that in order to automate the interval selection, I needed to go back to the raster data since only the min/max are stored in the rasterVis object.Aerophyte

© 2022 - 2024 — McMap. All rights reserved.