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')
Cotton.Rockwood
  • 1,601
  • 12
  • 29
  • 2
    It's easier to help you if you include a simple [reproducible example](https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-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. – MrFlick Mar 14 '19 at 21:17
  • 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! – Rich Pauloo Mar 14 '19 at 23:50

1 Answers1

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

jbaums
  • 27,115
  • 5
  • 79
  • 119
  • 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. – Cotton.Rockwood Mar 28 '19 at 19:30