6  Layers

Map layers are the main building blocks of a map. They specify how the specific data should be visualized – for example, as polygons, symbols, lines, text labels, or raster. There is also an interplay between the data and the map layers: raster data can be plotted with the tm_raster() or tm_rgb() functions, while point data can be visualized with tm_symbols(), tm_dots(), tm_bubbles(), tm_markers(), or tm_squares(). Moreover, some data types can be simplified and shown with a different geometry, such as polygons that are usually visualized with tm_polygons() but can also converted into centroids and plotted with tm_symbols().

Each dataset may have one or more layers, and each layer can be visualized in different ways. For example, a dataset with polygons can be visualized using tm_polygons() to display the polygon areas, and then the names of the polygons can be added with tm_text().

Table 14.1 shows the available map layers in tmap. They are divided into two groups: basic functions and derived functions. The basic functions are the main functions for visualizing data, are useful for most cases, and are highly customizable. The derived functions are built on top of the basic functions but with different default values aimed at specific use cases.

Table 6.1: Available map layers
Function Element Geometry
Basic functions
tm_polygons() polygons (borders and fill) polygons
tm_symbols() symbols points, polygons, and lines
tm_lines() lines lines
tm_text() text points, polygons, and lines
tm_raster() raster raster
tm_basemap() tile
tm_tiles() tile
Derived functions
tm_borders() polygons (borders) polygons
tm_fill() polygons (fill) polygons
tm_bubbles() bubbles points, polygons, and lines
tm_dots() dots points, polygons, and lines
tm_markers() marker symbols points, polygons, and lines
tm_square() squares points, polygons, and lines
tm_labels() text labels points, polygons, and lines
tm_labels_highlighted() text labels with background points, polygons, and lines
tm_iso() lines with text labels lines
tm_rgb()/tm_rgba() raster (RGB image) raster

In this chapter, we focus on what map layers are available in tmap and how they differ. Chapter 7, on the other hand, is all about how to present information given in variables using colors, sizes, and shapes.

6.1 Polygons

library(tmap)
library(sf)
slo_borders = read_sf("data/slovenia/slo_border.gpkg")

The main function of visualizing polygons is tm_polygons(). By default, it plots areas of polygons in light gray (gray85) and polygons borders in slightly dark gray (gray25).

tm_shape(slo_borders) +
  tm_polygons()

Both, colors of areas (polygons’ fillings) and colors of borders can be modified using the fill and col arguments (Figure 6.1 (a)).

tm_shape(slo_borders) +
  tm_polygons(fill = "lightblue", col = "black", lwd = 0.5, lty = "dashed")

In fact, tm_polygons() is a combination of two separate functions: tm_fill() and tm_borders(). The tm_fill() function fills polygons with a fixed color or a color palette representing a selected variable (Figure 6.1 (b)).

tm_shape(slo_borders) +
  tm_fill(fill = "lightblue")

The tm_borders() function draws the borders of the polygons only (Figure 6.1 (c)). It allows you to change the colors of borders, their widths, or the lines type.

tm_shape(slo_borders) +
  tm_borders(col = "black", lwd = 0.5, lty = "dashed")
(a) tm_polygons()
(b) tm_fill()
(c) tm_borders()
Figure 6.1: Example of a map created using:

More information on colors and how they can be applied and modified is explained in detail in Section 7.3.

6.2 Symbols

slo_cities = read_sf("data/slovenia/slo_cities.gpkg")

Symbols are a versatile type of layer. They are usually used to represent point data but can also be used for lines and polygons. In the latter cases, they are located in the centroid coordinates of each feature. Their flexibility is also related to the ways symbols can be visualized – it is possible to show values of a given variable by colors of symbols, their sizes, or shapes (more about that is explained in Chapter 7).

The tm_symbols() is the main function in tmap, allowing to use and modify symbol elements (Figure 6.2). By default, this function draws a gray circle symbol with a black border for each element of an input feature.

Figure 6.2: A map showing the default tmap symbols.

In the above example, each symbol is related to one feature (row) in the slo_cities object. However, in a case when we provide multi-element features (such as MULTIPOINT; see Section 2.2.1), each multi-element object is first split into a number of single-element features and then plotted.

The tm_symbols() is a very flexible function with a large number of arguments. While this allows adjusting its results to almost any need, it also makes this function complicated. Therefore, four additional layers are implemented in tmap: tm_squares(), tm_bubbles(), tm_dots(), tm_markers(). All of them use tm_symbols(), but with different default values.

tm_squares() uses square symbols (shape = 22) instead of circles (shapes = 21) (Figure 6.3 (a)).

tm_shape(slo_cities) +
  tm_squares()

Next, tm_bubbles() uses circles with a larger size value than the default one (Figure 6.3 (b)).

tm_shape(slo_cities) +
  tm_bubbles()

The main role of tm_dots() is to present many locations at the same time. To do this, this layer has a small size value (0.02) at the default (Figure 6.3 (c)).

tm_shape(slo_cities) +
  tm_dots()

The last additional layer is tm_markers(), which uses a marker icon by default (Figure 6.3 (d)).

tm_shape(slo_cities) +
  tm_markers()
(a) tm_squares()
(b) tm_bubbles()
(c) tm_dots()
(d) tm_markers()
Figure 6.3: Maps showing default visualizations using various types of symbols.

In Section 7.4 and Section 7.5, we learn how to modify the size and shape of symbols, including creating custom shapes.

6.3 Lines

slo_railroads = read_sf("data/slovenia/slo_railroads.gpkg")

The tm_lines() function allows the visualization of different types of line data (Figure 6.4).

tm_shape(slo_railroads) + 
  tm_lines()
Figure 6.4: Example of a map created with tm_lines().

Lines can be presented using different colors, widths, or types (Chapter 7). This allows to show a hierarchy (for example, increased line widths for higher capacity roads) or distinguish between types of objects (for example, blue rivers comparing to gray roads).

6.4 Text

Text labels are often an integral part of many maps. They can serve several functions, from naming features, indicating relations between them, or representing a given variable’s values. The primary function for creating text labels is tm_text(), which adds a label to each spatial feature (Figure 6.5 (a)).

tm = tm_shape(slo_borders) +
  tm_polygons() + 
  tm_shape(slo_cities) +
  tm_dots()
tm +
  tm_text(text = "name")

Two additional functions for adding text labels are tm_labels() and tm_labels_highlighted(). They are similar to tm_text(), but have different defaults. The tm_labels() function is used to add text labels to the map, but also tries to automatically adjust the position of the labels to avoid overlapping with other features (Figure 6.5 (b)).

tm +
  tm_labels(text = "name")

The tm_labels_highlighted() additionally adds a background color to the labels, which makes them more visible (Figure 6.5 (c)).

tm +
  tm_labels_highlighted(text = "name")
(a) tm_text()
(b) tm_labels()
(c) tm_labels_highlighted()
Figure 6.5: Maps with text labels.

In the above examples, the text argument specifies the name of a variable that will be used for text labels. We can further adjust colors (col; Section 7.3), sizes (size; Section 7.4), font faces (fontface; Section 12.3), and background colors (bgcol) of labels by providing either a single value or the name of a data variable.

The tm_text(), tm_labels(), and tm_labels_highlighted() functions have many arguments that allow us to adjust the appearance of text labels. It includes arguments related to the x and y offset of the text labels (xmod and ymod), and the rotation angle (angle). Figure 6.6 (a) shows an example of a map with text labels moved a bit to the right and down, and rotated by 12 degrees.

tm +
  tm_text(text = "name", xmod = 1, ymod = -0.5, angle = 12)

Text labels can be further modified with the opt_tm_text() function provided to the options argument. It includes a set of arguments that allow for the adjustment of the text labels’ appearance, such as shadow, just, and remove_overlap (Figure 6.6 (b)).

tm +
  tm_text(text = "name", xmod = 1, ymod = -0.5, angle = 12,
          options = opt_tm_text(shadow = TRUE,
                                just = "left",
                                remove_overlap = TRUE))

To automatically adjust the position of text labels to avoid overlapping with other features, we can use the point.label argument in the opt_tm_text() function. When set to TRUE, it will use an algorithm to find the best position for each label, which can be useful when there are many overlapping features or when the labels are too close to each other.

tm +
  tm_text(text = "name", 
          options = opt_tm_text(point.label = TRUE))
(a) with x and y offsets and rotation angle
(b) with shadow and left justification
(c) with point.label = TRUE
Figure 6.6: Example of a map created with tm_text() with customized options

Text labels can be added to spatial (multi-)points, (multi-)lines, and (multi-)polygons, and each case is quite different. The simplest case is for POINT data, for which each text label will be located precisely in the coordinates of the given points (Figure 6.5). However, how to add text labels to multipoints, lines, multilines, polygons, or multipolygons? Should each label correspond to one spatial feature, or should every sub-feature have its own label? Where should the labels be placed for lines or polygons – in the center of a line and the centroid of a polygon, or somewhat different? The above decisions can be made with the options argument of the tm_text() function. By default, text labels are placed in the centroid of each feature; however, we can change this behavior with the point_per argument. The possible options are "feature" (default), "segment", and "largest". The "segment" option labels each subfeature (e.g, each point in a multipoint, each segment in a multilines, and each polygon in a multipolygon) and the "largest" option labels only the largest subfeature (e.g., the largest segment in a multilines or the largest polygon in a multipolygon).

Text labels are also often presented together with lines (Section 6.3). One example is an isopleth—a line drawn on a map through all points with the same value of a given variable, such as atmospheric pressure or elevation. Isopleths can be created with the tm_iso() function. This function combines tm_lines() and tm_text(), where text labels are placed on the isopleths along the lines. In the example below, we create contour lines (isopleths) from the elevation raster data of Slovenia, and then we plot them on the map with text labels (Figure 6.7).

library(terra)
slo_elev = rast("data/slovenia/slo_elev.tif")
slo_contours = as.contour(slo_elev, nlevels = 4)
tm_shape(slo_borders) +
  tm_borders() + 
  tm_shape(slo_contours) +
  tm_iso()
Figure 6.7: Example of a map created with tm_iso() showing contour lines with text labels.

6.5 Raster

library(stars)
slo_elev = read_stars("data/slovenia/slo_elev.tif")
slo_gm = read_stars("data/slovenia/slo_gm.tif")

Visualization of raster data depends on the raster type (continuous or categorical), its resolution, and the number of layers. Figure 19.1 shows two simple examples of continuous and categorical raster visualizations created with tm_raster(). This function attempts to recognize the type of a given raster – when the input raster is continuous then the pretty style is used (Figure 6.8 (a)).

tm_shape(slo_elev) +
  tm_raster()

However, the continuous scale (tm_scale_continuous()) may better represent phenomena that progressively vary in space – as you can see in Section 8.6.

On the other hand, when the given raster is categorical, then tm_raster() uses tm_scale_categorical() automatically (Figure 6.8 (b)).

tm_shape(slo_gm) +
  tm_raster()
(a) Continuous raster map
(b) Categorical raster map
Figure 6.8: Examples of raster maps

We can also adjust the legend title, used colors, and many more, in a similar fashion as in the previously mentioned layers.

The above examples used a raster with one layer only. However, rasters can have many layers, either represented by layers from the terra package or dimensions and attributes from the stars package. By default, tmap shows all of the layers, where each raster has its own legend.

raster2 = c(slo_elev, slo_gm)
tm_shape(raster2) +
  tm_raster()

We can modify their arrangement with tm_facets() (Figure 6.9).

tm_shape(raster2) +
  tm_raster() +
  tm_facets(ncol = 1) +
  tm_layout(panel.labels = c("Elevation", "Geomorphons"))
Figure 6.9: A map created from a multilayered raster.

If you want to learn more – we focus on how to modify map layout in Chapter 12 and how to specify and modify facets (also known as small multiples) in Chapter 16.

6.6 Tile

Tile layers can be used for two purposes: either as a basemap or an overlay layer. By default, three basemaps are used in the interactive mode (tmap_mode("view")): "Esri.WorldGrayCanvas", "OpenStreetMap", and "Esri.WorldTopoMap". However, we can change the basemaps with a vector with the names of the tile layers’ providers (Figure 6.10).

tmap_mode("view")
tm_basemap(c(StreetMap = "OpenStreetMap", TopoMap = "OpenTopoMap")) +
  tm_shape(slo_cities, is.main = TRUE) + 
  tm_dots(col = "red", group = "slo_cities")
Figure 6.10: OpenStreetMap tile layer used as a base map with the red dots representing cities in Slovenia.

In the above code, we made two basemaps available: "OpenStreetMap" and "OpenTopoMap", and for the map legend purpose, we renamed them as StreetMap and TopoMap. A complete list of available basemaps is in the leaflet::providers object and on the https://leaflet-extras.github.io/leaflet-providers/preview/ website1. The tm_basemap(NULL) function allows to disable basemaps entirely.

The tm_tiles() function, on the other hand, draws the tile layer on the top (as an overlay layer) of the previous tm_ layer. In the next example, we put the vector "CartoDB.PositronOnlyLabels" tiles on top of the previously set basemaps, but below the dots layer (Figure 6.11).

tm_basemap(c(StreetMap = "OpenStreetMap", TopoMap = "OpenTopoMap")) +
  tm_tiles(c(CartoDB = "CartoDB.PositronOnlyLabels")) +
  tm_shape(slo_cities, is.main = TRUE) + 
  tm_dots(col = "red", group = "slo_cities")
Figure 6.11: OpenStreetMap tile layer used as a base map with dashed lines representing island coastline and the red dots representing slo_cities on Easter Island.

Tile layers are usually created to be used interactively. We can see it, for example, by the number of details varying depending on the zoom level we set. That being said, many people find them useful also for static maps, and tmap allows us to use them in this way. It uses the maptiles package to download the tiles and then plot them as a raster layer.

A complete list of available providers and some information about zoom levels are in the help file of the ?maptiles::get_tiles() function. Different map tiles providers offer unique map styles, while zoom levels relate to different levels of detail – the larger level, the more details we will get. When using map tiles, we should also consider adding their attribution to the map. Attribution for each provider can be obtained using the maptiles::get_credit() function by specifying the provider name, for example, get_credit("CartoDB.VoyagerNoLabels").

The code below plots the "CartoDB.VoyagerNoLabels" tiles in the background, adds the island outline in light blue color, and puts attribution text in the bottom right corner of the map (Figure 6.12)).

tmap_mode("plot")
#> ℹ tmap mode set to "plot".
tm_basemap("CartoDB.VoyagerNoLabels") + 
  tm_shape(slo_borders) +
  tm_borders(lwd = 5, col = "lightblue") +
  tm_credits(maptiles::get_credit("CartoDB.VoyagerNoLabels"),
             bg.color = "white")
Figure 6.12: Example of a static map using a downloaded "CartoDB.VoyagerNoLabels" tile layer.

  1. Additional details can be found in the leaflet::providers.details object↩︎