Skip to content

Commit 851233f

Browse files
authored
Smarter keys (attempt 2) (#5502)
* fix typo * Switch for drawing keys * `lapply` -> `Map()` * Helper function * write test * accept snapshots * Add news bullet
1 parent 4bbba83 commit 851233f

File tree

5 files changed

+163
-5
lines changed

5 files changed

+163
-5
lines changed

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# ggplot2 (development version)
22

3+
* By default, `guide_legend()` now only draws a key glyph for a layer when
4+
the value is is the layer's data. To revert to the old behaviour, you
5+
can still set `show.legend = c({aesthetic} = TRUE)` (@teunbrand, #3648).
6+
37
* The spacing between legend keys and their labels, in addition to legends
48
and their titles, is now controlled by the text's `margin` setting. Not
59
specifying margins will automatically add appropriate text margins. To

R/guide-legend.R

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ GuideLegend <- ggproto(
322322

323323
get_layer_key = function(params, layers, data) {
324324

325-
decor <- lapply(layers, function(layer) {
325+
decor <- Map(layer = layers, df = data, f = function(layer, df) {
326326

327327
matched_aes <- matched_aes(layer, params)
328328

@@ -343,9 +343,10 @@ GuideLegend <- ggproto(
343343
"Failed to apply {.fn after_scale} modifications to legend",
344344
parent = cnd
345345
)
346-
layer$geom$use_defaults(params$key[matched], layer_params, list())
346+
layer$geom$use_defaults(params$key[matched_aes], layer_params, list())
347347
}
348348
)
349+
data$.draw <- keep_key_data(params$key, df, matched_aes, layer$show.legend)
349350
} else {
350351
reps <- rep(1, nrow(params$key))
351352
data <- layer$geom$use_defaults(NULL, layer$aes_params)[reps, ]
@@ -510,7 +511,12 @@ GuideLegend <- ggproto(
510511
draw <- function(i) {
511512
bg <- elements$key
512513
keys <- lapply(decor, function(g) {
513-
g$draw_key(vec_slice(g$data, i), g$params, key_size)
514+
data <- vec_slice(g$data, i)
515+
if (data$.draw %||% TRUE) {
516+
g$draw_key(data, g$params, key_size)
517+
} else {
518+
zeroGrob()
519+
}
514520
})
515521
c(list(bg), keys)
516522
}
@@ -804,3 +810,38 @@ measure_legend_keys <- function(decor, n, dim, byrow = FALSE,
804810
heights = pmax(default_height, apply(size, 1, max))
805811
)
806812
}
813+
814+
# For legend keys, check if the guide key's `.value` also occurs in the layer
815+
# data when `show.legend = NA` and data is discrete. Note that `show.legend`
816+
# besides TRUE (always show), FALSE (never show) and NA (show in relevant legend),
817+
# can also take *named* logical vector to set this behaviour per aesthetic.
818+
keep_key_data <- function(key, data, aes, show) {
819+
# First, can we exclude based on anything else than actually checking the
820+
# data that we should include or drop the key?
821+
if (!is.discrete(key$.value)) {
822+
return(TRUE)
823+
}
824+
if (is_named(show)) {
825+
aes <- intersect(aes, names(show))
826+
show <- show[aes]
827+
} else {
828+
show <- show[rep(1L, length(aes))]
829+
}
830+
if (isTRUE(any(show)) || length(show) == 0) {
831+
return(TRUE)
832+
}
833+
if (isTRUE(all(!show))) {
834+
return(FALSE)
835+
}
836+
# Second, we go find if the value is actually present in the data.
837+
aes <- aes[is.na(show)]
838+
match <- which(names(data) %in% aes)
839+
if (length(match) == 0) {
840+
return(TRUE)
841+
}
842+
keep <- rep(FALSE, nrow(key))
843+
for (column in match) {
844+
keep <- keep | vec_in(key$.value, data[[column]])
845+
}
846+
keep
847+
}
Lines changed: 76 additions & 0 deletions
Loading

tests/testthat/_snaps/geom-polygon/open-and-closed-munched-polygons.svg

Lines changed: 0 additions & 2 deletions
Loading

tests/testthat/test-draw-key.R

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,42 @@ test_that("horizontal key glyphs work", {
5555
guides(color = guide_legend(order = 1))
5656
)
5757
})
58+
59+
test_that("keep_draw_key", {
60+
61+
key <- data_frame0(.value = c("A", "C"))
62+
data <- data_frame0(foo = c("A", "B"), bar = c("B", "C"))
63+
64+
expect_true( keep_key_data(key, data, "foo", show = TRUE))
65+
expect_false(keep_key_data(key, data, "foo", show = FALSE))
66+
expect_equal(keep_key_data(key, data, "foo", show = NA), c(TRUE, FALSE))
67+
expect_equal(keep_key_data(key, data, "bar", show = NA), c(FALSE, TRUE))
68+
expect_equal(keep_key_data(key, data, c("foo", "bar"), show = NA), c(TRUE, TRUE))
69+
70+
# Named show
71+
expect_true(
72+
keep_key_data(key, data, c("foo", "bar"), show = c(foo = TRUE, bar = FALSE))
73+
)
74+
expect_equal(
75+
keep_key_data(key, data, c("foo", "bar"), show = c(foo = NA, bar = FALSE)),
76+
c(TRUE, FALSE)
77+
)
78+
expect_equal(
79+
keep_key_data(key, data, c("foo", "bar"), show = c(foo = FALSE, bar = NA)),
80+
c(FALSE, TRUE)
81+
)
82+
83+
p <- ggplot(data.frame(x = 1:2), aes(x, x)) +
84+
geom_point(
85+
aes(colour = "point", alpha = "point"),
86+
show.legend = c("colour" = NA, alpha = FALSE)
87+
) +
88+
geom_line(
89+
aes(colour = "line", alpha = "line"),
90+
show.legend = c("colour" = NA, alpha = TRUE)
91+
) +
92+
suppressWarnings(scale_alpha_discrete())
93+
94+
expect_doppelganger("appropriate colour key with alpha key as lines", p)
95+
96+
})

0 commit comments

Comments
 (0)