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));
}