简体   繁体   中英

How to make segments that preserve angles in different aspect ratios in ggplot2?

I want to plot wind magnitude and direction as vectors. The problem is that the usual geoms recommended for vectors ( geom_segment() or geom_spoke() ) change angle if the axes have different ratios or are of different type (for example, x = date, y = height). My current workaround is using geom_text(label = "→", ...) per this answer .

The problem is that this solution is slow and the size of the text based arrow is not well mapped to the magnitude of the wind. Also, importantly, for some reason it fails when exporting to PDF. I want a solution based on proper segments. Working within the geom call, I know I can work with the native coordinates of the plot to draw proper segments even if one axis is of different class than the other. My current attempt is this:


library(ggplot2)

geom_arrow2 <- function(mapping = NULL, data = NULL, stat = "identity",
                              position = "identity", na.rm = FALSE, show.legend = NA,
                              inherit.aes = TRUE, scale = 1,...) {
    layer(geom = GeomArrow2,
          mapping = mapping,
          data = data,
          stat = stat,
          position = position,
          show.legend = show.legend,
          inherit.aes = inherit.aes,
          params = list(na.rm = na.rm, scale = scale, ...)
    )
}
GeomArrow2 <- ggplot2::ggproto("GeomArrow2", Geom,
   required_aes = c("x", "y", "mag", "angle"),
   default_aes = ggplot2::aes(color = "black", scale = 1),
   draw_key = draw_key_polygon,
   draw_panel = function(data, panel_scales, coord, scale = 1) {
       coords <- coord$transform(data, panel_scales)

       Mmag <- max(coords$mag)
       coords$mag <- with(coords, mag/Mmag*coords$scale)

       coords$dx <- with(coords, cos(angle)*mag)*scale
       coords$dy <- with(coords, sin(angle)*mag)*scale

       pol <- grid::polylineGrob(x = c(coords$x,
                                        coords$x + coords$dx),
                                  y = c(coords$y,
                                        coords$y + coords$dy),
                                  default.units = "npc",
                                  arrow = grid::arrow(angle = 15, length = unit(0.5, "lines")),
                                  gp = grid::gpar(col = coords$colour),
                                  id = rep(seq(nrow(coords)), 2))
       pol

   })


field <- expand.grid(x = seq.Date(as.Date("2017-01-01"), as.Date("2017-01-31"), "2 days"),
                     y = 1:10)
field$u <- rnorm(nrow(field))
field$v <- rnorm(nrow(field))
field$V <- with(field, sqrt(u^2 + v^2))
field$dir <- with(field, atan2(v, u))*180/pi

ggplot(field, aes(x, y)) +
    geom_point() +
    geom_arrow2(aes(mag = V, angle = dir), scale = 0.5)

But now the problem is back and different aspect ratios distort the actual angle of the arrows. I've been trying different things and found out that using "snpc" units instead of "npc" preserves angles, but the position is shifted, so that's no good.

If I could get information of the aspect ratio of the plot inside the geom call that would be great.

Any help will be very appreciated.

you can mix units,

xx <- unit.c(unit(coords$x, "npc"), 
             unit(coords$x, "npc") + unit(coords$dx, "snpc"))
yy <- unit.c(unit(coords$y, "npc"), 
             unit(coords$y, "npc") + unit(coords$dy, "snpc"))
pol <- grid::polylineGrob(x = xx, y = yy,
                          default.units = "npc",
                          arrow = grid::arrow(angle = 15, length = unit(0.5, "lines")),
                          gp = grid::gpar(col = coords$colour),
                          id = rep(seq(nrow(coords)), 2))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM