User Tools

Site Tools


java:prefuse-scatterplot

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
java:prefuse-scatterplot [2009/08/06 13:05]
Alexander Rind Phase 2: Refinements
java:prefuse-scatterplot [2009/10/05 15:52] (current)
Alexander Rind some updates
Line 6: Line 6:
 Therefore, this tutorial starts with the minimum source code to create a scatter plot and incrementally adds features. Therefore, this tutorial starts with the minimum source code to create a scatter plot and incrementally adds features.
 Further, it discusses an axis-based visualization technique, whereas the documentation of prefuse focuses on graph visualization. ​ Further, it discusses an axis-based visualization technique, whereas the documentation of prefuse focuses on graph visualization. ​
 +The demo application is inspired by Jeffrey Heer's Congress demo, but achieves its functionality in small incremental steps.
 +((Please [[http://​ike.donau-uni.ac.at/​~rind/​ | let me know]], if you spot any errors or misleading information.))
 +
 +===== Introduction =====
 +
 +Developing with prefuse is for the most part configuration. ​
 +You can achieve both basic and advanced visualizations simply by creating objects and setting their properties.
 +However, this makes it very easy produce source code that is hard to understand.
 +
 +I recommend to structure your prefuse configuration in four parts:
 +  * data setup
 +  * renderers
 +  * action lists
 +  * displays and controls
  
 ===== Phase 0: Infrastructure ===== ===== Phase 0: Infrastructure =====
Line 59: Line 73:
 </​code>​ </​code>​
  
-{{:it:​scatterplot0.java| Source Code}}+{{:java:​scatterplot0.java| Source Code}}
  
 ===== Phase 1: Essentials ===== ===== Phase 1: Essentials =====
Line 110: Line 124:
 This is the resulting visualization: ​ This is the resulting visualization: ​
  
-{{:it:​prefuse-scatterplot1.png?​50}}+{{:java:​prefuse-scatterplot1.png?​100}}
  
-{{:it:​scatterplot1.java|Source Code}}+{{:java:​scatterplot1.java|Source Code}} 
 + 
 +=== Further Information === 
 + 
 +__Alternative:​__ Our visualization class can also be a subclass of either [[http://​prefuse.org/​doc/​api/​prefuse/​Visualization.html | Visualization]] or [[http://​prefuse.org/​doc/​api/​prefuse/​Display.html | Display]] instead of referencing them. 
 + 
 +__Note:__ A [[http://​prefuse.org/​doc/​api/​prefuse/​Visualization.html | Visualization]] objects can handle multiple [[http://​prefuse.org/​doc/​api/​prefuse/​Display.html | Display]]. However, in some situation prefuse only takes the first display in account -- it is still beta.
  
 ===== Phase 2: Refinements ===== ===== Phase 2: Refinements =====
Line 119: Line 139:
  
 First, we want smaller shapes. ​ First, we want smaller shapes. ​
 +For this, we create a [[http://​prefuse.org/​doc/​api/​prefuse/​render/​ShapeRenderer.html | ShapeRenderer]] with default size 7.
  
 <code java> <code java>
Line 162: Line 183:
 This is the resulting visualization: ​ This is the resulting visualization: ​
  
-{{:it:​prefuse-scatterplot2.png?​50}}+{{:java:​prefuse-scatterplot2.png?​150}} 
 + 
 +{{:​java:​scatterplot2.java|Source Code}} 
 + 
 +===== Phase 3: Axis Labels and Grid Lines ===== 
 + 
 +In this step, we will add axis labels and grid lines to the visualization. 
 + 
 +First, we have to use a new [[http://​prefuse.org/​doc/​api/​prefuse/​render/​RendererFactory.html | RendererFactory]]. 
 +Visual items from our data table will still be rendered by the [[http://​prefuse.org/​doc/​api/​prefuse/​render/​ShapeRenderer.html | ShapeRenderer]],​ 
 +but for axis labels we will use [[http://​prefuse.org/​doc/​api/​prefuse/​render/​AxisRenderer.html | AxisRenderer]]. 
 + 
 +<code java> 
 + vis.setRendererFactory(new RendererFactory() { 
 + AbstractShapeRenderer sr = new ShapeRenderer(7);​ 
 + Renderer arY = new AxisRenderer(Constants.FAR_LEFT,​ 
 + Constants.CENTER);​ 
 + Renderer arX = new AxisRenderer(Constants.CENTER,​ 
 + Constants.FAR_BOTTOM);​ 
 + 
 + public Renderer getRenderer(VisualItem item) { 
 + return item.isInGroup("​ylab"​) ? arY 
 + : item.isInGroup("​xlab"​) ? arX : sr; 
 +
 + }); 
 +</​code>​ 
 + 
 +Second, we create two instances of [[http://​prefuse.org/​doc/​api/​prefuse/​action/​layout/​AxisLabelLayout.html | AxisLabelLayout]] and initialize them with the [[http://​prefuse.org/​doc/​api/​prefuse/​action/​layout/​AxisLayout.html | AxisLayouts]].  
 + 
 +<code java> 
 + AxisLabelLayout x_labels = new AxisLabelLayout("​xlab",​ x_axis); 
 + 
 + AxisLabelLayout y_labels = new AxisLabelLayout("​ylab",​ y_axis); 
 +</​code>​ 
 + 
 +Third, the new layout actions needs to be included in the action list. 
 + 
 +<code java> 
 + draw.add(x_labels);​ 
 + draw.add(y_labels);​ 
 +</​code>​ 
 + 
 +Fourth, we create an [[http://​prefuse.org/​doc/​api/​prefuse/​visual/​sort/​ItemSorter.html | ItemSorter]],​ so that data items will be displayed in front of the grid lines. 
 + 
 +<code java> 
 + display.setItemSorter(new ItemSorter() { 
 + public int score(VisualItem item) { 
 + int score = super.score(item);​ 
 + if (item.isInGroup("​data"​)) 
 + score++;​ 
 + return score; 
 +
 + }); 
 +</​code>​ 
 + 
 +This is the resulting visualization:​  
 + 
 +{{:​java:​prefuse-scatterplot3.png?​150}} 
 + 
 +{{:​java:​scatterplot3.java|Source Code}} 
 + 
 +=== Further Information === 
 + 
 +__Alternative:​__ Instead of extending RendererFactory,​ we can use [[http://​prefuse.org/​doc/​api/​prefuse/​render/​DefaultRendererFactory.html | DefaultRendererFactory]].  
 +In this case we use predicates to identify the axes.  
 + 
 +<code java> 
 + DefaultRendererFactory rf = new DefaultRendererFactory();​  
 + rf.setDefaultRenderer(new ShapeRenderer(7));​ 
 + rf.add(new InGroupPredicate("​ylab"​),​  
 + new AxisRenderer(Constants.FAR_LEFT,​ Constants.CENTER));​ 
 + rf.add(new InGroupPredicate("​xlab"​),​  
 + new AxisRenderer(Constants.CENTER,​ Constants.FAR_BOTTOM));​ 
 + vis.setRendererFactory(rf);​ 
 +</​code>​ 
 + 
 + 
 +__Warning:​__ When the [[http://​prefuse.org/​doc/​api/​prefuse/​action/​layout/​AxisLabelLayout.html | AxisLabelLayout]] actions are run,  
 +they generate new visual items for the axis labels.  
 + 
 +"​xlab"​ and "​xlab"​ are the group names of these visual items.  
 +Do not put the group name of your data here! 
 + 
 +===== Phase 4: Interactivity ===== 
 + 
 +Here we will some interactivity to our demo application. 
 + 
 +First, we create a new [[http://​prefuse.org/​doc/​api/​prefuse/​action/​ActionList.html | ActionList]] with all actions that we need to update the visualization. 
 + 
 +<code java> 
 + ActionList update = new ActionList();​ 
 + update.add(x_axis);​ 
 + update.add(y_axis);​ 
 + update.add(x_labels);​ 
 + update.add(y_labels);​ 
 + update.add(new RepaintAction());​ 
 + vis.putAction("​update",​ update); 
 +</​code>​ 
 + 
 +Second, we an implementation of [[http://​java.sun.com/​javase/​6/​docs/​api/​java/​awt/​event/​ComponentListener.html | ComponentListener]] to the [[http://​prefuse.org/​doc/​api/​prefuse/​Display.html | Display]], which will run the new [[http://​prefuse.org/​doc/​api/​prefuse/​action/​ActionList.html | ActionList]],​ whenever the user changes the size of the window -- and thus resizes the [[http://​prefuse.org/​doc/​api/​prefuse/​Display.html | Display]].  
 + 
 +<code java> 
 + display.addComponentListener(new ComponentAdapter() { 
 + public void componentResized(ComponentEvent e) { 
 + vis.run("​update"​);​ 
 +
 + }); 
 +</​code>​ 
 + 
 +Third, we add three ready-made controls to the [[http://​prefuse.org/​doc/​api/​prefuse/​Display.html | Display]]. 
 + 
 +  * [[http://​prefuse.org/​doc/​api/​prefuse/​controls/​PanControl.html | PanControl]] allows the user to pan by dragging the left mouse button in the background. 
 +  * [[http://​prefuse.org/​doc/​api/​prefuse/​controls/​ZoomControl.html | ZoomControl]] allows the user to zoom by dragging the right mouse button in the background. 
 +  * [[http://​prefuse.org/​doc/​api/​prefuse/​controls/​ZoomToFitControl.html | ZoomToFitControl]] automatically zooms so that the whole visualization is visible, whenever the user clicks with the right mouse button. 
 + 
 +<code java> 
 + display.addControlListener(new PanControl());​ 
 + display.addControlListener(new ZoomControl());​ 
 + display.addControlListener(new ZoomToFitControl());​ 
 +</​code>​ 
 + 
 + 
 +This is the resulting visualization:​  
 + 
 +{{:​java:​prefuse-scatterplot4.png?​150}} 
 + 
 +{{:​java:​scatterplot4.java|Source Code}} 
 + 
 +=== Further Information === 
 + 
 +__Warning:​__ Panning and Zooming doesn'​t work as one might expect: it simply performs a geometric transformation of the display. 
 + 
 +__Alternative:​__ Using range sliders is an possible alternative to zoom and pan. 
 +However the underlying interaction metaphor is different and can be harder to understand. 
 + 
 +===== Phase 5: Refinements on axes ===== 
 + 
 +Now, we add a few refinements on the display of axis. 
 + 
 +First, we add a new column to the [[http://​prefuse.org/​doc/​api/​prefuse/​visual/​VisualTable.html | VisualTable]],​ which will contain a derived value from two columns. We also insert captions and format one of the variables. 
 + 
 +__Note:__ In order to access the [[http://​prefuse.org/​doc/​api/​prefuse/​visual/​VisualTable.html | VisualTable]] we have to replace ''​add''​ with ''​addTable''​. 
 + 
 +<code java> 
 + VisualTable vt = vis.addTable("​data",​ data); 
 + 
 + vt.addColumn("​label",​ 
 + "​CONCAT('​NBZ:​ ', [NBZ], '; BMI: ', FORMAT([BMI],​1))"​);​ 
 +</​code>​ 
 + 
 +Second, we set the range of the y axis, so that values from 1 to 40 are visible. 
 +<code java> 
 + y_axis.setRangeModel(new NumberRangeModel(1,​ 40, 1, 40)); 
 +</​code>​ 
 + 
 +Third, we use a square root scale for y axis. 
 +<code java> 
 + y_axis.setScale(Constants.SQRT_SCALE);​ 
 + y_labels.setScale(Constants.SQRT_SCALE);​ 
 +</​code>​ 
 + 
 +Fourth, we define a number format and use it for y axis labels. 
 +<code java> 
 + NumberFormat nf = NumberFormat.getNumberInstance();​ 
 + nf.setMaximumFractionDigits(1);​ 
 + nf.setMinimumFractionDigits(1);​ 
 + y_labels.setNumberFormat(nf);​ 
 +</​code>​ 
 + 
 +Finally, we use the new derived column for tool tips. 
 +<code java> 
 + ToolTipControl ttc = new ToolTipControl("​label"​);​ 
 +</​code>​ 
 + 
 +This is the resulting visualization:​  
 + 
 +{{:​java:​prefuse-scatterplot5.png?​150}} 
 + 
 +{{:​java:​scatterplot5.java|Source Code}} 
 + 
 +===== Phase 6: Bounding boxes for data and axes ===== 
 + 
 +Here we manipulate the bounding boxes for the visualization of the data and both axes. This has two effects: 
 + 
 +  * We have fine-grained access on the location of visual items 
 +    * In this example we do not display vertical grid lines behind the data visualizations.  
 +    * Further, we draw horizontal grid line to the right edge, but do not place data items there. 
 +    * Finally, we added some space between y axis labels and the left-most data items.  
 +  * We do not need the empty border defined in phase 2 anymore.  
 +    * Now we or someone else using the [[http://​prefuse.org/​doc/​api/​prefuse/​Display.html | Display]] can add a border, and items at the edge of the visualization (especially axis labels) stay completely visible. 
 + 
 + 
 +First, we define three rectangles for the visualization of the data and both axes. 
 + 
 +<code java> 
 + final Rectangle2D boundsData = new Rectangle2D.Double();​ 
 + final Rectangle2D boundsLabelsX = new Rectangle2D.Double();​ 
 + final Rectangle2D boundsLabelsY = new Rectangle2D.Double();​ 
 +</​code>​ 
 + 
 +Second, we set the bounding box for data items on the [[http://​prefuse.org/​doc/​api/​prefuse/​action/​layout/​AxisLayout.html | AxisLayouts]]. 
 + 
 +<code java> 
 + x_axis.setLayoutBounds(boundsData);​ 
 + y_axis.setLayoutBounds(boundsData);​ 
 +</​code>​ 
 + 
 +Third, we add the bounding box for axes to their initialization. 
 + 
 +<code java> 
 + AxisLabelLayout x_labels = new AxisLabelLayout("​xlab",​ x_axis, 
 + boundsLabelsX);​ 
 + 
 + AxisLabelLayout y_labels = new AxisLabelLayout("​ylab",​ y_axis, 
 + boundsLabelsY);​ 
 +</​code>​ 
 + 
 +Fourth, instead of the empty border, we now use a titled border. 
 + 
 +<code java> 
 + display.setBorder(BorderFactory.createTitledBorder("​Demo"​));​ 
 +</​code>​ 
 + 
 +Fifth, we add the call to a new method ''​updateBounds()'',​ before each call to an action list (two times). 
 + 
 +<code java> 
 + updateBounds(display,​ boundsData, boundsLabelsX,​ boundsLabelsY);​ 
 +... 
 + updateBounds(display,​ boundsData, boundsLabelsX,​ boundsLabelsY);​ 
 +</​code>​ 
 + 
 +Finally, this is the method ''​updateBounds()'',​ which sets the bounds as described above. 
 + 
 +<code java> 
 + private static void updateBounds(Display display, Rectangle2D boundsData,​ 
 + Rectangle2D boundsLabelsX,​ Rectangle2D boundsLabelsY) { 
 + 
 + int paddingLeft = 30; 
 + int paddingTop = 15; 
 + int paddingRight = 30; 
 + int paddingBottom = 15; 
 + 
 + int axisWidth = 20; 
 + int axisHeight = 10; 
 + 
 + Insets i = display.getInsets();​ 
 + 
 + int left = i.left + paddingLeft;​ 
 + int top = i.top + paddingTop;​ 
 + int innerWidth = display.getWidth() - i.left - i.right - paddingLeft 
 + - paddingRight;​ 
 + int innerHeight = display.getHeight() - i.top - i.bottom - paddingTop 
 + - paddingBottom;​ 
 + 
 + boundsData.setRect(left + axisWidth, top, innerWidth - axisWidth,​ 
 + innerHeight - axisHeight);​ 
 + boundsLabelsX.setRect(left + axisWidth, top + innerHeight - axisHeight,​ 
 + innerWidth - axisWidth, axisHeight);​ 
 + boundsLabelsY.setRect(left,​ top, innerWidth + paddingRight,​ innerHeight 
 + - axisHeight);​ 
 +
 +</​code>​ 
 + 
 + 
 +This is the resulting visualization:​  
 + 
 +{{:​java:​prefuse-scatterplot6.png?​150}} 
 + 
 +{{:​java:​scatterplot6.java|Source Code}} 
 + 
 +=== Further Information === 
 + 
 +__Note:__ If [[http://​prefuse.org/​doc/​api/​prefuse/​render/​AxisRenderer.html | AxisRenderer]] have the ''​FAR_LEFT'',​ ''​FAR_RIGHT'',​ ''​FAR_TOP'',​ or ''​FAR_BOTTOM'',​ the grid lines will fill the bounding box completely and labels will be placed outside :!: the bounding box. 
 + 
 +__Warning:​__ Be careful, prefuse will not stop you from designing misleading visualizations,​ if the bounding boxes of data and axes are not correctly aligned :!: 
 + 
 +__Note:__ The bounding rectangles are final variables so that they are accessible from inner classes.  
 +Alternatively they could be defined as instance variables. 
 + 
 +__Alternative:​__ Instead of the method ''​updateBounds()'',​ we can write an action that takes care of updating the bounding rectangles.  
 +Then, this task can be included in the ''​update''​ action list. 
  
-{{:​it:​scatterplot2.java|Source Code}} 
java/prefuse-scatterplot.1249556754.txt · Last modified: 2009/08/11 09:53 (external edit)

alex @ ieg: home about me publications research