mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
add ride plot stacked view
This commit is contained in:
committed by
Sean Rhea
parent
19223e51b3
commit
afdc862cc2
166
src/AllPlot.cpp
166
src/AllPlot.cpp
@@ -152,7 +152,12 @@ class AllPlotZoneLabel: public QwtPlotItem
|
||||
);
|
||||
|
||||
text = QwtText(zone_names[zone_number]);
|
||||
text.setFont(QFont("Helvetica",24, QFont::Bold));
|
||||
if (_parent->referencePlot == NULL) {
|
||||
text.setFont(QFont("Helvetica",24, QFont::Bold));
|
||||
} else {
|
||||
text.setFont(QFont("Helvetica",12, QFont::Bold));
|
||||
}
|
||||
|
||||
QColor text_color = zoneColor(zone_number, num_zones);
|
||||
text_color.setAlpha(64);
|
||||
text.setColor(text_color);
|
||||
@@ -202,6 +207,8 @@ AllPlot::AllPlot(QWidget *parent, MainWindow *mainWindow):
|
||||
boost::shared_ptr<QSettings> settings = GetApplicationSettings();
|
||||
unit = settings->value(GC_UNIT);
|
||||
|
||||
referencePlot = NULL;
|
||||
|
||||
useMetricUnits = (unit.toString() == "Metric");
|
||||
|
||||
smooth = settings->value(GC_RIDE_PLOT_SMOOTHING).toInt();
|
||||
@@ -311,6 +318,10 @@ void AllPlot::refreshZoneLabels()
|
||||
void
|
||||
AllPlot::recalc()
|
||||
{
|
||||
if (referencePlot !=NULL){
|
||||
return;
|
||||
}
|
||||
|
||||
if (timeArray.empty())
|
||||
return;
|
||||
int rideTimeSecs = (int) ceil(timeArray[arrayLength - 1]);
|
||||
@@ -337,13 +348,13 @@ AllPlot::recalc()
|
||||
|
||||
QList<DataPoint> list;
|
||||
|
||||
QVector<double> smoothWatts(rideTimeSecs + 1);
|
||||
QVector<double> smoothHr(rideTimeSecs + 1);
|
||||
QVector<double> smoothSpeed(rideTimeSecs + 1);
|
||||
QVector<double> smoothCad(rideTimeSecs + 1);
|
||||
QVector<double> smoothTime(rideTimeSecs + 1);
|
||||
QVector<double> smoothDistance(rideTimeSecs + 1);
|
||||
QVector<double> smoothAltitude(rideTimeSecs + 1);
|
||||
smoothWatts.resize(rideTimeSecs + 1); //(rideTimeSecs + 1);
|
||||
smoothHr.resize(rideTimeSecs + 1);
|
||||
smoothSpeed.resize(rideTimeSecs + 1);
|
||||
smoothCad.resize(rideTimeSecs + 1);
|
||||
smoothTime.resize(rideTimeSecs + 1);
|
||||
smoothDistance.resize(rideTimeSecs + 1);
|
||||
smoothAltitude.resize(rideTimeSecs + 1);
|
||||
|
||||
for (int secs = 0; ((secs < smooth)
|
||||
&& (secs < rideTimeSecs)); ++secs) {
|
||||
@@ -471,7 +482,10 @@ AllPlot::setYMax()
|
||||
{
|
||||
if (wattsCurve->isVisible()) {
|
||||
setAxisTitle(yLeft, "Watts");
|
||||
setAxisScale(yLeft, 0.0, 1.05 * wattsCurve->maxYValue());
|
||||
if (referencePlot == NULL)
|
||||
setAxisScale(yLeft, 0.0, 1.05 * wattsCurve->maxYValue());
|
||||
else
|
||||
setAxisScale(yLeft, 0.0, 1.05 * referencePlot->wattsCurve->maxYValue());
|
||||
setAxisLabelRotation(yLeft,270);
|
||||
setAxisLabelAlignment(yLeft,Qt::AlignVCenter);
|
||||
}
|
||||
@@ -480,11 +494,17 @@ AllPlot::setYMax()
|
||||
QStringList labels;
|
||||
if (hrCurve->isVisible()) {
|
||||
labels << "BPM";
|
||||
ymax = hrCurve->maxYValue();
|
||||
if (referencePlot == NULL)
|
||||
ymax = hrCurve->maxYValue();
|
||||
else
|
||||
ymax = referencePlot->hrCurve->maxYValue();
|
||||
}
|
||||
if (cadCurve->isVisible()) {
|
||||
labels << "RPM";
|
||||
ymax = qMax(ymax, cadCurve->maxYValue());
|
||||
if (referencePlot == NULL)
|
||||
ymax = qMax(ymax, cadCurve->maxYValue());
|
||||
else
|
||||
ymax = qMax(ymax, referencePlot->cadCurve->maxYValue());
|
||||
}
|
||||
setAxisTitle(yLeft2, labels.join(" / "));
|
||||
setAxisScale(yLeft2, 0.0, 1.05 * ymax);
|
||||
@@ -493,14 +513,24 @@ AllPlot::setYMax()
|
||||
}
|
||||
if (speedCurve->isVisible()) {
|
||||
setAxisTitle(yRight, (useMetricUnits ? tr("KPH") : tr("MPH")));
|
||||
setAxisScale(yRight, 0.0, 1.05 * speedCurve->maxYValue());
|
||||
if (referencePlot == NULL)
|
||||
setAxisScale(yRight, 0.0, 1.05 * speedCurve->maxYValue());
|
||||
else
|
||||
setAxisScale(yRight, 0.0, 1.05 * referencePlot->speedCurve->maxYValue());
|
||||
setAxisLabelRotation(yRight,90);
|
||||
setAxisLabelAlignment(yRight,Qt::AlignVCenter);
|
||||
}
|
||||
if (altCurve->isVisible()) {
|
||||
setAxisTitle(yRight2, useMetricUnits ? tr("Meters") : tr("Feet"));
|
||||
double ymin = altCurve->minYValue();
|
||||
double ymax = qMax(ymin + 100, 1.05 * altCurve->maxYValue());
|
||||
double ymin,ymax;
|
||||
|
||||
if (referencePlot == NULL) {
|
||||
ymin = altCurve->minYValue();
|
||||
ymax = qMax(ymin + 100, 1.05 * altCurve->maxYValue());
|
||||
} else {
|
||||
ymin = referencePlot->altCurve->minYValue();
|
||||
ymax = qMax(ymin + 100, 1.05 * referencePlot->altCurve->maxYValue());
|
||||
}
|
||||
setAxisScale(yRight2, ymin, ymax);
|
||||
setAxisLabelRotation(yRight2,90);
|
||||
setAxisLabelAlignment(yRight2,Qt::AlignVCenter);
|
||||
@@ -523,9 +553,86 @@ AllPlot::setXTitle()
|
||||
}
|
||||
|
||||
void
|
||||
AllPlot::setData(RideItem *_rideItem)
|
||||
AllPlot::setDataP(AllPlot *plot, int startidx, int stopidx)
|
||||
{
|
||||
if (plot == NULL) return;
|
||||
|
||||
referencePlot = plot;
|
||||
|
||||
rideItem = plot->rideItem;
|
||||
shade_zones = plot->shade_zones;
|
||||
bydist = plot->bydist;
|
||||
|
||||
arrayLength = stopidx-startidx;
|
||||
wattsArray = plot->wattsArray.mid(startidx, stopidx-startidx);
|
||||
hrArray = plot->hrArray.mid(startidx, stopidx-startidx);
|
||||
speedArray = plot->speedArray.mid(startidx, stopidx-startidx);
|
||||
cadArray = plot->cadArray.mid(startidx, stopidx-startidx);
|
||||
altArray = plot->altArray.mid(startidx, stopidx-startidx);
|
||||
timeArray = plot->timeArray.mid(startidx, stopidx-startidx);
|
||||
distanceArray = plot->distanceArray.mid(startidx, stopidx-startidx);
|
||||
|
||||
if (bydist) {
|
||||
startidx = plot->distanceIndex(plot->distanceArray[startidx]);
|
||||
stopidx = plot->distanceIndex(plot->distanceArray[(stopidx>=plot->distanceArray.size()?plot->distanceArray.size()-1:stopidx)])-1;
|
||||
} else {
|
||||
startidx = plot->timeIndex(plot->timeArray[startidx]/60);
|
||||
stopidx = plot->timeIndex(plot->timeArray[(stopidx>=plot->timeArray.size()?plot->timeArray.size()-1:stopidx)]/60)-1;
|
||||
}
|
||||
|
||||
smoothWatts = plot->smoothWatts.mid(startidx, stopidx-startidx);
|
||||
smoothHr = plot->smoothHr.mid(startidx, stopidx-startidx);
|
||||
smoothSpeed = plot->smoothSpeed.mid(startidx, stopidx-startidx);
|
||||
smoothCad = plot->smoothCad.mid(startidx, stopidx-startidx);
|
||||
smoothAltitude = plot->smoothAltitude.mid(startidx, stopidx-startidx);
|
||||
smoothTime = plot->smoothTime.mid(startidx, stopidx-startidx);
|
||||
smoothDistance = plot->smoothDistance.mid(startidx, stopidx-startidx);
|
||||
|
||||
QVector<double> &xaxis = bydist ? smoothDistance : smoothTime;
|
||||
|
||||
// attach appropriate curves
|
||||
wattsCurve->detach();
|
||||
hrCurve->detach();
|
||||
speedCurve->detach();
|
||||
cadCurve->detach();
|
||||
altCurve->detach();
|
||||
|
||||
wattsCurve->setData(xaxis, smoothWatts);
|
||||
hrCurve->setData(xaxis, smoothHr);
|
||||
speedCurve->setData(xaxis, smoothSpeed);
|
||||
cadCurve->setData(xaxis, smoothCad);
|
||||
altCurve->setData(xaxis, smoothAltitude);
|
||||
|
||||
setYMax();
|
||||
|
||||
setAxisMaxMajor(yLeft, 5);
|
||||
setAxisMaxMajor(yLeft2, 5);
|
||||
setAxisMaxMajor(yRight, 5);
|
||||
setAxisMaxMajor(yRight2, 5);
|
||||
|
||||
wattsCurve->setVisible(plot->wattsCurve->isVisible());
|
||||
hrCurve->setVisible(plot->hrCurve->isVisible());
|
||||
speedCurve->setVisible(plot->speedCurve->isVisible());
|
||||
cadCurve->setVisible(plot->cadCurve->isVisible());
|
||||
altCurve->setVisible(plot->altCurve->isVisible());
|
||||
|
||||
if (!smoothWatts.empty()) wattsCurve->attach(this);
|
||||
if (!smoothHr.empty()) hrCurve->attach(this);
|
||||
if (!smoothSpeed.empty()) speedCurve->attach(this);
|
||||
if (!smoothCad.empty()) cadCurve->attach(this);
|
||||
if (!smoothAltitude.empty()) altCurve->attach(this);
|
||||
|
||||
refreshIntervalMarkers();
|
||||
refreshZoneLabels();
|
||||
|
||||
replot();
|
||||
}
|
||||
|
||||
void
|
||||
AllPlot::setDataI(RideItem *_rideItem)
|
||||
{
|
||||
rideItem = _rideItem;
|
||||
if (_rideItem == NULL) return;
|
||||
|
||||
wattsArray.clear();
|
||||
|
||||
@@ -676,6 +783,35 @@ AllPlot::setByDistance(int id)
|
||||
recalc();
|
||||
}
|
||||
|
||||
struct ComparePoints {
|
||||
bool operator()(const double p1, const double p2) {
|
||||
return p1 < p2;
|
||||
}
|
||||
};
|
||||
|
||||
int
|
||||
AllPlot::timeIndex(double min) const
|
||||
{
|
||||
// return index offset for specified time
|
||||
QVector<double>::const_iterator i = std::lower_bound(
|
||||
smoothTime.begin(), smoothTime.end(), min, ComparePoints());
|
||||
if (i == smoothTime.end())
|
||||
return smoothTime.size();
|
||||
return i - smoothTime.begin();
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
AllPlot::distanceIndex(double km) const
|
||||
{
|
||||
// return index offset for specified distance in km
|
||||
QVector<double>::const_iterator i = std::lower_bound(
|
||||
smoothDistance.begin(), smoothDistance.end(), km, ComparePoints());
|
||||
if (i == smoothDistance.end())
|
||||
return smoothDistance.size();
|
||||
return i - smoothDistance.begin();
|
||||
}
|
||||
/*----------------------------------------------------------------------
|
||||
* Interval plotting
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
@@ -52,7 +52,15 @@ class AllPlot : public QwtPlot
|
||||
void refreshZoneLabels();
|
||||
void refreshIntervalMarkers();
|
||||
|
||||
void setData(RideItem *_rideItem);
|
||||
void setDataI(RideItem *_rideItem);
|
||||
void setDataP(AllPlot *plot, int startidx, int stopidx);
|
||||
|
||||
int timeIndex(double) const; // get index offset for time in secs
|
||||
int distanceIndex(double) const; // get index offset for distance in KM
|
||||
|
||||
QwtPlotMarker *allMarker1;
|
||||
QwtPlotMarker *allMarker2;
|
||||
QwtPlotMarker *allMarker3;
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -95,6 +103,15 @@ class AllPlot : public QwtPlot
|
||||
QVector<double> timeArray;
|
||||
QVector<double> distanceArray;
|
||||
QVector<double> altArray;
|
||||
|
||||
QVector<double> smoothWatts;
|
||||
QVector<double> smoothHr;
|
||||
QVector<double> smoothSpeed;
|
||||
QVector<double> smoothCad;
|
||||
QVector<double> smoothTime;
|
||||
QVector<double> smoothDistance;
|
||||
QVector<double> smoothAltitude;
|
||||
|
||||
int arrayLength;
|
||||
|
||||
int smooth;
|
||||
@@ -112,6 +129,9 @@ class AllPlot : public QwtPlot
|
||||
int showSpeedState;
|
||||
int showCadState;
|
||||
int showAltState;
|
||||
|
||||
private:
|
||||
AllPlot *referencePlot;
|
||||
};
|
||||
|
||||
#endif // _GC_AllPlot_h
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "TimeUtils.h"
|
||||
#include "Settings.h"
|
||||
#include "Units.h" // for MILES_PER_KM
|
||||
#include <qwt_plot_layout.h>
|
||||
#include <qwt_plot_panner.h>
|
||||
#include <qwt_plot_zoomer.h>
|
||||
#include <qwt_plot_picker.h>
|
||||
@@ -41,6 +42,12 @@ AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) :
|
||||
QLabel *showLabel = new QLabel(tr("Show:"), this);
|
||||
showLayout->addWidget(showLabel);
|
||||
|
||||
showStack = new QCheckBox(tr("Stacked view"), this);
|
||||
showStack->setCheckState(Qt::Unchecked);
|
||||
showLayout->addWidget(showStack);
|
||||
|
||||
stackWidth = 15;
|
||||
|
||||
QCheckBox *showGrid = new QCheckBox(tr("Grid"), this);
|
||||
showGrid->setCheckState(Qt::Checked);
|
||||
showLayout->addWidget(showGrid);
|
||||
@@ -135,38 +142,51 @@ AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) :
|
||||
connect(allPicker, SIGNAL(appended(const QPoint &)),
|
||||
SLOT(plotPickerSelected(const QPoint &)));
|
||||
|
||||
allMarker1 = new QwtPlotMarker();
|
||||
QwtPlotMarker* allMarker1 = new QwtPlotMarker();
|
||||
allMarker1->setLineStyle(QwtPlotMarker::VLine);
|
||||
allMarker1->attach(allPlot);
|
||||
allMarker1->setLabelAlignment(Qt::AlignTop|Qt::AlignRight);
|
||||
allPlot->allMarker1=allMarker1;
|
||||
|
||||
allMarker2 = new QwtPlotMarker();
|
||||
QwtPlotMarker* allMarker2 = new QwtPlotMarker();
|
||||
allMarker2->setLineStyle(QwtPlotMarker::VLine);
|
||||
allMarker2->attach(allPlot);
|
||||
allMarker2->setLabelAlignment(Qt::AlignTop|Qt::AlignRight);
|
||||
allPlot->allMarker2=allMarker2;
|
||||
|
||||
allMarker3 = new QwtPlotMarker();
|
||||
QwtPlotMarker* allMarker3 = new QwtPlotMarker();
|
||||
allMarker3->setLineStyle(QwtPlotMarker::VLine);
|
||||
allMarker3->attach(allPlot);
|
||||
allPlot->allMarker3=allMarker3;
|
||||
|
||||
|
||||
stackFrame = new QScrollArea();
|
||||
stackFrame->hide();
|
||||
|
||||
vlayout->addWidget(allPlot);
|
||||
vlayout->addWidget(stackFrame);
|
||||
vlayout->addLayout(showLayout);
|
||||
vlayout->addLayout(smoothLayout);
|
||||
|
||||
|
||||
setLayout(vlayout);
|
||||
|
||||
connect(showPower, SIGNAL(currentIndexChanged(int)),
|
||||
allPlot, SLOT(showPower(int)));
|
||||
this, SLOT(setShowPower(int)));
|
||||
connect(showHr, SIGNAL(stateChanged(int)),
|
||||
allPlot, SLOT(showHr(int)));
|
||||
this, SLOT(setShowHr(int)));
|
||||
connect(showSpeed, SIGNAL(stateChanged(int)),
|
||||
allPlot, SLOT(showSpeed(int)));
|
||||
this, SLOT(setShowSpeed(int)));
|
||||
connect(showCad, SIGNAL(stateChanged(int)),
|
||||
allPlot, SLOT(showCad(int)));
|
||||
this, SLOT(setShowCad(int)));
|
||||
connect(showAlt, SIGNAL(stateChanged(int)),
|
||||
allPlot, SLOT(showAlt(int)));
|
||||
this, SLOT(setShowAlt(int)));
|
||||
connect(showGrid, SIGNAL(stateChanged(int)),
|
||||
allPlot, SLOT(showGrid(int)));
|
||||
this, SLOT(setShowGrid(int)));
|
||||
connect(showStack, SIGNAL(stateChanged(int)),
|
||||
this, SLOT(setShowStack(int)));
|
||||
connect(comboDistance, SIGNAL(currentIndexChanged(int)),
|
||||
allPlot, SLOT(setByDistance(int)));
|
||||
this, SLOT(setByDistance(int)));
|
||||
connect(smoothSlider, SIGNAL(valueChanged(int)),
|
||||
this, SLOT(setSmoothingFromSlider()));
|
||||
connect(smoothLineEdit, SIGNAL(editingFinished()),
|
||||
@@ -185,8 +205,13 @@ AllPlotWindow::rideSelected()
|
||||
return;
|
||||
clearSelection(); // clear any ride interval selection data
|
||||
setAllPlotWidgets(ride);
|
||||
allPlot->setData(ride);
|
||||
allPlot->setDataI(ride);
|
||||
allZoomer->setZoomBase();
|
||||
|
||||
// update stacked view if that is set
|
||||
int showit = showStack->isChecked();
|
||||
setShowStack(0); // zap whats there
|
||||
setShowStack(showit);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -201,20 +226,23 @@ AllPlotWindow::intervalsChanged()
|
||||
{
|
||||
allPlot->refreshIntervalMarkers();
|
||||
allPlot->replot();
|
||||
foreach (AllPlot *plot, allPlots) {
|
||||
plot->refreshIntervalMarkers();
|
||||
plot->replot();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::intervalSelected()
|
||||
{
|
||||
hideSelection();
|
||||
allPlot->replot();
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::setSmoothingFromSlider()
|
||||
{
|
||||
if (allPlot->smoothing() != smoothSlider->value()) {
|
||||
allPlot->setSmoothing(smoothSlider->value());
|
||||
setSmoothing(smoothSlider->value());
|
||||
smoothLineEdit->setText(QString("%1").arg(allPlot->smoothing()));
|
||||
}
|
||||
}
|
||||
@@ -224,7 +252,7 @@ AllPlotWindow::setSmoothingFromLineEdit()
|
||||
{
|
||||
int value = smoothLineEdit->text().toInt();
|
||||
if (value != allPlot->smoothing()) {
|
||||
allPlot->setSmoothing(value);
|
||||
setSmoothing(value);
|
||||
smoothSlider->setValue(value);
|
||||
}
|
||||
}
|
||||
@@ -269,39 +297,91 @@ AllPlotWindow::zoomInterval(IntervalItem *which)
|
||||
void
|
||||
AllPlotWindow::plotPickerSelected(const QPoint &pos)
|
||||
{
|
||||
QwtPlotPicker* pick = qobject_cast<QwtPlotPicker *>(sender());
|
||||
AllPlot* plot = qobject_cast<AllPlot *>(pick->plot());
|
||||
|
||||
// set start of selection in xunits (minutes assumed for now)
|
||||
setStartSelection(allPlot->invTransform(QwtPlot::xBottom, pos.x()));
|
||||
setStartSelection(plot, pos.x());
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::plotPickerMoved(const QPoint &pos)
|
||||
{
|
||||
QString name = QString("Selection #%1 ").arg(selection);
|
||||
// set end of selection in xunits (minutes assumed for now)
|
||||
setEndSelection(allPlot->invTransform(QwtPlot::xBottom, pos.x()), true, name);
|
||||
QwtPlotPicker* pick = qobject_cast<QwtPlotPicker *>(sender());
|
||||
AllPlot* plot = qobject_cast<AllPlot *>(pick->plot());
|
||||
|
||||
if (allPlots.length()>0) {
|
||||
int posX = pos.x();
|
||||
int posY = plot->y()+pos.y()+50;
|
||||
|
||||
foreach (AllPlot *_plot, allPlots) {
|
||||
_plot->allMarker1->setValue(plot->allMarker1->value());
|
||||
|
||||
if (_plot->y()<=plot->y() && posY<_plot->y()){
|
||||
if (_plot->transform(QwtPlot::xBottom, _plot->allMarker2->xValue())>0){
|
||||
setEndSelection(_plot, 0, false, name);
|
||||
_plot->allMarker2->setLabel(QString(""));
|
||||
}
|
||||
}
|
||||
else if (_plot->y()>=plot->y() && posY>_plot->y()+_plot->height()){
|
||||
if (_plot->transform(QwtPlot::xBottom, _plot->allMarker2->xValue())<plot->width()){
|
||||
setEndSelection(_plot, plot->width(), false, name);
|
||||
}
|
||||
}
|
||||
else if (posY>_plot->y() && posY<_plot->y()+_plot->height()){
|
||||
if (pos.x()<6)
|
||||
posX = 6;
|
||||
else if (!_plot->byDistance() && pos.x()>_plot->transform(QwtPlot::xBottom, _plot->timeArray[_plot->timeArray.size()-1]))
|
||||
posX = _plot->transform(QwtPlot::xBottom, _plot->timeArray[_plot->timeArray.size()-1]);
|
||||
else if (plot->byDistance() && pos.x()>_plot->transform(QwtPlot::xBottom, _plot->distanceArray[_plot->distanceArray.size()-1]))
|
||||
posX = _plot->transform(QwtPlot::xBottom, _plot->distanceArray[_plot->distanceArray.size()-1]);
|
||||
|
||||
setEndSelection(_plot, posX, true, name);
|
||||
if (plot->y()<_plot->y()) {
|
||||
plot->allMarker1->setLabel(_plot->allMarker1->label());
|
||||
plot->allMarker2->setLabel(_plot->allMarker2->label());
|
||||
}
|
||||
} else {//if (_plot->y()<posY+3*_plot->height() || _plot->y()<plot->y()+3*plot->height())
|
||||
//hide plot for 3 next plots
|
||||
_plot->allMarker1->hide();
|
||||
_plot->allMarker2->hide();
|
||||
_plot->allMarker3->hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
else // set end of selection in xunits (minutes assumed for now)
|
||||
setEndSelection(plot, pos.x(), true, name);
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::setStartSelection(double xValue)
|
||||
AllPlotWindow::setStartSelection(AllPlot* plot, int xPosition)
|
||||
{
|
||||
double xValue = plot->invTransform(QwtPlot::xBottom, xPosition);
|
||||
QwtPlotMarker* allMarker1 = plot->allMarker1;
|
||||
|
||||
selection++;
|
||||
|
||||
if (!allMarker1->isVisible() || allMarker1->xValue() != xValue) {
|
||||
|
||||
allMarker1->hide();
|
||||
allMarker2->hide();
|
||||
allMarker3->hide();
|
||||
allMarker1->setValue(xValue, allPlot->byDistance() ? 0 : 100);
|
||||
hideSelection();
|
||||
allMarker1->setValue(xValue, plot->byDistance() ? 0 : 100);
|
||||
allMarker1->show();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name)
|
||||
AllPlotWindow::setEndSelection(AllPlot* plot, int xPosition, bool newInterval, QString name)
|
||||
{
|
||||
bool useMetricUnits = allPlot->useMetricUnits;
|
||||
double xValue = plot->invTransform(QwtPlot::xBottom, xPosition);
|
||||
|
||||
QwtPlotMarker* allMarker1 = plot->allMarker1;
|
||||
QwtPlotMarker* allMarker2 = plot->allMarker2;
|
||||
QwtPlotMarker* allMarker3 = plot->allMarker3;
|
||||
|
||||
bool useMetricUnits = plot->useMetricUnits;
|
||||
if (!allMarker2->isVisible() || allMarker2->xValue() != xValue) {
|
||||
allMarker2->setValue(xValue, allPlot->byDistance() ? 0 : 100);
|
||||
allMarker2->setValue(xValue, plot->byDistance() ? 0 : 100);
|
||||
allMarker2->show();
|
||||
double x1, x2; // time or distance depending upon mode selected
|
||||
|
||||
@@ -313,7 +393,7 @@ AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name)
|
||||
x1 = allMarker1->xValue();
|
||||
x2 = allMarker2->xValue();
|
||||
}
|
||||
double lwidth=allPlot->transform(QwtPlot::xBottom, x2)-allPlot->transform(QwtPlot::xBottom, x1);
|
||||
double lwidth=plot->transform(QwtPlot::xBottom, x2)-plot->transform(QwtPlot::xBottom, x1);
|
||||
|
||||
allMarker3->setValue((x2-x1)/2+x1, 100);
|
||||
QColor marker_color = QColor(Qt::blue);
|
||||
@@ -340,14 +420,14 @@ AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name)
|
||||
// if we are in distance mode then x1 and x2 are distances
|
||||
// we need to make sure they are in KM for the rest of this
|
||||
// code.
|
||||
if (allPlot->byDistance() && useMetricUnits == false) {
|
||||
if (plot->byDistance() && useMetricUnits == false) {
|
||||
x1 *= KM_PER_MILE;
|
||||
x2 *= KM_PER_MILE;
|
||||
}
|
||||
|
||||
foreach (const RideFilePoint *point, ride->ride()->dataPoints()) {
|
||||
if ((allPlot->byDistance()==true && point->km>=x1 && point->km<x2) ||
|
||||
(allPlot->byDistance()==false && point->secs/60>=x1 && point->secs/60<x2)) {
|
||||
if ((plot->byDistance()==true && point->km>=x1 && point->km<x2) ||
|
||||
(plot->byDistance()==false && point->secs/60>=x1 && point->secs/60<x2)) {
|
||||
|
||||
if (distance1 == -1) distance1 = point->km;
|
||||
distance2 = point->km;
|
||||
@@ -389,7 +469,14 @@ AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name)
|
||||
s = s.arg("");
|
||||
}
|
||||
|
||||
allMarker1->setLabel(s);
|
||||
if (allMarker1->xValue()<allMarker2->xValue()) {
|
||||
allMarker1->setLabel(s);
|
||||
allMarker2->setLabel(QString(""));
|
||||
}
|
||||
else {
|
||||
allMarker2->setLabel(s);
|
||||
allMarker1->setLabel(QString(""));
|
||||
}
|
||||
|
||||
if (newInterval) {
|
||||
|
||||
@@ -425,9 +512,244 @@ AllPlotWindow::clearSelection()
|
||||
void
|
||||
AllPlotWindow::hideSelection()
|
||||
{
|
||||
allMarker1->setVisible(false);
|
||||
allMarker2->setVisible(false);
|
||||
allMarker3->setVisible(false);
|
||||
allPlot->allMarker1->setVisible(false);
|
||||
allPlot->allMarker2->setVisible(false);
|
||||
allPlot->allMarker3->setVisible(false);
|
||||
allPlot->replot();
|
||||
|
||||
foreach (AllPlot *plot, allPlots) {
|
||||
plot->allMarker1->setVisible(false);
|
||||
plot->allMarker2->setVisible(false);
|
||||
plot->allMarker3->setVisible(false);
|
||||
plot->replot();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::setShowPower(int value)
|
||||
{
|
||||
allPlot->showPower(value);
|
||||
if (allPlots.count()>0)
|
||||
resetStackedDatas();
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::setShowHr(int value)
|
||||
{
|
||||
allPlot->showHr(value);
|
||||
foreach (AllPlot *plot, allPlots)
|
||||
plot->showHr(value);
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::setShowSpeed(int value)
|
||||
{
|
||||
allPlot->showSpeed(value);
|
||||
foreach (AllPlot *plot, allPlots)
|
||||
plot->showSpeed(value);
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::setShowCad(int value)
|
||||
{
|
||||
allPlot->showCad(value);
|
||||
foreach (AllPlot *plot, allPlots)
|
||||
plot->showCad(value);
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::setShowAlt(int value)
|
||||
{
|
||||
allPlot->showAlt(value);
|
||||
foreach (AllPlot *plot, allPlots)
|
||||
plot->showAlt(value);
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::setShowGrid(int value)
|
||||
{
|
||||
allPlot->showGrid(value);
|
||||
foreach (AllPlot *plot, allPlots)
|
||||
plot->showGrid(value);
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::setByDistance(int value)
|
||||
{
|
||||
allPlot->setByDistance(value);
|
||||
if (allPlots.count()>0) {
|
||||
setShowStack(false);
|
||||
setShowStack(true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::setSmoothing(int value)
|
||||
{
|
||||
allPlot->setSmoothing(value);
|
||||
|
||||
if (allPlots.count()>0)
|
||||
resetStackedDatas();
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::resetStackedDatas()
|
||||
{
|
||||
|
||||
int _stackWidth = stackWidth;
|
||||
if (allPlot->byDistance())
|
||||
_stackWidth = stackWidth/3;
|
||||
|
||||
for(int i = 0 ; i < allPlots.count() ; i++)
|
||||
{
|
||||
AllPlot *plot = allPlots[i];
|
||||
int startIndex, stopIndex;
|
||||
if (plot->byDistance()) {
|
||||
startIndex = allPlot->rideItem->ride()->distanceIndex((allPlot->useMetricUnits ? 1 : KM_PER_MILE) * _stackWidth*i);
|
||||
stopIndex = allPlot->rideItem->ride()->distanceIndex((allPlot->useMetricUnits ? 1 : KM_PER_MILE) * _stackWidth*(i+1));
|
||||
} else {
|
||||
startIndex = allPlot->rideItem->ride()->timeIndex(60*_stackWidth*i);
|
||||
stopIndex = allPlot->rideItem->ride()->timeIndex(60*_stackWidth*(i+1));
|
||||
}
|
||||
|
||||
// Update AllPlot for stacked view
|
||||
plot->setDataP(allPlot, startIndex, stopIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::setShowStack(int value)
|
||||
{
|
||||
if (value) {
|
||||
int _stackWidth = stackWidth;
|
||||
if (allPlot->byDistance())
|
||||
_stackWidth = stackWidth/3;
|
||||
|
||||
allPlot->hide();
|
||||
|
||||
QVBoxLayout *vLayout = new QVBoxLayout();
|
||||
|
||||
RideItem* rideItem = allPlot->rideItem;
|
||||
double duration = rideItem->ride()->dataPoints().last()->secs;
|
||||
double distance = (allPlot->useMetricUnits ? 1 : MILES_PER_KM) * rideItem->ride()->dataPoints().last()->km;
|
||||
int nbplot;
|
||||
|
||||
if (allPlot->byDistance())
|
||||
nbplot = (int)floor(distance/_stackWidth)+1;
|
||||
else
|
||||
nbplot = (int)floor(duration/_stackWidth/60)+1;
|
||||
|
||||
for(int i = 0 ; i < nbplot ; i++)
|
||||
{
|
||||
AllPlot *_allPlot = new AllPlot(this, mainWindow);
|
||||
allPlots.append(_allPlot);
|
||||
addPickers(_allPlot);
|
||||
|
||||
int startIndex, stopIndex;
|
||||
if (allPlot->byDistance()) {
|
||||
startIndex = allPlot->rideItem->ride()->distanceIndex((allPlot->useMetricUnits ? 1 : KM_PER_MILE) * _stackWidth*i);
|
||||
stopIndex = allPlot->rideItem->ride()->distanceIndex((allPlot->useMetricUnits ? 1 : KM_PER_MILE) * _stackWidth*(i+1));
|
||||
} else {
|
||||
startIndex = allPlot->rideItem->ride()->timeIndex(60*_stackWidth*i);
|
||||
stopIndex = allPlot->rideItem->ride()->timeIndex(60*_stackWidth*(i+1));
|
||||
}
|
||||
vLayout->addWidget(_allPlot);
|
||||
|
||||
// Update AllPlot for stacked view
|
||||
_allPlot->setDataP(allPlot, startIndex, stopIndex);
|
||||
_allPlot->setAxisScale(QwtPlot::xBottom, _stackWidth*i, _stackWidth*(i+1), 1);
|
||||
|
||||
if (i==0){
|
||||
// First plot view title and legend
|
||||
_allPlot->setTitle(allPlot->title());
|
||||
_allPlot->plotLayout()->setLegendPosition(QwtPlot::TopLegend);
|
||||
_allPlot->setFixedHeight(200);
|
||||
}
|
||||
else {
|
||||
_allPlot->setTitle("");
|
||||
_allPlot->insertLegend(NULL);
|
||||
_allPlot->setFixedHeight(150);
|
||||
}
|
||||
|
||||
// No x axis titles
|
||||
_allPlot->setAxisTitle(QwtPlot::xBottom,NULL);
|
||||
// Smaller y axis Titles
|
||||
QFont axisFont = QFont("Helvetica",10, QFont::Normal);
|
||||
QwtText text = _allPlot->axisTitle(QwtPlot::yLeft);
|
||||
text.setFont(axisFont);
|
||||
_allPlot->setAxisTitle(QwtPlot::yLeft,text);
|
||||
text = _allPlot->axisTitle(QwtPlot::yLeft2);
|
||||
text.setFont(axisFont);
|
||||
_allPlot->setAxisTitle(QwtPlot::yLeft2,text);
|
||||
text = _allPlot->axisTitle(QwtPlot::yRight);
|
||||
text.setFont(axisFont);
|
||||
_allPlot->setAxisTitle(QwtPlot::yRight,text);
|
||||
text = _allPlot->axisTitle(QwtPlot::yRight2);
|
||||
text.setFont(axisFont);
|
||||
_allPlot->setAxisTitle(QwtPlot::yRight2,text);
|
||||
}
|
||||
|
||||
QWidget *stack = new QWidget();
|
||||
stack->setLayout(vLayout);
|
||||
|
||||
stackFrame->setWidgetResizable(true);
|
||||
stackFrame->setWidget(stack);
|
||||
stackFrame->show();
|
||||
} else {
|
||||
stackFrame->hide();
|
||||
allPlot->show();
|
||||
|
||||
if (allPlots.count()>1) {
|
||||
foreach (AllPlot *plot, allPlots) {
|
||||
//layout()->removeWidget(plot);
|
||||
delete plot;
|
||||
allPlots.removeOne(plot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::addPickers(AllPlot *_allPlot)
|
||||
{
|
||||
QwtPlotMarker* allMarker1 = new QwtPlotMarker();
|
||||
allMarker1->setLineStyle(QwtPlotMarker::VLine);
|
||||
allMarker1->attach(_allPlot);
|
||||
allMarker1->setLabelAlignment(Qt::AlignTop|Qt::AlignRight);
|
||||
_allPlot->allMarker1 = allMarker1;
|
||||
|
||||
QwtPlotMarker* allMarker2 = new QwtPlotMarker();
|
||||
allMarker2->setLineStyle(QwtPlotMarker::VLine);
|
||||
allMarker2->attach(_allPlot);
|
||||
allMarker2->setLabelAlignment(Qt::AlignTop|Qt::AlignRight);
|
||||
_allPlot->allMarker2 = allMarker2;
|
||||
|
||||
QwtPlotMarker* allMarker3 = new QwtPlotMarker();
|
||||
allMarker3->setLineStyle(QwtPlotMarker::VLine);
|
||||
allMarker3->attach(_allPlot);
|
||||
_allPlot->allMarker3 = allMarker3;
|
||||
|
||||
// Interval selection 2
|
||||
QwtPlotPicker* allPicker = new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft,
|
||||
QwtPicker::RectSelection | QwtPicker::CornerToCorner|QwtPicker::DragSelection,
|
||||
QwtPicker::NoRubberBand,
|
||||
QwtPicker::ActiveOnly, _allPlot->canvas());
|
||||
allPickers.append(allPicker);
|
||||
allPicker->setRubberBandPen(QColor(Qt::blue));
|
||||
allPicker->setRubberBand(QwtPicker::NoRubberBand);
|
||||
allPicker->setTrackerPen(QColor(Qt::blue));
|
||||
// now select rectangles
|
||||
allPicker->setSelectionFlags(QwtPicker::PointSelection | QwtPicker::RectSelection | QwtPicker::DragSelection);
|
||||
allPicker->setMousePattern(QwtEventPattern::MouseSelect1,
|
||||
Qt::LeftButton, Qt::ShiftModifier);
|
||||
|
||||
|
||||
|
||||
//void appended(const QwtPlotPicker* pick, const QwtDoublePoint &pos);
|
||||
connect(allPicker, SIGNAL(appended(const QPoint &)),
|
||||
SLOT(plotPickerSelected(const QPoint &)));
|
||||
connect(allPicker, SIGNAL(moved(const QPoint &)),
|
||||
SLOT(plotPickerMoved(const QPoint &)));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ class AllPlotWindow : public QWidget
|
||||
|
||||
AllPlotWindow(MainWindow *mainWindow);
|
||||
void setData(RideItem *ride);
|
||||
void setStartSelection(double seconds);
|
||||
void setEndSelection(double seconds, bool newInterval, QString name);
|
||||
void setStartSelection(AllPlot* plot, int xPosition);
|
||||
void setEndSelection(AllPlot* plot, int xPosition, bool newInterval, QString name);
|
||||
void clearSelection();
|
||||
void hideSelection();
|
||||
void zoomInterval(IntervalItem *); // zoom into a specified interval
|
||||
@@ -53,6 +53,16 @@ class AllPlotWindow : public QWidget
|
||||
void zonesChanged();
|
||||
void intervalsChanged();
|
||||
|
||||
void setShowStack(int state);
|
||||
void setShowPower(int state);
|
||||
void setShowHr(int state);
|
||||
void setShowSpeed(int state);
|
||||
void setShowCad(int state);
|
||||
void setShowAlt(int state);
|
||||
void setShowGrid(int state);
|
||||
void setSmoothing(int value);
|
||||
void setByDistance(int value);
|
||||
|
||||
protected:
|
||||
|
||||
// whilst we refactor, lets make friend
|
||||
@@ -63,13 +73,15 @@ class AllPlotWindow : public QWidget
|
||||
|
||||
MainWindow *mainWindow;
|
||||
AllPlot *allPlot;
|
||||
QList <AllPlot *> allPlots;
|
||||
QList <QwtPlotPicker *> allPickers;
|
||||
|
||||
QScrollArea *stackFrame;
|
||||
QwtPlotPanner *allPanner;
|
||||
QwtPlotZoomer *allZoomer;
|
||||
QwtPlotPicker *allPicker;
|
||||
int selection;
|
||||
QwtPlotMarker *allMarker1;
|
||||
QwtPlotMarker *allMarker2;
|
||||
QwtPlotMarker *allMarker3;
|
||||
QCheckBox *showStack;
|
||||
QCheckBox *showHr;
|
||||
QCheckBox *showSpeed;
|
||||
QCheckBox *showCad;
|
||||
@@ -80,8 +92,12 @@ class AllPlotWindow : public QWidget
|
||||
|
||||
private:
|
||||
void showInfo(QString);
|
||||
void resetStackedDatas();
|
||||
int stackWidth;
|
||||
|
||||
private slots:
|
||||
void addPickers(AllPlot *allPlot2);
|
||||
|
||||
void plotPickerMoved(const QPoint &);
|
||||
void plotPickerSelected(const QPoint &);
|
||||
};
|
||||
|
||||
@@ -55,7 +55,13 @@ RideFile::fillInIntervals()
|
||||
mark();
|
||||
}
|
||||
|
||||
struct ComparePoints {
|
||||
struct ComparePointKm {
|
||||
bool operator()(const RideFilePoint *p1, const RideFilePoint *p2) {
|
||||
return p1->km < p2->km;
|
||||
}
|
||||
};
|
||||
|
||||
struct ComparePointSecs {
|
||||
bool operator()(const RideFilePoint *p1, const RideFilePoint *p2) {
|
||||
return p1->secs < p2->secs;
|
||||
}
|
||||
@@ -67,7 +73,7 @@ RideFile::intervalBegin(const RideFileInterval &interval) const
|
||||
RideFilePoint p;
|
||||
p.secs = interval.start;
|
||||
QVector<RideFilePoint*>::const_iterator i = std::lower_bound(
|
||||
dataPoints_.begin(), dataPoints_.end(), &p, ComparePoints());
|
||||
dataPoints_.begin(), dataPoints_.end(), &p, ComparePointSecs());
|
||||
if (i == dataPoints_.end())
|
||||
return dataPoints_.size();
|
||||
return i - dataPoints_.begin();
|
||||
@@ -84,10 +90,38 @@ RideFile::timeToDistance(double secs) const
|
||||
if (secs < dataPoints_.first()->secs) return dataPoints_.first()->km;
|
||||
if (secs > dataPoints_.last()->secs) return dataPoints_.last()->km;
|
||||
|
||||
QVector<RideFilePoint*>::const_iterator i = std::lower_bound(dataPoints_.begin(), dataPoints_.end(), &p, ComparePoints());
|
||||
QVector<RideFilePoint*>::const_iterator i = std::lower_bound(dataPoints_.begin(), dataPoints_.end(), &p, ComparePointSecs());
|
||||
return (*i)->km;
|
||||
}
|
||||
|
||||
int
|
||||
RideFile::timeIndex(double secs) const
|
||||
{
|
||||
// return index offset for specified time
|
||||
RideFilePoint p;
|
||||
p.secs = secs;
|
||||
|
||||
QVector<RideFilePoint*>::const_iterator i = std::lower_bound(
|
||||
dataPoints_.begin(), dataPoints_.end(), &p, ComparePointSecs());
|
||||
if (i == dataPoints_.end())
|
||||
return dataPoints_.size();
|
||||
return i - dataPoints_.begin();
|
||||
}
|
||||
|
||||
int
|
||||
RideFile::distanceIndex(double km) const
|
||||
{
|
||||
// return index offset for specified distance in km
|
||||
RideFilePoint p;
|
||||
p.km = km;
|
||||
|
||||
QVector<RideFilePoint*>::const_iterator i = std::lower_bound(
|
||||
dataPoints_.begin(), dataPoints_.end(), &p, ComparePointKm());
|
||||
if (i == dataPoints_.end())
|
||||
return dataPoints_.size();
|
||||
return i - dataPoints_.begin();
|
||||
}
|
||||
|
||||
void RideFile::writeAsCsv(QFile &file, bool bIsMetric) const
|
||||
{
|
||||
|
||||
@@ -159,7 +193,7 @@ RideFileFactory::rideFileRegExp() const
|
||||
{
|
||||
QStringList suffixList = RideFileFactory::instance().suffixes();
|
||||
QString s("^(\\d\\d\\d\\d)_(\\d\\d)_(\\d\\d)_(\\d\\d)_(\\d\\d)_(\\d\\d)\\.(%1)$");
|
||||
return QRegExp(s.arg(suffixList.join("|")), Qt::CaseInsensitive);
|
||||
return QRegExp(s.arg(suffixList.join("|")));
|
||||
}
|
||||
|
||||
RideFile *RideFileFactory::openRideFile(QFile &file,
|
||||
|
||||
@@ -121,6 +121,8 @@ class RideFile
|
||||
void resetDataPresent();
|
||||
|
||||
double timeToDistance(double) const; // get distance in km at time in secs
|
||||
int timeIndex(double) const; // get index offset for time in secs
|
||||
int distanceIndex(double) const; // get index offset for distance in KM
|
||||
|
||||
QMap<QString,QMap<QString,QString> > metricOverrides;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user