diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 98482780a..27efd0025 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -27,7 +27,7 @@ jobs: name: Analyze runs-on: ubuntu-latest container: - image: gpsbabel-docker.jfrog.io/tsteven4/gpsbabel_build_environment_focal + image: gpsbabel-docker.jfrog.io/tsteven4/gpsbabel_build_environment_jammy env: CMAKE_GENERATOR: Ninja diff --git a/.github/workflows/fedora.yml b/.github/workflows/fedora.yml index 6c7388528..76543d9d5 100644 --- a/.github/workflows/fedora.yml +++ b/.github/workflows/fedora.yml @@ -13,10 +13,7 @@ jobs: strategy: fail-fast: false matrix: - # version 32, though obsolete, uses Qt 5.14 so we keep it for that. include: - - IMAGE: '32' - CMAKE_PREFIX_PATH: '/usr/lib64/cmake/Qt5' - IMAGE: '35' CMAKE_PREFIX_PATH: '/usr/lib64/cmake/Qt5' - IMAGE: '37' diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 2cf8d4331..9a1f92802 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -18,8 +18,9 @@ jobs: fail-fast: false matrix: include: - - IMAGE: 'focal' - SCRIPT: './tools/build_and_test_cmake.sh' + # focal has Qt 5.12, end of standard support 4/2025, end of life 4/2030. + #- IMAGE: 'focal' + # SCRIPT: './tools/build_and_test_cmake.sh' - IMAGE: 'jammy' CMAKE_PREFIX_PATH: '/usr/lib/x86_64-linux-gnu/cmake/Qt5' SCRIPT: './tools/build_and_test_cmake.sh' @@ -63,12 +64,12 @@ jobs: coverage: name: coverage Build - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: install run: | sudo apt-get update - sudo apt-get install gcovr lcov libusb-1.0-0-dev qt5-default qtwebengine5-dev libqt5serialport5-dev ninja-build + sudo apt-get install gcovr lcov libusb-1.0-0-dev qtbase5-dev qtwebengine5-dev libqt5serialport5-dev ninja-build - name: Checkout repository uses: actions/checkout@v4 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 4a5a69411..fa003068d 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -19,27 +19,27 @@ jobs: fail-fast: false matrix: include: - - QT_VERSION: '5.12.12' + - QT_VERSION: '5.15.2' ARCH: 'amd64' HOST_ARCH: 'amd64' - COMPILER: 'msvc2017_64' + COMPILER: 'msvc2019_64' METHOD: 'aqt' GENERATOR: 'Visual Studio 16 2019' RELEASE: false os: windows-2019 - - QT_VERSION: '5.12.12' + - QT_VERSION: '5.15.2' ARCH: 'amd64' HOST_ARCH: 'amd64' - COMPILER: 'msvc2017_64' + COMPILER: 'msvc2019_64' TOOLSET: 'v141,version=14.16.27023' METHOD: 'aqt' GENERATOR: 'Visual Studio 16 2019' RELEASE: false os: windows-2019 - - QT_VERSION: '5.12.12' + - QT_VERSION: '5.15.2' ARCH: 'x86' HOST_ARCH: 'amd64' - COMPILER: 'msvc2017' + COMPILER: 'msvc2019' METHOD: 'aqt' GENERATOR: 'Visual Studio 16 2019' RELEASE: false diff --git a/CMakeLists.txt b/CMakeLists.txt index eb8badaaa..315137eef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,8 +29,8 @@ add_executable(gpsbabel) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED) list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::Core) -if(${Qt${QT_VERSION_MAJOR}Core_VERSION} VERSION_LESS 5.12) - message(FATAL_ERROR "Qt version ${Qt${QT_VERSION_MAJOR}Core_VERSION} found, but version 5.12 or newer is required.") +if(${Qt${QT_VERSION_MAJOR}Core_VERSION} VERSION_LESS 5.15) + message(FATAL_ERROR "Qt version ${Qt${QT_VERSION_MAJOR}Core_VERSION} found, but version 5.15 or newer is required.") else() message(STATUS "Using Qt${QT_VERSION_MAJOR} version ${Qt${QT_VERSION_MAJOR}Core_VERSION}") endif() diff --git a/formspec.h b/formspec.h index 942e596aa..44261561c 100644 --- a/formspec.h +++ b/formspec.h @@ -26,6 +26,7 @@ enum FsType { kFsUnknown = 0L, kFsGpx = 0x67707800L, + kFsGpxWpt = 0x67707877L, kFsOzi = 0x6f7a6900L, kFsGmsd = 0x474d5344L, /* GMSD = Garmin specific data */ kFsQstarzBl1000 = 0x5173747aL, diff --git a/garmin_fs.cc b/garmin_fs.cc index 1827fe120..c6846efce 100644 --- a/garmin_fs.cc +++ b/garmin_fs.cc @@ -20,17 +20,17 @@ */ -#include // for snprintf, sscanf -#include // for strtod +#include // for snprintf, sscanf +#include // for strtod -#include // for QString, QStringLiteral -#include // for QXmlStreamWriter -#include // for CaseInsensitive +#include // for QString, QStringLiteral +#include // for CaseInsensitive #include "defs.h" #include "garmin_fs.h" -#include "garmin_tables.h" // for gt_switch_display_mode_value, gt_display_mode_symbol, gt_display_mode_symbol_and_comment, gt_display_mode_symbol_and_name -#include "inifile.h" // for inifile_readstr +#include "garmin_tables.h" // for gt_switch_display_mode_value, gt_display_mode_symbol, gt_display_mode_symbol_and_comment, gt_display_mode_symbol_and_name +#include "inifile.h" // for inifile_readstr +#include "src/core/xmlstreamwriter.h" // for XmlStreamWriter #define MYNAME "garmin_fs" @@ -80,113 +80,75 @@ garmin_fs_t::~garmin_fs_t() void garmin_fs_xml_fprint(const Waypoint* waypt, - QXmlStreamWriter* writer) + gpsbabel::XmlStreamWriter* writer) { garmin_fs_t* gmsd = garmin_fs_t::find(waypt); - if (gmsd == nullptr) { - return; - } + writer->stackOptionalStartElement(QStringLiteral("extensions")); + writer->stackOptionalStartElement(QStringLiteral("gpxx:WaypointExtension")); + writer->stackNamespace(QStringLiteral("http://www.garmin.com/xmlschemas/GpxExtensions/v3"), + "gpxx"); - /* Find out if there is at least one field set */ - QString addr = garmin_fs_t::get_addr(gmsd, ""); - if (addr.isEmpty()) { - addr = garmin_fs_t::get_city(gmsd, ""); - } - if (addr.isEmpty()) { - addr = garmin_fs_t::get_country(gmsd, ""); + if (waypt->proximity_has_value()) { + writer->stackTextElement(QStringLiteral("gpxx:Proximity"), QString::number(waypt->proximity_value(), 'f', 6)); } - if (addr.isEmpty()) { - addr = garmin_fs_t::get_postal_code(gmsd, ""); + + if (waypt->temperature_has_value()) { + writer->stackTextElement(QStringLiteral("gpxx:Temperature"), QString::number(waypt->temperature_value(), 'f', 6)); } - if (addr.isEmpty()) { - addr = garmin_fs_t::get_state(gmsd, ""); + + if (waypt->depth_has_value()) { + writer->stackTextElement(QStringLiteral("gpxx:Depth"), QString::number(waypt->depth_value(), 'f', 6)); } - QString phone = garmin_fs_t::get_phone_nr(gmsd, ""); - - if (!addr.isEmpty() || !phone.isEmpty() || - (gmsd->flags.category && gmsd->category) || - waypt->depth_has_value() || - waypt->proximity_has_value() || - waypt->temperature_has_value() || - gmsd->flags.display) { - writer->writeStartElement(QStringLiteral("extensions")); - writer->writeStartElement(QStringLiteral("gpxx:WaypointExtension")); - writer->writeNamespace(QStringLiteral("http://www.garmin.com/xmlschemas/GpxExtensions/v3"), - "gpxx"); - if (waypt->proximity_has_value()) { - writer->writeTextElement(QStringLiteral("gpxx:Proximity"), QString::number(waypt->proximity_value(), 'f', 6)); - } - if (waypt->temperature_has_value()) { - writer->writeTextElement(QStringLiteral("gpxx:Temperature"), QString::number(waypt->temperature_value(), 'f', 6)); - } - if (waypt->depth_has_value()) { - writer->writeTextElement(QStringLiteral("gpxx:Depth"), QString::number(waypt->depth_value(), 'f', 6)); - } - if (gmsd->flags.display) { - const char* cx; - switch (gmsd->display) { - case gt_display_mode_symbol: - cx = "SymbolOnly"; - break; - case gt_display_mode_symbol_and_comment: - cx = "SymbolAndDescription"; - break; - default: - cx = "SymbolAndName"; - break; - } - writer->writeTextElement(QStringLiteral("gpxx:DisplayMode"), cx); + if (garmin_fs_t::has_display(gmsd)) { + const char* cx; + switch (gmsd->display) { + case gt_display_mode_symbol: + cx = "SymbolOnly"; + break; + case gt_display_mode_symbol_and_comment: + cx = "SymbolAndDescription"; + break; + default: + cx = "SymbolAndName"; + break; } - if (gmsd->flags.category && gmsd->category) { - uint16_t cx = gmsd->category; - writer->writeStartElement(QStringLiteral("gpxx:Categories")); - for (int i = 0; i < 16; i++) { - if (cx & 1) { - writer->writeTextElement(QStringLiteral("gpxx:Category"), QStringLiteral("Category %1").arg(i+1)); - } - cx = cx >> 1; - } - writer->writeEndElement(); // gpxx:Categories - } - if (!addr.isEmpty()) { - QString str; - writer->writeStartElement(QStringLiteral("gpxx:Address")); + writer->stackTextElement(QStringLiteral("gpxx:DisplayMode"), cx); + } - if (!(str = garmin_fs_t::get_addr(gmsd, nullptr)).isEmpty()) { - writer->writeTextElement(QStringLiteral("gpxx:StreetAddress"), str); - } - if (!(str = garmin_fs_t::get_city(gmsd, nullptr)).isEmpty()) { - writer->writeTextElement(QStringLiteral("gpxx:City"), str); - } - if (!(str = garmin_fs_t::get_state(gmsd, nullptr)).isEmpty()) { - writer->writeTextElement(QStringLiteral("gpxx:State"), str); - } - if (!(str = garmin_fs_t::get_country(gmsd, nullptr)).isEmpty()) { - writer->writeTextElement(QStringLiteral("gpxx:Country"), str); + if (garmin_fs_t::has_category(gmsd)) { + uint16_t cx = gmsd->category; + writer->stackStartElement(QStringLiteral("gpxx:Categories")); + for (int i = 0; i < 16; i++) { + if (cx & 1) { + writer->stackTextElement(QStringLiteral("gpxx:Category"), QStringLiteral("Category %1").arg(i+1)); } - if (!(str = garmin_fs_t::get_postal_code(gmsd, nullptr)).isEmpty()) { - writer->writeTextElement(QStringLiteral("gpxx:PostalCode"), str); - } - writer->writeEndElement(); // /gpxx::Address + cx = cx >> 1; } + writer->stackEndElement(); // gpxx:Categories + } - if (!phone.isEmpty()) { - writer->writeTextElement(QStringLiteral("gpxx:PhoneNumber"), phone); - } + writer->stackOptionalStartElement(QStringLiteral("gpxx:Address")); + writer->stackOptionalTextElement(QStringLiteral("gpxx:StreetAddress"), garmin_fs_t::get_addr(gmsd, nullptr)); + writer->stackOptionalTextElement(QStringLiteral("gpxx:City"), garmin_fs_t::get_city(gmsd, nullptr)); + writer->stackOptionalTextElement(QStringLiteral("gpxx:State"), garmin_fs_t::get_state(gmsd, nullptr)); + writer->stackOptionalTextElement(QStringLiteral("gpxx:Country"), garmin_fs_t::get_country(gmsd, nullptr)); + writer->stackOptionalTextElement(QStringLiteral("gpxx:PostalCode"), garmin_fs_t::get_postal_code(gmsd, nullptr)); + writer->stackEndElement(); // gpxx:Address + + writer->stackOptionalTextElement(QStringLiteral("gpxx:PhoneNumber"), garmin_fs_t::get_phone_nr(gmsd, nullptr)); - writer->writeEndElement(); // /gpxx::WaypointExtension - writer->writeEndElement(); // /extensions. - } + writer->stackEndElement(); // gpxx:WaypointExtension + writer->stackEndElement(); // extensions } void garmin_fs_xml_convert(const int base_tag, int tag, const QString& qstr, Waypoint* waypt) { // FIXME: eliminate C string copy/use here: - const char *cdatastr = xstrdup(qstr); + const char* cdatastr = xstrdup(qstr); garmin_fs_t* gmsd = garmin_fs_t::find(waypt); if (gmsd == nullptr) { gmsd = garmin_fs_alloc(-1); diff --git a/garmin_fs.h b/garmin_fs.h index 25d3bbb18..d225339dc 100644 --- a/garmin_fs.h +++ b/garmin_fs.h @@ -24,14 +24,13 @@ #ifndef GARMIN_FS_H #define GARMIN_FS_H -#include // for size_t -#include // for int32_t, int16_t, uint16_t +#include // for int32_t, int16_t, uint16_t -#include // for QString -#include // for QXmlStreamWriter +#include // for QString #include "defs.h" -#include "formspec.h" // for FsChainFind, kFsGmsd, FormatSpecificData +#include "formspec.h" // for FsChainFind, kFsGmsd, FormatSpecificData +#include "src/core/xmlstreamwriter.h" // for XmlStreamWriter /* this order is used by most devices */ @@ -222,7 +221,7 @@ void garmin_fs_copy(void** dest, const void* src); /* for GPX */ void garmin_fs_xml_convert(int base_tag, int tag, const QString& qstr, Waypoint* waypt); -void garmin_fs_xml_fprint(const Waypoint* waypt, QXmlStreamWriter*); +void garmin_fs_xml_fprint(const Waypoint* waypt, gpsbabel::XmlStreamWriter*); /* common garmin_fs utilities */ diff --git a/gpx.cc b/gpx.cc index 9b8c8738f..ad5bfcaaa 100644 --- a/gpx.cc +++ b/gpx.cc @@ -21,6 +21,7 @@ #include "gpx.h" +#include // for assert #include // for lround #include // for sscanf #include // for strchr, strncpy @@ -514,10 +515,15 @@ GpxFormat::gpx_end(QStringView /*unused*/) delete link_; link_ = nullptr; } + if (wpt_fsdata != nullptr) { + wpt_tmp->fs.FsChainAdd(wpt_fsdata); + wpt_fsdata = nullptr; + } waypt_add(wpt_tmp); logpoint_ct = 0; cur_tag = nullptr; wpt_tmp = nullptr; + fs_ptr = nullptr; break; case tt_cache_name: wpt_tmp->notes = cdatastr; @@ -568,10 +574,10 @@ GpxFormat::gpx_end(QStringView /*unused*/) gc_log_date = gpsbabel::DateTime(); break; case tt_cache_favorite_points: - wpt_tmp->AllocGCData()->favorite_points = cdatastr.toInt(); + wpt_tmp->AllocGCData()->favorite_points = cdatastr.toInt(); break; case tt_cache_personal_note: - wpt_tmp->AllocGCData()->personal_note = cdatastr; + wpt_tmp->AllocGCData()->personal_note = cdatastr; break; /* @@ -612,6 +618,7 @@ GpxFormat::gpx_end(QStringView /*unused*/) delete rh_link_; rh_link_ = nullptr; } + fs_ptr = nullptr; break; case tt_rte_rtept: if (link_) { @@ -621,8 +628,13 @@ GpxFormat::gpx_end(QStringView /*unused*/) delete link_; link_ = nullptr; } + if (wpt_fsdata != nullptr) { + wpt_tmp->fs.FsChainAdd(wpt_fsdata); + wpt_fsdata = nullptr; + } route_add_wpt(rte_head, wpt_tmp); wpt_tmp = nullptr; + fs_ptr = nullptr; break; case tt_rte_desc: rte_head->rte_desc = cdatastr; @@ -653,9 +665,11 @@ GpxFormat::gpx_end(QStringView /*unused*/) delete rh_link_; rh_link_ = nullptr; } + fs_ptr = nullptr; break; case tt_trk_trkseg: next_trkpt_is_new_seg = 1; + fs_ptr = nullptr; break; case tt_trk_trkseg_trkpt: if (link_) { @@ -665,8 +679,13 @@ GpxFormat::gpx_end(QStringView /*unused*/) delete link_; link_ = nullptr; } + if (wpt_fsdata != nullptr) { + wpt_tmp->fs.FsChainAdd(wpt_fsdata); + wpt_fsdata = nullptr; + } track_add_wpt(trk_head, wpt_tmp); wpt_tmp = nullptr; + fs_ptr = nullptr; break; case tt_trk_desc: trk_head->rte_desc = cdatastr; @@ -727,6 +746,12 @@ GpxFormat::gpx_end(QStringView /*unused*/) case tt_wpttype_time: wpt_tmp->SetCreationTime(xml_parse_time(cdatastr)); break; + case tt_wpttype_magvar: + if (wpt_fsdata == nullptr) { + wpt_fsdata = new gpx_wpt_fsdata; + } + wpt_fsdata->magvar = cdatastr; + break; case tt_wpttype_geoidheight: wpt_tmp->set_geoidheight(cdatastr.toDouble()); break; @@ -736,6 +761,18 @@ GpxFormat::gpx_end(QStringView /*unused*/) case tt_wpttype_desc: wpt_tmp->notes = cdatastr; break; + case tt_wpttype_src: + if (wpt_fsdata == nullptr) { + wpt_fsdata = new gpx_wpt_fsdata; + } + wpt_fsdata->src = cdatastr; + break; + case tt_wpttype_type: + if (wpt_fsdata == nullptr) { + wpt_fsdata = new gpx_wpt_fsdata; + } + wpt_fsdata->type = cdatastr; + break; case tt_wpttype_pdop: wpt_tmp->pdop = cdatastr.toFloat(); break; @@ -745,6 +782,18 @@ GpxFormat::gpx_end(QStringView /*unused*/) case tt_wpttype_vdop: wpt_tmp->vdop = cdatastr.toFloat(); break; + case tt_wpttype_ageofdgpsdata: + if (wpt_fsdata == nullptr) { + wpt_fsdata = new gpx_wpt_fsdata; + } + wpt_fsdata->ageofdgpsdata = cdatastr; + break; + case tt_wpttype_dgpsid: + if (wpt_fsdata == nullptr) { + wpt_fsdata = new gpx_wpt_fsdata; + } + wpt_fsdata->dgpsid = cdatastr; + break; case tt_wpttype_sat: wpt_tmp->sat = cdatastr.toInt(); break; @@ -878,7 +927,7 @@ GpxFormat::wr_init(const QString& fname) // normalization makes them null. if (gpx_write_version.isNull() || (gpx_write_version < gpx_1_0)) { fatal(FatalMsg() << MYNAME ": gpx version number" - << gpx_write_version << "not valid."); + << gpx_write_version << "not valid."); } writer->setAutoFormatting(true); @@ -985,6 +1034,7 @@ GpxFormat::qualifiedName() const {"http://www.garmin.com/xmlschemas/GpxExtensions/v3", "gpxx"}, {"http://www.garmin.com/xmlschemas/TrackPointExtension/v1", "gpxtpx"}, {"http://www.groundspeak.com/cache/1/0", "groundspeak"}, + {"http://www.groundspeak.com/cache/1/0/1", "groundspeak"}, {"http://humminbird.com", "h"} }; @@ -998,7 +1048,7 @@ GpxFormat::qualifiedName() const void GpxFormat::read() { - for (bool atEnd = false; !reader->atEnd() && !atEnd;) { + for (bool atEnd = false; !reader->atEnd() && !atEnd;) { reader->readNext(); // do processing switch (reader->tokenType()) { @@ -1048,11 +1098,11 @@ GpxFormat::read() } } - if (reader->hasError()) { + if (reader->hasError()) { fatal(FatalMsg() << MYNAME << "Read error:" << reader->errorString() - << "File:" << iqfile->fileName() - << "Line:" << reader->lineNumber() - << "Column:" << reader->columnNumber()); + << "File:" << iqfile->fileName() + << "Line:" << reader->lineNumber() + << "Column:" << reader->columnNumber()); } } @@ -1065,7 +1115,7 @@ GpxFormat::write_attributes(const QXmlStreamAttributes& attributes) const } void -GpxFormat::fprint_xml_chain(XmlTag* tag, const Waypoint* wpt) const +GpxFormat::fprint_xml_chain(XmlTag* tag) const { while (tag) { writer->writeStartElement(tag->tagname); @@ -1078,7 +1128,7 @@ GpxFormat::fprint_xml_chain(XmlTag* tag, const Waypoint* wpt) const writer->writeCharacters(tag->cdata); } if (tag->child) { - fprint_xml_chain(tag->child, wpt); + fprint_xml_chain(tag->child); } writer->writeEndElement(); } @@ -1138,7 +1188,7 @@ GpxFormat::write_gpx_url(const route_head* rh) const * Order counts. */ void -GpxFormat::gpx_write_common_acc(const Waypoint* waypointp) const +GpxFormat::gpx_write_common_acc(const Waypoint* waypointp, const gpx_wpt_fsdata* fs_gpxwpt) const { const char* fix = nullptr; @@ -1179,13 +1229,15 @@ GpxFormat::gpx_write_common_acc(const Waypoint* waypointp) const if (waypointp->pdop) { writer->writeTextElement(QStringLiteral("pdop"), toString(waypointp->pdop)); } - /* TODO: ageofdgpsdata should go here */ - /* TODO: dgpsid should go here */ + if (fs_gpxwpt) { + writer->writeOptionalTextElement(QStringLiteral("ageofdgpsdata"), fs_gpxwpt->ageofdgpsdata); + writer->writeOptionalTextElement(QStringLiteral("dgpsid"), fs_gpxwpt->dgpsid); + } } void -GpxFormat::gpx_write_common_position(const Waypoint* waypointp, const gpx_point_type point_type) const +GpxFormat::gpx_write_common_position(const Waypoint* waypointp, const gpx_point_type point_type, const gpx_wpt_fsdata* fs_gpxwpt) const { if (waypointp->altitude != unknown_alt) { writer->writeTextElement(QStringLiteral("ele"), QString::number(waypointp->altitude, 'f', elevation_precision)); @@ -1201,7 +1253,9 @@ GpxFormat::gpx_write_common_position(const Waypoint* waypointp, const gpx_point_ writer->writeTextElement(QStringLiteral("speed"), toString(waypointp->speed_value())); } } - /* TODO: magvar should go here */ + if (fs_gpxwpt) { + writer->writeOptionalTextElement(QStringLiteral("magvar"), fs_gpxwpt->magvar); + } if (waypointp->geoidheight_has_value()) { writer->writeOptionalTextElement(QStringLiteral("geoidheight"),QString::number(waypointp->geoidheight_value(), 'f', 1)); } @@ -1210,92 +1264,90 @@ GpxFormat::gpx_write_common_position(const Waypoint* waypointp, const gpx_point_ void GpxFormat::gpx_write_common_extensions(const Waypoint* waypointp, const gpx_point_type point_type) const { - // gpx version we are writing is >= 1.1. - garmin_fs_t* gmsd = (opt_garminext) ? garmin_fs_t::find(waypointp) : nullptr; // only needed if garmin extensions selected + assert(gpx_write_version >= gpx_1_1); - if ((opt_humminbirdext && (waypointp->depth_has_value() || waypointp->temperature_has_value())) || - (opt_garminext && gpxpt_route==point_type && gmsd != nullptr && gmsd->ilinks != nullptr) || - (opt_garminext && gpxpt_waypoint==point_type && (waypointp->proximity_has_value() || waypointp->temperature_has_value() || waypointp->depth_has_value())) || - (opt_garminext && gpxpt_track==point_type && (waypointp->temperature_has_value() || waypointp->depth_has_value() || waypointp->heartrate != 0 || waypointp->cadence != 0))) { - writer->writeStartElement(QStringLiteral("extensions")); - if (opt_humminbirdext) { - if (waypointp->depth_has_value()) { - writer->writeTextElement(QStringLiteral("h:depth"), toString(waypointp->depth_value() * 100.0)); + writer->stackOptionalStartElement(QStringLiteral("extensions")); + + if (opt_humminbirdext) { + if (waypointp->depth_has_value()) { + writer->stackTextElement(QStringLiteral("h:depth"), toString(waypointp->depth_value() * 100.0)); + } + if (waypointp->temperature_has_value()) { + writer->stackTextElement(QStringLiteral("h:temperature"), toString(waypointp->temperature_value())); + } + } + + if (opt_garminext) { + // Although not required by the schema we assume that gpxx:WaypointExtension must be a child of gpx:wpt. + // Although not required by the schema we assume that gpxx:RoutePointExtension must be a child of gpx:rtept. + // Although not required by the schema we assume that gpxx:TrackPointExtension must be a child of gpx:trkpt. + // Although not required by the schema we assume that gpxtpx:TrackPointExtension must be a child of gpx:trkpt. + switch (point_type) { + case gpxpt_waypoint: + writer->stackOptionalStartElement(QStringLiteral("gpxx:WaypointExtension")); + if (waypointp->proximity_has_value()) { + writer->stackTextElement(QStringLiteral("gpxx:Proximity"), toString(waypointp->proximity_value())); } if (waypointp->temperature_has_value()) { - writer->writeTextElement(QStringLiteral("h:temperature"), toString(waypointp->temperature_value())); + writer->stackTextElement(QStringLiteral("gpxx:Temperature"), toString(waypointp->temperature_value())); } - } - - if (opt_garminext) { - // Although not required by the schema we assume that gpxx:WaypointExtension must be a child of gpx:wpt. - // Although not required by the schema we assume that gpxx:RoutePointExtension must be a child of gpx:rtept. - // Although not required by the schema we assume that gpxx:TrackPointExtension must be a child of gpx:trkpt. - // Although not required by the schema we assume that gpxtpx:TrackPointExtension must be a child of gpx:trkpt. - switch (point_type) { - case gpxpt_waypoint: - if (waypointp->proximity_has_value() || waypointp->temperature_has_value() || waypointp->depth_has_value()) { - writer->writeStartElement(QStringLiteral("gpxx:WaypointExtension")); - if (waypointp->proximity_has_value()) { - writer->writeTextElement(QStringLiteral("gpxx:Proximity"), toString(waypointp->proximity_value())); - } - if (waypointp->temperature_has_value()) { - writer->writeTextElement(QStringLiteral("gpxx:Temperature"), toString(waypointp->temperature_value())); - } - if (waypointp->depth_has_value()) { - writer->writeTextElement(QStringLiteral("gpxx:Depth"), toString(waypointp->depth_value())); - } - writer->writeEndElement(); // "gpxx:WaypointExtension" - } - break; - case gpxpt_route: - if (gmsd != nullptr && gpxpt_route==point_type && gmsd->ilinks != nullptr) { - writer->writeStartElement(QStringLiteral("gpxx:RoutePointExtension")); - garmin_ilink_t* link = gmsd->ilinks; - garmin_ilink_t* prior = nullptr; // GDB files sometime contain repeated point; omit them - while (link != nullptr) { - if (prior == nullptr || prior->lat != link->lat || prior->lon != link->lon) { - writer->writeStartElement(QStringLiteral("gpxx:rpt")); - writer->writeAttribute(QStringLiteral("lat"), toString(link->lat)); - writer->writeAttribute(QStringLiteral("lon"), toString(link->lon)); - writer->writeEndElement(); // "gpxx:rpt" - } - prior = link; - link = link->next; - } - writer->writeEndElement(); // "gpxx:RoutePointExtension" - } - break; - case gpxpt_track: - if (waypointp->temperature_has_value() || waypointp->depth_has_value() || waypointp->heartrate != 0 || waypointp->cadence != 0) { - // gpxtpx:TrackPointExtension is a replacement for gpxx:TrackPointExtension. - writer->writeStartElement(QStringLiteral("gpxtpx:TrackPointExtension")); - if (waypointp->temperature_has_value()) { - writer->writeTextElement(QStringLiteral("gpxtpx:atemp"), toString(waypointp->temperature_value())); - } - if (waypointp->depth_has_value()) { - writer->writeTextElement(QStringLiteral("gpxtpx:depth"), toString(waypointp->depth_value())); - } - if (waypointp->heartrate != 0) { - writer->writeTextElement(QStringLiteral("gpxtpx:hr"), QString::number(waypointp->heartrate)); - } - if (waypointp->cadence != 0) { - writer->writeTextElement(QStringLiteral("gpxtpx:cad"), QString::number(waypointp->cadence)); + if (waypointp->depth_has_value()) { + writer->stackTextElement(QStringLiteral("gpxx:Depth"), toString(waypointp->depth_value())); + } + writer->stackEndElement(); // gpxx:WaypointExtension + break; + case gpxpt_route: { + garmin_fs_t* gmsd = garmin_fs_t::find(waypointp); + if (gmsd != nullptr && gmsd->ilinks != nullptr) { + writer->stackOptionalStartElement(QStringLiteral("gpxx:RoutePointExtension")); + garmin_ilink_t* link = gmsd->ilinks; + garmin_ilink_t* prior = nullptr; // GDB files sometime contain repeated point; omit them + while (link != nullptr) { + if (prior == nullptr || prior->lat != link->lat || prior->lon != link->lon) { + writer->stackStartElement(QStringLiteral("gpxx:rpt")); + writer->stackAttribute(QStringLiteral("lat"), toString(link->lat)); + writer->stackAttribute(QStringLiteral("lon"), toString(link->lon)); + writer->stackEndElement(); // "gpxx:rpt" } - writer->writeEndElement(); // "gpxtpx:TrackPointExtension" + prior = link; + link = link->next; } - break; + writer->stackEndElement(); // gpxx:RoutePointExtension } } - - writer->writeEndElement(); // "extensions" + break; + case gpxpt_track: + // gpxtpx:TrackPointExtension is a replacement for gpxx:TrackPointExtension. + writer->stackOptionalStartElement(QStringLiteral("gpxtpx:TrackPointExtension")); + if (waypointp->temperature_has_value()) { + writer->stackTextElement(QStringLiteral("gpxtpx:atemp"), toString(waypointp->temperature_value())); + } + if (waypointp->depth_has_value()) { + writer->stackTextElement(QStringLiteral("gpxtpx:depth"), toString(waypointp->depth_value())); + } + if (waypointp->heartrate != 0) { + writer->stackTextElement(QStringLiteral("gpxtpx:hr"), QString::number(waypointp->heartrate)); + } + if (waypointp->cadence != 0) { + writer->stackTextElement(QStringLiteral("gpxtpx:cad"), QString::number(waypointp->cadence)); + } + writer->stackEndElement(); // gpxtpx:TrackPointExtension + break; + } } + + writer->stackEndElement(); // "extensions" } void -GpxFormat::gpx_write_common_description(const Waypoint* waypointp, const QString& oname) const +GpxFormat::gpx_write_common_description(const Waypoint* waypointp, const gpx_point_type point_type, const gpx_wpt_fsdata* fs_gpxwpt) const { + QString oname; + if (!((point_type == gpxpt_track) && waypointp->wpt_flags.shortname_is_synthetic)) { + oname = global_opts.synthesize_shortnames ? + mkshort_handle->mkshort_from_wpt(waypointp) : waypointp->shortname; + } writer->writeOptionalTextElement(QStringLiteral("name"), oname); writer->writeOptionalTextElement(QStringLiteral("cmt"), waypointp->description); @@ -1304,10 +1356,24 @@ GpxFormat::gpx_write_common_description(const Waypoint* waypointp, const QString } else { writer->writeOptionalTextElement(QStringLiteral("desc"), waypointp->description); } - /* TODO: src should go here */ + if (fs_gpxwpt) { + writer->writeOptionalTextElement(QStringLiteral("src"), fs_gpxwpt->src); + } write_gpx_url(waypointp); writer->writeOptionalTextElement(QStringLiteral("sym"), waypointp->icon_descr); - /* TODO: type should go here */ + if (fs_gpxwpt) { + writer->writeOptionalTextElement(QStringLiteral("type"), fs_gpxwpt->type); + } +} + +void GpxFormat::gpx_write_common_core(const Waypoint* waypointp, + const gpx_point_type point_type) const +{ + const auto* fs_gpxwpt = reinterpret_cast(waypointp->fs.FsChainFind(kFsGpxWpt)); + + gpx_write_common_position(waypointp, point_type, fs_gpxwpt); + gpx_write_common_description(waypointp, point_type, fs_gpxwpt); + gpx_write_common_acc(waypointp, fs_gpxwpt); } void @@ -1317,19 +1383,22 @@ GpxFormat::gpx_waypt_pr(const Waypoint* waypointp) const writer->writeAttribute(QStringLiteral("lat"), toString(waypointp->latitude)); writer->writeAttribute(QStringLiteral("lon"), toString(waypointp->longitude)); - QString oname = global_opts.synthesize_shortnames ? - mkshort_handle->mkshort_from_wpt(waypointp) : - waypointp->shortname; - gpx_write_common_position(waypointp, gpxpt_waypoint); - gpx_write_common_description(waypointp, oname); - gpx_write_common_acc(waypointp); + gpx_write_common_core(waypointp, gpxpt_waypoint); if (!(opt_humminbirdext || opt_garminext)) { const auto* fs_gpx = reinterpret_cast(waypointp->fs.FsChainFind(kFsGpx)); auto* gmsd = garmin_fs_t::find(waypointp); /* gARmIN sPECIAL dATA */ if (fs_gpx) { if (! gmsd) { - fprint_xml_chain(fs_gpx->tag, waypointp); + if (fs_gpx->tag) { + if (gpx_write_version > gpx_1_0) { + writer->writeStartElement("extensions"); + } + fprint_xml_chain(fs_gpx->tag); + if (gpx_write_version > gpx_1_0) { + writer->writeEndElement(); + } + } } } if (gmsd && (gpx_write_version > gpx_1_0)) { @@ -1356,23 +1425,21 @@ GpxFormat::gpx_track_hdr(const route_head* rte) writer->writeTextElement(QStringLiteral("number"), QString::number(rte->rte_num)); } - if (gpx_write_version > gpx_1_0) { - if (!(opt_humminbirdext || opt_garminext)) { - const auto* fs_gpx = reinterpret_cast(rte->fs.FsChainFind(kFsGpx)); - if (fs_gpx) { - fprint_xml_chain(fs_gpx->tag, nullptr); - } - } else if (opt_garminext) { - if (rte->line_color.bbggrr > unknown_color) { - int ci = gt_color_index_by_rgb(rte->line_color.bbggrr); - if (ci > 0) { - writer->writeStartElement(QStringLiteral("extensions")); - writer->writeStartElement(QStringLiteral("gpxx:TrackExtension")); - writer->writeTextElement(QStringLiteral("gpxx:DisplayColor"), QStringLiteral("%1") - .arg(gt_color_name(ci))); - writer->writeEndElement(); // Close gpxx:TrackExtension tag - writer->writeEndElement(); // Close extensions tag - } + if (!(opt_humminbirdext || opt_garminext)) { + const auto* fs_gpx = reinterpret_cast(rte->fs.FsChainFind(kFsGpx)); + if (fs_gpx) { + fprint_xml_chain(fs_gpx->tag); + } + } else if (opt_garminext && (gpx_write_version > gpx_1_0)) { + if (rte->line_color.bbggrr > unknown_color) { + int ci = gt_color_index_by_rgb(rte->line_color.bbggrr); + if (ci > 0) { + writer->writeStartElement(QStringLiteral("extensions")); + writer->writeStartElement(QStringLiteral("gpxx:TrackExtension")); + writer->writeTextElement(QStringLiteral("gpxx:DisplayColor"), QStringLiteral("%1") + .arg(gt_color_name(ci))); + writer->writeEndElement(); // Close gpxx:TrackExtension tag + writer->writeEndElement(); // Close extensions tag } } } @@ -1394,20 +1461,12 @@ GpxFormat::gpx_track_disp(const Waypoint* waypointp) const writer->writeAttribute(QStringLiteral("lat"), toString(waypointp->latitude)); writer->writeAttribute(QStringLiteral("lon"), toString(waypointp->longitude)); - gpx_write_common_position(waypointp, gpxpt_track); - - QString oname = global_opts.synthesize_shortnames ? - mkshort_handle->mkshort_from_wpt(waypointp) : - waypointp->shortname; - gpx_write_common_description(waypointp, - waypointp->wpt_flags.shortname_is_synthetic ? - nullptr : oname); - gpx_write_common_acc(waypointp); + gpx_write_common_core(waypointp, gpxpt_track); if (!(opt_humminbirdext || opt_garminext)) { const auto* fs_gpx = reinterpret_cast(waypointp->fs.FsChainFind(kFsGpx)); if (fs_gpx) { - fprint_xml_chain(fs_gpx->tag, waypointp); + fprint_xml_chain(fs_gpx->tag); } } else { gpx_write_common_extensions(waypointp, gpxpt_track); @@ -1454,25 +1513,23 @@ GpxFormat::gpx_route_hdr(const route_head* rte) const writer->writeTextElement(QStringLiteral("number"), QString::number(rte->rte_num)); } - if (gpx_write_version > gpx_1_0) { - if (!(opt_humminbirdext || opt_garminext)) { - const auto* fs_gpx = reinterpret_cast(rte->fs.FsChainFind(kFsGpx)); - if (fs_gpx) { - fprint_xml_chain(fs_gpx->tag, nullptr); - } - } else if (opt_garminext) { - if (rte->line_color.bbggrr > unknown_color) { - int ci = gt_color_index_by_rgb(rte->line_color.bbggrr); - if (ci > 0) { - writer->writeStartElement(QStringLiteral("extensions")); - writer->writeStartElement(QStringLiteral("gpxx:RouteExtension")); - // FIXME: the value to use for IsAutoNamed is questionable. - writer->writeTextElement(QStringLiteral("gpxx:IsAutoNamed"), rte->rte_name.isEmpty()? QStringLiteral("true") : QStringLiteral("false")); // Required element - writer->writeTextElement(QStringLiteral("gpxx:DisplayColor"), QStringLiteral("%1") - .arg(gt_color_name(ci))); - writer->writeEndElement(); // Close gpxx:RouteExtension tag - writer->writeEndElement(); // Close extensions tag - } + if (!(opt_humminbirdext || opt_garminext)) { + const auto* fs_gpx = reinterpret_cast(rte->fs.FsChainFind(kFsGpx)); + if (fs_gpx) { + fprint_xml_chain(fs_gpx->tag); + } + } else if (opt_garminext && (gpx_write_version > gpx_1_0)) { + if (rte->line_color.bbggrr > unknown_color) { + int ci = gt_color_index_by_rgb(rte->line_color.bbggrr); + if (ci > 0) { + writer->writeStartElement(QStringLiteral("extensions")); + writer->writeStartElement(QStringLiteral("gpxx:RouteExtension")); + // FIXME: the value to use for IsAutoNamed is questionable. + writer->writeTextElement(QStringLiteral("gpxx:IsAutoNamed"), rte->rte_name.isEmpty()? QStringLiteral("true") : QStringLiteral("false")); // Required element + writer->writeTextElement(QStringLiteral("gpxx:DisplayColor"), QStringLiteral("%1") + .arg(gt_color_name(ci))); + writer->writeEndElement(); // Close gpxx:RouteExtension tag + writer->writeEndElement(); // Close extensions tag } } } @@ -1485,17 +1542,12 @@ GpxFormat::gpx_route_disp(const Waypoint* waypointp) const writer->writeAttribute(QStringLiteral("lat"), toString(waypointp->latitude)); writer->writeAttribute(QStringLiteral("lon"), toString(waypointp->longitude)); - QString oname = global_opts.synthesize_shortnames ? - mkshort_handle->mkshort_from_wpt(waypointp) : - waypointp->shortname; - gpx_write_common_position(waypointp, gpxpt_route); - gpx_write_common_description(waypointp, oname); - gpx_write_common_acc(waypointp); + gpx_write_common_core(waypointp, gpxpt_route); if (!(opt_humminbirdext || opt_garminext)) { const auto* fs_gpx = reinterpret_cast(waypointp->fs.FsChainFind(kFsGpx)); if (fs_gpx) { - fprint_xml_chain(fs_gpx->tag, waypointp); + fprint_xml_chain(fs_gpx->tag); } } else { gpx_write_common_extensions(waypointp, gpxpt_route); diff --git a/gpx.h b/gpx.h index 1fd5b9692..0fee9ae46 100644 --- a/gpx.h +++ b/gpx.h @@ -67,6 +67,28 @@ class GpxFormat : public Format void exit() override; private: + /* + * This structure holds the element contents of elements in the + * the gpx namespace that are only passed from the gpx reader to + * the gpx writer. + * We explcitly write these instead of passing them through so they + * are written in the correct sequence. + */ + struct gpx_wpt_fsdata : FormatSpecificData { + gpx_wpt_fsdata() : FormatSpecificData(kFsGpxWpt) {} + + gpx_wpt_fsdata* clone() const override + { + return new gpx_wpt_fsdata(*this); + } + + QString magvar; + QString src; + QString type; + QString ageofdgpsdata; + QString dgpsid; + }; + enum gpx_point_type { gpxpt_waypoint, gpxpt_track, @@ -91,10 +113,12 @@ class GpxFormat : public Format tt_wpt, tt_wpttype_ele, tt_wpttype_time, + tt_wpttype_magvar, tt_wpttype_geoidheight, tt_wpttype_name, tt_wpttype_cmt, tt_wpttype_desc, + tt_wpttype_src, tt_wpttype_url, /* Not in GPX 1.1 */ tt_wpttype_urlname, /* Not in GPX 1.1 */ tt_wpttype_link, /* New in GPX 1.1 */ @@ -107,6 +131,8 @@ class GpxFormat : public Format tt_wpttype_hdop, /* HDOPS are common for all three */ tt_wpttype_vdop, /* VDOPS are common for all three */ tt_wpttype_pdop, /* PDOPS are common for all three */ + tt_wpttype_ageofdgpsdata, + tt_wpttype_dgpsid, tt_cache, tt_cache_name, tt_cache_container, @@ -197,15 +223,16 @@ class GpxFormat : public Format void gpx_cdata(QStringView s); QString qualifiedName() const; void write_attributes(const QXmlStreamAttributes& attributes) const; - void fprint_xml_chain(XmlTag* tag, const Waypoint* wpt) const; + void fprint_xml_chain(XmlTag* tag) const; void write_gpx_url(const UrlList& urls) const; void write_gpx_url(const Waypoint* waypointp) const; void write_gpx_url(const route_head* rh) const; - void gpx_write_common_acc(const Waypoint* waypointp) const; - void gpx_write_common_position(const Waypoint* waypointp, gpx_point_type point_type) const; + void gpx_write_common_acc(const Waypoint* waypointp, const gpx_wpt_fsdata* fs_gpxwpt) const; + void gpx_write_common_position(const Waypoint* waypointp, gpx_point_type point_type, const gpx_wpt_fsdata* fs_gpxwpt) const; void gpx_write_common_extensions(const Waypoint* waypointp, gpx_point_type point_type) const; - void gpx_write_common_description(const Waypoint* waypointp, const QString& oname) const; + void gpx_write_common_description(const Waypoint* waypointp, gpx_point_type point_type, const gpx_wpt_fsdata* fs_gpxwpt) const; void gpx_waypt_pr(const Waypoint* waypointp) const; + void gpx_write_common_core(const Waypoint* waypointp, gpx_point_type point_type) const; void gpx_track_hdr(const route_head* rte); void gpx_track_disp(const Waypoint* waypointp) const; void gpx_track_tlr(const route_head* unused); @@ -261,6 +288,7 @@ class GpxFormat : public Format int next_trkpt_is_new_seg{}; FormatSpecificDataList* fs_ptr{}; + gpx_wpt_fsdata* wpt_fsdata{nullptr}; /* * The file-level information. @@ -298,9 +326,7 @@ class GpxFormat : public Format {"/gpx/metadata/" name, {type, false}} #define GEOTAG(type,name) \ - {"/gpx/wpt/groundspeak:cache/groundspeak:" name, {type, true}}, \ - {"/gpx/wpt/extensions/cache/" name, {type, true}}, \ - {"/gpx/wpt/geocache/" name, {type, true}} /* opencaching.de */ + {"/gpx/wpt/groundspeak:cache/groundspeak:" name, {type, true}} #define GPXWPTTYPETAG(name,type,passthrough) \ {"/gpx/wpt/" name, {type, passthrough}}, \ @@ -338,18 +364,14 @@ class GpxFormat : public Format GEOTAG(tt_cache_difficulty, "difficulty"), GEOTAG(tt_cache_terrain, "terrain"), GEOTAG(tt_cache_hint, "encoded_hints"), - GEOTAG(tt_cache_hint, "hints"), /* opencaching.de */ GEOTAG(tt_cache_desc_short, "short_description"), GEOTAG(tt_cache_desc_long, "long_description"), GEOTAG(tt_cache_placer, "owner"), GEOTAG(tt_cache_favorite_points, "favorite_points"), GEOTAG(tt_cache_personal_note, "personal_note"), {"/gpx/wpt/groundspeak:cache/groundspeak:logs/groundspeak:log/groundspeak:log_wpt", {tt_cache_log_wpt, true}}, - {"/gpx/wpt/extensions/cache/logs/log/log_wpt", {tt_cache_log_wpt, true}}, {"/gpx/wpt/groundspeak:cache/groundspeak:logs/groundspeak:log/groundspeak:type", {tt_cache_log_type, true}}, - {"/gpx/wpt/extensions/cache/logs/log/type", {tt_cache_log_type, true}}, {"/gpx/wpt/groundspeak:cache/groundspeak:logs/groundspeak:log/groundspeak:date", {tt_cache_log_date, true}}, - {"/gpx/wpt/extensions/cache/logs/log/date", {tt_cache_log_date, true}}, {"/gpx/wpt/extensions", {tt_wpt_extensions, false}}, @@ -409,22 +431,26 @@ class GpxFormat : public Format /* Common to tracks, routes, and waypts */ GPXWPTTYPETAG("ele", tt_wpttype_ele, false), GPXWPTTYPETAG("time", tt_wpttype_time, false), + GPXWPTTYPETAG("magvar", tt_wpttype_magvar, false), GPXWPTTYPETAG("geoidheight", tt_wpttype_geoidheight, false), GPXWPTTYPETAG("name", tt_wpttype_name, false), GPXWPTTYPETAG("cmt", tt_wpttype_cmt, false), GPXWPTTYPETAG("desc", tt_wpttype_desc, false), + GPXWPTTYPETAG("src", tt_wpttype_src, false), GPXWPTTYPETAG("url", tt_wpttype_url, false), /* GPX 1.0 */ GPXWPTTYPETAG("urlname", tt_wpttype_urlname, false), /* GPX 1.0 */ GPXWPTTYPETAG("link", tt_wpttype_link, false), /* GPX 1.1 */ GPXWPTTYPETAG("link/text", tt_wpttype_link_text, false), /* GPX 1.1 */ GPXWPTTYPETAG("link/type", tt_wpttype_link_type, false), /* GPX 1.1 */ GPXWPTTYPETAG("sym", tt_wpttype_sym, false), - GPXWPTTYPETAG("type", tt_wpttype_type, true), + GPXWPTTYPETAG("type", tt_wpttype_type, false), GPXWPTTYPETAG("fix", tt_wpttype_fix, false), GPXWPTTYPETAG("sat", tt_wpttype_sat, false), GPXWPTTYPETAG("hdop", tt_wpttype_hdop, false), GPXWPTTYPETAG("vdop", tt_wpttype_vdop, false), GPXWPTTYPETAG("pdop", tt_wpttype_pdop, false), + GPXWPTTYPETAG("ageofdgpsdata", tt_wpttype_ageofdgpsdata, false), + GPXWPTTYPETAG("dgpsid", tt_wpttype_dgpsid, false), }; QVector gpx_args = { diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 97062e8a9..a64f6813a 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -25,8 +25,8 @@ endif() find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network SerialPort Widgets Xml REQUIRED) list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::SerialPort Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Xml) -if(${Qt${QT_VERSION_MAJOR}Core_VERSION} VERSION_LESS 5.12) - message(FATAL_ERROR "Qt version ${Qt${QT_VERSION_MAJOR}Core_VERSION} found, but version 5.12 or newer is required.") +if(${Qt${QT_VERSION_MAJOR}Core_VERSION} VERSION_LESS 5.15) + message(FATAL_ERROR "Qt version ${Qt${QT_VERSION_MAJOR}Core_VERSION} found, but version 5.15 or newer is required.") else() message(STATUS "Using Qt${QT_VERSION_MAJOR} version ${Qt${QT_VERSION_MAJOR}Core_VERSION}") endif() diff --git a/html.cc b/html.cc index 3273a1d96..0385c2687 100644 --- a/html.cc +++ b/html.cc @@ -168,7 +168,7 @@ HtmlFormat::html_disp(const Waypoint* wpt) const logpart = curlog->xml_findfirst(u"groundspeak:date"); if (logpart) { - gpsbabel::DateTime logtime = xml_parse_time(logpart->cdata).toLocalTime(); + gpsbabel::DateTime logtime = xml_parse_time(logpart->cdata).toUTC(); *file_out << "" << logtime.toString(u"yyyy-MM-dd") << "
\n"; } diff --git a/igc.cc b/igc.cc index db86b97bb..817f527a1 100644 --- a/igc.cc +++ b/igc.cc @@ -683,11 +683,7 @@ void IgcFormat::wr_header() // Other header data may have been stored in track description if (track && track->rte_desc.startsWith(HDRMAGIC)) { QString desc = track->rte_desc.mid(QString(HDRMAGIC).size()); -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - const QStringList fields = desc.split(HDRDELIM, QString::SkipEmptyParts); -#else const QStringList fields = desc.split(HDRDELIM, Qt::SkipEmptyParts); -#endif for (const auto& field : fields) { gbfprintf(file_out, "%s\r\n", CSTR(field)); } diff --git a/main.cc b/main.cc index f99f47903..21cafdee1 100644 --- a/main.cc +++ b/main.cc @@ -738,7 +738,7 @@ main(int argc, char* argv[]) // MIN_QT_VERSION in GPSBabel.pro should correspond to the QT_VERSION_CHECK // arguments in main.cc and gui/main.cc and the version check in // CMakeLists.txt, gui/CMakeLists.txt. -#if (QT_VERSION < QT_VERSION_CHECK(5, 12, 0)) +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) #error This version of Qt is not supported. #endif diff --git a/reference/basecamp~gpx.gpx b/reference/basecamp~gpx.gpx index 77234b87f..fe07c4a21 100644 --- a/reference/basecamp~gpx.gpx +++ b/reference/basecamp~gpx.gpx @@ -13,6 +13,7 @@ Hwy 119 Hwy 119 Flag, Blue + user SymbolAndName @@ -25,6 +26,7 @@ Hwy 72 Hwy 72 Flag, Blue + user SymbolAndName diff --git a/reference/earth-gc.kml b/reference/earth-gc.kml index 94d1bfde0..9fe1aa587 100644 --- a/reference/earth-gc.kml +++ b/reference/earth-gc.kml @@ -5,11 +5,11 @@ 2002-08-15T07:00:00Z - 2003-06-29T07:00:00Z + 2003-06-29T00:00:00Z - -79.930833 + -79.930834 41.027500 - 2109328.437865 + 2109328.427385