From 613e7ebf39faa394aaaa03abc797aba408f362ee Mon Sep 17 00:00:00 2001 From: Mark Liversedge Date: Thu, 19 Dec 2013 11:48:55 +0000 Subject: [PATCH] LTM chart user selectable trend lines .. now allows the user to select the type of trend line for a curve, only two options at present; * linear (linear regress) * quadratic (quadratic lsm) --- src/LTMPlot.cpp | 110 +++++++++++++++++++++++++++++++------------- src/LTMSettings.cpp | 14 +++++- src/LTMSettings.h | 3 +- src/LTMTool.cpp | 14 +++++- src/LTMTool.h | 4 +- 5 files changed, 106 insertions(+), 39 deletions(-) diff --git a/src/LTMPlot.cpp b/src/LTMPlot.cpp index 0be912415..bd646ae3e 100644 --- a/src/LTMPlot.cpp +++ b/src/LTMPlot.cpp @@ -529,48 +529,92 @@ LTMPlot::setData(LTMSettings *set) } // trend - clone the data for the curve and add a curvefitted - // curve with no symbols and use a dashed pen - // need more than 2 points for a trend line - if (metricDetail.trend == true && count > 2) { + if (metricDetail.trendtype) { - QString trendName = QString(tr("%1 trend")).arg(metricDetail.uname); - QString trendSymbol = QString("%1_trend") - .arg(metricDetail.type == METRIC_BEST ? + // linear regress + if (metricDetail.trendtype == 1 && count > 2) { + + // override class variable as doing it temporarily for trend line only + double maxX = 0.5 + groupForDate(settings->end.date(), settings->groupBy) - + groupForDate(settings->start.date(), settings->groupBy); + + QString trendName = QString(tr("%1 trend")).arg(metricDetail.uname); + QString trendSymbol = QString("%1_trend") + .arg(metricDetail.type == METRIC_BEST ? metricDetail.bestSymbol : metricDetail.symbol); - QwtPlotCurve *trend = new QwtPlotCurve(trendName); - // cosmetics - QPen cpen = QPen(metricDetail.penColor.darker(200)); - cpen.setWidth(2); // double thickness for trend lines - cpen.setStyle(Qt::SolidLine); - trend->setPen(cpen); - if (appsettings->value(this, GC_ANTIALIAS, false).toBool()==true) - trend->setRenderHint(QwtPlotItem::RenderAntialiased); - trend->setBaseline(0); - trend->setYAxis(axisid); - trend->setStyle(QwtPlotCurve::Lines); + QwtPlotCurve *trend = new QwtPlotCurve(trendName); - // perform quadratic curve fit to data - LTMTrend2 regress(xdata.data(), ydata.data(), count); + // cosmetics + QPen cpen = QPen(metricDetail.penColor.darker(200)); + cpen.setWidth(2); // double thickness for trend lines + cpen.setStyle(Qt::SolidLine); + trend->setPen(cpen); + if (appsettings->value(this, GC_ANTIALIAS, false).toBool()==true) + trend->setRenderHint(QwtPlotItem::RenderAntialiased); + trend->setBaseline(0); + trend->setYAxis(axisid); + trend->setStyle(QwtPlotCurve::Lines); - // override class variable as doing it temporarily for trend line only - double maxX = 0.5 + groupForDate(settings->end.date(), settings->groupBy) - - groupForDate(settings->start.date(), settings->groupBy); + // perform linear regression + LTMTrend regress(xdata.data(), ydata.data(), count); + double xtrend[2], ytrend[2]; + xtrend[0] = 0.0; + ytrend[0] = regress.getYforX(0.0); + // point 2 is at far right of chart, not the last point + // since we may be forecasting... + xtrend[1] = maxX; + ytrend[1] = regress.getYforX(maxX); + trend->setSamples(xtrend,ytrend, 2); - QVector xtrend; - QVector ytrend; + trend->attach(this); + curves.insert(trendSymbol, trend); - double inc = (regress.maxx - regress.minx) / 100; - for (double i=regress.minx; i<=(regress.maxx+inc); i+= inc) { - xtrend << i; - ytrend << regress.yForX(i); } - // point 2 is at far right of chart, not the last point - // since we may be forecasting... - trend->setSamples(xtrend.data(),ytrend.data(), xtrend.count()); - trend->attach(this); - curves.insert(trendSymbol, trend); + // quadratic lsm regression + if (metricDetail.trendtype == 2 && count > 3) { + QString trendName = QString(tr("%1 trend")).arg(metricDetail.uname); + QString trendSymbol = QString("%1_trend") + .arg(metricDetail.type == METRIC_BEST ? + metricDetail.bestSymbol : metricDetail.symbol); + + QwtPlotCurve *trend = new QwtPlotCurve(trendName); + + // cosmetics + QPen cpen = QPen(metricDetail.penColor.darker(200)); + cpen.setWidth(2); // double thickness for trend lines + cpen.setStyle(Qt::SolidLine); + trend->setPen(cpen); + if (appsettings->value(this, GC_ANTIALIAS, false).toBool()==true) + trend->setRenderHint(QwtPlotItem::RenderAntialiased); + trend->setBaseline(0); + trend->setYAxis(axisid); + trend->setStyle(QwtPlotCurve::Lines); + + // perform quadratic curve fit to data + LTMTrend2 regress(xdata.data(), ydata.data(), count); + + // override class variable as doing it temporarily for trend line only + double maxX = 0.5 + groupForDate(settings->end.date(), settings->groupBy) - + groupForDate(settings->start.date(), settings->groupBy); + + QVector xtrend; + QVector ytrend; + + double inc = (regress.maxx - regress.minx) / 100; + for (double i=regress.minx; i<=(regress.maxx+inc); i+= inc) { + xtrend << i; + ytrend << regress.yForX(i); + } + + // point 2 is at far right of chart, not the last point + // since we may be forecasting... + trend->setSamples(xtrend.data(),ytrend.data(), xtrend.count()); + + trend->attach(this); + curves.insert(trendSymbol, trend); + } } // highlight outliers diff --git a/src/LTMSettings.cpp b/src/LTMSettings.cpp index 986819613..9fef12f96 100644 --- a/src/LTMSettings.cpp +++ b/src/LTMSettings.cpp @@ -142,7 +142,7 @@ QDataStream &operator<<(QDataStream &out, const LTMSettings &settings) out<(metric.series); + out<>x; m.series = static_cast(x); } + + if (version >= 3) { // trendtype added + in>>m.trendtype; + } else { + m.trendtype = 0; // default! + } + + if (m.trend == true) { // migrating from old trendline checkbox + m.trendtype = 1; + m.trend = false; // lets forget it now + } // get a metric pointer (if it exists) m.metric = factory.rideMetric(m.symbol); settings.metrics.append(m); diff --git a/src/LTMSettings.h b/src/LTMSettings.h index f3024e4ef..dfd4d072c 100644 --- a/src/LTMSettings.h +++ b/src/LTMSettings.h @@ -85,7 +85,8 @@ class MetricDetail { // user configurable settings bool smooth, // smooth the curve - trend; // add a trend line + trend; // add a trend line XX deprecated XX + int trendtype; // 0 - no trend, 1 - linear, 2 - quadratic int topN; // highlight top N points int topOut; // highlight N ranked outlier points double baseline; // baseline for chart diff --git a/src/LTMTool.cpp b/src/LTMTool.cpp index da2d82c33..e9926a1b9 100644 --- a/src/LTMTool.cpp +++ b/src/LTMTool.cpp @@ -1095,6 +1095,14 @@ EditMetricDetailDialog::EditMetricDetailDialog(Context *context, LTMTool *ltmToo curveTrend = new QCheckBox(tr("Trend Line"), this); curveTrend->setChecked(metricDetail->trend); + curveTrend->hide(); // for now .. in 3.1 we moved to a checkbox, but this is + // kept for backward compatibility with the settings etc + + trendType = new QComboBox(this); + trendType->addItem(tr("No trend Line")); + trendType->addItem(tr("Linear Trend")); + trendType->addItem(tr("Quadratic Trend")); + trendType->setCurrentIndex(metricDetail->trendtype); // add to grid grid->addLayout(radioButtons, 0, 0, 1, 1, Qt::AlignTop|Qt::AlignLeft); @@ -1125,8 +1133,8 @@ EditMetricDetailDialog::EditMetricDetailDialog(Context *context, LTMTool *ltmToo grid->addWidget(showOut, 4,3); grid->addWidget(baseline, 5, 2); grid->addWidget(baseLine, 5,3); - grid->addWidget(curveSmooth, 7,2); - grid->addWidget(curveTrend, 8,2); + grid->addWidget(trendType, 7,2); + grid->addWidget(curveSmooth, 8,2); mainLayout->addLayout(grid); @@ -1220,6 +1228,7 @@ EditMetricDetailDialog::metricSelected() showOut->setValue(ltmTool->metrics[index].topOut); baseLine->setValue(ltmTool->metrics[index].baseline); penColor = ltmTool->metrics[index].penColor; + trendType->setCurrentIndex(ltmTool->metrics[index].trendtype); setButtonIcon(penColor); // curve style @@ -1317,6 +1326,7 @@ EditMetricDetailDialog::applyClicked() metricDetail->uname = userName->text(); metricDetail->uunits = userUnits->text(); metricDetail->stack = stack->isChecked(); + metricDetail->trendtype = trendType->currentIndex(); accept(); } diff --git a/src/LTMTool.h b/src/LTMTool.h index 6204f9ee7..74b514d35 100644 --- a/src/LTMTool.h +++ b/src/LTMTool.h @@ -192,8 +192,8 @@ class EditMetricDetailDialog : public QDialog *showOut, *baseLine; QCheckBox *curveSmooth, - *curveTrend; - + *curveTrend; // is now replaced with below, but kept for compatibility + QComboBox *trendType; // replaces above with a selection of trend line types QPushButton *applyButton, *cancelButton; QColor penColor; // chosen from color Picker