Sennahoj
Sennahoj

Reputation: 131

How to draw a planar graph with networkx?

I'm trying to draw a planar portayal of a digraph with the python packages "matplotlib" and "networkx".

I've tried using the "networkx.planar_layout" for the node positions in the plot, but don't like the outcome.

In the following example, "graph" is a (planar) directed graph. The keys of the dictionary "graph" are the nodes. The value of a key is a list, which contains all neighbors of this node:

import networkx as nx
import matplotlib.pyplot as plt

graph = {'s1': ['v', 't1','w'],
     's2': ['t1','s1'],
     's3': ['v','w'],
     's4': ['x','y'],
     'x': ['v','w'],
     'v': ['t1', 'w'],
     'w': ['y','t1','t2'],
     'y': ['v','t1','t2'],
     't1': [],
     't2': []
     }

def main(G):
    fig = plt.figure()
    fig.show()

    graph = nx.DiGraph()

    for v in G.keys():
        graph.add_node(v)

    for delta in G.items():
        for w in delta[1]:
            graph.add_edge(delta[0],w)

    posit = nx.planar_layout(G)

    nx.draw(graph, posit , with_labels = True)
    fig.canvas.draw()


main(graph)

The image i get: enter image description here

What I don't like about it is that the nodes are lined up in a way which results in a "stack" of edges. For example, it is not possible to tell from the plot where the edge (s2,t1) is really ending, since the edges are all overlapping in this part of the Image (I don't even think this fits the definition of a planar portrayal of my graph, which is strange since the layout I used is called "planar_layout" and the graph is in fact planar).

Is there a better way to plot this?

Upvotes: 7

Views: 7014

Answers (2)

Konchog
Konchog

Reputation: 2188

The important thing is to ensure that the graph is actually planar. One way of doing this is by using a PlanarEmbedding rather than a DiGraph - while the PlanarEmbedding is a subclass of DiGraph, it ensures that the graph is planar.

import json
from random import random

import networkx as nx
import matplotlib.pyplot as plt


def draw(n : dict):
    g = nx.PlanarEmbedding()
    g.set_data(n)
    pos = nx.planar_layout(g)  # here are your positions.
    # pos = nx.spring_layout(g, pos=pos, seed=int(2**32 - 1 * random()))
    nx.draw_networkx(g, pos, with_labels=True)
    plt.show()

if __name__ == '__main__':
    j_obj = {}
    with open('planar.json', 'r') as infile:
        nodes = json.load(infile)
        infile.close()
    draw(nodes)

planar.json

{
   "s1": ["s2","t1","w", "v"],
   "s2": ["t1","s1"],
   "s3": ["w","v"],
   "s4": ["x","y"],
   "x": ["v","w","s4"],
   "v": ["s1","s3","w","x","y","t1"],
   "w": ["t1","t2","y","x","v","s3","s1"],
   "y": ["v","s4","w","t2","t1"],
   "t1": ["s2","v","y","w","s1"],
   "t2": ["y","w"]
}

planar

Upvotes: 2

Sennahoj
Sennahoj

Reputation: 131

I've found a function that could help: I'm using "nx.draw_planar" instead of "nx.draw" like this:

def main(G):
    fig = plt.figure()
    fig.show()

    graph = nx.DiGraph()

    for v in G.keys():
        graph.add_node(v)

    for delta in G.items():
        for w in delta[1]:
            graph.add_edge(delta[0],w)

    #posit = nx.shell_layout(G) #ISN'T NEEDED ANYMORE

    nx.draw_planar(graph,with_labels = True, alpha=0.8) #NEW FUNCTION
    fig.canvas.draw()


    main(graph)

I get the following result: result

The problem with this solution is that I don't get to save the node positions as in the previous version with "posit". I would like to use those later in the program, though. Does anyone know how I can get them without using a layout from networkx?

Upvotes: 6

Related Questions