Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Networkx (or Graphviz) rotate node labels clockwise around the center of the plot

I have a similar plot drawn with Networkx/Graphviz:

import networkx as nx
from networkx.drawing.nx_agraph import graphviz_layout

T = nx.balanced_tree(2, 5)
pos = graphviz_layout(T, prog="twopi")
nx.draw_networkx(T, pos, with_labels=True)
plt.show()

Which gives me the following plot:

Example Plot 1

What I want:

I want that all the node labels are rotated clockwise around the center of the graph. How do i do this? I would prefer a way directly with Networkx or Graphviz.

With "rotated clockwise" I mean similar like the labels on this polar plot:

Example Plot 2

I also tried with the following code:

T = nx.balanced_tree(2, 5)
pos = graphviz_layout(T, prog="twopi")
nx.draw_networkx(T, pos, with_labels=False)

text = nx.draw_networkx_labels(T, pos=pos)
for _, t in text.items():
    t.set_rotation('clockwise')
    
plt.show()

But for set_rotation() only 'horizontal', 'vertical', numeric value, or None are supported.

I also found similar questions, but non of which could help me:

Does not plot the node labels and it's a quiet old question from 2017, also it seems for me to complicated: NetworkX node labels relative position

Answer is that it is not possible with graphviz in 2019: https://stackoverflow.com/questions/55009159/change-label-orientation-90º-of-a-node-in-graphviz

like image 921
Veritas_in_Numeris Avatar asked Sep 06 '25 02:09

Veritas_in_Numeris


2 Answers

A possible workaround of the limitations listed by @sroush is removing the node labels all together and label your nodes using plt.text.

See code below:

import networkx as nx
import matplotlib.pyplot as plt

T = nx.balanced_tree(2, 5)
N_nodes=T.number_of_nodes()
fig,ax=plt.subplots(figsize=(10,5))
#Circular layout as an example
pos = nx.circular_layout(T)

#Setting up the text by using node position 
texts=[plt.text(pos[i][0],pos[i][1],str(i),rotation=(i/N_nodes)*360,fontsize=10,horizontalalignment='center',verticalalignment='center') for i in range(N_nodes)]

#Plot result
nx.draw(T, pos)
ax.set_aspect('equal')
plt.show()

And the output gives:

enter image description here

EDIT: Using graphviz_layout(T, prog="twopi") layout:

import networkx as nx
import matplotlib.pyplot as plt
from networkx.drawing.nx_agraph import graphviz_layout

T = nx.balanced_tree(2, 5)
N_nodes=T.number_of_nodes()
pos = pos = graphviz_layout(T, prog="twopi")
fig,ax=plt.subplots(figsize=(10,5))
texts=[plt.text(pos[i][0],pos[i][1],str(i),rotation=(i/N_nodes)*360,fontsize=10,horizontalalignment='center',verticalalignment='center') for i in range(N_nodes)]
nx.draw(T, pos)
ax.set_aspect('equal')
plt.show()

And the output gives:

enter image description here

EDIT 2: Fine tuning node layout:

import networkx as nx
import matplotlib.pyplot as plt
from networkx.drawing.nx_agraph import graphviz_layout

T = nx.balanced_tree(2, 5)
N_nodes=T.number_of_nodes()
first_rot_node=31
N_rot=T.number_of_nodes()-first_rot_node+1
pos = pos = graphviz_layout(T, prog="twopi")

fig,ax=plt.subplots(figsize=(10,5))
cmt=0
for i in range(N_nodes):
  if i>=first_rot_node:
    cmt+=1
    if ((cmt-1)/N_rot)*360<90 or ((cmt-1)/N_rot)*360>=270:
      plt.text(pos[i][0],pos[i][1],str(i),rotation=((cmt-1)/N_rot)*360,fontsize=11,horizontalalignment='center',verticalalignment='center') 
    elif ((cmt-1)/N_rot)*360>=90 and ((cmt-1)/N_rot)*360<270:
      plt.text(pos[i][0],pos[i][1],str(i),rotation=((cmt-1)/N_rot)*360+180,fontsize=11,horizontalalignment='center',verticalalignment='center') 
  else:
     plt.text(pos[i][0],pos[i][1],str(i),rotation=0,fontsize=11,horizontalalignment='center',verticalalignment='center') 

nx.draw(T, pos)
ax.set_aspect('equal')
plt.show()

And the output gives: enter image description here

like image 105
jylls Avatar answered Sep 10 '25 06:09

jylls


Graphviz does not support rotated text. Here is an outstanding request to provide this capability - https://gitlab.com/graphviz/graphviz/-/issues/2006.

Two possible Graphviz work-arounds:

  • Create SVG output and then postprocess the SVG to add rotation to each text string.
  • Replace the node labels with images which include rotated text.
    1. Run twopi once with dot output format. Calculate angles for each node from the embedded pos information (see https://graphviz.org/docs/attrs/pos/ and https://graphviz.org/docs/outputs/canon/).
    2. Using other tools (ImageMagick, netpbm, ...), create images for each node with rotated text.
    3. Modify the twopi input to include the images (https://graphviz.org/docs/attrs/image/).
    4. Run twopi again to produce the final output.
like image 44
sroush Avatar answered Sep 10 '25 07:09

sroush