---
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.** `scale_cheysson()` is new in ggCheysson 1.1.0, and was changed in 1.2.0 to a
single scale using the *swatches* described below; its interface may change again. In 1.2.0,
`aes_cheysson()` is deprecated: it is now the same as `aes(pattern = x)`.

```{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: swatches and `scale_cheysson()`

ggCheysson registers each element of each palette as a `ggpattern` pattern of its own, a
*swatch*, named `"<palette>-<element>"` (e.g. `"1886_28-1"`). A swatch draws all of the
element's properties together: its fill or paper color, hatch type, line colors and angle, as
well as its own line spacing and density. So only one aesthetic needs to be mapped, `pattern`,
and `scale_cheysson("1886_28")` is a single scale that gives each level of `country` one
swatch. This draws essentially the same plot as above (the swatches' line spacings differ
slightly, following Cheysson's; see [Line spacing and density](#line-spacing-and-density)):

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

The other pattern aesthetics, such as `pattern_fill` or `pattern_angle`, aren't needed, and a
swatch ignores them if they are mapped or set. The exceptions are `pattern_spacing` and
`pattern_density`, which change how close and how thick the lines are; see
[Line spacing and density](#line-spacing-and-density) below.

## Choosing a palette: the swatch atlas

`show_swatches()` draws every swatch, one row per palette and one column per element, with the
palettes grouped by type. A swatch's name is its row and column: row `1886_28`, column 3 is
`"1886_28-3"`. `scale_cheysson("1886_28")` uses that row from left to right, and when the data
have fewer levels than the palette has elements, it picks elements spread across the row (see
[Ordered data](#ordered-data-sequential-and-diverging-palettes)).

```{r swatch-atlas, fig.width=8, fig.height=10, out.width="100%", fig.alt="Atlas of all 134 Cheysson swatches: one row per palette, grouped into panels by palette type (sequential, diverging, grouped, category), with columns numbered by element; each cell shows a solid fill, stripes or crosshatching"}
show_swatches()
```

The atlas shows how Cheysson built each kind of palette. In sequential palettes such as
`1888_27` and `1900_28`, the steps are hatchings of increasing density, ending in a solid
color. In grouped palettes such as `1886_11` and `1886_24`, each color comes as stripes and
crosshatching, or solid and hatched. Category palettes are mostly solid colors. To see only
some palettes, give their names or a type, e.g. `show_swatches("diverging")`.

## Legends

There is one scale, so there is one legend, and its keys show the full swatches. Arguments to
`scale_cheysson()` such as `name` and `labels` go to that scale. 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, pattern = type)) +
  geom_col_pattern(position = "dodge", 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, pattern = response)) +
  geom_col_pattern(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 the pattern `na.value`, by default `"none"`: no swatch, so the bar shows
the geom's own `fill`. Since every swatch covers the geom's fill, setting `fill` in the geom
sets the color for missing values only:

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

ggplot(trade_na, aes(country, exports, pattern = country)) +
  geom_col_pattern(fill = "grey90", colour = "black") +
  scale_cheysson("1886_28") +
  labs(title = "Exports by Nation, 1885", x = NULL, y = "Thousands of francs") +
  theme_cheysson() +
  theme(legend.position = "none")
```

## Line spacing and density

The swatches of a palette keep the relative line spacings Cheysson used, measured on his
plates: in a sequential palette such as `1888_27` the hatching gets denser at each step (see
the literacy map in `vignette("guerry-maps", package = "ggCheysson")`). A palette's typical
swatch has a spacing of 0.025, and all draw their lines with a density of 0.3, which suit
plots of about this size.

`pattern_spacing` in the geom sets that typical spacing, and the other swatches keep their
proportions to it; `pattern_density` sets the density. What they mean depends on the version
of `ggpattern`:

- **ggpattern 1.4.0 or later**: they replace the defaults, so `pattern_spacing = 0.05` gives
  lines twice as far apart.
- **Earlier versions**: these always supply their own values (0.05 and 0.2), so the swatches
  treat a setting as relative to those: `pattern_spacing = 0.1` gives lines twice as far apart.

So code that sets them may draw differently when `ggpattern` is updated; set them only when
needed. See `?cheysson_swatches` for details, including `pattern_units`.

## Using only some of a palette's properties

A swatch always applies all of an element's properties. To use only some of them, use the
individual scales, `scale_fill_cheysson_pattern()` and the `scale_pattern_*_cheysson()`
family, as in [the long way](#the-long-way-one-mapping-and-one-scale-per-property) above,
and map only the aesthetics you want. Here the bars keep the palette's fills, 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_fill_cheysson_pattern("1886_28") +
  scale_pattern_type_cheysson("1886_28") +
  scale_pattern_fill_cheysson("1886_28") +
  labs(title = "Without the palette's angles", x = NULL, y = "Thousands of francs") +
  theme_cheysson() +
  theme(legend.position = "none")
```

## What is not included

- Line widths: all swatches use the same density, so a hatching's darkness comes from its
  spacing alone. A few palettes, such as `1891_19`, trade spacing against line width, and draw
  less faithfully.
- 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.
