which of the open source Java graph drawing frameworks to use for a network diagram with the following requirements? The graph will have less than 1000 nodes.
1) has par
A few years back (2007?) I use prefuse to visualize call data records. I considered prefuse, jung, jgraph and a few others and chose prefuse. At first it's a bit hard to wrap my head around prefuse but once I got familiar with it it's really easy (to extend) and fun to use. I guess the same can be said for JUNG but I never tried it.
1) In prefuse it's very easy to add your own custom renderer for drawing parallel edges - you can subclass the default EdgeRenderer and override the render() method. There's no "basic data level changes" needed. This is all in the view part if you'd like to think of it as an MVC stuff.
2) This is not really an issue at all. There are more than one way to do this: 1) You can have two renderers - one for drawing the directed edges and one for drawing the undirected edges and they'll work just fine, and group the edges appropriately. 2) Put a flag (add a boolean column in the backing table tuple in prefuse speak) to indicate whether the edge is directed and skip the arrow drawing portion accordingly in the EdgeRender according to that flag.
3) This is super easy
4) ditto
5) The last prefuse release is "prefuse beta release 2007.10.21". I used the one before that, which have a possible race condition when adding or deleting nodes dynamically - it was missing a few synchronized keywords I guess. I solved that by making sure to stop all the animation and actions (color, size, layout) when adding or removing nodes - also don't forget to update your lucene indexes as well (if you do use its built-in lucene search engine). The latest one is supposed to solve this race issue but I never had the chance to try it out.
6) Since you mentioned "multiple labelling" I think this not a matter of "modifying the graph and redrawing it" - it's just a matter of customizing your label/edge renderers to draw only the relevant labels so this is not really a big issue. Also I don't think this is related to 5 at all.
7) I'm not surprised that prefuse and JUNG's rendering of the FruchtermanReingoldLayout are different - there are a few factors that might affect this one of them the starting node where each implementation start the calculation so I wouldn't worry much about this issue. It's quite easy to try out the different builtin graph layout algorithms in prefuse so you can go ahead and check out which one is closest to what you'd like to have. Check out the RadialLayout and BalloonTreeLayout for the star layout. ForceDirectedLayout needs quite a few iterations for the placement of nodes to be "stable". Note that these iterations is not necessary to be shown so you can run it in the background and render the final result.
I haven't use JUNG so I can't comment much on it.
Based on my experience with prefuse I highly recommend it due to the very well (IMHO) thought-out design and separation of resposibility between the components. Jeffrey Heer (prefuse author) really did a good job there.
Things to watch out for if you use prefuse (these are the two "sore-thumbs" that I vividly remember when working with prefuse):
1) There's a bug where when zooming out, the node labels are not zoomed out appropriately such that it overflows the bounding box of the node which will leave font drawing artifacts when the node moves because the renderer only clears and redraws stuff within the node's bounding box. IIRC this is caused by a bug in AWT font metric itself. The workaround is to leave ample margin between the label and the node bounding box.
2) When extending the built-in layouts, you might encounter one or two "scoping issue" where a member of the superclass that you'd like to have access to is given the private attribute instead of protected so the solution is to either modify the library itself or create a new class without inheriting (that can be a bit painful!). I guess you can say the same for some other java libraries. Not everyone have the benefit of hindsight no? :)
Since you asked this question about a month ago (at the time of me writing this) I'd like to know what your decision was and how it turned out for you if you went ahead with the implementation.
I know you specified jung and prefuse but... I've had good experience with both TomSawyer and yFiles. The requirements list you proposed is very basic to these two - and they support much more.
Ran.
I'd suggest evaluating JGraph too.
I'm one of the creators and maintainers of JUNG, so bear that in mind for the responses below.
First, though, I should say that the author of Prefuse is a friend of a friend (and yes, we've met) and he's done a great job. I am not experienced with Prefuse, but I've seen some beautiful visualizations created with it.
Here are the answers to those questions for JUNG. Several of them ((1), (2), (4)are demonstrated in PluggableRendererDemo
:
ImageShaperDemo
)GraphEditorDemo
)Hope this helps.
I like @holygeek's answer. Here is my implementation to the solution for 2 (both directed and undirected edges), for Prefuse:
public class MyRenderFactory implements RendererFactory
{
private NodeRenderer nodeRenderer = new NodeRenderer();
private EdgeRenderer defaultEdgeRenderer = new EdgeRenderer();
private EdgeRenderer undirectedEdgeRenderer = new EdgeRenderer(EdgeRenderer.EdgeType.LINE, EdgeRenderer.EdgeArrowType.NONE);
public static String directedness = "myEdgeDirectedness";
public enum EdgeDirected
{
directed, undirected;
public static EdgeDirected fromIsDirected(boolean isDirected)
{
if (isDirected)
{
return directed;
}
return undirected;
}
}
@Override
public Renderer getRenderer(VisualItem<?> visualItem)
{
if (visualItem instanceof EdgeItem)
{
if (visualItem.get(directedness).equals(PrefuseGraphConverter.EdgeDirected.undirected))
{
return undirectedEdgeRenderer;
}
return defaultEdgeRenderer;
}
return nodeRenderer;
}
}
...elsewhere, where the graph is created...
MyRenderFactory.EdgeDirected directedness =
MyRenderFactory.EdgeDirected.fromIsDirected(myEdge.isDirected());
prefuseEdge.set(MyRenderFactory.directedness, directedness);