r-tmap/tmap

Alignment of plots with tmap_arrange() with different legend size

olivroy opened this issue · 1 comments

Something else that I noticed was the alignment of legends (left = v3, right = v4)
image

Originally posted by @olivroy in edzer/sdsr#119 (comment)

data(pol_pres15, package = "spDataLarge")
m3 <- tm_shape(pol_pres15) +
  tm_fill("hs_C", 
    palette = RColorBrewer::brewer.pal(4, "Set3")[c(4, 1, 3, 2)],
    colorNA = "grey95", textNA = "Not \"interesting\"",
    title = "First round turnout\nLocal Geary C") +
    tm_layout(legend.outside=TRUE, legend.outside.position="bottom")
m4 <- tm_shape(pol_pres15) +
  tm_fill("hs_C_II", 
    palette = RColorBrewer::brewer.pal(4, "Set3")[c(4, 1, 3, 2)], 
    colorNA = "grey95", textNA = "Not \"interesting\"",
    title="Second round turnout\nLocal Geary C") +
    tm_layout(legend.outside=TRUE, legend.outside.position="bottom")
m5 <- tm_shape(pol_pres15) +
  tm_fill("hs_MvC", 
    palette = RColorBrewer::brewer.pal(4, "Set3")[c(4, 1)],
    colorNA = "grey95", textNA = "Not \"interesting\"",
    title = "Both rounds turnout\nLocal Multivariate Geary C") +
    tm_layout(legend.outside=TRUE, legend.outside.position="bottom")
tmap_arrange(m3, m4, m5, nrow=1)

tmap_arrange does not use align layouts of multiple maps. The maps are rendered independently. The difference is that in v3, the outside-legend-margin was fixed, and in v4, this is adjusted to the map aspect ratio and legend size. The downside of this improvement is illustrated by the misalignment in this example. This can be fixed by setting the margin in which the legend is drawn manually. It is called the 'meta.margins' where the first value is bottom margin:

m3 = tm_shape(pol_pres15) +
    tm_fill("hs_C",
            fill.scale = tm_scale_categorical(values = cols4all::c4a("brewer.set3", order = c(4, 1, 3, 2)),
                                              value.na = "grey95",
                                              label.na = "Not \"interesting\""),
            fill.legend = tm_legend("First round turnout\nLocal Geary C", position = tm_pos_out("center", "bottom"))) +
    tm_layout(meta.margins = c(.2, 0, 0, 0))

m4 = tm_shape(pol_pres15) +
    tm_fill("hs_C_II",
            fill.scale = tm_scale_categorical(values = cols4all::c4a("brewer.set3", order = c(4, 1, 3, 2)),
                                              value.na = "grey95",
                                              label.na = "Not \"interesting\""),
            fill.legend = tm_legend("Second round turnout\nLocal Geary C", position = tm_pos_out("center", "bottom"))) +
    tm_layout(meta.margins = c(.2, 0, 0, 0))
m5 <- tm_shape(pol_pres15) +
    tm_fill("hs_MvC", 
            fill.scale = tm_scale_categorical(values = cols4all::c4a("brewer.set3", order = c(4, 1)),
                                              value.na = "grey95",
                                              label.na = "Not \"interesting\""),
            fill.legend = tm_legend("Both rounds turnout\nLocal Multivariate Geary C", position = tm_pos_out("center", "bottom"))) +
    tm_layout(meta.margins = c(.2, 0, 0, 0))

tmap_arrange(m3, m4, m5, nrow=1)
#> [plot mode] fit legend/component: Some legend items or map compoments do not fit well, and are therefore rescaled. Set the tmap option 'component.autoscale' to FALSE to disable rescaling.
#> [plot mode] fit legend/component: Some legend items or map compoments do not fit well, and are therefore rescaled. Set the tmap option 'component.autoscale' to FALSE to disable rescaling.

In case the legend is shared (not sure if that is supposed to be in this example, you can set the .free argument, and use panel labels:

tm_shape(pol_pres15) +
    tm_fill(c("hs_C", "hs_C_II", "hs_MvC"),
            fill.scale = tm_scale_categorical(values = cols4all::c4a("brewer.set3", order = c(4, 1, 3, 2)),
                                              value.na = "grey95",
                                              label.na = "Not \"interesting\""),
            fill.free = FALSE,
            fill.legend = tm_legend("Local Geary C", position = tm_pos_out("center", "bottom"))) +
    tm_layout(panel.labels = c("First round turnout", "Second round turnout", "Both rounds turnout"))