1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-30 22:51:16 +01:00

Added support for SeeYou CUP files

This commit is contained in:
Martin Tůma 2019-08-15 21:27:55 +02:00
parent ab6ea84255
commit ecb82952f6
7 changed files with 305 additions and 23 deletions

View File

@ -181,7 +181,9 @@ HEADERS += src/common/config.h \
src/GUI/limitedcombobox.h \ src/GUI/limitedcombobox.h \
src/GUI/pathtickitem.h \ src/GUI/pathtickitem.h \
src/map/IMG/textitem.h \ src/map/IMG/textitem.h \
src/map/IMG/label.h src/map/IMG/label.h \
src/data/csv.h \
src/data/cupparser.h
SOURCES += src/main.cpp \ SOURCES += src/main.cpp \
src/common/coordinates.cpp \ src/common/coordinates.cpp \
src/common/rectc.cpp \ src/common/rectc.cpp \
@ -312,7 +314,9 @@ SOURCES += src/main.cpp \
src/map/IMG/style.cpp \ src/map/IMG/style.cpp \
src/map/IMG/netfile.cpp \ src/map/IMG/netfile.cpp \
src/GUI/pathtickitem.cpp \ src/GUI/pathtickitem.cpp \
src/map/IMG/textitem.cpp src/map/IMG/textitem.cpp \
src/data/csv.cpp \
src/data/cupparser.cpp
greaterThan(QT_MAJOR_VERSION, 4) { greaterThan(QT_MAJOR_VERSION, 4) {
HEADERS += src/data/geojsonparser.h HEADERS += src/data/geojsonparser.h

66
src/data/csv.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "csv.h"
/*
RFC 4180 parser with the following enchancements:
- allows an arbitrary delimiter
- allows LF line ends in addition to CRLF line ends
*/
bool CSV::readEntry(QStringList &list)
{
int state = 0;
char c;
QByteArray field;
while (_device->getChar(&c)) {
switch (state) {
case 0:
if (c == '\r')
state = 3;
else if (c == '\n') {
list.append(field);
_line++;
return true;
} else if (c == _delimiter) {
list.append(field);
field.clear();
} else if (c == '"') {
if (!field.isEmpty())
return false;
state = 1;
} else
field.append(c);
break;
case 1:
if (c == '"')
state = 2;
else {
field.append(c);
if (c == '\n')
_line++;
}
break;
case 2:
if (c == '"') {
field.append('"');
state = 1;
} else if (c == _delimiter || c == '\r' || c == '\n') {
_device->ungetChar(c);
state = 0;
} else
return false;
break;
case 3:
if (c == '\n') {
_device->ungetChar(c);
state = 0;
} else
return false;
break;
}
}
list.append(field);
return (state == 0);
}

22
src/data/csv.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef CSV_H
#define CSV_H
#include <QIODevice>
class CSV
{
public:
CSV(QIODevice *device, char delimiter = ',')
: _device(device), _delimiter(delimiter), _line(1) {}
bool readEntry(QStringList &list);
bool atEnd() const {return _device->atEnd();}
int line() const {return _line;}
private:
QIODevice *_device;
char _delimiter;
int _line;
};
#endif // CSV_H

View File

@ -1,3 +1,4 @@
#include "csv.h"
#include "csvparser.h" #include "csvparser.h"
bool CSVParser::parse(QFile *file, QList<TrackData> &tracks, bool CSVParser::parse(QFile *file, QList<TrackData> &tracks,
@ -7,42 +8,38 @@ bool CSVParser::parse(QFile *file, QList<TrackData> &tracks,
Q_UNUSED(tracks); Q_UNUSED(tracks);
Q_UNUSED(routes); Q_UNUSED(routes);
Q_UNUSED(polygons); Q_UNUSED(polygons);
bool res; CSV csv(file);
QStringList entry;
bool ok;
_errorLine = 1;
_errorString.clear();
while (!file->atEnd()) { while (!csv.atEnd()) {
QByteArray line = file->readLine(); if (!csv.readEntry(entry) || entry.size() < 3) {
QList<QByteArray> list = line.split(',');
if (list.size() < 3) {
_errorString = "Parse error"; _errorString = "Parse error";
_errorLine = csv.line();
return false; return false;
} }
qreal lon = list[0].trimmed().toDouble(&res); double lon = entry.at(0).trimmed().toDouble(&ok);
if (!res || (lon < -180.0 || lon > 180.0)) { if (!ok || (lon < -180.0 || lon > 180.0)) {
_errorString = "Invalid longitude"; _errorString = "Invalid longitude";
_errorLine = csv.line();
return false; return false;
} }
qreal lat = list[1].trimmed().toDouble(&res); double lat = entry.at(1).trimmed().toDouble(&ok);
if (!res || (lat < -90.0 || lat > 90.0)) { if (!ok || (lat < -90.0 || lat > 90.0)) {
_errorString = "Invalid latitude"; _errorString = "Invalid latitude";
_errorLine = csv.line();
return false; return false;
} }
Waypoint wp(Coordinates(lon, lat)); Waypoint wp(Coordinates(lon, lat));
wp.setName(entry.at(2).trimmed());
QByteArray ba = list[2].trimmed(); if (entry.size() > 3)
QString name = QString::fromUtf8(ba.data(), ba.size()); wp.setDescription(entry.at(3).trimmed());
wp.setName(name);
if (list.size() > 3) {
ba = list[3].trimmed();
wp.setDescription(QString::fromUtf8(ba.data(), ba.size()));
}
waypoints.append(wp); waypoints.append(wp);
_errorLine++;
entry.clear();
} }
return true; return true;

163
src/data/cupparser.cpp Normal file
View File

@ -0,0 +1,163 @@
#include <cmath>
#include "csv.h"
#include "cupparser.h"
enum SegmentType {
Header, Waypoints, Tasks
};
static double latitude(const QString &str)
{
bool ok;
if (str.length() != 9)
return NAN;
int deg = str.left(2).toInt(&ok);
if (!ok || deg > 90)
return NAN;
double min = str.mid(2, 6).toDouble(&ok);
if (!ok || min > 60)
return NAN;
double dd = deg + min/60.0;
return (str.right(1) == 'S') ? -dd : dd;
}
static double longitude(const QString &str)
{
bool ok;
if (str.length() != 10)
return NAN;
int deg = str.left(3).toInt(&ok);
if (!ok || deg > 180)
return NAN;
double min = str.mid(3, 6).toDouble(&ok);
if (!ok || min > 60)
return NAN;
double dd = deg + min/60.0;
return (str.right(1) == 'W') ? -dd : dd;
}
static double elevation(const QString &str)
{
bool ok;
double ele;
if (str.right(2) == "ft")
ele = str.left(str.length() - 2).toDouble(&ok) * 0.3048;
else if (str.right(1) == "m")
ele = str.left(str.length() - 1).toDouble(&ok);
else
return NAN;
return ok ? ele : NAN;
}
bool CUPParser::waypoint(const QStringList &entry, QVector<Waypoint> &waypoints,
QMap<QString, Coordinates> &turnpoints)
{
if (entry.size() < 11) {
_errorString = "Invalid number of fields";
return false;
}
double lon = longitude(entry.at(4));
if (std::isnan(lon)) {
_errorString = "Invalid longitude";
return false;
}
double lat = latitude(entry.at(3));
if (std::isnan(lat)) {
_errorString = "Invalid latitude";
return false;
}
Waypoint wp(Coordinates(lon, lat));
wp.setName(entry.at(0));
wp.setDescription(entry.at(10));
wp.setElevation(elevation(entry.at(5)));
waypoints.append(wp);
turnpoints.insert(wp.name(), wp.coordinates());
return true;
}
bool CUPParser::task(const QStringList &entry, QList<RouteData> &routes,
const QMap<QString, Coordinates> &turnpoints)
{
if (entry.size() < 2) {
_errorString = "Invalid number of fields";
return false;
}
RouteData r;
r.setName(entry.at(0));
for (int i = 1; i < entry.size(); i++) {
if (!turnpoints.contains(entry.at(i))) {
_errorString = entry.at(i) + ": unknown turnpoint";
return false;
}
Waypoint w(turnpoints[entry.at(i)]);
w.setName(entry.at(i));
r.append(w);
}
routes.append(r);
return true;
}
bool CUPParser::parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Area> &polygons,
QVector<Waypoint> &waypoints)
{
Q_UNUSED(tracks);
Q_UNUSED(polygons);
CSV csv(file);
SegmentType st = Header;
QStringList entry;
QMap<QString, Coordinates> turnpoints;
while (!csv.atEnd()) {
if (!csv.readEntry(entry)) {
_errorString = "CSV parse error";
_errorLine = csv.line();
return false;
}
if (st == Header) {
st = Waypoints;
if (entry.size() >= 11 && entry.at(3) == "lat"
&& entry.at(4) == "lon") {
entry.clear();
continue;
}
} else if (st == Waypoints && entry.size() == 1
&& entry.at(0) == "-----Related Tasks-----") {
st = Tasks;
entry.clear();
continue;
}
if (st == Waypoints) {
if (!waypoint(entry, waypoints, turnpoints))
return false;
} else if (st == Tasks) {
if (entry.at(0) != "Options" && entry.at(0) != "ObsZone"
&& !task(entry, routes, turnpoints))
return false;
}
entry.clear();
_errorLine = csv.line();
}
return true;
}

26
src/data/cupparser.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef CUPPARSER_H
#define CUPPARSER_H
#include "parser.h"
class CUPParser : public Parser
{
public:
CUPParser() : _errorLine(0) {}
bool parse(QFile *file, QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Area> &polygons, QVector<Waypoint> &waypoints);
QString errorString() const {return _errorString;}
int errorLine() const {return _errorLine;}
private:
bool waypoint(const QStringList &entry, QVector<Waypoint> &waypoints,
QMap<QString, Coordinates> &turnpoints);
bool task(const QStringList &entry, QList<RouteData> &routes,
const QMap<QString, Coordinates> &turnpoints);
QString _errorString;
int _errorLine;
};
#endif // CUPPARSER_H

View File

@ -17,6 +17,7 @@
#include "geojsonparser.h" #include "geojsonparser.h"
#endif // ENABLE_GEOJSON #endif // ENABLE_GEOJSON
#include "exifparser.h" #include "exifparser.h"
#include "cupparser.h"
#include "dem.h" #include "dem.h"
#include "data.h" #include "data.h"
@ -37,6 +38,7 @@ static SLFParser slf;
static GeoJSONParser geojson; static GeoJSONParser geojson;
#endif // ENABLE_GEOJSON #endif // ENABLE_GEOJSON
static EXIFParser exif; static EXIFParser exif;
static CUPParser cup;
static QHash<QString, Parser*> parsers() static QHash<QString, Parser*> parsers()
{ {
@ -60,6 +62,7 @@ static QHash<QString, Parser*> parsers()
#endif // ENABLE_GEOJSON #endif // ENABLE_GEOJSON
hash.insert("jpeg", &exif); hash.insert("jpeg", &exif);
hash.insert("jpg", &exif); hash.insert("jpg", &exif);
hash.insert("cup", &cup);
return hash; return hash;
} }
@ -147,6 +150,7 @@ QString Data::formats()
return return
qApp->translate("Data", "Supported files") + " (" + supported + ");;" qApp->translate("Data", "Supported files") + " (" + supported + ");;"
+ qApp->translate("Data", "CSV files") + " (*.csv);;" + qApp->translate("Data", "CSV files") + " (*.csv);;"
+ qApp->translate("Data", "CUP files") + " (*.cup);;"
+ qApp->translate("Data", "FIT files") + " (*.fit);;" + qApp->translate("Data", "FIT files") + " (*.fit);;"
#ifdef ENABLE_GEOJSON #ifdef ENABLE_GEOJSON
+ qApp->translate("Data", "GeoJSON files") + " (*.geojson *.json);;" + qApp->translate("Data", "GeoJSON files") + " (*.geojson *.json);;"