diff --git a/gpxsee.pro b/gpxsee.pro index d86eb930..e410fc93 100644 --- a/gpxsee.pro +++ b/gpxsee.pro @@ -249,6 +249,7 @@ SOURCES += src/main.cpp \ src/GUI/gearratiographitem.cpp \ src/GUI/mapview.cpp \ src/GUI/areaitem.cpp \ + src/data/waypoint.cpp \ src/map/IMG/bitmapline.cpp \ src/map/IMG/bitstream.cpp \ src/map/IMG/deltastream.cpp \ diff --git a/src/GUI/cadencegraphitem.cpp b/src/GUI/cadencegraphitem.cpp index 6d0c0297..6416fac5 100644 --- a/src/GUI/cadencegraphitem.cpp +++ b/src/GUI/cadencegraphitem.cpp @@ -5,7 +5,7 @@ CadenceGraphItem::CadenceGraphItem(const Graph &graph, GraphType type, int width, const QColor &color, QGraphicsItem *parent) - : GraphItem(graph, type, width, color, parent) + : GraphItem(graph, type, width, color, Qt::SolidLine, parent) { } diff --git a/src/GUI/elevationgraph.cpp b/src/GUI/elevationgraph.cpp index 53562716..9f644dc5 100644 --- a/src/GUI/elevationgraph.cpp +++ b/src/GUI/elevationgraph.cpp @@ -65,35 +65,38 @@ void ElevationGraph::setInfo() } } -GraphItem *ElevationGraph::loadGraph(const Graph &graph, Type type) +GraphItem *ElevationGraph::loadGraph(const Graph &graph, PathType type, + const QColor &color, bool primary) { - if (!graph.isValid()) { - _palette.nextColor(); + if (!graph.isValid()) return 0; - } ElevationGraphItem *gi = new ElevationGraphItem(graph, _graphType, _width, - _palette.nextColor()); + color, primary ? Qt::SolidLine : Qt::DashLine); gi->setUnits(_units); - if (type == Track) { + if (type == TrackPath) { _tracks.append(gi); if (_showTracks) addGraph(gi); - _trackAscent += gi->ascent(); - _trackDescent += gi->descent(); - _trackMax = nMax(_trackMax, gi->max()); - _trackMin = nMin(_trackMin, gi->min()); + if (primary) { + _trackAscent += gi->ascent(); + _trackDescent += gi->descent(); + _trackMax = nMax(_trackMax, gi->max()); + _trackMin = nMin(_trackMin, gi->min()); + } } else { _routes.append(gi); if (_showRoutes) addGraph(gi); - _routeAscent += gi->ascent(); - _routeDescent += gi->descent(); - _routeMax = nMax(_routeMax, gi->max()); - _routeMin = nMin(_routeMin, gi->min()); + if (primary) { + _routeAscent += gi->ascent(); + _routeDescent += gi->descent(); + _routeMax = nMax(_routeMax, gi->max()); + _routeMin = nMin(_routeMin, gi->min()); + } } return gi; @@ -102,11 +105,32 @@ GraphItem *ElevationGraph::loadGraph(const Graph &graph, Type type) QList ElevationGraph::loadData(const Data &data) { QList graphs; + GraphItem *primary, *secondary; - for (int i = 0; i < data.tracks().count(); i++) - graphs.append(loadGraph(data.tracks().at(i).elevation(), Track)); - for (int i = 0; i < data.routes().count(); i++) - graphs.append(loadGraph(data.routes().at(i).elevation(), Route)); + for (int i = 0; i < data.tracks().count(); i++) { + QColor color(_palette.nextColor()); + const GraphPair &gp = data.tracks().at(i).elevation(); + + primary = loadGraph(gp.primary(), TrackPath, color, true); + secondary = primary + ? loadGraph(gp.secondary(), TrackPath, color, false) : 0; + if (primary && secondary) + primary->setSecondaryGraph(secondary); + + graphs.append(primary); + } + for (int i = 0; i < data.routes().count(); i++) { + QColor color(_palette.nextColor()); + const GraphPair &gp = data.routes().at(i).elevation(); + + primary = loadGraph(gp.primary(), RoutePath, color, true); + secondary = primary + ? loadGraph(gp.secondary(), RoutePath, color, false) : 0; + if (primary && secondary) + primary->setSecondaryGraph(secondary); + + graphs.append(primary); + } for (int i = 0; i < data.areas().count(); i++) _palette.nextColor(); diff --git a/src/GUI/elevationgraph.h b/src/GUI/elevationgraph.h index c0b552be..46fe090e 100644 --- a/src/GUI/elevationgraph.h +++ b/src/GUI/elevationgraph.h @@ -21,7 +21,7 @@ public: void showRoutes(bool show); private: - enum Type {Track, Route}; + enum PathType {TrackPath, RoutePath}; qreal max() const; qreal min() const; @@ -31,7 +31,8 @@ private: void setYUnits(Units units); void setInfo(); - GraphItem *loadGraph(const Graph &graph, Type type); + GraphItem *loadGraph(const Graph &graph, PathType type, const QColor &color, + bool primary); void showItems(const QList &list, bool show); qreal _trackAscent, _trackDescent; diff --git a/src/GUI/elevationgraphitem.cpp b/src/GUI/elevationgraphitem.cpp index 9d08904e..ee36e4f7 100644 --- a/src/GUI/elevationgraphitem.cpp +++ b/src/GUI/elevationgraphitem.cpp @@ -4,8 +4,8 @@ ElevationGraphItem::ElevationGraphItem(const Graph &graph, GraphType type, - int width, const QColor &color, QGraphicsItem *parent) - : GraphItem(graph, type, width, color, parent) + int width, const QColor &color, Qt::PenStyle style, QGraphicsItem *parent) + : GraphItem(graph, type, width, color, style, parent) { _min = GraphItem::min(); _max = GraphItem::max(); @@ -42,5 +42,6 @@ QString ElevationGraphItem::info() const tt.insert(tr("Minimum"), l.toString(min() * scale, 'f', 0) + UNIT_SPACE + su); + return tt.toString(); } diff --git a/src/GUI/elevationgraphitem.h b/src/GUI/elevationgraphitem.h index dfae0184..c1928acb 100644 --- a/src/GUI/elevationgraphitem.h +++ b/src/GUI/elevationgraphitem.h @@ -8,8 +8,10 @@ class ElevationGraphItem : public GraphItem Q_OBJECT public: + enum DataType {GPS, DEM}; + ElevationGraphItem(const Graph &graph, GraphType type, int width, - const QColor &color, QGraphicsItem *parent = 0); + const QColor &color, Qt::PenStyle style, QGraphicsItem *parent = 0); qreal ascent() const {return _ascent;} qreal descent() const {return _descent;} diff --git a/src/GUI/gearratiographitem.cpp b/src/GUI/gearratiographitem.cpp index 22cf03fd..acf2a844 100644 --- a/src/GUI/gearratiographitem.cpp +++ b/src/GUI/gearratiographitem.cpp @@ -6,7 +6,7 @@ GearRatioGraphItem::GearRatioGraphItem(const Graph &graph, GraphType type, int width, const QColor &color, QGraphicsItem *parent) - : GraphItem(graph, type, width, color, parent) + : GraphItem(graph, type, width, color, Qt::SolidLine, parent) { for (int i = 0; i < graph.size(); i++) { const GraphSegment &segment = graph.at(i); diff --git a/src/GUI/graphitem.cpp b/src/GUI/graphitem.cpp index c6e42994..a9a198a5 100644 --- a/src/GUI/graphitem.cpp +++ b/src/GUI/graphitem.cpp @@ -5,13 +5,13 @@ GraphItem::GraphItem(const Graph &graph, GraphType type, int width, - const QColor &color, QGraphicsItem *parent) - : GraphicsItem(parent), _graph(graph), _type(type) + const QColor &color, Qt::PenStyle style, QGraphicsItem *parent) + : GraphicsItem(parent), _graph(graph), _type(type), _secondaryGraph(0) { Q_ASSERT(_graph.isValid()); _units = Metric; - _pen = QPen(color, width); + _pen = QPen(color, width, style); _sx = 0; _sy = 0; _time = _graph.hasTime(); setZValue(2.0); diff --git a/src/GUI/graphitem.h b/src/GUI/graphitem.h index 17c752cb..006ea014 100644 --- a/src/GUI/graphitem.h +++ b/src/GUI/graphitem.h @@ -12,8 +12,8 @@ class GraphItem : public QObject, public GraphicsItem Q_OBJECT public: - GraphItem(const Graph &graph, GraphType type, int width, const QColor &color, - QGraphicsItem *parent = 0); + GraphItem(const Graph &graph, GraphType type, int width, + const QColor &color, Qt::PenStyle style, QGraphicsItem *parent = 0); virtual ~GraphItem() {} virtual QString info() const = 0; @@ -35,6 +35,9 @@ public: void setWidth(int width); void setUnits(Units units) {_units = units;} + GraphItem *secondaryGraph() const {return _secondaryGraph;} + void setSecondaryGraph(GraphItem *graph) {_secondaryGraph = graph;} + qreal yAtX(qreal x); qreal distanceAtTime(qreal time); @@ -69,6 +72,8 @@ private: qreal _sx, _sy; QPen _pen; bool _time; + + GraphItem *_secondaryGraph; }; #endif // GRAPHITEM_H diff --git a/src/GUI/graphview.cpp b/src/GUI/graphview.cpp index 2e88811e..3b51c13c 100644 --- a/src/GUI/graphview.cpp +++ b/src/GUI/graphview.cpp @@ -419,14 +419,16 @@ void GraphView::updateSliderInfo() { QLocale l(QLocale::system()); qreal r = 0, y = 0; + GraphItem *cardinal = (_graphs.count() == 1 || (_graphs.count() == 2 + && _graphs.first()->secondaryGraph())) ? _graphs.first() : 0; - if (_graphs.count() == 1) { - QRectF br(_graphs.first()->bounds()); + if (cardinal) { + QRectF br(_bounds); if (br.height() < _minYRange) br.adjust(0, -(_minYRange/2 - br.height()/2), 0, _minYRange/2 - br.height()/2); - y = _graphs.first()->yAtX(_sliderPos); + y = cardinal->yAtX(_sliderPos); r = (y - br.bottom()) / br.height(); } @@ -436,11 +438,17 @@ void GraphView::updateSliderInfo() _sliderInfo->setSide(s); _sliderInfo->setPos(QPointF(0, _slider->boundingRect().height() * r)); - _sliderInfo->setText(_graphType == Time ? Format::timeSpan(_sliderPos, + QString xText(_graphType == Time ? Format::timeSpan(_sliderPos, bounds().width() > 3600) : l.toString(_sliderPos * _xScale, 'f', 1) - + UNIT_SPACE + _xUnits, (_graphs.count() > 1) ? QString() - : l.toString(-y * _yScale + _yOffset, 'f', _precision) + UNIT_SPACE - + _yUnits); + + UNIT_SPACE + _xUnits); + QString yText((!cardinal) ? QString() : l.toString(-y * _yScale + _yOffset, + 'f', _precision) + UNIT_SPACE + _yUnits); + if (cardinal && cardinal->secondaryGraph()) { + qreal delta = y - cardinal->secondaryGraph()->yAtX(_sliderPos); + yText += " \u0394" + l.toString(-delta * _yScale + _yOffset, 'f', + _precision) + UNIT_SPACE + _yUnits; + } + _sliderInfo->setText(xText, yText); } void GraphView::emitSliderPositionChanged(const QPointF &pos) diff --git a/src/GUI/gui.cpp b/src/GUI/gui.cpp index 410fb726..9a75ad6c 100644 --- a/src/GUI/gui.cpp +++ b/src/GUI/gui.cpp @@ -909,9 +909,14 @@ void GUI::openOptions() Track::action(options.option); \ reload = true; \ } -#define SET_DATA_OPTION(option, action) \ +#define SET_ROUTE_OPTION(option, action) \ if (options.option != _options.option) { \ - Data::action(options.option); \ + Route::action(options.option); \ + reload = true; \ + } +#define SET_WAYPOINT_OPTION(option, action) \ + if (options.option != _options.option) { \ + Waypoint::action(options.option); \ reload = true; \ } @@ -957,13 +962,18 @@ void GUI::openOptions() SET_TRACK_OPTION(pauseSpeed, setPauseSpeed); SET_TRACK_OPTION(pauseInterval, setPauseInterval); SET_TRACK_OPTION(useReportedSpeed, useReportedSpeed); + SET_TRACK_OPTION(dataUseDEM, useDEM); + SET_TRACK_OPTION(showSecondaryElevation, showSecondaryElevation); + SET_TRACK_OPTION(showSecondarySpeed, showSecondarySpeed); - SET_DATA_OPTION(dataUseDEM, useDEM); + SET_ROUTE_OPTION(dataUseDEM, useDEM); + SET_ROUTE_OPTION(showSecondaryElevation, showSecondaryElevation); + + SET_WAYPOINT_OPTION(dataUseDEM, useDEM); + SET_WAYPOINT_OPTION(showSecondaryElevation, showSecondaryElevation); if (options.poiRadius != _options.poiRadius) _poi->setRadius(options.poiRadius); - if (options.poiUseDEM != _options.poiUseDEM) - _poi->useDEM(options.poiUseDEM); if (options.pixmapCache != _options.pixmapCache) QPixmapCache::setCacheLimit(options.pixmapCache * 1024); @@ -1851,10 +1861,14 @@ void GUI::writeSettings() settings.setValue(USE_REPORTED_SPEED_SETTING, _options.useReportedSpeed); if (_options.dataUseDEM != DATA_USE_DEM_DEFAULT) settings.setValue(DATA_USE_DEM_SETTING, _options.dataUseDEM); + if (_options.showSecondaryElevation != SHOW_SECONDARY_ELEVATION_DEFAULT) + settings.setValue(SHOW_SECONDARY_ELEVATION_SETTING, + _options.showSecondaryElevation); + if (_options.showSecondarySpeed != SHOW_SECONDARY_SPEED_DEFAULT) + settings.setValue(SHOW_SECONDARY_SPEED_SETTING, + _options.showSecondarySpeed); if (_options.poiRadius != POI_RADIUS_DEFAULT) settings.setValue(POI_RADIUS_SETTING, _options.poiRadius); - if (_options.poiUseDEM != POI_USE_DEM_DEFAULT) - settings.setValue(POI_USE_DEM_SETTING, _options.poiUseDEM); if (_options.useOpenGL != USE_OPENGL_DEFAULT) settings.setValue(USE_OPENGL_SETTING, _options.useOpenGL); #ifdef ENABLE_HTTP2 @@ -2119,14 +2133,18 @@ void GUI::readSettings() USE_REPORTED_SPEED_DEFAULT).toBool(); _options.dataUseDEM = settings.value(DATA_USE_DEM_SETTING, DATA_USE_DEM_DEFAULT).toBool(); + _options.showSecondaryElevation = settings.value( + SHOW_SECONDARY_ELEVATION_SETTING, + SHOW_SECONDARY_ELEVATION_DEFAULT).toBool(); + _options.showSecondarySpeed = settings.value( + SHOW_SECONDARY_SPEED_SETTING, + SHOW_SECONDARY_SPEED_DEFAULT).toBool(); _options.automaticPause = settings.value(AUTOMATIC_PAUSE_SETTING, AUTOMATIC_PAUSE_DEFAULT).toBool(); _options.pauseInterval = settings.value(PAUSE_INTERVAL_SETTING, PAUSE_INTERVAL_DEFAULT).toInt(); _options.poiRadius = settings.value(POI_RADIUS_SETTING, POI_RADIUS_DEFAULT) .toInt(); - _options.poiUseDEM = settings.value(POI_USE_DEM_SETTING, - POI_USE_DEM_DEFAULT).toBool(); _options.useOpenGL = settings.value(USE_OPENGL_SETTING, USE_OPENGL_DEFAULT) .toBool(); #ifdef ENABLE_HTTP2 @@ -2206,10 +2224,15 @@ void GUI::readSettings() Track::setPauseSpeed(_options.pauseSpeed); Track::setPauseInterval(_options.pauseInterval); Track::useReportedSpeed(_options.useReportedSpeed); - Data::useDEM(_options.dataUseDEM); + Track::useDEM(_options.dataUseDEM); + Track::showSecondaryElevation(_options.showSecondaryElevation); + Track::showSecondarySpeed(_options.showSecondarySpeed); + Route::useDEM(_options.dataUseDEM); + Route::showSecondaryElevation(_options.showSecondaryElevation); + Waypoint::useDEM(_options.dataUseDEM); + Waypoint::showSecondaryElevation(_options.showSecondaryElevation); _poi->setRadius(_options.poiRadius); - _poi->useDEM(_options.poiUseDEM); QPixmapCache::setCacheLimit(_options.pixmapCache * 1024); diff --git a/src/GUI/heartrategraphitem.cpp b/src/GUI/heartrategraphitem.cpp index 90436639..1ff252fd 100644 --- a/src/GUI/heartrategraphitem.cpp +++ b/src/GUI/heartrategraphitem.cpp @@ -5,7 +5,7 @@ HeartRateGraphItem::HeartRateGraphItem(const Graph &graph, GraphType type, int width, const QColor &color, QGraphicsItem *parent) - : GraphItem(graph, type, width, color, parent) + : GraphItem(graph, type, width, color, Qt::SolidLine, parent) { } diff --git a/src/GUI/optionsdialog.cpp b/src/GUI/optionsdialog.cpp index b12794dc..10732440 100644 --- a/src/GUI/optionsdialog.cpp +++ b/src/GUI/optionsdialog.cpp @@ -407,6 +407,8 @@ QWidget *OptionsDialog::createDataPage() _reportedSpeed->setChecked(true); else _computedSpeed->setChecked(true); + _showSecondarySpeed = new QCheckBox(tr("Show secondary speed")); + _showSecondarySpeed->setChecked(_options->showSecondarySpeed); _dataGPSElevation = new QRadioButton(tr("GPS data")); _dataDEMElevation = new QRadioButton(tr("DEM data")); @@ -414,7 +416,8 @@ QWidget *OptionsDialog::createDataPage() _dataDEMElevation->setChecked(true); else _dataGPSElevation->setChecked(true); - + _showSecondaryElevation = new QCheckBox(tr("Show secondary elevation")); + _showSecondaryElevation->setChecked(_options->showSecondaryElevation); QWidget *sourceTab = new QWidget(); QVBoxLayout *sourceTabLayout = new QVBoxLayout(); @@ -426,6 +429,7 @@ QWidget *OptionsDialog::createDataPage() QVBoxLayout *speedOptions = new QVBoxLayout(); speedOptions->addWidget(_computedSpeed); speedOptions->addWidget(_reportedSpeed); + speedOptions->addWidget(_showSecondarySpeed); QButtonGroup *elevationGroup = new QButtonGroup(this); elevationGroup->addButton(_dataGPSElevation); @@ -433,6 +437,7 @@ QWidget *OptionsDialog::createDataPage() QVBoxLayout *elevationOptions = new QVBoxLayout(); elevationOptions->addWidget(_dataGPSElevation); elevationOptions->addWidget(_dataDEMElevation); + elevationOptions->addWidget(_showSecondaryElevation); QFormLayout *formLayout = new QFormLayout(); formLayout->addRow(tr("Speed:"), speedOptions); @@ -445,12 +450,14 @@ QWidget *OptionsDialog::createDataPage() speedLayout->addWidget(_computedSpeed); speedLayout->addWidget(_reportedSpeed); + speedLayout->addWidget(_showSecondarySpeed); QGroupBox *speedBox = new QGroupBox(tr("Speed")); speedBox->setLayout(speedLayout); elevationLayout->addWidget(_dataGPSElevation); elevationLayout->addWidget(_dataDEMElevation); + elevationLayout->addWidget(_showSecondaryElevation); QGroupBox *elevationBox = new QGroupBox(tr("Elevation")); elevationBox->setLayout(elevationLayout); @@ -472,13 +479,6 @@ QWidget *OptionsDialog::createDataPage() QWidget *OptionsDialog::createPOIPage() { - _poiGPSElevation = new QRadioButton(tr("GPS data")); - _poiDEMElevation = new QRadioButton(tr("DEM data")); - if (_options->poiUseDEM) - _poiDEMElevation->setChecked(true); - else - _poiGPSElevation->setChecked(true); - _poiRadius = new QDoubleSpinBox(); _poiRadius->setSingleStep(1); _poiRadius->setDecimals(1); @@ -493,13 +493,8 @@ QWidget *OptionsDialog::createPOIPage() _poiRadius->setSuffix(UNIT_SPACE + tr("km")); } - QVBoxLayout *elevationLayout = new QVBoxLayout(); - elevationLayout->addWidget(_poiGPSElevation); - elevationLayout->addWidget(_poiDEMElevation); - QFormLayout *poiLayout = new QFormLayout(); poiLayout->addRow(tr("Radius:"), _poiRadius); - poiLayout->addRow(tr("Elevation:"), elevationLayout); QWidget *poiTab = new QWidget(); poiTab->setLayout(poiLayout); @@ -725,13 +720,14 @@ void OptionsDialog::accept() _options->pauseInterval = _pauseInterval->value(); _options->useReportedSpeed = _reportedSpeed->isChecked(); _options->dataUseDEM = _dataDEMElevation->isChecked(); + _options->showSecondaryElevation = _showSecondaryElevation->isChecked(); + _options->showSecondarySpeed = _showSecondarySpeed->isChecked(); qreal poiRadius = (_options->units == Imperial) ? _poiRadius->value() * MIINM : (_options->units == Nautical) ? _poiRadius->value() * NMIINM : _poiRadius->value() * KMINM; if (qAbs(poiRadius - _options->poiRadius) > 0.01) _options->poiRadius = poiRadius; - _options->poiUseDEM = _poiDEMElevation->isChecked(); _options->useOpenGL = _useOpenGL->isChecked(); #ifdef ENABLE_HTTP2 diff --git a/src/GUI/optionsdialog.h b/src/GUI/optionsdialog.h index 290d1d77..417cac8c 100644 --- a/src/GUI/optionsdialog.h +++ b/src/GUI/optionsdialog.h @@ -54,9 +54,10 @@ struct Options { int pauseInterval; bool useReportedSpeed; bool dataUseDEM; + bool showSecondaryElevation; + bool showSecondarySpeed; // POI int poiRadius; - bool poiUseDEM; // System bool useOpenGL; #ifdef ENABLE_HTTP2 @@ -142,10 +143,10 @@ private: QRadioButton *_reportedSpeed; QRadioButton *_dataGPSElevation; QRadioButton *_dataDEMElevation; + QCheckBox *_showSecondaryElevation; + QCheckBox *_showSecondarySpeed; // POI QDoubleSpinBox *_poiRadius; - QRadioButton *_poiGPSElevation; - QRadioButton *_poiDEMElevation; // System QSpinBox *_pixmapCache; QSpinBox *_connectionTimeout; diff --git a/src/GUI/powergraphitem.cpp b/src/GUI/powergraphitem.cpp index 81c1377a..0333170c 100644 --- a/src/GUI/powergraphitem.cpp +++ b/src/GUI/powergraphitem.cpp @@ -5,7 +5,7 @@ PowerGraphItem::PowerGraphItem(const Graph &graph, GraphType type, int width, const QColor &color, QGraphicsItem *parent) - : GraphItem(graph, type, width, color, parent) + : GraphItem(graph, type, width, color, Qt::SolidLine, parent) { } diff --git a/src/GUI/settings.h b/src/GUI/settings.h index ad4f684d..84e1ee88 100644 --- a/src/GUI/settings.h +++ b/src/GUI/settings.h @@ -145,6 +145,10 @@ #define USE_REPORTED_SPEED_DEFAULT false #define DATA_USE_DEM_SETTING "dataUseDEM" #define DATA_USE_DEM_DEFAULT false +#define SHOW_SECONDARY_ELEVATION_SETTING "showSecondaryElevation" +#define SHOW_SECONDARY_ELEVATION_DEFAULT false +#define SHOW_SECONDARY_SPEED_SETTING "showSecondarySpeed" +#define SHOW_SECONDARY_SPEED_DEFAULT false #define POI_RADIUS_SETTING "poiRadius" #define POI_RADIUS_DEFAULT (int)(IMPERIAL_UNITS() ? MIINM : KMINM) #define POI_USE_DEM_SETTING "poiUseDEM" diff --git a/src/GUI/speedgraph.cpp b/src/GUI/speedgraph.cpp index e85f70b4..305981bd 100644 --- a/src/GUI/speedgraph.cpp +++ b/src/GUI/speedgraph.cpp @@ -40,31 +40,46 @@ void SpeedGraph::setInfo() clearInfo(); } +GraphItem *SpeedGraph::loadGraph(const Graph &graph, const Track &track, + const QColor &color, bool primary) +{ + if (!graph.isValid()) + return 0; + + SpeedGraphItem *gi = new SpeedGraphItem(graph, _graphType, _width, + color, primary ? Qt::SolidLine : Qt::DashLine, track.movingTime()); + gi->setTimeType(_timeType); + gi->setUnits(_units); + + _tracks.append(gi); + if (_showTracks) + addGraph(gi); + + if (primary) { + _avg.append(QPointF(track.distance(), gi->avg())); + _mavg.append(QPointF(track.distance(), gi->mavg())); + } + + return gi; +} + QList SpeedGraph::loadData(const Data &data) { QList graphs; for (int i = 0; i < data.tracks().count(); i++) { + GraphItem *primary, *secondary; + QColor color(_palette.nextColor()); const Track &track = data.tracks().at(i); - const Graph &graph = track.speed(); + const GraphPair &gp = track.speed(); - if (!graph.isValid()) { - _palette.nextColor(); - graphs.append(0); - } else { - SpeedGraphItem *gi = new SpeedGraphItem(graph, _graphType, _width, - _palette.nextColor(), track.movingTime()); - gi->setTimeType(_timeType); - gi->setUnits(_units); + primary = loadGraph(gp.primary(), track, color, true); + secondary = primary + ? loadGraph(gp.secondary(), track, color, false) : 0; + if (primary && secondary) + primary->setSecondaryGraph(secondary); - _tracks.append(gi); - if (_showTracks) - addGraph(gi); - - _avg.append(QPointF(track.distance(), gi->avg())); - _mavg.append(QPointF(track.distance(), gi->mavg())); - graphs.append(gi); - } + graphs.append(primary); } for (int i = 0; i < data.routes().count(); i++) { diff --git a/src/GUI/speedgraph.h b/src/GUI/speedgraph.h index ca06d6c6..8bcaccc2 100644 --- a/src/GUI/speedgraph.h +++ b/src/GUI/speedgraph.h @@ -5,6 +5,7 @@ #include "graphtab.h" class SpeedGraphItem; +class Track; class SpeedGraph : public GraphTab { @@ -22,6 +23,8 @@ public: void showTracks(bool show); private: + GraphItem *loadGraph(const Graph &graph, const Track &track, + const QColor &color, bool primary); qreal avg() const; qreal max() const {return bounds().bottom();} void setYUnits(); diff --git a/src/GUI/speedgraphitem.cpp b/src/GUI/speedgraphitem.cpp index 39d98b1b..57a682b1 100644 --- a/src/GUI/speedgraphitem.cpp +++ b/src/GUI/speedgraphitem.cpp @@ -5,8 +5,8 @@ SpeedGraphItem::SpeedGraphItem(const Graph &graph, GraphType type, int width, - const QColor &color, qreal movingTime, QGraphicsItem *parent) - : GraphItem(graph, type, width, color, parent) + const QColor &color, Qt::PenStyle style, qreal movingTime, + QGraphicsItem *parent) : GraphItem(graph, type, width, color, style, parent) { _timeType = Total; diff --git a/src/GUI/speedgraphitem.h b/src/GUI/speedgraphitem.h index 4c45f43c..649ca41b 100644 --- a/src/GUI/speedgraphitem.h +++ b/src/GUI/speedgraphitem.h @@ -10,7 +10,8 @@ class SpeedGraphItem : public GraphItem public: SpeedGraphItem(const Graph &graph, GraphType type, int width, - const QColor &color, qreal movingTime, QGraphicsItem *parent = 0); + const QColor &color, Qt::PenStyle style, qreal movingTime, + QGraphicsItem *parent = 0); qreal avg() const {return _avg;} qreal mavg() const {return _mavg;} diff --git a/src/GUI/temperaturegraphitem.cpp b/src/GUI/temperaturegraphitem.cpp index 886ff5b7..4844cf1d 100644 --- a/src/GUI/temperaturegraphitem.cpp +++ b/src/GUI/temperaturegraphitem.cpp @@ -5,7 +5,7 @@ TemperatureGraphItem::TemperatureGraphItem(const Graph &graph, GraphType type, int width, const QColor &color, QGraphicsItem *parent) - : GraphItem(graph, type, width, color, parent) + : GraphItem(graph, type, width, color, Qt::SolidLine, parent) { _min = GraphItem::min(); _max = GraphItem::max(); diff --git a/src/GUI/waypointitem.cpp b/src/GUI/waypointitem.cpp index cc05bb83..3e93de96 100644 --- a/src/GUI/waypointitem.cpp +++ b/src/GUI/waypointitem.cpp @@ -21,9 +21,13 @@ QString WaypointItem::info() const tt.insert(qApp->translate("WaypointItem", "Name"), _waypoint.name()); tt.insert(qApp->translate("WaypointItem", "Coordinates"), Format::coordinates(_waypoint.coordinates(), _format)); - if (_waypoint.hasElevation()) - tt.insert(qApp->translate("WaypointItem", "Elevation"), - Format::elevation(_waypoint.elevation(), _units)); + if (!std::isnan(_waypoint.elevations().first)) { + QString val = Format::elevation(_waypoint.elevations().first, _units); + if (!std::isnan(_waypoint.elevations().second)) + val += " (" + Format::elevation(_waypoint.elevations().second, + _units) + ")"; + tt.insert(qApp->translate("WaypointItem", "Elevation"), val); + } if (_waypoint.timestamp().isValid()) tt.insert(qApp->translate("WaypointItem", "Date"), _waypoint.timestamp().toString(Qt::SystemLocaleShortDate)); diff --git a/src/data/data.cpp b/src/data/data.cpp index 3731b901..205f5f32 100644 --- a/src/data/data.cpp +++ b/src/data/data.cpp @@ -20,7 +20,6 @@ #include "cupparser.h" #include "gpiparser.h" #include "smlparser.h" -#include "dem.h" #include "data.h" @@ -73,49 +72,17 @@ static QMap parsers() return map; } - QMap Data::_parsers = parsers(); -bool Data::_useDEM = false; void Data::processData(QList &trackData, QList &routeData) { - for (int i = 0; i < trackData.count(); i++) { - TrackData &track = trackData[i]; - for (int j = 0; j < track.size(); j++) { - SegmentData &segment = track[j]; - for (int k = 0; k < segment.size(); k++) { - Trackpoint &t = segment[k]; - if (!t.hasElevation() || _useDEM) { - qreal elevation = DEM::elevation(t.coordinates()); - if (!std::isnan(elevation)) - t.setElevation(elevation); - } - } - } + for (int i = 0; i < trackData.count(); i++) _tracks.append(Track(trackData.at(i))); - } - for (int i = 0; i < routeData.count(); i++) { - RouteData &route = routeData[i]; - for (int j = 0; j < route.size(); j++) { - Waypoint &w = route[j]; - if (!w.hasElevation() || _useDEM) { - qreal elevation = DEM::elevation(w.coordinates()); - if (!std::isnan(elevation)) - w.setElevation(elevation); - } - } + for (int i = 0; i < routeData.count(); i++) _routes.append(Route(routeData.at(i))); - } - for (int i = 0; i < _waypoints.size(); i++) { - if (!_waypoints.at(i).hasElevation() || _useDEM) { - qreal elevation = DEM::elevation(_waypoints.at(i).coordinates()); - if (!std::isnan(elevation)) - _waypoints[i].setElevation(elevation); - } - } } -Data::Data(const QString &fileName, bool poi) +Data::Data(const QString &fileName) { QFile file(fileName); QFileInfo fi(fileName); @@ -134,8 +101,7 @@ Data::Data(const QString &fileName, bool poi) if ((it = _parsers.find(fi.suffix().toLower())) != _parsers.end()) { if (it.value()->parse(&file, trackData, routeData, _polygons, _waypoints)) { - if (!poi) - processData(trackData, routeData); + processData(trackData, routeData); _valid = true; return; } else { @@ -146,8 +112,7 @@ Data::Data(const QString &fileName, bool poi) for (it = _parsers.begin(); it != _parsers.end(); it++) { if (it.value()->parse(&file, trackData, routeData, _polygons, _waypoints)) { - if (!poi) - processData(trackData, routeData); + processData(trackData, routeData); _valid = true; return; } @@ -198,8 +163,3 @@ QStringList Data::filter() return filter; } - -void Data::useDEM(bool use) -{ - _useDEM = use; -} diff --git a/src/data/data.h b/src/data/data.h index 9367efa8..3e77a895 100644 --- a/src/data/data.h +++ b/src/data/data.h @@ -14,7 +14,7 @@ class Data { public: - Data(const QString &fileName, bool poi = false); + Data(const QString &fileName); bool isValid() const {return _valid;} const QString &errorString() const {return _errorString;} @@ -28,8 +28,6 @@ public: static QString formats(); static QStringList filter(); - static void useDEM(bool use); - private: void processData(QList &trackData, QList &routeData); @@ -43,7 +41,6 @@ private: QVector _waypoints; static QMap _parsers; - static bool _useDEM; }; #endif // DATA_H diff --git a/src/data/graph.h b/src/data/graph.h index 28479487..f6fc267b 100644 --- a/src/data/graph.h +++ b/src/data/graph.h @@ -67,4 +67,17 @@ public: } }; +class GraphPair +{ +public: + GraphPair(const Graph &primary, const Graph &secondary) + : _primary(primary), _secondary(secondary) {} + + const Graph &primary() const {return _primary;} + const Graph &secondary() const {return _secondary;} + +private: + Graph _primary, _secondary; +}; + #endif // GRAPH_H diff --git a/src/data/poi.cpp b/src/data/poi.cpp index 10a28a4a..bf8927a3 100644 --- a/src/data/poi.cpp +++ b/src/data/poi.cpp @@ -14,12 +14,11 @@ POI::POI(QObject *parent) : QObject(parent) { _errorLine = 0; _radius = 1000; - _useDEM = false; } bool POI::loadFile(const QString &path) { - Data data(path, true); + Data data(path); FileIndex index; index.enabled = true; @@ -89,17 +88,6 @@ void POI::search(const RectC &rect, QSet &set) const _tree.Search(min, max, cb, &set); } -void POI::appendElevation(QList &points) const -{ - for (int i = 0; i < points.size(); i++) { - if (!points.at(i).hasElevation() || _useDEM) { - qreal elevation = DEM::elevation(points.at(i).coordinates()); - if (!std::isnan(elevation)) - points[i].setElevation(elevation); - } - } -} - QList POI::points(const Path &path) const { QList ret; @@ -135,8 +123,6 @@ QList POI::points(const Path &path) const for (it = set.constBegin(); it != set.constEnd(); ++it) ret.append(_data.at(*it)); - appendElevation(ret); - return ret; } @@ -158,8 +144,6 @@ QList POI::points(const Waypoint &point) const for (it = set.constBegin(); it != set.constEnd(); ++it) ret.append(_data.at(*it)); - appendElevation(ret); - return ret; } @@ -183,8 +167,6 @@ QList POI::points(const Area &area) const for (it = set.constBegin(); it != set.constEnd(); ++it) ret.append(_data.at(*it)); - appendElevation(ret); - return ret; } @@ -230,10 +212,3 @@ void POI::setRadius(unsigned radius) emit pointsChanged(); } - -void POI::useDEM(bool use) -{ - _useDEM = use; - - emit pointsChanged(); -} diff --git a/src/data/poi.h b/src/data/poi.h index fffe0480..f6b74378 100644 --- a/src/data/poi.h +++ b/src/data/poi.h @@ -26,7 +26,6 @@ public: unsigned radius() const {return _radius;} void setRadius(unsigned radius); - void useDEM(bool use); QList points(const Path &path) const; QList points(const Waypoint &point) const; @@ -49,7 +48,6 @@ private: bool loadFile(const QString &path, bool dir); void search(const RectC &rect, QSet &set) const; - void appendElevation(QList &points) const; POITree _tree; QVector _data; @@ -57,7 +55,6 @@ private: QList _indexes; unsigned _radius; - bool _useDEM; QString _errorString; int _errorLine; diff --git a/src/data/route.cpp b/src/data/route.cpp index fd610b5c..dec02bbf 100644 --- a/src/data/route.cpp +++ b/src/data/route.cpp @@ -1,5 +1,8 @@ +#include "dem.h" #include "route.h" +bool Route::_useDEM = false; +bool Route::_show2ndElevation = false; Route::Route(const RouteData &data) : _data(data) { @@ -25,7 +28,7 @@ Path Route::path() const return ret; } -Graph Route::elevation() const +Graph Route::gpsElevation() const { Graph graph; graph.append(GraphSegment()); @@ -38,6 +41,38 @@ Graph Route::elevation() const return graph; } +Graph Route::demElevation() const +{ + Graph graph; + graph.append(GraphSegment()); + GraphSegment &gs = graph.last(); + + for (int i = 0; i < _data.size(); i++) { + qreal dem = DEM::elevation(_data.at(i).coordinates()); + if (!std::isnan(dem)) + gs.append(GraphPoint(_distance.at(i), NAN, dem)); + } + + return graph; +} + +GraphPair Route::elevation() const +{ + if (_useDEM) { + Graph dem(demElevation()); + if (dem.isValid()) + return GraphPair(dem, _show2ndElevation ? gpsElevation() : Graph()); + else + return GraphPair(gpsElevation(), Graph()); + } else { + Graph gps(gpsElevation()); + if (gps.isValid()) + return GraphPair(gps, _show2ndElevation ? demElevation() : Graph()); + else + return GraphPair(demElevation(), Graph()); + } +} + qreal Route::distance() const { return (_distance.isEmpty()) ? 0 : _distance.last(); diff --git a/src/data/route.h b/src/data/route.h index e3eda33a..ba52ea03 100644 --- a/src/data/route.h +++ b/src/data/route.h @@ -11,12 +11,9 @@ class Route public: Route(const RouteData &data); - Path path() const; - const RouteData &data() const {return _data;} - - Graph elevation() const; - + Path path() const; + GraphPair elevation() const; qreal distance() const; const QString &name() const {return _data.name();} @@ -26,9 +23,19 @@ public: bool isValid() const {return _data.size() >= 2;} + static void useDEM(bool use) {_useDEM = use;} + static void showSecondaryElevation(bool show) + {_show2ndElevation = show;} + private: + Graph gpsElevation() const; + Graph demElevation() const; + RouteData _data; QVector _distance; + + static bool _useDEM; + static bool _show2ndElevation; }; #endif // ROUTE_H diff --git a/src/data/track.cpp b/src/data/track.cpp index b00a8061..94d46231 100644 --- a/src/data/track.cpp +++ b/src/data/track.cpp @@ -1,3 +1,4 @@ +#include "dem.h" #include "track.h" @@ -13,6 +14,9 @@ int Track::_pauseInterval = 10; bool Track::_outlierEliminate = true; bool Track::_useReportedSpeed = false; +bool Track::_useDEM = false; +bool Track::_show2ndElevation = false; +bool Track::_show2ndSpeed = false; static qreal avg(const QVector &v) @@ -213,7 +217,7 @@ Track::Track(const TrackData &data) : _data(data), _pause(0) } } -Graph Track::elevation() const +Graph Track::gpsElevation() const { Graph ret; @@ -237,7 +241,48 @@ Graph Track::elevation() const return ret; } -Graph Track::speed() const +Graph Track::demElevation() const +{ + Graph ret; + + for (int i = 0; i < _data.size(); i++) { + const SegmentData &sd = _data.at(i); + if (sd.size() < 2) + continue; + const Segment &seg = _segments.at(i); + GraphSegment gs; + + for (int j = 0; j < sd.size(); j++) { + qreal dem = DEM::elevation(sd.at(j).coordinates()); + if (std::isnan(dem) || seg.outliers.contains(j)) + continue; + gs.append(GraphPoint(seg.distance.at(j), seg.time.at(j), dem)); + } + + ret.append(filter(gs, _elevationWindow)); + } + + return ret; +} + +GraphPair Track::elevation() const +{ + if (_useDEM) { + Graph dem(demElevation()); + if (dem.isValid()) + return GraphPair(dem, _show2ndElevation ? gpsElevation() : Graph()); + else + return GraphPair(gpsElevation(), Graph()); + } else { + Graph gps(gpsElevation()); + if (gps.isValid()) + return GraphPair(gps, _show2ndElevation ? demElevation() : Graph()); + else + return GraphPair(demElevation(), Graph()); + } +} + +Graph Track::computedSpeed() const { Graph ret; @@ -251,14 +296,10 @@ Graph Track::speed() const qreal v; for (int j = 0; j < sd.size(); j++) { - if (seg.stop.contains(j) && (!std::isnan(seg.speed.at(j)) - || sd.at(j).hasSpeed())) { + if (seg.stop.contains(j) && !std::isnan(seg.speed.at(j))) { v = 0; stop.append(gs.size()); - } else if (_useReportedSpeed && sd.at(j).hasSpeed() - && !seg.outliers.contains(j)) - v = sd.at(j).speed(); - else if (!std::isnan(seg.speed.at(j)) && !seg.outliers.contains(j)) + } else if (!std::isnan(seg.speed.at(j)) && !seg.outliers.contains(j)) v = seg.speed.at(j); else continue; @@ -276,6 +317,60 @@ Graph Track::speed() const return ret; } +Graph Track::reportedSpeed() const +{ + Graph ret; + + for (int i = 0; i < _data.size(); i++) { + const SegmentData &sd = _data.at(i); + if (sd.size() < 2) + continue; + const Segment &seg = _segments.at(i); + GraphSegment gs; + QList stop; + qreal v; + + for (int j = 0; j < sd.size(); j++) { + if (seg.stop.contains(j) && sd.at(j).hasSpeed()) { + v = 0; + stop.append(gs.size()); + } else if (sd.at(j).hasSpeed() && !seg.outliers.contains(j)) + v = sd.at(j).speed(); + else + continue; + + gs.append(GraphPoint(seg.distance.at(j), seg.time.at(j), v)); + } + + ret.append(filter(gs, _speedWindow)); + GraphSegment &filtered = ret.last(); + + for (int j = 0; j < stop.size(); j++) + filtered[stop.at(j)].setY(0); + } + + return ret; +} + +GraphPair Track::speed() const +{ + if (_useReportedSpeed) { + Graph reported(reportedSpeed()); + if (reported.isValid()) + return GraphPair(reported, _show2ndSpeed ? computedSpeed() + : Graph()); + else + return GraphPair(computedSpeed(), Graph()); + } else { + Graph computed(computedSpeed()); + if (computed.isValid()) + return GraphPair(computed, _show2ndSpeed ? reportedSpeed() + : Graph()); + else + return GraphPair(reportedSpeed(), Graph()); + } +} + Graph Track::heartRate() const { Graph ret; diff --git a/src/data/track.h b/src/data/track.h index 09a294b0..03c926e6 100644 --- a/src/data/track.h +++ b/src/data/track.h @@ -17,8 +17,8 @@ public: Path path() const; - Graph elevation() const; - Graph speed() const; + GraphPair elevation() const; + GraphPair speed() const; Graph heartRate() const; Graph temperature() const; Graph cadence() const; @@ -48,6 +48,11 @@ public: static void setOutlierElimination(bool eliminate) {_outlierEliminate = eliminate;} static void useReportedSpeed(bool use) {_useReportedSpeed = use;} + static void useDEM(bool use) {_useDEM = use;} + static void showSecondaryElevation(bool show) + {_show2ndElevation = show;} + static void showSecondarySpeed(bool show) + {_show2ndSpeed = show;} private: struct Segment { @@ -60,6 +65,11 @@ private: bool discardStopPoint(const Segment &seg, int i) const; + Graph demElevation() const; + Graph gpsElevation() const; + Graph reportedSpeed() const; + Graph computedSpeed() const; + TrackData _data; QList _segments; qreal _pause; @@ -74,6 +84,9 @@ private: static qreal _pauseSpeed; static int _pauseInterval; static bool _useReportedSpeed; + static bool _useDEM; + static bool _show2ndElevation; + static bool _show2ndSpeed; }; #endif // TRACK_H diff --git a/src/data/waypoint.cpp b/src/data/waypoint.cpp new file mode 100644 index 00000000..e8f52114 --- /dev/null +++ b/src/data/waypoint.cpp @@ -0,0 +1,23 @@ +#include "dem.h" +#include "waypoint.h" + +bool Waypoint::_useDEM = false; +bool Waypoint::_show2ndElevation = false; + +QPair Waypoint::elevations() const +{ + if (_useDEM) { + qreal dem = DEM::elevation(coordinates()); + if (!std::isnan(dem)) + return QPair(dem, _show2ndElevation ? elevation() + : NAN); + else + return QPair(elevation(), NAN); + } else { + if (hasElevation()) + return QPair(elevation(), _show2ndElevation + ? DEM::elevation(coordinates()) : NAN); + else + return QPair(DEM::elevation(coordinates()), NAN); + } +} diff --git a/src/data/waypoint.h b/src/data/waypoint.h index 03e23f19..22e3ac6d 100644 --- a/src/data/waypoint.h +++ b/src/data/waypoint.h @@ -14,9 +14,9 @@ class Waypoint { public: - Waypoint() {_elevation = NAN;} - Waypoint(const Coordinates &coordinates) : _coordinates(coordinates) - {_elevation = NAN;} + Waypoint() : _elevation(NAN) {} + Waypoint(const Coordinates &coordinates) + : _coordinates(coordinates), _elevation(NAN) {} const Coordinates &coordinates() const {return _coordinates;} const QString &name() const {return _name;} @@ -28,6 +28,8 @@ public: const QDateTime ×tamp() const {return _timestamp;} qreal elevation() const {return _elevation;} + QPair elevations() const; + void setCoordinates(const Coordinates &coordinates) {_coordinates = coordinates;} void setName(const QString &name) {_name = name;} @@ -46,6 +48,10 @@ public: {return this->_name == other._name && this->_coordinates == other._coordinates;} + static void useDEM(bool use) {_useDEM = use;} + static void showSecondaryElevation(bool show) + {_show2ndElevation = show;} + private: Coordinates _coordinates; QString _name; @@ -56,6 +62,9 @@ private: QVector _links; QDateTime _timestamp; qreal _elevation; + + static bool _useDEM; + static bool _show2ndElevation; }; inline uint qHash(const Waypoint &key) @@ -67,11 +76,9 @@ inline uint qHash(const Waypoint &key) inline QDebug operator<<(QDebug dbg, const Waypoint &waypoint) { dbg.nospace() << "Waypoint(" << waypoint.coordinates() << ", " - << waypoint.name() << ", " << waypoint.description() << ")"; + << waypoint.name() << ")"; return dbg.space(); } #endif // QT_NO_DEBUG -Q_DECLARE_TYPEINFO(Waypoint, Q_MOVABLE_TYPE); - #endif // WAYPOINT_H