如何告诉ggplot2使用用户创建的比例来获得新的美学。

时间:2018-04-21 23:35:35

标签: r ggplot2

我正在编写一个扩展ggplot2的包。其中一个扩展是geom_arrow(),它使美学magangle按幅度和方向绘制矢量字段。我还创建了一个scale_mag()来操纵箭头的长度,同时创建一个新的指南。现在,当加在一起时,geom和scale都按预期工作。

ggplot(geo, aes(lon, lat)) +
    geom_arrow(aes(mag = mag, angle = angle)) +
    scale_mag()

但如果我不添加scale_mag(),它根本不起作用。我想要的是这个比例像scale_color()一样工作,默认情况下,当color美学存在时添加。

以下是现在的代码:

geom_arrow <- function(mapping = NULL, data = NULL,
                       stat = "arrow",
                       position = "identity", ...,
                       start = 0,
                       direction = 1,
                       # scale = 1,
                       min.mag = 0,
                       skip = 0,
                       skip.x = skip,
                       skip.y = skip,
                       arrow.angle = 15,
                       arrow.length = 0.5,
                       arrow.ends = "last",
                       arrow.type = "closed",
                       arrow = grid::arrow(arrow.angle, unit(arrow.length, "lines"),
                                           ends = arrow.ends, type = arrow.type),
                       lineend = "butt",
                       na.rm = FALSE,
                       show.legend = NA,
                       inherit.aes = TRUE) {
    layer(geom = GeomArrow,
          mapping = mapping,
          data = data,
          stat = stat,
          position = position,
          show.legend = show.legend,
          inherit.aes = inherit.aes,
          params = list(
              start = start,
              direction = direction,
              arrow = arrow,
              lineend = lineend,
              na.rm = na.rm,
              # scale = scale,
              skip.x = skip.x,
              skip.y = skip.y,
              min.mag = min.mag,
              ...)
    )
}

GeomArrow <- ggplot2::ggproto("GeomArrow", Geom,
  required_aes = c("x", "y"),
  default_aes = ggplot2::aes(color = "black", size = 0.5, min.mag = 0,
                             linetype = 1, alpha = NA),
  draw_key = ggplot2::draw_key_path,
  draw_panel = function(data, panel_scales, coord,
                        arrow = arrow, lineend = lineend,
                        start = start, direction = direction,
                        preserve.dir = TRUE) {
      coords <- coord$transform(data, panel_scales)
      unit.delta <- "snpc"
      if (preserve.dir == FALSE) {
          coords$angle <- with(coords, atan2(yend - y, xend - x)*180/pi)
          unit.delta <- "npc"
      }

      coords$dx <- with(coords, mag*cos(angle*pi/180))
      coords$dy <- with(coords, mag*sin(angle*pi/180))

      # from https://stackoverflow.com/questions/47814998/how-to-make-segments-that-preserve-angles-in-different-aspect-ratios-in-ggplot2
      xx <- grid::unit.c(grid::unit(coords$x, "npc"),
                         grid::unit(coords$x, "npc") + grid::unit(coords$dx, unit.delta))
      yy <- grid::unit.c(grid::unit(coords$y, "npc"),
                         grid::unit(coords$y, "npc") + grid::unit(coords$dy, unit.delta))


      mag <- with(coords, mag/max(mag, na.rm = T))
      arrow$length <- unit(as.numeric(arrow$length)*mag, attr(arrow$length, "unit"))

      pol <- grid::polylineGrob(x = xx, y = yy,
                                default.units = "npc",
                                arrow = arrow,
                                gp = grid::gpar(col = coords$colour,
                                                fill = scales::alpha(coords$colour, coords$alpha),
                                                alpha = ifelse(is.na(coords$alpha), 1, coords$alpha),
                                                lwd = coords$size*.pt,
                                                lty = coords$linetype,
                                                lineend = lineend),
                                id = rep(seq(nrow(coords)), 2))
      pol
  })


StatArrow <- ggplot2::ggproto("StatArrow", ggplot2::Stat,
    required_aes = c("x", "y"),
    default_aes = ggplot2::aes(min.mag = 0, dx = NULL, dy = NULL,
                               mag = NULL, angle = NULL),
    compute_group = function(data, scales,
                             skip.x = skip.x, skip.y = skip.y,
                             min.mag = min.mag) {
        min.mag <- data$min.mag %||% min.mag

        if (is.null(data$mag) | is.null(data$angle)) {
            if (is.null(data$dx) | is.null(data$dy)) stop("stat_arrow need dx, dy or mag angle (improve mesage!!)")
            data$mag <- with(data, Mag(dx, dy))
            data$angle <- with(data, atan2(dy, dx)*180/pi)
        } else {
            data$dx <- with(data, mag*cos(angle*pi/180))
            data$dy <- with(data, mag*sin(angle*pi/180))
        }

        data <- subset(data, x %in% JumpBy(unique(x), skip.x + 1) &
                             y %in% JumpBy(unique(y), skip.y + 1) &
                             mag >= min.mag)

        data$xend = with(data, x + dx)
        data$yend = with(data, y + dy)
        data

    }
)

scale_mag <- function(length = 0.1,
                      max = waiver(),
                      default_unit = "lines") {
    # if (!is.unit(length)) length <- ggplot2::unit(length, default_unit)

    continuous_scale("mag",
                     "mag",
                     identity,
                     rescaler = rescale_mag(length, max),
                     guide = "none")
}

# scale_type.mag <- function(x) "vector"

rescale_mag <- function(length, max) {
    function(x, from) {
        if (is.waive(max)) max <- max(x, na.rm = T)
        scales::rescale(x, c(0, length), c(0, max))
    }
}

3 个答案:

答案 0 :(得分:1)

我通过重载ggplot函数为工作包添加了一个默认主题ggplot,基本上是这样的:

ggplot <- function(...) {ggplot2::ggplot(...) + your_added_thing()}

如果您希望它不那么突兀,请重命名您的ggplot版本:

jjplot <- function (...) {ggplot2::ggplot(...) + my_added_thing()} 

答案 1 :(得分:0)

此页面将对您有所帮助。

https://gist.github.com/wch/3250485

尤其是以下代码:

#This tells ggplot2 what scale to look for, for yearmon

scale_type.yearmon <- function(x) "yearmon"

答案 2 :(得分:0)

最后,我找到了答案!

基于ggplot2 / R / scale-type.R中的代码,在scale_mag_continuous函数的父环境中应该有一个名为find_scale的比例尺。然后,可以自动找到该比例尺。

geo <- tibble(lon = 1:10, lat = 1:10, mag = 1:10, angle = 1:10)

scale_mag_continuous <- scale_mag

ggplot(geo, aes(lon, lat)) +
    geom_arrow(aes(mag = mag, angle = angle))