Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tm_inset / tm_minimap #1048

Open
mtennekes opened this issue Feb 24, 2025 · 7 comments
Open

tm_inset / tm_minimap #1048

mtennekes opened this issue Feb 24, 2025 · 7 comments
Labels
Layout Issues related to map layout (including many maps, facets, positions, etc.)

Comments

@mtennekes
Copy link
Member

Struggling myself with inset maps. It is possible with the vp argument of print.tmap, but requires a lot of trial and error:

Image

  1. tm_minimap with shows an inset of a world map, either a projected 'flat' one, or a globe, like mapsf does. This is already implemented for the view mode, but not for plot mode.
  2. a more general tm_inset, which takes any (grid-based) plot, and places it in a map component. In that way it is processed in exactly the same way as other components, like legends and scalebars. The user still has to provide a height and width, and specify the position. Use cases: inset maps (also like this tmap_arrange -> patchwork-ish functionality #1046) and ggplot2 plots.

What do you think @Nowosad and @olivroy ? Any additional ideas?

@Nowosad
Copy link
Member

Nowosad commented Feb 25, 2025

@mtennekes

  1. Should tm_minimap() work in the same way as tm_basemap, i.e., using the base map of the provider in a specific zoom level?
  2. tm_inset(), on the other hand, would be a way to compose inset maps based on the provided data. One suggestion -- I think that tm_inset could try to "stick" to the map frame. For example, when the inset is set to be inside the map frame, then it (by default) would be in one of the corners, while when it is set to be outside of the frame, then it would be aligned to one of the sides of the map.

@mtennekes
Copy link
Member Author

Good suggestions @Nowosad

  1. Yes, good idea. We could just draw a bbox rectangle on top of this basemap? And what if no basemap is provided? Use a default basemap (e.g. "OpenStreetMap"), or use World, or ...?
  2. tm_inset() is indeed supposed to be. used inside the map frame. So the defaults of tm_pos should be tm_pos_in.

As an enhancement of the 'sticking' process I had an idea on my walk back after work: in addition to sticking to one of the corners, we could also use a component-grid-layout, where components (whatever they may be: legends, insets, credits, titles), can be arranged in a regular grid.

As for the implementation: the easiest way is to use the pos.h and pos.v arguments of tm_pos. These can now be set to left-center-right, top-center-bottom or numbers, but we could add another option.

So it could be something like this:

tm_shape(NLD_muni) +
tm_polygons() +
tm_basemap() +
tm_minimap(position = tm_pos_in_cell(row = 2, col = 1:2)) +
tm_component_grid(nrow = 3, ncol = 3, heights = c(.1, .1, .8))
  • Intuitive? Instead of tm_pos_in_cell, we could also just use tm_pos_in, and the pos.h and pos.v arguments. However, we cannot (yet) use pos.h = 1:2, pos.v = 2, because this conflicts the 'free' numeric placement option. Or pos.h = tm_row(1:2), pos.v = tm_col(2). Or we could use I() function for this: pos.h = I(1:2), pos.v = I(2)`. Perhaps this is the best option (easy to implement and hopefully intuitive to use. What do you think?
  • Alternatives for tm_component_grid? E.g. tm_set_component_grid_layout, tm_grid_layout, etc...

Just an example with the current implementation:

tm_shape(World) + 
tm_polygons(fill = "HPI", 
			col = "economy", 
			lwd = "pop_est",
			lwd.legend= tm_legend(position = tm_pos_in(), width = 13), 
			col.legend= tm_legend(position = tm_pos_in(), width = 13), 
			fill.legend = tm_legend(position = tm_pos_in(), width = 13,
				resize_as_group = F,
				group.frame = F)) + 
tm_options(
	component.offset = c(inside = 0.3, INSIDE = 0, outside = 0, OUTSIDE = 0), 
	component.stack_margin = .3)

Image

The devil is in many details:

  • resize_as_group and group.frame are options for all components that are drawn on the same spot (e.g. top left corner). These determine whether they be stacked as separate components or put into one component frame. Because there can be several comments, I had decided to simply use the first specifications. However, the order of components is not necessarily the order in which the components are processed. In the example above, I had to set resize_as_group to fill.legend because this is processed before the other legends. So this should be improved....
  • component.offset is the offset to the map frame, and component.stack_margin between components. These are still only global options, but ideally, they should be set more locally (e.g. inside the new tm_component_grid function.
  • tm_options(component.offset = .2)fails because it expects this vector. To be improved...

@mtennekes
Copy link
Member Author

Open question from a users perspective: if you see the map above, what would the ideal tmap code be?

Also think about/consider these kind of shortcut functions, which haven't had much attention, but should be pretty useful: tm_place_legends_left.

@Nowosad
Copy link
Member

Nowosad commented Feb 26, 2025

We could just draw a bbox rectangle on top of this basemap? And what if no basemap is provided? Use a default basemap (e.g. "OpenStreetMap"), or use World, or ...?

If no basemap is provided -- I would suggest to use the default one.

tm_component_grid

I am unsure about this whole idea (including the syntax there). I.e., I like the possibility to customize the map, but -- is this actually needed for most of the tmap users? Maybe it would be better to start with working tm_inset, and then consider future steps when/if people will ask for it?

Also think about/consider these kind of shortcut functions, which haven't had much attention

My thinking is that we should be careful about adding shortcut functions, because then the package will be fun of these, and it would make it hard to find the actual "base" functions...

@Xiezhibin
Copy link

@Nowosad @mtennekes

ls this related to my previous issue, #1046 ?

Insert maps and combine separate map look different. Is it possible to implement some basic base methods?

  • Define the boundaries of the frame that combines the graphics, possibly the top, bottom, left, and right edges.
  • Define the distance between the boundaries of the frames when combining the graphics.
  • Em.....it might not be related, when combining map plots, can add things like legend.position, title and tags.

How exactly to implement this, I may have some unclear technical detail. Once a solution approach is discussed and decided upon, I would be glad to contribute to this effort as a newcomer by working on some of the less complex tasks.

@mtennekes
Copy link
Member Author

mtennekes commented Feb 27, 2025

@Nowosad @Xiezhibin thanks for your inputs. Agree in general.

First question: how shall we call the 'inset' components?

Currently, I can think of these ones, where I mention the input:

  1. Images (e.g. png), currently tm_logo
  2. grob objects, currently tm_component
  3. ggplot2 objects (via ggplotGrob)
  4. custom tmap objects (via print and vp)
  5. bounding boxes , currently tm_inset
  6. no input. Currently tm_minimap
  7. ... Any other inputs that I forgot?

Can we harmonize these names, e.g.

  1. tm_inset_image
  2. tm_inset_ggplot2
  3. tm_inset_grob
  4. tm_inset_tmap
  5. tm_inset_minimap
  6. also tm_inset_minimap, but without input?

Not sure about the name 'minimap'. Aren't all minimaps inset maps? The two things that distinguish a 'minimap' in its current use are: it's global, and it has a locator where the main map is. Perhaps tm_inset_locator?

You can add all current components to this list as well, like tm_scalebar and tm_title. However, tm_inset_title is less intuitive. But where to draw the border?

Also happy to hear your opinions @tim-salabim @Nowosad @olivroy , ...

Second: placement. Indeed perhaps tm_component_grid not needed and too complex.

What I need as user: distance from the map frame, distance between the components, option to align map frames.

For the pos.h and pos.v values, I think of three options now:

  • "right", "center", "left" / "top", "center", "bottom"
  • two numbers, where (this is new!) components are also stacked, just like as in the first option
  • I() function, e.g. I(0.5) and , 0.5), which is the same as the current numbers, but because I()stands forAsIs`, the numbers are as they are, so now stacking or whatsoever.

@mtennekes
Copy link
Member Author

Spoke with @Nowosad in person before lunch. He had a good idea:

Just one tm_inset which functions as one of the above, based on the input.

mtennekes added a commit that referenced this issue Feb 27, 2025
@Nowosad Nowosad added Layout Issues related to map layout (including many maps, facets, positions, etc.) and removed enhancement labels Feb 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Layout Issues related to map layout (including many maps, facets, positions, etc.)
Projects
None yet
Development

No branches or pull requests

3 participants