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).
#include <Wt/Chart/WCartesianChart.h>
#include <Wt/WContainerWidget.h>
#include <Wt/WStandardItemModel.h>
#include <Wt/WTimer.h>
#include <cmath>
using namespace Wt;
auto container = std::make_unique<WContainerWidget>();
auto model =
std::make_shared<WStandardItemModel>(40, 2);
model->setHeaderData(0, WString("X"));
model->setHeaderData(1, WString("Y = sin(X)"));
for (unsigned i = 0; i < 40; ++i) {
double x = (static_cast<double>(i) - 20) / 4;
model->setData(i, 0, x);
model->setData(i, 1, std::sin(x));
}
/*
* Create the scatter plot.
*/
Chart::WCartesianChart *chart = container->addNew<Chart::WCartesianChart>();
chart->setModel(model); // Set the model.
chart->setXSeriesColumn(0); // Set the column that holds the X data.
chart->setLegendEnabled(true); // Enable the legend.
chart->setType(Chart::ChartType::Scatter); // Set type to ScatterPlot.
// Typically, for mathematical functions, you want the axes to cross
// at the 0 mark:
chart->axis(Chart::Axis::X).setLocation(Chart::AxisValue::Zero);
chart->axis(Chart::Axis::Y).setLocation(Chart::AxisValue::Zero);
// Provide space for the X and Y axis and title.
chart->setPlotAreaPadding(120, Side::Right);
chart->setPlotAreaPadding(40, Side::Top | Side::Bottom);
// Add the curves
auto s = std::make_unique<Chart::WDataSeries>(1, Chart::SeriesType::Curve);
s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
chart->addSeries(std::move(s));
chart->resize(800, 300); // WPaintedWidget must be given explicit size.
chart->setMargin(10, Side::Top | Side::Bottom); // Add margin vertically
chart->setMargin(WLength::Auto, Side::Left | Side::Right); // Center horizontally
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.
#include <Wt/Chart/WAxisSliderWidget.h>
#include <Wt/Chart/WCartesianChart.h>
#include <Wt/Chart/WDataSeries.h>
#include <Wt/WAbstractItemModel.h>
#include <Wt/WAbstractItemView.h>
#include <Wt/WApplication.h>
#include <Wt/WContainerWidget.h>
#include <Wt/WDate.h>
#include <Wt/WEnvironment.h>
#include <Wt/WPaintedWidget.h>
#include <Wt/WItemDelegate.h>
#include <Wt/WShadow.h>
#include <Wt/WStandardItemModel.h>
#include "../../treeview-dragdrop/CsvUtil.h"
auto container = std::make_unique<WContainerWidget>();
auto model
= csvToModel(WApplication::appRoot() + "timeseries.csv");
if (!model)
return std::move(container);
/*
* Parses the first column as dates, to be able to use a date scale
*/
for (int row = 0; row < model->rowCount(); ++row) {
WString s = asString(model->data(row, 0));
WDate date = WDate::fromString(s, "dd/MM/yy");
model->setData(row, 0, date);
}
/*
* Creates the scatter plot.
*/
Chart::WCartesianChart *chart = container->addNew<Chart::WCartesianChart>();
chart->setBackground(WColor(220, 220, 220));
chart->setModel(model);
chart->setXSeriesColumn(0);
chart->setType(Chart::ChartType::Scatter);
chart->axis(Chart::Axis::X).setScale(Chart::AxisScale::Date);
double min = asNumber(model->data(0, 0));
double max = asNumber(model->data(model->rowCount() - 1, 0));
// Set maximum X zoom level to 16x zoom
chart->axis(Chart::Axis::X).setMinimumZoomRange((max - min) / 16.0);
/*
* Add the second and the third column as line series.
*/
{
auto s =
std::make_unique<Chart::WDataSeries>(2, Chart::SeriesType::Line);
s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
chart->addSeries(std::move(s));
}
{
auto s =
std::make_unique<Chart::WDataSeries>(3, Chart::SeriesType::Line);
s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
chart->addSeries(std::move(s));
}
chart->resize(800, 400);
// Enable pan and zoom
chart->setPanEnabled(true);
chart->setZoomEnabled(true);
chart->setMargin(WLength::Auto, Side::Left | Side::Right); // Center horizontally