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:
parent
635293ec1b
commit
e06a1bc148
16
gpxsee.pro
16
gpxsee.pro
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
167
src/gpx.cpp
167
src/gpx.cpp
@ -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();
|
|
||||||
}
|
|
||||||
|
17
src/gpx.h
17
src/gpx.h
@ -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
|
||||||
|
@ -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();
|
||||||
}
|
}
|
@ -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
|
12
src/gui.cpp
12
src/gui.cpp
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
27
src/parser.h
27
src/parser.h
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
59
src/poi.cpp
59
src/poi.cpp
@ -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];
|
||||||
|
|
||||||
|
24
src/poi.h
24
src/poi.h
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
457
src/track.cpp
457
src/track.cpp
@ -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);
|
|
||||||
}
|
}
|
||||||
|
73
src/track.h
73
src/track.h
@ -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
18
src/trackpoint.h
Normal 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
398
src/trackview.cpp
Normal 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
75
src/trackview.h
Normal 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
36
src/waypoint.h
Normal 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
|
Loading…
x
Reference in New Issue
Block a user