knitr::opts_chunk$set( collapse = TRUE, tidy = FALSE, comment = "#>", fig.width = 6, fig.height = 6 )
Here I demonstrate how to use the tricolore
library to generate ternary choropleth maps using both ggplot2
and leaflet
.
library(tricolore)
The data set euro_example
contains the administrative boundaries for the European NUTS-2 regions in the column geometry
. This data can be used to plot a choropleth map of Europe using the sf
package. Each region is represented by a single row. The name of a region is given by the variable name
while the respective NUTS-2 geocode is given by the variable id
. For each region some compositional statistics are available: Variables starting with ed
refer to the relative share of population ages 25 to 64 by educational attainment in 2016 and variables starting with lf
refer to the relative share of workers by labor-force sector in the European NUTS-2 regions 2016.
Take the first row of the data set as an example: in the Austrian region of "Burgenland" (id
= AT11
) 16.5% of the population aged 25--64 had attained an education of "Lower secondary or less" (ed_0to2
), 55.7% attained "upper secondary" education (ed_3to4
), and 27.9% attained "tertiary" education. In the very same region 4.4% of the labor-force works in the primary sector, 26.8% in the secondary and 68.2% in the tertiary sector.
The education and labor-force compositions are ternary, i.e. made up from three elements, and therefore can be color-coded as the weighted mixture of three primary colors, each primary mapped to one of the three elements. Such a color scale is called a ternary balance scheme^[See for example Dorling (2012) and Brewer (1994).]. This is what tricolore
does.
ggplot2
for ternary choropleth mapsHere I show how to create a choropleth map of the regional distribution of education attainment in Europe 2016 using ggplot2
.
1. Using the Tricolore()
function, color-code each educational composition in the euro_example
data set and add the resulting vector of hex-srgb colors as a new variable to the data frame. Store the color key separately.
# color-code the data set and generate a color-key tric <- Tricolore(euro_example, p1 = 'ed_0to2', p2 = 'ed_3to4', p3 = 'ed_5to8')
tric
contains both a vector of color-coded compositions (tric$rgb
) and the corresponding color key (tric$key
). We add the vector of colors to the map-data.
# add the vector of colors to the `euro_example` data euro_example$rgb <- tric$rgb
2. Using ggplot2
and the joined color-coded education data and geodata, plot a ternary choropleth map of education attainment in the European regions. Add the color key to the map.
The secret ingredient is scale_fill_identity()
to make sure that each region is colored according to the value in the rgb
variable of euro_educ_map
.
library(ggplot2) plot_educ <- # using sf dataframe `euro_example`... ggplot(euro_example) + # ...draw a polygon for each region... geom_sf(aes(fill = rgb, geometry = geometry), size = 0.1) + # ...and color each region according to the color code in the variable `rgb` scale_fill_identity() plot_educ
Using annotation_custom()
and ggplotGrob
we can add the color key produced by Tricolore()
to the map. Internally, the color key is produced with the ggtern
package. In order for it to render correctly we need to load ggtern
after loading ggplot2
. Don't worry, the ggplot2
functions still work.
library(ggtern) plot_educ + annotation_custom( ggplotGrob(tric$key), xmin = 55e5, xmax = 75e5, ymin = 8e5, ymax = 80e5 )
Because the color key behaves just like a ggplot2
plot we can change it to our liking.
plot_educ <- plot_educ + annotation_custom( ggplotGrob(tric$key + theme(plot.background = element_rect(fill = NA, color = NA)) + labs(L = '0-2', T = '3-4', R = '5-8')), xmin = 55e5, xmax = 75e5, ymin = 8e5, ymax = 80e5 ) plot_educ
Some final touches...
plot_educ + theme_void() + coord_sf(datum = NA) + labs( title = 'European inequalities in educational attainment', subtitle = 'Regional distribution of ISCED education levels for people aged 25-64 in 2016.' )
leaflet
for ternary choropleth mapsThe ggplot2
example above is easily adapted to leaflet
. This time I use a continuous color scale.
# color-code the data set and generate a color-key tric <- Tricolore(euro_example, p1 = 'ed_0to2', p2 = 'ed_3to4', p3 = 'ed_5to8', breaks = Inf) # add the vector of colors to the `euro_example` data euro_example$rgb <- tric$rgb
leaflet
requires geodata in spherical coordinates (longitude-latitude format). Therefore I reproject the data to a suitable crs using the sf
package.
library(sf) library(leaflet) euro_example %>% st_transform(crs = 4326) %>% leaflet() %>% addPolygons(smoothFactor = 0.1, weight = 0, fillColor = euro_example$rgb, fillOpacity = 1)
Adding a background map gives geographical context to the map. I also add a mouse pop-up of the actual data.
euro_example %>% st_transform(crs = 4326) %>% leaflet() %>% addProviderTiles(providers$Esri.WorldTerrain) %>% addPolygons(smoothFactor = 0.1, weight = 0, fillColor = euro_example$rgb, fillOpacity = 1, popup = paste0( '<b>', euro_example$name, '</b></br>', 'Primary: ', formatC(euro_example$ed_0to2*100, digits = 1, format = 'f'), '%</br>', 'Secondary: ', formatC(euro_example$ed_3to4*100, digits = 1, format = 'f'), '%</br>', 'Tertiary: ', formatC(euro_example$ed_5to8*100, digits = 1, format = 'f'), '%</br>' ) )
Adding the legend to the leaflet map requires a bit of a hack.
makePlotURI <- function(expr, width, height, ...) { pngFile <- shiny::plotPNG(function() { expr }, width = width, height = height, ...) on.exit(unlink(pngFile)) base64 <- httpuv::rawToBase64(readBin(pngFile, raw(1), file.size(pngFile))) paste0("data:image/png;base64,", base64) } legend_symbol <- makePlotURI({ print(tric$key + theme(plot.background = element_rect(fill = NA, color = NA)) + labs(L = '0-2', T = '3-4', R = '5-8')) }, 200, 200, bg = "transparent") df <- data.frame( lng = 30, lat = 70, plot = legend_symbol, stringsAsFactors = FALSE ) euro_example %>% st_transform(crs = 4326) %>% leaflet() %>% addProviderTiles(providers$Esri.WorldGrayCanvas) %>% addPolygons(smoothFactor = 0.1, weight = 0, fillColor = euro_example$rgb, fillOpacity = 1, popup = paste0( '<b>', euro_example$name, '</b></br>', 'Primary: ', formatC(euro_example$ed_0to2*100, digits = 1, format = 'f'), '%</br>', 'Secondary: ', formatC(euro_example$ed_3to4*100, digits = 1, format = 'f'), '%</br>', 'Tertiary: ', formatC(euro_example$ed_5to8*100, digits = 1, format = 'f'), '%</br>' ) ) %>% addMarkers(data = df, icon = ~icons(plot))
Brewer, C. A. (1994). Color Use Guidelines for Mapping and Visualization. In A. M. MacEachren & D. R. F. Taylor (Eds.), Visualization in Modern Cartography (pp. 123–147). Oxford, UK: Pergamon.
Dorling, D. (2012). The Visualization of Spatial Social Structure. Chichester, UK: Wiley.
Schöley, J. (2021). The centered ternary balance scheme: A technique to visualize surfaces of unbalanced three-part compositions. Demographic Research (44).
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.