mirror of
https://github.com/tumic0/GPXSee.git
synced 2024-12-02 23:49:09 +01:00
Added support for IGC files
This commit is contained in:
parent
9f06b042ca
commit
17c791d753
@ -68,7 +68,8 @@ HEADERS += src/config.h \
|
|||||||
src/path.h \
|
src/path.h \
|
||||||
src/assert.h \
|
src/assert.h \
|
||||||
src/cadencegraph.h \
|
src/cadencegraph.h \
|
||||||
src/powergraph.h
|
src/powergraph.h \
|
||||||
|
src/igcparser.h
|
||||||
SOURCES += src/main.cpp \
|
SOURCES += src/main.cpp \
|
||||||
src/gui.cpp \
|
src/gui.cpp \
|
||||||
src/poi.cpp \
|
src/poi.cpp \
|
||||||
@ -116,7 +117,8 @@ SOURCES += src/main.cpp \
|
|||||||
src/format.cpp \
|
src/format.cpp \
|
||||||
src/graph.cpp \
|
src/graph.cpp \
|
||||||
src/cadencegraph.cpp \
|
src/cadencegraph.cpp \
|
||||||
src/powergraph.cpp
|
src/powergraph.cpp \
|
||||||
|
src/igcparser.cpp
|
||||||
RESOURCES += gpxsee.qrc
|
RESOURCES += gpxsee.qrc
|
||||||
TRANSLATIONS = lang/gpxsee_cs.ts
|
TRANSLATIONS = lang/gpxsee_cs.ts
|
||||||
macx {
|
macx {
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "csvparser.h"
|
#include "csvparser.h"
|
||||||
#include "kmlparser.h"
|
#include "kmlparser.h"
|
||||||
#include "fitparser.h"
|
#include "fitparser.h"
|
||||||
|
#include "igcparser.h"
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
|
|
||||||
|
|
||||||
@ -21,6 +22,8 @@ Data::Data() : _errorLine(0)
|
|||||||
_waypointData));
|
_waypointData));
|
||||||
_parsers.insert("csv", new CSVParser(_trackData, _routeData,
|
_parsers.insert("csv", new CSVParser(_trackData, _routeData,
|
||||||
_waypointData));
|
_waypointData));
|
||||||
|
_parsers.insert("igc", new IGCParser(_trackData, _routeData,
|
||||||
|
_waypointData));
|
||||||
}
|
}
|
||||||
|
|
||||||
Data::~Data()
|
Data::~Data()
|
||||||
|
@ -108,16 +108,17 @@ GUI::~GUI()
|
|||||||
|
|
||||||
const QString GUI::fileFormats() const
|
const QString GUI::fileFormats() const
|
||||||
{
|
{
|
||||||
return tr("Supported files (*.csv *.fit *.gpx *.kml *.tcx)") + ";;"
|
return tr("Supported files (*.csv *.fit *.gpx *.igc *.kml *.tcx)") + ";;"
|
||||||
+ tr("CSV files (*.csv)") + ";;" + tr("FIT files (*.fit)") + ";;"
|
+ tr("CSV files (*.csv)") + ";;" + tr("FIT files (*.fit)") + ";;"
|
||||||
+ tr("GPX files (*.gpx)") + ";;" + tr("KML files (*.kml)") + ";;"
|
+ tr("GPX files (*.gpx)") + ";;" + tr("IGC files (*.igc)") + ";;"
|
||||||
+ tr("TCX files (*.tcx)") + ";;" + tr("All files (*)");
|
+ tr("KML files (*.kml)") + ";;" + tr("TCX files (*.tcx)") + ";;"
|
||||||
|
+ tr("All files (*)");
|
||||||
}
|
}
|
||||||
|
|
||||||
void GUI::createBrowser()
|
void GUI::createBrowser()
|
||||||
{
|
{
|
||||||
QStringList filter;
|
QStringList filter;
|
||||||
filter << "*.gpx" << "*.tcx" << "*.kml" << "*.fit" << "*.csv";
|
filter << "*.gpx" << "*.tcx" << "*.kml" << "*.fit" << "*.csv" << "*.igc";
|
||||||
_browser = new FileBrowser(this);
|
_browser = new FileBrowser(this);
|
||||||
_browser->setFilter(filter);
|
_browser->setFilter(filter);
|
||||||
}
|
}
|
||||||
|
204
src/igcparser.cpp
Normal file
204
src/igcparser.cpp
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
#include <cctype>
|
||||||
|
#include <cstring>
|
||||||
|
#include "igcparser.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int str2int(const char *str, size_t len)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
for (const char *sp = str; sp < str + len; sp++) {
|
||||||
|
if (::isdigit(*sp))
|
||||||
|
res = res * 10 + *sp - '0';
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readLat(const char *line, qreal &lat)
|
||||||
|
{
|
||||||
|
int d = str2int(line + 7, 2);
|
||||||
|
int mi = str2int(line + 9, 2);
|
||||||
|
int mf = str2int(line + 11, 3);
|
||||||
|
if (d < 0 || mi < 0 || mf < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!(line[14] == 'N' || line[14] == 'S'))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
lat = d + (((qreal)mi + (qreal)mf/1000) / 60);
|
||||||
|
if (lat > 90)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (line[14] == 'S')
|
||||||
|
lat = -lat;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readLon(const char *line, qreal &lon)
|
||||||
|
{
|
||||||
|
int d = str2int(line + 15, 3);
|
||||||
|
int mi = str2int(line + 18, 2);
|
||||||
|
int mf = str2int(line + 20, 3);
|
||||||
|
if (d < 0 || mi < 0 || mf < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!(line[23] == 'E' || line[23] == 'W'))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
lon = d + (((qreal)mi + (qreal)mf/1000) / 60);
|
||||||
|
if (lon > 180)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (line[23] == 'W')
|
||||||
|
lon = -lon;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readAltitude(const char *line, qreal &ele)
|
||||||
|
{
|
||||||
|
int p;
|
||||||
|
|
||||||
|
if (!(line[24] == 'A' || line[24] == 'V'))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (line[25] == '-')
|
||||||
|
p = str2int(line + 26, 4);
|
||||||
|
else
|
||||||
|
p = str2int(line + 25, 5);
|
||||||
|
|
||||||
|
int g = str2int(line + 30, 5);
|
||||||
|
if (p < 0 || g < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (line[24] == 'A')
|
||||||
|
ele = (qreal)g;
|
||||||
|
else
|
||||||
|
ele = NAN;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IGCParser::readDate(const char *line)
|
||||||
|
{
|
||||||
|
int d = str2int(line + 5, 2);
|
||||||
|
int m = str2int(line + 7, 2);
|
||||||
|
int y = str2int(line + 9, 2);
|
||||||
|
|
||||||
|
if (y < 0 || m < 0 || d < 0) {
|
||||||
|
_errorString = "Invalid date";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_date = QDate(2000 + y, m, d);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IGCParser::readRecord(const char *line)
|
||||||
|
{
|
||||||
|
qreal lat, lon, ele;
|
||||||
|
QDateTime timestamp;
|
||||||
|
|
||||||
|
|
||||||
|
if (_date.isNull()) {
|
||||||
|
_errorString = "Date header missing";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int h = str2int(line + 1, 2);
|
||||||
|
int m = str2int(line + 3, 2);
|
||||||
|
int s = str2int(line + 5, 2);
|
||||||
|
|
||||||
|
if (h <0 || m < 0 || s < 0) {
|
||||||
|
_errorString = "Invalid timestamp";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QTime time = QTime(h, m, s);
|
||||||
|
if (time < _time)
|
||||||
|
_date.addDays(1);
|
||||||
|
_time = time;
|
||||||
|
timestamp = QDateTime(_date, _time, Qt::UTC);
|
||||||
|
|
||||||
|
if (!readLat(line, lat)) {
|
||||||
|
_errorString = "Invalid latitude";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!readLon(line, lon)) {
|
||||||
|
_errorString = "Invalid longitude";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!readAltitude(line, ele)) {
|
||||||
|
_errorString = "Invalid altitude";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Trackpoint t(Coordinates(lon, lat));
|
||||||
|
t.setTimestamp(timestamp);
|
||||||
|
t.setElevation(ele);
|
||||||
|
_tracks.last().append(t);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IGCParser::loadFile(QFile *file)
|
||||||
|
{
|
||||||
|
qint64 len;
|
||||||
|
char line[76 + 2 + 1 + 1];
|
||||||
|
|
||||||
|
_errorLine = 1;
|
||||||
|
_errorString.clear();
|
||||||
|
|
||||||
|
_tracks.append(TrackData());
|
||||||
|
_time = QTime(0, 0);
|
||||||
|
|
||||||
|
// Read the initial A record
|
||||||
|
if ((len = file->readLine(line, sizeof(line))) < 0) {
|
||||||
|
_errorString = "I/O error";
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (len < 9 || len > (qint64)sizeof(line) - 1 || line[0] != 'A') {
|
||||||
|
_errorString = "Not a IGC file";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 1; i < 7; i++) {
|
||||||
|
if (!::isprint(line[i])) {
|
||||||
|
_errorString = "Not a IGC file";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_errorLine++;
|
||||||
|
|
||||||
|
// Read header (H) records and data (B) records
|
||||||
|
while ((len = file->readLine(line, sizeof(line))) > 0) {
|
||||||
|
if (len < 0) {
|
||||||
|
_errorString = "I/O error";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (len > (qint64)sizeof(line) - 1) {
|
||||||
|
_errorString = "Line limit exceeded";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line[0] == 'B') {
|
||||||
|
if (len > 35)
|
||||||
|
if (!readRecord(line))
|
||||||
|
return false;
|
||||||
|
} else if (line[0] == 'H') {
|
||||||
|
if (len > 10 && !::strncmp(line + 1, "FDTE", 4))
|
||||||
|
if (!readDate(line))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_errorLine++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
31
src/igcparser.h
Normal file
31
src/igcparser.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef IGCPARSER_H
|
||||||
|
#define IGCPARSER_H
|
||||||
|
|
||||||
|
#include <QDate>
|
||||||
|
#include <QTime>
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
|
||||||
|
class IGCParser : public Parser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IGCParser(QList<TrackData> &tracks, QList<RouteData> &routes,
|
||||||
|
QList<Waypoint> &waypoints) : Parser(tracks, routes, waypoints) {}
|
||||||
|
~IGCParser() {}
|
||||||
|
|
||||||
|
bool loadFile(QFile *file);
|
||||||
|
QString errorString() const {return _errorString;}
|
||||||
|
int errorLine() const {return _errorLine;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool readDate(const char *line);
|
||||||
|
bool readRecord(const char *line);
|
||||||
|
|
||||||
|
int _errorLine;
|
||||||
|
QString _errorString;
|
||||||
|
|
||||||
|
QDate _date;
|
||||||
|
QTime _time;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IGCPARSER_H
|
Loading…
Reference in New Issue
Block a user