ggplot2
ggplot2 copied to clipboard
WIP: Pass actual data to annotation_raster() and annotation_custom() instead of dummy data
Fix #3120
This PR does fix these two annotations to have xmin
, xmax
, ymin
and ymax
as data
so that scale transformations takes effect (this is the same strategy as annotate()
).
-
annotation_raster()
-
annotation_custom()
On the other hand, this PR doesn't modify annotation_map()
and annotation_logstick()
because:
-
annotation_map()
: we don't know what to do with the reversed scales for map yet (c.f. scale transformations won't work forcoord_sf()
orcoord_map()
). Besides, annotation itself should not be transformed, but I don't know how to implement this for maps. -
annotation_logstick()
: the document says "These tick marks probably make sense only for base 10"
library(ggplot2)
library(patchwork)
data <- data.frame(x = c(20, 85, 42, 78, 33, 74),
y = c(43, 40, 52, 56, 44, 71))
p <- ggplot(data) +
geom_point(aes(x = x, y = y)) +
annotate(geom = "rect", xmin = 8, xmax = 12, ymin = 38, ymax = 42) +
annotation_custom(
grob = grid::circleGrob(
r = grid::unit(1, "npc"),
gp = grid::gpar(col = "black",
fill = "white",
lwd = 2)),
xmin = 48, xmax = 52, ymin = 48, ymax = 52
)
p * (p + scale_x_reverse())
rainbow <- matrix(hcl(seq(0, 360, length.out = 50 * 50), 80, 70), nrow = 50)
p <- ggplot(mtcars, aes(mpg, wt)) +
geom_point() +
annotation_raster(rainbow, 15, 20, 3, 4)
p * (p + scale_x_reverse())
Created on 2019-02-14 by the reprex package (v0.2.1)
Yes, this is what I mean. Maybe add a regression test that checks for correct behavior under scale_x_reverse()
. Can this be done without adding a visual test, just by checking the layer_data()
?
Thanks, I'll need to modify the expectations here...
https://github.com/tidyverse/ggplot2/blob/18be30e1515b2b9d05ade4f3f2b5051aa1336fcf/tests/testthat/test-annotate.r#L33-L51
I feel
-
annotation_custom()
,anotation_raster()
, andannotation_map()
should be modified to have data. -
annotation_logstick()
can be kept as is.
Can you add a test for making sure that annotations doesn't affect the scales of the plot?
Thanks, let me think how to test it...
@thomasp85 Sorry, I didn't get your point. What do you mean by "annotations doesn't affect the scales of the plot"? I think annotations does affect.
library(ggplot2)
library(patchwork)
p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point()
p1 <- p + annotate("text", x = 4, y = 25, label = "Some text")
p2 <- p + annotate("text", x = 100, y = 100, label = "Some text")
p1 * p2
Created on 2019-02-19 by the reprex package (v0.2.1.9000)
Opps, I got it. That's the very reason we needed different annotations than annotate()
. Sorry, I didn't understand this.
https://github.com/tidyverse/ggplot2/blob/7bafc45d4db081341f3fcca61932d0efd3a2fd43/R/annotation-custom.r#L6-L10
So, by their definitions ("the grob will not be modified by any ggplot settings or mappings"), is this PR invalid? It sounds user's responsibility to provide the correct coordinates.
I think the intent of the PR is good. annotations should respond to scale transformations, but they should not affect the scales in any way (at least that's my feeling, @Hadley can you chime in?)
The only way they can respond to transformations is to have the positional data in the actual layer data, so I'm beginning to think that the best way would be to exempt annotation layers from the scale training somehow
Sure. One possible choice is to transform data in draw_layer()
because there are scales in layout
and data
with xmin
, xmax
, ymin
, and ymax
, mapped by Layer$compute_geom_2()
.
https://github.com/yutannihilation/ggplot2/commit/092f39f7e83280540e271fdd112e0347fff605da
But, it seems I need to wait for #3116 to get some conclusion?
I don't remember; but if the docs say annotations don't affect the scale limits, then we need to stick with that. My vague recollection is that this makes it possible to add annotations to free scales: e.g. imagining labelling cities on spatial data where each panel has different limits.
I agree that we should keep the behaviour in line with the documentation, but do you think we should let it respond to scale transformations? (I do)
Oh yes, definitely. It should respond but not affect.
Thanks, then I'll keep trying this.
I think we have two possible directions:
- Make
xmin
,xmax
,ymin
andymax
to aesthetics and add some option toLayer
(or somewhere) to choose not to use the layer data for training scales. (This is @thomasp85 suggests). - Keep them as parameters and transform them using the trained scale in
draw_layer()
.
The latter one is related to the discussion in #3116. Though this is possible with the status quo as this only needs x and y scales, I feel I should wait for the discussion so I can implement this cleaner.
I agree. The second approach seems cleaner. It would also fit in with another thought I've always had about annotations: You really want to be able to access either the data coordinate system or the plot coordinate system, depending on the context. Sometimes you want to place an annotation at a specific data location. Other times, you want to place it in a specific location on the canvas, e.g. 10% away from the top right corner. The second approach would allow for both, if you transform or not depending on some switch that you add to the annotation geoms.
You really want to be able to access either the data coordinate system or the plot coordinate system, depending on the context.
Oh, I haven't come up with this, but this is quite true. Thanks.
Ideally you'd also want to be able to use units in the second scenario.
Maybe worth considering is making annotation_custom
a special case of a geom_custom
, like most (all?) other annotations. There's an (ugly) attempt in egg
, which I guess also serves to illustrate the need for mixed native+relative units (here there is no way to specify the width of the grob in data space),
library(ggplot2)
library(egg)
data <- data.frame(x = c(20, 85, 42, 78, 33, 74),
y = c(43, 40, 52, 56, 44, 71))
dummy <- data.frame(x=50, y = 50)
dummy$grob <- list(grid::circleGrob(
r = grid::unit(2, "mm"),
gp = grid::gpar(col = "black",
fill = "white",
lwd = 2)))
p <- ggplot(data) +
geom_point(aes(x = x, y = y)) +
annotate(geom = "rect", xmin = 8, xmax = 12, ymin = 38, ymax = 42) +
geom_custom(data = dummy, aes(x, y, data=grob), grob_fun = identity)
p + scale_x_reverse()
@yutannihilation are you awaiting #3175 for finishing this off? if so, I'll not add it to the 3.2.0 milestone
Yes, I think I need to wait for it. Sorry I wasn't clear about the current status here.
No worries... will push this to after the next release
Any updates, please? The fix doesn't seem to work for me.
I suspect this might be fixable by using the position scales nested in panel_params
without the need for #3175 to be implemented. Should we continue to wait for this?