Tropilio
Tropilio

Reputation: 1515

Force nodes positions on concentric circles in graphviz graph

I am using pygraphviz library to plot a python graph created using networkx library. Overall I am quite happy with the 'neato' layout, which produces something like this:
enter image description here

Now, my data is structured in a way that I always have a central node, then a set of nodes that are at distance 1 from the center, and then another set of nodes which are at distance 2 from the center. By "distance" I mean number of links before reaching the central node. Because of this, I would like to force the position of the distance-1 nodes on a circumference of a fixed radius from the center, let's say r=1, and the distance-2 nodes on a circumference of r=2. I attach a sketch for clarity:

enter image description here

So far, I tried approaching the problem by manually defining the position of the nodes (this is not a minimum working example, it is just to show my failed approach):

# define distance levels
levels = [0, 1, 2]

# assign a radius to each level
radius = {}
for index, level in enumerate(levels):
    radius[level] = index

# assign nodes to each level
nodes = {}
for level in levels:
    nodes[level] = [x for x,y in graph.nodes(data=True) if y['level']==level]

# calculate position of nodes on the circumferences
pos = {}
for level in levels:
    n_nodes = len(nodes[level])
    # divide pi in n_nodes parts to get the angle increment of each node
    theta = 2*np.pi/n_nodes
    n = 1
    for node in nodes[level]:
        xPos = radius[level]*np.cos(theta*n)
        yPos = radius[level]*np.sin(theta*n)
        pos[node] = (xPos, yPos)
        n += 1

The problem with this method is that the order of the nodes on the circumference is not decided by graphviz anymore, and so the graph actually looks horrible because the arrows are all superimposed and crossing each other:
enter image description here

The nice thing of using 'neato' layout is that graphviz decides where to put nodes in order to have, well, a 'neat' graph. So my question is: can I tell to graphviz to keep the nodes on the circles, but still letting it decide what is the best order of the nodes?

Upvotes: 0

Views: 1437

Answers (1)

kirogasa
kirogasa

Reputation: 949

Image:
circle layout for nodes on the graph made with graphviz twopi
and full script:

digraph {
    layout=twopi // layout engine
    overlap=false
    splines=curved
    ranksep=4
    root="3, 446" // mark center node

    "3, 446" -> "3, 439"
    "3, 446" -> "3, 502"
    "3, 446" -> "3, 2366"
    "3, 446" -> "3, 382"
    "3, 446" -> "3, 3556"
    "3, 446" -> "4, 1778"
    "3, 446" -> "3, 1214"
    "3, 446" -> "3, 445"
    "3, 446" -> "4, 758"
    "3, 439" -> "2, 253"
    "3, 439" -> "3, 446"
    "3, 439" -> "3, 3482"
    "3, 439" -> "4, 1763"
    "3, 439" -> "3, 2359"
    "3, 439" -> "3, 319"
    "2, 253" -> "3, 439"
    "2, 127" -> "3, 502"
    "3, 502" -> "2, 127"
    "3, 502" -> "3, 446"
    "3, 502" -> "3, 4004"
    "3, 502" -> "3, 508"
    "3, 502" -> "4, 998"
    "3, 502" -> "3, 3986"
    "3, 502" -> "4, 2018"
    "3, 502" -> "3, 1270"
    "3, 4004" -> "3, 502"
    "3, 4004" -> "3, 3556"
    "4, 1270" -> "4, 758"
    "4, 758" -> "3, 446"
    "4, 758" -> "4, 998"
    "4, 758" -> "4, 502"
    "4, 758" -> "4, 638"
    "4, 758" -> "4, 743"
    "4, 758" -> "4, 1270"
    "3, 508" -> "3, 502"
    "4, 2300" -> "3, 382"
    "3, 382" -> "4, 2300"
    "3, 382" -> "3, 254"
    "3, 382" -> "3, 446"
    "3, 1270" -> "3, 1214"
    "3, 1270" -> "3, 502"
    "3, 1214" -> "3, 1270"
    "3, 1214" -> "3, 446"
    "3, 1214" -> "3, 1207"
    "3, 1214" -> "3, 254"
    "3, 3482" -> "3, 439"
    "3, 3482" -> "3, 3538"
    "2, 254" -> "3, 3556"
    "3, 3556" -> "3, 4004"
    "3, 3556" -> "2, 254"
    "3, 3556" -> "4, 1660"
    "3, 3556" -> "3, 446"
    "4, 1763" -> "3, 439"
    "4, 1763" -> "4, 1778"
    "2, 191" -> "3, 2366"
    "3, 2366" -> "4, 3190"
    "3, 2366" -> "3, 2359"
    "3, 2366" -> "3, 446"
    "3, 2366" -> "2, 191"
    "4, 3190" -> "3, 2366"
    "4, 998" -> "4, 758"
    "4, 998" -> "3, 502"
    "3, 3986" -> "3, 502"
    "3, 3986" -> "3, 3538"
    "3, 2359" -> "3, 2366"
    "3, 2359" -> "3, 439"
    "4, 1660" -> "3, 3556"
    "4, 3698" -> "4, 1778"
    "4, 1778" -> "4, 2018"
    "4, 1778" -> "4, 1780"
    "4, 1778" -> "4, 1763"
    "4, 1778" -> "3, 446"
    "4, 1778" -> "4, 3698"
    "4, 2018" -> "4, 1778"
    "3, 254" -> "3, 382"
    "3, 254" -> "3, 1214"
    "4, 1780" -> "4, 1778"
    "4, 502" -> "3, 382"
    "4, 502" -> "4, 758"
    "4, 638" -> "4, 758"
    "3, 319" -> "3, 439"
    "4, 743" -> "4, 758"
    "4, 743" -> "3, 439"
    "3, 1207" -> "3, 439"
    "4, 3320" -> "3, 445"
    "3, 445" -> "3, 446"
    "3, 445" -> "4, 3320"
    "3, 445" -> "3, 443"
    "3, 443" -> "3, 3538"
    "3, 3538" -> "3, 3986"
    "3, 3538" -> "3, 3482"
    "3, 3538" -> "3, 446"
    "3, 3538" -> "3, 443"
    "4, 1777" -> "4, 1778"
}

Unfortunately, I do not know how to avoid overlapping the edges of the nodes, it seems to be a bug.

Upvotes: 0

Related Questions