Draw multiple object of class tmap with just one common legend
Asked Answered
S

1

5

I am trying to draw multiple maps from the tmap-package using tm_shape() and tm_layout() in one page using grid.layout() from the grid-package. I would like to plot only one common legend for all maps, similar to the example shown here:

ggplot separate legend and plot

Unfortunately, tmap does not provide a ggplot-object. Does anybody know how to do the same with tmaps? Here is a reproducible example:

data(World, rivers, metro)

# creating two separate maps
africa <- World[World@data$continent=='Africa',]
asia <- World[World@data$continent=='Asia',]

my.breaks <- seq(0,80,20)

africa.map <- tm_shape(africa) + 
  tm_fill("HPI",style = 'fixed',breaks = my.breaks) + 
  tm_layout(bg.color = "white", legend.text.size = 1.3, legend.width = 0.6,
            legend.outside=TRUE, legend.outside.position = 'top', 
            legend.outside.size = .1, legend.position = c(0.8, 0.2))

asia.map <- tm_shape(asia) + 
  tm_fill("HPI",style = 'fixed',breaks = my.breaks) + 
  tm_layout(bg.color = "white", legend.text.size = 1.3, legend.width = 0.6,
            legend.outside=TRUE, legend.outside.position = 'top', 
            legend.outside.size = .1, legend.position = c(0.8, 0.2))

page.layout <- grid.layout(nrow = 8, ncol = 5,
                           widths = unit(c(1), "null"),
                           heights = unit(c(1), "null"),
                           default.units = "null",
                           respect = FALSE,
                           just = "centre")

grid.newpage()
pushViewport(viewport(layout = page.layout))
grid.text(paste('Happy Planet Index'), 
          vp = viewport(layout.pos.row = 1, layout.pos.col = 1:5),gp=gpar(fontsize=20))

grid.text('Africa', vp = viewport(layout.pos.row = 2, layout.pos.col = 1:2),gp=gpar(fontsize=20))
print(africa.map, vp=viewport(layout.pos.row = 3:6, layout.pos.col = 1:2))

grid.text('Asia', vp = viewport(layout.pos.row = 2, layout.pos.col = 3:5),gp=gpar(fontsize=20))
print(asia.map, vp=viewport(layout.pos.row = 3:6, layout.pos.col = 3:5))

Best, Erich

Slash answered 16/2, 2017 at 18:0 Comment(2)
That's definitely possible. Could you post a reproducible example of what you want to achieve?Samanthasamanthia
Thanks Martijn! I added an example. What I want to do is to draw only one common legend for both maps, best by separating the legend from the map and plotting it separately anywhere in the plotting area as in the ggplot2 example. But I'm happy if you have a better solution.Slash
S
8

I don't know if this is what you mean:

data(World)

tm_shape(World) +
    tm_polygons(c("economy", "HPI")) +
    tm_layout(legend.outside = TRUE)

The common legend is plotted outside the maps. Since both maps have different legends, only the legend of the first map is taken.

There are also ways to use grid.layout. In that case, you'll need to print tmap maps in grid viewports (see print.tmap), and the legend in another one.

UPDATE:

Just to be complete: normally, there should be a way to do this with facets:

data(World)
AfAs <- World[World@data$continent %in% c('Africa', 'Asia'),]
tm_shape(AfAs) + 
    tm_fill("HPI",style = 'fixed',breaks = my.breaks) + 
    tm_facets(by = "continent", drop.units = TRUE, free.coords = TRUE)

If you need to plot two tmap calls, it is most convenient to process the legend as a separate map, with legend.only=TRUE:

africa.map <- tm_shape(africa) + 
    tm_fill("HPI",style = 'fixed',breaks = my.breaks) +
    tm_layout(legend.show = FALSE)

asia.map <- tm_shape(asia) + 
    tm_fill("HPI",style = 'fixed',breaks = my.breaks) +
    tm_layout(legend.show = FALSE)

legend.map <- tm_shape(africa) + 
    tm_fill("HPI",style = 'fixed',breaks = my.breaks) +
    tm_layout(legend.only = TRUE)

grid.newpage()
page.layout <- grid.layout(nrow = 2, ncol = 2, widths=c(.4,.6), heights=c(.6,.4))
pushViewport(viewport(layout = page.layout))

print(africa.map, vp=viewport(layout.pos.row = 1:2, layout.pos.col = 1))
print(asia.map, vp=viewport(layout.pos.row = 1, layout.pos.col = 2))
print(legend.map, vp=viewport(layout.pos.row = 2, layout.pos.col = 2))

UPDATE 2

The legend can be enlarged with this code:

legend.map <- tm_shape(africa) + 
    tm_fill("HPI",style = 'fixed',breaks = my.breaks) +
    tm_layout(legend.only = TRUE, design.mode=TRUE, scale=2, asp=0)

design.mode=TRUE is handy when designing a map. I had to set asp to 0, because it took the aspect ratio of Africa: this is an error, since the aspect ratio should follow the device/viewport when legend.only=TRUE. This is fixed in the devel version.

Samanthasamanthia answered 17/2, 2017 at 20:48 Comment(4)
Thank you so much! That is exactly what I was searching for.Slash
But now how in the world do you make that legend bigger? I've tried all the available commands in the book:Slash
Sorry to bother you again. This does indeed make the legend bigger, but now I got a yellow "device" area and a blue "frame" around my legend. I played around with the inner and outer margins, as well as the asp argument, but can't make it go away. Any hints?Slash
Sorry, got it: Just needed to turn off design.mode and renew the viewport. Thanks again! Extremely helpful!Slash

© 2022 - 2024 — McMap. All rights reserved.