1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-02-17 16:20:48 +01:00

Added support for POI files in GPX format

This commit is contained in:
Martin Tůma 2016-02-11 20:58:52 +01:00
parent 635293ec1b
commit e06a1bc148
23 changed files with 855 additions and 737 deletions

View File

@ -8,8 +8,6 @@ HEADERS += src/config.h \
src/icons.h \ src/icons.h \
src/gui.h \ src/gui.h \
src/gpx.h \ src/gpx.h \
src/graph.h \
src/track.h \
src/parser.h \ src/parser.h \
src/poi.h \ src/poi.h \
src/rtree.h \ src/rtree.h \
@ -30,12 +28,15 @@ HEADERS += src/config.h \
src/downloader.h \ src/downloader.h \
src/units.h \ src/units.h \
src/scaleitem.h \ src/scaleitem.h \
src/nicenum.h src/nicenum.h \
src/waypoint.h \
src/trackview.h \
src/track.h \
src/graphview.h \
src/trackpoint.h
SOURCES += src/main.cpp \ SOURCES += src/main.cpp \
src/gui.cpp \ src/gui.cpp \
src/gpx.cpp \ src/gpx.cpp \
src/graph.cpp \
src/track.cpp \
src/parser.cpp \ src/parser.cpp \
src/poi.cpp \ src/poi.cpp \
src/ll.cpp \ src/ll.cpp \
@ -53,7 +54,10 @@ SOURCES += src/main.cpp \
src/maplist.cpp \ src/maplist.cpp \
src/downloader.cpp \ src/downloader.cpp \
src/scaleitem.cpp \ src/scaleitem.cpp \
src/nicenum.cpp src/nicenum.cpp \
src/trackview.cpp \
src/track.cpp \
src/graphview.cpp
RESOURCES += gpxsee.qrc RESOURCES += gpxsee.qrc
TRANSLATIONS = lang/gpxsee_cs.ts TRANSLATIONS = lang/gpxsee_cs.ts
macx:ICON = icons/gpxsee.icns macx:ICON = icons/gpxsee.icns

View File

@ -3,39 +3,39 @@
#include "elevationgraph.h" #include "elevationgraph.h"
ElevationGraph::ElevationGraph(QWidget *parent) : Graph(parent) ElevationGraph::ElevationGraph(QWidget *parent) : GraphView(parent)
{ {
_ascent = 0; _ascent = 0;
_descent = 0; _descent = 0;
_max = -FLT_MAX; _max = -FLT_MAX;
_min = FLT_MAX; _min = FLT_MAX;
Graph::setXLabel(tr("Distance")); GraphView::setXLabel(tr("Distance"));
Graph::setYLabel(tr("Elevation")); GraphView::setYLabel(tr("Elevation"));
Graph::setXUnits(tr("km")); GraphView::setXUnits(tr("km"));
Graph::setYUnits(tr("m")); GraphView::setYUnits(tr("m"));
Graph::setXScale(M2KM); GraphView::setXScale(M2KM);
} }
void ElevationGraph::addInfo() void ElevationGraph::addInfo()
{ {
Graph::addInfo(tr("Ascent"), QString::number(_ascent * _yScale, 'f', 0) GraphView::addInfo(tr("Ascent"), QString::number(_ascent * _yScale, 'f', 0)
+ THIN_SPACE + _yUnits); + THIN_SPACE + _yUnits);
Graph::addInfo(tr("Descent"), QString::number(_descent * _yScale, 'f', 0) GraphView::addInfo(tr("Descent"), QString::number(_descent * _yScale, 'f', 0)
+ THIN_SPACE + _yUnits); + THIN_SPACE + _yUnits);
Graph::addInfo(tr("Maximum"), QString::number(_max * _yScale, 'f', 0) GraphView::addInfo(tr("Maximum"), QString::number(_max * _yScale, 'f', 0)
+ THIN_SPACE + _yUnits); + THIN_SPACE + _yUnits);
Graph::addInfo(tr("Minimum"), QString::number(_min * _yScale, 'f', 0) GraphView::addInfo(tr("Minimum"), QString::number(_min * _yScale, 'f', 0)
+ THIN_SPACE + _yUnits); + THIN_SPACE + _yUnits);
} }
void ElevationGraph::loadGPX(const GPX &gpx) void ElevationGraph::loadGPX(const GPX &gpx)
{ {
for (int i = 0; i < gpx.count(); i++) { for (int i = 0; i < gpx.trackCount(); i++) {
QVector<QPointF> data; QVector<QPointF> data;
qreal min, max, ascent = 0, descent = 0; qreal min, max, ascent = 0, descent = 0;
gpx.elevationGraph(i, data); gpx.track(i).elevationGraph(data);
if (data.isEmpty()) if (data.isEmpty())
return; return;
@ -73,21 +73,21 @@ void ElevationGraph::clear()
_max = -FLT_MAX; _max = -FLT_MAX;
_min = FLT_MAX; _min = FLT_MAX;
Graph::clear(); GraphView::clear();
} }
void ElevationGraph::setUnits(enum Units units) void ElevationGraph::setUnits(enum Units units)
{ {
if (units == Metric) { if (units == Metric) {
Graph::setXUnits(tr("km")); GraphView::setXUnits(tr("km"));
Graph::setYUnits(tr("m")); GraphView::setYUnits(tr("m"));
Graph::setXScale(M2KM); GraphView::setXScale(M2KM);
Graph::setYScale(1); GraphView::setYScale(1);
} else { } else {
Graph::setXUnits(tr("mi")); GraphView::setXUnits(tr("mi"));
Graph::setYUnits(tr("ft")); GraphView::setYUnits(tr("ft"));
Graph::setXScale(M2MI); GraphView::setXScale(M2MI);
Graph::setYScale(M2FT); GraphView::setYScale(M2FT);
} }
clearInfo(); clearInfo();

View File

@ -1,11 +1,11 @@
#ifndef ELEVATIONGRAPH_H #ifndef ELEVATIONGRAPH_H
#define ELEVATIONGRAPH_H #define ELEVATIONGRAPH_H
#include "graph.h" #include "graphview.h"
#include "gpx.h" #include "gpx.h"
#include "units.h" #include "units.h"
class ElevationGraph : public Graph class ElevationGraph : public GraphView
{ {
Q_OBJECT Q_OBJECT

View File

@ -4,88 +4,12 @@
#include "gpx.h" #include "gpx.h"
#define WINDOW_EF 3
#define WINDOW_SE 11
#define WINDOW_SF 11
static bool lt(const QPointF &p1, const QPointF &p2)
{
return p1.y() < p2.y();
}
static qreal median(QVector<QPointF> v)
{
qSort(v.begin(), v.end(), lt);
return v.at(v.size() / 2).y();
}
static qreal MAD(QVector<QPointF> v, qreal m)
{
for (int i = 0; i < v.size(); i++)
v[i].setY(qAbs(v.at(i).y() - m));
qSort(v.begin(), v.end(), lt);
return v.at(v.size() / 2).y();
}
static QVector<QPointF> eliminate(const QVector<QPointF> &v, int window)
{
QList<int> rm;
QVector<QPointF> ret;
qreal m, M;
if (v.size() < window)
return QVector<QPointF>(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)
rm.append(i);
}
QList<int>::const_iterator it = rm.begin();
for (int i = 0; i < v.size(); i++) {
if (it == rm.end() || *it != i)
ret.append(v.at(i));
else
it++;
}
return ret;
}
static QVector<QPointF> filter(const QVector<QPointF> &v, int window)
{
qreal acc = 0;
QVector<QPointF> ret;
if (v.size() < window)
return QVector<QPointF>(v);
for (int i = 0; i < window; i++)
acc += v.at(i).y();
for (int i = 0; i <= window/2; i++)
ret.append(QPointF(v.at(i).x(), 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));
}
for (int i = v.size() - window/2; i < v.size(); i++)
ret.append(QPointF(v.at(i).x(), acc/window));
return ret;
}
bool GPX::loadFile(const QString &fileName) bool GPX::loadFile(const QString &fileName)
{ {
QFile file(fileName); QFile file(fileName);
bool ret; bool ret;
_data.clear(); _tracks.clear();
_error.clear(); _error.clear();
_errorLine = 0; _errorLine = 0;
@ -94,7 +18,7 @@ bool GPX::loadFile(const QString &fileName)
return false; return false;
} }
if (!(ret = _parser.loadFile(&file, &_data))) { if (!(ret = _parser.loadFile(&file))) {
_error = _parser.errorString(); _error = _parser.errorString();
_errorLine = _parser.errorLine(); _errorLine = _parser.errorLine();
} }
@ -102,90 +26,3 @@ bool GPX::loadFile(const QString &fileName)
return ret; return ret;
} }
void GPX::elevationGraph(int i, QVector<QPointF> &graph) const
{
const QVector<TrackPoint> &data = _data.at(i);
qreal dist = 0;
QVector<QPointF> raw;
if (!data.size())
return;
raw.append(QPointF(0, data.at(0).elevation));
for (int i = 1; i < data.size(); i++) {
dist += llDistance(data.at(i).coordinates, data.at(i-1).coordinates);
raw.append(QPointF(dist, data.at(i).elevation
- data.at(i).geoidheight));
}
graph = filter(raw, WINDOW_EF);
}
void GPX::speedGraph(int i, QVector<QPointF> &graph) const
{
const QVector<TrackPoint> &data = _data.at(i);
qreal dist = 0, v, ds, dt;
QVector<QPointF> raw;
if (!data.size())
return;
raw.append(QPointF(0, 0));
for (int i = 1; i < data.size(); i++) {
ds = llDistance(data.at(i).coordinates, data.at(i-1).coordinates);
dt = data.at(i-1).timestamp.msecsTo(data.at(i).timestamp) / 1000.0;
dist += ds;
if (data.at(i).speed < 0) {
if (dt == 0)
continue;
v = ds / dt;
} else
v = data.at(i).speed;
raw.append(QPointF(dist, v));
}
graph = filter(eliminate(raw, WINDOW_SE), WINDOW_SF);
}
void GPX::track(int i, QVector<QPointF> &track) const
{
const QVector<TrackPoint> &data = _data.at(i);
for (int i = 0; i < data.size(); i++)
track.append(ll2mercator(data.at(i).coordinates));
}
qreal GPX::distance(int i) const
{
const QVector<TrackPoint> &data = _data.at(i);
qreal dist = 0;
for (int i = 1; i < data.size(); i++)
dist += llDistance(data.at(i).coordinates, data.at(i-1).coordinates);
return dist;
}
qreal GPX::time(int i) const
{
const QVector<TrackPoint> &data = _data.at(i);
if (data.size() < 2)
return 0;
return (data.at(0).timestamp.msecsTo(data.at(data.size() - 1).timestamp)
/ 1000.0);
}
QDateTime GPX::date(int i) const
{
const QVector<TrackPoint> &data = _data.at(i);
if (data.size())
return data.at(0).timestamp;
else
return QDateTime();
}

View File

@ -5,28 +5,29 @@
#include <QList> #include <QList>
#include <QPointF> #include <QPointF>
#include <QString> #include <QString>
#include "waypoint.h"
#include "track.h"
#include "parser.h" #include "parser.h"
class GPX class GPX
{ {
public: public:
GPX() : _parser(_tracks, _waypoints) {}
bool loadFile(const QString &fileName); bool loadFile(const QString &fileName);
const QString &errorString() const {return _error;} const QString &errorString() const {return _error;}
int errorLine() const {return _errorLine;} int errorLine() const {return _errorLine;}
int count() const {return _data.count();} int trackCount() const {return _tracks.count();}
void elevationGraph(int i, QVector<QPointF> &graph) const; Track track(int i) const {return Track(_tracks.at(i));}
void speedGraph(int i, QVector<QPointF> &graph) const; const QList<WayPoint> &waypoints() const {return _waypoints;}
void track(int i, QVector<QPointF> &track) const;
qreal distance(int i) const;
qreal time(int i) const;
QDateTime date(int i) const;
private: private:
Parser _parser; Parser _parser;
QList<QVector<TrackPoint> > _data;
QString _error; QString _error;
int _errorLine; int _errorLine;
QList<QVector<TrackPoint> > _tracks;
QList<WayPoint> _waypoints;
}; };
#endif // GPX_H #endif // GPX_H

View File

@ -3,11 +3,11 @@
#include <QGraphicsSceneMouseEvent> #include <QGraphicsSceneMouseEvent>
#include <QEvent> #include <QEvent>
#include <QGraphicsSimpleTextItem> #include <QGraphicsSimpleTextItem>
#include "config.h"
#include "slideritem.h" #include "slideritem.h"
#include "sliderinfoitem.h" #include "sliderinfoitem.h"
#include "infoitem.h" #include "infoitem.h"
#include "config.h" #include "graphview.h"
#include "graph.h"
#define MARGIN 10.0 #define MARGIN 10.0
@ -21,7 +21,7 @@ void Scene::mousePressEvent(QGraphicsSceneMouseEvent *e)
} }
Graph::Graph(QWidget *parent) GraphView::GraphView(QWidget *parent)
: QGraphicsView(parent) : QGraphicsView(parent)
{ {
_scene = new Scene(this); _scene = new Scene(this);
@ -54,7 +54,7 @@ Graph::Graph(QWidget *parent)
_precision = 0; _precision = 0;
} }
Graph::~Graph() GraphView::~GraphView()
{ {
if (_xAxis->scene() != _scene) if (_xAxis->scene() != _scene)
delete _xAxis; delete _xAxis;
@ -70,7 +70,7 @@ Graph::~Graph()
delete _scene; delete _scene;
} }
void Graph::updateBounds(const QPointF &point) void GraphView::updateBounds(const QPointF &point)
{ {
if (point.x() < _xMin) if (point.x() < _xMin)
_xMin = point.x(); _xMin = point.x();
@ -82,51 +82,51 @@ void Graph::updateBounds(const QPointF &point)
_yMax = point.y(); _yMax = point.y();
} }
void Graph::createXLabel() void GraphView::createXLabel()
{ {
_xAxis->setLabel(QString("%1 [%2]").arg(_xLabel).arg(_xUnits)); _xAxis->setLabel(QString("%1 [%2]").arg(_xLabel).arg(_xUnits));
} }
void Graph::createYLabel() void GraphView::createYLabel()
{ {
_yAxis->setLabel(QString("%1 [%2]").arg(_yLabel).arg(_yUnits)); _yAxis->setLabel(QString("%1 [%2]").arg(_yLabel).arg(_yUnits));
} }
void Graph::setXLabel(const QString &label) void GraphView::setXLabel(const QString &label)
{ {
_xLabel = label; _xLabel = label;
createXLabel(); createXLabel();
} }
void Graph::setYLabel(const QString &label) void GraphView::setYLabel(const QString &label)
{ {
_yLabel = label; _yLabel = label;
createYLabel(); createYLabel();
} }
void Graph::setXUnits(const QString &units) void GraphView::setXUnits(const QString &units)
{ {
_xUnits = units; _xUnits = units;
createXLabel(); createXLabel();
} }
void Graph::setYUnits(const QString &units) void GraphView::setYUnits(const QString &units)
{ {
_yUnits = units; _yUnits = units;
createYLabel(); createYLabel();
} }
void Graph::setXScale(qreal scale) void GraphView::setXScale(qreal scale)
{ {
_xScale = scale; _xScale = scale;
} }
void Graph::setYScale(qreal scale) void GraphView::setYScale(qreal scale)
{ {
_yScale = scale; _yScale = scale;
} }
void Graph::loadData(const QVector<QPointF> &data) void GraphView::loadData(const QVector<QPointF> &data)
{ {
QPainterPath path; QPainterPath path;
QGraphicsPathItem *pi; QGraphicsPathItem *pi;
@ -156,13 +156,13 @@ void Graph::loadData(const QVector<QPointF> &data)
redraw(); redraw();
} }
void Graph::redraw() void GraphView::redraw()
{ {
if (!_graphs.isEmpty()) if (!_graphs.isEmpty())
resize(viewport()->size() - QSizeF(MARGIN, MARGIN)); resize(viewport()->size() - QSizeF(MARGIN, MARGIN));
} }
void Graph::resize(const QSizeF &size) void GraphView::resize(const QSizeF &size)
{ {
QRectF r; QRectF r;
QSizeF mx, my; QSizeF mx, my;
@ -222,13 +222,13 @@ void Graph::resize(const QSizeF &size)
_scene->setSceneRect(_scene->itemsBoundingRect()); _scene->setSceneRect(_scene->itemsBoundingRect());
} }
void Graph::resizeEvent(QResizeEvent *) void GraphView::resizeEvent(QResizeEvent *)
{ {
if (!_graphs.empty()) if (!_graphs.empty())
redraw(); redraw();
} }
void Graph::plot(QPainter *painter, const QRectF &target) void GraphView::plot(QPainter *painter, const QRectF &target)
{ {
qreal ratio = target.width() / target.height(); qreal ratio = target.width() / target.height();
QSizeF orig = _scene->sceneRect().size(); QSizeF orig = _scene->sceneRect().size();
@ -243,7 +243,7 @@ void Graph::plot(QPainter *painter, const QRectF &target)
resize(orig); resize(orig);
} }
void Graph::clear() void GraphView::clear()
{ {
if (_xAxis->scene() == _scene) if (_xAxis->scene() == _scene)
_scene->removeItem(_xAxis); _scene->removeItem(_xAxis);
@ -271,7 +271,7 @@ void Graph::clear()
_scene->setSceneRect(0, 0, 0, 0); _scene->setSceneRect(0, 0, 0, 0);
} }
void Graph::emitSliderPositionChanged(const QPointF &pos) void GraphView::emitSliderPositionChanged(const QPointF &pos)
{ {
if (_graphs.isEmpty()) if (_graphs.isEmpty())
return; return;
@ -287,17 +287,17 @@ void Graph::emitSliderPositionChanged(const QPointF &pos)
_sliderInfo->setText(QString::number(-p.y() * _yScale, 'f', _precision)); _sliderInfo->setText(QString::number(-p.y() * _yScale, 'f', _precision));
} }
qreal Graph::sliderPosition() const qreal GraphView::sliderPosition() const
{ {
return _slider->pos().x() / _slider->area().width(); return _slider->pos().x() / _slider->area().width();
} }
void Graph::setSliderPosition(qreal pos) void GraphView::setSliderPosition(qreal pos)
{ {
_slider->setPos(pos * _slider->area().width(), 0); _slider->setPos(pos * _slider->area().width(), 0);
} }
void Graph::newSliderPosition(const QPointF &pos) void GraphView::newSliderPosition(const QPointF &pos)
{ {
if (_slider->area().contains(pos)) { if (_slider->area().contains(pos)) {
_slider->setPos(pos); _slider->setPos(pos);
@ -305,12 +305,12 @@ void Graph::newSliderPosition(const QPointF &pos)
} }
} }
void Graph::addInfo(const QString &key, const QString &value) void GraphView::addInfo(const QString &key, const QString &value)
{ {
_info->insert(key, value); _info->insert(key, value);
} }
void Graph::clearInfo() void GraphView::clearInfo()
{ {
_info->clear(); _info->clear();
} }

View File

@ -1,5 +1,5 @@
#ifndef GRAPH_H #ifndef GRAPHVIEW_H
#define GRAPH_H #define GRAPHVIEW_H
#include <QGraphicsView> #include <QGraphicsView>
#include <QGraphicsScene> #include <QGraphicsScene>
@ -28,13 +28,13 @@ signals:
void mouseClicked(const QPointF &pos); void mouseClicked(const QPointF &pos);
}; };
class Graph : public QGraphicsView class GraphView : public QGraphicsView
{ {
Q_OBJECT Q_OBJECT
public: public:
Graph(QWidget *parent = 0); GraphView(QWidget *parent = 0);
~Graph(); ~GraphView();
void loadData(const QVector<QPointF> &data); void loadData(const QVector<QPointF> &data);
void setXLabel(const QString &label); void setXLabel(const QString &label);
@ -89,4 +89,4 @@ private:
ColorShop _colorShop; ColorShop _colorShop;
}; };
#endif // GRAPH_H #endif // GRAPHVIEW_H

View File

@ -16,7 +16,7 @@
#include "maplist.h" #include "maplist.h"
#include "elevationgraph.h" #include "elevationgraph.h"
#include "speedgraph.h" #include "speedgraph.h"
#include "track.h" #include "trackview.h"
#include "infoitem.h" #include "infoitem.h"
#include "filebrowser.h" #include "filebrowser.h"
#include "gui.h" #include "gui.h"
@ -302,7 +302,7 @@ void GUI::createToolBars()
void GUI::createTrackView() void GUI::createTrackView()
{ {
_track = new Track(this); _track = new TrackView(this);
if (_showMapAction->isChecked()) if (_showMapAction->isChecked())
_track->setMap(_currentMap); _track->setMap(_currentMap);
@ -444,12 +444,12 @@ bool GUI::loadFile(const QString &fileName)
if (_showPOIAction->isChecked()) if (_showPOIAction->isChecked())
_track->loadPOI(_poi); _track->loadPOI(_poi);
for (int i = 0; i < gpx.count(); i++) { for (int i = 0; i < gpx.trackCount(); i++) {
_distance += gpx.distance(i); _distance += gpx.track(i).distance();
_time += gpx.time(i); _time += gpx.track(i).time();
} }
_trackCount += gpx.count(); _trackCount += gpx.trackCount();
return true; return true;
} else { } else {

View File

@ -15,7 +15,7 @@
class FileBrowser; class FileBrowser;
class ElevationGraph; class ElevationGraph;
class SpeedGraph; class SpeedGraph;
class Track; class TrackView;
class Map; class Map;
class GUI : public QMainWindow class GUI : public QMainWindow
@ -114,7 +114,7 @@ private:
ElevationGraph *_elevationGraph; ElevationGraph *_elevationGraph;
SpeedGraph *_speedGraph; SpeedGraph *_speedGraph;
Track *_track; TrackView *_track;
POI _poi; POI _poi;
QList<Map*> _maps; QList<Map*> _maps;

View File

@ -18,12 +18,24 @@ void Parser::handleTrekPointData(QStringRef element, const QString &value)
_track->last().geoidheight = value.toLatin1().toDouble(); _track->last().geoidheight = value.toLatin1().toDouble();
} }
void Parser::handleWayPointData(QStringRef element, const QString &value)
{
if (element == "name")
_waypoints.last().setDescription(value);
}
void Parser::handleTrekPointAttributes(const QXmlStreamAttributes &attr) void Parser::handleTrekPointAttributes(const QXmlStreamAttributes &attr)
{ {
_track->last().coordinates.setY(attr.value("lat").toLatin1().toDouble()); _track->last().coordinates.setY(attr.value("lat").toLatin1().toDouble());
_track->last().coordinates.setX(attr.value("lon").toLatin1().toDouble()); _track->last().coordinates.setX(attr.value("lon").toLatin1().toDouble());
} }
void Parser::handleWayPointAttributes(const QXmlStreamAttributes &attr)
{
_waypoints.last().setCoordinates(QPointF(
attr.value("lon").toLatin1().toDouble(),
attr.value("lat").toLatin1().toDouble()));
}
void Parser::extensions() void Parser::extensions()
{ {
@ -50,13 +62,10 @@ void Parser::trackPointData()
void Parser::trackPoints() void Parser::trackPoints()
{ {
QXmlStreamAttributes attr;
while (_reader.readNextStartElement()) { while (_reader.readNextStartElement()) {
if (_reader.name() == "trkpt") { if (_reader.name() == "trkpt") {
attr = _reader.attributes();
_track->append(TrackPoint()); _track->append(TrackPoint());
handleTrekPointAttributes(attr); handleTrekPointAttributes(_reader.attributes());
trackPointData(); trackPointData();
} else } else
_reader.skipCurrentElement(); _reader.skipCurrentElement();
@ -73,13 +82,27 @@ void Parser::track()
} }
} }
void Parser::wayPointData()
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "name")
handleWayPointData(_reader.name(), _reader.readElementText());
else
_reader.skipCurrentElement();
}
}
void Parser::gpx() void Parser::gpx()
{ {
while (_reader.readNextStartElement()) { while (_reader.readNextStartElement()) {
if (_reader.name() == "trk") { if (_reader.name() == "trk") {
_data->append(QVector<TrackPoint>()); _tracks.append(QVector<TrackPoint>());
_track = &_data->back(); _track = &_tracks.back();
track(); track();
} else if (_reader.name() == "wpt") {
_waypoints.append(WayPoint());
handleWayPointAttributes(_reader.attributes());
wayPointData();
} else } else
_reader.skipCurrentElement(); _reader.skipCurrentElement();
} }
@ -97,11 +120,10 @@ bool Parser::parse()
return !_reader.error(); return !_reader.error();
} }
bool Parser::loadFile(QIODevice *device, QList<QVector<TrackPoint> > *data) bool Parser::loadFile(QIODevice *device)
{ {
_reader.clear(); _reader.clear();
_reader.setDevice(device); _reader.setDevice(device);
_data = data;
return parse(); return parse();
} }

View File

@ -2,25 +2,18 @@
#define PARSER_H #define PARSER_H
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include <QDateTime> #include <QVector>
#include <QPointF> #include <QList>
#include "trackpoint.h"
#include "waypoint.h"
struct TrackPoint
{
QPointF coordinates;
QDateTime timestamp;
qreal elevation;
qreal geoidheight;
qreal speed;
TrackPoint() {elevation = 0; geoidheight = 0; speed = -1;}
};
class Parser class Parser
{ {
public: public:
Parser() {_data = 0; _track = 0;} Parser(QList<QVector<TrackPoint> > &tracks, QList<WayPoint> &waypoints)
bool loadFile(QIODevice *device, QList<QVector<TrackPoint> > *data); : _tracks(tracks), _waypoints(waypoints) {_track = 0;}
bool loadFile(QIODevice *device);
QString errorString() const {return _reader.errorString();} QString errorString() const {return _reader.errorString();}
int errorLine() const {return _reader.lineNumber();} int errorLine() const {return _reader.lineNumber();}
@ -31,13 +24,17 @@ private:
void trackPoints(); void trackPoints();
void extensions(); void extensions();
void trackPointData(); void trackPointData();
void wayPointData();
void handleWayPointAttributes(const QXmlStreamAttributes &attr);
void handleWayPointData(QStringRef element, const QString &value);
void handleTrekPointAttributes(const QXmlStreamAttributes &attr); void handleTrekPointAttributes(const QXmlStreamAttributes &attr);
void handleTrekPointData(QStringRef element, const QString &value); void handleTrekPointData(QStringRef element, const QString &value);
void handleExtensionData(QStringRef element, const QString &value); void handleExtensionData(QStringRef element, const QString &value);
QXmlStreamReader _reader; QXmlStreamReader _reader;
QList<QVector<TrackPoint> > *_data; QList<QVector<TrackPoint> > &_tracks;
QList<WayPoint> &_waypoints;
QVector<TrackPoint> *_track; QVector<TrackPoint> *_track;
}; };

View File

@ -2,20 +2,58 @@
#include <QSet> #include <QSet>
#include <QList> #include <QList>
#include "ll.h" #include "ll.h"
#include "gpx.h"
#include "poi.h" #include "poi.h"
#define BOUNDING_RECT_SIZE 0.01 #define BOUNDING_RECT_SIZE 0.01
bool POI::loadFile(const QString &fileName) bool POI::loadFile(const QString &fileName)
{
_error.clear();
_errorLine = 0;
if (loadCSVFile(fileName))
return true;
if (loadGPXFile(fileName))
return true;
return false;
}
bool POI::loadGPXFile(const QString &fileName)
{
GPX gpx;
int cnt = _data.size();
if (gpx.loadFile(fileName)) {
for (int i = 0; i < gpx.waypoints().size(); i++)
_data.append(WayPoint(
ll2mercator(gpx.waypoints().at(i).coordinates()),
gpx.waypoints().at(i).description()));
for (int i = cnt; i < _data.size(); ++i) {
qreal c[2];
c[0] = _data.at(i).coordinates().x();
c[1] = _data.at(i).coordinates().y();
_tree.Insert(c, c, i);
}
return true;
} else {
_error = gpx.errorString();
_errorLine = gpx.errorLine();
}
return false;
}
bool POI::loadCSVFile(const QString &fileName)
{ {
QFile file(fileName); QFile file(fileName);
bool ret; bool ret;
int ln = 1, cnt = _data.size(); int ln = 1, cnt = _data.size();
_error.clear();
_errorLine = 0;
if (!file.open(QFile::ReadOnly | QFile::Text)) { if (!file.open(QFile::ReadOnly | QFile::Text)) {
_error = qPrintable(file.errorString()); _error = qPrintable(file.errorString());
return false; return false;
@ -44,18 +82,15 @@ bool POI::loadFile(const QString &fileName)
} }
QByteArray ba = list[2].trimmed(); QByteArray ba = list[2].trimmed();
Entry entry; _data.append(WayPoint(ll2mercator(QPointF(lon, lat)),
entry.description = QString::fromUtf8(ba.data(), ba.size()); QString::fromUtf8(ba.data(), ba.size())));
entry.coordinates = ll2mercator(QPointF(lon, lat));
_data.append(entry);
ln++; ln++;
} }
for (int i = cnt; i < _data.size(); ++i) { for (int i = cnt; i < _data.size(); ++i) {
qreal c[2]; qreal c[2];
c[0] = _data.at(i).coordinates.x(); c[0] = _data.at(i).coordinates().x();
c[1] = _data.at(i).coordinates.y(); c[1] = _data.at(i).coordinates().y();
_tree.Insert(c, c, i); _tree.Insert(c, c, i);
} }
@ -70,9 +105,9 @@ static bool cb(size_t data, void* context)
return true; return true;
} }
QVector<Entry> POI::points(const QVector<QPointF> &path) const QVector<WayPoint> POI::points(const QVector<QPointF> &path) const
{ {
QVector<Entry> ret; QVector<WayPoint> ret;
QSet<int> set; QSet<int> set;
qreal min[2], max[2]; qreal min[2], max[2];

View File

@ -4,24 +4,9 @@
#include <QVector> #include <QVector>
#include <QPointF> #include <QPointF>
#include <QString> #include <QString>
#include "waypoint.h"
#include "rtree.h" #include "rtree.h"
class Entry
{
public:
QPointF coordinates;
QString description;
bool operator==(const Entry &other) const
{return this->description == other.description
&& this->coordinates == other.coordinates;}
};
inline uint qHash(const Entry &key)
{
return ::qHash(key.description);
}
class POI class POI
{ {
@ -30,15 +15,18 @@ public:
QString errorString() const {return _error;} QString errorString() const {return _error;}
int errorLine() const {return _errorLine;} int errorLine() const {return _errorLine;}
QVector<Entry> points(const QVector<QPointF> &path) const; QVector<WayPoint> points(const QVector<QPointF> &path) const;
void clear(); void clear();
private: private:
typedef RTree<size_t, qreal, 2> POITree; typedef RTree<size_t, qreal, 2> POITree;
bool loadCSVFile(const QString &fileName);
bool loadGPXFile(const QString &fileName);
POITree _tree; POITree _tree;
QVector<Entry> _data; QVector<WayPoint> _data;
QString _error; QString _error;
int _errorLine; int _errorLine;
}; };

View File

@ -5,7 +5,7 @@
#define POINT_SIZE 8 #define POINT_SIZE 8
POIItem::POIItem(const Entry &entry, QGraphicsItem *parent) POIItem::POIItem(const WayPoint &entry, QGraphicsItem *parent)
: QGraphicsItem(parent) : QGraphicsItem(parent)
{ {
_entry = entry; _entry = entry;
@ -18,7 +18,7 @@ void POIItem::updateBoundingRect()
font.setPixelSize(FONT_SIZE); font.setPixelSize(FONT_SIZE);
font.setFamily(FONT_FAMILY); font.setFamily(FONT_FAMILY);
QFontMetrics fm(font); QFontMetrics fm(font);
QRect ts = fm.tightBoundingRect(_entry.description); QRect ts = fm.tightBoundingRect(_entry.description());
_boundingRect = QRectF(0, 0, ts.width() + POINT_SIZE, _boundingRect = QRectF(0, 0, ts.width() + POINT_SIZE,
ts.height() + fm.descent() + POINT_SIZE); ts.height() + fm.descent() + POINT_SIZE);
@ -33,11 +33,11 @@ void POIItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
font.setPixelSize(FONT_SIZE); font.setPixelSize(FONT_SIZE);
font.setFamily(FONT_FAMILY); font.setFamily(FONT_FAMILY);
QFontMetrics fm(font); QFontMetrics fm(font);
QRect ts = fm.tightBoundingRect(_entry.description); QRect ts = fm.tightBoundingRect(_entry.description());
painter->setFont(font); painter->setFont(font);
painter->drawText(POINT_SIZE - qMax(ts.x(), 0), POINT_SIZE + ts.height(), painter->drawText(POINT_SIZE - qMax(ts.x(), 0), POINT_SIZE + ts.height(),
_entry.description); _entry.description());
painter->setBrush(Qt::SolidPattern); painter->setBrush(Qt::SolidPattern);
painter->drawEllipse(0, 0, POINT_SIZE, POINT_SIZE); painter->drawEllipse(0, 0, POINT_SIZE, POINT_SIZE);

View File

@ -7,8 +7,8 @@
class POIItem : public QGraphicsItem class POIItem : public QGraphicsItem
{ {
public: public:
POIItem(const Entry &entry, QGraphicsItem *parent = 0); POIItem(const WayPoint &entry, QGraphicsItem *parent = 0);
const Entry &entry() const {return _entry;} const WayPoint &entry() const {return _entry;}
QRectF boundingRect() const {return _boundingRect;} QRectF boundingRect() const {return _boundingRect;}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
@ -17,7 +17,7 @@ public:
private: private:
void updateBoundingRect(); void updateBoundingRect();
Entry _entry; WayPoint _entry;
QRectF _boundingRect; QRectF _boundingRect;
}; };

View File

@ -2,38 +2,39 @@
#include "speedgraph.h" #include "speedgraph.h"
SpeedGraph::SpeedGraph(QWidget *parent) : Graph(parent) SpeedGraph::SpeedGraph(QWidget *parent) : GraphView(parent)
{ {
_max = 0; _max = 0;
Graph::setXLabel(tr("Distance")); GraphView::setXLabel(tr("Distance"));
Graph::setYLabel(tr("Speed")); GraphView::setYLabel(tr("Speed"));
Graph::setXUnits(tr("km")); GraphView::setXUnits(tr("km"));
Graph::setYUnits(tr("km/h")); GraphView::setYUnits(tr("km/h"));
Graph::setXScale(M2KM); GraphView::setXScale(M2KM);
Graph::setYScale(MS2KMH); GraphView::setYScale(MS2KMH);
Graph::setPrecision(1); GraphView::setPrecision(1);
} }
void SpeedGraph::addInfo() void SpeedGraph::addInfo()
{ {
Graph::addInfo(tr("Average"), QString::number(avg() * _yScale, 'f', 1) GraphView::addInfo(tr("Average"), QString::number(avg() * _yScale, 'f', 1)
+ THIN_SPACE + _yUnits); + THIN_SPACE + _yUnits);
Graph::addInfo(tr("Maximum"), QString::number(_max * _yScale, 'f', 1) GraphView::addInfo(tr("Maximum"), QString::number(_max * _yScale, 'f', 1)
+ THIN_SPACE + _yUnits); + THIN_SPACE + _yUnits);
} }
void SpeedGraph::loadGPX(const GPX &gpx) void SpeedGraph::loadGPX(const GPX &gpx)
{ {
for (int i = 0; i < gpx.count(); i++) { for (int i = 0; i < gpx.trackCount(); i++) {
QVector<QPointF> data; QVector<QPointF> data;
qreal max = 0; qreal max = 0;
gpx.speedGraph(i, data); gpx.track(i).speedGraph(data);
if (data.isEmpty()) if (data.isEmpty())
return; return;
_avg.append(QPointF(gpx.distance(i), gpx.distance(i) / gpx.time(i))); _avg.append(QPointF(gpx.track(i).distance(), gpx.track(i).distance()
/ gpx.track(i).time()));
for (int i = 0; i < data.size(); i++) for (int i = 0; i < data.size(); i++)
max = qMax(max, data.at(i).y()); max = qMax(max, data.at(i).y());
@ -62,21 +63,21 @@ void SpeedGraph::clear()
_max = 0; _max = 0;
_avg.clear(); _avg.clear();
Graph::clear(); GraphView::clear();
} }
void SpeedGraph::setUnits(enum Units units) void SpeedGraph::setUnits(enum Units units)
{ {
if (units == Metric) { if (units == Metric) {
Graph::setXUnits(tr("km")); GraphView::setXUnits(tr("km"));
Graph::setYUnits(tr("km/h")); GraphView::setYUnits(tr("km/h"));
Graph::setXScale(M2KM); GraphView::setXScale(M2KM);
Graph::setYScale(MS2KMH); GraphView::setYScale(MS2KMH);
} else { } else {
Graph::setXUnits(tr("mi")); GraphView::setXUnits(tr("mi"));
Graph::setYUnits(tr("mi/h")); GraphView::setYUnits(tr("mi/h"));
Graph::setXScale(M2MI); GraphView::setXScale(M2MI);
Graph::setYScale(MS2MIH); GraphView::setYScale(MS2MIH);
} }
clearInfo(); clearInfo();

View File

@ -2,11 +2,11 @@
#define SPEEDGRAPH_H #define SPEEDGRAPH_H
#include <QList> #include <QList>
#include "graph.h" #include "graphview.h"
#include "gpx.h" #include "gpx.h"
#include "units.h" #include "units.h"
class SpeedGraph : public Graph class SpeedGraph : public GraphView
{ {
Q_OBJECT Q_OBJECT

View File

@ -1,398 +1,155 @@
#include <cmath>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QPainterPath>
#include <QWheelEvent>
#include "poiitem.h"
#include "markeritem.h"
#include "scaleitem.h"
#include "ll.h" #include "ll.h"
#include "track.h" #include "track.h"
#define WINDOW_EF 3
#define WINDOW_SE 11
#define WINDOW_SF 11
#define MARGIN 10.0 static bool lt(const QPointF &p1, const QPointF &p2)
#define TRACK_WIDTH 3
#define SCALE_OFFSET 7
Track::Track(QWidget *parent)
: QGraphicsView(parent)
{ {
_scene = new QGraphicsScene(this); return p1.y() < p2.y();
setScene(_scene);
setCacheMode(QGraphicsView::CacheBackground);
setDragMode(QGraphicsView::ScrollHandDrag);
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
_mapScale = new ScaleItem();
_mapScale->setZValue(2.0);
_zoom = -1;
_scale = 1.0;
_map = 0;
_maxLen = 0;
} }
Track::~Track() static qreal median(QVector<QPointF> v)
{ {
delete _scene; qSort(v.begin(), v.end(), lt);
return v.at(v.size() / 2).y();
} }
void Track::loadGPX(const GPX &gpx) static qreal MAD(QVector<QPointF> v, qreal m)
{ {
for (int i = 0; i < gpx.count(); i++) { for (int i = 0; i < v.size(); i++)
QVector<QPointF> track; v[i].setY(qAbs(v.at(i).y() - m));
QPainterPath path; qSort(v.begin(), v.end(), lt);
QGraphicsPathItem *pi; return v.at(v.size() / 2).y();
MarkerItem *mi;
QColor color = _colorShop.color();
qreal prevScale = _scale;
gpx.track(i, track);
if (track.size() < 2)
continue;
_tracks.append(track);
path.moveTo(track.at(0).x(), -track.at(0).y());
for (int i = 1; i < track.size(); i++)
path.lineTo(track.at(i).x(), -track.at(i).y());
_maxLen = qMax(path.length(), _maxLen);
pi = new QGraphicsPathItem(path);
_trackPaths.append(pi);
_zoom = scale2zoom(trackScale());
_scale = mapScale();
QBrush brush(color, Qt::SolidPattern);
QPen pen(brush, TRACK_WIDTH * _scale);
pi->setPen(pen);
pi->setScale(1.0/_scale);
_scene->addItem(pi);
mi = new MarkerItem(pi);
_markers.append(mi);
mi->setPos(pi->path().pointAtPercent(0));
mi->setScale(_scale);
if (_trackPaths.size() > 1 && prevScale != _scale)
rescale(_scale);
QRectF br = trackBoundingRect();
QRectF ba = br.adjusted(-TILE_SIZE, -TILE_SIZE, TILE_SIZE, TILE_SIZE);
_scene->setSceneRect(ba);
centerOn(ba.center());
if (_mapScale->scene() != _scene)
_scene->addItem(_mapScale);
_mapScale->setLatitude(track.at(track.size() / 2).y());
_mapScale->setZoom(_zoom);
}
} }
QRectF Track::trackBoundingRect() const static QVector<QPointF> eliminate(const QVector<QPointF> &v, int window)
{ {
qreal bottom, top, left, right; QList<int> rm;
QVector<QPointF> ret;
qreal m, M;
bottom = _trackPaths.at(0)->sceneBoundingRect().bottom();
top = _trackPaths.at(0)->sceneBoundingRect().top();
left = _trackPaths.at(0)->sceneBoundingRect().left();
right = _trackPaths.at(0)->sceneBoundingRect().right();
for (int i = 1; i < _trackPaths.size(); i++) { if (v.size() < window)
bottom = qMax(bottom, _trackPaths.at(i)->sceneBoundingRect().bottom()); return QVector<QPointF>(v);
top = qMin(top, _trackPaths.at(i)->sceneBoundingRect().top());
right = qMax(right, _trackPaths.at(i)->sceneBoundingRect().right()); for (int i = window/2; i < v.size() - window/2; i++) {
left = qMin(left, _trackPaths.at(i)->sceneBoundingRect().left()); 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)
rm.append(i);
} }
return QRectF(QPointF(left, top), QPointF(right, bottom)); QList<int>::const_iterator it = rm.begin();
} for (int i = 0; i < v.size(); i++) {
if (it == rm.end() || *it != i)
qreal Track::trackScale() const ret.append(v.at(i));
{ else
qreal bottom, top, left, right; it++;
bottom = _trackPaths.at(0)->path().boundingRect().bottom();
top = _trackPaths.at(0)->path().boundingRect().top();
left = _trackPaths.at(0)->path().boundingRect().left();
right = _trackPaths.at(0)->path().boundingRect().right();
for (int i = 1; i < _trackPaths.size(); i++) {
bottom = qMax(bottom, _trackPaths.at(i)->path().boundingRect().bottom());
top = qMin(top, _trackPaths.at(i)->path().boundingRect().top());
right = qMax(right, _trackPaths.at(i)->path().boundingRect().right());
left = qMin(left, _trackPaths.at(i)->path().boundingRect().left());
} }
QRectF br(QPointF(left, top), QPointF(right, bottom)); return ret;
QPointF sc(br.width() / (viewport()->width() - MARGIN/2),
br.height() / (viewport()->height() - MARGIN/2));
return qMax(sc.x(), sc.y());
} }
qreal Track::mapScale() const static QVector<QPointF> filter(const QVector<QPointF> &v, int window)
{ {
return ((360.0/(qreal)(1<<_zoom))/(qreal)TILE_SIZE); qreal acc = 0;
QVector<QPointF> ret;
if (v.size() < window)
return QVector<QPointF>(v);
for (int i = 0; i < window; i++)
acc += v.at(i).y();
for (int i = 0; i <= window/2; i++)
ret.append(QPointF(v.at(i).x(), 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));
}
for (int i = v.size() - window/2; i < v.size(); i++)
ret.append(QPointF(v.at(i).x(), acc/window));
return ret;
} }
void Track::rescale(qreal scale) void Track::elevationGraph(QVector<QPointF> &graph) const
{ {
for (int i = 0; i < _trackPaths.size(); i++) { qreal dist = 0;
_markers.at(i)->setScale(scale); QVector<QPointF> raw;
_trackPaths.at(i)->setScale(1.0/scale);
QPen pen(_trackPaths.at(i)->pen()); if (!_data.size())
pen.setWidthF(TRACK_WIDTH * scale);
_trackPaths.at(i)->setPen(pen);
}
QHash<Entry, POIItem*>::const_iterator it, jt;
for (it = _pois.constBegin(); it != _pois.constEnd(); it++) {
it.value()->setPos(QPointF(it.value()->entry().coordinates.x()
* 1.0/scale, -it.value()->entry().coordinates.y() * 1.0/scale));
it.value()->show();
}
for (it = _pois.constBegin(); it != _pois.constEnd(); it++) {
for (jt = _pois.constBegin(); jt != _pois.constEnd(); jt++) {
if (it != jt && it.value()->isVisible() && jt.value()->isVisible()
&& it.value()->collidesWithItem(jt.value()))
jt.value()->hide();
}
}
_scale = scale;
}
void Track::loadPOI(const POI &poi)
{
QHash<Entry, POIItem*>::const_iterator it,jt;
if (!_tracks.size())
return; return;
for (int i = 0; i < _tracks.size(); i++) { raw.append(QPointF(0, _data.at(0).elevation));
QVector<Entry> p = poi.points(_tracks.at(i)); for (int i = 1; i < _data.size(); i++) {
dist += llDistance(_data.at(i).coordinates, _data.at(i-1).coordinates);
raw.append(QPointF(dist, _data.at(i).elevation
- _data.at(i).geoidheight));
}
for (int i = 0; i < p.size(); i++) { graph = filter(raw, WINDOW_EF);
if (_pois.contains(p.at(i))) }
void Track::speedGraph(QVector<QPointF> &graph) const
{
qreal dist = 0, v, ds, dt;
QVector<QPointF> raw;
if (!_data.size())
return;
raw.append(QPointF(0, 0));
for (int i = 1; i < _data.size(); i++) {
ds = llDistance(_data.at(i).coordinates, _data.at(i-1).coordinates);
dt = _data.at(i-1).timestamp.msecsTo(_data.at(i).timestamp) / 1000.0;
dist += ds;
if (_data.at(i).speed < 0) {
if (dt == 0)
continue; continue;
v = ds / dt;
} else
v = _data.at(i).speed;
POIItem *pi = new POIItem(p.at(i)); raw.append(QPointF(dist, v));
pi->setPos(p.at(i).coordinates.x() * 1.0/_scale,
-p.at(i).coordinates.y() * 1.0/_scale);
pi->setZValue(1);
_scene->addItem(pi);
_pois.insert(p.at(i), pi);
}
} }
for (it = _pois.constBegin(); it != _pois.constEnd(); it++) { graph = filter(eliminate(raw, WINDOW_SE), WINDOW_SF);
for (jt = _pois.constBegin(); jt != _pois.constEnd(); jt++) {
if (it != jt && it.value()->isVisible() && jt.value()->isVisible()
&& it.value()->collidesWithItem(jt.value()))
jt.value()->hide();
}
}
} }
void Track::setMap(Map *map) void Track::track(QVector<QPointF> &track) const
{ {
_map = map; for (int i = 0; i < _data.size(); i++)
if (_map) track.append(ll2mercator(_data.at(i).coordinates));
connect(_map, SIGNAL(loaded()), this, SLOT(redraw()));
resetCachedContent();
} }
void Track::setUnits(enum Units units) qreal Track::distance() const
{ {
_mapScale->setUnits(units); qreal dist = 0;
for (int i = 1; i < _data.size(); i++)
dist += llDistance(_data.at(i).coordinates, _data.at(i-1).coordinates);
return dist;
} }
void Track::redraw() qreal Track::time() const
{ {
resetCachedContent(); if (_data.size() < 2)
return 0;
return (_data.at(0).timestamp.msecsTo(_data.at(_data.size() - 1).timestamp)
/ 1000.0);
} }
void Track::wheelEvent(QWheelEvent *event) QDateTime Track::date() const
{ {
if (_tracks.isEmpty()) if (_data.size())
return; return _data.at(0).timestamp;
QPointF pos = mapToScene(event->pos());
qreal scale = _scale;
_zoom = (event->delta() > 0) ?
qMin(_zoom + 1, ZOOM_MAX) : qMax(_zoom - 1, ZOOM_MIN);
rescale(mapScale());
QRectF br = trackBoundingRect();
QRectF ba = br.adjusted(-TILE_SIZE, -TILE_SIZE, TILE_SIZE, TILE_SIZE);
_scene->setSceneRect(ba);
if (br.width() < viewport()->size().width()
&& br.height() < viewport()->size().height())
centerOn(br.center());
else else
centerOn(pos * scale/_scale); return QDateTime();
_mapScale->setZoom(_zoom);
resetCachedContent();
}
void Track::setTrackLineWidth(qreal width)
{
for (int i = 0; i < _trackPaths.size(); i++) {
QPen pen(_trackPaths.at(i)->pen());
pen.setWidthF(width);
_trackPaths.at(i)->setPen(pen);
}
}
void Track::plot(QPainter *painter, const QRectF &target)
{
QRectF orig, adj;
qreal ratio, diff;
_scene->removeItem(_mapScale);
orig = _scene->itemsBoundingRect().adjusted(0, 0, 0,
_mapScale->boundingRect().height());
_scene->addItem(_mapScale);
if (target.width()/target.height() > orig.width()/orig.height()) {
ratio = target.width()/target.height();
diff = qAbs((orig.height() * ratio) - orig.width());
adj = orig.adjusted(-diff/2, 0, diff/2, 0);
} else {
ratio = target.height()/target.width();
diff = fabs((orig.width() * ratio) - orig.height());
adj = orig.adjusted(0, -diff/2, 0, diff/2);
}
_mapScale->setPos(adj.bottomRight()
+ QPoint(-_mapScale->boundingRect().width(),
-_mapScale->boundingRect().height()));
setTrackLineWidth(0);
_scene->render(painter, target, adj);
setTrackLineWidth(TRACK_WIDTH * _scale);
}
enum QPrinter::Orientation Track::orientation() const
{
return (sceneRect().width() > sceneRect().height())
? QPrinter::Landscape : QPrinter::Portrait;
}
void Track::clearPOI()
{
QHash<Entry, POIItem*>::const_iterator it;
for (it = _pois.constBegin(); it != _pois.constEnd(); it++) {
_scene->removeItem(it.value());
delete it.value();
}
_pois.clear();
}
void Track::clear()
{
if (_mapScale->scene() == _scene)
_scene->removeItem(_mapScale);
_pois.clear();
_tracks.clear();
_trackPaths.clear();
_markers.clear();
_scene->clear();
_colorShop.reset();
_maxLen = 0;
_scene->setSceneRect(0, 0, 0, 0);
}
void Track::movePositionMarker(qreal val)
{
for (int i = 0; i < _trackPaths.size(); i++) {
qreal f = _maxLen / _trackPaths.at(i)->path().length();
QPointF pos = _trackPaths.at(i)->path().pointAtPercent(qMin(val * f,
1.0));
_markers.at(i)->setPos(pos);
}
}
void Track::drawBackground(QPainter *painter, const QRectF &rect)
{
if (_tracks.isEmpty() || !_map) {
painter->fillRect(rect, Qt::white);
return;
}
painter->setWorldMatrixEnabled(false);
QRectF rr(rect.topLeft() * _scale, rect.size());
QPoint tile = mercator2tile(QPointF(rr.topLeft().x(), -rr.topLeft().y()),
_zoom);
QPointF tm = tile2mercator(tile, _zoom);
QPoint tl = mapFromScene(QPointF(tm.x() / _scale, -tm.y() / _scale));
QList<Tile> tiles;
for (int i = 0; i <= rr.size().width() / TILE_SIZE + 1; i++) {
for (int j = 0; j <= rr.size().height() / TILE_SIZE + 1; j++) {
tiles.append(Tile(QPoint(tile.x() + i, tile.y() + j), _zoom));
}
}
_map->loadTiles(tiles);
for (int i = 0; i < tiles.count(); i++) {
Tile &t = tiles[i];
QPoint tp(tl.x() + (t.xy().rx() - tile.rx()) * TILE_SIZE,
tl.y() + (t.xy().ry() - tile.ry()) * TILE_SIZE);
painter->drawPixmap(tp, t.pixmap());
}
}
void Track::resizeEvent(QResizeEvent *e)
{
if (_tracks.isEmpty())
return;
QRectF br = trackBoundingRect();
QRectF ba = br.adjusted(-TILE_SIZE, -TILE_SIZE, TILE_SIZE, TILE_SIZE);
if (ba.width() < e->size().width()) {
qreal diff = e->size().width() - ba.width();
ba.adjust(-diff/2, 0, diff/2, 0);
}
if (ba.height() < e->size().height()) {
qreal diff = e->size().height() - ba.height();
ba.adjust(0, -diff/2, 0, diff/2);
}
_scene->setSceneRect(ba);
centerOn(br.center());
resetCachedContent();
}
void Track::paintEvent(QPaintEvent *e)
{
QPointF scenePos = mapToScene(rect().bottomLeft() + QPoint(SCALE_OFFSET,
-(SCALE_OFFSET + _mapScale->boundingRect().height())));
if (_mapScale->pos() != scenePos)
_mapScale->setPos(scenePos);
QGraphicsView::paintEvent(e);
} }

View File

@ -1,75 +1,24 @@
#ifndef TRACK_H #ifndef TRACK_H
#define TRACK_H #define TRACK_H
#include <QGraphicsView>
#include <QVector> #include <QVector>
#include <QHash> #include <QDateTime>
#include <QList> #include "trackpoint.h"
#include <QPrinter>
#include "poi.h"
#include "gpx.h"
#include "map.h"
#include "units.h"
#include "colorshop.h"
class Track
class POIItem;
class MarkerItem;
class ScaleItem;
class Track : public QGraphicsView
{ {
Q_OBJECT
public: public:
Track(QWidget *parent = 0); Track(const QVector<TrackPoint> &data) : _data(data) {}
~Track();
void loadGPX(const GPX &gpx); void elevationGraph(QVector<QPointF> &graph) const;
void speedGraph(QVector<QPointF> &graph) const;
void loadPOI(const POI &poi); void track(QVector<QPointF> &track) const;
void clearPOI(); qreal distance() const;
void clear(); qreal time() const;
QDateTime date() const;
void setMap(Map *map);
void setUnits(enum Units units);
void plot(QPainter *painter, const QRectF &target);
enum QPrinter::Orientation orientation() const;
public slots:
void movePositionMarker(qreal val);
private slots:
void redraw();
private: private:
QRectF trackBoundingRect() const; const QVector<TrackPoint> &_data;
qreal trackScale() const;
qreal mapScale() const;
void rescale(qreal scale);
void showMarkers(bool show);
void setTrackLineWidth(qreal width);
void wheelEvent(QWheelEvent *event);
void drawBackground(QPainter *painter, const QRectF &rect);
void resizeEvent(QResizeEvent *e);
void paintEvent(QPaintEvent *e);
QGraphicsScene *_scene;
QList<QVector<QPointF> > _tracks;
QList<QGraphicsPathItem*> _trackPaths;
QList<MarkerItem*> _markers;
QHash<Entry, POIItem*> _pois;
Map *_map;
ScaleItem *_mapScale;
ColorShop _colorShop;
qreal _maxLen;
qreal _scale;
int _zoom;
}; };
#endif // TRACK_H #endif // TRACK_H

18
src/trackpoint.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef TRACKPOINT_H
#define TRACKPOINT_H
#include <QPointF>
#include <QDateTime>
struct TrackPoint
{
QPointF coordinates;
QDateTime timestamp;
qreal elevation;
qreal geoidheight;
qreal speed;
TrackPoint() {elevation = 0; geoidheight = 0; speed = -1;}
};
#endif // TRACKPOINT_H

398
src/trackview.cpp Normal file
View File

@ -0,0 +1,398 @@
#include <cmath>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QPainterPath>
#include <QWheelEvent>
#include "poiitem.h"
#include "markeritem.h"
#include "scaleitem.h"
#include "ll.h"
#include "trackview.h"
#define MARGIN 10.0
#define TRACK_WIDTH 3
#define SCALE_OFFSET 7
TrackView::TrackView(QWidget *parent)
: QGraphicsView(parent)
{
_scene = new QGraphicsScene(this);
setScene(_scene);
setCacheMode(QGraphicsView::CacheBackground);
setDragMode(QGraphicsView::ScrollHandDrag);
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
_mapScale = new ScaleItem();
_mapScale->setZValue(2.0);
_zoom = -1;
_scale = 1.0;
_map = 0;
_maxLen = 0;
}
TrackView::~TrackView()
{
delete _scene;
}
void TrackView::loadGPX(const GPX &gpx)
{
for (int i = 0; i < gpx.trackCount(); i++) {
QVector<QPointF> track;
QPainterPath path;
QGraphicsPathItem *pi;
MarkerItem *mi;
QColor color = _colorShop.color();
qreal prevScale = _scale;
gpx.track(i).track(track);
if (track.size() < 2)
continue;
_tracks.append(track);
path.moveTo(track.at(0).x(), -track.at(0).y());
for (int i = 1; i < track.size(); i++)
path.lineTo(track.at(i).x(), -track.at(i).y());
_maxLen = qMax(path.length(), _maxLen);
pi = new QGraphicsPathItem(path);
_trackPaths.append(pi);
_zoom = scale2zoom(trackScale());
_scale = mapScale();
QBrush brush(color, Qt::SolidPattern);
QPen pen(brush, TRACK_WIDTH * _scale);
pi->setPen(pen);
pi->setScale(1.0/_scale);
_scene->addItem(pi);
mi = new MarkerItem(pi);
_markers.append(mi);
mi->setPos(pi->path().pointAtPercent(0));
mi->setScale(_scale);
if (_trackPaths.size() > 1 && prevScale != _scale)
rescale(_scale);
QRectF br = trackBoundingRect();
QRectF ba = br.adjusted(-TILE_SIZE, -TILE_SIZE, TILE_SIZE, TILE_SIZE);
_scene->setSceneRect(ba);
centerOn(ba.center());
if (_mapScale->scene() != _scene)
_scene->addItem(_mapScale);
_mapScale->setLatitude(track.at(track.size() / 2).y());
_mapScale->setZoom(_zoom);
}
}
QRectF TrackView::trackBoundingRect() const
{
qreal bottom, top, left, right;
bottom = _trackPaths.at(0)->sceneBoundingRect().bottom();
top = _trackPaths.at(0)->sceneBoundingRect().top();
left = _trackPaths.at(0)->sceneBoundingRect().left();
right = _trackPaths.at(0)->sceneBoundingRect().right();
for (int i = 1; i < _trackPaths.size(); i++) {
bottom = qMax(bottom, _trackPaths.at(i)->sceneBoundingRect().bottom());
top = qMin(top, _trackPaths.at(i)->sceneBoundingRect().top());
right = qMax(right, _trackPaths.at(i)->sceneBoundingRect().right());
left = qMin(left, _trackPaths.at(i)->sceneBoundingRect().left());
}
return QRectF(QPointF(left, top), QPointF(right, bottom));
}
qreal TrackView::trackScale() const
{
qreal bottom, top, left, right;
bottom = _trackPaths.at(0)->path().boundingRect().bottom();
top = _trackPaths.at(0)->path().boundingRect().top();
left = _trackPaths.at(0)->path().boundingRect().left();
right = _trackPaths.at(0)->path().boundingRect().right();
for (int i = 1; i < _trackPaths.size(); i++) {
bottom = qMax(bottom, _trackPaths.at(i)->path().boundingRect().bottom());
top = qMin(top, _trackPaths.at(i)->path().boundingRect().top());
right = qMax(right, _trackPaths.at(i)->path().boundingRect().right());
left = qMin(left, _trackPaths.at(i)->path().boundingRect().left());
}
QRectF br(QPointF(left, top), QPointF(right, bottom));
QPointF sc(br.width() / (viewport()->width() - MARGIN/2),
br.height() / (viewport()->height() - MARGIN/2));
return qMax(sc.x(), sc.y());
}
qreal TrackView::mapScale() const
{
return ((360.0/(qreal)(1<<_zoom))/(qreal)TILE_SIZE);
}
void TrackView::rescale(qreal scale)
{
for (int i = 0; i < _trackPaths.size(); i++) {
_markers.at(i)->setScale(scale);
_trackPaths.at(i)->setScale(1.0/scale);
QPen pen(_trackPaths.at(i)->pen());
pen.setWidthF(TRACK_WIDTH * scale);
_trackPaths.at(i)->setPen(pen);
}
QHash<WayPoint, POIItem*>::const_iterator it, jt;
for (it = _pois.constBegin(); it != _pois.constEnd(); it++) {
it.value()->setPos(QPointF(it.value()->entry().coordinates().x()
* 1.0/scale, -it.value()->entry().coordinates().y() * 1.0/scale));
it.value()->show();
}
for (it = _pois.constBegin(); it != _pois.constEnd(); it++) {
for (jt = _pois.constBegin(); jt != _pois.constEnd(); jt++) {
if (it != jt && it.value()->isVisible() && jt.value()->isVisible()
&& it.value()->collidesWithItem(jt.value()))
jt.value()->hide();
}
}
_scale = scale;
}
void TrackView::loadPOI(const POI &poi)
{
QHash<WayPoint, POIItem*>::const_iterator it,jt;
if (!_tracks.size())
return;
for (int i = 0; i < _tracks.size(); i++) {
QVector<WayPoint> p = poi.points(_tracks.at(i));
for (int i = 0; i < p.size(); i++) {
if (_pois.contains(p.at(i)))
continue;
POIItem *pi = new POIItem(p.at(i));
pi->setPos(p.at(i).coordinates().x() * 1.0/_scale,
-p.at(i).coordinates().y() * 1.0/_scale);
pi->setZValue(1);
_scene->addItem(pi);
_pois.insert(p.at(i), pi);
}
}
for (it = _pois.constBegin(); it != _pois.constEnd(); it++) {
for (jt = _pois.constBegin(); jt != _pois.constEnd(); jt++) {
if (it != jt && it.value()->isVisible() && jt.value()->isVisible()
&& it.value()->collidesWithItem(jt.value()))
jt.value()->hide();
}
}
}
void TrackView::setMap(Map *map)
{
_map = map;
if (_map)
connect(_map, SIGNAL(loaded()), this, SLOT(redraw()));
resetCachedContent();
}
void TrackView::setUnits(enum Units units)
{
_mapScale->setUnits(units);
}
void TrackView::redraw()
{
resetCachedContent();
}
void TrackView::wheelEvent(QWheelEvent *event)
{
if (_tracks.isEmpty())
return;
QPointF pos = mapToScene(event->pos());
qreal scale = _scale;
_zoom = (event->delta() > 0) ?
qMin(_zoom + 1, ZOOM_MAX) : qMax(_zoom - 1, ZOOM_MIN);
rescale(mapScale());
QRectF br = trackBoundingRect();
QRectF ba = br.adjusted(-TILE_SIZE, -TILE_SIZE, TILE_SIZE, TILE_SIZE);
_scene->setSceneRect(ba);
if (br.width() < viewport()->size().width()
&& br.height() < viewport()->size().height())
centerOn(br.center());
else
centerOn(pos * scale/_scale);
_mapScale->setZoom(_zoom);
resetCachedContent();
}
void TrackView::setTrackLineWidth(qreal width)
{
for (int i = 0; i < _trackPaths.size(); i++) {
QPen pen(_trackPaths.at(i)->pen());
pen.setWidthF(width);
_trackPaths.at(i)->setPen(pen);
}
}
void TrackView::plot(QPainter *painter, const QRectF &target)
{
QRectF orig, adj;
qreal ratio, diff;
_scene->removeItem(_mapScale);
orig = _scene->itemsBoundingRect().adjusted(0, 0, 0,
_mapScale->boundingRect().height());
_scene->addItem(_mapScale);
if (target.width()/target.height() > orig.width()/orig.height()) {
ratio = target.width()/target.height();
diff = qAbs((orig.height() * ratio) - orig.width());
adj = orig.adjusted(-diff/2, 0, diff/2, 0);
} else {
ratio = target.height()/target.width();
diff = fabs((orig.width() * ratio) - orig.height());
adj = orig.adjusted(0, -diff/2, 0, diff/2);
}
_mapScale->setPos(adj.bottomRight()
+ QPoint(-_mapScale->boundingRect().width(),
-_mapScale->boundingRect().height()));
setTrackLineWidth(0);
_scene->render(painter, target, adj);
setTrackLineWidth(TRACK_WIDTH * _scale);
}
enum QPrinter::Orientation TrackView::orientation() const
{
return (sceneRect().width() > sceneRect().height())
? QPrinter::Landscape : QPrinter::Portrait;
}
void TrackView::clearPOI()
{
QHash<WayPoint, POIItem*>::const_iterator it;
for (it = _pois.constBegin(); it != _pois.constEnd(); it++) {
_scene->removeItem(it.value());
delete it.value();
}
_pois.clear();
}
void TrackView::clear()
{
if (_mapScale->scene() == _scene)
_scene->removeItem(_mapScale);
_pois.clear();
_tracks.clear();
_trackPaths.clear();
_markers.clear();
_scene->clear();
_colorShop.reset();
_maxLen = 0;
_scene->setSceneRect(0, 0, 0, 0);
}
void TrackView::movePositionMarker(qreal val)
{
for (int i = 0; i < _trackPaths.size(); i++) {
qreal f = _maxLen / _trackPaths.at(i)->path().length();
QPointF pos = _trackPaths.at(i)->path().pointAtPercent(qMin(val * f,
1.0));
_markers.at(i)->setPos(pos);
}
}
void TrackView::drawBackground(QPainter *painter, const QRectF &rect)
{
if (_tracks.isEmpty() || !_map) {
painter->fillRect(rect, Qt::white);
return;
}
painter->setWorldMatrixEnabled(false);
QRectF rr(rect.topLeft() * _scale, rect.size());
QPoint tile = mercator2tile(QPointF(rr.topLeft().x(), -rr.topLeft().y()),
_zoom);
QPointF tm = tile2mercator(tile, _zoom);
QPoint tl = mapFromScene(QPointF(tm.x() / _scale, -tm.y() / _scale));
QList<Tile> tiles;
for (int i = 0; i <= rr.size().width() / TILE_SIZE + 1; i++) {
for (int j = 0; j <= rr.size().height() / TILE_SIZE + 1; j++) {
tiles.append(Tile(QPoint(tile.x() + i, tile.y() + j), _zoom));
}
}
_map->loadTiles(tiles);
for (int i = 0; i < tiles.count(); i++) {
Tile &t = tiles[i];
QPoint tp(tl.x() + (t.xy().rx() - tile.rx()) * TILE_SIZE,
tl.y() + (t.xy().ry() - tile.ry()) * TILE_SIZE);
painter->drawPixmap(tp, t.pixmap());
}
}
void TrackView::resizeEvent(QResizeEvent *e)
{
if (_tracks.isEmpty())
return;
QRectF br = trackBoundingRect();
QRectF ba = br.adjusted(-TILE_SIZE, -TILE_SIZE, TILE_SIZE, TILE_SIZE);
if (ba.width() < e->size().width()) {
qreal diff = e->size().width() - ba.width();
ba.adjust(-diff/2, 0, diff/2, 0);
}
if (ba.height() < e->size().height()) {
qreal diff = e->size().height() - ba.height();
ba.adjust(0, -diff/2, 0, diff/2);
}
_scene->setSceneRect(ba);
centerOn(br.center());
resetCachedContent();
}
void TrackView::paintEvent(QPaintEvent *e)
{
QPointF scenePos = mapToScene(rect().bottomLeft() + QPoint(SCALE_OFFSET,
-(SCALE_OFFSET + _mapScale->boundingRect().height())));
if (_mapScale->pos() != scenePos)
_mapScale->setPos(scenePos);
QGraphicsView::paintEvent(e);
}

75
src/trackview.h Normal file
View File

@ -0,0 +1,75 @@
#ifndef TRACKVIEW_H
#define TRACKVIEW_H
#include <QGraphicsView>
#include <QVector>
#include <QHash>
#include <QList>
#include <QPrinter>
#include "poi.h"
#include "gpx.h"
#include "map.h"
#include "units.h"
#include "colorshop.h"
class POIItem;
class MarkerItem;
class ScaleItem;
class TrackView : public QGraphicsView
{
Q_OBJECT
public:
TrackView(QWidget *parent = 0);
~TrackView();
void loadGPX(const GPX &gpx);
void loadPOI(const POI &poi);
void clearPOI();
void clear();
void setMap(Map *map);
void setUnits(enum Units units);
void plot(QPainter *painter, const QRectF &target);
enum QPrinter::Orientation orientation() const;
public slots:
void movePositionMarker(qreal val);
private slots:
void redraw();
private:
QRectF trackBoundingRect() const;
qreal trackScale() const;
qreal mapScale() const;
void rescale(qreal scale);
void showMarkers(bool show);
void setTrackLineWidth(qreal width);
void wheelEvent(QWheelEvent *event);
void drawBackground(QPainter *painter, const QRectF &rect);
void resizeEvent(QResizeEvent *e);
void paintEvent(QPaintEvent *e);
QGraphicsScene *_scene;
QList<QVector<QPointF> > _tracks;
QList<QGraphicsPathItem*> _trackPaths;
QList<MarkerItem*> _markers;
QHash<WayPoint, POIItem*> _pois;
Map *_map;
ScaleItem *_mapScale;
ColorShop _colorShop;
qreal _maxLen;
qreal _scale;
int _zoom;
};
#endif // TRACKVIEW_H

36
src/waypoint.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef WAYPOINT_H
#define WAYPOINT_H
#include <QPointF>
#include <QString>
#include <QHash>
class WayPoint
{
public:
WayPoint() {}
WayPoint(const QPointF &coordinates, const QString &description)
: _coordinates(coordinates), _description(description) {}
const QPointF &coordinates() const {return _coordinates;}
const QString &description() const {return _description;}
void setCoordinates(const QPointF &coordinates)
{_coordinates = coordinates;}
void setDescription(const QString &description)
{_description = description;}
bool operator==(const WayPoint &other) const
{return this->_description == other._description
&& this->_coordinates == other._coordinates;}
private:
QPointF _coordinates;
QString _description;
};
inline uint qHash(const WayPoint &key)
{
return ::qHash(key.description());
}
#endif // WAYPOINT_H