Tmap: Question - bivariate map

Created on 7 Apr 2018  路  13Comments  路  Source: mtennekes/tmap

@mtennekes do you plan to implement possibility to create bivariate maps (e.g. http://axismaps.github.io/thematic-cartography/articles/bivariate_choropleth.html)? Or maybe could be done already?

enhancement

Most helpful comment

Another (working, but not perfectly integrated with tmap) solution to create bivariate maps can be seen below.
It was inspired by the work of @sdesabbata (see https://github.com/sdesabbata/BivariateTMap).
@mtennekes can we think of integrating it into tmap somehow?

library(tmap)
library(sf)
library(stars)
library(spData)
library(classInt)
library(lattice)
library(grid)
library(pals)
legend_creator = function(col.regions, xlab, ylab, nbins){
  bilegend = levelplot(matrix(1:(nbins * nbins), nrow = nbins),
                       axes = FALSE, col.regions = col.regions,
                       xlab = xlab, ylab = ylab,
                       cuts = 8, colorkey = FALSE, scales = list(draw = 0))
  bilegend
}
add_new_var = function(x, var1, var2, nbins, style = "quantile"){
  class1 = suppressWarnings(findCols(classIntervals(c(x[[var1]]), 
                                                    n = nbins, 
                                                    style = style)))

  class2 = suppressWarnings(findCols(classIntervals(c(x[[var2]]), 
                                                    n = nbins, 
                                                    style = style)))

  x$new_class = class1 + nbins * (class2 - 1)
  return(x)
}
# vector example ----------------------------------------------------------
africa = subset(world, continent == "Africa")
africa = add_new_var(africa, 
                     var1 = "lifeExp", 
                     var2 = "gdpPercap", 
                     nbins = 3)

bilegend = legend_creator(stevens.pinkblue(n = 9), 
                          xlab = "lifeExp", 
                          ylab = "gdpPercap", 
                          nbins = 3)

bimap = tm_shape(africa) +
  tm_polygons("new_class", style = "cat", palette = stevens.pinkblue(n = 9)) +
  tm_layout(legend.show = FALSE)

grid.newpage()
print(bimap)
vp = viewport(x = 0.25, y = 0.25, width = 0.25, height = 0.25)
pushViewport(vp)
print(bilegend, newpage = FALSE)

# raster example ----------------------------------------------------------
data("land")
land = add_new_var(land, 
                   var1 = "trees", 
                   var2 = "elevation",
                   nbins = 3, 
                   style = "equal")

bilegend = legend_creator(stevens.pinkblue(n = 9),
                          xlab = "trees", 
                          ylab = "elevation",
                          nbins = 3)

bimap = tm_shape(land) +
  tm_raster("new_class", style = "cat", palette = stevens.pinkblue(n = 9)) +
  tm_layout(legend.show = FALSE)

grid.newpage()
print(bimap)
vp = viewport(x = 0.15, y = 0.45, width = 0.25, height = 0.25)
pushViewport(vp)
print(bilegend, newpage = FALSE)

Created on 2020-08-07 by the reprex package (v0.3.0)

All 13 comments

Coincidentally @eugenividal and I discussed this last week. It can be done and I think the example below with 2 or 3 layers could be useful in some cases, it's just the legend that's tricky to create it seems:

``` r
library(tmap)
data("World")
names(World)

> [1] "iso_a3" "name" "sovereignt" "continent"

> [5] "subregion" "area" "pop_est" "pop_est_dens"

> [9] "gdp_md_est" "gdp_cap_est" "economy" "income_grp"

> [13] "life_exp" "well_being" "HPI"

m1 = tm_shape(World) + tm_fill("pop_est", alpha = 0.3, "Purples")
m2 = tm_shape(World) + tm_fill("HPI", alpha = 0.3, palette = "Blues")
m3 = tm_shape(World) + tm_fill("life_exp", alpha = 0.3, palette = "Oranges")
m1 + m2 + m3
```

Wow, be careful there! The order of semi-transparent layers will influence the outcome colors. See for instance m2 + m1 + m3.

It would definitely be nice to support bivariate maps. Any ideas how to implement them?

Indeed - just a starter for 10. The legend in that pals package vignette looks awesome, thanks for sharing.

For context / interest - this is I think a 'trivariate' colourscheme generator: https://github.com/jschoeley/tricolore

@mtennekes this could be useful - https://github.com/Robinlovelace/geocompr/issues/376

Another (working, but not perfectly integrated with tmap) solution to create bivariate maps can be seen below.
It was inspired by the work of @sdesabbata (see https://github.com/sdesabbata/BivariateTMap).
@mtennekes can we think of integrating it into tmap somehow?

library(tmap)
library(sf)
library(stars)
library(spData)
library(classInt)
library(lattice)
library(grid)
library(pals)
legend_creator = function(col.regions, xlab, ylab, nbins){
  bilegend = levelplot(matrix(1:(nbins * nbins), nrow = nbins),
                       axes = FALSE, col.regions = col.regions,
                       xlab = xlab, ylab = ylab,
                       cuts = 8, colorkey = FALSE, scales = list(draw = 0))
  bilegend
}
add_new_var = function(x, var1, var2, nbins, style = "quantile"){
  class1 = suppressWarnings(findCols(classIntervals(c(x[[var1]]), 
                                                    n = nbins, 
                                                    style = style)))

  class2 = suppressWarnings(findCols(classIntervals(c(x[[var2]]), 
                                                    n = nbins, 
                                                    style = style)))

  x$new_class = class1 + nbins * (class2 - 1)
  return(x)
}
# vector example ----------------------------------------------------------
africa = subset(world, continent == "Africa")
africa = add_new_var(africa, 
                     var1 = "lifeExp", 
                     var2 = "gdpPercap", 
                     nbins = 3)

bilegend = legend_creator(stevens.pinkblue(n = 9), 
                          xlab = "lifeExp", 
                          ylab = "gdpPercap", 
                          nbins = 3)

bimap = tm_shape(africa) +
  tm_polygons("new_class", style = "cat", palette = stevens.pinkblue(n = 9)) +
  tm_layout(legend.show = FALSE)

grid.newpage()
print(bimap)
vp = viewport(x = 0.25, y = 0.25, width = 0.25, height = 0.25)
pushViewport(vp)
print(bilegend, newpage = FALSE)

# raster example ----------------------------------------------------------
data("land")
land = add_new_var(land, 
                   var1 = "trees", 
                   var2 = "elevation",
                   nbins = 3, 
                   style = "equal")

bilegend = legend_creator(stevens.pinkblue(n = 9),
                          xlab = "trees", 
                          ylab = "elevation",
                          nbins = 3)

bimap = tm_shape(land) +
  tm_raster("new_class", style = "cat", palette = stevens.pinkblue(n = 9)) +
  tm_layout(legend.show = FALSE)

grid.newpage()
print(bimap)
vp = viewport(x = 0.15, y = 0.45, width = 0.25, height = 0.25)
pushViewport(vp)
print(bilegend, newpage = FALSE)

Created on 2020-08-07 by the reprex package (v0.3.0)

Hi all, just thought would flag this convo , it's not tmap but same principle should apply...

Thanks for you inputs, @Nowosad and @aspina7.

Although the implementation is not hard, there are a few difficult obstacles that need to be taken:

User input How does a user specify a bivariate aesthetic? This is not straightforward; tm_polygons(c("varA", "varB")) interferes with drawing two small multiples, one for varA and one for varB. Since we use standard evaluation, we could introduce a new operator, e.g. tm_polygons("varA _x_ varB"). Any ideas?

Color palettes I'm not sure which bivariate color palettes to use. My initial approach would probably be to use the HCL color space. However, it would also be good to have a palette with yellow as neutral mid-color, since it is better distinguishable from gray that is often used for missing values. Update: I just discovered your package colorblindcheck and vignette about bivatiate palettes (https://nowosad.github.io/colorblindcheck/articles/articles/check_bivariate_pals.html) Awesome work @Nowosad! Also didn't know that there are already a lot of bivariate palettes in pals. Which ones would you prefer in tmap?

Legend This is not a question of how to implement it, but this would take time. On the other hand, there is no distinction between portrait and landscape for this legend type.

  1. User input - my thinking is as following:
  2. One new argument (e.g., is_bivariate, FALSE by default). When this argument is FALSE then tm_polygons(c("varA", "varB")) creates two small multiples, and when this argument is TRUE then we have one bivariate map.
  3. Every tmap color scale style for discrete maps (https://geocompr.github.io/post/2019/tmap-color-scales/) should work for bivariate maps (e.g, "fisher", "quantile", "equal", etc.). It includes "fixed" style as well.
  4. When is_bivariate is TRUE, then only a subset of color palettes (see below) is possible.
  5. Probably, there are also some other things to consider, but that is all of what I can think of now...
  6. Color palettes - The pals package and the blog post "How to choose a bivariate color palette?" could be used as starting points. (I also think there is a lot more to explore in this topic)
  7. Legend - I would assume that a proper bivariate legend would have some x and y values on both axis.

it is possible to save it via tmap_save()

it is possible to save it via tmap_save()

Not yet.

Was this page helpful?
0 / 5 - 0 ratings