aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Willem Ferguson <willemferguson@zoology.up.ac.za>2020-01-19 12:06:50 -0800
committerGravatar Dirk Hohndel <dirk@hohndel.org>2020-01-24 09:51:02 -0800
commit1ecd5065a0f821bb9406b5f651ebf6db5d6c2040 (patch)
tree005526bde95db900712a9de2e37d1f9886305c0b
parent1f51251f1b80304d65c36b46cfa84ba892a62683 (diff)
downloadsubsurface-1ecd5065a0f821bb9406b5f651ebf6db5d6c2040.tar.gz
Desktop: Import dive coordinates directly from GPS
This allows Subsurface to obtain the coordinates of a dive directly from a GPS track. It parses a GPX file (GPX V1.0 or V1.1) from a GPS to locate the trackpoint immediatedly after the start of a dive. There is an additional "Use GPS file" button in the Edit Dive Site panel that is selected from the Notes tab. Image: This allows one to select a GPX file, bringing up the Import GPS dialog. There is extensive provision for cross-checking that the dive track synchronises with the dive start and end. If the Save button in the dialog is pressed the dive coordinates are copied into the Dive Coordinates text box in the Edit Dive Site panel. The map moves to indicate the location of the dive site. The bulk of the work is done in importgps.cpp. The code is pretty intergrated: I tried to break it up in smaller commits but that was not feasible. The code includes responses to the comments by @neolit123 and @bstoeger. The C-based file input was replaced with Qt-based code using QChar, QString and QFile. [Dirk Hohndel: fixed several small issues in the .ui file, removed various headers includes that weren't needed and fixed printing of minutes as zero padded] Signed-off-by: willemferguson <willemferguson@zoology.up.ac.za> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
-rw-r--r--desktop-widgets/CMakeLists.txt3
-rw-r--r--desktop-widgets/importgps.cpp294
-rw-r--r--desktop-widgets/importgps.h49
-rw-r--r--desktop-widgets/importgps.ui575
-rw-r--r--desktop-widgets/locationinformation.cpp24
-rw-r--r--desktop-widgets/locationinformation.h4
-rw-r--r--desktop-widgets/locationinformation.ui37
-rw-r--r--icons/resultgreen.pngbin0 -> 596 bytes
-rw-r--r--icons/resultred.pngbin0 -> 595 bytes
-rw-r--r--icons/resultyellow.pngbin0 -> 599 bytes
-rw-r--r--subsurface.qrc3
11 files changed, 977 insertions, 12 deletions
diff --git a/desktop-widgets/CMakeLists.txt b/desktop-widgets/CMakeLists.txt
index 14c829526..82a81321e 100644
--- a/desktop-widgets/CMakeLists.txt
+++ b/desktop-widgets/CMakeLists.txt
@@ -28,6 +28,7 @@ set (SUBSURFACE_UI
downloadfromdivecomputer.ui
filterwidget2.ui
findmovedimagesdialog.ui
+ importgps.ui
listfilter.ui
locationinformation.ui
mainwindow.ui
@@ -83,6 +84,8 @@ set(SUBSURFACE_INTERFACE
findmovedimagesdialog.h
groupedlineedit.cpp
groupedlineedit.h
+ importgps.cpp
+ importgps.h
kmessagewidget.cpp
kmessagewidget.h
locationinformation.cpp
diff --git a/desktop-widgets/importgps.cpp b/desktop-widgets/importgps.cpp
new file mode 100644
index 000000000..5e4845951
--- /dev/null
+++ b/desktop-widgets/importgps.cpp
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "desktop-widgets/importgps.h"
+
+/* Import dive coordinates from a GPS device and synchronise them with the dive profile information
+ of a dive computer. This file contains the infrastructure to:
+ 1) Read a .GPX file from a GPS system.
+ 2) Find the first gpx trackpoint that follows after the start of the dive.
+ 3) Allow modification of the coordinates dependent on international time zone and
+ on differences in local time settings between the GPS and the dive computer.
+ 4) Saving the coordinates into the Coordinates text box in the Dive Site Edit panel
+ and which which causes the map to show the location of the dive site.
+ The structure coords is used to store critical information. */
+ImportGPS::ImportGPS(QWidget *parent, QString fileName, class Ui::LocationInformation *LocationUI) : QDialog(parent),
+ fileName(fileName), LocationUI(LocationUI)
+{
+ ui.setupUi(this);
+ connect(ui.timeDiffEdit, &QTimeEdit::timeChanged, this, &ImportGPS::timeDiffEditChanged);
+ connect(ui.timeZoneEdit, &QTimeEdit::timeChanged, this, &ImportGPS::timeZoneEditChanged);
+ connect(ui.timezone_backwards, &QRadioButton::toggled, this, &ImportGPS::changeZoneBackwards);
+ connect(ui.timezone_forward, &QRadioButton::toggled, this, &ImportGPS::changeZoneForward);
+ connect(ui.diff_backwards, &QRadioButton::toggled, this, &ImportGPS::changeDiffBackwards);
+ connect(ui.diff_forward, &QRadioButton::toggled, this, &ImportGPS::changeDiffForward);
+ connect(ui.GPSbuttonBox, &QDialogButtonBox::clicked, this, &ImportGPS::buttonClicked);
+ coords.settingsDiff_offset = 0;
+ coords.timeZone_offset = 0;
+ coords.lat = 0;
+ coords.lon = 0;
+ pixmapSize = (int) (ui.diveDateLabel->height() / 2);
+}
+
+void ImportGPS::buttonClicked(QAbstractButton *button)
+{
+ if (ui.GPSbuttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) {
+ // Write the coordinates in decimal degree format to the Coordinates QLineEdit of the LocationaInformationWidget UI:
+ LocationUI->diveSiteCoordinates->setText(QString::number(coords.lat) + ", " + QString::number(coords.lon));
+ LocationUI->diveSiteCoordinates->editingFinished();
+ } else {
+ close();
+ }
+}
+
+// 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()
+{
+ struct tm time;
+ int dive_day, gps_day;
+ char datestr[50];
+ QString problemString = "";
+
+ utc_mkdate(coords.start_track, &time); // Display GPS date and local start and end times of track:
+ gps_day = time.tm_mday;
+ datestr[0] = 0x0;
+ strftime(datestr, sizeof(datestr), "%A %d %B ", &time); // GPS date
+ ui.trackDateLabel->setText("GPS date = " + QString(datestr) + QString::number(time.tm_year));
+ ui.startTimeLabel->setText(QString::number(time.tm_hour) + ":" + QString("%1").arg(time.tm_min, 2, 10, QChar('0'))); // track start time
+ utc_mkdate(coords.end_track, &time);
+ ui.endTimeLabel->setText(QString::number(time.tm_hour) + ":" + QString("%1").arg(time.tm_min, 2, 10, QChar('0'))); // track end time
+
+ utc_mkdate(coords.start_dive, &time); // Display dive date and start and end times of dive:
+ dive_day = time.tm_mday;
+ datestr[0] = 0x0;
+ strftime(datestr, sizeof(datestr), "%A %d %B ", localtime(&(coords.start_dive))); // dive date
+ ui.diveDateLabel->setText("Dive date = " + QString(datestr) + QString::number(time.tm_year));
+ ui.startTimeSyncLabel->setText( QString::number(time.tm_hour) + ":" + QString("%1").arg(time.tm_min, 2, 10, QChar('0'))); // dive start time
+ utc_mkdate(coords.end_dive, &time);
+ ui.endTimeSyncLabel->setText(QString::number(time.tm_hour) + ":" + QString("%1").arg(time.tm_min, 2, 10, QChar('0'))); // dive end time
+
+ // This section implements extensive warnings in case there is not synchronisation between dive and GPS data:
+
+ if (gps_day != dive_day)
+ problemString = "(different dates)";
+ // Create 3 icons to indicate the quality of the synchrinisation between dive and GPS
+ QPixmap goodResultIcon (":gps_good_result-icon");
+ ui.goodIconLabel->setPixmap(goodResultIcon.scaled(pixmapSize,pixmapSize,Qt::KeepAspectRatio));
+ ui.goodIconLabel->setVisible(false);
+
+ QPixmap warningResultIcon (":gps_warning_result-icon");
+ ui.warningIconLabel->setPixmap(warningResultIcon.scaled(pixmapSize,pixmapSize,Qt::KeepAspectRatio));
+ ui.warningIconLabel->setVisible(false);
+
+ QPixmap badResultIcon (":gps_bad_result-icon");
+ ui.badIconLabel->setPixmap(badResultIcon.scaled(pixmapSize,pixmapSize,Qt::KeepAspectRatio));
+ ui.badIconLabel->setVisible(false);
+ // Show information or warning message as well as synch quality icon
+ if (coords.start_dive < coords.start_track) {
+ ui.resultLabel->setStyleSheet("QLabel { color: red;} ");
+ ui.resultLabel->setText("PROBLEM: Dive started before the GPS track "+ problemString);
+ ui.badIconLabel->setVisible(true);
+ } else {
+ if (coords.start_dive > coords.end_track) {
+ ui.resultLabel->setStyleSheet("QLabel { color: red;} ");
+ ui.resultLabel->setText("PROBLEM: Dive started after the GPS track " + problemString);
+ ui.badIconLabel->setVisible(true);
+ } else {
+ if (coords.end_dive > coords.end_track) {
+ ui.resultLabel->setStyleSheet("QLabel { color: red;} ");
+ ui.resultLabel->setText("WARNING: Dive ended after the GPS track " + problemString);
+ ui.warningIconLabel->setVisible(true);
+ } else {
+ ui.resultLabel->setStyleSheet("QLabel { color: darkgreen;} ");
+ ui.resultLabel->setText("Dive coordinates: "+ QString::number(coords.lat) + "S, " + QString::number(coords.lon) + "E");
+ ui.goodIconLabel->setVisible(true);
+ }
+ }
+ }
+}
+
+void ImportGPS::changeZoneForward()
+{
+ coords.timeZone_offset = abs(coords.timeZone_offset);
+ getCoordsFromFile(); // If any of the time controls are changed
+ updateUI(); // .. then recalculate the synchronisation
+}
+
+void ImportGPS::changeZoneBackwards()
+{
+ if (coords.timeZone_offset > 0)
+ coords.timeZone_offset = 0 - coords.timeZone_offset;
+ getCoordsFromFile();
+ updateUI();
+}
+
+void ImportGPS::changeDiffForward()
+{
+ coords.settingsDiff_offset = abs(coords.settingsDiff_offset);
+ getCoordsFromFile();
+ updateUI();
+}
+
+void ImportGPS::changeDiffBackwards()
+{
+ if (coords.settingsDiff_offset > 0)
+ coords.settingsDiff_offset = 0 - coords.settingsDiff_offset;
+ getCoordsFromFile();
+ updateUI();
+}
+
+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();
+ updateUI();
+}
+
+void ImportGPS::timeZoneEditChanged()
+{
+ coords.timeZone_offset = ui.timeZoneEdit->time().hour() * 3600;
+ if (ui.timezone_backwards->isChecked())
+ coords.timeZone_offset = 0 - coords.timeZone_offset;
+ getCoordsFromFile();
+ updateUI();
+}
+
diff --git a/desktop-widgets/importgps.h b/desktop-widgets/importgps.h
new file mode 100644
index 000000000..1cef9e57a
--- /dev/null
+++ b/desktop-widgets/importgps.h
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+#ifndef IMPORT_GPS_H
+#define IMPORT_GPS_H
+
+#include "ui_importgps.h"
+#include "desktop-widgets/locationinformation.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
+slots:
+ void timeDiffEditChanged();
+ void timeZoneEditChanged();
+ void changeZoneForward();
+ void changeZoneBackwards();
+ void changeDiffForward();
+ void changeDiffBackwards();
+ void buttonClicked(QAbstractButton *button);
+
+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;
+};
+
+#endif
+
diff --git a/desktop-widgets/importgps.ui b/desktop-widgets/importgps.ui
new file mode 100644
index 000000000..d7eaadb7b
--- /dev/null
+++ b/desktop-widgets/importgps.ui
@@ -0,0 +1,575 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ImportGPS</class>
+ <widget class="QDialog" name="ImportGPS">
+ <property name="windowModality">
+ <enum>Qt::WindowModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>600</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Get dive coordinates from GPS</string>
+ </property>
+ <property name="windowIcon">
+ <iconset>
+ <normalon>:subsurface-icon</normalon>
+ </iconset>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+
+ <item>
+ <widget class="QGroupBox" name="groupBox"> <!-- Main group box -->
+ <property name="title">
+ <string>Synchronise the dive and the GPS track</string>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+
+ <item>
+ <layout class="QGridLayout" name="gridLayout1">
+
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>2</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+
+ <item row="0" column="0" colspan="4">
+ <widget class="QLabel" name="trackDateLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>GPS strack date </string>
+ </property>
+ </widget>
+ </item>
+
+ <item row="1" column="0" colspan="4">
+ <widget class="QLabel" name="diveDateLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Dive date </string>
+ </property>
+ </widget>
+ </item>
+
+ <item row="3" column="0" >
+ <widget class="QLabel" name="startEndTextLabel">
+ <property name="text">
+ <string>Start and end times:</string>
+ </property>
+ </widget>
+ </item>
+
+ <item row="2" column="1" colspan="3" rowspan="3">
+ <widget class="QGroupBox" name="TimesBox"> <!-- Start and end times GroupBox -->
+ <layout class="QGridLayout" name="TimesLayout">
+
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>2</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+
+ <item row="0" column="1" alignment="Qt::AlignCenter">
+ <widget class="QLabel" name="trackTextLabel">
+ <property name="text">
+ <string> GPS </string>
+ </property>
+ </widget>
+ </item>
+
+ <item row="0" column="2" alignment="Qt::AlignCenter">
+ <widget class="QLabel" name="diveTextLabel">
+ <property name="text">
+ <string> Dive </string>
+ </property>
+ </widget>
+ </item>
+
+ <item row="1" column="0" alignment="Qt::AlignRight">
+ <widget class="QLabel" name="startTextLabel">
+ <property name="text">
+ <string>Start: </string>
+ </property>
+ </widget>
+ </item>
+
+
+ <item row="1" column="1" alignment="Qt::AlignCenter">
+ <widget class="QLabel" name="startTimeLabel">
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </item>
+
+ <item row="1" column="2" alignment="Qt::AlignCenter">
+ <widget class="QLabel" name="startTimeSyncLabel">
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </item>
+
+ <item row="2" column="0" alignment="Qt::AlignRight">
+ <widget class="QLabel" name="endTextLabel">
+ <property name="text">
+ <string>End: </string>
+ </property>
+ </widget>
+ </item>
+
+ <item row="2" column="1" alignment="Qt::AlignCenter">
+ <widget class="QLabel" name="endTimeLabel">
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </item>
+
+ <item row="2" column="2" alignment="Qt::AlignCenter">
+ <widget class="QLabel" name="endTimeSyncLabel">
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </item>
+
+ </layout>
+ </widget> <!-- TimesBox: Start and end times GroupBox -->
+ </item>
+
+ <item row="3" column="5" alignment="Qt::AlignCenter">
+ <widget class="QLabel" name="goodIconLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </item>
+
+ <item row="3" column="6" alignment="Qt::AlignCenter">
+ <widget class="QLabel" name="warningIconLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </item>
+
+ <item row="3" column="7" alignment="Qt::AlignCenter">
+ <widget class="QLabel" name="badIconLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </item>
+
+ <item row="3" column="8">
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+
+ </layout>
+ </item>
+
+ <item>
+ <widget class="QLabel" name="resultLabel">
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </item>
+
+ <item>
+ <widget class="QGroupBox" name="timeZoneGroupBox">
+ <property name="title">
+ <string>International time zone correction</string>
+ </property>
+ <layout class="QVBoxLayout" name="TimeZoneLayout">
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+
+ <item>
+ <widget class="QLabel" name="timeZoneInstructionLabel">
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>60</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string>By how many hours did the international time zone at the dive site differ from UCT (also regarded as Greenwich Mean Time)? Normally one would specify this value in whole hours (h:00):</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+
+ <item>
+ <widget class="QTimeEdit" name="timeZoneEdit">
+ <property name="date">
+ <date>
+ <year>2000</year>
+ <month>1</month>
+ <day>1</day>
+ </date>
+ </property>
+ <property name="maximumDateTime">
+ <datetime>
+ <hour>23</hour>
+ <minute>59</minute>
+ <second>59</second>
+ <year>2010</year>
+ <month>12</month>
+ <day>31</day>
+ </datetime>
+ </property>
+ <property name="minimumDateTime">
+ <datetime>
+ <hour>0</hour>
+ <minute>0</minute>
+ <second>0</second>
+ <year>2000</year>
+ <month>1</month>
+ <day>1</day>
+ </datetime>
+ </property>
+ <property name="maximumDate">
+ <date>
+ <year>2010</year>
+ <month>12</month>
+ <day>31</day>
+ </date>
+ </property>
+ <property name="minimumDate">
+ <date>
+ <year>2000</year>
+ <month>1</month>
+ <day>1</day>
+ </date>
+ </property>
+ <property name="maximumTime">
+ <time>
+ <hour>23</hour>
+ <minute>59</minute>
+ <second>59</second>
+ </time>
+ </property>
+ <property name="minimumTime">
+ <time>
+ <hour>0</hour>
+ <minute>0</minute>
+ <second>0</second>
+ </time>
+ </property>
+ <property name="displayFormat">
+ <string>h:mm</string>
+ </property>
+ <property name="timeSpec">
+ <enum>Qt::LocalTime</enum>
+ </property>
+ </widget>
+ </item>
+
+ <item>
+ <widget class="QRadioButton" name="timezone_backwards">
+ <property name="text">
+ <string>Earlier</string>
+ </property>
+ </widget>
+ </item>
+
+ <item>
+ <widget class="QRadioButton" name="timezone_forward">
+ <property name="text">
+ <string>Later</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+
+ </layout>
+ </widget> <!-- timeZoneGroupBox -->
+ </item>
+
+ <item>
+ <widget class="QGroupBox" name="timeDiffGroupBox">
+ <property name="title">
+ <string>Equipment time settings correction</string>
+ </property>
+ <layout class="QVBoxLayout" name="TimeDiffLayout">
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+
+ <item>
+ <widget class="QLabel" name="timeDiffInstructionLabel">
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>90</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string>This setting deals with differences in time settings between the GPS and the dive computer that recorded the dive. Ensure that the GPS equipment and your dive computer show the same local time. If not, adjust the time of the GPS earlier or later to coincide with that of the dive (h:min):</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+
+ <item>
+ <widget class="QTimeEdit" name="timeDiffEdit">
+ <property name="date">
+ <date>
+ <year>2000</year>
+ <month>1</month>
+ <day>1</day>
+ </date>
+ </property>
+ <property name="maximumDateTime">
+ <datetime>
+ <hour>23</hour>
+ <minute>59</minute>
+ <second>59</second>
+ <year>2010</year>
+ <month>12</month>
+ <day>31</day>
+ </datetime>
+ </property>
+ <property name="minimumDateTime">
+ <datetime>
+ <hour>0</hour>
+ <minute>0</minute>
+ <second>0</second>
+ <year>2000</year>
+ <month>1</month>
+ <day>1</day>
+ </datetime>
+ </property>
+ <property name="maximumDate">
+ <date>
+ <year>2010</year>
+ <month>12</month>
+ <day>31</day>
+ </date>
+ </property>
+ <property name="minimumDate">
+ <date>
+ <year>2000</year>
+ <month>1</month>
+ <day>1</day>
+ </date>
+ </property>
+ <property name="maximumTime">
+ <time>
+ <hour>23</hour>
+ <minute>59</minute>
+ <second>59</second>
+ </time>
+ </property>
+ <property name="minimumTime">
+ <time>
+ <hour>0</hour>
+ <minute>0</minute>
+ <second>0</second>
+ </time>
+ </property>
+ <property name="displayFormat">
+ <string>h:mm</string>
+ </property>
+ <property name="timeSpec">
+ <enum>Qt::LocalTime</enum>
+ </property>
+ </widget>
+ </item>
+
+ <item>
+ <widget class="QRadioButton" name="diff_backwards">
+ <property name="text">
+ <string>Earlier</string>
+ </property>
+ </widget>
+ </item>
+
+ <item>
+ <widget class="QRadioButton" name="diff_forward">
+ <property name="text">
+ <string>Later</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+
+ </layout>
+ </widget> <!-- TimeDiffGroupBox -->
+ </item>
+
+ <item>
+ <widget class="QDialogButtonBox" name="GPSbuttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Close|QDialogButtonBox::Save</set>
+ </property>
+ </widget>
+ </item>
+
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+
+
+ </layout>
+ </widget>
+ </item> <!-- Main Group box -->
+
+ </layout>
+ </widget> <!-- QDialog -->
+
+ <resources>
+ <include location="../subsurface.qrc"/>
+ </resources>
+</ui>
diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp
index 1595db3eb..1ee848e83 100644
--- a/desktop-widgets/locationinformation.cpp
+++ b/desktop-widgets/locationinformation.cpp
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include "desktop-widgets/locationinformation.h"
+#include "desktop-widgets/importgps.h"
#include "core/subsurface-string.h"
#include "desktop-widgets/mainwindow.h"
#include "desktop-widgets/divelistview.h"
@@ -13,12 +14,12 @@
#include "core/settings/qPrefUnit.h"
#include "commands/command.h"
-#include <QDebug>
#include <QShowEvent>
#include <QItemSelectionModel>
#include <qmessagebox.h>
#include <cstdlib>
#include <QDesktopWidget>
+#include <QFileDialog>
#include <QScrollBar>
LocationInformationWidget::LocationInformationWidget(QWidget *parent) : QGroupBox(parent), diveSite(nullptr), closeDistance(0)
@@ -220,6 +221,27 @@ void LocationInformationWidget::initFields(dive_site *ds)
}
}
+
+void LocationInformationWidget::on_GPSbutton_clicked()
+{
+ QFileInfo finfo(system_default_directory());
+ QString fileName = QFileDialog::getOpenFileName(this,
+ tr("Select GPS file to open"),
+ finfo.absolutePath(),
+ tr("GPS files (*.gpx *.GPX)"));
+ if (fileName.isEmpty())
+ return;
+
+ 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
+ GPSDialog.updateUI(); // If successful, put results in Dialog
+ if (!GPSDialog.exec()) // and show QDialog
+ return;
+ }
+}
+
void LocationInformationWidget::on_diveSiteCoordinates_editingFinished()
{
if (diveSite)
diff --git a/desktop-widgets/locationinformation.h b/desktop-widgets/locationinformation.h
index 0e53a4439..a936269e6 100644
--- a/desktop-widgets/locationinformation.h
+++ b/desktop-widgets/locationinformation.h
@@ -7,6 +7,7 @@
#include "ui_locationinformation.h"
#include "modeldelegates.h"
#include "qt-models/divelocationmodel.h"
+#include "desktop-widgets/importgps.h"
#include <stdint.h>
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
@@ -17,6 +18,7 @@ public:
LocationInformationWidget(QWidget *parent = 0);
bool eventFilter(QObject*, QEvent*) override;
void initFields(dive_site *ds);
+ Ui::LocationInformation ui;
protected:
void enableLocationButtons(bool enable);
@@ -31,6 +33,7 @@ public slots:
void on_diveSiteDistance_textChanged(const QString &s);
void reverseGeocode();
void mergeSelectedDiveSites();
+ void on_GPSbutton_clicked();
private slots:
void updateLabels();
void diveSiteChanged(struct dive_site *ds, int field);
@@ -39,7 +42,6 @@ private slots:
private:
void keyPressEvent(QKeyEvent *e) override;
void clearLabels();
- Ui::LocationInformation ui;
GPSLocationInformationModel filter_model;
dive_site *diveSite;
int64_t closeDistance; // Distance of "close" dive sites in mm
diff --git a/desktop-widgets/locationinformation.ui b/desktop-widgets/locationinformation.ui
index 72e7fb2e1..3894d623c 100644
--- a/desktop-widgets/locationinformation.ui
+++ b/desktop-widgets/locationinformation.ui
@@ -44,16 +44,7 @@
</property>
</widget>
</item>
- <item row="3" column="2">
- <widget class="QLineEdit" name="diveSiteCoordinates"/>
- </item>
- <item row="3" column="0">
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Coordinates</string>
- </property>
- </widget>
- </item>
+
<item row="7" column="2" rowspan="2" colspan="3">
<widget class="TextEdit" name="diveSiteNotes">
<property name="focusPolicy">
@@ -61,12 +52,37 @@
</property>
</widget>
</item>
+
<item row="1" column="2" colspan="3">
<widget class="QLineEdit" name="diveSiteName"/>
</item>
+
<item row="5" column="2" colspan="3">
<widget class="QLineEdit" name="diveSiteDescription"/>
</item>
+
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Coordinates</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QLineEdit" name="diveSiteCoordinates"/>
+ </item>
+
+ <item row="3" column="3">
+ <widget class="QPushButton" name="GPSbutton">
+ <property name="toolTip">
+ <string>Load dive coordinates from GPS file</string>
+ </property>
+ <property name="text">
+ <string>Use GPS file</string>
+ </property>
+ </widget>
+ </item>
+
<item row="3" column="4">
<widget class="QToolButton" name="geoCodeButton">
<property name="toolTip">
@@ -81,6 +97,7 @@
</property>
</widget>
</item>
+
<item row="0" column="0" colspan="5">
<widget class="KMessageWidget" name="diveSiteMessage">
<property name="sizePolicy">
diff --git a/icons/resultgreen.png b/icons/resultgreen.png
new file mode 100644
index 000000000..c9beafc5a
--- /dev/null
+++ b/icons/resultgreen.png
Binary files differ
diff --git a/icons/resultred.png b/icons/resultred.png
new file mode 100644
index 000000000..68e7ad8e4
--- /dev/null
+++ b/icons/resultred.png
Binary files differ
diff --git a/icons/resultyellow.png b/icons/resultyellow.png
new file mode 100644
index 000000000..bd40afc73
--- /dev/null
+++ b/icons/resultyellow.png
Binary files differ
diff --git a/subsurface.qrc b/subsurface.qrc
index f7a023289..988ad8804 100644
--- a/subsurface.qrc
+++ b/subsurface.qrc
@@ -105,5 +105,8 @@
<file alias="video-overlay">icons/video_overlay.svg</file>
<file alias="unknown-icon">icons/unknown.svg</file>
<file alias="tissue-icon">icons/tissue.png</file>
+ <file alias="gps_good_result-icon">icons/resultgreen.png</file>
+ <file alias="gps_warning_result-icon">icons/resultyellow.png</file>
+ <file alias="gps_bad_result-icon">icons/resultred.png</file>
</qresource>
</RCC>