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

Justifying the legend with respect to the full plot area rather than the panel #4020

Closed
LiRogers opened this issue May 22, 2020 · 17 comments
Closed
Labels
feature a feature request or enhancement guides 📏

Comments

@LiRogers
Copy link

Now that the plot titles and captions can be aligned with respect to the full plot area, it would be great if the same was true for the legend. At the moment, if the legend is positioned at the top of the plot below the title it is aligned to the plot panel, making it misaligned with the title:

library(ggplot2)

ggplot(data = diamonds, mapping = aes(x = carat, y = price, color = cut)) +
  geom_point() +
  labs(title = "A plot title") +
  theme(plot.title.position = "plot",
        legend.position = "top",
        legend.justification = "left")

Created on 2020-05-22 by the reprex package (v0.3.0)

Experimenting with negative values in legend.justification = c(x,y) can get the legend to align with the title, but the value is dependent on the size of the y-axis labels

@clauswilke clauswilke added this to the ggplot2 3.4.0 milestone May 22, 2020
@clauswilke
Copy link
Member

Yes, that's a good point. Marking this as TODO for ggplot2 3.4.

@teunbrand
Copy link
Collaborator

Possibly related, #3989 also proposes more legend alignment options

@thomasp85
Copy link
Member

Agreed that this would make sense

@malcalakovalski
Copy link

Would it be possible to do the same for plot.tag.position? If the tag position is set to "topleft" it's also misaligned with the title.

library(ggplot2)

ggplot(data = diamonds, mapping = aes(x = carat, y = price, color = cut)) +
  geom_point() +
  labs(title = "A plot title",
       tag = 'Figure 1') +
  theme(plot.title.position = "plot",
        plot.tag.position = 'topleft')

diamonds_tag

@clauswilke
Copy link
Member

clauswilke commented Aug 5, 2021

@malcalakovalski Use plot.tag.position = c(0, 1):

library(ggplot2)

ggplot(data = diamonds, mapping = aes(x = carat, y = price, color = cut)) +
  geom_point() +
  labs(
    title = "A plot title",
    tag = "Figure 1"
  ) +
  theme(
    plot.title.position = "plot",
    plot.tag.position = c(0, 1),
    plot.tag = element_text(hjust = 0, vjust = 1)
  )

Screen Shot 2021-08-04 at 7 08 59 PM

@malcalakovalski
Copy link

@clauswilke Thank you! But is there a way to do that such that the tag is above the title?

@clauswilke
Copy link
Member

You could add some top margin to the title with plot.title = element_text(margin = margin(16, 0, 0, 0)). Not sure if there's a different, more elegant approach.

@malcalakovalski
Copy link

That works for my current purposes. Thank you!

@thomasp85 thomasp85 removed this from the ggplot2 3.4.0 milestone May 18, 2022
@brunomioto
Copy link

Is there a workaround for it?

@teunbrand
Copy link
Collaborator

This should not be terribly difficult to implement, as Hadley already left the solution in his comments:

ggplot2/R/plot-build.r

Lines 232 to 234 in 0d0de37

panel_dim <- find_panel(plot_table)
# for align-to-device, use this:
# panel_dim <- summarise(plot_table$layout, t = min(t), r = max(r), b = max(b), l = min(l))

However, this brings us to a harder problem: what would the name of this argument be?

@teunbrand
Copy link
Collaborator

There are a bunch of *.position arguments like plot.title.position, plot.caption.position, plot.tag.position that all decide between aligning to the plot or to the panels. For consistency purposes it would be best to name it legend.position. However, legend.position already exists and means something different. We could do legend.position.align = "panel"/"plot" though.

I also like the strip.placement name that can be paralleled in legend.placement = "panel"/"plot". Any other thoughts?

@mfherman
Copy link

Does anyone have an idea of how to make this work now until a full solution is implemented? Not knowing much about the ggplot internals, I'm not sure I follow @teunbrand's suggestion above.

@teunbrand
Copy link
Collaborator

It was not a suggestion. The only thing that has prevented me so far from putting in a PR to fix this is that I don't know what the setting should be named.

@mfherman
Copy link

Ah, I see. legend.position.align = "panel"/"plot" makes sense to me - although there are other *.align settings that take (0, 1), so this may be slightly confusing?

teunbrand added a commit to teunbrand/ggplot2 that referenced this issue Nov 2, 2023
teunbrand added a commit that referenced this issue Dec 8, 2023
* Add legends in all positions

* Assemble separate guide boxes

* Add position argument to guides

* reoxygenate

* adapt tests

* deal with old R units

* rename manual position to "inside"

* resolve spacing once

* omit 'inside' option in justification

* Move more responsibility to `Guides$draw()`

* Propagate "manual" -> "inside" rename

* Fallback for inside position

* Rearrange methods into logical order

* remove vestigial stuff

* Separate numeric inside positioning from `legend.position` argument

* Implement plot-wise justification (#4020)

* Partially revert bd917cf

* Add extra justification theme settings

* Document `legend.justification.{position}`

* Apply justification

* Prevent FP warnings by partial matching

* Switch to new inside position

* Add test for justification per position

* Fix subsetting bug

* always add gtable rows/cols

* adjust table dimension expectations

* adapt test

* Don't calculate key sizes twice

* Use `calc_element()`

* Use conventional indexing

* prevent partial matching

* Move justification responsiblity to `Guides$package_box()`

* Fix bug

* incorporate guide_custom

* incorporate guide_custom
@teunbrand
Copy link
Collaborator

Considering this fixed by #5488.

@kylebutts
Copy link

@teunbrand I might be missing how to do this in v3.5.0. How would you modify the original example to have it start justified to the plot?

library(ggplot2)

ggplot(
  data = diamonds |> dplyr::slice_sample(prop = 0.01), 
  mapping = aes(x = carat, y = price, color = cut)
) +
  geom_point() +
  labs(title = "A plot title") +
  theme(
    plot.title.position = "plot",
    legend.position = "top",
    legend.justification = c(0, 1)
  )

@teunbrand
Copy link
Collaborator

Should be possible by adding legend.location = "plot" to the theme.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature a feature request or enhancement guides 📏
Projects
None yet
Development

No branches or pull requests

8 participants