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 11:09]
Alexander Rind source code
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 =====
  
 +Next, we extend the method ''​createVisualization(Table)''​ and add the minimal source code to show a scatter plot.
 +
 +First, we need to create instances of [[http://​prefuse.org/​doc/​api/​prefuse/​Visualization.html | Visualization]] and [[http://​prefuse.org/​doc/​api/​prefuse/​Display.html | Display]] and link these with each other and the data table.
 +
 +<code java>
 + Visualization vis = new Visualization();​
 + Display display = new Display(vis);​
 + vis.add("​data",​ data);
 +</​code>​
 +
 +Second, we create a [[http://​prefuse.org/​doc/​api/​prefuse/​action/​layout/​AxisLayout.html | AxisLayout]] for the x axis and another one for the y axis. 
 +When these layouts are run, they will set the x and  coordinate of all visual items.
 +
 +<code java>
 + AxisLayout x_axis = new AxisLayout("​data",​ "​NBZ",​ Constants.X_AXIS,​
 + VisiblePredicate.TRUE);​
 +
 + AxisLayout y_axis = new AxisLayout("​data",​ "​BMI",​ Constants.Y_AXIS,​
 + VisiblePredicate.TRUE);​
 +</​code>​
 +
 +Third, a [[http://​prefuse.org/​doc/​api/​prefuse/​action/​assignment/​ColorAction.html | ColorAction]] will set the color of all visual items to blue.
 +
 +<code java>
 + ColorAction color = new ColorAction("​data",​ VisualItem.STROKECOLOR,​
 + ColorLib.rgb(100,​ 100, 255));
 +</​code>​
 +
 +Fourth, these actions are combined to an [[http://​prefuse.org/​doc/​api/​prefuse/​action/​ActionList.html | ActionList]] and linked to the [[http://​prefuse.org/​doc/​api/​prefuse/​Visualization.html | Visualization]].
 +<code java>
 + ActionList draw = new ActionList();​
 + draw.add(x_axis);​
 + draw.add(y_axis);​
 + draw.add(color);​
 + vis.putAction("​draw",​ draw);
 +</​code>​
 +
 +Finally, we run this [[http://​prefuse.org/​doc/​api/​prefuse/​action/​ActionList.html | ActionList]] and return the [[http://​prefuse.org/​doc/​api/​prefuse/​Display.html | Display]] so that it can be shown in the window.
 +
 +<code java>
 + vis.run("​draw"​);​
 +
 + return display;
 +</​code>​
 +
 +This is the resulting visualization: ​
 +
 +{{:​java:​prefuse-scatterplot1.png?​100}}
 +
 +{{:​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 =====
 +
 +Now, we will add some more features to the scatter plot:
 +
 +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>
 + vis.setRendererFactory(new DefaultRendererFactory(
 + new ShapeRenderer(7)));​
 +</​code>​
 +
 +Second, we add a [[http://​prefuse.org/​doc/​api/​prefuse/​action/​assignment/​DataShapeAction.html | DataShapeAction]],​ so that our nominal parameter is shown as star or ellipse.
 +
 +<code java>
 + int[] palette = { Constants.SHAPE_STAR,​ Constants.SHAPE_ELLIPSE };
 + DataShapeAction shape = new DataShapeAction("​data",​ "​Insult",​ palette);
 +</​code>​
 +
 +Third, the new action needs to be included in the action list. 
 +
 +We also add a [[http://​prefuse.org/​doc/​api/​prefuse/​action/​RepaintAction.html | RepaintAction]],​ just to make sure that the visualization is repainted after the other actions.
 + 
 +<code java>
 + draw.add(shape);​
 + draw.add(new RepaintAction());​
 +</​code>​
 +
 +Fourth, we set the [[http://​prefuse.org/​doc/​api/​prefuse/​Display.html | Display]] to high quality (anti-aliasing),​ and change its size.
 +
 +We also add an empty border, so that visual items at the edge of the visualization are better visible.
 +
 +<code java>
 + display.setHighQuality(true);​
 + display.setSize(700,​ 450);
 +
 + display.setBorder(BorderFactory.createEmptyBorder(15,​ 30, 15, 30));
 +</​code>​
 +
 +Finally, we add a [[http://​prefuse.org/​doc/​api/​prefuse/​controls/​ToolTipControl.html | ToolTipControl]] to show the values of the two numeric parameters.
 +
 +<code java>
 + String[] tooltipparams = { "​NBZ",​ "​BMI"​ };
 + ToolTipControl ttc = new ToolTipControl(tooltipparams);​
 + display.addControlListener(ttc);​
 +</​code>​
 +
 +This is the resulting visualization: ​
 +
 +{{:​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.
  
  
java/prefuse-scatterplot.1249549745.txt · Last modified: 2009/08/11 09:53 (external edit)

alex @ ieg: home about me publications research