Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Plotting with Draw2D and SWT with the Java platform

Write your own platform-independent code to create X-Y plots, bar charts, and more

Indiver Dwivedi (dindiver@in.ibm.com), Senior Software Engineer, IBM India
Indiver Dwivedi
Indiver Dwivedi is a senior software engineer working in the Pune Lab for IBM India Software Labs. He joined IBM in 2000, and has worked on projects like Lotus® SmartSuite and an IBM data access tool for IBM Workplace. He has four years of programming experience in Ladder Logic, C, and C++. He has programmed logic controllers, and PC-based supervisory control and data acquisition (SCADA) software for industrial automation and control (IAC) systems. He has an interest in topics related to device communications and charting/plotting historical data stored by data acquisition systems under the IAC domain. He is currently working with a team in the IBM Pune Lab and contributing to the IBM Workplace Designer project.

Summary:  Plotting charts or graphs with the Java™ platform has always captured developers' interest. Traditionally, Java language developers have plotted charts with java.awt.Graphics or Java 2D API. Some may have even explored ready-made open source kits like JSci to draw charts. Many available options restrict you to AWT or Swing, however. To minimize dependency on third-party tool kits or simplify the basics of drawing charts, consider using Draw2D and writing your own code for charting or plotting.

Date:  24 May 2005
Level:  Introductory
Also available in:   Russian

Activity:  22820 views
Comments:  

In brief: Draw2D

Draw2D is a lightweight widget system hosted on a SWT Composite. A Draw2D instance consists of a SWT Composite, a lightweight system, and its contents' figures. Figures are the building blocks for Draw2D. Complete details on the Draw2D API come with Eclipse help in the Draw2D Developer's Guide. Because this article is not intended to teach Draw2D, for simplicity's sake, it is enough that you understand that the Draw2D API helps you draw figures on an SWT Canvas. You can use some standard shape figures, such as Ellipse, Polyline, RectangleFigure, and Triangle, directly; or you can extend them to create your own figures. Also, some container figures like Panel can act as an overall container for all child figures.

Draw2D has two important packages: org.eclipse.draw2d.geometry and org.eclipse.draw2d.graph, which I use in this article. The org.eclipse.draw2d.geometry package has useful classes, like Rectangle, Point, and PointList, which are self-explanatory. Developers have probably not explored the other package, org.eclipse.draw2d.graph, much. This package provides some important classes, such as DirectedGraph, Node, Edge, NodeList, and EdgeList, which help in creating a graph.

In this article, I explain how to use Draw2D to write code that can help you visualize your data graphically. I start by describing the technique to scale a data value lying in one range (for example, 0 to 2048) to its equivalent data value lying in another range (for example, 0 to 100). Then I illustrate how to chart out an X-Y plot of any number of series, each series containing a set of data elements. You can easily plot other chart types, such as pie charts and bar graphs, after you learn the concepts in this article.


Step-by-step chart-plotting process

Step 1: What do you want to plot?

Obviously, you want to plot data from some data source. So, you need the data you want to visualize in a graphical format. For brevity, instead of reading data from an XML file or some other data source, I generated data in a simple function named dataGenerator that uses a for(;;) loop and returns the generated values as an array list.


Listing 1. Generate some data

	private ArrayList dataGenerator() {
		double series1[] = new double[5]; 
	    	for(int i=0; i<series1.length; i++)
	    		series1[i] = (i*10) + 10; // a linear 
	    		series containing	10,20,30,40,50
	    	
	    	double series2[] = new double[9]; 
	    	series2[0] = 20; series2[1] = 150; series2[2] = 5;
	    	series2[3] = 90; series2[4] = 35;  series2[5] = 20;
                            series2[6] = 150; series2[7] = 5; series2[8] = 45;
	    	
    		double series3[] = new double[7]; 
    		for(int i=0; i<series3.length; i++)
    			series3[i] = (i*20) + 15;
   		
    		seriesData.add(series1);
    		seriesData.add(series2);
    		seriesData.add(series3);	
	                return seriesData;
	}

Step 2: Scaling technique -- Generate X and Y coordinates from given data

New terms

FigureCanvas
A FigureCanvas in Draw2D is an extension of the SWT Canvas. A FigureCanvas can contain Draw2D figures.
Panel
A Panel is a general-purpose container figure in Draw2D that can contain child figures. You can add a lot of figures to a single Panel figure, then provide this single Panel figure to a FigureCanvas.
DirectedGraph
A DirectedGraph is a 2-D graph that has a finite number of Nodes with each Node located at some Point and the adjacent Nodes joined (or connected) to each other by Edges.

When you want to plot points on a 2-D plane, you must find the X and Y coordinates of each point. The magic of plotting graphs lies in being able to scale a given data value from one range to another; that is, given a set of values like {10,20,30}, you should be able to decide exactly which points (X and Y coordinates) on a 2-D plane represent the data values 10, 20, and 30.

Always plot on a finite scale. In other words, you can plot any number of points in the same finite area. Because the area is fixed, you can always find the span (length) of the X axis and the span (height) of the Y axis. Knowing the span of the X and Y axes is only one part of the equation. The other part is to find the range of data values and calculate a coordinate of each value by finding its equivalent value in the new range.

Calculating X and Y coordinates
X coordinates: The X coordinate is a point's horizontal distance from the origin. Compute the horizontal distances of all points in a set simply by counting the number of elements and dividing the span of the X axis into n segments, where n equals the number of elements in a given set. This division yields the length of each segment. The first point in a set lies at a distance equal to the segment length. Each successive point lies at a distance of the segment length plus the distance of the previous point from the origin.

For example, given a set {10,20,30,40}, you immediately know that you want to plot four points because the set contains four elements. Therefore, the X-axis span should be divided into four equal segments of length=span/4. So, if the span of the X axis is 800, the segment length will be 800/4, which is 200. The X coordinate of the first element (10) will be 200, the second element (20) will be 400, and so on.


Listing 2. Calculate X coordinates

private int[] getXCoordinates(ArrayList seriesData){
	int xSpan = (int)GraFixConstants.xSpan;
           int longestSeries = Utilities.getLongestSeries(seriesData);
           int numSegments = 
           ((double[])seriesData.get(longestSeries)).length;        
           int sectionWidth = 
           (int)xSpan / numSegments; //want to divide span of xAxis
        
           int xPositions[] = 
           new int[numSegments]; // will contain X-coordinate of all dots.
	for(int i=0; i<numSegments; i++){
		xPositions[i]= 
		(i+1)*sectionWidth;//dots spaced at distance of sectionWidth			
	}
	return xPositions;
}

Y coordinates: The Y coordinate is a point's vertical distance from the origin. Computing Y coordinates involves scaling a value from one range to another. For example, given the same set {10,20,30,40}, you can see that the data range is 0 to 40, and the new range is the span (height) of the Y axis. Assuming that the height of the Y axis is 400, the equivalent height of the first element (10) will be 100, the second element (20) will be 200, and so on.

You can better understand scaling a value from one range to another by the following example: Assuming that one range spans from 0 to 2048, and you want to scale any value from this range (for example, 1024) into another range that spans from 0 to 100, you can immediately see that the equivalent scaled value is 50. The three-line arithmetic behind the scaling follows:

line 1---> 2048 / 1024 equals 2.
line 2---> 100 - 0 equals 100. 
line 3---> 100 / 2 equals 50, which is the desired scaled value.

Step 3: Where do you want to plot?

You need some area on which you want to plot. Create your own view by extending an Eclipse ViewPart and using the SWT Composite. Alternatively, you can also use a SWT shell invoked from inside a main() function.

When you extend an Eclipse ViewPart, you must implement at least two functions: createPartControl(Composite parent) and setFocus(). The function createPartControl(Composite parent) is called automatically when your view has to be painted on the screen. Your interest is only in the SWT Composite thus received. Pass it on to a class you will code to plot the graphs.


Listing 3. Using an Eclipse ViewPart to plot

public class MainGraFixView extends ViewPart{
	public void createPartControl(Composite parent) {
		
		//create or get data in an arraylist
		ArrayList seriesData = dataGenerator();

		//instantiate a plotter, and provide data to it.
		DirectedGraphXYPlotter dgXYGraph = new DirectedGraphXYPlotter(parent);
		dgXYGraph.setData(seriesData);
		dgXYGraph.plot(); //ask it to plot 		

	}
	public void setFocus() {	
	}
}

Step 4: What kind of graph do you need?

Once you have the data and the area you want to plot, you must decide which kind of visualization you need. In this article, I demonstrate how to write code to create an X-Y plot or line graph. Once you understand the technique behind an X-Y plot, you should be able to chart out other plotters, like bar and pie. To understand more about the X-Y plot, look at the DirectedGraphXYPlotter class I wrote for this article (see \src\GraFix\Plotters\DirectedGraphXYPlotter.java in the attached source code).

Step 5: Create your own X-Y plotter

An X-Y plotter should be able to plot any number of series lines on a 2-D plane. Each series line should graphically show the position of each point in the series with reference to the X and Y reference lines. Each point should be connected to the next in series by a line. You can create such a plotter by using Draw2D figures that represent a point and a line. For example, to represent a point, I created a Dot figure by extending an Ellipse figure and used a PolylineConnection figure to represent the connecting lines.

The DirectedGraphXYPlotter class has only two public functions: setData(ArrayList seriesData) and plot(). The function setData(ArrayList seriesData) accepts data (see Step 1) you want to visualize graphically, and the plot() function starts plotting the graph.

Once the plot() function is called, you need to take the following steps, in sequence:

  1. Take an SWT Composite and put a FigureCanvas on it. Then put a general-purpose container figure like Panel on the canvas.
  2. Count the number of series to be plotted and populate as many NodeLists and EdgeLists as required to create DirectedGraphs.
  3. Draw the X axis and the Y axis on the Panel figure. (See XRulerBar.java and YRulerBar.java under \src\GraFix\Figure in the attached source code.)
  4. Create a number of DirectedGraphs equal to the number of series to plot.
  5. Draw dots and connecting wires on the Panel figure while taking the graph data from the DirectedGraphs created in Step d.
  6. Finally, set the contents of the canvas by providing the Panel figure, which contains all the dots and connecting lines you have prepared so far.

In the code below:

  • Lines 6-11 correspond to Step a above.
  • Line 14, the populateNodesAndEdges() function, corresponds to Step b above.
  • Line 16, the drawAxis() function, corresponds to Step c above.
  • Lines 17, 18, and 19 correspond to Steps d and e above.
  • Line 20 corresponds to Step f above.

Listing 4. The plot() function

1.	public void plot(){
2.	    //if no place to plot, or no data to plot, return.
3.	    if(null==_parent || null==_seriesData)
4.	        return;
5.
6.	    Composite composite = new Composite(_parent, SWT.BORDER);
7.	    composite.setLayout(new FillLayout());
8.	    FigureCanvas canvas = new FigureCanvas(composite);
9.	    
10.	    Panel contents = new Panel();//A Panel is a general purpose container figure
11.	    contents.setLayoutManager(new XYLayout());
12.	    initializeSpan(contents.getClientArea());
13.	    
14.	    populateNodesAndEdges();	    
15.	    
16.	    drawAxis(contents);
17.	    for(int i=0; i<_numSeries; i++){
18.	    	drawDotsAndConnections(contents,getDirectedGraph(i)); // 
draw points & connecting wires
19.	    }
20.	    canvas.setContents(contents);
21.	}

Two important internal functions called by plot() help in plotting points: populateNodesAndEdges() and drawDotsAndConnections(). Before you discover exactly what these functions do, let's look at a DirectedGraph.

What is a DirectedGraph? To plot a graph in Draw2D, you must first create a graph virtually that will define the points and lines to be plotted. Once you have created this graph, you can use it later to actually start drawing figures on a canvas. You can visualize a DirectedGraph as a 2-D graph that has a finite number of Nodes, with each Node located at some Point, and the adjacent Nodes joined (or connected) to each other by Edges.

You can understand the crux of creating a DirectedGraph with the following lines of code. First, create a list of Nodes and a list of Edges. Next, create a new DirectedGraph and set its members (Nodes and Edges) by the NodeList and EdgeList just created. Now use a GraphVisitor to visit this DirectedGraph. To make things simple, the package org.eclipse.draw2d.internal.graph has many implementations of GraphVisitor that already have specific algorithms to visit a graph.

So, the sample code to make a DirectedGraph turns out something like this:


Listing 5. A sample DirectedGraph


//This is a sample, you will need to add actual Node(s) to this NodeList.
NodeList nodes = new NodeList(); //create a list of nodes.
//This is a sample, you will need to add actual Edge(s) to this EdgeList.
EdgeList edges = new EdgeList(); //create a list of edges. 
DirectedGraph graph = new DirectedGraph();
graph.nodes = nodes;
graph.edges = edges;
new BreakCycles().visit(graph);//ask BreakCycles to visit the graph.
//now our "graph" is ready to be used.

Now that you know that a DirectedGraph contains a list of Nodes, where each Node may contain some data and also store its X and Y coordinates, and a list of Edges, where each Edge knows it has a Node at both its ends, you can use this information to plot graphs with the following technique, which involves two parts:

Part A -- Populate Nodes and Edges by:

  • Creating a NodeList having one Node per element in a set. For example, the set {10,20,30,40} requires four Nodes.
  • Finding the X and Y coordinates for each element and storing them in node.x and node.y member variables.
  • Creating an EdgeList having n-1 Edges, where n is the number of elements in a set. For example, the set {10,20,30,40} requires three Edges.
  • Associating a Node to the left and right of each Edge and setting the edge.start and edge.end member variables appropriately.

Part B -- Draw figures that represent Nodes and Edges by:

  • Drawing a Dot figure to represent each Node.
  • Drawing a PolylineConnection figure to represent each Edge.
  • Anchoring every PolylineConnection figure to a Dot figure on the left and right.

Now, coming back to the workings of the internal functions:

  • The function populateNodesAndEdges() implements Part A of the technique, and drawDotsAndConnections() implements Part B of the technique.
  • The function populateNodesAndEdges() counts how many series have to be plotted. It creates one NodeList and one EdgeList for each series.
  • Every NodeList contains a list of Nodes for a particular series. Every Node keeps the information on where X and Y coordinates should be plotted. The functions getXCoordinates() and getYCoordinates() retrieve the value of the X and Y coordinates, respectively. These functions also internally scale data values from one range to another, using the same algorithm in Step 2.
  • Every EdgeList contains a list of Edges for a particular series. Every Edge has a Node on its left and another Node on its right.

Listing 6. The populateNodesAndEdges() function

private void populateNodesAndEdges(){
	    
    _seriesScaledValues = new ArrayList(getScaledValues(_seriesData));
		_nodeLists = new ArrayList();
		_edgeLists = new ArrayList();
		
		for(int i=0; i<_numSeries; i++){
			_nodeLists.add(new NodeList());// one NodeList per series.
			_edgeLists.add(new EdgeList());// one EdgeList per series.
		}
		//populate all NodeLists with the Nodes. 
		for(int i=0; i<_numSeries; i++){//for each series
			double data[] = (double[])_seriesData.get(i);//get the series
			int xCoOrds[] = getXCoordinates(_seriesData);
			int yCoOrds[] = getYCoordinates(i, data);
			//each NodeList will have as many Nodes as number of points in a series
			for(int j=0; j<data.length; j++){
				Double doubleValue = new Double(data[j]);
				Node node = new Node(doubleValue);
				node.x = xCoOrds[j];
				node.y = yCoOrds[j];				
				((NodeList)_nodeLists.get(i)).add(node);
			}					
		}
		//populate all EdgeLists with the Edges. 
		for(int i=0; i<_numSeries; i++){
			NodeList nodes = (NodeList)_nodeLists.get(i);			
			for(int j=0; j<nodes.size()-1; j++){
				Node leftNode = nodes.getNode(j);
				Node rightNode = nodes.getNode(j+1);
				Edge edge = new Edge(leftNode,rightNode);
				edge.start = new Point(leftNode.x, leftNode.y); 
				edge.end = new Point(rightNode.x, rightNode.y);
				((EdgeList)_edgeLists.get(i)).add(edge);
			}	
		}
		int breakpoint = 0;
	}

Once the function populateNodesAndEdges() has done its job of creating NodeLists and EdgeLists for all the series to be plotted, the other function drawDotsAndConnections() starts drawing a Dot figure for each Node and a PolylineConnection figure for each Edge.


Listing 7. The drawDotsAndConnections(), drawNode(), and drawEdge() functions

	private void drawDotsAndConnections(IFigure contents, DirectedGraph graph){
	    for (int i = 0; i < graph.nodes.size(); i++) {
	        Node node = graph.nodes.getNode(i);
	        drawNode(contents, node);
	    }
	    for (int i = 0; i < graph.edges.size(); i++) {
	        Edge edge = graph.edges.getEdge(i);
	        drawEdge(contents, edge);
	    }		
	}
	
private void drawNode(IFigure contents, Node node){ Dot dotFigure = new Dot(); node.data = dotFigure; int xPos = node.x; int yPos = node.y; contents.add(dotFigure); contents.setConstraint(dotFigure, new Rectangle(xPos,yPos,-1,-1)); }
private void drawEdge(IFigure contents, Edge edge){ PolylineConnection wireFigure = new PolylineConnection(); //edge.source is the Node to the left of this edge EllipseAnchor sourceAnchor = new EllipseAnchor((Dot)edge.source.data); //edge.target is the Node to the right of this edge EllipseAnchor targetAnchor = new EllipseAnchor((Dot)edge.target.data); wireFigure.setSourceAnchor(sourceAnchor); wireFigure.setTargetAnchor(targetAnchor); contents.add(wireFigure); }


Plotted graph results
Sample XY Plot

Summary

If you're drawn to graphically representing data, Draw2D is a good tool. Using Draw2D to write your own Java language code to plot charts and graphs can help you concentrate on scaling and plotting code, leaving the rendering/painting related jobs to Draw2D and SWT. You can also control the appearance of your graphs by using Draw2D figures of your choice. Draw2D simplifies the basics of drawing charts and graphs, and minimizes your dependence on third-party tool kits.



Download

DescriptionNameSizeDownload method
Unzip and open this plug-in as an Eclipse projectGraFix.zip24KB HTTP

Information about download methods


Resources

About the author

Indiver Dwivedi

Indiver Dwivedi is a senior software engineer working in the Pune Lab for IBM India Software Labs. He joined IBM in 2000, and has worked on projects like Lotus® SmartSuite and an IBM data access tool for IBM Workplace. He has four years of programming experience in Ladder Logic, C, and C++. He has programmed logic controllers, and PC-based supervisory control and data acquisition (SCADA) software for industrial automation and control (IAC) systems. He has an interest in topics related to device communications and charting/plotting historical data stored by data acquisition systems under the IAC domain. He is currently working with a team in the IBM Pune Lab and contributing to the IBM Workplace Designer project.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source, Java technology
ArticleID=83833
ArticleTitle=Plotting with Draw2D and SWT with the Java platform
publish-date=05242005
author1-email=dindiver@in.ibm.com
author1-email-cc=