Question

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 parallel edges
2) directed and undirected edges within a single graph
3) nodes represented by images
4) user interaction with nodes and edges
5) dynamically adding/deleting nodes and edges
6) multiple labelling on nodes and edges , different levels of labelling can be turned off/on by users. (like drawing in layers and turning off / on a layer)
7) different layout algorithms to display star, ring, mesh topologies

I evaluated JUNG and Prefuse. This is what I found for each of my requirements.

1) Prefuse cannot display parallel edges while JUNG supports it. Can prefuse code be manipulated to display parallel edges ? Since this involves basic data level changes I believe this would be more difficult that the usual customized rendering changes.

2) I didnt find any reference to combined graph (both directed and undirected edges) in both prefuse and JUNG. Does anyone know otherwise?

3) This seems easy with both Prefuse and JUNG

4) Again both prefuse and JUNG provides support for user interaction.

5) Both prefuse and JUNG supports it. How does each framework perform while redrawing the graph ? I saw in another post that prefuse does not perform well for dynamic updates (Prefuse Toolkit: dynamically adding nodes and edges)

6) This comes down to modifying the graph and redrawing it. So the question becomes the same as 5)

7) Both JUNG and prefuse has multiple layout algorithms. But When I tried to display the same dataset using FruchtermanReingoldLayout in both JUNG and Prefuse I get different displays. Any ideas why ? Somehow the layout algorithms in Prefuse seem to display a better layout than in JUNG (rendering is also better I think) though most of the layout algorithms in Prefuse are based on JUNG implementation. Prefuse layouts such as ForceDirectedLayout/FruchtermanReingoldLayout and CircleLayout directly maps to star,circle, mesh topologies.

Outside of these requirements, prefuse has good support for expressions and query language but looks like it is not actively developed unlike JUNG. which one has better visualization? Any suggestions on which one will be suitable and how to overcome the shortcomings ?

Any other frameworks out there which I can use ?

Was it helpful?

Solution

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.

OTHER TIPS

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:

  1. Supported (you'll need the right data model, not all support parallel edges for performance reasons)
  2. Supported (again, you need the right data model)
  3. Supported (see ImageShaperDemo)
  4. Supported (most demos)
  5. Supported (see GraphEditorDemo)
  6. Not directly supported, although you can certainly change labels dynamically and use HTML to render complex labels.
  7. JUNG's layout algorithms are more for general networks (with a few exceptions for trees, etc.). You can certainly construct your own layout algorithms, however, and many have done so.

Hope this helps.

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 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);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top