thomasp85 / ggraph

Grammar of Graph Graphics
https://ggraph.data-imaginist.com
Other
1.08k stars 116 forks source link

ggraph: make filled arrow edges appear in legend #314

Closed friendly closed 2 years ago

friendly commented 2 years ago

I filed a query on SO, https://stackoverflow.com/questions/72395410/ggraph-make-filled-arrow-edges-appear-in-legend regarding how to make filled arrow heads appear as filled in the legend, but have had no useful answer.

Can someone here help?

This is the figure I'm trying to create, but with filled arrow heads in the legend

EDA-ggraph

friendly commented 2 years ago

There was another post on SO, https://stackoverflow.com/questions/57193353/show-filled-arrow-in-legend-ggplot which suggested this function to fill arrow heads in the legend,

# legend drawing function, copied from ggplot2
draw_key_segment_custom <- function(data, params, size) {
  if (is.null(data$linetype)) {
    data$linetype <- 0
  } else {
    data$linetype[is.na(data$linetype)] <- 0
  }

  segmentsGrob(0.1, 0.5, 0.9, 0.5,
               gp = gpar(
                 col = alpha(data$colour %||% data$fill %||% "black", data$alpha),
                 # the following line was added relative to the ggplot2 code
                 fill = alpha(data$colour %||% data$fill %||% "black", data$alpha),
                 lwd = (data$size %||% 0.5) * .pt,
                 lty = data$linetype %||% 1,
                 lineend = "butt"
               ),
               arrow = params$arrow
  )
}

It works in the example there, which calls geom_segment().

# sample data
dat <- data.frame(
  x = as.factor(1:10),
  y = c(20,30,13,37,12,50,31,2,40,30),
  z = rep('a', 10)
)

# basic plot
ggplot(dat) +
  geom_segment(
    aes(x = x, xend = x, y = 0, yend = y+15, linetype = z), 
    arrow = arrow(length = unit(0.25, 'cm'), type = 'closed'),
    size = 0.7,
    key_glyph = "segment_custom"
  ) 

I tried this, with my example, which uses geom_edge_link(), but I gen an error: Error in check.length("col") : 'gpar' element 'col' must not be length 0. Any idea how I can make this work?

ggraph(EDA_graph, layout="kk") + 
  geom_edge_link(aes(color=Institution, fill=Institution),
                 arrow = grid::arrow(type = "closed", 
                                     angle=15, 
                                     length = unit(0.15, "inches")),
                 key_glyph = "segment_custom"        # override legend
                ) + 
  geom_node_point() +
  geom_node_text(aes(label = name), repel = TRUE) +
  ggtitle("Specimen of a Chart of Geneaology of EDA") + 
  theme_graph() +
  theme(legend.position = 'bottom') 
friendly commented 2 years ago

Solved!

The solution is to modify GeomEdgePath using a fill parameter

draw_key_custom = function(data, params, size) {
  segmentsGrob(0.1, 0.5, 0.9, 0.5,
               gp = gpar(
                 col = alpha(data$edge_colour, data$edge_alpha),
                 fill = alpha(data$edge_colour, data$edge_alpha),  # <- add fill to arrow head!
                 lwd = data$edge_width * .pt,
                 lty = data$edge_linetype, 
                 lineend = 'butt'
               ),
               arrow = params$arrow
  )
}

If necessary, I can fork the project and issue a PR, but it is a one line addition to the code.