首页 > 解决方案 > 如何在ggplot中找到geom_curve控制点

问题描述

我想知道如何在ggplot2中找到geom_curve控制点?例如

p <- ggplot(mtcars, aes(wt, mpg)) + 
geom_curve(aes(x = 2.62, y = 21.0, xend = 3.57, yend = 15.0),curvature = -0.2, data = df) + 
geom_point()

b <- ggplot_build(p)
b$data[[1]]
p$layers[[1]]$geom_params

b$data[[1]]给出起点终点
p$layers[[1]]$geom_params给出曲线信息(角度,曲率,...)。
但是我怎样才能找到控制点,从而重现图形呢?

标签: rggplot2

解决方案


library(ggplot)
library(grid)
library(stringr)

df <- data.frame(x = 1:3, y = 1:3)
df2 <- data.frame(x = c(1,3), y = c(1,3),
              xend = c(2,2), yend = c(2,2))
g <- ggplot(df, aes(x, y)) +
  geom_point() +
  geom_curve(aes(x = x ,y = y,
                 xend = xend, yend = yend),
             data = df2,
             color = c("red", "blue"))
g

getCurve_controlPoints <- function(ggplotObject) {
  len_layers <- length(ggplotObject$layers)
  layerNames <- lapply(seq_len(len_layers),
                       function(j) {
                         className <- class(ggplotObject$layers[[j]]$geom)
                         className[-which(className %in% c("ggproto"  ,"gg", "Geom"))] 
                       })
  curveLayerId <- which(sapply(layerNames,
                               function(layerName){
                                 "GeomCurve" %in% layerName
                               }) == TRUE
  )
  gg_build <- ggplot_build(ggplotObject)
  # you can also add yes or no in your code
  # answer <- utils::menu(c("y", "n"), title="Do you want to draw the ggplot?")
  grid.draw(ggplotObject)
  grid.force()
  gridList <- grid.ls(print = FALSE)
  gridList.name <- gridList$name
  xspline.name <- gridList.name[which(str_detect(gridList.name, "curve") == TRUE)]
  xspline.len <- length(xspline.name)

  controlPoints <- lapply(seq_len(length(curveLayerId)),
                          function (j) {
                            # curve data
                            curve_data <- gg_build$data[[curveLayerId[j]]]
                            # avoid duplicated rows
                            curve_data <- curve_data[!duplicated(curve_data), ]
                            n <- dim(curve_data)[1]
                            # here we go! But wait a second, it seems like the starting and ending position do not match
                            xsplinePoints <- xsplinePoints(grid.get("xspline", grep=TRUE))  
                            # mapping data to our coordinates
                            control_data <- lapply(seq_len(n),
                                                   function(i){
                                                     if (n == 1) {
                                                       xy <- lapply(xsplinePoints,
                                                                    function(coord){
                                                                      as.numeric(coord) 
                                                                    })
                                                     } else {
                                                       xy <- lapply(xsplinePoints[[i]],
                                                                    function(coord){
                                                                      as.numeric(coord)
                                                                    })
                                                     }
                                                     x.start <- curve_data[i, ]$x
                                                     x.end <- curve_data[i, ]$xend
                                                     y.start <- curve_data[i, ]$y
                                                     y.end <- curve_data[i, ]$yend
                                                     # mapping to ggplot coordinates
                                                     xy_x.diff <- xy$x[length(xy$x)] - xy$x[1] 
                                                     xy_y.diff <- xy$y[length(xy$y)] - xy$y[1]
                                                     # maybe there is a better way?
                                                    if(xy_x.diff == 0){
                                                       xy_x.diff <- 1e-16
                                                     }
                                                     if(xy_y.diff == 0){
                                                        xy_y.diff <- 1e-16
                                                     }
                                                     x <- (x.end - x.start) / (xy_x.diff) * (xy$x - xy$x[1]) + x.start
                                                     y <- (y.end - y.start) / (xy_y.diff) * (xy$y - xy$y[1]) + y.start
                                                     list(x = x, y = y)
                                                   })
                            # grid remove
                            grid.remove(xspline.name[j], redraw = FALSE)
                            control_data
                          })

  controlPoints
}
controlPoints <- getCurve_controlPoints(g)
# check the points
plot(controlPoints[[1]][[1]]$x, controlPoints[[1]][[1]]$y, 
     xlim = c(1,3), 
     ylim = c(1,3),
     xlab = "x", 
     ylab = "y", 
     pch = 19)
points(controlPoints[[1]][[2]]$x, controlPoints[[1]][[2]]$y, pch = 19)

我认为到目前为止效果很好,我使用的 ggplot 版本是3.0.0。如果您使用低于2.2.1的任何版本,可能会出现错误。

这个想法是由Paul Murrell教授提出的。也许是捕获控制点的最简单方法geom_curve?缺点是必须首先绘制grobs( ggplotobject),因为这些点仅在绘制时生成。


推荐阅读