diff --git a/gpxsee.pro b/gpxsee.pro
index 08a5da4b..fd75bf1e 100644
--- a/gpxsee.pro
+++ b/gpxsee.pro
@@ -50,7 +50,9 @@ HEADERS += src/config.h \
src/tooltip.h \
src/route.h \
src/routeitem.h \
- src/graphitem.h
+ src/graphitem.h \
+ src/graph.h \
+ src/pathitem.h
SOURCES += src/main.cpp \
src/gui.cpp \
src/gpx.cpp \
diff --git a/gpxsee.qrc b/gpxsee.qrc
index 928d15e9..ac4aedfb 100644
--- a/gpxsee.qrc
+++ b/gpxsee.qrc
@@ -14,6 +14,7 @@
icons/arrow-left-double.png
icons/arrow-right-double.png
icons/view-fullscreen.png
+ icons/office-chart-line-stacked.png
lang/gpxsee_cs.qm
diff --git a/lang/gpxsee_cs.ts b/lang/gpxsee_cs.ts
index ccc3555d..b14809e2 100644
--- a/lang/gpxsee_cs.ts
+++ b/lang/gpxsee_cs.ts
@@ -5,54 +5,37 @@
ElevationGraph
- Distance
- Vzdálenost
-
-
-
-
+
Elevation
Výška
-
- km
- km
-
-
-
-
+
m
m
-
+
Ascent
Stoupání
-
+
Descent
Klesání
-
+
Minimum
Minimum
-
- mi
- mi
-
-
-
-
+
ft
ft
-
+
Maximum
Maximum
@@ -163,370 +146,377 @@
GUI
-
+
GPXSee is distributed under the terms of the GNU General Public License version 3. For more info about GPXSee visit the project homepage at
Program GPXSee je distribuován pod podmínkami licence GNU General Public License verze 3. Pro více informací navštivte stránky programu na adrese
-
+
Open file
Otevřít soubor
-
+
Open POI file
Otevřít POI soubor
-
+
Open
Otevřít
-
+
Quit
Ukončit
-
-
-
+
+
+
Keyboard controls
Ovládací klávesy
-
+
Close
Zavřít
-
+
Reload
Znovu načíst
-
+
Show
Zobrazit
-
-
+
+
File
Soubor
-
-
-
+
+
+
Data sources
Zdroje dat
-
+
Load POI file
Nahrát POI soubor
-
+
Close POI files
Zavřit POI soubory
-
+
Overlap POIs
Překrývat POI
-
+
Show POI labels
Zobrazovat názvy POI
-
+
Show POIs
Zobrazit POI
-
+
Show map
Zobrazit mapu
-
+
Clear tile cache
Vymazat mezipaměť dlaždic
-
-
-
+
+
+
Next map
Následující mapa
-
+
Show tracks
Zobrazit cesty
-
+
Show routes
Zobrazit trasy
-
+
Show waypoints
Zobrazit navigační body
-
+
Waypoint labels
Názvy navigačních bodů
-
+
Show graphs
Zobrazovat grafy
-
+
Show toolbars
Zobrazovat nástrojové lišty
-
+
Metric
Metrické
-
+
Imperial
Imperiální
-
+
Fullscreen mode
Celoobrazovkový režim
-
+
Next
Následující
-
+
Previous
Předchozí
-
+
Last
Poslední
-
+
First
První
-
+
Map
Mapa
-
+
+ Graph
+ Graf
+
+
+
POI
POI
-
+
POI files
POI soubory
-
+
Data
Data
-
+
Display
Zobrazit
-
+
Settings
Nastavení
-
+
Units
Jednotky
-
+
Help
Nápověda
-
+
Previous map
Předchozí mapa
-
-
+
+
Date
Datum
-
+
Routes
Trasy
-
+
No GPX files loaded
Nejsou načteny žádné GPX soubory
-
+
%1 files
%1 souborů
-
+
Next file
Následující soubor
-
+
Version
Verze
-
+
Print...
Tisknout...
-
+
Export to PDF...
Exportovat do PDF...
-
+
Waypoints
Navigační body
-
+
Previous file
Předchozí soubor
-
+
Route waypoints
Body tras
-
+
First file
První soubor
-
+
Last file
Poslední soubor
-
+
Append modifier
Modifikátor nahradit/přidat
-
+
Map (tiles) source URLs are read on program startup from the following file:
URL mapových zdrojů (dlaždic) jsou načteny při startu programu z následujícího souboru:
-
+
The file format is one map entry per line, consisting of the map name and tiles URL delimited by a TAB character. The tile X and Y coordinates are replaced with $x and $y in the URL and the zoom level is replaced with $z. An example map file could look like:
Formát souboru je jeden mapový záznam na řádku, kde mapový záznam sestává ze jména mapy a URL dlaždic navzájem oddělených tabulátorem. Souřadnice dlaždice jsou v URL nahrazeny řetězci $x a $y, úroven přiblížení (zoom) pak řetězcem $z. Příklad:
-
+
To make GPXSee load a POI file automatically on startup, add the file to the following directory:
POI soubory, které se mají automaticky nahrát při startu programu jsou načítány z následujícího adresáře:
-
+
GPX files (*.gpx);;All files (*)
Soubory GPX (*.gpx);;Všechny soubory (*)
-
-
+
+
Line: %1
Řádka: %1
-
+
GPX files (*.gpx);;CSV files (*.csv);;All files (*)
Soubory GPX (*.gpx);;Soubory CSV (*.csv);;Všechny soubory (*)
-
+
Tracks
Cesty
-
-
+
+
About GPXSee
O aplikaci GPXSee
-
+
Navigation
Navigace
-
+
Map sources
Mapové zdroje
-
+
POIs
POI body
-
+
+
Distance
Vzdálenost
-
+
+
Time
Čas
-
-
+
+
Error
Chyba
-
+
Error loading GPX file:
%1
Soubor GPX nelze otevřít:
%1
-
+
Error loading POI file:
%1
Soubor POI nelze otevřít:
@@ -534,53 +524,76 @@
- HeartRateGraph
+ GraphView
-
+
+ m
+ m
+
+
+
+ km
+ km
+
+
+
+ ft
+ ft
+
+
+
+ mi
+ mi
+
+
+
+ s
+ s
+
+
+
+ min
+ min
+
+
+
+ h
+ h
+
+
+
Distance
Vzdálenost
-
+
+ Time
+ Čas
+
+
+
+ HeartRateGraph
+
+
Heart rate
Tep
-
-
- km
- km
-
1/min
1/min
-
+
Average
Průměr
-
+
Maximum
Maximum
-
-
- m
- m
-
-
-
- ft
- ft
-
-
-
- mi
- mi
-
Misc
@@ -610,7 +623,7 @@
RouteItem
-
+
Distance
Vzdálenost
@@ -618,22 +631,22 @@
ScaleItem
-
+
mi
mi
-
+
ft
ft
-
+
km
km
-
+
m
m
@@ -642,52 +655,27 @@
SpeedGraph
- Distance
- Vzdálenost
-
-
-
Speed
Rychlost
-
- m
- m
-
-
-
- km
-
-
-
-
- ft
- ft
-
-
-
+
km/h
km/h
-
+
Average
Průměr
-
+
Maximum
Maximum
-
- mi
- mi
-
-
-
+
mi/h
mi/h
@@ -696,57 +684,32 @@
TemperatureGraph
- Distance
- Vzdálenost
-
-
-
Temperature
Teplota
-
+
Average
Průměr
-
+
Minimum
Minimum
-
+
Maximum
Maximum
-
- m
- m
-
-
-
- km
- km
-
-
-
- ft
- ft
-
-
-
- mi
- mi
-
-
-
+
C
C
-
+
F
F
@@ -754,17 +717,17 @@
TrackItem
-
+
Distance
Vzdálenost
-
+
Time
Čas
-
+
Date
Datum
@@ -772,27 +735,27 @@
WaypointItem
-
+
Name
Název
-
+
Coordinates
Souřadnice
-
+
Elevation
Výška
-
+
Date
Datum
-
+
Description
Popis
diff --git a/src/elevationgraph.cpp b/src/elevationgraph.cpp
index 3facfa47..06df870b 100644
--- a/src/elevationgraph.cpp
+++ b/src/elevationgraph.cpp
@@ -45,7 +45,6 @@ ElevationGraph::ElevationGraph(QWidget *parent) : GraphTab(parent)
_units = Metric;
setYUnits();
- setXLabel(tr("Distance"));
setYLabel(tr("Elevation"));
setMinYRange(50.0);
@@ -67,20 +66,20 @@ void ElevationGraph::setInfo()
}
}
-void ElevationGraph::loadPath(const QVector &data, Type type)
+void ElevationGraph::loadGraph(const Graph &graph, Type type, PathItem *path)
{
qreal ascent = 0, descent = 0;
qreal min, max;
- if (data.count() < 2) {
+ if (graph.y.count() < 2) {
skipColor();
return;
}
- max = min = data.at(0).y();
- for (int j = 1; j < data.size(); j++) {
- qreal cur = data.at(j).y();
- qreal prev = data.at(j-1).y();
+ max = min = graph.y.at(0);
+ for (int j = 1; j < graph.y.size(); j++) {
+ qreal cur = graph.y.at(j);
+ qreal prev = graph.y.at(j-1);
if (cur > prev)
ascent += cur - prev;
@@ -105,17 +104,18 @@ void ElevationGraph::loadPath(const QVector &data, Type type)
_routeMin = nMin(_routeMin, min);
}
- loadData(data, type);
+ GraphView::loadGraph(graph, path, type);
}
-void ElevationGraph::loadGPX(const GPX &gpx)
+void ElevationGraph::loadGPX(const GPX &gpx, const QList &paths)
{
- for (int i = 0; i < gpx.tracks().count(); i++)
- loadPath(gpx.tracks().at(i)->elevation(), Track);
- for (int i = 0; i < gpx.routes().count(); i++)
- loadPath(gpx.routes().at(i)->elevation(), Route);
+ int p = 0;
+
+ for (int i = 0; i < gpx.tracks().count(); i++)
+ loadGraph(gpx.tracks().at(i)->elevation(), Track, paths.at(p++));
+ for (int i = 0; i < gpx.routes().count(); i++)
+ loadGraph(gpx.routes().at(i)->elevation(), Route, paths.at(p++));
- setXUnits();
setInfo();
redraw();
@@ -135,27 +135,6 @@ void ElevationGraph::clear()
GraphView::clear();
}
-void ElevationGraph::setXUnits()
-{
- if (_units == Metric) {
- if (bounds().width() < KMINM) {
- GraphView::setXUnits(tr("m"));
- setXScale(1);
- } else {
- GraphView::setXUnits(tr("km"));
- setXScale(M2KM);
- }
- } else {
- if (bounds().width() < MIINM) {
- GraphView::setXUnits(tr("ft"));
- setXScale(M2FT);
- } else {
- GraphView::setXUnits(tr("mi"));
- setXScale(M2MI);
- }
- }
-}
-
void ElevationGraph::setYUnits()
{
if (_units == Metric) {
@@ -171,9 +150,9 @@ void ElevationGraph::setUnits(enum Units units)
{
_units = units;
- setXUnits();
setYUnits();
setInfo();
+ GraphView::setUnits(units);
redraw();
}
@@ -184,7 +163,6 @@ void ElevationGraph::showTracks(bool show)
setInfo();
showGraph(show, Track);
- setXUnits();
redraw();
}
@@ -194,7 +172,6 @@ void ElevationGraph::showRoutes(bool show)
_showRoutes = show;
showGraph(show, Route);
- setXUnits();
setInfo();
redraw();
diff --git a/src/elevationgraph.h b/src/elevationgraph.h
index b49b73b2..b3a75585 100644
--- a/src/elevationgraph.h
+++ b/src/elevationgraph.h
@@ -4,6 +4,8 @@
#include "graphtab.h"
class GPX;
+class Graph;
+class PathItem;
class ElevationGraph : public GraphTab
{
@@ -13,7 +15,7 @@ public:
ElevationGraph(QWidget *parent = 0);
QString label() const {return tr("Elevation");}
- void loadGPX(const GPX &gpx);
+ void loadGPX(const GPX &gpx, const QList &paths);
void clear();
void setUnits(enum Units units);
void showTracks(bool show);
@@ -27,11 +29,10 @@ private:
qreal ascent() const;
qreal descent() const;
- void setXUnits();
void setYUnits();
void setInfo();
- void loadPath(const QVector &data, Type type);
+ void loadGraph(const Graph &graph, Type type, PathItem *path);
qreal _trackAscent, _trackDescent;
qreal _routeAscent, _routeDescent;
diff --git a/src/graphitem.cpp b/src/graphitem.cpp
index e9c8d625..b6ddc6f5 100644
--- a/src/graphitem.cpp
+++ b/src/graphitem.cpp
@@ -1,10 +1,118 @@
-#include
-#include
+#include
#include "graphitem.h"
+
+static qreal yAtX(const QPainterPath &path, qreal x)
+{
+ int low = 0;
+ int high = path.elementCount() - 1;
+ int mid = 0;
+
+ Q_ASSERT(high > low);
+ Q_ASSERT(x >= path.elementAt(low).x && x <= path.elementAt(high).x);
+
+ while (low <= high) {
+ mid = low + ((high - low) / 2);
+ const QPainterPath::Element &e = path.elementAt(mid);
+ if (e.x > x)
+ high = mid - 1;
+ else if (e.x < x)
+ low = mid + 1;
+ else
+ return e.y;
+ }
+
+ QLineF l;
+ if (path.elementAt(mid).x < x)
+ l = QLineF(path.elementAt(mid).x, path.elementAt(mid).y,
+ path.elementAt(mid+1).x, path.elementAt(mid+1).y);
+ else
+ l = QLineF(path.elementAt(mid-1).x, path.elementAt(mid-1).y,
+ path.elementAt(mid).x, path.elementAt(mid).y);
+
+ return l.pointAt((x - l.p1().x()) / (l.p2().x() - l.p1().x())).y();
+}
+
+GraphItem::GraphItem(const Graph &graph, QGraphicsItem *parent)
+ : QGraphicsObject(parent)
+{
+ _id = 0;
+ _pen = QPen(QBrush(Qt::SolidPattern), 0);
+ _type = Graph::Distance;
+
+ _distancePath.moveTo(graph.distance.first(), -graph.y.first());
+ for (int i = 1; i < graph.y.size(); i++)
+ _distancePath.lineTo(graph.distance.at(i), -graph.y.at(i));
+
+ if (!graph.time.isEmpty()) {
+ _timePath.moveTo(graph.time.first(), -graph.y.first());
+ for (int i = 1; i < graph.y.size(); i++)
+ _timePath.lineTo(graph.time.at(i), -graph.y.at(i));
+ }
+}
+
+void GraphItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+
+ painter->setPen(_pen);
+ painter->drawPath((_type == Graph::Distance) ? _distancePath : _timePath);
+}
+
void GraphItem::setColor(const QColor &color)
{
QBrush brush(color, Qt::SolidPattern);
- QPen pen(brush, 0);
- setPen(pen);
+ _pen.setBrush(brush);
+}
+
+qreal GraphItem::yAtX(qreal x)
+{
+ return ::yAtX((_type == Graph::Distance) ? _distancePath : _timePath, x);
+}
+
+qreal GraphItem::distanceAtTime(qreal time)
+{
+ int low = 0;
+ int high = _timePath.elementCount() - 1;
+ int mid = 0;
+
+ Q_ASSERT(high > low);
+ Q_ASSERT(time >= _timePath.elementAt(low).x
+ && time <= _timePath.elementAt(high).x);
+
+ while (low <= high) {
+ mid = low + ((high - low) / 2);
+ const QPainterPath::Element &e = _timePath.elementAt(mid);
+ if (e.x > time)
+ high = mid - 1;
+ else if (e.x < time)
+ low = mid + 1;
+ else
+ return _distancePath.elementAt(mid).x;
+ }
+
+ if (_timePath.elementAt(mid).x < time)
+ return ((_distancePath.elementAt(mid+1).x
+ + _distancePath.elementAt(mid).x) / 2.0);
+ else
+ return ((_distancePath.elementAt(mid).x
+ + _distancePath.elementAt(mid-1).x) / 2.0);
+}
+
+void GraphItem::emitSliderPositionChanged(qreal pos)
+{
+ if (_type == Graph::Time) {
+ if (!_timePath.isEmpty()) {
+ if (pos <= _timePath.elementAt(_timePath.elementCount() - 1).x)
+ emit sliderPositionChanged(distanceAtTime(pos));
+ else
+ emit sliderPositionChanged(_distancePath.elementAt(
+ _distancePath.elementCount() - 1).x + 1);
+ } else
+ emit sliderPositionChanged(_distancePath.elementAt(
+ _distancePath.elementCount() - 1).x + 1);
+ } else
+ emit sliderPositionChanged(pos);
}
diff --git a/src/graphitem.h b/src/graphitem.h
index 39447a81..fc8d1ee2 100644
--- a/src/graphitem.h
+++ b/src/graphitem.h
@@ -1,20 +1,42 @@
#ifndef GRAPHITEM_H
#define GRAPHITEM_H
-#include
+#include
+#include
+#include "graph.h"
-class GraphItem : public QGraphicsPathItem
+class GraphItem : public QGraphicsObject
{
-public:
- GraphItem(const QPainterPath &path, QGraphicsItem * parent = 0)
- : QGraphicsPathItem(path, parent) {_id = 0;}
+ Q_OBJECT
+public:
+ GraphItem(const Graph &graph, QGraphicsItem *parent = 0);
+
+ QRectF boundingRect() const
+ {return (_type == Graph::Distance) ? _distancePath.boundingRect()
+ : _timePath.boundingRect();}
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget);
+
+ void setGraphType(Graph::Type type) {_type = type;}
int id() const {return _id;}
void setId(int id) {_id = id;}
void setColor(const QColor &color);
+ qreal yAtX(qreal x);
+ qreal distanceAtTime(qreal time);
+
+signals:
+ void sliderPositionChanged(qreal);
+
+public slots:
+ void emitSliderPositionChanged(qreal);
+
private:
int _id;
+ QPen _pen;
+ QPainterPath _distancePath, _timePath;
+ Graph::Type _type;
};
#endif // GRAPHITEM_H
diff --git a/src/graphtab.h b/src/graphtab.h
index 77a8f82d..a68c386f 100644
--- a/src/graphtab.h
+++ b/src/graphtab.h
@@ -1,10 +1,12 @@
#ifndef GRAPHTAB_H
#define GRAPHTAB_H
+#include
#include "graphview.h"
#include "units.h"
class GPX;
+class PathItem;
class GraphTab : public GraphView
{
@@ -15,7 +17,7 @@ public:
{setFrameShape(QFrame::NoFrame);}
virtual QString label() const = 0;
- virtual void loadGPX(const GPX &gpx) = 0;
+ virtual void loadGPX(const GPX &gpx, const QList &paths) = 0;
virtual void clear() = 0;
virtual void setUnits(enum Units units) = 0;
virtual void showTracks(bool show) = 0;
diff --git a/src/graphview.cpp b/src/graphview.cpp
index e2e61e5a..bb3dfed0 100644
--- a/src/graphview.cpp
+++ b/src/graphview.cpp
@@ -7,7 +7,9 @@
#include "slideritem.h"
#include "sliderinfoitem.h"
#include "infoitem.h"
+#include "graph.h"
#include "graphitem.h"
+#include "pathitem.h"
#include "graphview.h"
@@ -55,6 +57,12 @@ GraphView::GraphView(QWidget *parent)
_minYRange = 0.01;
_sliderPos = 0;
+
+ _units = Metric;
+ _graphType = Graph::Distance;
+
+ setGraphType(_graphType);
+ setUnits(_units);
}
GraphView::~GraphView()
@@ -83,55 +91,101 @@ void GraphView::createYLabel()
_yAxis->setLabel(QString("%1 [%2]").arg(_yLabel).arg(_yUnits));
}
-void GraphView::setXLabel(const QString &label)
-{
- _xLabel = label;
- createXLabel();
-}
-
void GraphView::setYLabel(const QString &label)
{
_yLabel = label;
createYLabel();
}
-void GraphView::setXUnits(const QString &units)
-{
- _xUnits = units;
- createXLabel();
-}
-
void GraphView::setYUnits(const QString &units)
{
_yUnits = units;
createYLabel();
}
-void GraphView::loadData(const QVector &data, int id)
+void GraphView::setXUnits()
{
- QPainterPath path;
- GraphItem *pi;
-
-
- if (data.size() < 2)
- return;
-
- path.moveTo(data.at(0).x(), -data.at(0).y());
- for (int i = 1; i < data.size(); i++) {
- const QPointF &p = data.at(i);
- path.lineTo(p.x(), -p.y());
+ if (_graphType == Graph::Distance) {
+ if (_units == Metric) {
+ if (bounds().width() < KMINM) {
+ _xUnits = tr("m");
+ _xScale = 1;
+ } else {
+ _xUnits = tr("km");
+ _xScale = M2KM;
+ }
+ } else {
+ if (bounds().width() < MIINM) {
+ _xUnits = tr("ft");
+ _xScale = M2FT;
+ } else {
+ _xUnits = tr("mi");
+ _xScale = M2MI;
+ }
+ }
+ } else {
+ if (bounds().width() < MININS) {
+ _xUnits = tr("s");
+ _xScale = 1;
+ } else if (bounds().width() < HINS) {
+ _xUnits = tr("min");
+ _xScale = MIN2S;
+ } else {
+ _xUnits = tr("h");
+ _xScale = H2S;
+ }
}
- pi = new GraphItem(path);
- pi->setId(id);
- pi->setColor(_palette.color());
+ createXLabel();
+}
- _graphs.append(pi);
+void GraphView::setUnits(Units units)
+{
+ _units = units;
+ setXUnits();
+}
+
+void GraphView::setGraphType(Graph::Type type)
+{
+ _graphType = type;
+ _bounds = QRectF();
+
+ for (int i = 0; i < _graphs.count(); i++) {
+ _graphs.at(i)->setGraphType(type);
+ updateBounds(_graphs.at(i)->boundingRect());
+ }
+
+ if (type == Graph::Distance)
+ _xLabel = tr("Distance");
+ else
+ _xLabel = tr("Time");
+ setXUnits();
+
+ redraw();
+}
+
+void GraphView::loadGraph(const Graph &graph, PathItem *path, int id)
+{
+ if (graph.y.size() < 2)
+ return;
+
+ GraphItem *gi = new GraphItem(graph);
+ gi->setGraphType(_graphType);
+ gi->setId(id);
+ gi->setColor(_palette.color());
+
+ connect(this, SIGNAL(sliderPositionChanged(qreal)), gi,
+ SLOT(emitSliderPositionChanged(qreal)));
+ connect(gi, SIGNAL(sliderPositionChanged(qreal)), path,
+ SLOT(moveMarker(qreal)));
+
+ _graphs.append(gi);
if (!_hide.contains(id)) {
- _visible.append(pi);
- _scene->addItem(pi);
- updateBounds(path);
+ _visible.append(gi);
+ _scene->addItem(gi);
+ updateBounds(gi->boundingRect());
+ setXUnits();
}
}
@@ -163,7 +217,7 @@ void GraphView::showGraph(bool show, int id)
else {
addItem(gi);
_visible.append(gi);
- updateBounds(gi->path());
+ updateBounds(gi->boundingRect());
}
}
}
@@ -173,9 +227,9 @@ void GraphView::redraw()
redraw(viewport()->size() - QSizeF(MARGIN, MARGIN));
}
-void GraphView::updateBounds(const QPainterPath &path)
+void GraphView::updateBounds(const QRectF &boundingRect)
{
- QRectF br = path.boundingRect();
+ QRectF br(boundingRect);
br.moveTopLeft(QPointF(br.left(), -br.top() - br.height()));
_bounds |= br;
}
@@ -213,8 +267,8 @@ void GraphView::redraw(const QSizeF &size)
addItem(_slider);
addItem(_info);
- rx = RangeF(_bounds.left() * _xScale, _bounds.right() * _xScale);
- ry = RangeF(_bounds.top() * _yScale + _yOffset, _bounds.bottom() * _yScale
+ rx = RangeF(bounds().left() * _xScale, bounds().right() * _xScale);
+ ry = RangeF(bounds().top() * _yScale + _yOffset, bounds().bottom() * _yScale
+ _yOffset);
if (ry.size() < _minYRange)
ry.resize(_minYRange);
@@ -299,45 +353,14 @@ void GraphView::clear()
_scene->setSceneRect(0, 0, 0, 0);
}
-static qreal yAtX(const QPainterPath &path, qreal x)
-{
- int low = 0;
- int high = path.elementCount() - 1;
- int mid = 0;
-
- Q_ASSERT(high > low);
- Q_ASSERT(x >= path.elementAt(low).x && x <= path.elementAt(high).x);
-
- while (low <= high) {
- mid = low + ((high - low) / 2);
- const QPainterPath::Element &e = path.elementAt(mid);
- if (e.x > x)
- high = mid - 1;
- else if (e.x < x)
- low = mid + 1;
- else
- return e.y;
- }
-
- QLineF l;
- if (path.elementAt(mid).x < x)
- l = QLineF(path.elementAt(mid).x, path.elementAt(mid).y,
- path.elementAt(mid+1).x, path.elementAt(mid+1).y);
- else
- l = QLineF(path.elementAt(mid-1).x, path.elementAt(mid-1).y,
- path.elementAt(mid).x, path.elementAt(mid).y);
-
- return l.pointAt((x - l.p1().x()) / (l.p2().x() - l.p1().x())).y();
-}
-
void GraphView::updateSliderPosition()
{
- if (_bounds.width() <= 0)
+ if (bounds().width() <= 0)
return;
- if (_sliderPos <= _bounds.right() && _sliderPos >= _bounds.left()) {
- _slider->setPos((_sliderPos / _bounds.width()) * _slider->area().width(),
- _slider->area().bottom());
+ if (_sliderPos <= bounds().right() && _sliderPos >= bounds().left()) {
+ _slider->setPos((_sliderPos / bounds().width())
+ * _slider->area().width(), _slider->area().bottom());
_slider->setVisible(!_visible.isEmpty());
} else {
_slider->setPos(_slider->area().left(), _slider->area().bottom());
@@ -353,16 +376,15 @@ void GraphView::updateSliderInfo()
if (!_sliderInfo->isVisible())
return;
- const QPainterPath &path = _visible.first()->path();
- QRectF br = path.boundingRect();
+ QRectF br(_visible.first()->boundingRect());
if (br.height() < _minYRange)
br.adjust(0, -(_minYRange/2 - br.height()/2), 0,
_minYRange/2 - br.height()/2);
- qreal y = yAtX(path, _sliderPos);
+ qreal y = _visible.first()->yAtX(_sliderPos);
qreal r = (y - br.bottom()) / br.height();
- qreal pos = (_sliderPos / _bounds.width()) * _slider->area().width();
+ qreal pos = (_sliderPos / bounds().width()) * _slider->area().width();
SliderInfoItem::Side s = (pos + _sliderInfo->boundingRect().width()
> _slider->area().right()) ? SliderInfoItem::Left : SliderInfoItem::Right;
@@ -377,7 +399,7 @@ void GraphView::emitSliderPositionChanged(const QPointF &pos)
if (_slider->area().width() <= 0)
return;
- _sliderPos = (pos.x() / _slider->area().width()) * _bounds.width();
+ _sliderPos = (pos.x() / _slider->area().width()) * bounds().width();
updateSliderPosition();
emit sliderPositionChanged(_sliderPos);
diff --git a/src/graphview.h b/src/graphview.h
index af22d467..f1ef7192 100644
--- a/src/graphview.h
+++ b/src/graphview.h
@@ -8,12 +8,16 @@
#include
#include
#include "palette.h"
+#include "units.h"
+#include "graph.h"
+
class AxisItem;
class SliderItem;
class SliderInfoItem;
class InfoItem;
class GraphItem;
+class PathItem;
class Scene : public QGraphicsScene
{
@@ -35,26 +39,21 @@ public:
GraphView(QWidget *parent = 0);
~GraphView();
- void loadData(const QVector &data, int id = 0);
+ void loadGraph(const Graph &graph, PathItem *path, int id = 0);
int count() const {return _graphs.count();}
void redraw();
void clear();
void showGraph(bool show, int id = 0);
+ void setGraphType(Graph::Type type);
+ void setUnits(Units units);
- const QString &xLabel() const {return _xLabel;}
const QString &yLabel() const {return _yLabel;}
- const QString &xUnits() const {return _xUnits;}
const QString &yUnits() const {return _yUnits;}
- qreal xScale() const {return _xScale;}
qreal yScale() const {return _yScale;}
qreal yOffset() const {return _yOffset;}
-
- void setXLabel(const QString &label);
void setYLabel(const QString &label);
- void setXUnits(const QString &units);
void setYUnits(const QString &units);
- void setXScale(qreal scale) {_xScale = scale;}
void setYScale(qreal scale) {_yScale = scale;}
void setYOffset(qreal offset) {_yOffset = offset;}
@@ -82,11 +81,12 @@ private slots:
void newSliderPosition(const QPointF &pos);
private:
+ void setXUnits();
void createXLabel();
void createYLabel();
void updateSliderPosition();
void updateSliderInfo();
- void updateBounds(const QPainterPath &path);
+ void updateBounds(const QRectF &boundingRect);
QRectF graphsBoundingRect() const;
void removeItem(QGraphicsItem *item);
void addItem(QGraphicsItem *item);
@@ -111,6 +111,9 @@ private:
QSet _hide;
QRectF _bounds;
Palette _palette;
+
+ Units _units;
+ Graph::Type _graphType;
};
#endif // GRAPHVIEW_H
diff --git a/src/gui.cpp b/src/gui.cpp
index a4fa9749..2d123dad 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -173,6 +173,9 @@ QAction *GUI::createPOIFileAction(int index)
void GUI::createActions()
{
+ QActionGroup *ag;
+
+
// Action Groups
_fileActionGroup = new QActionGroup(this);
_fileActionGroup->setExclusive(false);
@@ -297,18 +300,35 @@ void GUI::createActions()
connect(_showRouteWaypointsAction, SIGNAL(triggered(bool)), _track,
SLOT(showRouteWaypoints(bool)));
- // Settings actions
- _showGraphsAction = new QAction(tr("Show graphs"), this);
+ // Graph actions
+ _showGraphsAction = new QAction(QIcon(QPixmap(SHOW_GRAPHS_ICON)),
+ tr("Show graphs"), this);
_showGraphsAction->setCheckable(true);
_showGraphsAction->setShortcut(SHOW_GRAPHS_SHORTCUT);
connect(_showGraphsAction, SIGNAL(triggered(bool)), this,
SLOT(showGraphs(bool)));
addAction(_showGraphsAction);
+ ag = new QActionGroup(this);
+ ag->setExclusive(true);
+ _distanceGraphAction = new QAction(tr("Distance"), this);
+ _distanceGraphAction->setCheckable(true);
+ _distanceGraphAction->setActionGroup(ag);
+ _distanceGraphAction->setShortcut(DISTANCE_GRAPH_SHORTCUT);
+ connect(_distanceGraphAction, SIGNAL(triggered()), this,
+ SLOT(setDistanceGraph()));
+ _timeGraphAction = new QAction(tr("Time"), this);
+ _timeGraphAction->setCheckable(true);
+ _timeGraphAction->setActionGroup(ag);
+ _timeGraphAction->setShortcut(TIME_GRAPH_SHORTCUT);
+ connect(_timeGraphAction, SIGNAL(triggered()), this,
+ SLOT(setTimeGraph()));
+
+ // Settings actions
_showToolbarsAction = new QAction(tr("Show toolbars"), this);
_showToolbarsAction->setCheckable(true);
connect(_showToolbarsAction, SIGNAL(triggered(bool)), this,
SLOT(showToolbars(bool)));
- QActionGroup *ag = new QActionGroup(this);
+ ag = new QActionGroup(this);
ag->setExclusive(true);
_metricUnitsAction = new QAction(tr("Metric"), this);
_metricUnitsAction->setCheckable(true);
@@ -368,6 +388,12 @@ void GUI::createMenus()
mapMenu->addSeparator();
mapMenu->addAction(_showMapAction);
+ QMenu *graphMenu = menuBar()->addMenu(tr("Graph"));
+ graphMenu->addAction(_distanceGraphAction);
+ graphMenu->addAction(_timeGraphAction);
+ graphMenu->addSeparator();
+ graphMenu->addAction(_showGraphsAction);
+
QMenu *poiMenu = menuBar()->addMenu(tr("POI"));
_poiFilesMenu = poiMenu->addMenu(tr("POI files"));
_poiFilesMenu->addActions(_poiFilesActions);
@@ -395,7 +421,6 @@ void GUI::createMenus()
unitsMenu->addAction(_imperialUnitsAction);
settingsMenu->addSeparator();
settingsMenu->addAction(_showToolbarsAction);
- settingsMenu->addAction(_showGraphsAction);
settingsMenu->addSeparator();
settingsMenu->addAction(_fullscreenAction);
@@ -421,6 +446,7 @@ void GUI::createToolBars()
_showToolBar = addToolBar(tr("Show"));
_showToolBar->addAction(_showPOIAction);
_showToolBar->addAction(_showMapAction);
+ _showToolBar->addAction(_showGraphsAction);
_navigationToolBar = addToolBar(tr("Navigation"));
_navigationToolBar->addAction(_firstAction);
@@ -586,13 +612,14 @@ bool GUI::openFile(const QString &fileName)
bool GUI::loadFile(const QString &fileName)
{
GPX gpx;
+ QList paths;
if (gpx.loadFile(fileName)) {
+ paths = _track->loadGPX(gpx);
for (int i = 0; i < _tabs.count(); i++)
- _tabs.at(i)->loadGPX(gpx);
+ _tabs.at(i)->loadGPX(gpx, paths);
updateGraphTabs();
_track->setHidden(false);
- _track->loadGPX(gpx);
if (_showPOIAction->isChecked())
_track->loadPOI(_poi);
@@ -986,7 +1013,7 @@ void GUI::poiFileChecked(int index)
void GUI::sliderPositionChanged(qreal pos)
{
_sliderPos = pos;
- _track->movePositionMarker(_sliderPos);
+ //_track->movePositionMarker(_sliderPos);
}
void GUI::graphChanged(int index)
@@ -1066,6 +1093,18 @@ void GUI::setImperialUnits()
updateStatusBarInfo();
}
+void GUI::setDistanceGraph()
+{
+ for (int i = 0; i <_tabs.count(); i++)
+ _tabs.at(i)->setGraphType(Graph::Distance);
+}
+
+void GUI::setTimeGraph()
+{
+ for (int i = 0; i <_tabs.count(); i++)
+ _tabs.at(i)->setGraphType(Graph::Time);
+}
+
void GUI::next()
{
QString file = _browser->next();
@@ -1158,7 +1197,6 @@ void GUI::writeSettings()
settings.setValue(UNITS_SETTING, _imperialUnitsAction->isChecked()
? Imperial : Metric);
settings.setValue(SHOW_TOOLBARS_SETTING, _showToolbarsAction->isChecked());
- settings.setValue(SHOW_GRAPHS_SETTING, _showGraphsAction->isChecked());
settings.endGroup();
settings.beginGroup(MAP_SETTINGS_GROUP);
@@ -1167,6 +1205,12 @@ void GUI::writeSettings()
settings.setValue(SHOW_MAP_SETTING, _showMapAction->isChecked());
settings.endGroup();
+ settings.beginGroup(GRAPH_SETTINGS_GROUP);
+ settings.setValue(SHOW_GRAPHS_SETTING, _showGraphsAction->isChecked());
+ settings.setValue(GRAPH_TYPE_SETTING, _timeGraphAction->isChecked()
+ ? Graph::Time : Graph::Distance);
+ settings.endGroup();
+
settings.beginGroup(POI_SETTINGS_GROUP);
settings.setValue(SHOW_POI_SETTING, _showPOIAction->isChecked());
settings.setValue(OVERLAP_POI_SETTING, _overlapPOIAction->isChecked());
@@ -1216,10 +1260,6 @@ void GUI::readSettings()
showToolbars(false);
else
_showToolbarsAction->setChecked(true);
- if (settings.value(SHOW_GRAPHS_SETTING, true).toBool() == false)
- showGraphs(false);
- else
- _showGraphsAction->setChecked(true);
settings.endGroup();
settings.beginGroup(MAP_SETTINGS_GROUP);
@@ -1235,6 +1275,19 @@ void GUI::readSettings()
_currentMap = 0;
settings.endGroup();
+ settings.beginGroup(GRAPH_SETTINGS_GROUP);
+ if (settings.value(SHOW_GRAPHS_SETTING, true).toBool() == false)
+ showGraphs(false);
+ else
+ _showGraphsAction->setChecked(true);
+ if (settings.value(GRAPH_TYPE_SETTING, Graph::Distance).toInt()
+ == Graph::Time) {
+ setTimeGraph();
+ _timeGraphAction->setChecked(true);
+ } else
+ _distanceGraphAction->setChecked(true);
+ settings.endGroup();
+
settings.beginGroup(POI_SETTINGS_GROUP);
if (settings.value(OVERLAP_POI_SETTING, true).toBool() == false)
_track->setPOIOverlap(false);
diff --git a/src/gui.h b/src/gui.h
index cd5c8f1c..4c7d20eb 100644
--- a/src/gui.h
+++ b/src/gui.h
@@ -58,8 +58,6 @@ private slots:
void graphChanged(int);
void poiFileChecked(int);
-
-
void next();
void prev();
void last();
@@ -67,6 +65,8 @@ private slots:
void setMetricUnits();
void setImperialUnits();
+ void setDistanceGraph();
+ void setTimeGraph();
void sliderPositionChanged(qreal pos);
@@ -132,6 +132,8 @@ private:
QAction *_fullscreenAction;
QAction *_clearMapCacheAction;
QAction *_showGraphsAction;
+ QAction *_distanceGraphAction;
+ QAction *_timeGraphAction;
QAction *_showToolbarsAction;
QAction *_nextAction;
QAction *_prevAction;
diff --git a/src/heartrategraph.cpp b/src/heartrategraph.cpp
index 2e83cf39..da92ce69 100644
--- a/src/heartrategraph.cpp
+++ b/src/heartrategraph.cpp
@@ -8,7 +8,6 @@ HeartRateGraph::HeartRateGraph(QWidget *parent) : GraphTab(parent)
_showTracks = true;
GraphView::setYUnits(tr("1/min"));
- setXLabel(tr("Distance"));
setYLabel(tr("Heart rate"));
setSliderPrecision(0);
@@ -25,30 +24,30 @@ void HeartRateGraph::setInfo()
clearInfo();
}
-void HeartRateGraph::loadGPX(const GPX &gpx)
+void HeartRateGraph::loadGPX(const GPX &gpx, const QList &paths)
{
for (int i = 0; i < gpx.tracks().count(); i++) {
- QVector data = gpx.tracks().at(i)->heartRate();
+ const Graph &graph = gpx.tracks().at(i)->heartRate();
qreal sum = 0, w = 0;
- if (data.count() < 2) {
+ if (graph.y.count() < 2) {
skipColor();
continue;
}
- for (int j = 1; j < data.size(); j++) {
- sum += data.at(j).y() * (data.at(j).x() - data.at(j-1).x());
- w += data.at(j).x() - data.at(j-1).x();
+ for (int j = 1; j < graph.y.size(); j++) {
+ qreal ds = graph.distance.at(j) - graph.distance.at(j-1);
+ sum += graph.y.at(j) * ds;
+ w += ds;
}
_avg.append(QPointF(gpx.tracks().at(i)->distance(), sum/w));
- loadData(data);
+ GraphView::loadGraph(graph, paths.at(i));
}
for (int i = 0; i < gpx.routes().count(); i++)
skipColor();
- setXUnits();
setInfo();
redraw();
@@ -74,43 +73,11 @@ void HeartRateGraph::clear()
GraphView::clear();
}
-void HeartRateGraph::setXUnits()
-{
- if (_units == Metric) {
- if (bounds().width() < KMINM) {
- GraphView::setXUnits(tr("m"));
- setXScale(1);
- } else {
- GraphView::setXUnits(tr("km"));
- setXScale(M2KM);
- }
- } else {
- if (bounds().width() < MIINM) {
- GraphView::setXUnits(tr("ft"));
- setXScale(M2FT);
- } else {
- GraphView::setXUnits(tr("mi"));
- setXScale(M2MI);
- }
- }
-}
-
-void HeartRateGraph::setUnits(enum Units units)
-{
- _units = units;
-
- setXUnits();
- setInfo();
-
- redraw();
-}
-
void HeartRateGraph::showTracks(bool show)
{
_showTracks = show;
showGraph(show);
- setXUnits();
setInfo();
redraw();
diff --git a/src/heartrategraph.h b/src/heartrategraph.h
index b193677e..c70fb2a9 100644
--- a/src/heartrategraph.h
+++ b/src/heartrategraph.h
@@ -13,16 +13,15 @@ public:
HeartRateGraph(QWidget *parent = 0);
QString label() const {return tr("Heart rate");}
- void loadGPX(const GPX &gpx);
+ void loadGPX(const GPX &gpx, const QList &paths);
void clear();
- void setUnits(enum Units units);
+ void setUnits(enum Units) {}
void showTracks(bool show);
void showRoutes(bool show) {Q_UNUSED(show);}
private:
qreal avg() const;
qreal max() const {return bounds().bottom();}
- void setXUnits();
void setInfo();
QList _avg;
diff --git a/src/icons.h b/src/icons.h
index 0cf2ae06..705b4770 100644
--- a/src/icons.h
+++ b/src/icons.h
@@ -8,6 +8,7 @@
#define CLOSE_FILE_ICON ":/icons/dialog-close.png"
#define SHOW_POI_ICON ":/icons/flag.png"
#define SHOW_MAP_ICON ":/icons/applications-internet.png"
+#define SHOW_GRAPHS_ICON ":/icons/office-chart-line-stacked.png"
#define QUIT_ICON ":/icons/application-exit.png"
#define RELOAD_FILE_ICON ":/icons/view-refresh.png"
#define NEXT_FILE_ICON ":/icons/arrow-right.png"
diff --git a/src/keys.h b/src/keys.h
index 42b81c60..99b4503e 100644
--- a/src/keys.h
+++ b/src/keys.h
@@ -4,26 +4,28 @@
#include
#include
-#define NEXT_KEY Qt::Key_Space
-#define PREV_KEY Qt::Key_Backspace
-#define FIRST_KEY Qt::Key_Home
-#define LAST_KEY Qt::Key_End
-#define MODIFIER Qt::ShiftModifier
+#define NEXT_KEY Qt::Key_Space
+#define PREV_KEY Qt::Key_Backspace
+#define FIRST_KEY Qt::Key_Home
+#define LAST_KEY Qt::Key_End
+#define MODIFIER Qt::ShiftModifier
-#define QUIT_SHORTCUT QKeySequence::Quit
-#define OPEN_SHORTCUT QKeySequence::Open
-#define CLOSE_SHORTCUT QKeySequence::Close
-#define RELOAD_SHORTCUT QKeySequence::Refresh
-#define EXPORT_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_E)
-#define SHOW_POI_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_P)
-#define SHOW_MAP_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_M)
-#define NEXT_MAP_SHORTCUT QKeySequence::Forward
-#define PREV_MAP_SHORTCUT QKeySequence::Back
-#define SHOW_GRAPHS_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_G)
+#define QUIT_SHORTCUT QKeySequence::Quit
+#define OPEN_SHORTCUT QKeySequence::Open
+#define CLOSE_SHORTCUT QKeySequence::Close
+#define RELOAD_SHORTCUT QKeySequence::Refresh
+#define EXPORT_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_E)
+#define SHOW_POI_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_P)
+#define SHOW_MAP_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_M)
+#define NEXT_MAP_SHORTCUT QKeySequence::Forward
+#define PREV_MAP_SHORTCUT QKeySequence::Back
+#define SHOW_GRAPHS_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_G)
+#define DISTANCE_GRAPH_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_D)
+#define TIME_GRAPH_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_T)
#ifdef Q_OS_MAC
-#define FULLSCREEN_SHORTCUT QKeySequence(Qt::META + Qt::CTRL + Qt::Key_F)
+#define FULLSCREEN_SHORTCUT QKeySequence(Qt::META + Qt::CTRL + Qt::Key_F)
#else // Q_OS_MAC
-#define FULLSCREEN_SHORTCUT QKeySequence(Qt::Key_F11)
+#define FULLSCREEN_SHORTCUT QKeySequence(Qt::Key_F11)
#endif // Q_OS_MAC
#endif // KEYS_H
diff --git a/src/pathitem.h b/src/pathitem.h
index adb9b867..de883bfd 100644
--- a/src/pathitem.h
+++ b/src/pathitem.h
@@ -1,4 +1,19 @@
#ifndef PATHITEM_H
#define PATHITEM_H
+#include
+#include "units.h"
+
+class PathItem : public QGraphicsObject
+{
+ Q_OBJECT
+
+public:
+ PathItem(QGraphicsItem *parent = 0) : QGraphicsObject(parent) {}
+ virtual void showMarker(bool show) = 0;
+
+public slots:
+ virtual void moveMarker(qreal distance) = 0;
+};
+
#endif // PATHITEM_H
diff --git a/src/route.cpp b/src/route.cpp
index 9f0bfcf0..80d9a6f3 100644
--- a/src/route.cpp
+++ b/src/route.cpp
@@ -12,14 +12,15 @@ Route::Route(const QVector &data) : _data(data)
}
}
-QVector Route::elevation() const
+Graph Route::elevation() const
{
- QVector graph;
+ Graph graph;
for (int i = 0; i < _data.size(); i++)
if (_data.at(i).hasElevation())
- graph.append(QPointF(_dd.at(i), _data.at(i).elevation()
- - _data.at(i).geoidHeight()));
+ graph.y.append(_data.at(i).elevation() - _data.at(i).geoidHeight());
+
+ graph.distance = _dd;
return graph;
}
diff --git a/src/route.h b/src/route.h
index 60a654d7..2371a421 100644
--- a/src/route.h
+++ b/src/route.h
@@ -3,6 +3,7 @@
#include
#include "waypoint.h"
+#include "graph.h"
class Route
{
@@ -10,7 +11,7 @@ public:
Route(const QVector &data);
const QVector &route() const {return _data;}
- QVector elevation() const;
+ Graph elevation() const;
qreal distance() const;
diff --git a/src/routeitem.cpp b/src/routeitem.cpp
index 931628ed..87df61a8 100644
--- a/src/routeitem.cpp
+++ b/src/routeitem.cpp
@@ -34,7 +34,7 @@ void RouteItem::updateShape()
}
RouteItem::RouteItem(const Route &route, QGraphicsItem *parent)
- : QGraphicsItem(parent)
+ : PathItem(parent)
{
WaypointItem *wi;
diff --git a/src/routeitem.h b/src/routeitem.h
index e2be4d62..e987b8de 100644
--- a/src/routeitem.h
+++ b/src/routeitem.h
@@ -1,14 +1,17 @@
#ifndef ROUTEITEM_H
#define ROUTEITEM_H
-#include
+#include
+#include "pathitem.h"
#include "markeritem.h"
#include "route.h"
#include "units.h"
-class RouteItem : public QGraphicsItem
+class RouteItem : public PathItem
{
+ Q_OBJECT
+
public:
RouteItem(const Route &route, QGraphicsItem *parent = 0);
diff --git a/src/settings.h b/src/settings.h
index 3b34238e..2b89035f 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -8,7 +8,10 @@
#define SETTINGS_SETTINGS_GROUP "Settings"
#define UNITS_SETTING "units"
#define SHOW_TOOLBARS_SETTING "toolbar"
-#define SHOW_GRAPHS_SETTING "graphs"
+
+#define GRAPH_SETTINGS_GROUP "Graph"
+#define SHOW_GRAPHS_SETTING "show"
+#define GRAPH_TYPE_SETTING "type"
#define MAP_SETTINGS_GROUP "Map"
#define CURRENT_MAP_SETTING "map"
diff --git a/src/slideritem.cpp b/src/slideritem.cpp
index bfb18b8e..6d4fc9f4 100644
--- a/src/slideritem.cpp
+++ b/src/slideritem.cpp
@@ -4,7 +4,7 @@
#define SIZE 10
-SliderItem::SliderItem(QGraphicsObject *parent) : QGraphicsObject(parent)
+SliderItem::SliderItem(QGraphicsItem *parent) : QGraphicsObject(parent)
{
setFlag(ItemIsMovable);
setFlag(ItemSendsGeometryChanges);
diff --git a/src/slideritem.h b/src/slideritem.h
index 39a45d49..ab490a99 100644
--- a/src/slideritem.h
+++ b/src/slideritem.h
@@ -1,14 +1,14 @@
#ifndef SLIDERITEM_H
#define SLIDERITEM_H
-#include
+#include
class SliderItem : public QGraphicsObject
{
Q_OBJECT
public:
- SliderItem(QGraphicsObject *parent = 0);
+ SliderItem(QGraphicsItem *parent = 0);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
diff --git a/src/speedgraph.cpp b/src/speedgraph.cpp
index f792ce75..ea8e95e1 100644
--- a/src/speedgraph.cpp
+++ b/src/speedgraph.cpp
@@ -9,7 +9,6 @@ SpeedGraph::SpeedGraph(QWidget *parent) : GraphTab(parent)
_showTracks = true;
setYUnits();
- setXLabel(tr("Distance"));
setYLabel(tr("Speed"));
setSliderPrecision(1);
@@ -26,11 +25,11 @@ void SpeedGraph::setInfo()
clearInfo();
}
-void SpeedGraph::loadGPX(const GPX &gpx)
+void SpeedGraph::loadGPX(const GPX &gpx, const QList &paths)
{
for (int i = 0; i < gpx.tracks().count(); i++) {
- QVector data = gpx.tracks().at(i)->speed();
- if (data.count() < 2) {
+ const Graph &graph = gpx.tracks().at(i)->speed();
+ if (graph.y.count() < 2) {
skipColor();
continue;
}
@@ -38,13 +37,12 @@ void SpeedGraph::loadGPX(const GPX &gpx)
_avg.append(QPointF(gpx.tracks().at(i)->distance(),
gpx.tracks().at(i)->distance() / gpx.tracks().at(i)->time()));
- loadData(data);
+ GraphView::loadGraph(graph, paths.at(i));
}
for (int i = 0; i < gpx.routes().count(); i++)
skipColor();
- setXUnits();
setInfo();
redraw();
@@ -70,27 +68,6 @@ void SpeedGraph::clear()
GraphView::clear();
}
-void SpeedGraph::setXUnits()
-{
- if (_units == Metric) {
- if (bounds().width() < KMINM) {
- GraphView::setXUnits(tr("m"));
- setXScale(1);
- } else {
- GraphView::setXUnits(tr("km"));
- setXScale(M2KM);
- }
- } else {
- if (bounds().width() < MIINM) {
- GraphView::setXUnits(tr("ft"));
- setXScale(M2FT);
- } else {
- GraphView::setXUnits(tr("mi"));
- setXScale(M2MI);
- }
- }
-}
-
void SpeedGraph::setYUnits()
{
if (_units == Metric) {
@@ -106,9 +83,9 @@ void SpeedGraph::setUnits(enum Units units)
{
_units = units;
- setXUnits();
setYUnits();
setInfo();
+ GraphView::setUnits(units);
redraw();
}
@@ -118,7 +95,6 @@ void SpeedGraph::showTracks(bool show)
_showTracks = show;
showGraph(show);
- setXUnits();
setInfo();
redraw();
diff --git a/src/speedgraph.h b/src/speedgraph.h
index b483e7dc..e5c50831 100644
--- a/src/speedgraph.h
+++ b/src/speedgraph.h
@@ -14,7 +14,7 @@ public:
SpeedGraph(QWidget *parent = 0);
QString label() const {return tr("Speed");}
- void loadGPX(const GPX &gpx);
+ void loadGPX(const GPX &gpx, const QList &paths);
void clear();
void setUnits(enum Units units);
void showTracks(bool show);
@@ -23,7 +23,6 @@ public:
private:
qreal avg() const;
qreal max() const {return bounds().bottom();}
- void setXUnits();
void setYUnits();
void setInfo();
diff --git a/src/temperaturegraph.cpp b/src/temperaturegraph.cpp
index 174e0d88..e5435229 100644
--- a/src/temperaturegraph.cpp
+++ b/src/temperaturegraph.cpp
@@ -8,7 +8,6 @@ TemperatureGraph::TemperatureGraph(QWidget *parent) : GraphTab(parent)
_showTracks = true;
setYUnits();
- setXLabel(tr("Distance"));
setYLabel(tr("Temperature"));
setSliderPrecision(1);
@@ -27,30 +26,30 @@ void TemperatureGraph::setInfo()
clearInfo();
}
-void TemperatureGraph::loadGPX(const GPX &gpx)
+void TemperatureGraph::loadGPX(const GPX &gpx, const QList &paths)
{
for (int i = 0; i < gpx.tracks().count(); i++) {
- QVector data = gpx.tracks().at(i)->temperature();
+ const Graph &graph = gpx.tracks().at(i)->temperature();
qreal sum = 0, w = 0;
- if (data.count() < 2) {
+ if (graph.y.count() < 2) {
skipColor();
continue;
}
- for (int j = 1; j < data.size(); j++) {
- sum += data.at(j).y() * (data.at(j).x() - data.at(j-1).x());
- w += data.at(j).x() - data.at(j-1).x();
+ for (int j = 1; j < graph.y.size(); j++) {
+ qreal ds = graph.distance.at(j) - graph.distance.at(j-1);
+ sum += graph.y.at(j) * ds;
+ w += ds;
}
_avg.append(QPointF(gpx.tracks().at(i)->distance(), sum/w));
- loadData(data);
+ GraphView::loadGraph(graph, paths.at(i));
}
for (int i = 0; i < gpx.routes().count(); i++)
skipColor();
- setXUnits();
setInfo();
redraw();
@@ -76,27 +75,6 @@ void TemperatureGraph::clear()
GraphView::clear();
}
-void TemperatureGraph::setXUnits()
-{
- if (_units == Metric) {
- if (bounds().width() < KMINM) {
- GraphView::setXUnits(tr("m"));
- setXScale(1);
- } else {
- GraphView::setXUnits(tr("km"));
- setXScale(M2KM);
- }
- } else {
- if (bounds().width() < MIINM) {
- GraphView::setXUnits(tr("ft"));
- setXScale(M2FT);
- } else {
- GraphView::setXUnits(tr("mi"));
- setXScale(M2MI);
- }
- }
-}
-
void TemperatureGraph::setYUnits()
{
if (_units == Metric) {
@@ -114,9 +92,9 @@ void TemperatureGraph::setUnits(enum Units units)
{
_units = units;
- setXUnits();
setYUnits();
setInfo();
+ GraphView::setUnits(units);
redraw();
}
@@ -126,7 +104,6 @@ void TemperatureGraph::showTracks(bool show)
_showTracks = show;
showGraph(show);
- setXUnits();
setInfo();
redraw();
diff --git a/src/temperaturegraph.h b/src/temperaturegraph.h
index 27a61891..4960f724 100644
--- a/src/temperaturegraph.h
+++ b/src/temperaturegraph.h
@@ -13,7 +13,7 @@ public:
TemperatureGraph(QWidget *parent = 0);
QString label() const {return tr("Temperature");}
- void loadGPX(const GPX &gpx);
+ void loadGPX(const GPX &gpx, const QList &paths);
void clear();
void setUnits(enum Units units);
void showTracks(bool show);
@@ -23,7 +23,6 @@ private:
qreal avg() const;
qreal min() const {return bounds().top();}
qreal max() const {return bounds().bottom();}
- void setXUnits();
void setYUnits();
void setInfo();
diff --git a/src/track.cpp b/src/track.cpp
index 04834246..e4da1b37 100644
--- a/src/track.cpp
+++ b/src/track.cpp
@@ -8,39 +8,39 @@
#define WINDOW_HE 11
#define WINDOW_HF 3
-static bool lt(const QPointF &p1, const QPointF &p2)
+static bool lt(qreal v1, qreal v2)
{
- return p1.y() < p2.y();
+ return v1 < v2;
}
-static qreal median(QVector v)
+static qreal median(QVector v)
{
qSort(v.begin(), v.end(), lt);
- return v.at(v.size() / 2).y();
+ return v.at(v.size() / 2);
}
-static qreal MAD(QVector v, qreal m)
+static qreal MAD(QVector v, qreal m)
{
for (int i = 0; i < v.size(); i++)
- v[i].setY(qAbs(v.at(i).y() - m));
+ v[i] = (qAbs(v.at(i) - m));
qSort(v.begin(), v.end(), lt);
- return v.at(v.size() / 2).y();
+ return v.at(v.size() / 2);
}
-static QVector eliminate(const QVector &v, int window)
+static QVector eliminate(const QVector &v, int window)
{
QList rm;
- QVector ret;
+ QVector ret;
qreal m, M;
if (v.size() < window)
- return QVector(v);
+ return QVector(v);
for (int i = window/2; i < v.size() - window/2; i++) {
m = median(v.mid(i - window/2, window));
M = MAD(v.mid(i - window/2, window), m);
- if (qAbs((0.6745 * (v.at(i).y() - m)) / M) > 3.5)
+ if (qAbs((0.6745 * (v.at(i) - m)) / M) > 3.5)
rm.append(i);
}
@@ -55,26 +55,26 @@ static QVector eliminate(const QVector &v, int window)
return ret;
}
-static QVector filter(const QVector &v, int window)
+static QVector filter(const QVector &v, int window)
{
qreal acc = 0;
- QVector ret;
+ QVector ret;
if (v.size() < window)
- return QVector(v);
+ return QVector(v);
for (int i = 0; i < window; i++)
- acc += v.at(i).y();
+ acc += v.at(i);
for (int i = 0; i <= window/2; i++)
- ret.append(QPointF(v.at(i).x(), acc/window));
+ ret.append(acc/window);
for (int i = window/2 + 1; i < v.size() - window/2; i++) {
- acc += v.at(i + window/2).y() - v.at(i - (window/2 + 1)).y();
- ret.append(QPointF(v.at(i).x(), acc/window));
+ acc += v.at(i + window/2) - v.at(i - (window/2 + 1));
+ ret.append(acc/window);
}
for (int i = v.size() - window/2; i < v.size(); i++)
- ret.append(QPointF(v.at(i).x(), acc/window));
+ ret.append(acc/window);
return ret;
}
@@ -82,80 +82,107 @@ static QVector filter(const QVector &v, int window)
Track::Track(const QVector &data) : _data(data)
{
qreal dist = 0;
+ qint64 time;
- _dd.append(dist);
+ _dd.append(0);
+ _td.append(0);
for (int i = 1; i < data.count(); i++) {
dist += llDistance(data.at(i).coordinates(), data.at(i-1).coordinates());
_dd.append(dist);
+ if (data.first().hasTimestamp() && data.at(i).hasTimestamp()) {
+ time = _data.first().timestamp().msecsTo(_data.at(i).timestamp());
+ _td.append((qreal)time / 1000.0);
+ }
}
+
+ if (_dd.size() != _td.size())
+ _td.clear();
}
-QVector Track::elevation() const
+Graph Track::elevation() const
{
- QVector raw;
+ Graph ret;
+ QVector raw;
+
if (!_data.size())
- return raw;
+ return ret;
for (int i = 0; i < _data.size(); i++)
if (_data.at(i).hasElevation())
- raw.append(QPointF(_dd.at(i), _data.at(i).elevation()
- - _data.at(i).geoidHeight()));
+ raw.append(_data.at(i).elevation() - _data.at(i).geoidHeight());
- return filter(raw, WINDOW_EF);
+ ret.y = filter(raw, WINDOW_EF);
+ ret.distance = _dd;
+ ret.time = _td;
+
+ return ret;
}
-QVector Track::speed() const
+Graph Track::speed() const
{
- qreal v, ds;
- qint64 dt;
- QVector raw;
+ Graph ret;
+ qreal v, ds, dt;
+ QVector raw;
+
if (!_data.size())
- return raw;
+ return ret;
- raw.append(QPointF(0, 0));
+ raw.append(0);
for (int i = 1; i < _data.size(); i++) {
if (_data.at(i).hasSpeed())
v = _data.at(i).speed();
else if (_data.at(i).hasTimestamp()) {
- dt = _data.at(i-1).timestamp().msecsTo(_data.at(i).timestamp());
+ dt = _td.at(i) - _td.at(i-1);
if (!dt)
continue;
ds = _dd.at(i) - _dd.at(i-1);
- v = ds / ((qreal)dt / 1000.0);
+ v = ds / dt;
} else
continue;
- raw.append(QPointF(_dd.at(i), v));
+ raw.append(v);
}
- return filter(eliminate(raw, WINDOW_SE), WINDOW_SF);
+ ret.y = filter(eliminate(raw, WINDOW_SE), WINDOW_SF);
+ ret.distance = _dd;
+ ret.time = _td;
+
+ return ret;
}
-QVector Track::heartRate() const
+Graph Track::heartRate() const
{
- QVector raw;
+ Graph ret;
+ QVector raw;
if (!_data.size())
- return raw;
+ return ret;
for (int i = 0; i < _data.count(); i++)
if (_data.at(i).hasHeartRate())
- raw.append(QPointF(_dd.at(i), _data.at(i).heartRate()));
+ raw.append(_data.at(i).heartRate());
- return filter(eliminate(raw, WINDOW_HE), WINDOW_HF);
+ ret.y = filter(eliminate(raw, WINDOW_HE), WINDOW_HF);
+ ret.distance = _dd;
+ ret.time = _td;
+
+ return ret;
}
-QVector Track::temperature() const
+Graph Track::temperature() const
{
- QVector graph;
+ Graph ret;
for (int i = 0; i < _data.size(); i++)
if (_data.at(i).hasTemperature())
- graph.append(QPointF(_dd.at(i), _data.at(i).temperature()));
+ ret.y.append(_data.at(i).temperature());
- return graph;
+ ret.distance = _dd;
+ ret.time = _td;
+
+ return ret;
}
qreal Track::distance() const
diff --git a/src/track.h b/src/track.h
index a2c03683..d6a515ce 100644
--- a/src/track.h
+++ b/src/track.h
@@ -4,6 +4,7 @@
#include
#include
#include "trackpoint.h"
+#include "graph.h"
class Track
{
@@ -11,10 +12,10 @@ public:
Track(const QVector &data);
const QVector &track() const {return _data;}
- QVector elevation() const;
- QVector speed() const;
- QVector heartRate() const;
- QVector temperature() const;
+ Graph elevation() const;
+ Graph speed() const;
+ Graph heartRate() const;
+ Graph temperature() const;
qreal distance() const;
qreal time() const;
@@ -25,6 +26,7 @@ public:
private:
const QVector &_data;
QVector _dd;
+ QVector _td;
};
#endif // TRACK_H
diff --git a/src/trackitem.cpp b/src/trackitem.cpp
index 69166bc2..13f53997 100644
--- a/src/trackitem.cpp
+++ b/src/trackitem.cpp
@@ -38,7 +38,7 @@ void TrackItem::updateShape()
}
TrackItem::TrackItem(const Track &track, QGraphicsItem *parent)
- : QGraphicsItem(parent)
+ : PathItem(parent)
{
const QVector &t = track.track();
Q_ASSERT(t.count() >= 2);
diff --git a/src/trackitem.h b/src/trackitem.h
index 8483070b..11e694fd 100644
--- a/src/trackitem.h
+++ b/src/trackitem.h
@@ -1,15 +1,18 @@
#ifndef TRACKITEM_H
#define TRACKITEM_H
-#include
#include
+#include
+#include "pathitem.h"
#include "units.h"
#include "track.h"
#include "markeritem.h"
-class TrackItem : public QGraphicsItem
+class TrackItem : public PathItem
{
+ Q_OBJECT
+
public:
TrackItem(const Track &track, QGraphicsItem *parent = 0);
diff --git a/src/trackview.cpp b/src/trackview.cpp
index c6d06c8b..845a6d09 100644
--- a/src/trackview.cpp
+++ b/src/trackview.cpp
@@ -45,7 +45,7 @@ TrackView::TrackView(QWidget *parent)
_showRouteWaypoints = true;
_plot = false;
- _markerPos = 0;
+ //_markerPos = 0;
}
TrackView::~TrackView()
@@ -54,11 +54,11 @@ TrackView::~TrackView()
delete _mapScale;
}
-void TrackView::addTrack(const Track &track)
+PathItem *TrackView::addTrack(const Track &track)
{
if (track.isNull()) {
_palette.color();
- return;
+ return 0;
}
TrackItem *ti = new TrackItem(track);
@@ -68,15 +68,17 @@ void TrackView::addTrack(const Track &track)
ti->setScale(1.0/_scale);
ti->setColor(_palette.color());
ti->setVisible(_showTracks);
- ti->moveMarker(_markerPos);
+ //ti->moveMarker(_markerPos);
_scene->addItem(ti);
+
+ return ti;
}
-void TrackView::addRoute(const Route &route)
+PathItem *TrackView::addRoute(const Route &route)
{
if (route.isNull()) {
_palette.color();
- return;
+ return 0;
}
RouteItem *ri = new RouteItem(route);
@@ -88,8 +90,10 @@ void TrackView::addRoute(const Route &route)
ri->setVisible(_showRoutes);
ri->showWaypoints(_showRouteWaypoints);
ri->showWaypointLabels(_showWaypointLabels);
- ri->moveMarker(_markerPos);
+ //ri->moveMarker(_markerPos);
_scene->addItem(ri);
+
+ return ri;
}
void TrackView::addWaypoints(const QList &waypoints)
@@ -111,18 +115,25 @@ void TrackView::addWaypoints(const QList &waypoints)
_scale = mapScale(_zoom);
}
-void TrackView::loadGPX(const GPX &gpx)
+QList TrackView::loadGPX(const GPX &gpx)
{
+ QList paths;
+ PathItem *pi;
+
int zoom = _zoom;
- for (int i = 0; i < gpx.tracks().count(); i++)
- addTrack(*(gpx.tracks().at(i)));
- for (int i = 0; i < gpx.routes().count(); i++)
- addRoute(*(gpx.routes().at(i)));
+ for (int i = 0; i < gpx.tracks().count(); i++) {
+ if ((pi = addTrack(*(gpx.tracks().at(i)))))
+ paths.append(pi);
+ }
+ for (int i = 0; i < gpx.routes().count(); i++) {
+ if ((pi = addRoute(*(gpx.routes().at(i)))))
+ paths.append(pi);
+ }
addWaypoints(gpx.waypoints());
if (_tracks.empty() && _routes.empty() && _waypoints.empty())
- return;
+ return paths;
if ((_tracks.size() > 1 && _zoom < zoom)
|| (_routes.size() > 1 && _zoom < zoom)
@@ -138,6 +149,8 @@ void TrackView::loadGPX(const GPX &gpx)
_mapScale->setZoom(_zoom, -(br.center().ry() * _scale));
if (_mapScale->scene() != _scene)
_scene->addItem(_mapScale);
+
+ return paths;
}
QRectF TrackView::trackBoundingRect() const
@@ -478,9 +491,10 @@ void TrackView::clear()
_scene->setSceneRect(QRectF());
- _markerPos = 0;
+ //_markerPos = 0;
}
+/*
void TrackView::movePositionMarker(qreal val)
{
_markerPos = val;
@@ -491,6 +505,7 @@ void TrackView::movePositionMarker(qreal val)
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->moveMarker(val);
}
+*/
void TrackView::showTracks(bool show)
{
diff --git a/src/trackview.h b/src/trackview.h
index 13794e7a..27b579e2 100644
--- a/src/trackview.h
+++ b/src/trackview.h
@@ -18,6 +18,7 @@ class TrackItem;
class RouteItem;
class WaypointItem;
class ScaleItem;
+class PathItem;
class TrackView : public QGraphicsView
{
@@ -27,7 +28,7 @@ public:
TrackView(QWidget *parent = 0);
~TrackView();
- void loadGPX(const GPX &gpx);
+ QList loadGPX(const GPX &gpx);
void loadPOI(const POI &poi);
void clearPOI();
@@ -43,7 +44,7 @@ public:
int waypointCount() const {return _waypoints.count();}
public slots:
- void movePositionMarker(qreal val);
+ //void movePositionMarker(qreal val);
void redraw();
void setPOIOverlap(bool overlap);
@@ -55,8 +56,8 @@ public slots:
void showRouteWaypoints(bool show);
private:
- void addTrack(const Track &track);
- void addRoute(const Route &route);
+ PathItem *addTrack(const Track &track);
+ PathItem *addRoute(const Route &route);
void addWaypoints(const QList &waypoints);
void addPOI(const QVector &waypoints);
@@ -103,7 +104,7 @@ private:
bool _showRouteWaypoints;
bool _plot;
- qreal _markerPos;
+ //qreal _markerPos;
};
#endif // TRACKVIEW_H
diff --git a/src/units.h b/src/units.h
index cf53388b..734c661e 100644
--- a/src/units.h
+++ b/src/units.h
@@ -13,10 +13,14 @@ enum Units {
#define MS2MIH 2.236936290000 // m/s -> mi/h
#define FT2MI 0.000189393939 // ft -> mi
#define MM2IN 0.039370100000 // mm -> in
+#define H2S 0.000277777778 // h -> s
+#define MIN2S 0.016666666667 // min -> s
#define KMINM 1000 // 1 km in m
#define MIINFT 5280 // 1 mi in ft
-#define MIINM 1609.344 // 1mi in m
+#define MIINM 1609.344 // 1 mi in m
+#define MININS 60 // 1 min in s
+#define HINS 3600 // 1 hins
#define C2FS 1.8 // Celsius to Farenheit - scale
#define C2FO 32 // Celsius to Farenheit - offset