eliocamp / metR

Tools for Easier Analysis of Meteorological Fields
https://eliocamp.github.io/metR/
139 stars 22 forks source link

Suggestion for `geom_vector()` key glyphs #186

Closed teunbrand closed 4 months ago

teunbrand commented 7 months ago

When using geom_vector() + scale_mag(), you get a legend displaying the magnitude. This is fine, but I can imagine maintaining guide_vector() might be a bit of a hassle (certainly given recent guide kerfuffle).

library(ggplot2)
library(metR)

ggplot(seals, aes(long, lat)) +
  geom_vector(aes(dx = delta_long, dy = delta_lat), skip = 2) +
  scale_mag()
#> Warning: The S3 guide system was deprecated in ggplot2 3.5.0.
#> ℹ It has been replaced by a ggproto system that can be extended.
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.

So the suggestion I'd like to put out there, is that you can use a gimmick in ggplot2 >3.5.0 that lets you assign size to legend glyphs. That way, you can just use regular legends and you're able to display multiple breaks if needed. As a quick and dirty demo, here is how that might work:

draw_key_vector <- function(data, params, size) {
  if (is.null(data$linetype)) {
    data$linetype <- 0
  } else {
    data$linetype[is.na(data$linetype)] <- 0
  }

  `%||%` <- function(a, b) if (is.null(a)) b else a

  grob <- grid::segmentsGrob(
    x0 = 0.1, y0 = 0.5, x1 = unit(0.1, "npc") + unit(data$mag, "cm"), y1 = 0.5,
    gp = grid::gpar(
      col = alpha(data$colour %||% data$fill %||% "black", data$alpha),
      fill = alpha(params$arrow.fill %||% data$colour %||% data$fill %||% "black", data$alpha),
      lwd = (data$linewidth %||% 0.5) * .pt,
      lty = data$linetype %||% 1,
      lineend = params$lineend %||% "butt"
    ),
    arrow = params$arrow
  )
  # Magick number is 1.25 because we like to span 80% of the width with the segment, so the
  # total width is 1 / 0.8 * size == 1.25 * size
  attr(grob, "width") <- 1.25 * data$mag # assumes cm
  grob

}

ggplot(seals, aes(long, lat)) +
    geom_vector(aes(dx = delta_long, dy = delta_lat), 
                skip = 2, key_glyph = draw_key_vector) +
    continuous_scale("mag", palette = scales::identity_pal())

Created on 2024-01-19 with reprex v2.1.0

No need to implement this of course, but as I've been rummaging around metR this idea struck me and thought I'd share.

eliocamp commented 7 months ago

Thanks! It could be a good alternative. The reason I implemented a single arrow legend is that this is how I often see it in the papers I read in atmospheric science. But other disciplines might have other conventions, so ~adding this option might be useful for them.~

Wait, no, scratch that. This implementation would also allow the user to plot only one arrow by customising the breaks, right? In that case, I should probably switch to this.

teunbrand commented 7 months ago

This implementation would also allow the user to plot only one arrow by customising the breaks, right?

Yes that's right. You control scale_mag() and you could have it just return 1 break by default. I don't know the conventions of your field, so I can't really judge in any meaningful capacity. Major caveat though is that this won't work in ggplot2 <3.5.0.

eliocamp commented 7 months ago

That's great. I will defer this to a bit later after 3.5.0 is updated, then.

PanfengZhang commented 4 months ago

That's great. I will defer this to a bit later after 3.5.0 is updated, then.

The arrow drawn is solid, but the legend is indeed hollow. How to solve this problem? Make the default legend the same as the one drawn.

eliocamp commented 4 months ago

This is now implemented! It limits some of the flexibility, but I think it's a much better fit for ggplot2.