Programming Basics · Axis Labelling · Batching Chart Updates
Coordinate Conversion Methods · Double Buffering · FastAction
Programming Actions · Programming ChartStyles
Using a Time-axis · Data Sources · Stock Data Sources
Image Filled Bar Charts · Using JCStrings
Controlling the chart in an application program is generally quite straightforward once you are familiar with the programming basics and the object hierarchy. For most JClass Chart objects, all the information needed to program them can be found in the Application Programming Interface (API). In addition, extensive information on how they can be used can be found in the numerous example and demonstration programs provided with JClass Chart.
This chapter covers some basic programming concepts for JClass Chart and also looks at more complex chart programming tasks.
The following topics are covered:
The header and footer are titles used to annotate the chart. The legend shows how each data series is represented on the chart. The chart area contains the charted data and the axes. All four components can be positioned explicitly; by default, they will be laid out by JClass Chart.
Most of the properties of JClass Chart are accessed through JCChartArea. The chart area is responsible for plotting the data. As a result, it is responsible for managing all the X- and Y- axes (JCAxis). JCChartArea performs all the layout necessary to display the graph.
The data in JClass Chart is managed by the JCChart object. JCChart contains an array of ChartDataView instances. Each ChartDataView instance can, in turn, contain one or more ChartDataViewSeries instance. The ChartDataViewSeries objects represent each individual data series, and are constructed as a result of the data contained in the data source. For more information on the data source model in JClass Chart, see the Data Sources section later in this chapter.
The following illustration depicts the high-level objects defined as children of the JClass Chart control:
As can be seen in the above figure, a JCChart object contains two JCTitle objects representing the header and footer, a JCLegend object for the legend, and a JCChartArea object for the ChartArea. The ChartArea is the part of the graph where the actual charting occurs. JCChartArea contains two or more JCAxis objects, meant to represent each axis in the chart. The JCAxis objects are referenced in different collections: one for the X axes and one for the Y axes.
The JCChart object also contains one or more ChartDataView objects. ChartDataView references the data that is being charted. Each ChartDataView can have its own chart type. A ChartDataview object contains one or more ChartDataViewSeries objects. Each ChartDataViewSeries represents one series of points (e.g. one set of bars of the same color).
The ChartDataView object does not "own" the data. The data are owned by the DataSource. The DataSource can be a "stock" data source provided with JClass Chart, or a custom data source. For more details, see Data Sources later in this chapter.
c.getChartArea().setBackground();
This statement navigates the JClass Chart object hierarchy by retrieving the values of successive properties. First, the value of the ChartArea property is retrieved, which is a JCChartArea object. Then, the object's Background property is set.
To access a particular element of a collection, specify the index which uniquely identifies this element. For example, the following code changes the maximum value of the first X axis to 25.1:
c.getChartArea().getAxis(0).setMax(25.1);
Note that the index refers to the first element of a collection. Also, note that by default JCChartArea contains one element in XAxis and one in YAxis.
JCDataCoord dc = c.getDataView(0).coordToDataCoord(10,15);
Details on each method can be found in the API documentation for each class. More information on coordToDataCoord and related methods can be found in the Coordinate Conversion Methods later in this chapter.
If the AnnotationMethod property is set to JCAxis.VALUE_LABELS, the chart places user-provided labels at explicit locations along an axis. The ValueLabels property of JCAxis, which is a ValueLabels collection, supplies this list of strings and their locations. For example, the following code sets value labels at the locations 10, 20 and 30:
JCAxis x=c.getChartArea.getXAxis(0); x.setValueLabel(0, new JCValueLabel(10, 0, "Label")); x.setValueLabel(1, new JCValueLabel(20, 0, "Label 2")); x.setValueLabel(2, new JCValueLabel(30, 0, "Label 3"));
The ValueLabels collection can be indexed either by subscript or by value:
JCValueLabel v1 // this retrieves the label for the second Value-label v1=c.getChartLabelArea().getXAxis(0); getValueLabel(2); // this retrieves the label at chart coordinate 2.0 v1=c.getChartLabelArea().getXAxis(0); getValueLabel(2.0);
If the AnnotationMethod property is set to JCAxis.POINT_LABELS, the chart evenly spaces the points across the axis, and annotates them with a list of strings. This labelling method can only be used on the X-axis. It is most common to associate point labels with the data. This can be done either by using the file format required by one of the stock data sources, or by implementing the getPointLabels() method in a custom data source. For more details, see Data Sources later in this chapter.
The labels can also be supplied by setting the PointLabels property of the ChartDataView object for this chart. For example, the following code specifies labels for each of the three points on the X-axis:
c.getChartArea.getxAxis(0).setAnnotationMethod(JCAxisPOINT_LABELS); ChartDataView cd = c.getDataView(0); cd.setPointLabel(0, "Point 1"); cd.setPointLabel(1, "Point 2"); cd.setPointLabel(2, "Point 3");
If the AnnotationMethod property is set to JCAxis.TIME_LABELS, the chart treats the axis as a time-axis. To define the values to appear on a time-axis, use the following properties:
The IsBatched property is also defined for the ChartDataView object. This IsBatched property is independent of JCChart.IsBatched. It is used to control the update requests sent from the DataSource to the chart.
It is recommended that you batch around the creation or updating of multiple chart labels.
These functions can be accomplished by using coordToDataCoord and dataIndexToCoord respectively, or by using their functional equivalents map and unmap.
Pointp=c.getDataView(0).dataCoordToCoord(5.1,10.2);
This works in the same way as unmap. Note that the screen coordinate positioning is relative to the upper left corner of the JCChart component display.
To convert from pixel coordinates to data coordinates, call coordToDataCoord(). For example, the following converts the pixel coordinates (225, 92) to their equivalent data coordinates:
JCDataCoord cd=c.getDataView(0).coordToDataCoord(225,92);
This works in the same manner as map. coordToDataCoord returns a JCDataCoord object containing the x and y values in the data space.
To determine the pixel coordinates of a given data point, call dataIndexToCoord. For example, the following code obtains the pixel coordinates of the third point in the first data series:
JCDataIndex di=newJCDataIndex(3,c.getDataView(0).getSeries(0)); Point cdc=c.getDataView(0).dataIndexToCoord(di);
To determine the closest data point to a set of pixel coordinates, call coordToDataIndex:
JCDataIndex di=c.getDataView(0).coordToDataIndex(225,92, ChartDataView.PICK_FOCUSXY);
Essentially, these last two examples demonstrate that dataIndexToCoord works in much the same way as pick and unpick. The third argument passed to coordToDataIndex specifies how the nearest series and point value are determined. This argument can be one of ChartDataView.PICK_FOCUSXY, ChartDataView.PICK_FOCUSX or ChartDataView.PICK_FOCUSY. For more information on the pick and unpick methods, see the Using Pick and Unpick section later in this chapter.
JCDataIndex contains the series and point value corresponding to the closest data point, and also returns the distance in pixels between the pixel coordinates and the point. coordToDataIndex returns a JCDataIndex instance.
When double buffering is turned on, every time the chart changes, it will:
When double buffering is turned off, the chart clears the screen image every time a chart is changed (possibly causing a visual flash). Then, it renders the complete image to the visible window (possibly allowing the user to see the chart being drawn piece by piece).
Turning off double buffering can improve the chart's graphing performance and reduce its memory requirements, but it will likely result in a chart display that "flashes" as updates are continually drawn to the screen. By default DoubleBuffer is true.
Using FastAction can greatly improve the performance of a chart display, because relatively more time is needed to draw such things as axis annotations or grid lines than for simply updating the points on a chart. It is designed for use in dynamic chart displays, such as charts that enable the user to perform translation or rotation actions.
The following line of code shows how FastAction can be used in a program:
c.getChartArea().setFastAction(true);
An event trigger has two parts:
Valid actions include EventTrigger.ZOOM, EventTrigger.TRANSLATE, EventTrigger.ROTATE and EventTrigger.DEPTH.
If you are using the JDK 1.1, you can also specify the mouse button using one of the following modifiers:
For example, the following tells JClass Chart to perform a zoom operation when Shift and mouse button 1 are pressed:
c.setTrigger(0,newEventTrigger(Event.SHIFT_MASK, EventTrigger.ZOOM);
c.setTrigger(0,null);
ChartDataView arr = c.getDataView(0); c.getChartArea().setHorizActionAxis(arr.getXAxis()); c.getChartArea().setVertActionAxis(arr.getYAxis());
Note that it is possible to have a null value for an action axis. This means that chart actions like translation do not have any effect in that direction. By default, the HorizActionAxis is set to the default X-axis, and the VertActionAxis is set to the default Y-axis.
ChartStyle is an indexed property of ChartDataView that "owns" the JCChartStyle objects for that data view. It can be manipulated like any other indexed property, as demonstrated in the following example:
arr.setChartStyle(0, new JCChartStyle());
This adds the specified ChartStyle to the indexed property at the specified index. If the ChartStyle is null, the JCChartStyle at the specified point is removed. The following lists some of the other ways ChartStyle can be used:
Normally, you will not need to add or remove JCChartStyle objects from the collection yourself. If a JCChartStyle object already exists when its corresponding series is created, the previously created JCChartStyle object is used to display the data in this series.
By looping through the JCChartStyle indexed property, you can quickly change the behavior of all of the bars, lines or points in a chart. For example, the following code lightens all of the bars in a chart whenever the mouse is clicked:
JCChartStyle[] style=c.getDataView(1).getChartStyle(); for(int:=0; i < styles.length, i++); JCFillStyle fs=styles[i].getFillStyle(); fs.setColor(fg.getColor().brighten());
Many of the most common chart style sub-properties are repeated in JCChartStyle. For example, FillColor is a property of JCChartStyle that corresponds to the Color property of JCFillStyle object. A complete list of repeated properties is provided in the following list:
For example, the following statement sets the starting point to January 15, 1985:
c.getChartArea().getXAxis(0).setTimeBase(newDate(15,0,85));
And the following sets the starting point to the current time:
c.getChartArea().getXAxis(0).setTimeBase(newDate());
JCAxis y; date d.valueToDate(3.0)double val=y.datetoValue(newDate(90,1,0));
To convert a date to a formatted text string, use the JCChartTimeUtil.timeLabel. This method takes a format string and a Java Date object.
ChartDataView and ChartDataViewSeries are used to control how the data is used by JClass Chart. DataSource is a property of ChartDataView that points to the data source. In JClass Chart, the DataSource is an object derived from Chartable, EditableChartable or ChartDataModel.
The Chartable interface contains elements for retrieving data. An object that implements the Chartable interface can be attached to ChartDataView as a data source using the DataSource property. ChartDataView uses its DataSource property to retrieve data for display. Example uses of Chartable can be found throughout the demos. The basic demo (jclass/chart/demos/basic/) contains ArrayData.java and GeneralData.java, two good examples of data sources that use the Chartable interface.
The EditableChartable interface extends the Chartable interface so that it includes methods for setting data in the data source. EditableChartable can be attached to the DataSource property of ChartDataView. In this case, user changes to the data are sent back to the data source. This represents another level of connectivity between JClass Chart and the external data source. A good example can be found in the table demo (jclass/chart/demos/table/) in Data.java.
The ChartDataModel class is an abstract class that extends EditableChartable. In order to make use of ChartDataModel, developers must use it as a base class. The advantage of using ChartDataModel is that the ChartDataModel can use model-view to update JClass Chart. In other words, ChartDataView will be registered as an Observer of ChartDataModel. This allows the external data object to inform JClass Chart of changes to the data.
The model-view aspect of ChartDataModel is handled by the Java utility classes Observer and Observable. ChartDataView inherits from Observer. ChartDataModel inherts from Observable. If a ChartDataModel is attached to the DataSource property of a ChartDataView instance, it will register itself with the ChartDataModel. In the object derived from ChartDataModel (i.e. the external data source object), a call to update() will send a message to ChartDataView.
The details of the messages sent from ChartDataModel to ChartDataView are handled by the ChartDataModelUpdate class. An instance of ChartDataModelUpdate must be constructed by the external data source and passed to the update() method. Possible messages include CHANGE_VALUE, NEW_VALUE, CHANGE_ROW, NEW_ROW. For a complete list of messages, see the API documentation for ChartDataModelUpdate. A good example of an updating data source can be found in the strip chart demonstration program (jclass/chart/demos/stripper/) in StripperData.java.
Note for all of the data sources listed above, a fixed format for file data is used, as is explained in the following section.
InputStreamDataSource uses the readArrayData method, which reads the data from the stream as if it is in array format--one set of X-axis data, multiple sets of Y-axis data.
The data may or may not be transposed. For example, the two following data files are equivalent:
ARRAY 2 4 # 2 series (+x-data) w. 4 points each 1 2 3 4 # x-data 1 2 3 4 # y1 1 4 9 16 # y2 ARRAY 2 4 T 1 1 1 2 2 4 3 3 9 4 4 16
The following are two further examples of equivalent data files that incorporate both name and point labels:
ARRAY 'My Chart' 2 4 'Point Label 1' 'Point Label 2' 'Point Label 3' 'Point Label 4' 'X Axis Data' 1 2 3 4 'Y Axis #1 Data' 1 2 3 4 'Y Axis #2 Data' 1 4 9 16 ARRAY 'MyChart' 2 4 T 'X Data' 'Y Data' 'Y 2 Data' 'Point Label 1' 1 1 1 'Point Label 2' 2 2 4 'Point Label 3' 3 3 9 'Point Label 4' 4 4 16
The other external file formats are all derived from InputStreamDataSource. URLDataSource takes a URL and turns it into a Chartable data source. FileDataSource creates an InputStreamDataSource from a file, and AppletDataSource pulls the data from the DATA <PARAM> tag associated with the specified applet.
For more detailed information on the format used by the stock data sources, see the API documentation for InputStreamDataSource.
The following code fragment shows how Image can be incorporated into a program:
// Set the labels of each series. String seriesLabels[] = {"CD", "Cassette"}; String imageStrings[] = {"cd.gif", "tape.gif"}; ChartDataViewSeries seriesList[] = arr.getSeries(); for (int i = 0; i < arr.getNumSeries(); i++) { if (i < seriesLabels.length) { seriesList[i].setLabel(seriesLabels[i]); if (imageStrings[i] != null) { seriesList[i].getStyle().getFillStyle().setImage( this, imageStrings[i]); } } }
The effects can be seen in the ImageBar demonstration program, (in the jclass/chart/demos/imagebar/ directory), which comes with JClass Chart.
The image is clipped at the point of the highest value indicated for the bar chart.
Image only tiles the image along a single axis. For example, if the bars were widened in the above illustration, it would still tile along the vertical Y-axis only, and would not fill in the image across the horizontal X-axis. This same principle applies (though along different axes) of the bar chart is rotated 90 degree.
Note: Image can only be used with the image formats that can be used in Java.
For example, the following line of code adds a number of JCStrings within a chart (in this case, the Exploder demonstration included with JClass Chart):
c.getHeader().getLabel().setText("[IMAGE=yuri.gif][FONT=TimesRoman-italic-20] Yuri Presents ...[NEWLINE][VERT_SPACE=5][FONT=TimesRoman-bold-16]The Exploding Pie Slice Demo [NEWLINE][FONT=TimesRoman-plain-16]Pick a slice and watch it explode![NEWLINE]", true);
For more information on the types of JCStrings available in JClass Chart, see "JCString Properties".
Consider the following code listing (the code that comprises the DrillDown demonstration program that comes with JClass Chart, in jclass/chart/demo/drilldown/) in which demonstrates how pick can be used to "drill down" to reveal more information:
package jclass.chart.demos.drilldown; import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Event; import java.awt.GridLayout; import jclass.bwt.BWTEnum; import jclass.chart.ChartDataView; import jclass.chart.ChartDataViewSeries; import jclass.chart.EventTrigger; import jclass.chart.JCAxis; import jclass.chart.JCPickListener; import jclass.chart.JCPickEvent; import jclass.chart.JCChartStyle; import jclass.chart.JCChart; import jclass.chart.JCChartLabel; import jclass.chart.JCDataIndex; import jclass.chart.JCLegend; import jclass.chart.JCChartArea; import jclass.chart.demos.DemoFrame; /** This applet demonstrates using pick to drill down to more refined data **/ public class DrillDown extends Applet implements JCPickListener { Data d = null; JCChart c = null; public void init() { setLayout(new BorderLayout(10,10)); d = new Data(); Color Turquoise = new Color(64,224,208); Color DarkTurquoise = new Color(0x00,0xce,0xd1); //Color DarkerTurquoise = new Color(0x00,0x9c,0x9e); c = new JCChart(this, "chart"); c.setTrigger(0, new EventTrigger(0, EventTrigger.PICK)); c.setBackground(DarkTurquoise); c.getChartArea().getInterior().setBackground(Turquoise); c.getChartArea().setBorderWidth(4); c.getChartArea().setBorderType(BWTEnum.SHADOW_ETCHED_IN); c.getHeader().setBackground(Turquoise); c.getHeader().getLabel().setText("[FONT=TimesRoman-bold-16]Drill Down Demo\n[FONT=TimesRoman-plain-16]Independent Comic Book Sales 1996", true); c.getHeader().setBorderType(BWTEnum.SHADOW_ETCHED_OUT); c.getHeader().setIsShowing(true); c.getLegend().setIsShowing(true); c.getLegend().setBackground(Turquoise); c.getLegend().setBorderType(BWTEnum.SHADOW_IN); c.setIsBatched(false); JCChartStyle.resetDefaults(); c.getDataView(0).setDataSource(d); c.getDataView(0).setChartType(JCChart.BAR); c.getDataView(0).setHoleValue(-1000); c.getFooter().setIsShowing(true); c.getFooter().getLabel().setText("[FONT=TimesRoman-italic-12]Drill Down -> Mouse Down on Bar or Legend\nDrill Up -> Mouse Down on Other Area of Graph", true); c.getFooter().setAdjust(JCChartLabel.LEFT); c.getChartArea().setDepth(10); c.getChartArea().setElevation(20); c.getChartArea().setRotation(20); // Set colors for each data series Color colors[] = {Color.red, Color.blue, Color.white, Color.magenta, Color.green, Color.cyan, Color.orange, Color.yellow}; ChartDataView arr = c.getDataView(0); ChartDataViewSeries seriesList[] = arr.getSeries(); for (int i = 0; i < arr.getNumSeries(); i++) { seriesList[i].getStyle().setFillColor(colors[i]); } JCAxis yaxis = arr.getYAxis(); // Set up pick and rotate trigger c.setTrigger(0, new EventTrigger(0, EventTrigger.PICK)); c.setTrigger(0, new EventTrigger(Event.SHIFT_MASK, EventTrigger.ROTATE)); // Add listener for pick events c.addPickListener(this); add("Center",c); } /** Pick event listener. Upon receipt of a pick event, it either drills * up or down to more general or refined data. */ public void pick(JCPickEvent e) { JCDataIndex di = e.getPickResult(); // If clicked on bar or legend item, drill down. If clicked on // any other area of chart, drill up. if (di != null) { Object obj = di.getObject(); ChartDataView vw = di.getDataView(); int srs = di.getSeriesIndex(); int pt = di.getPoint(); int dist = di.getDistance(); if (vw != null && srs != -1) { if (srs >= 0) { if ((obj instanceof JCLegend) || (obj instanceof JCChartArea && dist == 0)) d.downLevel(srs); else d.upLevel(); } } else { d.upLevel(); } } else { d.upLevel(); } } public static void main(String args[]) { DemoFrame f = new DemoFrame("Basic Drilldown example"); DrillDown tc = new DrillDown(); tc.init(); f.setLayout(new GridLayout(1,1)); f.add(tc); f.pack(); f.show(); f.resize(500,400); } }
When compiled and run, the DrillDown.class program displays the following:
When a bar or legend within this chart is clicked by the user, the program "drills down" to reveal more refined data comprising that bar. If an area outside of the bars is clicked upon, then the program "drills up" to reveal more general data.
pick is key to this program, determining the way the program interacts with the user. pick requires an event trigger and listener to work, as the following code fragment shows:
c.setTrigger(0, new EventTrigger(0, EventTrigger.PICK)); c.setTrigger(0, new EventTrigger(Event.SHIFT_MASK, EventTrigger.ROTATE)); c.addPickListener(this); add("Center",c); } public void pick(JCPickEvent e) { JCDataIndex di = e.getPickResult();
When a user clicks in the DrillDown demonstration program, the event is triggered, the x,y coordinates are passed along to the pick event listener, which in turn takes the information and performs the indicated action. The pick method returns a JCDataIndex which encapsulates the point index and data series of the selected point.
This is a particularly useful method to use within programs that display typical bar charts. In most cases it is more desirable to know which bar the user is over than which bar the user is closest to when the user clicks their mouse over a chart.
For example, a user may click over a relatively small bar in a bar chart, with the intension of raising the value of the bar displayed. If an adjacent bar in the chart is closer to the area of the mouse click along the Y-axis than the X-axis, then the adjacent bar could be selected instead of the intended target bar.
To overcome this, use PickFocus and select the axis whose values are to be reported back to the program. For example, the following line of code sets PickFocus to only report the x coordinate of a pick event:
arr.setPickFocus(ChartDataView.PICK_FOCUS_X);