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:
parent
ab6ea84255
commit
ecb82952f6
@ -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
66
src/data/csv.cpp
Normal 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
22
src/data/csv.h
Normal 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
|
@ -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
163
src/data/cupparser.cpp
Normal 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
26
src/data/cupparser.h
Normal 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
|
@ -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);;"
|
||||||
|
Loading…
Reference in New Issue
Block a user