ggraph icon indicating copy to clipboard operation
ggraph copied to clipboard

Support for outlined node text (shadowtext)

Open pbreheny opened this issue 2 years ago • 6 comments

First of all, thank you for the excellent work on this package! Enhancement suggestion here, which is to allow users access to the shadowtext package, either as an option to geom_node_text() (as with repel) or as a separate function. It makes a huge difference in readability. Compare these two figures:

Code is trivial, just replace the existing geom with geom=shadowtext::GeomShadowText, which is how I made the above figure.

pbreheny avatar Jul 01 '22 14:07 pbreheny

I like this! You can actually simply use geom_shadowtext() since the layout coordinates of nodes are stored in a "hidden" data.frame.

library(igraph)
library(ggraph)

g <- sample_gnp(50, 0.7)
V(g)$name <- sapply(1:50, function(x) paste0(sample(LETTERS, 4), collapse = ""))
E(g)$weight <- runif(ecount(g))

ggraph(g,"stress") +
  geom_edge_link0(aes(edge_color = weight, edge_width = weight), show.legend = FALSE) +
  geom_node_point(size = 8, color = "#44a6c6") +
  # geom_node_text(aes(label=name),fontface="bold")+
  shadowtext::geom_shadowtext(aes(x, y, label = name), color = "black", size = 4, bg.colour = "white") +
  scale_edge_color_continuous(low = "grey66", high = "black") +
  scale_edge_width(range = c(0.1, 0.5)) +
  theme_graph() +
  coord_fixed()

Created on 2022-07-05 by the reprex package (v2.0.1)

schochastics avatar Jul 05 '22 12:07 schochastics

Oh that's very cool...I didn't know it was possible to do this sort of thing with hidden data frames and ggplot.

pbreheny avatar Jul 05 '22 15:07 pbreheny

By the way, one limitation of the above workaround is that it doesn't work with filter:

library(igraph)
library(ggraph)

g <- sample_gnp(50, 0.7)
V(g)$name <- sapply(1:50, function(x) paste0(sample(LETTERS, 4), collapse = ""))
V(g)$centrality <- eigen_centrality(g)$vector
E(g)$weight <- runif(ecount(g))

ggraph(g,"stress") +
  geom_edge_link0(aes(edge_color = weight, edge_width = weight), show.legend = FALSE) +
  geom_node_point(size = 8, color = "#44a6c6") +
  # geom_node_text(aes(label=name),fontface="bold")+
  shadowtext::geom_shadowtext(aes(x, y, label = name, filter = centrality > 0.8), color = "black", size = 4, bg.colour = "white") +
  scale_edge_color_continuous(low = "grey66", high = "black") +
  scale_edge_width(range = c(0.1, 0.5)) +
  theme_graph() +
  coord_fixed()

produces

Warning message:
Ignoring unknown aesthetics: filter 

and the figure looks the same (all nodes are labeled; no filter is applied).

pbreheny avatar Aug 05 '22 20:08 pbreheny

filters do not exist for regular ggplot geoms. You need to do some more hacking to get this to work. I think it is best to create a new variable name_filtered which is the empty string if the filter is not fulfiled

library(igraph)
library(ggraph)

g <- sample_gnp(50, 0.7)
V(g)$name <- sapply(1:50, function(x) paste0(sample(LETTERS, 4), collapse = ""))
V(g)$centrality <- eigen_centrality(g)$vector
V(g)$name_filtered <- ifelse(V(g)$centrality>0.8,V(g)$name,"")
E(g)$weight <- runif(ecount(g))

ggraph(g,"stress") +
  geom_edge_link0(aes(edge_color = weight, edge_width = weight), show.legend = FALSE) +
  geom_node_point(size = 8, color = "#44a6c6") +
  shadowtext::geom_shadowtext(aes(x, y, label = name_filtered), 
                              color = "black", size = 4, bg.colour = "white") +
  scale_edge_color_continuous(low = "grey66", high = "black") +
  scale_edge_width(range = c(0.1, 0.5)) +
  theme_graph() +
  coord_fixed()

schochastics avatar Aug 06 '22 06:08 schochastics

Actually, I just learned that geom_text_repel() accepts arguments bg.r and bg.color, allowing it to replicate the functionality of shadowtext. So, the following works -- note that I'm specifying repel=TRUE so that ggraph() uses geom_text_repel(), but setting force=0 so that it doesn't actually do any repelling.

ggraph(g,"stress") +
  geom_edge_link0(aes(edge_color = weight, edge_width = weight), show.legend = FALSE) +
  geom_node_point(size = 8, color = "#44a6c6") +
  geom_node_text(aes(label=name, filter = centrality > 0.8), repel=TRUE, bg.color="white", bg.r=0.15, force=0) +
  scale_edge_color_continuous(low = "grey66", high = "black") +
  scale_edge_width(range = c(0.1, 0.5)) +
  theme_graph() +
  coord_fixed()

I guess with this, we don't really need a modification to the package itself to accomplish what I originally wanted. This issue could be closed, although I'll let you do that (maybe you want to add an example like this to the documentation or something?).

pbreheny avatar Aug 08 '22 20:08 pbreheny

Didn't know this either and this seems like an elegant enough solution for this problem

schochastics avatar Aug 09 '22 05:08 schochastics