Search code examples
rggplot2raster

Mapping Raster Data Using ggplot2 with Multiple Color Scales


Question 1: I am using the ggplot2 package. I want to map two different raster variables, one over global water bodies (variable 1) and the other one over global lands (variable 2). I need two different color scales for each of them. As far as I know, when you use ggplot2, just one color scale is accepted. I have two color palettes, "jet.colors" and "YlOrBr" and I would like to assign each of them to one of the variables. These figures show spatial coverage of the two variables using solid colors and just one color palette(jet.colors). I want to assign jet.colors to the variable 1 and YlOrBr to the variable 2. The code and the data are provided.

Question 2: In my project, variable1 and variable2 both have 3 dimensions, this dimension implies for time (3rd dimension’s size is 50). I want to plot 50 maps. I am interested to have one color scale for each variable for all the maps. The maximum and minimum of the color scales are defined as max(variable1[,3,],na.rm=TRUE) and min(variable1[,3,],na.rm=TRUE) and max(variable2[,3,],na.rm=TRUE) and min(variable2[,3,],na.rm=TRUE) for Variable1 and Variable2 respectively. To get one color scale for each variable, after getting the max and min of two variables based on the original data (that was explained), I rescaled variable1[,3,] and variable3[,3,] altogether. I would appreciate your help.

jet.colors <- colorRampPalette(c("#00007F", "blue", "#007FFF", "cyan", 
"#7FFF7F", "yellow", "#FF7F00", "red", "#7F0000"))
YlOrBr <- c("#FFFFD4", "#FED98E", "#FE9929", "#D95F0E", "#993404")
Variable1 <- read.table("Variable1.txt",header = TRUE, sep = "\t");
Variable2  <- read.table("Variable2.txt",header = TRUE, sep = "\t");

Variable1=as.data.frame(Variable1)
Variable2=as.data.frame(Variable2)

library(ggplot2)
map.world <- map_data("world")
gg <- ggplot()
gg <- gg + geom_raster(data=  Variable1, aes(  Variable1$V2,   
Variable1$V1,fill=  Variable1$V3 ))
gg <- gg + geom_raster(data=  Variable2, aes(  Variable2$V2,   
Variable2$V1,fill=  Variable2$V3 ))
gg <- gg  +scale_fill_gradientn(colours=jet.colors(7))
gg <- gg + geom_map(dat=map.world, map = map.world, aes(map_id=region), 
fill="NA", color="black",size=0.4)
gg <- gg + expand_limits(x = map.world$long, y = map.world$lat)
gg <- gg + theme(panel.grid=element_blank(), panel.border=element_blank())
gg <- gg + theme(axis.ticks=element_blank(), axis.text=element_blank())
gg <- gg + theme(legend.position="right",plot.title = element_text(size = 10, 
face = "bold"))
gg <- gg 
+theme(axis.title.x=element_blank())+theme(axis.title.y=element_blank())
gg <- gg+ coord_equal() 
gg 

You can get the data here


Solution

  • I got around this restriction in ggplot by mapping the fill to non-overlapping regions:

    library(ggplot2)
    library(scales)
    library(gridExtra)
    library(grid)
    
    jet.colors <- colorRampPalette(c("#00007F", "blue", "#007FFF", "cyan", "#7FFF7F", "yellow", "#FF7F00", "red", "#7F0000"))
    YlOrBr <- c("#FFFFD4", "#FED98E", "#FE9929", "#D95F0E", "#993404")
    Variable1 <- read.table("Variable1.txt",header = TRUE, sep = "\t");
    Variable2  <- read.table("Variable2.txt",header = TRUE, sep = "\t");
    
    Variable1=as.data.frame(Variable1)
    Variable2=as.data.frame(Variable2)
    Variable1$V3_rescale = rescale(Variable1$V3)
    Variable2$V3_rescale = rescale(Variable2$V3)+100 # Map to non-overlapping range
    
    
    map.world <- map_data("world")
    gg <- ggplot() + 
      geom_raster(data= Variable1, aes(V2,V1,fill=V3_rescale)) +
      geom_raster(data=Variable2, aes(V2,V1,fill=V3_rescale)) + 
      scale_fill_gradientn(
        colours=c(jet.colors(7),YlOrBr),
        values = rescale(
          c(rescale(seq(from = min(Variable1$V3), # range for Variable 1
                        to = max(Variable1$V3),
                        length.out=7)),
            rescale(seq(from = min(Variable2$V3), # range for Variable 2
                        to = max(Variable2$V3),
                        length.out = length(YlOrBr)))+100)))
    
    gg <- gg + geom_map(dat=map.world, map = map.world, aes(map_id=region), 
                        fill="NA", color="black",size=0.4)
    gg <- gg + expand_limits(x = map.world$long, y = map.world$lat)
    gg <- gg + theme(panel.grid=element_blank(), panel.border=element_blank())
    gg <- gg + theme(axis.ticks=element_blank(), axis.text=element_blank())
    gg <- gg + theme(legend.position="right",plot.title = element_text(size = 10, 
                                                                       face = "bold"))
    gg <- gg +theme(axis.title.x=element_blank())+theme(axis.title.y=element_blank())
    gg <- gg + coord_equal() + theme(legend.position = "none") # remove legend
    

    To add the legend, I modified the answer from this post Inserting a table under the legend in a ggplot2 histogram:

    # Make plot with only Variable 1 to extract legend
    Variable1_plot <- ggplot() + 
      geom_raster(data= Variable1, aes(V2,V1,fill=V3)) + 
      scale_fill_gradientn(colours = jet.colors(7)) + 
      guides(fill=guide_legend(title="Variable1"))
    
    # Make plot with only Variable 2 to extract legend
    Variable2_plot <- ggplot() + 
      geom_raster(data= Variable2, aes(V2,V1,fill=V3)) + 
      scale_fill_gradientn(colours = YlOrBr) + 
      guides(fill=guide_legend(title="Variable2"))
    
    #Extract Legend
    g_legend <- function(a.gplot){
      tmp <- ggplot_gtable(ggplot_build(a.gplot))
      leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
      legend <- tmp$grobs[[leg]]
      return(legend)}
    
    legend_Variable1 <- g_legend(Variable1_plot)
    legend_Variable2 <- g_legend(Variable2_plot)
    
    grid.newpage()
    vp1 <- viewport(width = 0.75, height = 1, x = 0.375, y = .5)
    vpleg <- viewport(width = 0.25, height = 0.5, x = 0.85, y = 0.75)
    subvp <- viewport(width = 0.3, height = 0.3, x = 0.85, y = 0.25)
    print(gg, vp = vp1)
    upViewport(0)
    pushViewport(vpleg)
    grid.draw(legend_Variable2)
    #Make the new viewport active and draw
    upViewport(0)
    pushViewport(subvp)
    grid.draw(legend_Variable1)