Comenzamos con este post una serie de entradas relacionadas con el uso de diferentes herramientas y técnicas de visualización gráfica de datos.
La visualización gráfica de datos es crucial para mostrar de una manera rápida y sencilla una gran cantidad de información, además de ser una útil técnica para generar información adicional acerca de esos mismos datos, que puede estar oculta pero que puede hacerse visible cuando esos mismos datos se relacionan de una forma visual.
La primera de las herramientas que se van a usar es la libreria en Java de desarrollo de visualizaciones gráficas llamada Prefuse. Prefuse es una libreria gratuita implementada en Java y de gran potencia para el desarrollo de visualizaciones de datos complejas. Permite el desarrollo de diferentes visualizaciones como grafos, árboles, mapas, etc. Es especialmente adecuada para su uso desde aplicaciones Java estándar o applets.
En este ejemplo se presenta una sencilla visualización usando un grafo que además implementa un sistema de fuerzas con fuerzas sencillas como la Ley de Hook o fuerzas gravitatorias, para dar mayor dinamismo e interacción a la visualización.
1. GraphML
Una de las capacidades de Prefuse es la de procesar información formateada en GraphML. GraphML es un lenguaje de etiquetas para definir grafos, es decir, un XML para definir grafos. Esto facilita exportar e importar información contenida en un grafo. En el ejemplo en cuestión, se usa GraphML para definir un grafo y cargarlo via Prefuse, tal que así:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?xml version="1.0" encoding="ISO-8859-1"?> <graphml xmlns="http://graphml.graphdrawing.org/xmlns"> <graph xmlns="" edgedefault="undirected"> <key id="value" for="node" attr.name="value" attr.type="string" /> <key id="dividers" for="node" attr.name="dividers" attr.type="string" /> <node id="1"> <data key="value">1</data> <data key="dividers">1,</data> </node> <edge source="1" target="1" /> <node id="2"> <data key="value">2</data> <data key="dividers">2,1,</data> </node> <edge source="2" target="2" /> <edge source="2" target="1" /> <node id="3"> <data key="value">3</data> <data key="dividers">3,1,</data> </node> <edge source="3" target="3" /> <edge source="3" target="1" /> ... </graphml> |
2. Cargar el grafo
El GraphML se cargaría en Prefuse:
1 2 |
// Load graph Graph graph = new GraphMLReader().readGraph("data/output.xml"); |
3. Clase de visualización del grafo
Seguidamente, se crea una clase que implementará la visualización del grafo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
public class VisBrowser extends Visualization { private Graph graph; /** * Constructor. * * @param graph */ public VisBrowser(Graph graph) { super(); this.graph = graph; initVisualization(); } private void initVisualization() { this.add("graph", this.graph); // draw the "name" label for NodeItems // MyNodeRenderer r = new MyNodeRenderer("value"); LabelRenderer r = new LabelRenderer("value"); r.setRoundedCorner(8, 8); // round the corners // create a new default renderer factory // return our name label renderer as the default for all non-EdgeItems // includes straight line edges for EdgeItems by default this.setRendererFactory(new DefaultRendererFactory(r)); // map nominal data values to colors using our provided palette ColorAction fill = new ColorAction("graph.nodes", VisualItem.FILLCOLOR, ColorLib.rgb(135, 206, 235)); fill.add(VisualItem.HIGHLIGHT, ColorLib.rgb(255, 215, 0)); // use black for node text ColorAction text = new ColorAction("graph.nodes", VisualItem.TEXTCOLOR, ColorLib.gray(0)); // use light grey for edges ColorAction edges = new ColorAction("graph.edges", VisualItem.STROKECOLOR, ColorLib.gray(200)); // create an action list containing all color assignments ActionList color = new ActionList(); color.add(fill); color.add(text); color.add(edges); // create an action list with an animated layout // the INFINITY parameter tells the action list to run indefinitely ActionList layout = new ActionList(Activity.INFINITY); ForceDirectedLayout forceLayout = new ForceDirectedLayout("graph"); layout.add(fill); layout.add(forceLayout); layout.add(new RepaintAction()); // add the actions to the visualization this.putAction("color", color); this.putAction("layout", layout); this.runAfter("color", "layout"); } } |
4. Visualización del grafo
El grafo cargado anteriormente se pasa a la clase de visualización del mismo:
1 |
VisBrowser vis = new VisBrowser(graph); |
5. Contenedor de la visualización
Aunque ya se ha creado la visualización, falta agregar la misma al contenedor que la mostrará. Para ello se crea la clase DisplayBrowser:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public class DisplayBrowser extends Display { /** * Default constructor. */ public DisplayBrowser() { super(new Visualization()); initDisplay(); } /** * Constructor. * * @param vis */ public DisplayBrowser(Visualization vis) { super(vis); initDisplay(); } private void initDisplay() { this.setBackground(Color.GRAY); this.pan(getWidth() / 2, getHeight() / 2); // set initial position this.setSize(720, 500); // set display size this.addControlListener(new DragControl()); // drag items around this.addControlListener(new PanControl()); // pan with background left-drag this.addControlListener(new ZoomControl()); // zoom with vertical right-drag this.addControlListener(new WheelZoomControl()); // zoom with mouse wheel this.addControlListener(new NeighborHighlightControl()); // Neighbour highlight control } } |
Se añade la visualización al contenedor:
1 2 3 |
DisplayBrowser display = new DisplayBrowser(vis); display.getVisualization().run("color"); // assign the colors display.getVisualization().run("layout"); // start up the animated layout |
Por último, el contenedor de la visualización se puede agregar a cualquier otro contenedor, por ejemplo, un JPanel en una aplicación con SWING.
JNumbers muestra un grafo con los números naturales del 1 al 100. Cada nodo es un número que esta relacionado con sus correspondientes múltiplos y divisores.
Se puede descargar el proyecto completo para Eclipse de la aplicación desde este enlace.