Scatter Plot

The Charting library

See Category chart for an introduction to the charting library.

What is a scatter plot?

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).

Scatter plot of a time series

The table below shows an extract from historical financial market data. The scatter plot shows the second and the third column as line series.

Example
Date
AMSTEOE
DAXINDX
FTSE100
HNGKNGI
JAPDOWA
SNGALLS
SPCOMP
06/01/1986
275.76
1425.56
1424.1
1796.59
13053.8
233.63
210.65
07/01/1986
275.43
1428.54
1415.2
1815.53
12991.2
237.37
213.8
08/01/1986
278.76
1474.24
1404.2
1826.84
13056.4
240.99
207.97
09/01/1986
272.29
1461.18
1379.6
1798.51
13034.2
239.68
206.11
10/01/1986
272.89
1448.97
1394.5
1807.94
12998.2
238.95
205.96
1 of 100
source
  void ScatterPlotData() {
    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);
    }
    WTableView table = new WTableView((WContainerWidget) container);
    table.setModel(model);
    table.setSortingEnabled(false);
    table.setColumnResizeEnabled(true);
    table.setAlternatingRowColors(true);
    table.setColumnAlignment(0, AlignmentFlag.Center);
    table.setHeaderAlignment(0, EnumSet.of(AlignmentFlag.Center));
    table.setRowHeight(new WLength(28));
    table.setHeaderHeight(new WLength(28));
    table.setColumnWidth(0, new WLength(80));
    for (int column = 1; column < model.getColumnCount(); ++column) {
      table.setColumnWidth(column, new WLength(90));
    }
    table.resize(new WLength(783), new WLength(200));
    WItemDelegate delegate = new WItemDelegate();
    delegate.setTextFormat("%.1f");
    table.setItemDelegate(delegate);
    table.setItemDelegateForColumn(0, new WItemDelegate());
    WCartesianChart chart = new WCartesianChart((WContainerWidget) container);
    chart.setBackground(new WBrush(new WColor(220, 220, 220)));
    chart.setModel(model);
    chart.setXSeriesColumn(0);
    chart.setLegendEnabled(true);
    chart.setType(ChartType.Scatter);
    chart.getAxis(Axis.X).setScale(AxisScale.Date);
    chart.setPlotAreaPadding(40, EnumSet.of(Side.Left, Side.Top, Side.Bottom));
    chart.setPlotAreaPadding(120, EnumSet.of(Side.Right));
    for (int i = 2; i < 4; ++i) {
      WDataSeries s = new WDataSeries(i, 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.setMargin(WLength.Auto, EnumSet.of(Side.Left, Side.Right));
  }

Top

Scatter plot of a function

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).

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

Remark

Missing data in a model series Y values is interpreted as a break. For curve-like series, this breaks the curve (or line).

Top

Interactive features

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.

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

Top