mirror of
https://github.com/tumic0/GPXSee.git
synced 2024-11-24 03:35:53 +01:00
Initial commit
This commit is contained in:
commit
928e259d0c
36
gpxsee.pro
Normal file
36
gpxsee.pro
Normal file
@ -0,0 +1,36 @@
|
||||
TARGET = gpxsee
|
||||
QT += core \
|
||||
gui
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += printsupport
|
||||
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 \
|
||||
src/ll.h \
|
||||
src/axisitem.h \
|
||||
src/poiitem.h \
|
||||
src/colorshop.h \
|
||||
src/keys.h \
|
||||
src/slideritem.h \
|
||||
src/markeritem.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 \
|
||||
src/axisitem.cpp \
|
||||
src/poiitem.cpp \
|
||||
src/colorshop.cpp \
|
||||
src/slideritem.cpp \
|
||||
src/markeritem.cpp
|
||||
RESOURCES += gpxsee.qrc
|
||||
TRANSLATIONS = lang/gpxsee_cs.ts
|
14
gpxsee.qrc
Normal file
14
gpxsee.qrc
Normal file
@ -0,0 +1,14 @@
|
||||
<RCC>
|
||||
<qresource>
|
||||
<file>icons/dialog-close.png</file>
|
||||
<file>icons/document-open.png</file>
|
||||
<file>icons/document-save-as.png</file>
|
||||
<file>icons/document-save.png</file>
|
||||
<file>icons/flag.png</file>
|
||||
<file>icons/gpxsee.png</file>
|
||||
</qresource>
|
||||
|
||||
<qresource>
|
||||
<file>lang/gpxsee_cs.qm</file>
|
||||
</qresource>
|
||||
</RCC>
|
BIN
icons/dialog-close.png
Normal file
BIN
icons/dialog-close.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
icons/document-open.png
Normal file
BIN
icons/document-open.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 796 B |
BIN
icons/document-save-as.png
Normal file
BIN
icons/document-save-as.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
icons/document-save.png
Normal file
BIN
icons/document-save.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 798 B |
BIN
icons/flag.png
Normal file
BIN
icons/flag.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
BIN
icons/gpxsee.dia
Normal file
BIN
icons/gpxsee.dia
Normal file
Binary file not shown.
BIN
icons/gpxsee.png
Normal file
BIN
icons/gpxsee.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
165
lang/gpxsee_cs.ts
Normal file
165
lang/gpxsee_cs.ts
Normal file
@ -0,0 +1,165 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="cs_CZ">
|
||||
<context>
|
||||
<name>GUI</name>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="56"/>
|
||||
<source>Exit</source>
|
||||
<translation>Konec</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="62"/>
|
||||
<source>About Qt</source>
|
||||
<translation>O Qt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="164"/>
|
||||
<source>GPXSee is distributed under the terms of the GNU General Public License version 3. For more info about GPXSee visit the project homepage at </source>
|
||||
<translation>Program GPXSee je distribuován pod podmínkami licence GNU General Public License verze 3. Pro více informací navštivte stránky programu na adrese </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="172"/>
|
||||
<source>Open file</source>
|
||||
<translation>Otevřít soubor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="74"/>
|
||||
<source>Save as</source>
|
||||
<translation>Uložit jako</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="212"/>
|
||||
<source>Open POI file</source>
|
||||
<translation>Otevřít POI soubor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="67"/>
|
||||
<source>Open</source>
|
||||
<translation>Otevřít</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="70"/>
|
||||
<source>Save</source>
|
||||
<translation>Uložit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="78"/>
|
||||
<source>Close</source>
|
||||
<translation>Zavřít</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="84"/>
|
||||
<source>Load file</source>
|
||||
<translation>Nahrát soubor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="87"/>
|
||||
<source>Show</source>
|
||||
<translation>Zobrazit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="94"/>
|
||||
<location filename="../src/gui.cpp" line="112"/>
|
||||
<source>File</source>
|
||||
<translation>Soubor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="101"/>
|
||||
<location filename="../src/gui.cpp" line="117"/>
|
||||
<source>POI</source>
|
||||
<translation>POI</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="105"/>
|
||||
<source>Help</source>
|
||||
<translation>Nápověda</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="129"/>
|
||||
<location filename="../src/gui.cpp" line="134"/>
|
||||
<source>Distance [km]</source>
|
||||
<translation>Vzdálenost [km]</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="130"/>
|
||||
<source>Elevation [m.a.s.l.]</source>
|
||||
<translation>Výška [m.n.m.]</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="135"/>
|
||||
<source>Speed [km/h]</source>
|
||||
<translation>Rychlost [km/h]</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="140"/>
|
||||
<source>Elevation</source>
|
||||
<translation>Výška</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="141"/>
|
||||
<source>Speed</source>
|
||||
<translation>Rychlost</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="60"/>
|
||||
<location filename="../src/gui.cpp" line="161"/>
|
||||
<source>About GPXSee</source>
|
||||
<translation>O aplikaci GPXSee</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="163"/>
|
||||
<source>GPX viewer and analyzer</source>
|
||||
<translation>Prohlížeč a analyzátor GPX</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="202"/>
|
||||
<location filename="../src/gui.cpp" line="216"/>
|
||||
<source>Error</source>
|
||||
<translation>Chyba</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="203"/>
|
||||
<source>Error loading GPX file:
|
||||
%1</source>
|
||||
<translation>Soubor GPX nelze otevřít:
|
||||
%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui.cpp" line="217"/>
|
||||
<source>Error loading POI file:
|
||||
%1</source>
|
||||
<translation>Soubor POI nelze otevřít:
|
||||
%1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QObject</name>
|
||||
<message>
|
||||
<location filename="../src/parser.cpp" line="73"/>
|
||||
<source>Not a GPX file.</source>
|
||||
<translation>Neplatný GPX soubor.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/parser.cpp" line="82"/>
|
||||
<source>%1
|
||||
Line %2</source>
|
||||
<translation>%1
|
||||
Rádka %2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/poi.cpp" line="31"/>
|
||||
<source>Invalid latitude
|
||||
Line %1</source>
|
||||
<translation>Neplatná zeměpisná šířka
|
||||
Rádka %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/poi.cpp" line="36"/>
|
||||
<source>Invalid longitude
|
||||
Line %1</source>
|
||||
<translation>Neplatná zeměpisná délka
|
||||
Rádka %1</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
204
src/axisitem.cpp
Normal file
204
src/axisitem.cpp
Normal file
@ -0,0 +1,204 @@
|
||||
#include <cmath>
|
||||
#include <QPainter>
|
||||
#include "axisitem.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#define TICK 6
|
||||
#define PADDING 6
|
||||
#define XTICKS 15
|
||||
#define YTICKS 10
|
||||
#define FONT_FAMILY "Arial"
|
||||
#define FONT_SIZE 12
|
||||
|
||||
|
||||
struct Label {
|
||||
double min;
|
||||
double max;
|
||||
double d;
|
||||
};
|
||||
|
||||
static double niceNum(double x, int round)
|
||||
{
|
||||
int expv;
|
||||
double f;
|
||||
double nf;
|
||||
|
||||
expv = floor(log10(x));
|
||||
f = x / pow(10.0, expv);
|
||||
|
||||
if (round) {
|
||||
if (f < 1.5)
|
||||
nf = 1.0;
|
||||
else if (f < 3.0)
|
||||
nf = 2.0;
|
||||
else if (f < 7.0)
|
||||
nf = 5.0;
|
||||
else
|
||||
nf = 10.0;
|
||||
} else {
|
||||
if (f <= 1.0)
|
||||
nf = 1.;
|
||||
else if (f <= 2.0)
|
||||
nf = 2.0;
|
||||
else if (f <= 5.0)
|
||||
nf = 5.0;
|
||||
else
|
||||
nf = 10.0;
|
||||
}
|
||||
|
||||
return nf * pow(10.0, expv);
|
||||
}
|
||||
|
||||
static struct Label label(double min, double max, int ticks)
|
||||
{
|
||||
double range;
|
||||
struct Label l;
|
||||
|
||||
range = niceNum(max - min, 0);
|
||||
l.d = niceNum(range / ticks, 1);
|
||||
l.min = ceil(min / l.d) * l.d;
|
||||
l.max = floor(max / l.d) * l.d;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
AxisItem::AxisItem(Type type)
|
||||
{
|
||||
_type = type;
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
void AxisItem::setRange(const QPointF &range)
|
||||
{
|
||||
_range = range;
|
||||
updateBoundingRect();
|
||||
prepareGeometryChange();
|
||||
}
|
||||
|
||||
void AxisItem::setSize(qreal size)
|
||||
{
|
||||
_size = size;
|
||||
updateBoundingRect();
|
||||
prepareGeometryChange();
|
||||
}
|
||||
|
||||
void AxisItem::setLabel(const QString& label)
|
||||
{
|
||||
_label = label;
|
||||
updateBoundingRect();
|
||||
prepareGeometryChange();
|
||||
}
|
||||
|
||||
void AxisItem::updateBoundingRect()
|
||||
{
|
||||
QFont font;
|
||||
font.setPixelSize(FONT_SIZE);
|
||||
font.setFamily(FONT_FAMILY);
|
||||
QFontMetrics fm(font);
|
||||
QRect ss, es, ls;
|
||||
struct Label l;
|
||||
|
||||
|
||||
l = label(_range.x(), _range.y(), (_type == X) ? XTICKS : YTICKS);
|
||||
es = fm.tightBoundingRect(QString::number(l.max));
|
||||
ss = fm.tightBoundingRect(QString::number(l.min));
|
||||
ls = fm.tightBoundingRect(_label);
|
||||
|
||||
if (_type == X) {
|
||||
_boundingRect = QRectF(-ss.width()/2, -TICK/2,
|
||||
_size + es.width()/2 + ss.width()/2,
|
||||
ls.height() + es.height() - fm.descent() + TICK + 2*PADDING);
|
||||
} else {
|
||||
_boundingRect = QRectF(-(ls.height() + es.width() + 2*PADDING
|
||||
- fm.descent() + TICK/2), -(_size + es.height()/2
|
||||
+ fm.descent()), ls.height() -fm.descent() + es.width() + 2*PADDING
|
||||
+ TICK, _size + es.height()/2 + fm.descent() + ss.height()/2);
|
||||
}
|
||||
}
|
||||
|
||||
void AxisItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
||||
QWidget *widget)
|
||||
{
|
||||
Q_UNUSED(option);
|
||||
Q_UNUSED(widget);
|
||||
QFont font;
|
||||
font.setPixelSize(FONT_SIZE);
|
||||
font.setFamily(FONT_FAMILY);
|
||||
painter->setFont(font);
|
||||
QFontMetrics fm(font);
|
||||
QRect ts, ls;
|
||||
struct Label l;
|
||||
qreal range = _range.y() - _range.x();
|
||||
qreal val;
|
||||
|
||||
|
||||
ls = fm.tightBoundingRect(_label);
|
||||
|
||||
if (_type == X) {
|
||||
painter->drawLine(0, 0, _size, 0);
|
||||
|
||||
l = label(_range.x(), _range.y(), XTICKS);
|
||||
for (int i = 0; i < ((l.max - l.min) / l.d) + 1; i++) {
|
||||
val = l.min + i * l.d;
|
||||
QString str = QString::number(val);
|
||||
|
||||
painter->drawLine((_size/range) * (val - _range.x()), TICK/2,
|
||||
(_size/range) * (val - _range.x()), -TICK/2);
|
||||
ts = fm.tightBoundingRect(str);
|
||||
painter->drawText(((_size/range) * (val - _range.x()))
|
||||
- (ts.width()/2), ts.height() + TICK/2 + PADDING, str);
|
||||
}
|
||||
|
||||
painter->drawText(_size/2 - ls.width()/2, ls.height() + ts.height()
|
||||
- 2*fm.descent() + TICK/2 + 2*PADDING, _label);
|
||||
} else {
|
||||
painter->drawLine(0, 0, 0, -_size);
|
||||
|
||||
l = label(_range.x(), _range.y(), YTICKS);
|
||||
for (int i = 0; i < ((l.max - l.min) / l.d) + 1; i++) {
|
||||
val = l.min + i * l.d;
|
||||
QString str = QString::number(val);
|
||||
|
||||
painter->drawLine(TICK/2, -((_size/range) * (val - _range.x())),
|
||||
-TICK/2, -((_size/range) * (val - _range.x())));
|
||||
ts = fm.tightBoundingRect(str);
|
||||
painter->drawText(-(ts.width() + PADDING + TICK/2), -((_size/range)
|
||||
* (val - _range.x())) + (ts.height()/2), str);
|
||||
}
|
||||
|
||||
painter->rotate(-90);
|
||||
painter->drawText(_size/2 - ls.width()/2, -(ts.width()
|
||||
+ 2*PADDING + TICK/2), _label);
|
||||
painter->rotate(90);
|
||||
}
|
||||
|
||||
/*
|
||||
painter->setPen(Qt::red);
|
||||
painter->drawRect(boundingRect());
|
||||
*/
|
||||
}
|
||||
|
||||
QSizeF AxisItem::margin()
|
||||
{
|
||||
QFont font;
|
||||
font.setPixelSize(FONT_SIZE);
|
||||
QFontMetrics fm(font);
|
||||
QRect ss, es, ls;
|
||||
struct Label l;
|
||||
|
||||
|
||||
l = label(_range.x(), _range.y(), (_type == X) ? XTICKS : YTICKS);
|
||||
es = fm.tightBoundingRect(QString::number(l.max));
|
||||
ss = fm.tightBoundingRect(QString::number(l.min));
|
||||
ls = fm.tightBoundingRect(_label);
|
||||
|
||||
if (_type == X) {
|
||||
return QSizeF(es.width()/2,
|
||||
ls.height() + es.height() - fm.descent() + TICK/2 + 2*PADDING);
|
||||
} else {
|
||||
return QSizeF(ls.height() -fm.descent() + es.width() + 2*PADDING
|
||||
+ TICK/2, es.height()/2 + fm.descent());
|
||||
}
|
||||
}
|
33
src/axisitem.h
Normal file
33
src/axisitem.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef AXISITEM_H
|
||||
#define AXISITEM_H
|
||||
|
||||
#include <QGraphicsItem>
|
||||
|
||||
class AxisItem : public QGraphicsItem
|
||||
{
|
||||
public:
|
||||
enum Type {X, Y};
|
||||
|
||||
AxisItem(Type type);
|
||||
|
||||
QRectF boundingRect() const {return _boundingRect;}
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
||||
QWidget *widget);
|
||||
|
||||
void setRange(const QPointF &range);
|
||||
void setSize(qreal size);
|
||||
void setLabel(const QString& label);
|
||||
|
||||
QSizeF margin();
|
||||
|
||||
private:
|
||||
void updateBoundingRect();
|
||||
|
||||
Type _type;
|
||||
QPointF _range;
|
||||
qreal _size;
|
||||
QString _label;
|
||||
QRectF _boundingRect;
|
||||
};
|
||||
|
||||
#endif // AXISITEM_H
|
62
src/colorshop.cpp
Normal file
62
src/colorshop.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
#include "colorshop.h"
|
||||
|
||||
#define HUE_INIT 0.1f
|
||||
#define HUE_INCREMENT 0.62f
|
||||
#define SATURATION 0.99f
|
||||
#define VALUE 0.99f
|
||||
|
||||
|
||||
static unsigned hsv2rgb(float h, float s, float v)
|
||||
{
|
||||
unsigned hi;
|
||||
float r = 0, g = 0, b = 0, p, q, t, f;
|
||||
|
||||
hi = (unsigned)(h * 6.0f);
|
||||
f = h * 6.0f - hi;
|
||||
p = v * (1.0f - s);
|
||||
q = v * (1.0f - f * s);
|
||||
t = v * (1.0f - (1.0f - f) * s);
|
||||
|
||||
switch (hi) {
|
||||
case 0:
|
||||
r = v; g = t; b = p;
|
||||
break;
|
||||
case 1:
|
||||
r = q; g = v; b = p;
|
||||
break;
|
||||
case 2:
|
||||
r = p; g = v; b = t;
|
||||
break;
|
||||
case 3:
|
||||
r = p; g = q; b = v;
|
||||
break;
|
||||
case 4:
|
||||
r = t; g = p; b = v;
|
||||
break;
|
||||
case 5:
|
||||
r = v; g = p; b = q;
|
||||
break;
|
||||
}
|
||||
|
||||
return ((unsigned)(r * 256) << 16)
|
||||
+ ((unsigned)(g * 256) << 8)
|
||||
+ (unsigned)(b * 256);
|
||||
}
|
||||
|
||||
ColorShop::ColorShop()
|
||||
{
|
||||
_hueState = HUE_INIT;
|
||||
}
|
||||
|
||||
QColor ColorShop::color()
|
||||
{
|
||||
_hueState += HUE_INCREMENT;
|
||||
_hueState -= (int) _hueState;
|
||||
|
||||
return QColor(hsv2rgb(_hueState, SATURATION, VALUE));
|
||||
}
|
||||
|
||||
void ColorShop::reset()
|
||||
{
|
||||
_hueState = HUE_INIT;
|
||||
}
|
17
src/colorshop.h
Normal file
17
src/colorshop.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef COLORSHOP_H
|
||||
#define COLORSHOP_H
|
||||
|
||||
#include <QColor>
|
||||
|
||||
class ColorShop
|
||||
{
|
||||
public:
|
||||
ColorShop();
|
||||
QColor color();
|
||||
void reset();
|
||||
|
||||
private:
|
||||
float _hueState;
|
||||
};
|
||||
|
||||
#endif // COLORSHOP_H
|
8
src/config.h
Normal file
8
src/config.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#define APP_NAME "GPXSee"
|
||||
#define APP_HOMEPAGE "http://tumic.wz.cz/gpxsee"
|
||||
#define APP_VERSION "0.1"
|
||||
|
||||
#endif /* CONFIG_H */
|
82
src/gpx.cpp
Normal file
82
src/gpx.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#include <QFile>
|
||||
#include <QLineF>
|
||||
#include "ll.h"
|
||||
#include "gpx.h"
|
||||
|
||||
|
||||
#define ALPHA_E 0.9
|
||||
#define ALPHA_S 0.1
|
||||
|
||||
bool GPX::loadFile(const QString &fileName)
|
||||
{
|
||||
QFile file(fileName);
|
||||
bool ret;
|
||||
|
||||
_data.clear();
|
||||
_error.clear();
|
||||
|
||||
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
_error = qPrintable(file.errorString());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(ret = _parser.loadFile(&file, _data)))
|
||||
_error = _parser.errorString();
|
||||
file.close();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QVector<QPointF> GPX::elevationGraph() const
|
||||
{
|
||||
QVector<QPointF> graph;
|
||||
qreal dist = 0, ds, dh, acc;
|
||||
|
||||
if (!_data.size())
|
||||
return graph;
|
||||
|
||||
graph.append(QPointF(0, _data.at(0).elevation));
|
||||
for (int i = 1; i < _data.size(); i++) {
|
||||
ds = llDistance(_data.at(i).coordinates, _data.at(i-1).coordinates);
|
||||
dh = _data.at(i).elevation;
|
||||
dist += ds;
|
||||
acc = (i == 1) ? dh : (ALPHA_E * dh) + (1.0 - ALPHA_E) * acc;
|
||||
graph.append(QPointF(dist, acc));
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
QVector<QPointF> GPX::speedGraph() const
|
||||
{
|
||||
QVector<QPointF> graph;
|
||||
qreal dist = 0, v, ds, dt, acc;
|
||||
|
||||
if (!_data.size())
|
||||
return graph;
|
||||
|
||||
graph.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;
|
||||
v = ds / dt;
|
||||
acc = (i == 1) ? v : (ALPHA_S * v) + (1.0 - ALPHA_S) * acc;
|
||||
graph.append(QPointF(dist, acc));
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
QVector<QPointF> GPX::track() const
|
||||
{
|
||||
QVector<QPointF> track;
|
||||
QPointF p;
|
||||
|
||||
for (int i = 0; i < _data.size(); i++) {
|
||||
ll2mercator(_data.at(i).coordinates, p);
|
||||
track.append(p);
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
24
src/gpx.h
Normal file
24
src/gpx.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef GPX_H
|
||||
#define GPX_H
|
||||
|
||||
#include <QVector>
|
||||
#include <QPointF>
|
||||
#include <QString>
|
||||
#include "parser.h"
|
||||
|
||||
class GPX
|
||||
{
|
||||
public:
|
||||
bool loadFile(const QString &fileName);
|
||||
const QString &errorString() const {return _error;}
|
||||
QVector<QPointF> elevationGraph() const;
|
||||
QVector<QPointF> speedGraph() const;
|
||||
QVector<QPointF> track() const;
|
||||
|
||||
private:
|
||||
Parser _parser;
|
||||
QVector<TrackPoint> _data;
|
||||
QString _error;
|
||||
};
|
||||
|
||||
#endif // GPX_H
|
187
src/graph.cpp
Normal file
187
src/graph.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
#include <float.h>
|
||||
#include <QGraphicsView>
|
||||
#include <QGraphicsScene>
|
||||
#include <QEvent>
|
||||
#include "graph.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#define MARGIN 10.0
|
||||
|
||||
Graph::Graph(QWidget *parent)
|
||||
: QGraphicsView(parent)
|
||||
{
|
||||
_scene = new QGraphicsScene(this);
|
||||
setScene(_scene);
|
||||
|
||||
_xAxis = new AxisItem(AxisItem::X);
|
||||
_yAxis = new AxisItem(AxisItem::Y);
|
||||
|
||||
_slider = new SliderItem();
|
||||
connect(_slider, SIGNAL(positionChanged(const QPointF&)), this,
|
||||
SLOT(emitSliderPositionChanged(const QPointF&)));
|
||||
|
||||
_xMax = -FLT_MAX;
|
||||
_xMin = FLT_MAX;
|
||||
_yMax = -FLT_MAX;
|
||||
_yMin = FLT_MAX;
|
||||
|
||||
_xScale = 1;
|
||||
_yScale = 1;
|
||||
}
|
||||
|
||||
Graph::~Graph()
|
||||
{
|
||||
if (_xAxis->scene() != _scene)
|
||||
delete _xAxis;
|
||||
if (_yAxis->scene() != _scene)
|
||||
delete _yAxis;
|
||||
|
||||
if (_slider->scene() != _scene)
|
||||
delete _slider;
|
||||
|
||||
delete _scene;
|
||||
}
|
||||
|
||||
void Graph::updateBounds(const QPointF &point)
|
||||
{
|
||||
if (point.x() < _xMin)
|
||||
_xMin = point.x();
|
||||
if (point.x() > _xMax)
|
||||
_xMax = point.x();
|
||||
if (point.y() < _yMin)
|
||||
_yMin = point.y();
|
||||
if (point.y() > _yMax)
|
||||
_yMax = point.y();
|
||||
}
|
||||
|
||||
void Graph::loadData(const QVector<QPointF> &data)
|
||||
{
|
||||
QPainterPath path;
|
||||
QGraphicsPathItem *pi;
|
||||
QColor color = _colorShop.color();
|
||||
|
||||
|
||||
if (data.size() < 2)
|
||||
return;
|
||||
|
||||
updateBounds(data.at(0));
|
||||
path.moveTo(data.at(0).x(), -data.at(0).y());
|
||||
for (int i = 1; i < data.size(); i++) {
|
||||
path.lineTo(data.at(i).x(), -data.at(i).y());
|
||||
updateBounds(data.at(i));
|
||||
}
|
||||
|
||||
QBrush brush(color, Qt::SolidPattern);
|
||||
QPen pen(brush, 0);
|
||||
|
||||
pi = new QGraphicsPathItem(path);
|
||||
pi->setPen(pen);
|
||||
_scene->addItem(pi);
|
||||
_graphs.append(pi);
|
||||
|
||||
resize(viewport()->size() - QSizeF(MARGIN, MARGIN));
|
||||
}
|
||||
|
||||
void Graph::resize(const QSizeF &size)
|
||||
{
|
||||
QRectF r;
|
||||
QSizeF mx, my;
|
||||
QTransform transform;
|
||||
qreal xs, ys;
|
||||
|
||||
|
||||
if (_xAxis->scene() == _scene)
|
||||
_scene->removeItem(_xAxis);
|
||||
if (_yAxis->scene() == _scene)
|
||||
_scene->removeItem(_yAxis);
|
||||
_xAxis->setRange(QPointF(_xMin * _xScale, _xMax * _xScale));
|
||||
_yAxis->setRange(QPointF(_yMin * _yScale, _yMax * _yScale));
|
||||
|
||||
if (_slider->scene() == _scene)
|
||||
_scene->removeItem(_slider);
|
||||
|
||||
for (int i = 0; i < _graphs.size(); i++)
|
||||
_graphs.at(i)->resetTransform();
|
||||
|
||||
mx = _xAxis->margin();
|
||||
my = _yAxis->margin();
|
||||
r = _scene->itemsBoundingRect();
|
||||
xs = (size.width() - (my.width() + mx.width())) / r.width();
|
||||
ys = (size.height() - (mx.height() + my.height())) / r.height();
|
||||
transform.scale(xs, ys);
|
||||
|
||||
for (int i = 0; i < _graphs.size(); i++)
|
||||
_graphs.at(i)->setTransform(transform);
|
||||
|
||||
r = _scene->itemsBoundingRect();
|
||||
_xAxis->setSize(r.width());
|
||||
_yAxis->setSize(r.height());
|
||||
_xAxis->setPos(r.bottomLeft());
|
||||
_yAxis->setPos(r.bottomLeft());
|
||||
_scene->addItem(_xAxis);
|
||||
_scene->addItem(_yAxis);
|
||||
|
||||
_slider->setArea(r);
|
||||
_slider->setPos(r.bottomLeft());
|
||||
_scene->addItem(_slider);
|
||||
|
||||
_scene->setSceneRect(_scene->itemsBoundingRect());
|
||||
}
|
||||
|
||||
void Graph::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
if (!_graphs.empty())
|
||||
resize(viewport()->size() - QSizeF(MARGIN, MARGIN));
|
||||
}
|
||||
|
||||
void Graph::plot(QPainter *painter, const QRectF &target)
|
||||
{
|
||||
qreal ratio = target.width() / target.height();
|
||||
QSizeF orig = _scene->sceneRect().size();
|
||||
QSizeF canvas = QSizeF(orig.height() * ratio, orig.height());
|
||||
|
||||
resize(canvas);
|
||||
_slider->setVisible(false);
|
||||
_scene->render(painter, target, QRectF(), Qt::KeepAspectRatioByExpanding);
|
||||
_slider->setVisible(true);
|
||||
resize(orig);
|
||||
}
|
||||
|
||||
void Graph::clear()
|
||||
{
|
||||
if (_xAxis->scene() == _scene)
|
||||
_scene->removeItem(_xAxis);
|
||||
if (_yAxis->scene() == _scene)
|
||||
_scene->removeItem(_yAxis);
|
||||
|
||||
if (_slider->scene() == _scene)
|
||||
_scene->removeItem(_slider);
|
||||
|
||||
_scene->clear();
|
||||
_graphs.clear();
|
||||
_colorShop.reset();
|
||||
|
||||
_xMax = -FLT_MAX;
|
||||
_xMin = FLT_MAX;
|
||||
_yMax = -FLT_MAX;
|
||||
_yMin = FLT_MAX;
|
||||
|
||||
_scene->setSceneRect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
void Graph::emitSliderPositionChanged(const QPointF &pos)
|
||||
{
|
||||
qreal val = pos.x() / _slider->area().width();
|
||||
emit sliderPositionChanged(val);
|
||||
}
|
||||
|
||||
qreal Graph::sliderPosition() const
|
||||
{
|
||||
return _slider->pos().x() / _slider->area().width();
|
||||
}
|
||||
|
||||
void Graph::setSliderPosition(qreal pos)
|
||||
{
|
||||
_slider->setPos(pos * _slider->area().width(), 0);
|
||||
}
|
56
src/graph.h
Normal file
56
src/graph.h
Normal file
@ -0,0 +1,56 @@
|
||||
#ifndef GRAPH_H
|
||||
#define GRAPH_H
|
||||
|
||||
#include <QGraphicsView>
|
||||
#include <QVector>
|
||||
#include <QList>
|
||||
#include <QPointF>
|
||||
#include "axisitem.h"
|
||||
#include "slideritem.h"
|
||||
#include "colorshop.h"
|
||||
|
||||
|
||||
class Graph : public QGraphicsView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Graph(QWidget *parent = 0);
|
||||
~Graph();
|
||||
|
||||
void loadData(const QVector<QPointF> &data);
|
||||
void setXLabel(const QString &label) {_xAxis->setLabel(label);}
|
||||
void setYLabel(const QString &label) {_yAxis->setLabel(label);}
|
||||
void setXScale(qreal scale) {_xScale = scale;}
|
||||
void setYScale(qreal scale) {_yScale = scale;}
|
||||
|
||||
void plot(QPainter *painter, const QRectF &target);
|
||||
void clear();
|
||||
|
||||
qreal sliderPosition() const;
|
||||
void setSliderPosition(qreal pos);
|
||||
|
||||
signals:
|
||||
void sliderPositionChanged(qreal);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *);
|
||||
|
||||
private slots:
|
||||
void emitSliderPositionChanged(const QPointF &pos);
|
||||
|
||||
private:
|
||||
void updateBounds(const QPointF &point);
|
||||
void resize(const QSizeF &size);
|
||||
|
||||
|
||||
QGraphicsScene *_scene;
|
||||
AxisItem *_xAxis, *_yAxis;
|
||||
SliderItem *_slider;
|
||||
qreal _xMin, _xMax, _yMin, _yMax;
|
||||
QList<QGraphicsPathItem*> _graphs;
|
||||
qreal _xScale, _yScale;
|
||||
ColorShop _colorShop;
|
||||
};
|
||||
|
||||
#endif // GRAPH_H
|
320
src/gui.cpp
Normal file
320
src/gui.cpp
Normal file
@ -0,0 +1,320 @@
|
||||
#include <QApplication>
|
||||
#include <QVBoxLayout>
|
||||
#include <QMenuBar>
|
||||
#include <QStatusBar>
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
#include <QPrinter>
|
||||
#include <QPainter>
|
||||
#include <QKeyEvent>
|
||||
#include <QDir>
|
||||
#include "gui.h"
|
||||
#include "config.h"
|
||||
#include "icons.h"
|
||||
#include "keys.h"
|
||||
#include "gpx.h"
|
||||
#include "graph.h"
|
||||
#include "track.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
GUI::GUI()
|
||||
{
|
||||
createActions();
|
||||
createMenus();
|
||||
createToolBars();
|
||||
createTrackView();
|
||||
createTrackGraphs();
|
||||
createStatusBar();
|
||||
|
||||
connect(_elevationGraph, SIGNAL(sliderPositionChanged(qreal)), _track,
|
||||
SLOT(movePositionMarker(qreal)));
|
||||
connect(_speedGraph, SIGNAL(sliderPositionChanged(qreal)), _track,
|
||||
SLOT(movePositionMarker(qreal)));
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
layout->addWidget(_track);
|
||||
layout->addWidget(_trackGraphs);
|
||||
|
||||
QWidget *widget = new QWidget;
|
||||
widget->setLayout(layout);
|
||||
setCentralWidget(widget);
|
||||
|
||||
setWindowTitle(APP_NAME);
|
||||
setUnifiedTitleAndToolBarOnMac(true);
|
||||
|
||||
_dirIndex = -1;
|
||||
|
||||
resize(600, 800);
|
||||
}
|
||||
|
||||
void GUI::createActions()
|
||||
{
|
||||
// Action Groups
|
||||
_fileActionGroup = new QActionGroup(this);
|
||||
_fileActionGroup->setExclusive(false);
|
||||
_fileActionGroup->setEnabled(false);
|
||||
|
||||
|
||||
// General actions
|
||||
_exitAction = new QAction(tr("Exit"), this);
|
||||
connect(_exitAction, SIGNAL(triggered()), this, SLOT(close()));
|
||||
|
||||
_aboutAction = new QAction(QIcon(QPixmap(APP_ICON)),
|
||||
tr("About GPXSee"), this);
|
||||
connect(_aboutAction, SIGNAL(triggered()), this, SLOT(about()));
|
||||
_aboutQtAction = new QAction(tr("About Qt"), this);
|
||||
connect(_aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
|
||||
|
||||
// File related actions
|
||||
_openFileAction = new QAction(QIcon(QPixmap(OPEN_FILE_ICON)),
|
||||
tr("Open"), this);
|
||||
connect(_openFileAction, SIGNAL(triggered()), this, SLOT(openFile()));
|
||||
_saveFileAction = new QAction(QIcon(QPixmap(SAVE_FILE_ICON)),
|
||||
tr("Save"), this);
|
||||
_saveFileAction->setActionGroup(_fileActionGroup);
|
||||
connect(_saveFileAction, SIGNAL(triggered()), this, SLOT(saveFile()));
|
||||
_saveAsAction = new QAction(QIcon(QPixmap(SAVE_AS_ICON)),
|
||||
tr("Save as"), this);
|
||||
_saveAsAction->setActionGroup(_fileActionGroup);
|
||||
connect(_saveAsAction, SIGNAL(triggered()), this, SLOT(saveAs()));
|
||||
_closeFileAction = new QAction(QIcon(QPixmap(CLOSE_FILE_ICON)),
|
||||
tr("Close"), this);
|
||||
_closeFileAction->setActionGroup(_fileActionGroup);
|
||||
connect(_closeFileAction, SIGNAL(triggered()), this, SLOT(closeFile()));
|
||||
|
||||
// POI actions
|
||||
_openPOIAction = new QAction(QIcon(QPixmap(OPEN_FILE_ICON)),
|
||||
tr("Load file"), this);
|
||||
connect(_openPOIAction, SIGNAL(triggered()), this, SLOT(openPOIFile()));
|
||||
_showPOIAction = new QAction(QIcon(QPixmap(SHOW_POI_ICON)),
|
||||
tr("Show"), this);
|
||||
_showPOIAction->setCheckable(true);
|
||||
connect(_showPOIAction, SIGNAL(triggered()), this, SLOT(showPOI()));
|
||||
}
|
||||
|
||||
void GUI::createMenus()
|
||||
{
|
||||
_fileMenu = menuBar()->addMenu(tr("File"));
|
||||
_fileMenu->addAction(_openFileAction);
|
||||
_fileMenu->addAction(_saveFileAction);
|
||||
_fileMenu->addAction(_saveAsAction);
|
||||
_fileMenu->addAction(_closeFileAction);
|
||||
_fileMenu->addAction(_exitAction);
|
||||
|
||||
_poiMenu = menuBar()->addMenu(tr("POI"));
|
||||
_poiMenu->addAction(_openPOIAction);
|
||||
_poiMenu->addAction(_showPOIAction);
|
||||
|
||||
_aboutMenu = menuBar()->addMenu(tr("Help"));
|
||||
_aboutMenu->addAction(_aboutAction);
|
||||
_aboutMenu->addAction(_aboutQtAction);
|
||||
}
|
||||
|
||||
void GUI::createToolBars()
|
||||
{
|
||||
_fileToolBar = addToolBar(tr("File"));
|
||||
_fileToolBar->addAction(_openFileAction);
|
||||
_fileToolBar->addAction(_saveFileAction);
|
||||
_fileToolBar->addAction(_closeFileAction);
|
||||
|
||||
_poiToolBar = addToolBar(tr("POI"));
|
||||
_poiToolBar->addAction(_showPOIAction);
|
||||
}
|
||||
|
||||
void GUI::createTrackView()
|
||||
{
|
||||
_track = new Track(this);
|
||||
}
|
||||
|
||||
void GUI::createTrackGraphs()
|
||||
{
|
||||
_elevationGraph = new Graph;
|
||||
_elevationGraph->setXLabel(tr("Distance [km]"));
|
||||
_elevationGraph->setYLabel(tr("Elevation [m.a.s.l.]"));
|
||||
_elevationGraph->setXScale(0.001);
|
||||
|
||||
_speedGraph = new Graph;
|
||||
_speedGraph->setXLabel(tr("Distance [km]"));
|
||||
_speedGraph->setYLabel(tr("Speed [km/h]"));
|
||||
_speedGraph->setXScale(0.001);
|
||||
_speedGraph->setYScale(3.6);
|
||||
|
||||
_trackGraphs = new QTabWidget;
|
||||
_trackGraphs->addTab(_elevationGraph, tr("Elevation"));
|
||||
_trackGraphs->addTab(_speedGraph, tr("Speed"));
|
||||
connect(_trackGraphs, SIGNAL(currentChanged(int)), this,
|
||||
SLOT(graphChanged(int)));
|
||||
|
||||
_trackGraphs->setFixedHeight(200);
|
||||
_trackGraphs->setSizePolicy(
|
||||
QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
|
||||
}
|
||||
|
||||
void GUI::createStatusBar()
|
||||
{
|
||||
_fileName = new QLabel();
|
||||
_zoom = new QLabel();
|
||||
_zoom->setAlignment(Qt::AlignHCenter);
|
||||
|
||||
statusBar()->addPermanentWidget(_fileName, 9);
|
||||
statusBar()->addPermanentWidget(_zoom, 1);
|
||||
statusBar()->setSizeGripEnabled(false);
|
||||
}
|
||||
|
||||
void GUI::about()
|
||||
{
|
||||
QMessageBox::about(this, tr("About GPXSee"),
|
||||
QString("<h3>") + QString(APP_NAME" "APP_VERSION)
|
||||
+ QString("</h3><p>") + tr("GPX viewer and analyzer") + QString("<p/>")
|
||||
+ QString("<p>") + tr("GPXSee is distributed under the terms of the "
|
||||
"GNU General Public License version 3. For more info about GPXSee visit "
|
||||
"the project homepage at ")
|
||||
+ QString("<a href=\""APP_HOMEPAGE"\">"APP_HOMEPAGE"</a>.</p>"));
|
||||
}
|
||||
|
||||
void GUI::openFile()
|
||||
{
|
||||
QStringList files = QFileDialog::getOpenFileNames(this, tr("Open file"));
|
||||
QStringList list = files;
|
||||
QStringList::Iterator it = list.begin();
|
||||
|
||||
while(it != list.end()) {
|
||||
openFile(*it);
|
||||
++it;
|
||||
}
|
||||
|
||||
if (!list.empty())
|
||||
setDir(list.back());
|
||||
}
|
||||
|
||||
bool GUI::openFile(const QString &fileName)
|
||||
{
|
||||
GPX gpx;
|
||||
|
||||
if (!fileName.isEmpty()) {
|
||||
if (gpx.loadFile(fileName)) {
|
||||
_elevationGraph->loadData(gpx.elevationGraph());
|
||||
_speedGraph->loadData(gpx.speedGraph());
|
||||
_track->loadData(gpx.track());
|
||||
if (_showPOIAction->isChecked())
|
||||
_track->loadPOI(_poi);
|
||||
|
||||
_fileActionGroup->setEnabled(true);
|
||||
_fileName->setText(fileName);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
QMessageBox::critical(this, tr("Error"), fileName + QString("\n\n")
|
||||
+ tr("Error loading GPX file:\n%1").arg(gpx.errorString()));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GUI::openPOIFile()
|
||||
{
|
||||
QString fileName = QFileDialog::getOpenFileName(this, tr("Open POI file"));
|
||||
|
||||
if (!fileName.isEmpty()) {
|
||||
if (!_poi.loadFile(fileName)) {
|
||||
QMessageBox::critical(this, tr("Error"),
|
||||
tr("Error loading POI file:\n%1").arg(_poi.errorString()));
|
||||
} else {
|
||||
_showPOIAction->setChecked(true);
|
||||
_track->loadPOI(_poi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::saveAs()
|
||||
{
|
||||
QString fileName = QFileDialog::getSaveFileName(this, "Export to PDF",
|
||||
QString(), "*.pdf");
|
||||
|
||||
if (!fileName.isEmpty()) {
|
||||
saveFile(fileName);
|
||||
_saveFileName = fileName;
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::saveFile()
|
||||
{
|
||||
if (_saveFileName.isEmpty())
|
||||
emit saveAs();
|
||||
else
|
||||
saveFile(_saveFileName);
|
||||
}
|
||||
|
||||
void GUI::saveFile(const QString &fileName)
|
||||
{
|
||||
QPrinter printer(QPrinter::HighResolution);
|
||||
printer.setPageSize(QPrinter::A4);
|
||||
printer.setOrientation(_track->orientation());
|
||||
printer.setOutputFormat(QPrinter::PdfFormat);
|
||||
printer.setOutputFileName(fileName);
|
||||
|
||||
QPainter p(&printer);
|
||||
int margin = (printer.paperRect().height() - printer.pageRect().height())
|
||||
/ 2;
|
||||
_track->plot(&p, QRectF(0, 0, printer.width(), (0.80 * printer.height())
|
||||
- margin));
|
||||
_elevationGraph->plot(&p, QRectF(0, 0.80 * printer.height(),
|
||||
printer.width(), printer.height() * 0.20));
|
||||
p.end();
|
||||
}
|
||||
|
||||
void GUI::closeFile()
|
||||
{
|
||||
_elevationGraph->clear();
|
||||
_speedGraph->clear();
|
||||
_track->clear();
|
||||
_fileName->clear();
|
||||
|
||||
_fileActionGroup->setEnabled(false);
|
||||
}
|
||||
|
||||
void GUI::showPOI()
|
||||
{
|
||||
if (_showPOIAction->isChecked())
|
||||
_track->loadPOI(_poi);
|
||||
else
|
||||
_track->clearPOI();
|
||||
}
|
||||
|
||||
void GUI::graphChanged(int index)
|
||||
{
|
||||
if (_trackGraphs->widget(index) == _elevationGraph)
|
||||
_elevationGraph->setSliderPosition(_speedGraph->sliderPosition());
|
||||
else if (_trackGraphs->widget(index) == _speedGraph)
|
||||
_speedGraph->setSliderPosition(_elevationGraph->sliderPosition());
|
||||
}
|
||||
|
||||
|
||||
void GUI::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (_dirIndex < 0 || _dirFiles.count() == 1)
|
||||
return;
|
||||
|
||||
if (event->key() == PREV_KEY) {
|
||||
if (_dirIndex == 0)
|
||||
return;
|
||||
closeFile();
|
||||
openFile(_dirFiles.at(--_dirIndex).absoluteFilePath());
|
||||
}
|
||||
if (event->key() == NEXT_KEY) {
|
||||
if (_dirIndex == _dirFiles.size() - 1)
|
||||
return;
|
||||
closeFile();
|
||||
openFile(_dirFiles.at(++_dirIndex).absoluteFilePath());
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::setDir(const QString &file)
|
||||
{
|
||||
QDir dir = QFileInfo(file).absoluteDir();
|
||||
_dirFiles = dir.entryInfoList(QStringList("*.gpx"), QDir::Files);
|
||||
_dirIndex = _dirFiles.empty() ? -1 : _dirFiles.indexOf(file);
|
||||
}
|
85
src/gui.h
Normal file
85
src/gui.h
Normal file
@ -0,0 +1,85 @@
|
||||
#ifndef GUI_H
|
||||
#define GUI_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QMenu>
|
||||
#include <QToolBar>
|
||||
#include <QTabWidget>
|
||||
#include <QGraphicsView>
|
||||
#include <QActionGroup>
|
||||
#include <QAction>
|
||||
#include <QLabel>
|
||||
#include <QFileInfoList>
|
||||
#include "poi.h"
|
||||
|
||||
class Graph;
|
||||
class Track;
|
||||
|
||||
class GUI : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GUI();
|
||||
|
||||
bool openFile(const QString &fileName);
|
||||
void setDir(const QString &file);
|
||||
|
||||
private slots:
|
||||
void about();
|
||||
void saveFile();
|
||||
void saveAs();
|
||||
void openFile();
|
||||
void closeFile();
|
||||
void openPOIFile();
|
||||
void showPOI();
|
||||
void graphChanged(int);
|
||||
|
||||
private:
|
||||
void createActions();
|
||||
void createMenus();
|
||||
void createToolBars();
|
||||
void createStatusBar();
|
||||
|
||||
void createTrackView();
|
||||
void createTrackGraphs();
|
||||
|
||||
void keyPressEvent(QKeyEvent * event);
|
||||
|
||||
void saveFile(const QString &fileName);
|
||||
|
||||
QMenu *_fileMenu;
|
||||
QMenu *_aboutMenu;
|
||||
QMenu *_poiMenu;
|
||||
|
||||
QToolBar *_fileToolBar;
|
||||
QToolBar *_poiToolBar;
|
||||
QTabWidget *_trackGraphs;
|
||||
QActionGroup *_fileActionGroup;
|
||||
|
||||
QAction *_exitAction;
|
||||
QAction *_aboutAction;
|
||||
QAction *_aboutQtAction;
|
||||
QAction *_saveFileAction;
|
||||
QAction *_saveAsAction;
|
||||
QAction *_openFileAction;
|
||||
QAction *_closeFileAction;
|
||||
QAction *_openPOIAction;
|
||||
QAction *_showPOIAction;
|
||||
|
||||
QLabel *_fileName;
|
||||
QLabel *_zoom;
|
||||
|
||||
Graph *_elevationGraph;
|
||||
Graph *_speedGraph;
|
||||
Track *_track;
|
||||
|
||||
POI _poi;
|
||||
|
||||
QFileInfoList _dirFiles;
|
||||
int _dirIndex;
|
||||
|
||||
QString _saveFileName;
|
||||
};
|
||||
|
||||
#endif // GUI_H
|
11
src/icons.h
Normal file
11
src/icons.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef ICONS_H
|
||||
#define ICONS_H
|
||||
|
||||
#define APP_ICON ":/icons/gpxsee.png"
|
||||
#define OPEN_FILE_ICON ":/icons/document-open.png"
|
||||
#define SAVE_FILE_ICON ":/icons/document-save.png"
|
||||
#define SAVE_AS_ICON ":/icons/document-save-as.png"
|
||||
#define CLOSE_FILE_ICON ":/icons/dialog-close.png"
|
||||
#define SHOW_POI_ICON ":/icons/flag.png"
|
||||
|
||||
#endif /* ICONS_H */
|
7
src/keys.h
Normal file
7
src/keys.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef KEYS_H
|
||||
#define KEYS_H
|
||||
|
||||
#define NEXT_KEY Qt::Key_Space
|
||||
#define PREV_KEY Qt::Key_Backspace
|
||||
|
||||
#endif // KEYS_H
|
27
src/ll.cpp
Normal file
27
src/ll.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include <cmath>
|
||||
#include "ll.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif // M_PI
|
||||
|
||||
#define WGS84_RADIUS 6378137.0
|
||||
#define deg2rad(d) (((d)*M_PI)/180.0)
|
||||
#define rad2deg(d) (((d)*180.0)/M_PI)
|
||||
|
||||
qreal llDistance(const QPointF &p1, const QPointF &p2)
|
||||
{
|
||||
qreal dLat = deg2rad(p2.y() - p1.y());
|
||||
qreal dLon = deg2rad(p2.x() - p1.x());
|
||||
qreal a = pow(sin(dLat / 2.0), 2.0)
|
||||
+ cos(deg2rad(p1.y())) * cos(deg2rad(p2.y())) * pow(sin(dLon / 2.0), 2.0);
|
||||
qreal c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a));
|
||||
|
||||
return (WGS84_RADIUS * c);
|
||||
}
|
||||
|
||||
void ll2mercator(const QPointF &src, QPointF &dst)
|
||||
{
|
||||
dst.setX(src.x());
|
||||
dst.setY(rad2deg(log(tan(M_PI/4.0 + deg2rad(src.y())/2.0))));
|
||||
}
|
9
src/ll.h
Normal file
9
src/ll.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef LL_H
|
||||
#define LL_H
|
||||
|
||||
#include <QPointF>
|
||||
|
||||
void ll2mercator(const QPointF &src, QPointF &dst);
|
||||
qreal llDistance(const QPointF &p1, const QPointF &p2);
|
||||
|
||||
#endif // LL_H
|
28
src/main.cpp
Normal file
28
src/main.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#include <QApplication>
|
||||
#include <QTranslator>
|
||||
#include <QLocale>
|
||||
#include "gui.h"
|
||||
#include "icons.h"
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QString locale = QLocale::system().name();
|
||||
QTranslator translator;
|
||||
translator.load(QString(":/lang/gpxsee_") + locale);
|
||||
app.installTranslator(&translator);
|
||||
|
||||
GUI gui;
|
||||
gui.setWindowIcon(QIcon(QPixmap(APP_ICON)));
|
||||
gui.show();
|
||||
|
||||
for (int i = 1; i < argc; i++)
|
||||
gui.openFile(argv[i]);
|
||||
|
||||
if (argc > 1)
|
||||
gui.setDir(QString(argv[argc - 1]));
|
||||
|
||||
return app.exec();
|
||||
}
|
27
src/markeritem.cpp
Normal file
27
src/markeritem.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include <QPainter>
|
||||
#include "markeritem.h"
|
||||
|
||||
#define SIZE 8
|
||||
|
||||
MarkerItem::MarkerItem()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QRectF MarkerItem::boundingRect() const
|
||||
{
|
||||
return QRectF(-SIZE/2, -SIZE/2, SIZE, SIZE);
|
||||
}
|
||||
|
||||
void MarkerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
||||
QWidget *widget)
|
||||
{
|
||||
Q_UNUSED(option);
|
||||
Q_UNUSED(widget);
|
||||
|
||||
painter->setPen(Qt::red);
|
||||
painter->drawLine(-SIZE/2, 0, SIZE/2, 0);
|
||||
painter->drawLine(0, -SIZE/2, 0, SIZE/2);
|
||||
|
||||
// painter->drawRect(boundingRect());
|
||||
}
|
16
src/markeritem.h
Normal file
16
src/markeritem.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef MARKERITEM_H
|
||||
#define MARKERITEM_H
|
||||
|
||||
#include <QGraphicsItem>
|
||||
|
||||
class MarkerItem : public QGraphicsItem
|
||||
{
|
||||
public:
|
||||
MarkerItem();
|
||||
|
||||
QRectF boundingRect() const;
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
||||
QWidget *widget);
|
||||
};
|
||||
|
||||
#endif // MARKERITEM_H
|
93
src/parser.cpp
Normal file
93
src/parser.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
#include "parser.h"
|
||||
|
||||
|
||||
void Parser::handleTrekPointData(QVector<TrackPoint> &data,
|
||||
QStringRef element, const QString &value)
|
||||
{
|
||||
if (element == "ele")
|
||||
data.last().elevation = value.toLatin1().toDouble();
|
||||
if (element == "time")
|
||||
data.last().timestamp = QDateTime::fromString(value.toLatin1(),
|
||||
Qt::ISODate);
|
||||
}
|
||||
|
||||
void Parser::handleTrekPointAttributes(QVector<TrackPoint> &data,
|
||||
const QXmlStreamAttributes &attr)
|
||||
{
|
||||
data.last().coordinates.setY(attr.value("lat").toLatin1().toDouble());
|
||||
data.last().coordinates.setX(attr.value("lon").toLatin1().toDouble());
|
||||
}
|
||||
|
||||
|
||||
void Parser::trekPointData(QVector<TrackPoint> &data)
|
||||
{
|
||||
while (_reader.readNextStartElement()) {
|
||||
if (_reader.name() == "ele" || _reader.name() == "time")
|
||||
handleTrekPointData(data, _reader.name(), _reader.readElementText());
|
||||
else
|
||||
_reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::trekPoints(QVector<TrackPoint> &data)
|
||||
{
|
||||
QXmlStreamAttributes attr;
|
||||
|
||||
while (_reader.readNextStartElement()) {
|
||||
if (_reader.name() == "trkpt") {
|
||||
attr = _reader.attributes();
|
||||
data.append(TrackPoint());
|
||||
handleTrekPointAttributes(data, attr);
|
||||
trekPointData(data);
|
||||
} else
|
||||
_reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::trek(QVector<TrackPoint> &data)
|
||||
{
|
||||
while (_reader.readNextStartElement()) {
|
||||
if (_reader.name() == "trkseg") {
|
||||
trekPoints(data);
|
||||
} else
|
||||
_reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::gpx(QVector<TrackPoint> &data)
|
||||
{
|
||||
while (_reader.readNextStartElement()) {
|
||||
if (_reader.name() == "trk")
|
||||
trek(data);
|
||||
else
|
||||
_reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
bool Parser::parse(QVector<TrackPoint> &data)
|
||||
{
|
||||
if (_reader.readNextStartElement()) {
|
||||
if (_reader.name() == "gpx")
|
||||
gpx(data);
|
||||
else
|
||||
_reader.raiseError(QObject::tr("Not a GPX file."));
|
||||
}
|
||||
|
||||
return !_reader.error();
|
||||
}
|
||||
|
||||
|
||||
QString Parser::errorString() const
|
||||
{
|
||||
return QObject::tr("%1\nLine %2")
|
||||
.arg(_reader.errorString())
|
||||
.arg(_reader.lineNumber());
|
||||
}
|
||||
|
||||
bool Parser::loadFile(QIODevice *device, QVector<TrackPoint> &data)
|
||||
{
|
||||
_reader.clear();
|
||||
_reader.setDevice(device);
|
||||
|
||||
return parse(data);
|
||||
}
|
36
src/parser.h
Normal file
36
src/parser.h
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef PARSER_H
|
||||
#define PARSER_H
|
||||
|
||||
#include <QXmlStreamReader>
|
||||
#include <QDateTime>
|
||||
#include <QPointF>
|
||||
|
||||
struct TrackPoint
|
||||
{
|
||||
QPointF coordinates;
|
||||
QDateTime timestamp;
|
||||
qreal elevation;
|
||||
};
|
||||
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
bool loadFile(QIODevice *device, QVector<TrackPoint> &data);
|
||||
QString errorString() const;
|
||||
|
||||
private:
|
||||
bool parse(QVector<TrackPoint> &data);
|
||||
void gpx(QVector<TrackPoint> &data);
|
||||
void trek(QVector<TrackPoint> &data);
|
||||
void trekPoints(QVector<TrackPoint> &data);
|
||||
void trekPointData(QVector<TrackPoint> &data);
|
||||
|
||||
void handleTrekPointAttributes(QVector<TrackPoint> &data,
|
||||
const QXmlStreamAttributes &attr);
|
||||
void handleTrekPointData(QVector<TrackPoint> &data, QStringRef element,
|
||||
const QString &value);
|
||||
|
||||
QXmlStreamReader _reader;
|
||||
};
|
||||
|
||||
#endif // PARSER_H
|
97
src/poi.cpp
Normal file
97
src/poi.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
#include <QFile>
|
||||
#include <QSet>
|
||||
#include <QList>
|
||||
#include "ll.h"
|
||||
#include "poi.h"
|
||||
|
||||
|
||||
bool POI::loadFile(const QString &fileName)
|
||||
{
|
||||
QFile file(fileName);
|
||||
bool ret;
|
||||
int ln = 1;
|
||||
|
||||
_error.clear();
|
||||
|
||||
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
_error = qPrintable(file.errorString());
|
||||
return false;
|
||||
}
|
||||
|
||||
while (!file.atEnd()) {
|
||||
QByteArray line = file.readLine();
|
||||
QList<QByteArray> list = line.split(',');
|
||||
if (list.size() < 3) {
|
||||
_error = QString("Parse error\nLine %1").arg(ln);
|
||||
return false;
|
||||
}
|
||||
|
||||
qreal lat = list[0].trimmed().toDouble(&ret);
|
||||
if (!ret) {
|
||||
_error = QObject::tr("Invalid latitude\nLine %1").arg(ln);
|
||||
return false;
|
||||
}
|
||||
qreal lon = list[1].trimmed().toDouble(&ret);
|
||||
if (!ret) {
|
||||
_error = QObject::tr("Invalid longitude\nLine %1").arg(ln);
|
||||
return false;
|
||||
}
|
||||
QByteArray ba = list[2].trimmed();
|
||||
|
||||
QPointF p;
|
||||
Entry entry;
|
||||
ll2mercator(QPointF(lon, lat), p);
|
||||
entry.description = QString::fromUtf8(ba.data(), ba.size());
|
||||
entry.coordinates = p;
|
||||
|
||||
_data.append(entry);
|
||||
ln++;
|
||||
}
|
||||
|
||||
for (int i = 0; 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, &_data.at(i));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cb(const Entry* data, void* context)
|
||||
{
|
||||
QSet<const Entry*> *set = (QSet<const Entry*>*) context;
|
||||
set->insert(data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define RECT 0.01
|
||||
QVector<Entry> POI::points(const QVector<QPointF> &path) const
|
||||
{
|
||||
QVector<Entry> ret;
|
||||
QSet<const Entry*> set;
|
||||
qreal min[2], max[2];
|
||||
|
||||
for (int i = 0; i < path.count(); i++) {
|
||||
min[0] = path.at(i).x() - RECT;
|
||||
min[1] = path.at(i).y() - RECT;
|
||||
max[0] = path.at(i).x() + RECT;
|
||||
max[1] = path.at(i).y() + RECT;
|
||||
_tree.Search(min, max, cb, &set);
|
||||
}
|
||||
|
||||
QSet<const Entry *>::const_iterator i = set.constBegin();
|
||||
while (i != set.constEnd()) {
|
||||
ret.append(*(*i));
|
||||
++i;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void POI::clear()
|
||||
{
|
||||
_tree.RemoveAll();
|
||||
_data.clear();
|
||||
}
|
43
src/poi.h
Normal file
43
src/poi.h
Normal file
@ -0,0 +1,43 @@
|
||||
#ifndef POI_H
|
||||
#define POI_H
|
||||
|
||||
#include <QVector>
|
||||
#include <QPointF>
|
||||
#include <QString>
|
||||
#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
|
||||
{
|
||||
public:
|
||||
bool loadFile(const QString &fileName);
|
||||
QString errorString() const {return _error;}
|
||||
QVector<Entry> points(const QVector<QPointF> &path) const;
|
||||
|
||||
void clear();
|
||||
|
||||
private:
|
||||
typedef RTree<const Entry*, qreal, 2> POITree;
|
||||
|
||||
POITree _tree;
|
||||
QVector<Entry> _data;
|
||||
QString _error;
|
||||
};
|
||||
|
||||
#endif // POI_H
|
51
src/poiitem.cpp
Normal file
51
src/poiitem.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include <QPainter>
|
||||
#include "poiitem.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#define FONT_FAMILY "Arial"
|
||||
#define FONT_SIZE 12
|
||||
#define POINT_SIZE 8
|
||||
|
||||
|
||||
POIItem::POIItem(const QString &text)
|
||||
{
|
||||
_text = text;
|
||||
updateBoundingRect();
|
||||
}
|
||||
|
||||
void POIItem::updateBoundingRect()
|
||||
{
|
||||
QFont font;
|
||||
font.setPixelSize(FONT_SIZE);
|
||||
font.setFamily(FONT_FAMILY);
|
||||
QFontMetrics fm(font);
|
||||
QRect ts = fm.tightBoundingRect(_text);
|
||||
|
||||
_boundingRect = QRectF(0, 0, ts.width() + POINT_SIZE,
|
||||
ts.height() + fm.descent() + POINT_SIZE);
|
||||
}
|
||||
|
||||
void POIItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
||||
QWidget *widget)
|
||||
{
|
||||
Q_UNUSED(option);
|
||||
Q_UNUSED(widget);
|
||||
QFont font;
|
||||
font.setPixelSize(FONT_SIZE);
|
||||
font.setFamily(FONT_FAMILY);
|
||||
QFontMetrics fm(font);
|
||||
QRect ts = fm.tightBoundingRect(_text);
|
||||
|
||||
painter->setFont(font);
|
||||
painter->drawText(POINT_SIZE - qMax(ts.x(), 0), POINT_SIZE + ts.height(),
|
||||
_text);
|
||||
painter->setBrush(Qt::SolidPattern);
|
||||
painter->drawEllipse(0, 0, POINT_SIZE, POINT_SIZE);
|
||||
|
||||
/*
|
||||
painter->setPen(Qt::red);
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
painter->drawRect(boundingRect());
|
||||
*/
|
||||
}
|
22
src/poiitem.h
Normal file
22
src/poiitem.h
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef POIITEM_H
|
||||
#define POIITEM_H
|
||||
|
||||
#include <QGraphicsItem>
|
||||
|
||||
class POIItem : public QGraphicsItem
|
||||
{
|
||||
public:
|
||||
POIItem(const QString &text);
|
||||
|
||||
QRectF boundingRect() const {return _boundingRect;}
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
||||
QWidget *widget);
|
||||
|
||||
private:
|
||||
void updateBoundingRect();
|
||||
|
||||
QString _text;
|
||||
QRectF _boundingRect;
|
||||
};
|
||||
|
||||
#endif // POIITEM_H
|
1313
src/rtree.h
Normal file
1313
src/rtree.h
Normal file
File diff suppressed because it is too large
Load Diff
49
src/slideritem.cpp
Normal file
49
src/slideritem.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include <QPainter>
|
||||
#include "slideritem.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#define SIZE 10
|
||||
|
||||
SliderItem::SliderItem()
|
||||
{
|
||||
setFlag(ItemIsMovable);
|
||||
setFlag(ItemSendsGeometryChanges);
|
||||
}
|
||||
|
||||
QRectF SliderItem::boundingRect() const
|
||||
{
|
||||
return QRectF(-SIZE/2, -_area.height(), SIZE, _area.height());
|
||||
}
|
||||
|
||||
void SliderItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
||||
QWidget *widget)
|
||||
{
|
||||
Q_UNUSED(option);
|
||||
Q_UNUSED(widget);
|
||||
|
||||
painter->setPen(Qt::red);
|
||||
painter->drawLine(0, 0, 0, -_area.height());
|
||||
|
||||
//painter->drawRect(boundingRect());
|
||||
}
|
||||
|
||||
QVariant SliderItem::itemChange(GraphicsItemChange change, const QVariant &value)
|
||||
{
|
||||
if (change == ItemPositionChange && scene()) {
|
||||
QPointF pos = value.toPointF();
|
||||
|
||||
if (!_area.contains(QRectF(pos, boundingRect().size()))) {
|
||||
pos.setX(qMin(_area.right(), qMax(pos.x(), _area.left())));
|
||||
pos.setY(qMin(_area.bottom(), qMax(pos.y(), _area.top()
|
||||
+ boundingRect().height())));
|
||||
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
if (change == ItemPositionHasChanged)
|
||||
emit positionChanged(value.toPointF());
|
||||
|
||||
return QGraphicsItem::itemChange(change, value);
|
||||
}
|
30
src/slideritem.h
Normal file
30
src/slideritem.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef SLIDERITEM_H
|
||||
#define SLIDERITEM_H
|
||||
|
||||
#include <QGraphicsItem>
|
||||
|
||||
class SliderItem : public QGraphicsObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SliderItem();
|
||||
|
||||
QRectF boundingRect() const;
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
||||
QWidget *widget);
|
||||
|
||||
QRectF area() {return _area;}
|
||||
void setArea(const QRectF &area) {_area = area;}
|
||||
|
||||
signals:
|
||||
void positionChanged(const QPointF&);
|
||||
|
||||
protected:
|
||||
QVariant itemChange(GraphicsItemChange change, const QVariant &value);
|
||||
|
||||
private:
|
||||
QRectF _area;
|
||||
};
|
||||
|
||||
#endif // SLIDERITEM_H
|
192
src/track.cpp
Normal file
192
src/track.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
#include <cmath>
|
||||
#include <QGraphicsView>
|
||||
#include <QGraphicsScene>
|
||||
#include <QPainterPath>
|
||||
#include <QWheelEvent>
|
||||
#include <QGraphicsEllipseItem>
|
||||
#include "poiitem.h"
|
||||
#include "markeritem.h"
|
||||
#include "track.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
Track::Track(QWidget *parent)
|
||||
: QGraphicsView(parent)
|
||||
{
|
||||
_scene = new QGraphicsScene(this);
|
||||
setScene(_scene);
|
||||
setResizeAnchor(QGraphicsView::AnchorViewCenter);
|
||||
|
||||
_maxLen = 0;
|
||||
}
|
||||
|
||||
Track::~Track()
|
||||
{
|
||||
delete _scene;
|
||||
}
|
||||
|
||||
void Track::loadData(const QVector<QPointF> &track)
|
||||
{
|
||||
QPainterPath path;
|
||||
QGraphicsPathItem *pi;
|
||||
MarkerItem *mi;
|
||||
QColor color = _colorShop.color();
|
||||
|
||||
if (track.size() < 2)
|
||||
return;
|
||||
|
||||
_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);
|
||||
|
||||
for (int i = 0; i < _trackPaths.size(); i++) {
|
||||
_trackPaths.at(i)->resetTransform();
|
||||
_scene->removeItem(_markers.at(i));
|
||||
}
|
||||
|
||||
QBrush brush(color, Qt::SolidPattern);
|
||||
QPen pen(brush, 0);
|
||||
pi = new QGraphicsPathItem(path);
|
||||
pi->setPen(pen);
|
||||
_scene->addItem(pi);
|
||||
_trackPaths.append(pi);
|
||||
|
||||
QTransform t = transform();
|
||||
|
||||
mi = new MarkerItem();
|
||||
mi->setPos(pi->path().pointAtPercent(0));
|
||||
_markers.append(mi);
|
||||
|
||||
|
||||
for (int i = 0; i < _trackPaths.size(); i++) {
|
||||
_markers.at(i)->setTransform(t);
|
||||
_scene->addItem(_markers.at(i));
|
||||
}
|
||||
|
||||
_scene->setSceneRect(_scene->itemsBoundingRect());
|
||||
fitInView(_scene->sceneRect(), Qt::KeepAspectRatio);
|
||||
}
|
||||
|
||||
QTransform Track::transform() const
|
||||
{
|
||||
QPointF scale(_scene->itemsBoundingRect().width() / viewport()->width(),
|
||||
_scene->itemsBoundingRect().height() / viewport()->height());
|
||||
QTransform transform;
|
||||
transform.scale(qMax(scale.x(), scale.y()), qMax(scale.x(), scale.y()));
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
void Track::loadPOI(const POI &poi)
|
||||
{
|
||||
QHash<Entry, POIItem*>::const_iterator it,jt;
|
||||
|
||||
for (int i = 0; i < _tracks.size(); i++) {
|
||||
QVector<Entry> 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).description);
|
||||
pi->setPos(p.at(i).coordinates.x(), -p.at(i).coordinates.y());
|
||||
pi->setTransform(transform());
|
||||
pi->setZValue(1);
|
||||
_scene->addItem(pi);
|
||||
|
||||
_pois.insert(p.at(i), pi);
|
||||
}
|
||||
}
|
||||
|
||||
for (it = _pois.constBegin(); it != _pois.constEnd(); it++)
|
||||
it.value()->setTransform(transform());
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
_scene->setSceneRect(_scene->itemsBoundingRect());
|
||||
fitInView(_scene->sceneRect(), Qt::KeepAspectRatio);
|
||||
}
|
||||
|
||||
void Track::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
float factor;
|
||||
|
||||
factor = pow(2.0, -event->delta() / 400.0);
|
||||
scale(factor, factor);
|
||||
}
|
||||
|
||||
void Track::plot(QPainter *painter, const QRectF &target)
|
||||
{
|
||||
QRectF orig = sceneRect();
|
||||
QRectF adj;
|
||||
qreal ratio, diff;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _markers.size(); i++)
|
||||
_markers.at(i)->setVisible(false);
|
||||
_scene->render(painter, target, adj, Qt::KeepAspectRatioByExpanding);
|
||||
for (int i = 0; i < _markers.size(); i++)
|
||||
_markers.at(i)->setVisible(true);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
_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);
|
||||
}
|
||||
}
|
50
src/track.h
Normal file
50
src/track.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef TRACK_H
|
||||
#define TRACK_H
|
||||
|
||||
#include <QGraphicsView>
|
||||
#include <QVector>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QPrinter>
|
||||
#include "poi.h"
|
||||
#include "colorshop.h"
|
||||
|
||||
|
||||
class POIItem;
|
||||
class MarkerItem;
|
||||
|
||||
class Track : public QGraphicsView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Track(QWidget *parent = 0);
|
||||
~Track();
|
||||
|
||||
void loadData(const QVector<QPointF> &track);
|
||||
void loadPOI(const POI &poi);
|
||||
|
||||
void clearPOI();
|
||||
void clear();
|
||||
|
||||
void plot(QPainter *painter, const QRectF &target);
|
||||
enum QPrinter::Orientation orientation() const;
|
||||
|
||||
public slots:
|
||||
void movePositionMarker(qreal val);
|
||||
|
||||
private:
|
||||
QTransform transform() const;
|
||||
void wheelEvent(QWheelEvent *event);
|
||||
|
||||
QGraphicsScene *_scene;
|
||||
QList<QVector<QPointF> > _tracks;
|
||||
QList<QGraphicsPathItem*> _trackPaths;
|
||||
QList<MarkerItem*> _markers;
|
||||
QHash<Entry, POIItem*> _pois;
|
||||
|
||||
ColorShop _colorShop;
|
||||
qreal _maxLen;
|
||||
};
|
||||
|
||||
#endif // TRACK_H
|
Loading…
Reference in New Issue
Block a user