diff --git a/gpxsee.pro b/gpxsee.pro index 5978bb93..befab9ef 100644 --- a/gpxsee.pro +++ b/gpxsee.pro @@ -118,6 +118,7 @@ HEADERS += src/common/config.h \ src/data/style.h \ src/data/twonavparser.h \ src/data/txtparser.h \ + src/data/vtkparser.h \ src/map/ENC/data.h \ src/map/IMG/light.h \ src/map/downloader.h \ @@ -346,6 +347,7 @@ SOURCES += src/main.cpp \ src/GUI/projectioncombobox.cpp \ src/GUI/passwordedit.cpp \ src/data/txtparser.cpp \ + src/data/vtkparser.cpp \ src/map/downloader.cpp \ src/map/demloader.cpp \ src/map/ENC/atlasdata.cpp \ diff --git a/src/data/data.cpp b/src/data/data.cpp index 6ed63882..ecba0159 100644 --- a/src/data/data.cpp +++ b/src/data/data.cpp @@ -24,6 +24,7 @@ #include "twonavparser.h" #include "gpsdumpparser.h" #include "txtparser.h" +#include "vtkparser.h" #include "data.h" @@ -51,6 +52,7 @@ static GHPParser ghp; static TwoNavParser twonav; static GPSDumpParser gpsdump; static TXTParser txt; +static VTKParser vtk; static QMultiMap parsers() { @@ -85,6 +87,7 @@ static QMultiMap parsers() map.insert("wpt", &twonav); map.insert("wpt", &gpsdump); map.insert("txt", &txt); + map.insert("vtk", &vtk); return map; } @@ -245,6 +248,7 @@ QString Data::formats() + qApp->translate("Data", "SML files") + " (*.sml);;" + qApp->translate("Data", "TCX files") + " (*.tcx);;" + qApp->translate("Data", "70mai GPS log files") + " (*.txt);;" + + qApp->translate("Data", "VTK files") + " (*.vtk);;" + qApp->translate("Data", "TwoNav files") + " (*.rte *.trk *.wpt);;" + qApp->translate("Data", "GPSDump files") + " (*.wpt);;" + qApp->translate("Data", "All files") + " (*)"; diff --git a/src/data/vtkparser.cpp b/src/data/vtkparser.cpp new file mode 100644 index 00000000..523e0798 --- /dev/null +++ b/src/data/vtkparser.cpp @@ -0,0 +1,214 @@ +#include +#include +#include "vtkparser.h" + +#define TYPE(tag) (tag & 0x07) +#define FIELD(tag) (tag >> 3) + +#define VARINT 0 +#define I64 1 +#define LEN 2 +#define I32 5 + +struct CTX +{ + CTX(const QByteArray &ba) + : bp(ba.constData()), be(bp + ba.size()), tag(0) {} + + const char *bp; + const char *be; + quint32 tag; +}; + +static inline qint32 zigzag32decode(quint32 value) +{ + return static_cast((value >> 1u) ^ static_cast( + -static_cast(value & 1u))); +} + +template +static bool varint(CTX &ctx, T &val) +{ + unsigned int shift = 0; + val = 0; + + while (ctx.bp < ctx.be) { + val |= ((quint8)*ctx.bp & 0x7F) << shift; + shift += 7; + if (!((quint8)*ctx.bp++ & 0x80)) + return true; + } + + return false; +} + +static bool length(CTX &ctx, qint32 &val) +{ + if (TYPE(ctx.tag) != LEN) + return false; + + if (!varint(ctx, val)) + return false; + + return (val >= 0); +} + +static bool skip(CTX &ctx) +{ + qint32 len = 0; + + switch (TYPE(ctx.tag)) { + case VARINT: + return varint(ctx, len); + case I64: + len = 8; + break; + case LEN: + if (!varint(ctx, len) || len < 0) + return false; + break; + case I32: + len = 4; + break; + default: + return false; + } + + if (ctx.bp + len > ctx.be) + return false; + ctx.bp += len; + + return true; +} + +static bool trackpoint(CTX &ctx, Trackpoint &t) +{ + qint32 len, lon = 0xFFFFFFF, lat = 0xFFFFFFF; + quint32 val, seconds = 0, centiSeconds = 0, speed = 0; + + if (!length(ctx, len)) + return false; + + const char *ee = ctx.bp + len; + if (ee > ctx.be) + return false; + + while (ctx.bp < ee) { + if (!varint(ctx, ctx.tag)) + return false; + + switch (FIELD(ctx.tag)) { + case 1: + if (TYPE(ctx.tag) != VARINT) + return false; + if (!varint(ctx, seconds)) + return false; + break; + case 2: + if (TYPE(ctx.tag) != VARINT) + return false; + if (!varint(ctx, centiSeconds)) + return false; + break; + case 3: + if (TYPE(ctx.tag) != VARINT) + return false; + if (!varint(ctx, val)) + return false; + lat = zigzag32decode(val); + break; + case 4: + if (TYPE(ctx.tag) != VARINT) + return false; + if (!varint(ctx, val)) + return false; + lon = zigzag32decode(val); + break; + case 5: + if (TYPE(ctx.tag) != VARINT) + return false; + if (!varint(ctx, speed)) + return false; + break; + default: + if (!skip(ctx)) + return false; + } + } + + t.setCoordinates(Coordinates(lon / 1e7, lat / 1e7)); + t.setTimestamp(QDateTime::fromMSecsSinceEpoch( + ((qint64)seconds * 1000) + ((qint64)centiSeconds * 10), + QTimeZone::utc())); + t.setSpeed(speed / 5.1444); + + return (ctx.bp == ee); +} + +static bool record(CTX &ctx, Trackpoint &t) +{ + while (ctx.bp < ctx.be) { + if (!varint(ctx, ctx.tag)) + return false; + + switch (FIELD(ctx.tag)) { + case 1: + if (!trackpoint(ctx, t)) + return false; + break; + default: + if (!skip(ctx)) + return false; + } + } + + return (ctx.bp == ctx.be); +} + +bool VTKParser::parse(QFile *file, QList &tracks, + QList &routes, QList &polygons, QVector &waypoints) +{ + Q_UNUSED(routes); + Q_UNUSED(polygons); + Q_UNUSED(waypoints); + qint64 len; + quint16 recordLen; + QByteArray ba; + SegmentData segment; + Trackpoint t; + + _errorString = ""; + + while (true) { + len = file->read((char*)&recordLen, 2); + if (len < 0) { + _errorString = "I/O error"; + return false; + } else if (len == 0) + break; + + recordLen = qFromLittleEndian(recordLen); + ba.resize(recordLen); + if (file->read(ba.data(), ba.size()) != ba.size()) { + _errorString = "Error reading VTK record"; + return false; + } + + CTX ctx(ba); + t.setCoordinates(Coordinates()); + if (!record(ctx, t)) { + _errorString = "Invalid VTK record"; + return false; + } else { + if (t.coordinates().isValid()) + segment.append(t); + else if (!t.coordinates().isNull()) { + _errorString = "Invalid VTK record coordinates"; + return false; + } + } + } + + tracks.append(segment); + return true; +} diff --git a/src/data/vtkparser.h b/src/data/vtkparser.h new file mode 100644 index 00000000..81e0cab8 --- /dev/null +++ b/src/data/vtkparser.h @@ -0,0 +1,18 @@ +#ifndef VTKPARSER_H +#define VTKPARSER_H + +#include "parser.h" + +class VTKParser : public Parser +{ +public: + bool parse(QFile *file, QList &tracks, QList &routes, + QList &polygons, QVector &waypoints); + QString errorString() const {return _errorString;} + int errorLine() const {return 0;} + +private: + QString _errorString; +}; + +#endif // VTKPARSER_H