Section 5.3 Text
There are important pieces of information with every map which are represented by text. Titles, subtitles, legend labels, and annotations all help to make the message communicated by your map more clear and obvious to your readers. Further, important information about the underlying data can be communicated through product notes. You want to acknowledge the sources of your data (both attribute data and geometry data), as well as leave some information about yourself as the map-maker, so consumers of your map can understand who is behind this map, and leave some contact information to get in touch with any questions. In this section we go through how to add such text information to your maps.
Subsection 5.3.1 Titles and subtitles
To give your map a title and subtitle, you can use the appropriate functions from the
ggplot2() package. In this case we can add both within the ggtitle function. Make sure that your title is short and specific, so it is clear what your map is about. You can include a subtitle to elaborate on this, or you can add additional information such as the period for which your map represents data (in this case, January 2020).
First, let’s save our map into an object, call it
map. Then we can add to this object.
map <- ggplot(data = hu_dd) +
geom_sf(aes(fill = total_quantiles),
lwd = 0.5, col = "white") +
scale_fill_brewer(type = "seq", palette = "Greens") +
theme_void()
Now we can add a title, with the
ggtitle() function.
map <- map +
# specify both title and subtitle:
ggtitle(label = "Number of breathalyser tests per county in Hungary",
subtitle = "January 2020")
map

Subsection 5.3.2 Legend
Besides an informative title/subtitle, you need your legend to be clear to your readers as well. To modify the title to your legend, you can use the
name = parameter in your scale_fill_brewer() function, where we specified the colour palette.
map <- map +
scale_fill_brewer(type = "seq", palette = "Greens",
name = "Total tests (quantiles)") # desired legend title
map

Besides the legend title, we can also change how the levels are labeled. For this, we can use text manipulation functions, such as
gsub() which substitutes one string for another. For example, we can replace the "," with a " - " if we’d like using the gsub() function. We can create a new object, here new_levels, which has the desired labels:
# create object new_levels with desired labels
new_levels <- gsub(","," - ",levels(hu_dd$total_quantiles))
We can then assign this new levels object in the
labels = parameter of the scale_fill_brewer() function:
map <- map +
scale_fill_brewer(type = "seq", palette = "Greens",
name = "Total tests (quantiles)",
labels = new_levels) # specify our new labels
map

Of course it is possible that we want to completely re-write the levels, rather than just swap out one character for another. In this case, we can completely rename the levels if we liked by passing the new, desired labels into this
new_levels object:
new_levels <- c("< 4796", "4796 to < 10785", "10785 to < 15070", "> 15070")
And once again, we specify to use these labels in the
labels = parameter of the scale_fill_brewer() function:
map +
scale_fill_brewer(type = "seq", palette = "Greens",
name = "Total tests (quantiles)",
labels = new_levels) # again specify labels object

You can change the labels you would like, but do keep in mind any loss of information you may introduce. For example with this second version, we no longer know what are the minimum and maximum values on our map, as we’ve removed that information with our new levels. Again, there are no wrong answers here, but whatever best fits the data and the purpose of the map. Depending on the context, for example, you may use the title to draw home the key finding or lesson you want the reader to take from the map (rather than describing the plot variable).
Subsection 5.3.3 Annotation
In certain cases, it might be that we want to point out something specific on our map. This would be the case if we imagine showing someone the map in person, and pointing to a specific region, or area, to highlight it. Or we might just want to label all polygons, for clarity. If we are not present to discuss our map, we might want to include some text annotation instead, which will do this for us. From
ggplot2 versions v.3.1.0, the functions geom_sf_text() and geom_sf_label() make it very smooth for us to do this.
In the below example, let’s say we want to label each polygon with the name of the county which it represents. In this case, we use the
geom_sf_label() geometry, and inside it we use aes() to point to which column in our dataframe we want to use (in this case, the column is called name).
map +
geom_sf_label(aes(label = name)) # add layer of labels from the name column

You may notice, there is some overlapping here which renders some names unreadable. Well while there is work in this space to develop the function
geom_sf_label_repel(), at the time of writing this is not yet available. However this application of the geom_label_repel() function from the ggrepel package advised by [152] achieves the same outcome:
library(ggrepel)
map +
geom_label_repel(data = hu_dd, # add repel layer, specify dataframe
aes(label = name, # specify where to find label (name column)
geometry = geometry), # specify geometry
stat = "sf_coordinates", # transformation to use on the data
min.segment.length = 0) # don't draw segments shorter than this

This way we can display the names of all the counties without overlap, so they all become legible. While this is achievable, think back to the principles of good design. This seems busy, and it may overwhelm the reader. Not to mention, is it important that all polygons are labelled here? It might be; remember this depends on the message the map is intended to communicate! But here let’s consider a different scenario, where we want to use annotation to label only those counties which meet some specific criteria. For example, you might want to label only those which are in the top quartile. One way to achieve this is to create a separate dataframe, which only includes the desired polygons, and pass this into the
geom_sf_label() function.
#create new dataframe with only top counties
labs_df <- hu_dd %>% filter(total_breath_tests >= 15070)
#add to map
map +
geom_sf_label(data = labs_df, # specify to use the labels df
aes(label = name))

In another scenario, you might want to label only one region of interest. In this case, we might actually want to keep our annotation off the map, and draw an arrow onto the map pointing to where this annotation refers to. We can do this by using the
nudge_x and nudge_y parameters of the geom_sf_label() function, to nudge the label position along the x and y axes respectively. In this case, let’s label only Budapest.
#create new labels dataframe
labs_df <- hu_dd %>% filter(name == "Budapest")
map +
geom_sf_label(data = labs_df, aes(label = name), # label from name column
nudge_y = 0.9, # move label on y axis
nudge_x = -0.1) # move label on x axis

But this floating label is a little ambiguous, and needs to be more explicitly connected to the map. To achieve this, we might want to use an arrow to point out Budapest on the map. To do this, we can use
geom_curve() within ggplot2. We will need two sets of x and y values for this segment, the start point (x and y) and the end point (xend and yend). The end point will be the coordinates where we want the arrow pointing to. This would be some x,y pair within Budapest. We can use the st_coordinates() function once again to extract the centroid, this time of the Budapest polygon. Let’s extract the longitude of the centroid into an object called bp_x for our x value, and the latitude of the centroid into an object called bp_y for our y value.
# get x coordinate
bp_x <- labs_df %>%
mutate(cent_lng = st_coordinates(st_centroid(.))[,1]) %>% pull(cent_lng)
# get y coordinate
bp_y <- labs_df %>%
mutate(cent_lat = st_coordinates(st_centroid(.))[,2]) %>% pull(cent_lat)
Great, so we have the end point for our segment! But where should it start? Well we want it pointing from our label, so we can think back to how we adjusted this label with the
nudge_x and nudge_y parameters inside the geom_sf_label() function earlier. We can add (or subtract) these values to our bp_x and bp_y objects to determine the start points for our curve. Finally, we can also specify some characteristics of the arrow head on our curve with the arrow = parameter. Here we specify that we want 2 millimeters.
map <- map +
geom_curve(x = bp_x - 0.1, # starting x coordinate (the label)
y = bp_y + 0.9, # starting y coordinate (the label)
xend = bp_x , # ending x coordinate (BP centroid)
yend = bp_y, # ending y coordinate (BP centroid)
arrow = arrow(length = unit(2, "mm"))) +
geom_sf_label(data = labs_df,
aes(label = name),
nudge_y = 0.9,
nudge_x = -0.1)
map

This is one way to include annotation while keeping the map clear, but still using the geographic information to reference. Annotations can be useful, but think carefully about whether you need them for your map, as they can also be distracting if not used appropriately.
Subsection 5.3.4 Production notes
Something that should be a key feature of all maps is the inclusion of production notes. This includes some information about who made it, as well as any attributions for data. Here we can string together a series of information we want to include, appended with a newline character (
\n), in order to keep our notes nice and legible. We save this into a new object called caption_text.
caption_text <- paste("Map created by Réka Solymosi (@r_solymosi)",
"Contains data from Police Hungary",
"http://www.police.hu/hu/a-rendorsegrol/",
"statisztikak/kozrendvedelem",
"Map data copyrighted OpenStreetMap contributors",
"available from https://www.openstreetmap.org",
sep = "\n")
Then we can include this
caption_text object as a caption in the function labs(), which stands for labels.
map <- map + labs(caption = caption_text) # include production notes here
map

In this way we give credibility to our map, and we also make the proper attributions to where our data come from.
