1
votes

I have generated a 3D graph network using the following code and Mayavi has been used for visualization.

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import networkx as nx
from mayavi import mlab


pos = [[0.1, 2, 0.3], [40, 0.5, -10],
       [0.1, -40, 0.3], [-49, 0.1, 2],
       [10.3, 0.3, 0.4], [-109, 0.3, 0.4]]
pos = pd.DataFrame(pos, columns=['x', 'y', 'z'])

ed_ls = [(x, y) for x, y in zip(range(0, 5), range(1, 6))]

G = nx.Graph()
G.add_edges_from(ed_ls)

nx.draw(G)
plt.show()


# plot 3D in mayavi
edge_size = 0.2
edge_color = (0.8, 0.8, 0.8)
bgcolor = (0, 0, 0)


mlab.figure(1, bgcolor=bgcolor)
mlab.clf()

for i, e in enumerate(G.edges()):
    # ----------------------------------------------------------------------------
    # the x,y, and z co-ordinates are here
    pts = mlab.points3d(pos['x'], pos['y'], pos['z'],
                        scale_mode='none',
                        scale_factor=1)
    # ----------------------------------------------------------------------------
    pts.mlab_source.dataset.lines = np.array(G.edges())
    tube = mlab.pipeline.tube(pts, tube_radius=edge_size)

    mlab.pipeline.surface(tube, color=edge_color)

mlab.show()

I would like to ask for suggestions on how to save this 3D graph in VTK format/ how to convert Networkx graph object to a VTK file for visualization in Paraview.

EDIT: I've tried to adapt the code available here for the input Networkx graph shared above. However, I am not able to obtain the output. I just get an empty window and the vtkpolyData is not plotted in the window.

"""
This code converts netwrokx graph to vtk polyData
ref: https://networkx.github.io/documentation/networkx-0.37/networkx.drawing.nx_vtk-pysrc.html
"""

import vtk
import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt

from vtk.util.colors import banana, plum


def draw_nxvtk(G, node_pos):
    """
    Draw networkx graph in 3d with nodes at node_pos.

    See layout.py for functions that compute node positions.

    node_pos is a dictionary keyed by vertex with a three-tuple
    of x-y positions as the value.

    The node color is plum.
    The edge color is banana.

    All the nodes are the same size.

    """
    # set node positions
    np={}
    for n in G.nodes():
       try:
           np[n]=node_pos[n]
       except nx.NetworkXError:
           print("node %s doesn't have position"%n)

    nodePoints = vtk.vtkPoints()

    i=0
    for (x,y,z) in np.values():
       nodePoints.InsertPoint(i, x, y, z)
       i=i+1

    # Create a polydata to be glyphed.
    inputData = vtk.vtkPolyData()
    inputData.SetPoints(nodePoints)

    # Use sphere as glyph source.
    balls = vtk.vtkSphereSource()
    balls.SetRadius(.05)
    balls.SetPhiResolution(20)
    balls.SetThetaResolution(20)

    glyphPoints = vtk.vtkGlyph3D()
    glyphPoints.SetInputData(inputData)
    glyphPoints.SetSourceData(balls.GetOutput())

    glyphMapper = vtk.vtkPolyDataMapper()
    glyphMapper.SetInputData(glyphPoints.GetOutput())

    glyph = vtk.vtkActor()
    glyph.SetMapper(glyphMapper)
    glyph.GetProperty().SetDiffuseColor(plum)
    glyph.GetProperty().SetSpecular(.3)
    glyph.GetProperty().SetSpecularPower(30)

    # Generate the polyline for the spline.
    points = vtk.vtkPoints()
    edgeData = vtk.vtkPolyData()

    # Edges

    lines = vtk.vtkCellArray()
    i = 0
    for e in G.edges():
        # The edge e can be a 2-tuple (Graph) or a 3-tuple (Xgraph)
        u = e[0]
        v = e[1]
        if v in node_pos and u in node_pos:
            lines.InsertNextCell(2)
            for n in (u, v):
                (x, y, z) = node_pos[n]
                points.InsertPoint(i, x, y, z)
                lines.InsertCellPoint(i)
                i = i+1

    edgeData.SetPoints(points)
    edgeData.SetLines(lines)

    # Add thickness to the resulting line.
    Tubes = vtk.vtkTubeFilter()
    Tubes.SetNumberOfSides(16)
    Tubes.SetInputData(edgeData)
    Tubes.SetRadius(.01)
    #
    profileMapper = vtk.vtkPolyDataMapper()
    profileMapper.SetInputData(Tubes.GetOutput())

    #
    profile = vtk.vtkActor()
    profile.SetMapper(profileMapper)
    profile.GetProperty().SetDiffuseColor(banana)
    profile.GetProperty().SetSpecular(.3)
    profile.GetProperty().SetSpecularPower(30)

    # Now create the RenderWindow, Renderer and Interactor
    ren = vtk.vtkRenderer()
    renWin = vtk.vtkRenderWindow()
    renWin.AddRenderer(ren)

    iren = vtk.vtkRenderWindowInteractor()
    iren.SetRenderWindow(renWin)

    # Add the actors
    ren.AddActor(glyph)
    ren.AddActor(profile)

    renWin.SetSize(640, 640)

    iren.Initialize()
    renWin.Render()
    iren.Start()


if __name__ == "__main__":

    pos = [[0.1, 2, 0.3], [40, 0.5, -10],
           [0.1, -40, 0.3], [-49, 0.1, 2],
           [10.3, 0.3, 0.4], [-109, 0.3, 0.4]]
    pos = pd.DataFrame(pos, columns=['x', 'y', 'z'])
    pos_d = pos.T.to_dict(orient='list')
    
    ed_ls = [(x, y) for x, y in zip(range(0, 5), range(1, 6))]

    G = nx.Graph()
    G.add_edges_from(ed_ls)
    # nx.draw(G, with_labels=True, pos=nx.spring_layout(G))
    # plt.show()
    draw_nxvtk(G=G, node_pos=pos_d)

Suggestions on how to view the output with the polyData displayed while running the above code and how to save the vtkPolyData for importing in Paraview will be really helpful.

1
Does this question and the answer help you? stackoverflow.com/questions/54131133/…Sparky05
@Sparky05 Thank you, I will give it a tryNatasha
@Sparky05 Could you please have a look at my edit?Natasha

1 Answers

1
votes

If you're ok with using vedo, which is built on top of vtk, this becomes easy:

import networkx as nx

pos = [[0.1, 2, 0.3],    [40, 0.5, -10],
       [0.1, -40, 0.3],  [-49, 0.1, 2],
       [10.3, 0.3, 0.4], [-109, 0.3, 0.4]]

ed_ls = [(x, y) for x, y in zip(range(0, 5), range(1, 6))]

G = nx.Graph()
G.add_edges_from(ed_ls)
nxpos = nx.spring_layout(G)
nxpts = [nxpos[pt] for pt in sorted(nxpos)]
# nx.draw(G, with_labels=True, pos=nxpos)
# plt.show()

raw_lines = [(pos[x],pos[y]) for x, y in ed_ls]
nx_lines = []
for x, y in ed_ls:
    p1 = nxpos[x].tolist() + [0] # add z-coord
    p2 = nxpos[y].tolist() + [0]
    nx_lines.append([p1,p2])

from vedo import *
raw_pts = Points(pos, r=12)
raw_edg = Lines(raw_lines).lw(2)
show(raw_pts, raw_edg, raw_pts.labels('id'),
     at=0, N=2, axes=True, sharecam=False)

nx_pts = Points(nxpts, r=12)
nx_edg = Lines(nx_lines).lw(2)
show(nx_pts, nx_edg, nx_pts.labels('id'),
     at=1, interactive=True)

write(nx_edg, 'afile.vtk') # save the lines

enter image description here

The package also supports DirectedGraphs, so a second options is:

from vedo import *
from vedo.pyplot import DirectedGraph

# Layouts: [2d, fast2d, clustering2d, circular, circular3d, cone, force, tree]
g = DirectedGraph(layout='fast2d')
g.arrowScale =0.1
for i in range(6): g.addChild(i)
g.build()
show(g, axes=1)

write(g.unpack(0), 'afile.vtk')

EDIT: Following up the request,

How to include color mapping of the lines based on a scalar with cellColors():

# ... from the first example

from vedo import *
raw_pts = Points(pos, r=12)
raw_edg = Lines(raw_lines).lw(3)

nx_pts = Points(nxpts, r=12).c('red').alpha(0.5)
nx_edg = Lines(nx_lines).lw(2)

v1 = [sin(x)  for x in range(6)]
v2 = [sqrt(x) for x in range(6)]
vc = [x for x in range(nx_edg.NCells())]
labs1 = nx_pts.labels(v1, scale=.05).c('green').addPos(0.02,.04,0)
labs2 = nx_pts.labels(v2, scale=.05).c('red').addPos(0.02,-.04,0)
labsc = nx_edg.labels(vc, cells=True, scale=.04, precision=1, rotZ=-45)
labsc.c('black')

nx_edg.cellColors(vc, cmap='viridis').addScalarBar3D(c='k').addPos(.2,0,0)
# nx_edg.cellColors(vc, cmap='jet').addScalarBar() # this is a 2D scalarbar

show(nx_pts, nx_edg, labs1, labs2, labsc, axes=1)

enter image description here

How to hover points with the mouse to pop up a flag message with flag():

from vedo import *
raw_pts = Points(pos, r=12)
raw_edg = Lines(raw_lines).lw(3)

nx_pts = []
for p in nxpts:
    ap = Point(p, r=20).c('red').alpha(0.5)
    ap.flag('some text:\n'+'x='+precision(p[0],2)+'\ny='+precision(p[1],2))
    nx_pts.append(ap)

nx_edg = Lines(nx_lines).lw(3)
show(nx_pts, nx_edg, axes=1)

How to interpolate the line color to the node values:

(NB: here clean() removes duplicate points so please double check with possible mismatches with the initial array)

from vedo import *

nx_pts = Points(nxpts, r=12).c('grey').alpha(0.5)
nx_edg = Lines(nx_lines).lw(5)

v1 = [sin(x) for x in range(6)]
labs1 = nx_pts.labels(v1, scale=.05).c('green').addPos(0.02,.04,0)

nx_edg.clean().pointColors(v1, cmap='viridis').addScalarBar()

show(nx_pts, nx_edg, labs1, axes=1)

enter image description here