Section 3.2 Creating proportional symbol maps
In this chapter we are going to introduce the
tmap package. This package was developed to easily produce thematic maps. It is inspired by the ggplot2 package and the layered grammar of graphics. It was written by Martjin Tennekes, a Dutch data scientist. It is fairly user friendly and intuitive. To read more about tmap, see [143].
In
tmap each map can be plotted as a static map (plot mode) or shown interactively (view mode). We will start by focusing on static maps.
For the purpose of demonstrating some of the functionality of this package, we will use the Manchester crime data generated in the first chapter. We have now added a few variables obtained from the Census that will be handy later on. You can obtain the data in a GeoJSON format from the companion data to this book (see Preamble if you still need to download this data!). Now let’s import the data, saved in the file
manchester.geojson using the st_read() function from the sf package.
manchester <- st_read("data/manchester.geojson", quiet=TRUE)
Every time you use the
tmap package you will need a line of code that specifies the spatial object you will be using. Although originally developed to handle sp objects only, it now also has support for sf objects. For specifying the spatial object, we use the tm_shape() function and inside we specify the name of the spatial object we are using. Its key function is simply to do just that, to identify our spatial data. On its own, this will do nothing apparent. No map will be created. We need to add functions to specify what we are doing with that spatial object, how we want to represent it. If you try to run this line on its own, you’ll get an error that you must "Specify at least one layer after each tm_shape" (or it might say "Error: no layer elements defined after tm_shape" depending on your R version).
tm_shape(manchester)
The main plotting method consists of elements that we can add. The first element is the
tm_shape() function specifying the spatial object, and then we can add a series of elements specifying layers in the visualisation. They can include polygons, symbols, polylines, raster, and text labels as base layers.
In
tmap you can use tm_symbols for this. As noted, with tmap you can produce both static and interactive maps. The interactive maps rely on leaflet. You can control whether the map is static or interactive with the tmap_mode() function. If you want a static map, you pass plot as an argument; if you want an interactive map, you pass view as an argument.
Let’s create a static map first:
tmap_mode("plot") # specify we want static map
tm_shape(manchester) +
tm_bubbles("count") # add a 'bubbles' geometry layer

In the map produced, each bubble represents the number of crimes in each LSOA within the city of Manchester. We can add the borders of the census areas for better representation. The
border.lwd argument set to NA in the tm_bubbles() is asking R not to draw a border to the circles. Whereas tm_borders() brings back a layer with the borders of the polygons representing the different LSOAs in Manchester city. Notice how we are modifying the transparency of the borders with the alpha parameter. In addition, we are adding a tm_layout() function that makes explicit where and how we want the legend.
#use tm_shape function to specify spatial object
tm_shape(manchester) +
#use tm_bubbles to add the bubble visualisation,
# but set the 'border.lwd' parameter to NA,
# meaning no symbol borders are drawn
tm_bubbles("count", border.lwd=NA) +
#add the LSOA border outlines using tm_borders,
# but set their transparency using the alpha parameter
# (0 is totally transparent, 1 is not at all)
tm_borders(alpha=0.1) +
#use tm_layout to make the legend look nice
tm_layout(legend.position = c("right", "bottom"),
legend.title.size = 0.8, legend.text.size = 0.5)

There are several arguments that you can pass within the
tm_bubble() function that control the appearance of the symbols. The scale argument controls the symbol size multiplier number. Larger values will represent the largest bubble as larger. You can experiment with the value you find more appropriate. Another helpful parameter in tm_bubble() is alpha, which you can use to make the symbols more or less transparent as a possible way to deal with situations where you may have a significant degree of overlapping between the symbols. You can play around with this and modify the code we provide above with different values for these two arguments.
By default, the symbol area sizes are scaled proportionally to the data variable you are using. As noted above, this is done by taking the square root of the normalized data variable. This is called mathematical scaling. However, "it is well known that the perceived area of proportional symbols does not match their mathematical area; rather, we are inclined to underestimate the area of larger symbols. As a solution to this problem, it is reasonable to modify the area of larger circles in order to match it with the perceived area" ([142]). The
perceptual = TRUE option allows you to use a method that aims to compensate for how the default approach may underestimate the area of the larger symbols. Notice the difference between the map we produced and this new one.
tm_shape(manchester) +
tm_bubbles("count", border.lwd=NA, perceptual = TRUE) + # add perceptual = TRUE
tm_borders(alpha=0.1) +
tm_layout(legend.position = c("right", "bottom"),
legend.title.size = 0.8, legend.text.size = 0.5)

We can see that we get much larger bubbles in the City Centre region of Manchester local authority. There is a chance that this is due to a greater number of people present in this area. Unless we control for this, by mapping rates, we may not be able to meaningfully tell the difference.
