Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add access to the scales at draw time using a new layer_params object #3170

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# ggplot2 3.1.0.9000

* Scales are now accessible to the `Geom` at draw time through a new
`layer_params` argument to `Geom$draw_layer()` (#3116).

* `coord_sf()` graticule lines are now drawn in the same thickness as
panel grid lines in `coord_cartesian()`, and seting panel grid
lines to `element_blank()` now also works in `coord_sf()`
Expand Down
2 changes: 1 addition & 1 deletion R/geom-.r
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Geom <- ggproto("Geom",
)
},

draw_layer = function(self, data, params, layout, coord) {
draw_layer = function(self, data, params, layout, coord, layer_params, ...) {
if (empty(data)) {
n <- if (is.factor(data$PANEL)) nlevels(data$PANEL) else 1L
return(rep(list(zeroGrob()), n))
Expand Down
20 changes: 19 additions & 1 deletion R/layer.r
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ Layer <- ggproto("Layer", NULL,
mapping = NULL,
position = NULL,
inherit.aes = FALSE,
layer_params = NULL,

print = function(self) {
if (!is.null(self$mapping)) {
Expand Down Expand Up @@ -202,6 +203,23 @@ Layer <- ggproto("Layer", NULL,
# hook to allow a layer access to the final layer data
# in input form and to global plot info
setup_layer = function(self, data, plot) {

# generate the layer_params object that gets passed to
# Geom$draw_layer()
scales <- plot$scales
self$layer_params <- ggproto("LayerParams", NULL,
get_scale = function(self, scale, panel, layout) {
if(scale %in% c("x", "y")) {
# depends on panel
if(identical(panel, NA)) stop("Position scale depends on panel")
layout$get_scales(panel)[[scale]]
} else {
scales$get_scales(scale)
}
}
)

# return the data
data
},

Expand Down Expand Up @@ -332,7 +350,7 @@ Layer <- ggproto("Layer", NULL,
}

data <- self$geom$handle_na(data, self$geom_params)
self$geom$draw_layer(data, self$geom_params, layout, layout$coord)
self$geom$draw_layer(data, self$geom_params, layout, layout$coord, self$layer_params)
}
)

Expand Down
79 changes: 79 additions & 0 deletions tests/testthat/test-layer.r
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,85 @@ test_that("if an aes is mapped to a function that returns NULL, it is removed",
expect_identical(names(p[[1]]), c("PANEL", "x", "group"))
})

# The layer_params object -------------------------------------------------

test_that("layers in a built plot have a layer_params object", {
df <- data_frame(x = 1:10, y = 1:10)
built <- ggplot_build(ggplot(df, aes(x, y)) + geom_point())
expect_is(built$plot$layers[[1]]$layer_params, "LayerParams")
})

test_that("the correct scales are returned from layer_params$get_scale()", {

# test Geom that displays select scale information
GeomScaleInfo <- ggproto(
"GeomScaleInfo", Geom,
required_aes = "x",

draw_layer = function(self, data, params, layout, coord, layer_params, ...) {

# list the same length as number of panels in data$PANEL
lapply(unique(data$PANEL), function(panel) {
x_limits <- layer_params$get_scale("x", panel, layout)$get_limits()
y_limits <- layer_params$get_scale("y", panel, layout)$get_limits()
col_limits <- layer_params$get_scale("colour", panel, layout)$get_limits()
text <- sprintf(
"x: %s; y: %s; col: %s",
paste(x_limits, collapse = ", "),
paste(y_limits, collapse = ", "),
paste(col_limits, collapse = ", ")
)
grid::textGrob(text)
})
}
)

geom_scale_info <- function() {
layer(
geom = GeomScaleInfo, stat = "identity", data = data_frame(x = 1), mapping = aes(x = x),
position = "identity",
params = list(), inherit.aes = FALSE, show.legend = NA
)
}

# a test plot that has some position and non-position scales, function to extract text
# from the plot
df <- data_frame(x = 1:10, y = 21:30, col = factor(c(1, 1, 1, 1, 1, 2, 2, 2, 3, 3)))
p <- ggplot(df, aes(x, y, col = col)) + geom_blank() + geom_scale_info()
limits_from_plot <- function(p) {
built <- ggplot_build(p)
panels <- seq_along(built$layout$panel_params)
vapply(panels, function(panel) layer_grob(p, 2)[[panel]]$label, character(1))
}

# expect the correct limits for single, multi-panel plots with (possibly) free scales
expect_identical(limits_from_plot(p), "x: 1, 10; y: 21, 30; col: 1, 2, 3")
expect_identical(
unique(limits_from_plot(p + facet_wrap(vars(col)))),
"x: 1, 10; y: 21, 30; col: 1, 2, 3"
)
expect_identical(
unique(limits_from_plot(p + facet_grid(vars(col)))),
"x: 1, 10; y: 21, 30; col: 1, 2, 3"
)
expect_identical(
unique(limits_from_plot(p + facet_wrap(vars(col), scales = "free"))),
c(
"x: 1, 5; y: 21, 25; col: 1, 2, 3",
"x: 1, 8; y: 26, 28; col: 1, 2, 3",
"x: 1, 10; y: 29, 30; col: 1, 2, 3"
)
)
expect_identical(
unique(limits_from_plot(p + facet_grid(vars(col), scales = "free"))),
c(
"x: 1, 10; y: 21, 25; col: 1, 2, 3",
"x: 1, 10; y: 26, 28; col: 1, 2, 3",
"x: 1, 10; y: 29, 30; col: 1, 2, 3"
)
)
})

# Data extraction ---------------------------------------------------------

test_that("layer_data returns a data.frame", {
Expand Down