diff --git a/gpxsee.pro b/gpxsee.pro index 4e59c8e8..f9fcd0ad 100644 --- a/gpxsee.pro +++ b/gpxsee.pro @@ -112,6 +112,7 @@ HEADERS += src/common/config.h \ src/GUI/pngexportdialog.h \ src/GUI/timezoneinfo.h \ src/GUI/passwordedit.h \ + src/data/twonavparser.h \ src/map/proj/polyconic.h \ src/map/proj/webmercator.h \ src/map/proj/transversemercator.h \ @@ -312,6 +313,7 @@ SOURCES += src/main.cpp \ src/GUI/pngexportdialog.cpp \ src/GUI/projectioncombobox.cpp \ src/GUI/passwordedit.cpp \ + src/data/twonavparser.cpp \ src/map/proj/polyconic.cpp \ src/map/proj/webmercator.cpp \ src/map/proj/transversemercator.cpp \ diff --git a/src/data/data.cpp b/src/data/data.cpp index babf1d5e..c627908a 100644 --- a/src/data/data.cpp +++ b/src/data/data.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include "gpxparser.h" #include "tcxparser.h" #include "csvparser.h" @@ -20,6 +19,7 @@ #include "ov2parser.h" #include "itnparser.h" #include "onmoveparsers.h" +#include "twonavparser.h" #include "data.h" @@ -44,10 +44,11 @@ static OV2Parser ov2; static ITNParser itn; static OMDParser omd; static GHPParser ghp; +static TwoNavParser twonav; -static QMap parsers() +static QMultiMap parsers() { - QMap map; + QMultiMap map; map.insert("gpx", &gpx); map.insert("tcx", &tcx); @@ -72,11 +73,14 @@ static QMap parsers() map.insert("itn", &itn); map.insert("omd", &omd); map.insert("ghp", &ghp); + map.insert("trk", &twonav); + map.insert("rte", &twonav); + map.insert("wpt", &twonav); return map; } -QMap Data::_parsers = parsers(); +QMultiMap Data::_parsers = parsers(); void Data::processData(QList &trackData, QList &routeData) { @@ -101,16 +105,21 @@ Data::Data(const QString &fileName, bool tryUnknown) return; } - QMap::iterator it; - if ((it = _parsers.find(fi.suffix().toLower())) != _parsers.end()) { - if (it.value()->parse(&file, trackData, routeData, _polygons, - _waypoints)) { - processData(trackData, routeData); - _valid = true; - return; - } else { - _errorLine = it.value()->errorLine(); - _errorString = it.value()->errorString(); + QMultiMap::iterator it; + QString suffix(fi.suffix().toLower()); + if ((it = _parsers.find(suffix)) != _parsers.end()) { + while (it != _parsers.end() && it.key() == suffix) { + if (it.value()->parse(&file, trackData, routeData, _polygons, + _waypoints)) { + processData(trackData, routeData); + _valid = true; + return; + } else { + _errorLine = it.value()->errorLine(); + _errorString = it.value()->errorString(); + } + file.reset(); + ++it; } } else if (tryUnknown) { for (it = _parsers.begin(); it != _parsers.end(); it++) { @@ -137,6 +146,7 @@ QString Data::formats() { return qApp->translate("Data", "Supported files") + " (" + filter().join(" ") + ");;" + + qApp->translate("Data", "TwoNav files") + " (*.rte *.trk *.wpt);;" + qApp->translate("Data", "CSV files") + " (*.csv);;" + qApp->translate("Data", "CUP files") + " (*.cup);;" + qApp->translate("Data", "FIT files") + " (*.fit);;" @@ -162,7 +172,7 @@ QStringList Data::filter() { QStringList filter; - for (QMap::iterator it = _parsers.begin(); + for (QMultiMap::iterator it = _parsers.begin(); it != _parsers.end(); it++) filter << "*." + it.key(); diff --git a/src/data/data.h b/src/data/data.h index 76cb5ccd..ba332c84 100644 --- a/src/data/data.h +++ b/src/data/data.h @@ -2,7 +2,7 @@ #define DATA_H #include -#include +#include #include #include #include "waypoint.h" @@ -39,7 +39,7 @@ private: QList _polygons; QVector _waypoints; - static QMap _parsers; + static QMultiMap _parsers; }; #endif // DATA_H diff --git a/src/data/twonavparser.cpp b/src/data/twonavparser.cpp new file mode 100644 index 00000000..02f7de9b --- /dev/null +++ b/src/data/twonavparser.cpp @@ -0,0 +1,217 @@ +#include "common/textcodec.h" +#include "map/gcs.h" +#include "twonavparser.h" + +static double lon(const QString &str) +{ + QStringList l(str.split(QChar(0xBA))); + if (l.size() < 2) + return NAN; + + bool ok; + double val = l.at(0).toDouble(&ok); + if (!ok) + return NAN; + + if (l.at(1) == "W") + return -val; + else if (l.at(1) == "E") + return val; + else + return NAN; +} + +static double lat(const QString &str) +{ + QStringList l(str.split(QChar(0xBA))); + if (l.size() < 2) + return NAN; + + bool ok; + double val = l.at(0).toDouble(&ok); + if (!ok) + return NAN; + + if (l.at(1) == "S") + return -val; + else if (l.at(1) == "N") + return val; + else + return NAN; +} + +static QDateTime timestamp(const QString &dateStr, const QString &timeStr) +{ + QLocale l("C"); + + QDate date(l.toDate(dateStr, "dd-MMM-yy")); + if (date.isValid()) + date = date.addYears(100); + else { + date = l.toDate(dateStr, "dd-MMM-yyyy"); + if (!date.isValid()) + return QDateTime(); + } + + QTime time(l.toTime(timeStr, "H:m:s.z")); + if (!time.isValid()) { + time = l.toTime(timeStr, "H:m:s"); + if (!time.isValid()) + return QDateTime(); + } + + return QDateTime(date, time); +} + +bool TwoNavParser::parse(QFile *file, QList &tracks, + QList &routes, QList &polygons, + QVector &waypoints) +{ + Q_UNUSED(polygons); + TextCodec codec; + GCS gcs; + bool ok, route = false, track = false, waypoint = false; + + _errorLine = 1; + _errorString.clear(); + + quint8 bom[3]; + if (file->peek((char*)bom, sizeof(bom)) == sizeof(bom) + && bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) { + file->seek(3); + codec = TextCodec(65001); + } + + while (!file->atEnd()) { + QByteArray line(file->readLine().trimmed()); + if (!line.size()) + continue; + + switch (line.at(0)) { + case 'B': + {line.remove(0, 1); + QByteArray encoding(line.trimmed()); + if (encoding == "UTF-8") + codec = TextCodec(65001); + else { + _errorString = "Invalid/unknown encoding"; + return false; + }} + break; + case 'G': + {line.remove(0, 1); + QString datum(line.trimmed()); + gcs = GCS::gcs(datum); + if (gcs.isNull()) { + _errorString = "Invalid/unknown datum"; + return false; + }} + break; + case 'U': + {line.remove(0, 1); + QByteArray cs(line.trimmed()); + if (cs != "1") { + _errorString = "Invalid/unknown coordinate system"; + return false; + }} + break; + case 'T': + {QStringList list(codec.toString(line).split(' ', + Qt::SkipEmptyParts)); + if (list.size() < 4) { + _errorString = "Parse error"; + return false; + } + Coordinates c(lon(list.at(3)), lat(list.at(2))); + if (!c.isValid()) { + _errorString = "Invalid coordinates"; + return false; + } + + Trackpoint t(gcs.toWGS84(c)); + + if (list.size() > 5) { + QDateTime ts(timestamp(list.at(4), list.at(5))); + if (!ts.isValid()) { + _errorString = "Invalid date/time"; + return false; + } + t.setTimestamp(ts); + } + if (list.size() > 7) { + qreal elevation = list.at(7).toDouble(&ok); + if (!ok) { + _errorString = "Invalid altitude"; + return false; + } + t.setElevation(elevation); + } + + if (!track) { + tracks.append(TrackData()); + tracks.last().append(SegmentData()); + track = true; + } + + tracks.last().last().append(t);} + break; + case 'W': + {QStringList list(codec.toString(line).split(' ', + Qt::SkipEmptyParts)); + if (list.size() < 5) { + _errorString = "Parse error"; + return false; + } + Coordinates c(lon(list.at(4)), lat(list.at(3))); + if (!c.isValid()) { + _errorString = "Invalid coordinates"; + return false; + } + + Waypoint w(gcs.toWGS84(c)); + w.setName(list.at(1)); + + if (list.size() > 6) { + QDateTime ts(timestamp(list.at(5), list.at(6))); + if (!ts.isValid()) { + _errorString = "Invalid date/time"; + return false; + } + w.setTimestamp(ts); + } + if (list.size() > 7) { + qreal elevation = list.at(7).toDouble(&ok); + if (!ok) { + _errorString = "Invalid altitude"; + return false; + } + w.setElevation(elevation); + } + if (list.size() > 8) + w.setDescription(list.mid(8).join(' ')); + + if (route) + routes.last().append(w); + else { + waypoints.append(w); + waypoint = true; + }} + break; + case 'R': + {QStringList list(codec.toString(line).split(',', + Qt::SkipEmptyParts)); + routes.append(RouteData()); + routes.last().setName(list.at(1)); + route = true;} + break; + } + + _errorLine++; + } + + if (!(waypoint | route | track)) { + _errorString = "No valid data found"; + return false; + } else + return true; +} diff --git a/src/data/twonavparser.h b/src/data/twonavparser.h new file mode 100644 index 00000000..5f40c7dd --- /dev/null +++ b/src/data/twonavparser.h @@ -0,0 +1,21 @@ +#ifndef TWONAVPARSER_H +#define TWONAVPARSER_H + +#include "parser.h" + +class TwoNavParser : public Parser +{ +public: + TwoNavParser() : _errorLine(0) {} + + bool parse(QFile *file, QList &tracks, QList &routes, + QList &polygons, QVector &waypoints); + QString errorString() const {return _errorString;} + int errorLine() const {return _errorLine;} + +private: + QString _errorString; + int _errorLine; +}; + +#endif // TWONAVPARSER_H