forked from tidyverse/ggplot2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathguide-colorsteps.R
133 lines (130 loc) · 4.81 KB
/
guide-colorsteps.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#' Discretized colourbar guide
#'
#' This guide is version of [guide_colourbar()] for binned colour and fill
#' scales. It shows areas between breaks as a single constant colour instead of
#' the gradient known from the colourbar counterpart.
#'
#' @param even.steps Should the rendered size of the bins be equal, or should
#' they be proportional to their length in the data space? Defaults to `TRUE`
#' @param show.limits Should labels for the outer limits of the bins be printed?
#' Default is `NULL` which makes the guide use the setting from the scale
#' @param ticks A logical specifying if tick marks on the colourbar should be
#' visible.
#' @inheritDotParams guide_colourbar -nbin -raster -ticks -available_aes
#'
#' @inheritSection guide_bins Use with discrete scale
#'
#' @return A guide object
#' @export
#'
#' @family guides
#' @examples
#' df <- expand.grid(X1 = 1:10, X2 = 1:10)
#' df$value <- df$X1 * df$X2
#'
#' p <- ggplot(df, aes(X1, X2)) + geom_tile(aes(fill = value))
#'
#' # Coloursteps guide is the default for binned colour scales
#' p + scale_fill_binned()
#'
#' # By default each bin in the guide is the same size irrespectively of how
#' # their sizes relate in data space
#' p + scale_fill_binned(breaks = c(10, 25, 50))
#'
#' # This can be changed with the `even.steps` argument
#' p + scale_fill_binned(
#' breaks = c(10, 25, 50),
#' guide = guide_coloursteps(even.steps = FALSE)
#' )
#'
#' # By default the limits is not shown, but this can be changed
#' p + scale_fill_binned(guide = guide_coloursteps(show.limits = TRUE))
#'
#' # (can also be set in the scale)
#' p + scale_fill_binned(show.limits = TRUE)
#'
guide_coloursteps <- function(even.steps = TRUE, show.limits = NULL, ticks = FALSE, ...) {
guide <- guide_colourbar(raster = FALSE, ticks = ticks, nbin = 100, ...)
guide$even.steps <- even.steps
guide$show.limits <- show.limits
class(guide) <- c('colorsteps', class(guide))
guide
}
#' @export
#' @rdname guide_coloursteps
guide_colorsteps <- guide_coloursteps
#' @export
guide_train.colorsteps <- function(guide, scale, aesthetic = NULL) {
breaks <- scale$get_breaks()
breaks <- breaks[!is.na(breaks)]
if (guide$even.steps || !is.numeric(breaks)) {
if (length(breaks) == 0 || all(is.na(breaks))) {
return()
}
if (is.numeric(breaks)) {
limits <- scale$get_limits()
breaks <- breaks[!breaks %in% limits]
all_breaks <- c(limits[1], breaks, limits[2])
bin_at <- all_breaks[-1] - diff(all_breaks) / 2
} else {
# If the breaks are not numeric it is used with a discrete scale. We check
# if the breaks follow the allowed format "(<lower>, <upper>]", and if it
# does we convert it into bin specs
if (!guide$even.steps) {
warn("`even.steps = FALSE` is not supported when used together with a discrete scale")
}
bin_at <- breaks
breaks_num <- as.character(breaks)
breaks_num <- strsplit(gsub("\\(|\\)|\\[|\\]", "", breaks_num), ",\\s?")
breaks_num <- as.numeric(unlist(breaks_num))
if (anyNA(breaks_num)) {
abort('Breaks not formatted correctly for a bin legend. Use `(<lower>, <upper>]` format to indicate bins')
}
all_breaks <- breaks_num[c(1, seq_along(breaks) * 2)]
limits <- all_breaks[c(1, length(all_breaks))]
breaks <- all_breaks[-c(1, length(all_breaks))]
}
ticks <- new_data_frame(setNames(list(scale$map(breaks)), aesthetic %||% scale$aesthetics[1]))
ticks$.value <- seq_along(breaks) - 0.5
ticks$.label <- scale$get_labels(breaks)
guide$nbin <- length(breaks) + 1
guide$key <- ticks
guide$bar <- new_data_frame(list(colour = scale$map(bin_at), value = seq_along(bin_at) - 1), n = length(bin_at))
if (guide$reverse) {
guide$key <- guide$key[nrow(guide$key):1, ]
guide$bar <- guide$bar[nrow(guide$bar):1, ]
}
guide$hash <- with(guide, digest::digest(list(title, key$.label, bar, name)))
} else {
guide <- NextMethod()
limits <- scale$get_limits()
}
if (guide$show.limits %||% scale$show.limits %||% FALSE) {
edges <- rescale(c(0, 1), to = guide$bar$value[c(1, nrow(guide$bar))], from = c(0.5, guide$nbin - 0.5) / guide$nbin)
if (guide$reverse) edges <- rev(edges)
guide$key <- guide$key[c(NA, seq_len(nrow(guide$key)), NA), , drop = FALSE]
guide$key$.value[c(1, nrow(guide$key))] <- edges
guide$key$.label[c(1, nrow(guide$key))] <- scale$get_labels(limits)
}
guide
}
#' Calculate the default hjust and vjust settings depending on legend
#' direction and position.
#'
#' @noRd
label_just_defaults.colorbar <- function(direction, position) {
if (direction == "horizontal") {
switch(
position,
"top" = list(hjust = 0.5, vjust = 0),
list(hjust = 0.5, vjust = 1)
)
}
else {
switch(
position,
"left" = list(hjust = 1, vjust = 0.5),
list(hjust = 0, vjust = 0.5)
)
}
}