thomasp85 / ggraph

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

Plotting Overlapping Node's Text At the same Z-height as Point #342

Closed JZL closed 10 months ago

JZL commented 1 year ago

Hi

Great package, thanks for writing it!

This is somewhere between a feature request and pointers to anyone else with the same problem. I was plotting a graph where most of the nodes are non-overlapping, but some are in a jumble. I did the typical (as far as I understand, canonical)

geom_node_point()+
geom_node_text() + ...

The problem is that it first layers the nodes, then layers the points, so the text is all in a jumble on top of the points. In other words, if two nodes are partially occluding, the nodes correctly stack but the two labels are on top of eachother and look weird and overlapping, ruining the illusion of the text being pasted on top of the nodes

I think it would be nice to have a geom_node_point_and_text which combines both and layers (point_1 + text_1), (point_2+text_2) instead of (point_1 + point_2), (text_1 + text_2). It could be a pain to get all the mapping's right for this meta function, but it could be a simple as taking in a list of lists of paramaters, and then passing each half to one function in the grob generator. Or just assume the mapping will be used the same in each

Another possibility is to render the graph and then reshuffle/zip the list of point grobs and text grobs together, although I didn't get that to work

What I came to, was to use create_layout to make the data value, and then generate one geom_node_point and geom_text_point function for each row with a data= subset (inefficient but fast enough for me), before putting the whole mess together so they're properly interleaved. For anyone else, here's the sketch code but it's not ideal

gfromm_layout = create_layout(gfromm)

allPoints = map(1:nrow(gfromm_layout), function(subset_VI){

    list(
      geom_node_point(
        data=gfromm_layout[subset_VI,,drop=F],
        mapping = aes(fill = color,
                      x=x, y=y)
      ),
      geom_node_text(
        data=gfromm_layout[subset_VI,,drop=F],
        mapping=aes(label=name,
                    color=textColor,
                    x=x, y=y
        ))
    )

})

p = ggplot()+
  purrr::flatten(allA)+
  theme_graph()
thomasp85 commented 10 months ago

I think if you have a graphic with overplotting of text, the right course of action is to remove some of the text. If you set check_overlap = TRUE in geom_node_text() anything but the top label will not be plotted if they overlap. I'm unlikely to add a geom that entangles the two as it will lead to very bad performance (due to internal grid reasons).

ggplot2 and thus ggraph is extensible though, so anyone can create a geom that do that if they wish