---
title: "Combining Colors and Patterns"
author: "Michael Friendly"
date: "`r Sys.Date()`"
output:
  rmarkdown::html_vignette:
    toc: true
    toc_depth: 2
vignette: >
  %\VignetteIndexEntry{Combining Colors and Patterns}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.width = 7,
  fig.height = 4.5,
  fig.align = "center",
  out.width = "75%",
  # ragg, not the default png(): on Intel macOS the Quartz png() device
  # segfaults drawing ggpattern's grid masks at >= 96 dpi (CRAN check ERROR on 1.0.1)
  dev = if (requireNamespace("ragg", quietly = TRUE)) "ragg_png" else "png",
  # draw showtext fonts at the device's real dpi (pkgdown renders retina at 2x)
  fig.showtext = TRUE,
  warning = FALSE,
  message = FALSE,
  # everything here needs ggpattern (in Suggests)
  eval = requireNamespace("ggpattern", quietly = TRUE)
)
```

**Experimental.** The functions described here, `scale_cheysson()` and `aes_cheysson()`, are
new in ggCheysson 1.1.0, and their interface may change in a future version.

```{r load-packages}
library(ggCheysson)
library(ggplot2)
library(ggpattern)
```

```{r load-fonts, include=FALSE}
if (requireNamespace("showtext", quietly = TRUE) &&
    requireNamespace("sysfonts", quietly = TRUE)) {
  load_cheysson_fonts(method = "showtext")
  showtext::showtext_auto()
}
```

## Palettes that combine color and hatching

In the *Albums de Statistique Graphique*, a class on a map or a bar in a chart was rarely
distinguished by color alone. Cheysson's palettes combine colors with hatching: solid fills,
stripes at different angles and spacings, and crosshatching, sometimes in two colors.

Each element of a palette in `cheysson_patterns` is therefore a small bundle of properties.
Here are the six elements of palette `1886_28`:

```{r bundle}
pats <- cheysson_pattern("1886_28")
data.frame(
  pattern       = cheysson_pattern_params(pats, "pattern_type"),
  fill          = cheysson_pattern_params(pats, "fill"),
  pattern_fill  = cheysson_pattern_params(pats, "pattern_fill"),
  pattern_fill2 = cheysson_pattern_params(pats, "pattern_fill2"),
  pattern_angle = cheysson_pattern_params(pats, "pattern_angle")
)
```

- `pattern`: the hatch type, `"none"` for a solid fill
- `fill`: the paper or fill color behind the hatching (`"transparent"` for hatching on bare
  paper)
- `pattern_fill`: the color of the hatch lines
- `pattern_fill2`: the color of a crosshatch's second set of lines (the same as `pattern_fill`
  except in two-color crosshatches)
- `pattern_angle`: the angle of the lines

## The long way: one mapping and one scale per property

In `ggplot2` with `ggpattern`, each of these properties is a separate aesthetic. To apply a
palette to a variable, each aesthetic needs both a **mapping** in `aes()` and a **scale** that
supplies its values:

```{r long-form}
trade <- data.frame(
  country = c("France", "England", "Germany", "Italy"),
  exports = c(2350, 3120, 2680, 1890)
)

ggplot(trade, aes(country, exports)) +
  geom_col_pattern(
    aes(fill = country, pattern = country, pattern_fill = country,
        pattern_fill2 = country, pattern_angle = country),
    pattern_colour = NA, pattern_density = 0.3, pattern_spacing = 0.025,
    colour = "black"
  ) +
  scale_fill_cheysson_pattern("1886_28") +
  scale_pattern_type_cheysson("1886_28") +
  scale_pattern_fill_cheysson("1886_28") +
  scale_pattern_fill2_cheysson("1886_28") +
  scale_pattern_angle_cheysson("1886_28") +
  labs(title = "Exports by Nation, 1885", x = NULL, y = "Thousands of francs") +
  theme_cheysson() +
  theme(legend.position = "none")
```

## The short way: `aes_cheysson()` and `scale_cheysson()`

Two functions collapse the five mappings and the five scales to one line each:

- `aes_cheysson(country)` maps `country` to all five aesthetics. Other mappings can be given
  as further arguments, e.g. `aes_cheysson(country, x = year, y = value)`.
- `scale_cheysson("1886_28")` returns the five scales as a list, which `+` adds to the plot in
  one step. All of them take their values from the same palette elements, so each level of
  `country` gets one complete historical swatch.

This draws the same plot as above:

```{r short-form}
ggplot(trade, aes(country, exports)) +
  geom_col_pattern(
    aes_cheysson(country),
    pattern_colour = NA, pattern_density = 0.3, pattern_spacing = 0.025,
    colour = "black"
  ) +
  scale_cheysson("1886_28") +
  labs(title = "Exports by Nation, 1885", x = NULL, y = "Thousands of francs") +
  theme_cheysson() +
  theme(legend.position = "none")
```

Settings that are better fixed than mapped stay in the geom: `pattern_density` and
`pattern_spacing` (how thick and how close the lines are), `pattern_colour = NA` (no outlines
around the hatch lines), and the outline `colour`.

### Why both are needed

A scale does nothing for an aesthetic the plot doesn't map. Here only `fill` and `pattern` are
mapped, so `scale_cheysson()`'s other scales have no effect, and `ggpattern` falls back to its
default grey hatch lines and angle:

```{r partial-mapping}
ggplot(trade, aes(country, exports, fill = country, pattern = country)) +
  geom_col_pattern(pattern_colour = NA, pattern_density = 0.3, pattern_spacing = 0.025,
                   colour = "black") +
  scale_cheysson("1886_28") +
  labs(title = "Only fill and pattern mapped", x = NULL, y = "Thousands of francs") +
  theme_cheysson() +
  theme(legend.position = "none")
```

## Legends

Arguments to `scale_cheysson()` such as `name` and `labels` are passed to every scale. With
the same title, `ggplot2` merges the legends of all the aesthetics into one, whose keys show
the full swatches. Here a sequential palette, `1881_12`, whose three steps are hatchings of
increasing density in a single color, distinguishes three kinds of transport:

```{r legend, fig.width=8}
infrastructure <- data.frame(
  region = rep(c("North", "South", "East", "West"), each = 3),
  type   = factor(rep(c("Road", "Canal", "Rail"), 4), levels = c("Road", "Canal", "Rail")),
  length = c(250, 300, 450,  250, 400, 350,  300, 200, 500,  250, 350, 400)
)

ggplot(infrastructure, aes(region, length)) +
  geom_col_pattern(
    aes_cheysson(type),
    position = "dodge",
    pattern_colour = NA, pattern_density = 0.35, pattern_spacing = 0.02,
    colour = "black"
  ) +
  scale_cheysson("1881_12", name = "Network") +
  labs(title = "Transportation Networks by Region", x = NULL, y = "Hundreds of km") +
  theme_cheysson()
```

## Ordered data: sequential and diverging palettes

Sequential palettes are stored from low to high (light to dark), and diverging palettes from
one extreme through the middle to the other, so `reverse = TRUE` flips every palette the same
way. When the data have fewer levels than the palette has elements, the scales pick elements
spread over the whole palette, keeping both ends.

Palette `1883_31` is diverging: two hues, each used solid and hatched. As in Cheysson's maps,
the solid fills mark the extremes and the hatched versions the milder classes:

```{r diverging}
opinion <- data.frame(
  response = factor(c("Strongly against", "Against", "For", "Strongly for"),
                    levels = c("Strongly against", "Against", "For", "Strongly for")),
  percent = c(18, 27, 34, 21)
)

ggplot(opinion, aes(response, percent)) +
  geom_col_pattern(
    aes_cheysson(response),
    pattern_colour = NA, pattern_density = 0.35, pattern_spacing = 0.025,
    colour = "black"
  ) +
  scale_cheysson("1883_31") +
  labs(title = "A Diverging Palette", x = NULL, y = "Percent") +
  theme_cheysson() +
  theme(legend.position = "none")
```

## Missing values

Missing values get no hatching and a plain fill, set by `na.value` (default `"grey80"`):

```{r missing}
trade_na <- rbind(trade, data.frame(country = NA, exports = 1500))

ggplot(trade_na, aes(country, exports)) +
  geom_col_pattern(
    aes_cheysson(country),
    pattern_colour = NA, pattern_density = 0.3, pattern_spacing = 0.025,
    colour = "black"
  ) +
  scale_cheysson("1886_28", na.value = "grey90") +
  labs(title = "Exports by Nation, 1885", x = NULL, y = "Thousands of francs") +
  theme_cheysson() +
  theme(legend.position = "none")
```

## Applying only some aesthetics

To use only some of the palette's properties, choose them with `aesthetics`, and map only
those. Here the bars keep the palette's hatch types and line colors, but not its angles:

```{r subset}
ggplot(trade, aes(country, exports, fill = country, pattern = country,
                  pattern_fill = country)) +
  geom_col_pattern(pattern_colour = NA, pattern_density = 0.3, pattern_spacing = 0.025,
                   colour = "black") +
  scale_cheysson("1886_28", aesthetics = c("fill", "pattern", "pattern_fill")) +
  labs(title = "Without the palette's angles", x = NULL, y = "Thousands of francs") +
  theme_cheysson() +
  theme(legend.position = "none")
```

The individual scales, `scale_fill_cheysson_pattern()` and the `scale_pattern_*_cheysson()`
family, remain available for full control over each aesthetic.

## What is not included

- `pattern_density` and `pattern_spacing` are not mapped by `aes_cheysson()` or set by
  `scale_cheysson()`. The palettes' spacings were measured on Cheysson's small swatches and
  need rescaling for a full-size plot; see the literacy map in
  `vignette("guerry-maps", package = "ggCheysson")` for mapping `pattern_spacing` with
  `scale_pattern_spacing_manual()`.
- The scales are discrete. For continuous data, cut it into classes first, as Cheysson did.
- For plots without patterns (`geom_col()`, `geom_point()`, ...), use `scale_fill_cheysson()`
  and `scale_color_cheysson()`, which use the palettes' colors only.
