Data:
library(terra)
library(legendry)
library(tidyverse)
library(tidyterra)
m <- matrix(c(1:24,25), nrow=5, ncol=5)
n <- matrix(rep(5,time=25),nrow = 5,ncol = 5)
## as.double() ----
dt <- rast(c(A = rast(m),B = rast(n))) |>
as.double()
The color obtained should be interpolated. But the highest/lowest values do not strictly correspond to red/blue.
ggplot() +
geom_spatraster(data = rast(m), na.rm = TRUE) +
scale_fill_steps2(
low = 'blue',
mid = 'white',
high = 'red',
midpoint = 10,
na.value = NA,
guide = guide_colourbar(
barwidth = unit(5, "cm"),
barheight = unit(0.4, "cm"),
title = 'Pre (mm/day)',
title.position = "top",
position = 'bottom'
)
) +
scale_y_continuous(expand = c(0, 0)) +
scale_x_continuous(expand = c(0, 0)) +
theme_bw() +
theme(aspect.ratio = 1)
Trying to make custom colors for different intervals, the results are not strictly corresponding results.
ggplot() +
geom_spatraster(data = dt, na.rm = TRUE) +
scale_fill_stepsn(
# n.breaks = 5,
colors = c("blue", 'green', "yellow", "orange", "red"),
breaks = c (5, 10, 15, 20),
na.value = NA,
guide = guide_colourbar(
barwidth = unit(5, "cm"),
barheight = unit(0.4, "cm"),
title = 'Pre (mm/day)',
title.position = "top",
position = 'bottom'
)
) +
facet_grid(~lyr) +
scale_y_continuous(expand = c(0, 0)) +
scale_x_continuous(expand = c(0, 0)) +
theme_bw() +
theme(aspect.ratio = 1)
I didn’t get a very clear answer in https://ggplot2-book.org/scales-colour#sec-binned-colour. What is the reason for this? Is there a good way to fix it?
0
Your scale is set up such that the lowest value (1) maps to pure blue, 11 maps to white, and the highest value (25) maps to pure red.
But the bins are coloured according to where the centre of each bin falls. This means that the first bin is not pure blue, but the colour that is mapped to the value in the middle of that bin (3). This will be a colour exactly one fifth of the way between pure blue and pure white. Similarly, the top bin is coloured at the value corresponding to 22.5, which is approximately 0.82 of the way between white and red.
If you want pure blue and pure red for the end bins, you need to ensure that the centre of each bin is at the given colour. You can’t do this with scale_fill_steps2
, but you can do it with scale_fill_stepsn
. This requires knowing where the centre of each specified bin lies on a scale of 0 to 1, and also “bookending” the scale with blue at 0 and red at 1:
ggplot() +
geom_spatraster(data = dt, na.rm = TRUE) +
scale_fill_stepsn(
colors = c('blue', "blue", "white", "red", "red"),
values = (c(1, 3, 13, 22.5, 25) - 1)/24,
breaks = c(0, 5, 10, 15, 20, 25),
limits = c(0, 25),
na.value = NA,
guide = guide_colourbar(
barwidth = unit(5, "cm"),
barheight = unit(0.4, "cm"),
title = 'Pre (mm/day)',
title.position = "top",
position = 'bottom'
)
) +
scale_y_continuous(expand = c(0, 0)) +
scale_x_continuous(expand = c(0, 0)) +
facet_wrap(~lyr) +
theme_bw() +
theme(aspect.ratio = 1)
Similarly, to have truly bespoke colours in each bin, set the value for each colour as the midpoint of the bins, except for the outer bins, which need “bookended”
ggplot() +
geom_spatraster(data = dt, na.rm = TRUE) +
scale_fill_stepsn(
colors = c("blue", "blue", 'green', "yellow", "orange", "red", "red"),
values = c(0, 2.5, 7.5, 12.5, 17.5, 22.5, 25)/25,
breaks = c (0, 5, 10, 15, 20, 25),
limits = c(0, 25),
oob = scales::oob_squish,
na.value = NA,
guide = guide_colourbar(
barwidth = unit(5, "cm"),
barheight = unit(0.4, "cm"),
title = 'Pre (mm/day)',
title.position = "top",
position = 'bottom'
)
) +
facet_grid(~lyr) +
scale_y_continuous(expand = c(0, 0)) +
scale_x_continuous(expand = c(0, 0)) +
theme_bw() +
theme(aspect.ratio = 1)
This seems like a hassle, but I guess a binned gradient scale is not intended to be used to define exact colors for each bin – this is where I would be more likely to use a discrete scale, as you might get from applying cut
to your data values, plotting them as factors and colouring with scale_fill_manual
Something like:
ggplot() +
geom_spatraster(data = dt,
aes(fill = after_stat(cut(.data[["value"]],
breaks = c(0:5) * 5,
labels = 1:5 * 5)))) +
scale_fill_manual('Pre (mm/day)',
values = c("blue", 'green', "yellow", "orange", "red")) +
facet_grid(~lyr) +
scale_y_continuous(expand = c(0, 0)) +
scale_x_continuous(expand = c(0, 0)) +
theme_bw(16) +
theme(aspect.ratio = 1,
legend.position = "bottom",
legend.title = element_text(margin = margin(10, 10, 10, 10)),
legend.text = element_text(hjust = 1, margin = margin(5, 0, 0, 0)),
legend.text.position = "bottom",
legend.title.position = "top",
legend.key.width = unit(15, "mm"),
legend.key.spacing = unit(0, "mm"))