`component.position` of `tm_layout` seems not to work
I've tried to use component.position of tm_layout to place all of the components in the same place -- but it seems not to work. Reprex:
library(tmap)
data("land")
tm_shape(land[1,]) +
tm_raster() +
tm_scalebar() +
tm_layout(component.position = tm_pos("left", "center"))
#> [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 = FALSE` to disable rescaling.
#> Scale bar set for latitude km and will be different at the top and bottom of the map.

Good one.
component.position is currently only used as fallback option, in case x.position is not specified inside the options. However, most component types have those options specified, e.g. legend.position and scalebar.position.
But that's not very user friendly. In the past I've created the shortcut functions like tm_place_legends_bottom, but these only apply to legends, not other map components.
What do we do?
- Let
component.position(when specified viatm_layout) overrule allx.positiondefaults. - Add a shortcut
tm_place_components(pos) - ...
I guess that option 1 makes more sense from a user perspective. However, I image that it's also desirable to have the legends in a different corner than the other map components.
@Nowosad an idea:
Shall we limit the argument list of tm_layout to those that are intended to be specified, i.e. the "overall" layout options? So all those related to frame, bg, panel, etc.
Now tm_layout is full of default settings, e.g. legend.text.size, which is the default for all legend text sizes. Normally, this is specified in the tm_legend calls.
For changing the default options, one can always use tm_options. For backwards compatibility we can have ... in tm_layout for the non-overall layout options.
We can still have the most useful 'default' settings in tm_layout, e.g. component.position`.
@mtennekes -- I think this limitation is a good idea. I've been recently working on the layout chapter, and looking at the v. long list of arguments was a bit overwhelming.
Way better now :-)
Just a start:
> formals(tm_layout) |> names()
[1] "scale" "asp" "bg" "bg.color" "outer.bg"
[6] "outer.bg.color" "frame" "frame.color" "frame.alpha" "frame.lwd"
[11] "frame.r" "frame.double_line" "outer.margins" "inner.margins" "inner.margins.extra"
[16] "meta.margins" "meta.auto_margins" "between_margin" "panel.margin" "panel.type"
[21] "panel.wrap.pos" "panel.xtab.pos" "color.sepia_intensity" "color.saturation" "color_vision_deficiency_sim"
[26] "panel.show" "panel.labels" "panel.label.size" "panel.label.color" "panel.label.fontface"
[31] "panel.label.fontfamily" "panel.label.alpha" "panel.label.bg" "panel.label.bg.color" "panel.label.bg.alpha"
[36] "panel.label.frame" "panel.label.frame.color" "panel.label.frame.alpha" "panel.label.frame.lwd" "panel.label.frame.r"
[41] "panel.label.height" "panel.label.rot" "earth_boundary" "earth_boundary.color" "earth_boundary.lwd"
[46] "earth_datum" "space.color" "..."
So all element related arguments (defaults) have been removed (these can still be set via tm_options). However, as you can see there is one 'element' that does not have a stand-alone tm_ function: the panels. Would it make sense to migrate them to tm_panels?
Please review @Nowosad @Rapsodia86 @jguelat @aonojeghuo others. Are there arguments missing? Are there arguments that could be removed? And what are your thoughts about panels?
Other question: are there default options, that apply to all elements, such as text.fontfamily, that are worthwhile to have in tm_layout?
About the initial topic of this issue, the position of all map components: would it be handy to set them like this:
library(tmap)
data("land")
tm_shape(land[1,]) +
tm_raster() +
tm_scalebar() +
tm_comp_group(position = tm_pos("left", "center"))
So calling comp_group without a group_id specification will apply to all components. Do you like this idea?
@mtennekes
- Yes -- I think that general options, such as the
text.fontfamilyargument should be intm_layout - I like the
tm_comp_groupidea -- I think it should work as you suggested above - Do we even need
tm_panels? Cannot these options be specified either in sometm_facetfunction or just kept intm_layout?
Thx @Nowosad
- So
text.fontfamilyandtext.fontfacewill be included. Totally agree. Not sure which else. An edge case isattr.color. It determines the default color of all non-data objects, such as text and frame colors, and map components like compass and scalebar. See eg..
tm_shape(World) +
tm_polygons("HPI") +
tm_crs("auto") +
tm_scalebar() +
tm_title("World") +
tm_layout(text.fontface = "italic", attr.color = "pink")
- Yes, we can keep them in
tm_layout
A bit disruptive idea: @mtennekes what do you think about renaming tm_comp_group to tm_components? (Maybe then atrr.color can go there)
Renaming is fine with me. Another advantage is that there is also tm_group() which may be confusing with tm_com_group.
I would still like to keepattr.color in tm_layout, mainly because it also affects the frame color (which is not a component).
Hi everyone. @mtennekes for the panels, I think they should be set through tm_facet if the panel defaults are to be changed. The need to adjust panels is more to do with the appearance of facets in grouped plots. Another thought: I noticed that tmap V3 on Windows only allowed 3 default font families. Is there any upgrade like you did with cols4all that automatically provides a wider range of fonts without needing to load extrafonts?
Working. Also I had the idea to apply tm_components to all legends, all charts or all other components. So did that:
# the default for group_id is "ALL"
tm_shape(World) +
tm_polygons("HPI", lwd = "footprint") +
tm_compass() +
tm_credits("Some text") +
tm_components(position = tm_pos_out("left", "center"))
tm_shape(World) +
tm_polygons("HPI", lwd = "footprint") +
tm_compass() +
tm_credits("Some text") +
tm_components("LEGENDS", position = tm_pos_out("left", "center")) +
tm_components("OTHERS", position = tm_pos_out("center", "bottom"))
Open questions:
- If there are conflicts, e.g. a position defined via a specific
group_idis different from a position defined via"LEGENDS"? The first, the latter, or the one that is called last? - We could also have a special value for titles. Now they fall under
"OTHERS".
Hi everyone. @mtennekes for the panels, I think they should be set through tm_facet if the panel defaults are to be changed. The need to adjust panels is more to do with the appearance of facets in grouped plots.
True, but panels could also be useful for single maps, and in case facets are generated without using tm_facets explicitly, it may not be clear that tm_facets() is used rather than tm_layout. The former is used to specify how the facets are generated and not so much the appearance.
Another thought: I noticed that tmap V3 on Windows only allowed 3 default font families. Is there any upgrade like you did with cols4all that automatically provides a wider range of fonts without needing to load extrafonts?
Could you refresh my memory? Where did you find that? I can't recall that we did anything with fonts in cols4all
We could also have a special value for titles. Now they fall under "OTHERS".
So, is it only for a legend that has this special value (LEGENDS)? Or does each component have its own? Is it somewhere explained in the documentation, or is it a new thing? Sorry, there have been many changes recently in this grouping etc, it is easy to lose track. But those changes are needed for improvement, so thank you for your hard work:)
Working. Also I had the idea to apply tm_components to all legends, all charts or all other components.
I really like the idea of having the option for the placement: all in one!
Could you refresh my memory? Where did you find that? I can't recall that we did anything with fonts in cols4all
To clarify: You needed RColorBrewer for colors in the past. With cols4all in version 4, you now have a wide range of color selections for mapping built in. I meant that having an extension similar to this but for fonts specifically could be great as the default is just 3 font families.
tm_shape(World) + tm_polygons("HPI", lwd = "footprint") + tm_compass() + tm_credits("Some text") + tm_components("LEGENDS", position = tm_pos_out("left", "center")) + tm_components("OTHERS", position = tm_pos_out("center", "bottom"))
Open questions:
- If there are conflicts, e.g. a position defined via a specific
group_idis different from a position defined via"LEGENDS"? The first, the latter, or the one that is called last?
I think the one that is called last should be used. This allows all other components to retain their position specified by the group_id ("ALL") and only the positioning for "LEGENDS" changes. This is what I mean:
tm_shape(World) +
tm_polygons("HPI", lwd = "footprint") +
tm_compass() +
tm_credits("Some text") +
tm_components( position = tm_pos_out("left", "center")) +
tm_components("LEGENDS", position = tm_pos_out("center", "bottom"))
Could we add a scale bar to the tests to ensure it still works when the positions of a few components are changed?
On positions in general, I could combine text and numbers (e.g. c("left",0.1)) in the past but this is no longer possible with tmap V4. Is there a different way to specify it or a default number equivalent to "left", for example? It comes in handy when you need to align components to a side but at various heights.
On positions in general, I could combine text and numbers (e.g. c("left",0.1)) in the past but this is no longer possible with tmap V4. Is there a different way to specify it or a default number equivalent to "left", for example? It comes in handy when you need to align components to a side but at various heights.
See this: https://github.com/r-tmap/tmap/issues/539#issuecomment-2596897887
On positions in general, I could combine text and numbers (e.g. c("left",0.1)) in the past but this is no longer possible with tmap V4. Is there a different way to specify it or a default number equivalent to "left", for example? It comes in handy when you need to align components to a side but at various heights.See this: #539 (comment)
Thanks @Rapsodia86
@mtennekes, have you considered slightly different options for the group_id argument? I do not know why exactly, but I am not fully sold on "LEGENDS", etc. One alternative I could think of was to list the functions we want to group together, e.g.:
tm_components("tm_legend", position = tm_pos_out("left", "center"))
tm_components(c("tm_scalebar", "tm_compass"), position = tm_pos_out("left", "center"))
I do not know if this is more intuitive, though. Maybe someone else has other ideas...
Absolutely that list is better/more intuitive than "LEGENDS" etc.!
Haven't thought about that option @Nowosad I like this! Perhaps for lazy users (like me:-) we can still have something to group all 'other' components, maybe "tm_<other>"? I'll give it a go.
About priority: I'll give priority to the last called. So in case tm_components is called without group_id, so for all components, it will overrule all previously set tm_components. This is needed because otherwise it is not possible to update the map (same behaviour for tmap options).
@Rapsodia86 Yes totally agree that it is confusing with all the changes, also for me:-) But I think we do a good thing here. I'll update the vignettes when it is settled.
@aonojeghuo It would be great to have as many fonts available. However, I don't know how to do that, because to the best of my knowledge this is system dependent. It deserves a separate issue.
@mtennekes -- maybe a solution could be to (as for color palettes) have a minus sign operator, e.g., tm_components("tm_legend", position = tm_pos_out("left", "center")) -- legend only, but tm_components("-tm_legend", position = tm_pos_out("left", "center")) -- everything except the legend
Working now. Please test:
tm = tm_shape(World) +
tm_polygons("HPI", lwd = "footprint", lwd.legend = tm_legend(group_id = "hpi")) +
tm_compass() +
tm_credits("Some text", group_id = "cre")
tm
tm +
tm_components("hpi", position = tm_pos_out("right", "center"))
tm +
tm_components("hpi", position = tm_pos_out("right", "center")) +
tm_components("tm_legend", position = tm_pos_out("left", "center"))
tm +
tm_components("hpi", position = tm_pos_out("right", "center")) +
tm_components("tm_legend", position = tm_pos_out("left", "center")) +
tm_components(position = tm_pos_in("left", "top"))
tm +
tm_components("hpi", position = tm_pos_out("right", "center")) +
tm_components("tm_legend", position = tm_pos_out("left", "center")) +
tm_components(position = tm_pos_in("left", "top")) +
tm_components("cre", position = tm_pos_in("center", "bottom"))
Excellent idea @Nowosad to include the minus sign. Also easy to implement.
@mtennekes looks good!
Other question: are there default options, that apply to all elements, such as
text.fontfamily, that are worthwhile to have intm_layout?
Sorry for being late in the discussion... I usually prefer sharp corners for layout elements (sorry 🤭), it would be nice to have a general setting in tm_layout to set r=0 for all frames (map frame, legend, etc.). But I guess this would be actually a feature request since such an argument doesn't exist yet.
Oops, I posted before reading the rest of the discussion... I guess this should be possible using the frame.r argument inside tm_components(), did I understand correctly?
Working now. Please test:
tm + tm_components("hpi", position = tm_pos_out("right", "center")) + tm_components("tm_legend", position = tm_pos_out("left", "center")) + tm_components(position = tm_pos_in("left", "top"))
Excellent idea @Nowosad to include the minus sign. Also easy to implement.
I'm not sure I understand the output of this one... Shouldn't the north arrow and the credits also move to "left, top"? They stay in the bottom right corner.
I'm struggling a bit to understand what belongs to tm_layout and what should be in tm_components... Since both functions affect the general layout of the map, I find it a bit confusing to have 2 distinct functions. What do you think of having the tm_components function as an argument of tm_layout (similarly to tm_legend inside tm_polygon)?
Some repeated arguments are also a bit confusing for me (e.g. frame.r is both in tm_layout and tm_components).
Working now. Please test: tm + tm_components("hpi", position = tm_pos_out("right", "center")) + tm_components("tm_legend", position = tm_pos_out("left", "center")) + tm_components(position = tm_pos_in("left", "top")) Excellent idea @Nowosad to include the minus sign. Also easy to implement.
I'm not sure I understand the output of this one... Shouldn't the north arrow and the credits also move to "left, top"? They stay in the bottom right corner.
It seems to work as expected using the current dev version of tmap. Could you recheck it on your side as well?
library(tmap)
library(spData)
data(World)
tm = tm_shape(World) +
tm_polygons("HPI", lwd = "footprint", lwd.legend = tm_legend(group_id = "hpi")) +
tm_compass() +
tm_credits("Some text", group_id = "cre")
tm +
tm_components("hpi", position = tm_pos_out("right", "center")) +
tm_components("tm_legend", position = tm_pos_out("left", "center")) +
tm_components(position = tm_pos_in("left", "top"))

Created on 2025-06-24 with reprex v2.1.1
I'm struggling a bit to understand what belongs to
tm_layoutand what should be intm_components... Since both functions affect the general layout of the map, I find it a bit confusing to have 2 distinct functions. What do you think of having thetm_componentsfunction as an argument oftm_layout(similarly totm_legendinsidetm_polygon)?Some repeated arguments are also a bit confusing for me (e.g. frame.r is both in
tm_layoutandtm_components).
Thanks for letting us know about this, @jguelat -- I think we need to make as clear as possible explanation of these two ideas and differences between them. Long story short, with tm_components -- you can modify visual style of one or more components (e.g., legends, scale bars, etc.), but you do not change the general map layout. With tm_layout you can change other, general map elements like the frame, inner and outer margins, etc.
tm_components("hpi", position = tm_pos_out("right", "center"))
"right"? Why to set this if then everything is sent to the left? Or is it not the expected outcome?
This is what I'm getting with the current dev version:
tm_components("hpi", position = tm_pos_out("right", "center"))"right"? Why to set this if then everything is sent to the left? Or is it not the expected outcome?
I think it was just an example to show that the last positioning has priority