1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-27 21:24:47 +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/gui.h \
src/gpx.h \
src/graph.h \
src/track.h \
src/parser.h \
src/poi.h \
src/rtree.h \
@ -30,12 +28,15 @@ HEADERS += src/config.h \
src/downloader.h \
src/units.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 \
src/gui.cpp \
src/gpx.cpp \
src/graph.cpp \
src/track.cpp \
src/parser.cpp \
src/poi.cpp \
src/ll.cpp \
@ -53,7 +54,10 @@ SOURCES += src/main.cpp \
src/maplist.cpp \
src/downloader.cpp \
src/scaleitem.cpp \
src/nicenum.cpp
src/nicenum.cpp \
src/trackview.cpp \
src/track.cpp \
src/graphview.cpp
RESOURCES += gpxsee.qrc
TRANSLATIONS = lang/gpxsee_cs.ts
macx:ICON = icons/gpxsee.icns

View File

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

View File

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

View File

@ -4,88 +4,12 @@
#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)
{
QFile file(fileName);
bool ret;
_data.clear();
_tracks.clear();
_error.clear();
_errorLine = 0;
@ -94,7 +18,7 @@ bool GPX::loadFile(const QString &fileName)
return false;
}
if (!(ret = _parser.loadFile(&file, &_data))) {
if (!(ret = _parser.loadFile(&file))) {
_error = _parser.errorString();
_errorLine = _parser.errorLine();
}
@ -102,90 +26,3 @@ bool GPX::loadFile(const QString &fileName)
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 <QPointF>
#include <QString>
#include "waypoint.h"
#include "track.h"
#include "parser.h"
class GPX
{
public:
GPX() : _parser(_tracks, _waypoints) {}
bool loadFile(const QString &fileName);
const QString &errorString() const {return _error;}
int errorLine() const {return _errorLine;}
int count() const {return _data.count();}
void elevationGraph(int i, QVector<QPointF> &graph) const;
void speedGraph(int i, QVector<QPointF> &graph) const;
void track(int i, QVector<QPointF> &track) const;
qreal distance(int i) const;
qreal time(int i) const;
QDateTime date(int i) const;
int trackCount() const {return _tracks.count();}
Track track(int i) const {return Track(_tracks.at(i));}
const QList<WayPoint> &waypoints() const {return _waypoints;}
private:
Parser _parser;
QList<QVector<TrackPoint> > _data;
QString _error;
int _errorLine;
QList<QVector<TrackPoint> > _tracks;
QList<WayPoint> _waypoints;
};
#endif // GPX_H

View File

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

View File

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

View File

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

View File

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

View File

@ -18,12 +18,24 @@ void Parser::handleTrekPointData(QStringRef element, const QString &value)
_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)
{
_track->last().coordinates.setY(attr.value("lat").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()
{
@ -50,13 +62,10 @@ void Parser::trackPointData()
void Parser::trackPoints()
{
QXmlStreamAttributes attr;
while (_reader.readNextStartElement()) {
if (_reader.name() == "trkpt") {
attr = _reader.attributes();
_track->append(TrackPoint());
handleTrekPointAttributes(attr);
handleTrekPointAttributes(_reader.attributes());
trackPointData();
} else
_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()
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "trk") {
_data->append(QVector<TrackPoint>());
_track = &_data->back();
_tracks.append(QVector<TrackPoint>());
_track = &_tracks.back();
track();
} else if (_reader.name() == "wpt") {
_waypoints.append(WayPoint());
handleWayPointAttributes(_reader.attributes());
wayPointData();
} else
_reader.skipCurrentElement();
}
@ -97,11 +120,10 @@ bool Parser::parse()
return !_reader.error();
}
bool Parser::loadFile(QIODevice *device, QList<QVector<TrackPoint> > *data)
bool Parser::loadFile(QIODevice *device)
{
_reader.clear();
_reader.setDevice(device);
_data = data;
return parse();
}

View File

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

View File

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

View File

@ -4,24 +4,9 @@
#include <QVector>
#include <QPointF>
#include <QString>
#include "waypoint.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
{
@ -30,15 +15,18 @@ public:
QString errorString() const {return _error;}
int errorLine() const {return _errorLine;}
QVector<Entry> points(const QVector<QPointF> &path) const;
QVector<WayPoint> points(const QVector<QPointF> &path) const;
void clear();
private:
typedef RTree<size_t, qreal, 2> POITree;
bool loadCSVFile(const QString &fileName);
bool loadGPXFile(const QString &fileName);
POITree _tree;
QVector<Entry> _data;
QVector<WayPoint> _data;
QString _error;
int _errorLine;
};

View File

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

View File

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

View File

@ -2,38 +2,39 @@
#include "speedgraph.h"
SpeedGraph::SpeedGraph(QWidget *parent) : Graph(parent)
SpeedGraph::SpeedGraph(QWidget *parent) : GraphView(parent)
{
_max = 0;
Graph::setXLabel(tr("Distance"));
Graph::setYLabel(tr("Speed"));
Graph::setXUnits(tr("km"));
Graph::setYUnits(tr("km/h"));
Graph::setXScale(M2KM);
Graph::setYScale(MS2KMH);
Graph::setPrecision(1);
GraphView::setXLabel(tr("Distance"));
GraphView::setYLabel(tr("Speed"));
GraphView::setXUnits(tr("km"));
GraphView::setYUnits(tr("km/h"));
GraphView::setXScale(M2KM);
GraphView::setYScale(MS2KMH);
GraphView::setPrecision(1);
}
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);
Graph::addInfo(tr("Maximum"), QString::number(_max * _yScale, 'f', 1)
GraphView::addInfo(tr("Maximum"), QString::number(_max * _yScale, 'f', 1)
+ THIN_SPACE + _yUnits);
}
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;
qreal max = 0;
gpx.speedGraph(i, data);
gpx.track(i).speedGraph(data);
if (data.isEmpty())
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++)
max = qMax(max, data.at(i).y());
@ -62,21 +63,21 @@ void SpeedGraph::clear()
_max = 0;
_avg.clear();
Graph::clear();
GraphView::clear();
}
void SpeedGraph::setUnits(enum Units units)
{
if (units == Metric) {
Graph::setXUnits(tr("km"));
Graph::setYUnits(tr("km/h"));
Graph::setXScale(M2KM);
Graph::setYScale(MS2KMH);
GraphView::setXUnits(tr("km"));
GraphView::setYUnits(tr("km/h"));
GraphView::setXScale(M2KM);
GraphView::setYScale(MS2KMH);
} else {
Graph::setXUnits(tr("mi"));
Graph::setYUnits(tr("mi/h"));
Graph::setXScale(M2MI);
Graph::setYScale(MS2MIH);
GraphView::setXUnits(tr("mi"));
GraphView::setYUnits(tr("mi/h"));
GraphView::setXScale(M2MI);
GraphView::setYScale(MS2MIH);
}
clearInfo();

View File

@ -2,11 +2,11 @@
#define SPEEDGRAPH_H
#include <QList>
#include "graph.h"
#include "graphview.h"
#include "gpx.h"
#include "units.h"
class SpeedGraph : public Graph
class SpeedGraph : public GraphView
{
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 "track.h"
#define WINDOW_EF 3
#define WINDOW_SE 11
#define WINDOW_SF 11
#define MARGIN 10.0
#define TRACK_WIDTH 3
#define SCALE_OFFSET 7
Track::Track(QWidget *parent)
: QGraphicsView(parent)
static bool lt(const QPointF &p1, const QPointF &p2)
{
_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;
return p1.y() < p2.y();
}
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++) {
QVector<QPointF> track;
QPainterPath path;
QGraphicsPathItem *pi;
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);
}
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();
}
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++) {
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());
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);
}
return QRectF(QPointF(left, top), QPointF(right, bottom));
}
qreal Track::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());
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++;
}
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());
return ret;
}
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++) {
_markers.at(i)->setScale(scale);
_trackPaths.at(i)->setScale(1.0/scale);
qreal dist = 0;
QVector<QPointF> raw;
QPen pen(_trackPaths.at(i)->pen());
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())
if (!_data.size())
return;
for (int i = 0; i < _tracks.size(); i++) {
QVector<Entry> p = poi.points(_tracks.at(i));
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));
}
for (int i = 0; i < p.size(); i++) {
if (_pois.contains(p.at(i)))
graph = filter(raw, WINDOW_EF);
}
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;
v = ds / dt;
} else
v = _data.at(i).speed;
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);
}
raw.append(QPointF(dist, v));
}
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();
}
}
graph = filter(eliminate(raw, WINDOW_SE), WINDOW_SF);
}
void Track::setMap(Map *map)
void Track::track(QVector<QPointF> &track) const
{
_map = map;
if (_map)
connect(_map, SIGNAL(loaded()), this, SLOT(redraw()));
resetCachedContent();
for (int i = 0; i < _data.size(); i++)
track.append(ll2mercator(_data.at(i).coordinates));
}
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())
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());
if (_data.size())
return _data.at(0).timestamp;
else
centerOn(pos * scale/_scale);
_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);
return QDateTime();
}

View File

@ -1,75 +1,24 @@
#ifndef TRACK_H
#define TRACK_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"
#include <QDateTime>
#include "trackpoint.h"
class POIItem;
class MarkerItem;
class ScaleItem;
class Track : public QGraphicsView
class Track
{
Q_OBJECT
public:
Track(QWidget *parent = 0);
~Track();
Track(const QVector<TrackPoint> &data) : _data(data) {}
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();
void elevationGraph(QVector<QPointF> &graph) const;
void speedGraph(QVector<QPointF> &graph) const;
void track(QVector<QPointF> &track) const;
qreal distance() const;
qreal time() const;
QDateTime date() const;
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<Entry, POIItem*> _pois;
Map *_map;
ScaleItem *_mapScale;
ColorShop _colorShop;
qreal _maxLen;
qreal _scale;
int _zoom;
const QVector<TrackPoint> &_data;
};
#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