diff --git a/CMakeLists.txt b/CMakeLists.txt index 38f2ff4cd..3308221d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,6 +210,7 @@ set(HEADERS gbfile.h gbser.h gbser_private.h + garmin_txt.h garmin_xt.h gdb.h geocache.h diff --git a/garmin_txt.cc b/garmin_txt.cc index f7b1575fd..81f5d1b59 100644 --- a/garmin_txt.cc +++ b/garmin_txt.cc @@ -20,92 +20,60 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#if CSVFMTS_ENABLED -#include "defs.h" +#include "garmin_txt.h" -#if CSVFMTS_ENABLED #include // for for_each, sort -#include // for array +#include // for array, array<>::iterator #include // for toupper #include // for fabs, floor #include // for uint16_t #include // for sscanf, fprintf, snprintf, stderr #include // for abs #include // for strstr, strlen -#include // for time_t, gmtime, localtime, strftime -#include // for optional -#include // for as_const, pair, make_pair +#include // for gmtime, time_t, localtime, strftime, tm +#include // for optional +#include // for add_const_t +#include // for pair, as_const, make_pair #include // for QByteArray #include // for QChar, QChar::Other_Control #include // for QDateTime -#include // for QIODevice, QIODevice::ReadOnly, QIODevice::WriteOnly +#include // for QDebug +#include // for QIODevice, QIODeviceBase::ReadOnly, QIODeviceBase::WriteOnly #include // for QList, QList<>::const_iterator #include // for QString, operator!= #include // for QStringList +#include // for qMakeStringPrivate, QStringLiteral #include // for QTextStream #include // for QVector #include // for CaseInsensitive #include // for qRound, qPrintable +#include "defs.h" #include "csv_util.h" // for csv_linesplit #include "formspec.h" // for FormatSpecificDataList #include "garmin_fs.h" // for garmin_fs_t #include "garmin_tables.h" // for gt_display_modes_e, gt_find_desc_from_icon_number, gt_find_icon_number_from_desc, gt_get_mps_grid_longname, gt_lookup_datum_index, gt_lookup_grid_type, GDB, gt_get_icao_cc, gt_get_icao_country, gt_get_mps_datum_name, gt_waypt_class_names, GT_DISPLAY_MODE... #include "jeeps/gpsmath.h" // for GPS_Math_Known_Datum_To_UTM_EN, GPS_Math_WGS84_To_Known_Datum_M, GPS_Math_WGS84_To_Swiss_EN, GPS_Math_WGS84_To_UKOSMap_M #include "src/core/datetime.h" // for DateTime -#include "src/core/logging.h" // for Fatal +#include "src/core/logging.h" // for FatalMsg #include "src/core/textstream.h" // for TextStream #define MYNAME "garmin_txt" -struct gtxt_flags_t { - unsigned int metric:1; - unsigned int celsius:1; - unsigned int utc:1; - unsigned int enum_waypoints:1; - unsigned int route_header_written:1; - unsigned int track_header_written:1; -}; - -static gpsbabel::TextStream* fin = nullptr; -static gpsbabel::TextStream* fout = nullptr; -static route_head* current_trk; -static route_head* current_rte; -static int waypoints; -static int routepoints; -static const Waypoint** wpt_a; -static int wpt_a_ct; -static grid_type grid_index; -static int datum_index; -static const char* datum_str; -static int current_line; -static QString date_time_format; -static int precision = 3; -static time_t utc_offs = 0; -static gtxt_flags_t gtxt_flags; - -enum header_type { - waypt_header = 0, - rtept_header, - trkpt_header, - route_header, - track_header, - unknown_header +const QVector GarminTxtFormat::headers = { + "Name\tDescription\tType\tPosition\tAltitude\tDepth\tProximity\tTemperature\t" + "Display Mode\tColor\tSymbol\tFacility\tCity\tState\tCountry\t" + "Date Modified\tLink\tCategories", + "Waypoint Name\tDistance\tLeg Length\tCourse", + "Position\tTime\tAltitude\tDepth\tTemperature\tLeg Length\tLeg Time\tLeg Speed\tLeg Course", + "Name\tLength\tCourse\tWaypoints\tLink", + "Name\tStart Time\tElapsed Time\tLength\tAverage Speed\tLink" }; -inline header_type& operator++(header_type& s) // prefix -{ - return s = static_cast(s + 1); -} -inline header_type operator++(header_type& s, int) // postfix -{ - header_type ret(s); - ++s; - return ret; -} - inline gt_display_modes_e& operator++(gt_display_modes_e& s) // prefix { return s = static_cast(s + 1); @@ -117,78 +85,22 @@ inline gt_display_modes_e operator++(gt_display_modes_e& s, int) // postfix return ret; } -static std::array>, unknown_header> header_mapping_info; -static QStringList header_column_names; - -static constexpr double kGarminUnknownAlt = 1.0e25; -static constexpr char kDefaultDateFormat[] = "dd/mm/yyyy"; -static constexpr char kDefaultTimeFormat[] = "HH:mm:ss"; - -static bool is_valid_alt(double alt) +bool GarminTxtFormat::is_valid_alt(double alt) { return (alt != unknown_alt) && (alt < kGarminUnknownAlt); } -static char* opt_datum = nullptr; -static char* opt_dist = nullptr; -static char* opt_temp = nullptr; -static char* opt_date_format = nullptr; -static char* opt_time_format = nullptr; -static char* opt_precision = nullptr; -static char* opt_utc = nullptr; -static char* opt_grid = nullptr; - -static -QVector garmin_txt_args = { - {"date", &opt_date_format, "Read/Write date format (i.e. yyyy/mm/dd)", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, - {"datum", &opt_datum, "GPS datum (def. WGS 84)", "WGS 84", ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, - {"dist", &opt_dist, "Distance unit [m=metric, s=statute]", "m", ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, - {"grid", &opt_grid, "Write position using this grid.", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, - {"prec", &opt_precision, "Precision of coordinates", "3", ARGTYPE_INT, ARG_NOMINMAX, nullptr}, - {"temp", &opt_temp, "Temperature unit [c=Celsius, f=Fahrenheit]", "c", ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, - {"time", &opt_time_format, "Read/Write time format (i.e. HH:mm:ss xx)", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, - {"utc", &opt_utc, "Write timestamps with offset x to UTC time", nullptr, ARGTYPE_INT, "-23", "+23", nullptr}, -}; - -class PathInfo -{ -public: - double length {0}; - time_t start {0}; - time_t time {0}; - double speed {0}; - double total {0}; - int count {0}; - const Waypoint* prev_wpt {nullptr}; - const Waypoint* first_wpt {nullptr}; - const Waypoint* last_wpt {nullptr}; -}; - -static PathInfo* route_info; -static int route_idx; -static PathInfo* cur_info; - -static const QVector headers = { - "Name\tDescription\tType\tPosition\tAltitude\tDepth\tProximity\tTemperature\t" - "Display Mode\tColor\tSymbol\tFacility\tCity\tState\tCountry\t" - "Date Modified\tLink\tCategories", - "Waypoint Name\tDistance\tLeg Length\tCourse", - "Position\tTime\tAltitude\tDepth\tTemperature\tLeg Length\tLeg Time\tLeg Speed\tLeg Course", - "Name\tLength\tCourse\tWaypoints\tLink", - "Name\tStart Time\tElapsed Time\tLength\tAverage Speed\tLink" -}; - /* helpers */ -static const char* -get_option_val(const char* option, const char* def) +const char* +GarminTxtFormat::get_option_val(const char* option, const char* def) { const char* c = (option != nullptr) ? option : def; return c; } -static void -init_date_and_time_format() +void +GarminTxtFormat::init_date_and_time_format() { // This is old, and weird, code.. date_time_format is a global that's // explicitly malloced and freed elsewhere. This isn't very C++ at all, @@ -202,8 +114,8 @@ init_date_and_time_format() date_time_format = QStringLiteral("%1 %2").arg(d1, t1); } -static void -convert_datum(const Waypoint* wpt, double* dest_lat, double* dest_lon) +void +GarminTxtFormat::convert_datum(const Waypoint* wpt, double* dest_lat, double* dest_lon) const { double alt; @@ -218,8 +130,8 @@ convert_datum(const Waypoint* wpt, double* dest_lat, double* dest_lon) /* Waypoint preparation */ -static void -enum_waypt_cb(const Waypoint* wpt) +void +GarminTxtFormat::enum_waypt_cb(const Waypoint* wpt) { const garmin_fs_t* gmsd = garmin_fs_t::find(wpt); int wpt_class = garmin_fs_t::get_wpt_class(gmsd, 0); @@ -244,21 +156,21 @@ enum_waypt_cb(const Waypoint* wpt) /* common route and track pre-work */ -static void -prework_hdr_cb(const route_head* /*unused*/) +void +GarminTxtFormat::prework_hdr_cb(const route_head* /*unused*/) { cur_info = &route_info[route_idx]; } -static void -prework_tlr_cb(const route_head* /*unused*/) +void +GarminTxtFormat::prework_tlr_cb(const route_head* /*unused*/) { cur_info->last_wpt = cur_info->prev_wpt; route_idx++; } -static void -prework_wpt_cb(const Waypoint* wpt) +void +GarminTxtFormat::prework_wpt_cb(const Waypoint* wpt) { const Waypoint* prev = cur_info->prev_wpt; @@ -277,8 +189,8 @@ prework_wpt_cb(const Waypoint* wpt) /* output helpers */ -static void -print_position(const Waypoint* wpt) +void +GarminTxtFormat::print_position(const Waypoint* wpt) { int valid = 1; double lat, lon, north, east; @@ -363,8 +275,8 @@ print_position(const Waypoint* wpt) } } -static void -print_date_and_time(const time_t time, const bool time_only) +void +GarminTxtFormat::print_date_and_time(const time_t time, const bool time_only) { std::tm tm{}; char tbuf[32]; @@ -390,8 +302,8 @@ print_date_and_time(const time_t time, const bool time_only) *fout << "\t"; } -static void -print_categories(uint16_t categories) +void +GarminTxtFormat::print_categories(uint16_t categories) { const QStringList categoryList = garmin_fs_t::print_categories(categories); if (!categoryList.isEmpty()) { @@ -399,8 +311,8 @@ print_categories(uint16_t categories) } } -static void -print_course(const Waypoint* A, const Waypoint* B) /* seems to be okay */ +void +GarminTxtFormat::print_course(const Waypoint* A, const Waypoint* B) /* seems to be okay */ { if ((A != nullptr) && (B != nullptr) && (A != B)) { int course = qRound(waypt_course(A, B)); @@ -408,8 +320,8 @@ print_course(const Waypoint* A, const Waypoint* B) /* seems to be okay */ } } -static void -print_distance(const double distance, const bool no_scale, const bool with_tab, const int decis) +void +GarminTxtFormat::print_distance(const double distance, const bool no_scale, const bool with_tab, const int decis) { double dist = distance; @@ -443,8 +355,8 @@ print_distance(const double distance, const bool no_scale, const bool with_tab, } } -static void -print_speed(const double distance, const time_t time) +void +GarminTxtFormat::print_speed(const double distance, const time_t time) { double dist = distance; const char* unit; @@ -474,8 +386,8 @@ print_speed(const double distance, const time_t time) *fout << "\t"; } -static void -print_temperature(const float temperature) +void +GarminTxtFormat::print_temperature(const float temperature) { if (gtxt_flags.celsius) { *fout << QString::asprintf("%.f C", temperature); @@ -484,8 +396,8 @@ print_temperature(const float temperature) } } -static void -print_string(const char* fmt, const QString& string) +void +GarminTxtFormat::print_string(const char* fmt, const QString& string) { /* remove unwanted characters from source string */ QString cleanstring; @@ -502,8 +414,8 @@ print_string(const char* fmt, const QString& string) /* main cb's */ -static void -write_waypt(const Waypoint* wpt) +void +GarminTxtFormat::write_waypt(const Waypoint* wpt) { const char* wpt_type; @@ -588,8 +500,8 @@ write_waypt(const Waypoint* wpt) *fout << "\r\n"; } -static void -route_disp_hdr_cb(const route_head* rte) +void +GarminTxtFormat::route_disp_hdr_cb(const route_head* rte) { cur_info = &route_info[route_idx]; cur_info->prev_wpt = nullptr; @@ -614,14 +526,14 @@ route_disp_hdr_cb(const route_head* rte) *fout << QStringLiteral("\r\nHeader\t%1\r\n\r\n").arg(headers[rtept_header]); } -static void -route_disp_tlr_cb(const route_head* /*unused*/) +void +GarminTxtFormat::route_disp_tlr_cb(const route_head* /*unused*/) { route_idx++; } -static void -route_disp_wpt_cb(const Waypoint* wpt) +void +GarminTxtFormat::route_disp_wpt_cb(const Waypoint* wpt) { const Waypoint* prev = cur_info->prev_wpt; @@ -642,8 +554,8 @@ route_disp_wpt_cb(const Waypoint* wpt) cur_info->prev_wpt = wpt; } -static void -track_disp_hdr_cb(const route_head* track) +void +GarminTxtFormat::track_disp_hdr_cb(const route_head* track) { cur_info = &route_info[route_idx]; cur_info->prev_wpt = nullptr; @@ -669,14 +581,14 @@ track_disp_hdr_cb(const route_head* track) *fout << QStringLiteral("\r\n\r\nHeader\t%1\r\n\r\n").arg(headers[trkpt_header]); } -static void -track_disp_tlr_cb(const route_head* /*unused*/) +void +GarminTxtFormat::track_disp_tlr_cb(const route_head* /*unused*/) { route_idx++; } -static void -track_disp_wpt_cb(const Waypoint* wpt) +void +GarminTxtFormat::track_disp_wpt_cb(const Waypoint* wpt) { const Waypoint* prev = cur_info->prev_wpt; time_t delta; @@ -719,8 +631,8 @@ track_disp_wpt_cb(const Waypoint* wpt) * %%% global callbacks called by gpsbabel main process %%% * *******************************************************************************/ -static void -garmin_txt_utc_option() +void +GarminTxtFormat::garmin_txt_utc_option() { if (opt_utc != nullptr) { if (case_ignore_strcmp(opt_utc, "utc") == 0) { @@ -733,16 +645,16 @@ garmin_txt_utc_option() } } -static void -garmin_txt_adjust_time(QDateTime& dt) +void +GarminTxtFormat::garmin_txt_adjust_time(QDateTime& dt) const { if (gtxt_flags.utc) { dt = dt.toUTC().addSecs(dt.offsetFromUtc() - utc_offs); } } -static void -garmin_txt_wr_init(const QString& fname) +void +GarminTxtFormat::wr_init(const QString& fname) { gtxt_flags = {}; @@ -790,8 +702,8 @@ garmin_txt_wr_init(const QString& fname) garmin_txt_utc_option(); } -static void -garmin_txt_wr_deinit() +void +GarminTxtFormat::wr_deinit() { fout->close(); delete fout; @@ -800,9 +712,40 @@ garmin_txt_wr_deinit() date_time_format.squeeze(); } -static void -garmin_txt_write() +void +GarminTxtFormat::write() { + auto enum_waypt_cb_lambda = [this](const Waypoint* waypointp)->void { + enum_waypt_cb(waypointp); + }; + auto prework_hdr_cb_lambda = [this](const route_head* rte)->void { + prework_hdr_cb(rte); + }; + auto prework_tlr_cb_lambda = [this](const route_head* rte)->void { + prework_tlr_cb(rte); + }; + auto prework_wpt_cb_lambda = [this](const Waypoint* waypointp)->void { + prework_wpt_cb(waypointp); + }; + auto route_disp_hdr_cb_lambda = [this](const route_head* rte)->void { + route_disp_hdr_cb(rte); + }; + auto route_disp_tlr_cb_lambda = [this](const route_head* rte)->void { + route_disp_tlr_cb(rte); + }; + auto route_disp_wpt_cb_lambda = [this](const Waypoint* waypointp)->void { + route_disp_wpt_cb(waypointp); + }; + auto track_disp_hdr_cb_lambda = [this](const route_head* rte)->void { + track_disp_hdr_cb(rte); + }; + auto track_disp_tlr_cb_lambda = [this](const route_head* rte)->void { + track_disp_tlr_cb(rte); + }; + auto track_disp_wpt_cb_lambda = [this](const Waypoint* waypointp)->void { + track_disp_wpt_cb(waypointp); + }; + QString grid_str = gt_get_mps_grid_longname(grid_index, MYNAME); grid_str = grid_str.replace('*', "°"); *fout << "Grid\t" << grid_str << "\r\n"; @@ -812,15 +755,15 @@ garmin_txt_write() waypoints = 0; gtxt_flags.enum_waypoints = 1; /* enum all waypoints */ - waypt_disp_all(enum_waypt_cb); - route_disp_all(nullptr, nullptr, enum_waypt_cb); + waypt_disp_all(enum_waypt_cb_lambda); + route_disp_all(nullptr, nullptr, enum_waypt_cb_lambda); gtxt_flags.enum_waypoints = 0; if (waypoints > 0) { wpt_a_ct = 0; wpt_a = new const Waypoint*[waypoints] {}; - waypt_disp_all(enum_waypt_cb); - route_disp_all(nullptr, nullptr, enum_waypt_cb); + waypt_disp_all(enum_waypt_cb_lambda); + route_disp_all(nullptr, nullptr, enum_waypt_cb_lambda); auto sort_waypt_lambda = [](const Waypoint* wa, const Waypoint* wb)->bool { return wa->shortname.compare(wb->shortname, Qt::CaseInsensitive) < 0; }; @@ -835,10 +778,11 @@ garmin_txt_write() route_idx = 0; route_info = new PathInfo[route_count()]; routepoints = 0; - route_disp_all(prework_hdr_cb, prework_tlr_cb, prework_wpt_cb); + route_disp_all(prework_hdr_cb_lambda, prework_tlr_cb_lambda, prework_wpt_cb_lambda); + if (routepoints > 0) { route_idx = 0; - route_disp_all(route_disp_hdr_cb, route_disp_tlr_cb, route_disp_wpt_cb); + route_disp_all(route_disp_hdr_cb_lambda, route_disp_tlr_cb_lambda, route_disp_wpt_cb_lambda); } delete[] route_info; route_info = nullptr; @@ -847,11 +791,11 @@ garmin_txt_write() route_idx = 0; route_info = new PathInfo[track_count()]; routepoints = 0; - track_disp_all(prework_hdr_cb, prework_tlr_cb, prework_wpt_cb); + track_disp_all(prework_hdr_cb_lambda, prework_tlr_cb_lambda, prework_wpt_cb_lambda); if (routepoints > 0) { route_idx = 0; - track_disp_all(track_disp_hdr_cb, track_disp_tlr_cb, track_disp_wpt_cb); + track_disp_all(track_disp_hdr_cb_lambda, track_disp_tlr_cb_lambda, track_disp_wpt_cb_lambda); } delete[] route_info; } @@ -860,8 +804,8 @@ garmin_txt_write() /* helpers */ -static void -free_headers() +void +GarminTxtFormat::free_headers() { std::for_each(header_mapping_info.begin(), header_mapping_info.end(), [](auto& list)->void { list.clear(); }); @@ -870,8 +814,8 @@ free_headers() // Super simple attempt to convert strftime/strptime spec to Qt spec. // This misses a LOT of cases and vagaries, but the reality is that we // see very few date formats here. -static QString -strftime_to_timespec(const char* s) +QString +GarminTxtFormat::strftime_to_timespec(const char* s) { QString q; int l = strlen(s); @@ -944,15 +888,15 @@ strftime_to_timespec(const char* s) /* data parsers */ -static QDateTime -parse_date_and_time(const QString& str) +QDateTime +GarminTxtFormat::parse_date_and_time(const QString& str) { QString timespec = strftime_to_timespec(CSTR(date_time_format)); return QDateTime::fromString(QString(str).trimmed(), timespec); } -static uint16_t -parse_categories(const QString& str) +uint16_t +GarminTxtFormat::parse_categories(const QString& str) const { uint16_t res = 0; @@ -970,8 +914,8 @@ parse_categories(const QString& str) return res; } -static bool -parse_temperature(const QString& str, double* temperature) +bool +GarminTxtFormat::parse_temperature(const QString& str, double* temperature) const { double value; unsigned char unit; @@ -999,8 +943,8 @@ parse_temperature(const QString& str, double* temperature) return false; } -static void -parse_header(const QStringList& lineparts) +void +GarminTxtFormat::parse_header(const QStringList& lineparts) { header_column_names.clear(); for (const auto& name : lineparts) { @@ -1008,8 +952,8 @@ parse_header(const QStringList& lineparts) } } -static bool -parse_display(const QString& str, int* val) +bool +GarminTxtFormat::parse_display(const QString& str, int* val) const { if (str.isEmpty()) { return false; @@ -1025,8 +969,8 @@ parse_display(const QString& str, int* val) return false; } -static void -bind_fields(const header_type ht) +void +GarminTxtFormat::bind_fields(const header_type ht) { if ((grid_index < 0) || (datum_index < 0)) { fatal(MYNAME ": Incomplete or invalid file header!"); @@ -1058,8 +1002,8 @@ bind_fields(const header_type ht) header_column_names.clear(); } -static void -parse_grid(const QStringList& lineparts) +void +GarminTxtFormat::parse_grid(const QStringList& lineparts) { if (lineparts.empty()) { fatal(MYNAME ": Missing grid headline!\n"); @@ -1078,8 +1022,8 @@ parse_grid(const QStringList& lineparts) } } -static void -parse_datum(const QStringList& lineparts) +void +GarminTxtFormat::parse_datum(const QStringList& lineparts) { if (lineparts.empty()) { fatal(MYNAME ": Missing GPS datum headline!\n"); @@ -1089,8 +1033,8 @@ parse_datum(const QStringList& lineparts) datum_index = gt_lookup_datum_index(CSTR(str), MYNAME); } -static void -parse_waypoint(const QStringList& lineparts) +void +GarminTxtFormat::parse_waypoint(const QStringList& lineparts) { int column = -1; @@ -1197,8 +1141,8 @@ parse_waypoint(const QStringList& lineparts) waypt_add(wpt); } -static void -parse_route_header(const QStringList& lineparts) +void +GarminTxtFormat::parse_route_header(const QStringList& lineparts) { int column = -1; @@ -1226,8 +1170,8 @@ parse_route_header(const QStringList& lineparts) current_rte = rte; } -static void -parse_track_header(const QStringList& lineparts) +void +GarminTxtFormat::parse_track_header(const QStringList& lineparts) { int column = -1; @@ -1255,8 +1199,8 @@ parse_track_header(const QStringList& lineparts) } -static void -parse_route_waypoint(const QStringList& lineparts) +void +GarminTxtFormat::parse_route_waypoint(const QStringList& lineparts) { int column = -1; Waypoint* wpt = nullptr; @@ -1287,8 +1231,8 @@ parse_route_waypoint(const QStringList& lineparts) } } -static void -parse_track_waypoint(const QStringList& lineparts) +void +GarminTxtFormat::parse_track_waypoint(const QStringList& lineparts) { int column = -1; @@ -1349,8 +1293,8 @@ parse_track_waypoint(const QStringList& lineparts) /***************************************************************/ -static void -garmin_txt_rd_init(const QString& fname) +void +GarminTxtFormat::rd_init(const QString& fname) { gtxt_flags = {}; @@ -1367,8 +1311,8 @@ garmin_txt_rd_init(const QString& fname) garmin_txt_utc_option(); } -static void -garmin_txt_rd_deinit() +void +GarminTxtFormat::rd_deinit() { free_headers(); header_column_names.clear(); @@ -1379,8 +1323,8 @@ garmin_txt_rd_deinit() date_time_format.squeeze(); } -static void -garmin_txt_read() +void +GarminTxtFormat::read() { QString buff; @@ -1424,27 +1368,4 @@ garmin_txt_read() } } - -/* - * The file encoding is windows-1252. - * Conversion between windows-1252 and utf-16 is handled by the stream. - * Conversion between utf-16 and utf-8 is handled by this format. - * Let main know char strings have already been converted to utf-8 - * so it doesn't attempt to re-convert any char strings including gmsd data. - */ - -ff_vecs_t garmin_txt_vecs = { - ff_type_file, - FF_CAP_RW_ALL, - garmin_txt_rd_init, - garmin_txt_wr_init, - garmin_txt_rd_deinit, - garmin_txt_wr_deinit, - garmin_txt_read, - garmin_txt_write, - nullptr, - &garmin_txt_args, - NULL_POS_OPS -}; - #endif // CSVFMTS_ENABLED diff --git a/garmin_txt.h b/garmin_txt.h new file mode 100644 index 000000000..82346a387 --- /dev/null +++ b/garmin_txt.h @@ -0,0 +1,203 @@ +/* + + Support for MapSource Text Export (Tab delimited) files. + + Copyright (C) 2006 Olaf Klein, o.b.klein@gpsbabel.org + Copyright (C) 2004-2022 Robert Lipe, robertlipe+source@gpsbabel.org + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + */ +#ifndef GARMIN_TXT_H_INCLUDED_ +#define GARMIN_TXT_H_INCLUDED_ + +#if CSVFMTS_ENABLED + +#include // for array +#include // for uint16_t +#include // for time_t +#include // for pair + +#include // for QDateTime +#include // for QList +#include // for QString +#include // for QStringList +#include // for QVector + +#include "defs.h" +#include "format.h" // for Format +#include "src/core/textstream.h" // for TextStream + + +class GarminTxtFormat : public Format +{ +public: + QVector* get_args() override + { + return &garmin_txt_args; + } + + ff_type get_type() const override + { + return ff_type_file; + } + + QVector get_cap() const override + { + return FF_CAP_RW_ALL; + } + + void rd_init(const QString& fname) override; + void read() override; + void rd_deinit() override; + void wr_init(const QString& fname) override; + void write() override; + void wr_deinit() override; + +private: + /* Constants */ + + static constexpr double kGarminUnknownAlt = 1.0e25; + static constexpr char kDefaultDateFormat[] = "dd/mm/yyyy"; + static constexpr char kDefaultTimeFormat[] = "HH:mm:ss"; + + static const QVector headers; + + /* Types */ + + struct gtxt_flags_t { + unsigned int metric:1; + unsigned int celsius:1; + unsigned int utc:1; + unsigned int enum_waypoints:1; + unsigned int route_header_written:1; + unsigned int track_header_written:1; + }; + + enum header_type { + waypt_header = 0, + rtept_header, + trkpt_header, + route_header, + track_header, + unknown_header + }; + + class PathInfo + { + public: + double length {0}; + time_t start {0}; + time_t time {0}; + double speed {0}; + double total {0}; + int count {0}; + const Waypoint* prev_wpt {nullptr}; + const Waypoint* first_wpt {nullptr}; + const Waypoint* last_wpt {nullptr}; + }; + + /* Member Functions */ + + static bool is_valid_alt(double alt); + static const char* get_option_val(const char* option, const char* def); + void init_date_and_time_format(); + void convert_datum(const Waypoint* wpt, double* dest_lat, double* dest_lon) const; + void enum_waypt_cb(const Waypoint* wpt); + void prework_hdr_cb(const route_head* unused); + void prework_tlr_cb(const route_head* unused); + void prework_wpt_cb(const Waypoint* wpt); + void print_position(const Waypoint* wpt); + void print_date_and_time(time_t time, bool time_only); + void print_categories(uint16_t categories); + void print_course(const Waypoint* A, const Waypoint* B); + void print_distance(double distance, bool no_scale, bool with_tab, int decis); + void print_speed(double distance, time_t time); + void print_temperature(float temperature); + void print_string(const char* fmt, const QString& string); + void write_waypt(const Waypoint* wpt); + void route_disp_hdr_cb(const route_head* rte); + void route_disp_tlr_cb(const route_head* unused); + void route_disp_wpt_cb(const Waypoint* wpt); + void track_disp_hdr_cb(const route_head* track); + void track_disp_tlr_cb(const route_head* unused); + void track_disp_wpt_cb(const Waypoint* wpt); + void garmin_txt_utc_option(); + void garmin_txt_adjust_time(QDateTime& dt) const; + void free_headers(); + static QString strftime_to_timespec(const char* s); + QDateTime parse_date_and_time(const QString& str); + uint16_t parse_categories(const QString& str) const; + bool parse_temperature(const QString& str, double* temperature) const; + void parse_header(const QStringList& lineparts); + bool parse_display(const QString& str, int* val) const; + void bind_fields(header_type ht); + void parse_grid(const QStringList& lineparts); + void parse_datum(const QStringList& lineparts); + void parse_waypoint(const QStringList& lineparts); + void parse_route_header(const QStringList& lineparts); + void parse_track_header(const QStringList& lineparts); + void parse_route_waypoint(const QStringList& lineparts); + void parse_track_waypoint(const QStringList& lineparts); + + /* Data Members */ + + gpsbabel::TextStream* fin = nullptr; + gpsbabel::TextStream* fout = nullptr; + route_head* current_trk{}; + route_head* current_rte{}; + int waypoints{}; + int routepoints{}; + const Waypoint** wpt_a{}; + int wpt_a_ct{}; + grid_type grid_index{}; + int datum_index{}; + const char* datum_str{}; + int current_line{}; + QString date_time_format; + int precision = 3; + time_t utc_offs = 0; + gtxt_flags_t gtxt_flags{}; + + std::array>, unknown_header> header_mapping_info; + QStringList header_column_names; + + char* opt_datum = nullptr; + char* opt_dist = nullptr; + char* opt_temp = nullptr; + char* opt_date_format = nullptr; + char* opt_time_format = nullptr; + char* opt_precision = nullptr; + char* opt_utc = nullptr; + char* opt_grid = nullptr; + + QVector garmin_txt_args = { + {"date", &opt_date_format, "Read/Write date format (i.e. yyyy/mm/dd)", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, + {"datum", &opt_datum, "GPS datum (def. WGS 84)", "WGS 84", ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, + {"dist", &opt_dist, "Distance unit [m=metric, s=statute]", "m", ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, + {"grid", &opt_grid, "Write position using this grid.", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, + {"prec", &opt_precision, "Precision of coordinates", "3", ARGTYPE_INT, ARG_NOMINMAX, nullptr}, + {"temp", &opt_temp, "Temperature unit [c=Celsius, f=Fahrenheit]", "c", ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, + {"time", &opt_time_format, "Read/Write time format (i.e. HH:mm:ss xx)", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, + {"utc", &opt_utc, "Write timestamps with offset x to UTC time", nullptr, ARGTYPE_INT, "-23", "+23", nullptr}, + }; + + PathInfo* route_info{}; + int route_idx{}; + PathInfo* cur_info{}; +}; + +#endif // CSVFMTS_ENABLED +#endif // GARMIN_TXT_H_INCLUDED_ diff --git a/vecs.cc b/vecs.cc index a963531f8..0db4b44ad 100644 --- a/vecs.cc +++ b/vecs.cc @@ -46,6 +46,7 @@ #include "garmin.h" // for GarminFormat #include "garmin_fit.h" // for GarminFitFormat #include "garmin_gpi.h" // for GarminGPIFormat +#include "garmin_txt.h" // for GarminTxtFormat #include "garmin_xt.h" // for GarminXTFormat #include "gbversion.h" // for WEB_DOC_DIR #include "gdb.h" // for GdbFormat @@ -83,18 +84,11 @@ extern ff_vecs_t geo_vecs; extern ff_vecs_t ozi_vecs; #if MAXIMAL_ENABLED -extern ff_vecs_t gpl_vecs; extern ff_vecs_t mtk_vecs; extern ff_vecs_t mtk_fvecs; extern ff_vecs_t mtk_m241_vecs; extern ff_vecs_t mtk_m241_fvecs; #endif // MAXIMAL_ENABLED -#if MAXIMAL_ENABLED -#if CSVFMTS_ENABLED -extern ff_vecs_t garmin_txt_vecs; -#endif // CSVFMTS_ENABLED -extern ff_vecs_t ggv_log_vecs; -#endif // MAXIMAL_ENABLED #define MYNAME "vecs" @@ -138,7 +132,7 @@ struct Vecs::Impl { UnicsvFormat unicsv_fmt; GtmFormat gtm_fmt; #if CSVFMTS_ENABLED - LegacyFormat garmin_txt_fmt {garmin_txt_vecs}; + GarminTxtFormat garmin_txt_fmt; #endif // CSVFMTS_ENABLED GtrnctrFormat gtc_fmt; GarminGPIFormat garmin_gpi_fmt;