diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 381276e30..80fbd326e 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -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 diff --git a/core/parse-gpx.cpp b/core/parse-gpx.cpp new file mode 100644 index 000000000..16d2a17a2 --- /dev/null +++ b/core/parse-gpx.cpp @@ -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 "" 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: +// -53.7 +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 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