mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
core: move GPX parsing into core
This shouldn't be part of the desktop UI code; there's still the issue that we really shouldn't hand code XML parsing, but I'll leave that for later. Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
parent
1ecd5065a0
commit
ee5a156498
6 changed files with 177 additions and 160 deletions
|
@ -119,6 +119,7 @@ set(SUBSURFACE_CORE_LIB_SRCS
|
|||
metrics.cpp
|
||||
metrics.h
|
||||
ostctools.c
|
||||
parse-gpx.cpp
|
||||
parse-xml.c
|
||||
parse.c
|
||||
parse.h
|
||||
|
|
146
core/parse-gpx.cpp
Normal file
146
core/parse-gpx.cpp
Normal file
|
@ -0,0 +1,146 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "core/parse-gpx.h"
|
||||
#include "core/dive.h"
|
||||
|
||||
// TODO: instead of manually parsing the XML, we should consider using
|
||||
// one of the two XML frameworks that we already use elsewhere in
|
||||
// the code
|
||||
|
||||
// Read text from the present position in the file until
|
||||
// the first 'delim' character is encountered.
|
||||
static int getSubstring(QFile *file, QString *bufptr, char delim)
|
||||
{
|
||||
char c;
|
||||
bufptr->clear();
|
||||
do {
|
||||
if (file->read(&c, 1) <= 0) // EOF
|
||||
return 1;
|
||||
if (c == delim) break;
|
||||
bufptr->append(QChar(c));
|
||||
} while (c != delim);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find the next occurence of a specified target GPX element in the file,
|
||||
// characerised by a "<xxx " or "<xxx>" character sequence.
|
||||
// 'target' specifies the name of the element searched for.
|
||||
// termc is the ending character of the element name search = '>' or ' '.
|
||||
static int findXmlElement(QFile *fileptr, QString target, QString *bufptr, char termc)
|
||||
{
|
||||
bool match = false;
|
||||
char c;
|
||||
char skipdelim = (termc == ' ') ? '>' : ' ';
|
||||
do { // Skip input until first start new of element (<) is encountered:
|
||||
if (getSubstring(fileptr, bufptr, '<'))
|
||||
return 1; // EOF
|
||||
bufptr->clear();
|
||||
do { // Read name of following element and store it in buf
|
||||
if (fileptr->read(&c, 1) <= 0) // EOF encountered
|
||||
return 1;
|
||||
if ((c == '>') || (c == ' ')) // found a valid delimiter
|
||||
break;
|
||||
bufptr->append(QChar(c));
|
||||
} while ((c != '>') && (c != ' '));
|
||||
if (*bufptr == "/trk") // End of GPS track found: return EOF
|
||||
return 1;
|
||||
if (c == skipdelim)
|
||||
continue; // if inappropriate delimiter was found, redo from start
|
||||
if (*bufptr == target) // Compare xml element name from gpx file with the
|
||||
match = true; // the target element searched for.
|
||||
} while (match == false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find the coordinates at the time specified in coords.start_dive
|
||||
// by searching the gpx file "fileName". Here is a typical trkpt element in GPX:
|
||||
// <trkpt lat="-26.84" lon="32.88"><ele>-53.7</ele><time>2017-08-06T04:56:42Z</time></trkpt>
|
||||
int getCoordsFromGPXFile(struct dive_coords *coords, QString fileName)
|
||||
{
|
||||
struct tm tm1;
|
||||
time_t when = 0;
|
||||
double lon, lat;
|
||||
int line = 0;
|
||||
int64_t time_offset = coords->settingsDiff_offset + coords->timeZone_offset;
|
||||
time_t divetime;
|
||||
bool first_line = true;
|
||||
bool found = false;
|
||||
divetime = coords->start_dive;
|
||||
QString buf;
|
||||
QFile gpxFile;
|
||||
gpxFile.setFileName(fileName);
|
||||
if (!gpxFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QByteArray local8bitBAString1 = fileName.toLocal8Bit();
|
||||
char *fname = local8bitBAString1.data(); // convert QString to a C string fileName
|
||||
fprintf(stderr, "GPS file open error: file name = %s\n", fname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef GPSDEBUG
|
||||
struct tm time; // decode the time of start of dive:
|
||||
utc_mkdate(divetime, &time);
|
||||
int dyr,dmon,dday,dhr,dmin;
|
||||
dyr = time.tm_year;
|
||||
dmon = time.tm_mon;
|
||||
dday = time.tm_mday;
|
||||
dhr = time.tm_hour;
|
||||
dmin = time.tm_min;
|
||||
#endif
|
||||
|
||||
do {
|
||||
line++; // this is the sequence number of the trkpt xml element processed
|
||||
// Find next trkpt xml element (This function also detects </trk> that signals EOF):
|
||||
if (findXmlElement(&gpxFile, QString("trkpt"), &buf, ' ')) // This is the normal exit point
|
||||
break; // for this routine
|
||||
// == Get coordinates: ==
|
||||
if (getSubstring(&gpxFile, &buf, '"')) // read up to the end of the "lat=" label
|
||||
break; // on EOF
|
||||
if (buf != "lat=") {
|
||||
fprintf(stderr, "GPX parse error: cannot find latitude (trkpt #%d)\n", line);
|
||||
return 1;
|
||||
}
|
||||
if (getSubstring(&gpxFile, &buf, '"')) // read up to the end of the latitude value
|
||||
break; // on EOF
|
||||
lat = buf.toDouble(); // Convert lat to decimal
|
||||
if (getSubstring(&gpxFile, &buf, ' ')) // Read past space char
|
||||
break; // on EOF
|
||||
if (getSubstring(&gpxFile, &buf, '"')) // Read up to end of "lon=" label
|
||||
break; // on EOF
|
||||
if (buf != "lon=") {
|
||||
fprintf(stderr, "GPX parse error: cannot find longitude (trkpt #%d)\n", line);
|
||||
return 1;
|
||||
}
|
||||
if (getSubstring(&gpxFile, &buf, '"')) // get string with longitude
|
||||
break; // on EOF
|
||||
lon = buf.toDouble(); // Convert longitude to decimal
|
||||
// == get time: ==
|
||||
if (findXmlElement(&gpxFile, QString("time"), &buf, '>')) // Find the <time> element
|
||||
break; // on EOF
|
||||
if (getSubstring(&gpxFile, &buf, '<')) // Read the string containing date/time
|
||||
break; // on EOF
|
||||
bool ok;
|
||||
tm1.tm_year = buf.left(4).toInt(&ok, 10); // Extract the different substrings:
|
||||
tm1.tm_mon = buf.mid(5,2).toInt(&ok,10) - 1;
|
||||
tm1.tm_mday = buf.mid(8,2).toInt(&ok,10);
|
||||
tm1.tm_hour = buf.mid(11,2).toInt(&ok,10);
|
||||
tm1.tm_min = buf.mid(14,2).toInt(&ok,10);
|
||||
tm1.tm_sec = buf.mid(17,2).toInt(&ok,10);
|
||||
when = utc_mktime(&tm1) + time_offset;
|
||||
if (first_line) {
|
||||
first_line = false;
|
||||
coords->start_track = when; // Local time of start of GPS track
|
||||
}
|
||||
if ((when > divetime && found == false)) { // This GPS local time corresponds to the start time of the dive
|
||||
coords->lon = lon; // save the coordinates
|
||||
coords->lat = lat;
|
||||
found = true;
|
||||
}
|
||||
#ifdef GPSDEBUG
|
||||
utc_mkdate(when, &time); // print time and coordinates of each of the trkpt elements of the GPX file
|
||||
fprintf(stderr, " %02d: lat=%f lon=%f timestamp=%ld (%ld) %02d/%02d/%02d %02d:%02d dt=%ld %02d/%02d/%02d %02d:%02d\n", line, lat,
|
||||
lon, when, time_offset, time.tm_year, time.tm_mon+1, time.tm_mday, time.tm_hour, time.tm_min, divetime, dyr, dmon+1, dday,dhr, dmin );
|
||||
#endif
|
||||
} while (true); // This loop executes until EOF causes a break out of the loop
|
||||
coords->end_track = when; // This is the local time of the end of the GPS track
|
||||
gpxFile.close();
|
||||
return 0;
|
||||
}
|
22
core/parse-gpx.h
Normal file
22
core/parse-gpx.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#ifndef PARSE_GPX_H
|
||||
#define PARSE_GPX_H
|
||||
|
||||
|
||||
#include <QFile>
|
||||
|
||||
struct dive_coords { // This structure holds important information after parsing the GPX file:
|
||||
time_t start_dive; // Start time of the current dive, obtained using current_dive (local time)
|
||||
time_t end_dive; // End time of current dive (local time)
|
||||
time_t start_track; // Start time of GPX track (UCT)
|
||||
time_t end_track; // End time of GPX track (UCT)
|
||||
double lon; // Longitude of the first trackpoint after the start of the dive
|
||||
double lat; // Latitude of the first trackpoint after the start of the dive
|
||||
int64_t settingsDiff_offset; // Local time difference between dive computer and GPS equipment
|
||||
int64_t timeZone_offset; // UCT international time zone offset of dive site
|
||||
};
|
||||
|
||||
int getCoordsFromGPXFile(dive_coords *coords, QString fileName);
|
||||
|
||||
#endif
|
||||
|
|
@ -39,145 +39,6 @@ void ImportGPS::buttonClicked(QAbstractButton *button)
|
|||
}
|
||||
}
|
||||
|
||||
// Read text from the present position in the file until
|
||||
// the first 'delim' character is encountered.
|
||||
int ImportGPS::getSubstring(QFile *file, QString *bufptr, char delim)
|
||||
{
|
||||
char c;
|
||||
bufptr->clear();
|
||||
do {
|
||||
if (file->read(&c, 1) <= 0) // EOF
|
||||
return 1;
|
||||
if (c == delim) break;
|
||||
bufptr->append(QChar(c));
|
||||
} while (c != delim);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find the next occurence of a specified target GPX element in the file,
|
||||
// characerised by a "<xxx " or "<xxx>" character sequence.
|
||||
// 'target' specifies the name of the element searched for.
|
||||
// termc is the ending character of the element name search = '>' or ' '.
|
||||
int ImportGPS::findXmlElement(QFile *fileptr, QString target, QString *bufptr, char termc)
|
||||
{
|
||||
bool match = false;
|
||||
char c;
|
||||
char skipdelim = (termc == ' ') ? '>' : ' ';
|
||||
do { // Skip input until first start new of element (<) is encountered:
|
||||
if (getSubstring(fileptr, bufptr, '<'))
|
||||
return 1; // EOF
|
||||
bufptr->clear();
|
||||
do { // Read name of following element and store it in buf
|
||||
if (fileptr->read(&c, 1) <= 0) // EOF encountered
|
||||
return 1;
|
||||
if ((c == '>') || (c == ' ')) // found a valid delimiter
|
||||
break;
|
||||
bufptr->append(QChar(c));
|
||||
} while ((c != '>') && (c != ' '));
|
||||
if (*bufptr == "/trk") // End of GPS track found: return EOF
|
||||
return 1;
|
||||
if (c == skipdelim)
|
||||
continue; // if inappropriate delimiter was found, redo from start
|
||||
if (*bufptr == target) // Compare xml element name from gpx file with the
|
||||
match = true; // the target element searched for.
|
||||
} while (match == false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find the coordinates at the time specified in coords.start_dive
|
||||
// by searching the gpx file "fileName". Here is a typical trkpt element in GPX:
|
||||
// <trkpt lat="-26.84" lon="32.88"><ele>-53.7</ele><time>2017-08-06T04:56:42Z</time></trkpt>
|
||||
int ImportGPS::getCoordsFromFile()
|
||||
{
|
||||
struct tm tm1;
|
||||
time_t when = 0;
|
||||
double lon, lat;
|
||||
int line = 0;
|
||||
int64_t time_offset = coords.settingsDiff_offset + coords.timeZone_offset;
|
||||
time_t divetime;
|
||||
bool first_line = true;
|
||||
bool found = false;
|
||||
divetime = coords.start_dive;
|
||||
QString buf;
|
||||
QFile f1;
|
||||
f1.setFileName(fileName);
|
||||
if (!f1.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QByteArray local8bitBAString1 = fileName.toLocal8Bit();
|
||||
char *fname = local8bitBAString1.data(); // convert QString to a C string fileName
|
||||
fprintf(stderr, "GPS file open error: file name = %s\n", fname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef GPSDEBUG
|
||||
struct tm time; // decode the time of start of dive:
|
||||
utc_mkdate(divetime, &time);
|
||||
int dyr,dmon,dday,dhr,dmin;
|
||||
dyr = time.tm_year;
|
||||
dmon = time.tm_mon;
|
||||
dday = time.tm_mday;
|
||||
dhr = time.tm_hour;
|
||||
dmin = time.tm_min;
|
||||
#endif
|
||||
|
||||
do {
|
||||
line++; // this is the sequence number of the trkpt xml element processed
|
||||
// Find next trkpt xml element (This function also detects </trk> that signals EOF):
|
||||
if (findXmlElement(&f1, QString("trkpt"), &buf, ' ')) // This is the normal exit point
|
||||
break; // for this routine
|
||||
// == Get coordinates: ==
|
||||
if (getSubstring(&f1, &buf, '"')) // read up to the end of the "lat=" label
|
||||
break; // on EOF
|
||||
if (buf != "lat=") {
|
||||
fprintf(stderr, "GPX parse error: cannot find latitude (trkpt #%d)\n", line);
|
||||
return 1;
|
||||
}
|
||||
if (getSubstring(&f1, &buf, '"')) // read up to the end of the latitude value
|
||||
break; // on EOF
|
||||
lat = buf.toDouble(); // Convert lat to decimal
|
||||
if (getSubstring(&f1, &buf, ' ')) // Read past space char
|
||||
break; // on EOF
|
||||
if (getSubstring(&f1, &buf, '"')) // Read up to end of "lon=" label
|
||||
break; // on EOF
|
||||
if (buf != "lon=") {
|
||||
fprintf(stderr, "GPX parse error: cannot find longitude (trkpt #%d)\n", line);
|
||||
return 1;
|
||||
}
|
||||
if (getSubstring(&f1, &buf, '"')) // get string with longitude
|
||||
break; // on EOF
|
||||
lon = buf.toDouble(); // Convert longitude to decimal
|
||||
// == get time: ==
|
||||
if (findXmlElement(&f1, QString("time"), &buf, '>')) // Find the <time> element
|
||||
break; // on EOF
|
||||
if (getSubstring(&f1, &buf, '<')) // Read the string containing date/time
|
||||
break; // on EOF
|
||||
bool ok;
|
||||
tm1.tm_year = buf.left(4).toInt(&ok, 10); // Extract the different substrings:
|
||||
tm1.tm_mon = buf.mid(5,2).toInt(&ok,10) - 1;
|
||||
tm1.tm_mday = buf.mid(8,2).toInt(&ok,10);
|
||||
tm1.tm_hour = buf.mid(11,2).toInt(&ok,10);
|
||||
tm1.tm_min = buf.mid(14,2).toInt(&ok,10);
|
||||
tm1.tm_sec = buf.mid(17,2).toInt(&ok,10);
|
||||
when = utc_mktime(&tm1) + time_offset;
|
||||
if (first_line) {
|
||||
first_line = false;
|
||||
coords.start_track = when; // Local time of start of GPS track
|
||||
}
|
||||
if ((when > divetime && found == false)) { // This GPS local time corresponds to the start time of the dive
|
||||
coords.lon = lon; // save the coordinates
|
||||
coords.lat = lat;
|
||||
found = true;
|
||||
}
|
||||
#ifdef GPSDEBUG
|
||||
utc_mkdate(when, &time); // print time and coordinates of each of the trkpt elements of the GPX file
|
||||
fprintf(stderr, " %02d: lat=%f lon=%f timestamp=%ld (%ld) %02d/%02d/%02d %02d:%02d dt=%ld %02d/%02d/%02d %02d:%02d\n", line, lat,
|
||||
lon, when, time_offset, time.tm_year, time.tm_mon+1, time.tm_mday, time.tm_hour, time.tm_min, divetime, dyr, dmon+1, dday,dhr, dmin );
|
||||
#endif
|
||||
} while (true); // This loop executes until EOF causes a break out of the loop
|
||||
coords.end_track = when; // This is the local time of the end of the GPS track
|
||||
f1.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Fill the visual elements of the synchronisation panel with information
|
||||
void ImportGPS::updateUI()
|
||||
{
|
||||
|
@ -247,7 +108,7 @@ void ImportGPS::updateUI()
|
|||
void ImportGPS::changeZoneForward()
|
||||
{
|
||||
coords.timeZone_offset = abs(coords.timeZone_offset);
|
||||
getCoordsFromFile(); // If any of the time controls are changed
|
||||
getCoordsFromGPXFile(&coords, fileName); // If any of the time controls are changed
|
||||
updateUI(); // .. then recalculate the synchronisation
|
||||
}
|
||||
|
||||
|
@ -255,14 +116,14 @@ void ImportGPS::changeZoneBackwards()
|
|||
{
|
||||
if (coords.timeZone_offset > 0)
|
||||
coords.timeZone_offset = 0 - coords.timeZone_offset;
|
||||
getCoordsFromFile();
|
||||
getCoordsFromGPXFile(&coords, fileName);
|
||||
updateUI();
|
||||
}
|
||||
|
||||
void ImportGPS::changeDiffForward()
|
||||
{
|
||||
coords.settingsDiff_offset = abs(coords.settingsDiff_offset);
|
||||
getCoordsFromFile();
|
||||
getCoordsFromGPXFile(&coords, fileName);
|
||||
updateUI();
|
||||
}
|
||||
|
||||
|
@ -270,7 +131,7 @@ void ImportGPS::changeDiffBackwards()
|
|||
{
|
||||
if (coords.settingsDiff_offset > 0)
|
||||
coords.settingsDiff_offset = 0 - coords.settingsDiff_offset;
|
||||
getCoordsFromFile();
|
||||
getCoordsFromGPXFile(&coords, fileName);
|
||||
updateUI();
|
||||
}
|
||||
|
||||
|
@ -279,7 +140,7 @@ void ImportGPS::timeDiffEditChanged()
|
|||
coords.settingsDiff_offset = ui.timeDiffEdit->time().hour() * 3600 + ui.timeDiffEdit->time().minute() * 60;
|
||||
if (ui.diff_backwards->isChecked())
|
||||
coords.settingsDiff_offset = 0 - coords.settingsDiff_offset;
|
||||
getCoordsFromFile();
|
||||
getCoordsFromGPXFile(&coords, fileName);
|
||||
updateUI();
|
||||
}
|
||||
|
||||
|
@ -288,7 +149,7 @@ void ImportGPS::timeZoneEditChanged()
|
|||
coords.timeZone_offset = ui.timeZoneEdit->time().hour() * 3600;
|
||||
if (ui.timezone_backwards->isChecked())
|
||||
coords.timeZone_offset = 0 - coords.timeZone_offset;
|
||||
getCoordsFromFile();
|
||||
getCoordsFromGPXFile(&coords, fileName);
|
||||
updateUI();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,27 +4,16 @@
|
|||
|
||||
#include "ui_importgps.h"
|
||||
#include "desktop-widgets/locationinformation.h"
|
||||
#include "core/parse-gpx.h"
|
||||
|
||||
#include <QFile>
|
||||
|
||||
struct dive_coords { // This structure holds important information after parsing the GPX file:
|
||||
time_t start_dive; // Start time of the current dive, obtained using current_dive (local time)
|
||||
time_t end_dive; // End time of current dive (local time)
|
||||
time_t start_track; // Start time of GPX track (UCT)
|
||||
time_t end_track; // End time of GPX track (UCT)
|
||||
double lon; // Longitude of the first trackpoint after the start of the dive
|
||||
double lat; // Latitude of the first trackpoint after the start of the dive
|
||||
int64_t settingsDiff_offset; // Local time difference between dive computer and GPS equipment
|
||||
int64_t timeZone_offset; // UCT international time zone offset of dive site
|
||||
};
|
||||
|
||||
class ImportGPS : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Ui::ImportGPS ui;
|
||||
explicit ImportGPS(QWidget *parent, QString fileName, class Ui::LocationInformation *LocationUI);
|
||||
struct dive_coords coords;
|
||||
int getCoordsFromFile();
|
||||
void updateUI();
|
||||
|
||||
private
|
||||
|
@ -40,8 +29,6 @@ slots:
|
|||
private:
|
||||
QString fileName;
|
||||
class Ui::LocationInformation *LocationUI;
|
||||
int getSubstring(QFile *f, QString *buf, char delim);
|
||||
int findXmlElement(QFile *f, QString target, QString *buf, char termc);
|
||||
int pixmapSize;
|
||||
};
|
||||
|
||||
|
|
|
@ -235,7 +235,7 @@ void LocationInformationWidget::on_GPSbutton_clicked()
|
|||
ImportGPS GPSDialog(this, fileName, &ui); // Create a GPS import QDialog
|
||||
GPSDialog.coords.start_dive = current_dive->when; // initialise
|
||||
GPSDialog.coords.end_dive = dive_endtime(current_dive);
|
||||
if (!GPSDialog.getCoordsFromFile()) { // Get coordinates from GPS file
|
||||
if (getCoordsFromGPXFile(&GPSDialog.coords, fileName) == 0) { // Get coordinates from GPS file
|
||||
GPSDialog.updateUI(); // If successful, put results in Dialog
|
||||
if (!GPSDialog.exec()) // and show QDialog
|
||||
return;
|
||||
|
|
Loading…
Add table
Reference in a new issue