See Category chart for an introduction to the charting library.
A scatter plot is very much like a category chart, but uses numerical data on the X axis. By default, these numerical data are mapped linearly on the X axis, but may also be log transformed (This can also be configured for the Y axes). In addition, there is special support for displaying date series, by means of smart heuristics for choosing the labels on the X axis.
In a ScatterPlot, the X series data are interpreted as numbers on a
linear scale. The scale for the X axis defaults to a
LinearScale, but this may be changed to a
DateScale when the X series contains dates (of type
WDate) to create a time series chart, or to a
LogScale. A ScatterPlot supports the same types of data
series as a CategoryChart, but does not support stacking. In a scatter
plot, the X series do not need to be ordered in increasing values, and it
may be set differently for each data series using
WDataSeries.setXSeriesColumn(int modelColumn).
The table below shows an extract from historical financial market data. The scatter plot shows the second and the third column as line series.
Below we plot a single sine curve. We use 'curve' data series, which creates a smooth spline curve that interpolates the data points. As is typical when showing mathematical functions, we let the axes cross each other at the origin (0, 0).
void ScatterPlotCurve() {
WContainerWidget container = new WContainerWidget();
WStandardItemModel model = new WStandardItemModel(40, 2);
model.setHeaderData(0, new WString("X"));
model.setHeaderData(1, new WString("Y = sin(X)"));
for (int i = 0; i < 40; ++i) {
double x = ((double) i - 20) / 4;
model.setData(i, 0, x);
model.setData(i, 1, Math.sin(x));
}
WCartesianChart chart = new WCartesianChart((WContainerWidget) container);
chart.setModel(model);
chart.setXSeriesColumn(0);
chart.setLegendEnabled(true);
chart.setType(ChartType.Scatter);
chart.getAxis(Axis.X).setLocation(AxisValue.Zero);
chart.getAxis(Axis.Y).setLocation(AxisValue.Zero);
chart.setPlotAreaPadding(120, EnumSet.of(Side.Right));
chart.setPlotAreaPadding(40, EnumSet.of(Side.Top, Side.Bottom));
WDataSeries s = new WDataSeries(1, SeriesType.Curve);
s.setShadow(new WShadow(3, 3, new WColor(0, 0, 0, 127), 3));
chart.addSeries(s);
chart.resize(new WLength(800), new WLength(300));
chart.setMargin(new WLength(10), EnumSet.of(Side.Top, Side.Bottom));
chart.setMargin(WLength.Auto, EnumSet.of(Side.Left, Side.Right));
}
Missing data in a model series Y values is interpreted as a break. For curve-like series, this breaks the curve (or line).
WCartesianChart supports some forms of interaction that do not require a server round-trip. You can zoom in on the chart below using ctrl+scroll, or with a pinch movement, and pan it with the scroll-wheel, click and drag, or touch and drag.
void ScatterPlotInteractive() {
WContainerWidget container = new WContainerWidget();
WStandardItemModel model = CsvUtil.csvToModel("" + "timeseries.csv");
if (!(model != null)) {
return container;
}
for (int row = 0; row < model.getRowCount(); ++row) {
WString s = StringUtils.asString(model.getData(row, 0));
WDate date = WDate.fromString(s.toString(), "dd/MM/yy");
model.setData(row, 0, date);
}
WCartesianChart chart = new WCartesianChart((WContainerWidget) container);
chart.setBackground(new WBrush(new WColor(220, 220, 220)));
chart.setModel(model);
chart.setXSeriesColumn(0);
chart.setType(ChartType.Scatter);
chart.getAxis(Axis.X).setScale(AxisScale.Date);
double min = StringUtils.asNumber(model.getData(0, 0));
double max = StringUtils.asNumber(model.getData(model.getRowCount() - 1, 0));
chart.getAxis(Axis.X).setMinimumZoomRange((max - min) / 16.0);
{
WDataSeries s = new WDataSeries(2, SeriesType.Line);
s.setShadow(new WShadow(3, 3, new WColor(0, 0, 0, 127), 3));
chart.addSeries(s);
}
{
WDataSeries s = new WDataSeries(3, SeriesType.Line);
s.setShadow(new WShadow(3, 3, new WColor(0, 0, 0, 127), 3));
chart.addSeries(s);
}
chart.resize(new WLength(800), new WLength(400));
chart.setPanEnabled(true);
chart.setZoomEnabled(true);
chart.setMargin(WLength.Auto, EnumSet.of(Side.Left, Side.Right));
}