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

Community
  • 1
  • 1
Erich
  • 93
  • 1
  • 6
  • That's definitely possible. Could you post a reproducible example of what you want to achieve? – Martijn Tennekes Feb 17 '17 at 20:42
  • 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. – Erich Feb 21 '17 at 17:49

1 Answers1

7

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.

Martijn Tennekes
  • 1,951
  • 13
  • 19
  • Thank you so much! That is exactly what I was searching for. – Erich Feb 23 '17 at 20:47
  • But now how in the world do you make that legend bigger? I've tried all the available commands in the book: – Erich Feb 24 '17 at 00:05
  • 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? – Erich Feb 24 '17 at 18:28
  • Sorry, got it: Just needed to turn off `design.mode` and renew the `viewport`. Thanks again! Extremely helpful! – Erich Feb 24 '17 at 18:51