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.
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
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.arrow_scale =0.1
for i in range(6): g.add_child(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 :
# ... 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').shift(0.02,.04,0)
labs2 = nx_pts.labels(v2, scale=.05).c('red').shift(0.02,-.04,0)
labsc = nx_edg.labels(vc, on="cells", scale=.04, precision=1, rotZ=-45)
labsc.c('black')
nx_edg.cmap('viridis', vc, on="cells").add_scalarbar3d(c='k').shift(.2,0,0)
# nx_edg.cmap('jet', vc).add_scalarbar() # this is a 2D scalarbar
show(nx_pts, nx_edg, labs1, labs2, labsc, 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').shift(0.02,.04,0)
nx_edg.clean().cmap('viridis', v1).add_scalarbar()
show(nx_pts, nx_edg, labs1, axes=1)