-
-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathpositions.qmd
More file actions
392 lines (337 loc) · 16.8 KB
/
positions.qmd
File metadata and controls
392 lines (337 loc) · 16.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
```{r}
#| echo: false
source("code/before_script.R")
```
```{r}
#| echo: false
boxy = function(text, fill) {
grid::gList(
grid::rectGrob(gp = grid::gpar(fill = fill)),
grid::circleGrob(r = .45),
grid::textGrob(text)
)
}
g = \(text) boxy(text, fill = "#B34B37")
b = \(text) boxy(text, fill = "#2D536E") #"#B33788"
m = \(text) boxy(text, fill = "#AE7327")
```
# Positions {#sec-positions}
\index{map components!positioning}
\index{positions}
This chapter focuses on how to move map components, such as legends, titles, scale bars, and insets, freely in the map.
They can be placed both inside and outside the map frame -- with some being in and some being out (@sec-positioning).
Moreover, map components can be grouped together, which allows them to be moved and arranged as a single unit (@sec-position-many-components).
To demonstrate the positioning of map components, we use a simple map with the elevation raster data of Slovenia in meters above sea level (*m asl*) (not shown).
```{r}
#| message: false
library(terra)
library(tmap)
slo_elev = rast("data/slovenia/slo_elev.tif")
tm = tm_shape(slo_elev) +
tm_raster()
```
## Positioning {#sec-positioning}
\index{positions}
\index{tm\_pos\_in}
\index{tm\_pos\_out}
All of the map components can be positioned in any location in the map frame or outside of it.
The positioning is done via the `position` argument, which can take a variety of values.
The most common are:
- A character vector with two elements, where the first is the horizontal position and the second is the vertical position, e.g., `c("left", "top")` (@fig-positions-in-scheme).
- `tm_pos_in()`: a function that allows to control the position inside the map frame.
The first argument is the horizontal position and the second is the vertical position.
E.g., `tm_pos_in("left", "top")` locates a component in the top left corner of the map frame.
This function can also take additional arguments to control the position more precisely, including their justification and alignment.
- `tm_pos_out()`: a function that allows the control of the position outside the map frame.
For example, `tm_pos_out("center", "top")` places a component in the center of the top side of the map frame.
We are also able to specify the position inside the cell defined by the horizontal and vertical position, e.g., `tm_pos_out("left", "center", pos.v = "center")`.
The following two subsections describe how to position map components inside and outside the map frame.
### Inside the map frame
```{r}
#| echo: false
#| message: false
#| label: fig-positions-in-scheme
#| fig-cap: "Examples of positioning map components inside the map frame."
#| fig-width: 9
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(show = FALSE)) +
tm_inset(g('"left", "top"'), position = c("left", "top")) +
tm_inset(g('"right", "bottom"'), position = c("right", "bottom")) +
tm_inset(g('"LEFT", "BOTTOM"'), position = c("LEFT", "BOTTOM")) +
tm_inset(g('"left", "center"'), position = c("left", "center")) +
tm_inset(b('0.5, 0.5'), position = c(0.5, 0.5)) +
tm_inset(b('0.6, 1.0'), position = c(0.6, 1.0))
```
Map legends, by default, are placed outside of the map frame -- that is often expected as it does not overlap with the rest of the map content.
However, we can find ourselves in a situation where our spatial data is so sparse that the legend can be placed inside the map, filling a white space.
Then, we need to use the `position` argument of `tm_legend()`.
We may use it either with a vector with two elements, or with the `tm_pos_in()` function (@fig-positions-in-scheme).
For example, `position = c("right", "bottom")` or `position = tm_pos_in("right", "bottom")` are equivalent.
The first element of the vector is the horizontal position and the second is the vertical position inside the map frame.
These elements can be either in lower case letters, UPPER CASE LETTERS, or numbers between 0 and 1.
Lowercase letters, e.g., `"right," "bottom",` place the selected map component in the right bottom corner but leave some margin to the map frame (@fig-positions-in-1).
```{r}
#| label: position-in1
#| eval: false
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(position = c("right", "bottom")))
# same as:
# tm_shape(slo_elev) +
# tm_raster(col.legend = tm_legend(position = tm_pos_in("right", "bottom")))
```
Uppercase letters, e.g., `"RIGHT," "BOTTOM",` also places the map component in the bottom right corner but directly touching the map frame (@fig-positions-in-2).
```{r}
#| label: position-in2
#| eval: false
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(position = c("RIGHT", "BOTTOM")))
```
Numbers between 0 and 1, e.g., `c(0.8, 0.4)` places the map component in the right bottom corner -- 0.8 is the horizontal position and 0.4 is the vertical position of the top left corner of the map component in relation to the map frame (@fig-positions-in-3).
```{r}
#| label: position-in3
#| eval: false
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(position = c(0.8, 0.4)))
```
```{r}
#| label: fig-positions-in
#| warning: false
#| echo: false
#| layout-nrow: 3
#| fig-cap: "Positioning legends inside the map frame."
#| fig-subcap:
#| - "with lowercase letters: \"right\", \"bottom\""
#| - "with uppercase letters: \"RIGHT\", \"BOTTOM'\""
#| - "with numbers: 0.8, 0.4"
#| #fig-asp: 0.69
<<position-in1>>
<<position-in2>>
<<position-in3>>
```
\index{positions!justification}
There is one important difference between specifying the position with a vector and with the `tm_pos_in()` function.
The latter allows to control the position more precisely with additional arguments -- `just.h`, `just.v`, `align.h`, and `align.v`.
The `just.h` and `just.v` only work when the position is specified with numbers between 0 and 1, and they arguments control the justification of the map component to that position.
Take a look at @fig-justification.
The central map component is based on the default justification, which is `just.h = "left"` and `just.v = "top"` -- it means that the positioning starts from the top left corner of the map component.
However, depending on our needs, we can change the justification to `just.h = "right"` and `just.v = "top"` (top right corner) or `just.h = "left"` and `just.v = "center"` (left central side of the map component).
```{r}
#| label: fig-justification
#| fig-cap: "Examples of justification of map components inside the map frame."
#| echo: false
#| message: false
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(show = FALSE)) +
tm_inset(b("pos.h = 0.5\npos.v = 0.5\n"),
position = tm_pos_in(pos.h = 0.5, pos.v = 0.5)) +
tm_inset(b("pos.h = 1\npos.v = 1\njust.h = \"right\"\njust.v = \"top\""),
position = tm_pos_in(pos.h = 1, pos.v = 1,
just.h = "right", just.v = "top")) +
tm_inset(b("pos.h = 0\npos.v = 0.5\njust.h = \"left\"\njust.v = \"center\""),
position = tm_pos_in(pos.h = 0, pos.v = 0.5,
just.h = "left", just.v = "center"))
```
The `align.h` and `align.v` arguments are used when many map components are grouped together, which is explained in [@sec-position-many-components].
### Outside the map frame
\index{tm\_pos\_out}
In general, there are eight locations for map components outside the map frame -- each of them can be specified with `tm_pos_out()` (@fig-positions-out-scheme).
We may place a map component either on one of the sides of the map frame (e.g., `tm_pos_out("center", "top")`), or in its corner (e.g., `tm_pos_out("left", "top")`).
```{r}
#| echo: false
#| message: false
#| fig-asp: 1
#| label: fig-positions-out-scheme
#| fig-cap: "Examples of positioning map components outside the map frame."
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(show = FALSE)) +
# tm_inset(g('"center", "center"'), position = tm_pos_on_top(pos.h = "center", pos.v = "center")) +
tm_inset(m('"left", "center"'), position = tm_pos_out("left", "center", pos.v = "center")) +
tm_inset(m('"right", "center"'), position = tm_pos_out("right", "center", pos.v = "center")) +
tm_inset(m('"left", "top"'), position = tm_pos_out("left", "top")) +
tm_inset(m('"center", "top"'), position = tm_pos_out("center", "top", pos.h = "center")) +
tm_inset(m('"right", "top"'), position = tm_pos_out("right", "top")) +
tm_inset(m('"left", "bottom"'), position = tm_pos_out("left", "bottom")) +
tm_inset(m('"center", "bottom"'), position = tm_pos_out("center", "bottom", pos.h = "center")) +
tm_inset(m('"right", "bottom"'), position = tm_pos_out("right", "bottom")) +
tm_layout(asp = 1, frame.lwd = 2)
```
At the same time, placing a map component outside a map frame brings some additional complexity and possibilities.
Now, we can not only put a component in a specified place, but also arrange it in that place.
For example, `tm_pos_out("center," "top")` puts the specified component to the top of the map frame, but it is positioned to the left.
What should we do if we want to center it?
The `tm_pos_out()` has four sets of arguments that control the position of a map component outside the map frame:
- `cell.h` and `cell.v`: the horizontal and vertical position of the map component outside the map frame.
- `pos.h` and `pos.v`: the horizontal and vertical position of the map component inside the cell defined by `cell.h` and `cell.v`.
- `just.h` and `just.v`: the justification of the map components in relation to the position of the map component inside the cell -- only used when `pos.h` and `pos.v` are specified as numbers between 0 and 1.
- `align.h` and `align.v`: the alignment of the components is only used when many components are grouped together (@sec-position-many-components).
Let's see how these arguments work in practice.
The `cell.h` and `cell.v` arguments are the first two arguments of the `tm_pos_out()` function and they define the cell in which the map component are placed (@fig-positions-out-1; @fig-positions-out-2).
```{r}
#| label: position-out1
#| eval: false
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(position = tm_pos_out("center", "bottom")))
```
```{r}
#| label: position-out2
#| eval: false
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(position = tm_pos_out("left", "center")))
```
Then, we may to control the location of the map component inside the cell with `pos.h` and `pos.v` arguments.
The `pos.h` argument controls the horizontal position of the map component inside the cell, while the `pos.v` argument -- the vertical position.
The former is mostly useful when our legend is on the top of the map frame and the latter is useful when it is on the left or right side of the map frame (@fig-positions-out-3; @fig-positions-out-4).
```{r}
#| label: position-out3
#| eval: false
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(position = tm_pos_out("center", "bottom",
pos.h = "center")))
```
```{r}
#| label: position-out4
#| eval: false
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(position = tm_pos_out("left", "center",
pos.v = "center")))
```
```{r}
#| label: fig-positions-out
#| warning: false
#| echo: false
#| layout-nrow: 2
#| layout-ncol: 2
#| fig-cap: "Positioning legends outside the map frame."
#| fig-subcap:
#| - "with `cell.h = \"center\"` and `cell.v = \"bottom\"`"
#| - "with `cell.h = \"left\"` and `cell.v = \"center\"`"
#| - "with `pos.h = \"center\"` in cell (`cell.h = \"center\"`, `cell.v = \"bottom\"`)"
#| - "with `pos.v = \"center\"` in cell (`cell.h = \"left\"`, `cell.v = \"center\"`)"
<<position-out1>>
<<position-out2>>
<<position-out3>>
<<position-out4>>
```
```{r}
#| eval: false
#| echo: false
#| message: false
#| fig-asp: 1
#| label: fig-positions-out-pos-scheme
#| fig-cap: "Examples of positioning map components outside the map frame with different `pos.h` and `pos.v` arguments."
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(show = FALSE)) +
tm_inset(g("pos.v = \"top\" in\ncell (\"left\", \"center\")"),
position = tm_pos_out("left", "center", pos.v = "top")) +
tm_inset(g("pos.v = \"bottom\" in\ncell (\"right\", \"center\")"),
position = tm_pos_out("right", "center", pos.v = "bottom")) +
tm_inset(g("pos.h = \"center\" in\ncell (\"center\", \"top\")"),
position = tm_pos_out("center", "top", pos.h = "center")) +
tm_inset(g("pos.h = \"right\" in\ncell (\"center\", \"bottom\")"),
position = tm_pos_out("center", "bottom", pos.h = "right")) +
tm_layout(asp = 1, frame.lwd = 2)
```
```{r}
#| echo: false
#| eval: false
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(position = tm_pos_out("center", "center",
pos.h = "left",
pos.v = "bottom")))
```
## Many components {#sec-position-many-components}
Various map components can be positioned at the same time -- either to different locations or to the same one.
For example, we can place a scale bar outside the map frame and a legend inside the map frame (@fig-many-comp-1).
```{r}
#| label: many-comp1
#| eval: false
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(position = c("right", "bottom"))) +
tm_scalebar(position = tm_pos_out("left", "center"))
```
Now, you may be wondering what happens if we want to place two (or more) components in the same location.
As you can see in @fig-many-comp-2, they are stacked on top of each other inside one frame.
```{r}
#| label: many-comp2
#| eval: false
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(position = tm_pos_out("right", "center"))) +
tm_scalebar(position = tm_pos_out("right", "center"))
```
```{r}
#| eval: false
#| echo: false
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(position = tm_pos_out("left", "center"))) +
tm_scalebar(position = tm_pos_out("left", "center", pos.v = "bottom"))
```
```{r}
#| label: fig-many-comp
#| warning: false
#| echo: false
#| layout-nrow: 2
#| fig-cap: "Positioning two map components."
#| fig-subcap:
#| - "in different locations"
#| - "in the same location"
<<many-comp1>>
<<many-comp2>>
```
```{r}
#| eval: false
#| echo: false
tm_shape(slo_elev) +
tm_raster() +
tm_scalebar() +
tm_layout(component.position = tm_pos_out("left", "center"))
```
\index{tm\_components}
\index{grouping map components}
The **tmap** package also has a mechanism to group map components together and then position and organize them as a single unit.
This requires two steps:
1. Specifying the `group_id` argument in the map component that we want to group together -- this should be a unique number or text identifier.
2. Using the `tm_components()` function to select which group of map components we want to position together and where.
In the following example, we have three map components -- a legend, a scale bar, and credits -- and we want to group the scale bar and credits (@fig-comp-group-1).
First, we specify the `group_id` argument in the map components functions, and then we use the `tm_components()` function to position them together.
Here, the position argument works exactly as we already described in the previous sections, i.e., it can be specified with a vector or with the `tm_pos_in()` or `tm_pos_out()` functions.
```{r}
#| label: comp-group1
#| eval: false
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(group_id = 1)) +
tm_scalebar(group_id = 2) +
tm_credits("My credits", group_id = 2) +
tm_components(1, position = tm_pos_in("right", "bottom")) +
tm_components(2, position = tm_pos_in("left", "top"))
```
The `tm_components()` function can also be used to customize the arrangement of map components in the same location.
For example, we can stack them vertically (`stack = "vertical"`, default) or horizontally (`stack = "horizontal"`) (@fig-comp-group-2).
```{r}
#| label: comp-group2
#| eval: false
tm_shape(slo_elev) +
tm_raster(col.legend = tm_legend(group_id = 1)) +
tm_scalebar(group_id = 2) +
tm_credits("My credits", group_id = 2) +
tm_components(1, position = tm_pos_in("right", "bottom")) +
tm_components(2, position = tm_pos_in("left", "top"), stack = "horizontal")
```
```{r}
#| label: fig-comp-group
#| warning: false
#| echo: false
#| layout-nrow: 2
#| fig-cap: "Grouping map components together."
#| fig-subcap:
#| - "two groups of map components in different locations"
#| - "two groups of map components in the same location, one stacked horizontally"
<<comp-group1>>
<<comp-group2>>
```
Each additional map component is placed on the bottom of the previous one if they are in the same location.
This can be controlled with the `z` argument of the map component functions -- smaller numbers place the component above others.
<!-- any other things worth mentioning? -->
<!-- , frame_combine = FALSE -->
<!-- equalize = FALSE -->
<!-- , offset = 0, stack_margin = 1 -->
<!-- frame = TRUE, bg.color = "purple", bg.alpha = 0.5 -->