Creating a simple Graph Visualization
This is a very basic example of using the prefuse toolkit.
- Compiled JAR
- Source Code (both prefuse and Tester.xml must be in the classpath in order for this to successfully compile)
- Tester.xml (must be renamed Tester.xml in order to be used for compiling the source)
Written in the following format :
<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" >
<graph edgedefault="undirected">
<key id="color" for="node" attr.name="color" attr.type="string"/>
<key id="lame" for="node" attr.name="lame" attr.type="string"/>
<node id="1">
<data key="color">green</data>
<data key="lame">A</data>
</node>
<node id="2">
<data key="color">blue</data>
<data key="lame">B</data>
</node>
<node id="3">
<data key="color">red</data>
<data key="lame">C</data>
</node>
<node id="4">
<data key="color">turquoise</data>
<data key="lame">D</data>
</node>
<node id="5">
<data key="color">Zarustra</data>
<data key="lame">E</data>
</node>
<edge source="1" target="2"/>
<edge source="2" target="3"/>
<edge source="3" target="4"/>
<edge source="4" target="1"/>
<edge source="5" target="1"/>
<edge source="5" target="2"/>
<edge source="5" target="3"/>
<edge source="5" target="4"/>
</graph>
</graphml>
The file must be saved in the direct classpath of the script, otherwise it won't read and will spit out an exception error.
Imports
This changes constantly depending on what kinds of data or layout you're using. I've found the best strategy is to just use whatever you want and hunt down the specific imports after the fact.
import javax.swing.JFrame;
import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.layout.*;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.DataColorAction;
import prefuse.action.layout.graph.*;
import prefuse.activity.Activity;
import prefuse.controls.DragControl;
import prefuse.controls.PanControl;
import prefuse.controls.ZoomControl;
import prefuse.data.Graph;
import prefuse.data.io.DataIOException;
import prefuse.data.io.GraphMLReader;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.LabelRenderer;
import prefuse.util.ColorLib;
import prefuse.visual.VisualItem;
import prefuse.controls.WheelZoomControl;
Loading the Graph
You need to wrap the actual reading of the graph in a try/catch construct, since it throws an exception error if it's unable to read
public static void main(String argv[]){
Graph MyGraph = null;
try {
MyGraph = new GraphMLReader().readGraph("/Tester.xml");
}
catch ( DataIOException e ) {
System.err.println("Couldn't load the graph...");
System.exit(1);
}
Build the Visualization Object
Now that we've got data, the next step is to build the visualization object, register our graph with the object, and set the edges (links) of the visualization to not react to user input.
Visualization MyViz = new Visualization();
MyViz.add("graph", MyGraph);
MyViz.setInteractive("graph.edges", null, false);
Rendering
We'll just be using a simple label renderer for every node, so we can use the default render factory. Custom Renderer Factories can be created in order to adjust the look of the nodes based on search criteria.
LabelRenderer MyLabel = new LabelRenderer("lame");
MyLabel.setRoundedCorner(2, 2);
MyViz.setRendererFactory(new DefaultRendererFactory(MyLabel));
Coloring
Creating the color rules is a bit of a process. First you need to create a color pallete which holds all the colors you want to use in the program. The order you create the colors is important for how they are assigned if you aren't using the ColorMap function:
int[] MyPalette = new int[] {
ColorLib.rgb(0, 0, 255), //Blue
ColorLib.rgb(0, 255, 0), //Green
ColorLib.rgb(255, 0, 0), //Red
ColorLib.rgb(0, 245, 255), //Turquoise
ColorLib.rgb(255, 0, 255) //Purple
};
Apply the coloring to the graph objects based on the alphabetic ordering of the color field of each node :
DataColorAction NodeFill = new DataColorAction(
"graph.nodes", //The graph we're applying this to
"color", //The Datafield used to determine color
Constants.NOMINAL, //Define Datafield as Nominal (A Category)
VisualItem.FILLCOLOR, //We're Filling the Node with Color
MyPalette //We're using MyPalette for Color Values
);
Now we have to define the colors of the text and the edges of each node:
//Set the text color to Black
ColorAction text = new ColorAction(
"graph.nodes",
VisualItem.TEXTCOLOR,
ColorLib.rgb(255, 255, 255)
);
//Set the edge color to light gray
ColorAction edges = new ColorAction(
"graph.edges",
VisualItem.STROKECOLOR,
ColorLib.rgb(0, 0, 200)
);
Action Lists
Now that everything else is set up, we need to build the action lists which will define the behavior of the visual elements within the display. I'm not sure if it's the correct way to go about it, but I've found that including a repaint action at the end of each Action List makes things run more smoothly.
First is the Coloring Action List:
ActionList Coloring = new ActionList();
Coloring.add(NodeFill);
Coloring.add(text);
Coloring.add(edges);
Coloring.add(new RepaintAction());
Next up is the layout, because I'm using the force directed layout I'm setting up this Action List to continue running while the program is still executing:
ActionList DoLayout = new ActionList(Activity.INFINITY);
DoLayout.add(new ForceDirectedLayout("graph", true));
DoLayout.add(new RepaintAction());
Last Actionlist is one to randomly reposition the nodes every second (just to keep things interesting).
ActionList Randomize = new ActionList(Activity.INFINITY, 1000);
Randomize.add(new RandomLayout("graph"));
Randomize.add(new RepaintAction());
Now that the action lists are built, they need to be registered with the visualization:
MyViz.putAction("Coloring", Coloring);
MyViz.putAction("DoLayout", DoLayout);
MyViz.putAction("Randomize", Randomize);
Building the Display
Now that we've got the visualization together, the display object needs to be built :
Display MyDisplay = new Display(MyViz);
MyDisplay.setSize(300, 300);
Now the controller objects desired need to be created and registered with the display object :
MyDisplay.addControlListener(new DragControl()); //Dragging of Individual Nodes
MyDisplay.addControlListener(new PanControl()); //Panning Controls
MyDisplay.addControlListener(new ZoomControl()); //Right Click Zooming
MyDisplay.addControlListener(new WheelZoomControl()); //Mouse Wheel Zooming
Building the JFrame
Now create a JFrame object to hold the display, configure it and set it visible.
JFrame MyFrame = new JFrame("Graph Visualization Demo");
MyFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyFrame.add(MyDisplay);
MyFrame.pack();
MyFrame.setVisible(true);
Run Actionlists
All that's left is to execute the actionlists and start out the visualization :
MyViz.run("Coloring");
MyViz.run("DoLayout");
MyViz.run("Randomize");
}