You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2112 lines
53KB

  1. /*
  2. Universal CSV - support for csv files, divining field order from the header.
  3. Copyright (C) 2006-2013 Robert Lipe, robertlipe+source@gpsbabel.org
  4. copyright (C) 2007,2008 Olaf Klein, o.b.klein@gpsbabel.org
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
  16. */
  17. #include "defs.h"
  18. #include "cet.h"
  19. #include "cet_util.h"
  20. #include "csv_util.h"
  21. #include "garmin_fs.h"
  22. #include "garmin_tables.h"
  23. #include "jeeps/gpsmath.h"
  24. #include "src/core/logging.h"
  25. #include <QtCore/QVector>
  26. #include <cmath>
  27. #define MYNAME "unicsv"
  28. /* "UNICSV_FIELD_SEP" and "UNICSV_LINE_SEP" are only used by the writer */
  29. #define UNICSV_FIELD_SEP ","
  30. #define UNICSV_LINE_SEP "\r\n"
  31. #define UNICSV_QUOT_CHAR '"'
  32. /* GPSBabel internal and calculated fields */
  33. typedef enum {
  34. fld_shortname = 0,
  35. fld_latitude,
  36. fld_longitude,
  37. fld_description,
  38. fld_notes,
  39. fld_url,
  40. fld_altitude,
  41. fld_utm_zone,
  42. fld_utm_zone_char,
  43. fld_utm_northing,
  44. fld_utm_easting,
  45. fld_utm,
  46. fld_bng,
  47. fld_bng_zone,
  48. fld_bng_northing,
  49. fld_bng_easting,
  50. fld_swiss,
  51. fld_swiss_northing,
  52. fld_swiss_easting,
  53. fld_hdop,
  54. fld_pdop,
  55. fld_vdop,
  56. fld_sat,
  57. fld_fix,
  58. fld_utc_date,
  59. fld_utc_time,
  60. fld_course,
  61. fld_speed,
  62. fld_temperature,
  63. fld_temperature_f,
  64. fld_heartrate,
  65. fld_cadence,
  66. fld_power,
  67. fld_proximity,
  68. fld_depth,
  69. fld_symbol,
  70. fld_date,
  71. fld_time,
  72. fld_datetime,
  73. fld_iso_time,
  74. fld_year,
  75. fld_month,
  76. fld_day,
  77. fld_hour,
  78. fld_min,
  79. fld_sec,
  80. fld_ns,
  81. fld_ew,
  82. fld_garmin_city,
  83. fld_garmin_postal_code,
  84. fld_garmin_state,
  85. fld_garmin_country,
  86. fld_garmin_addr,
  87. fld_garmin_phone_nr,
  88. fld_garmin_phone_nr2,
  89. fld_garmin_fax_nr,
  90. fld_garmin_email,
  91. fld_garmin_facility,
  92. fld_gc_id,
  93. fld_gc_type,
  94. fld_gc_container,
  95. fld_gc_terr,
  96. fld_gc_diff,
  97. fld_gc_is_archived,
  98. fld_gc_is_available,
  99. fld_gc_exported,
  100. fld_gc_last_found,
  101. fld_gc_placer,
  102. fld_gc_placer_id,
  103. fld_gc_hint,
  104. fld_terminator
  105. } field_e;
  106. #define STR_LEFT 1
  107. #define STR_RIGHT 2
  108. #define STR_ANY 4
  109. #define STR_EQUAL 8
  110. #define STR_CASE 16
  111. #define unicsv_unknown 1e25
  112. typedef struct {
  113. const char* name;
  114. field_e type;
  115. uint32_t options;
  116. } field_t;
  117. /*
  118. * ! Please use always underscores in field names !
  119. * we check a second time after replacing underscores with spaces
  120. */
  121. static field_t fields_def[] = {
  122. /* unhandled columns */
  123. { "index", fld_terminator, STR_ANY },
  124. { "no", fld_terminator, STR_EQUAL },
  125. { "mini", fld_terminator, STR_ANY }, /* maybe minimum anything, so
  126. avoid detection as 'min' for minute */
  127. /* handled columns */
  128. { "name", fld_shortname, STR_ANY },
  129. { "title", fld_shortname, STR_ANY },
  130. { "desc", fld_description, STR_ANY },
  131. { "notes", fld_notes, STR_ANY },
  132. { "omment", fld_notes, STR_ANY }, /* works also for German "Kommentar" */
  133. { "text", fld_notes, STR_ANY },
  134. { "url", fld_url, STR_ANY },
  135. { "icon", fld_symbol, STR_ANY },
  136. { "symb", fld_symbol, STR_ANY },
  137. { "lat", fld_latitude, STR_ANY },
  138. { "lon", fld_longitude, STR_ANY },
  139. { "lng", fld_longitude, STR_ANY },
  140. { "x", fld_longitude, STR_EQUAL },
  141. { "y", fld_latitude, STR_EQUAL },
  142. { "z", fld_altitude, STR_EQUAL },
  143. { "x_pos", fld_longitude, STR_ANY },
  144. { "y_pos", fld_latitude, STR_ANY },
  145. { "alt", fld_altitude, STR_ANY },
  146. { "ele", fld_altitude, STR_ANY },
  147. { "height", fld_altitude, STR_ANY },
  148. { "utm_z", fld_utm_zone, STR_ANY },
  149. { "utm_c", fld_utm_zone_char, STR_ANY },
  150. { "utm_zc", fld_utm_zone_char, STR_ANY },
  151. { "utm_n", fld_utm_northing, STR_ANY },
  152. { "utm_e", fld_utm_easting, STR_ANY },
  153. { "utm", fld_utm, STR_EQUAL },
  154. { "utm_coo", fld_utm, STR_ANY },
  155. { "utm_pos", fld_utm, STR_ANY },
  156. { "bng_z", fld_bng_zone, STR_ANY },
  157. { "bng_n", fld_bng_northing, STR_ANY },
  158. { "bng_e", fld_bng_easting, STR_ANY },
  159. { "bng", fld_bng, STR_EQUAL },
  160. { "bng_coo", fld_bng, STR_ANY },
  161. { "bng_pos", fld_bng, STR_ANY },
  162. { "swiss_e", fld_swiss_easting, STR_ANY },
  163. { "swiss_n", fld_swiss_northing, STR_ANY },
  164. { "swiss", fld_swiss, STR_EQUAL },
  165. { "swiss_coo", fld_swiss, STR_ANY },
  166. { "swiss_pos", fld_swiss, STR_ANY },
  167. { "hdop", fld_hdop, STR_ANY },
  168. { "pdop", fld_pdop, STR_ANY },
  169. { "vdop", fld_vdop, STR_ANY },
  170. { "sat", fld_sat, STR_ANY },
  171. { "fix", fld_fix, STR_ANY },
  172. { "utc_d", fld_utc_date, STR_ANY },
  173. { "utc_t", fld_utc_time, STR_ANY },
  174. { "head", fld_course, STR_ANY },
  175. { "cour", fld_course, STR_ANY },
  176. { "speed", fld_speed, STR_ANY },
  177. { "velo", fld_speed, STR_ANY },
  178. { "geschw", fld_speed, STR_ANY }, /* speed in german */
  179. { "tempf", fld_temperature_f, STR_EQUAL }, /* degrees fahrenheit */
  180. { "temp", fld_temperature, STR_ANY }, /* degrees celsius by default */
  181. { "heart", fld_heartrate, STR_ANY },
  182. { "caden", fld_cadence, STR_ANY },
  183. { "power", fld_power, STR_ANY },
  184. { "prox", fld_proximity, STR_ANY },
  185. { "depth", fld_depth, STR_ANY },
  186. { "date", fld_date, STR_ANY },
  187. { "datum", fld_date, STR_ANY },
  188. { "time", fld_time, STR_ANY },
  189. { "zeit", fld_time, STR_ANY },
  190. { "hour", fld_hour, STR_LEFT },
  191. { "min", fld_min, STR_LEFT },
  192. { "sec", fld_sec, STR_LEFT },
  193. { "year", fld_year, STR_LEFT },
  194. { "month", fld_month, STR_LEFT },
  195. { "day", fld_day, STR_LEFT },
  196. { "n/s", fld_ns, STR_ANY },
  197. { "e/w", fld_ew, STR_ANY },
  198. /* garmin specials */
  199. { "addr", fld_garmin_addr, STR_ANY },
  200. { "street", fld_garmin_addr, STR_ANY },
  201. { "city", fld_garmin_city, STR_ANY },
  202. { "country", fld_garmin_country, STR_ANY },
  203. { "post", fld_garmin_postal_code, STR_ANY },
  204. { "zip", fld_garmin_postal_code, STR_ANY },
  205. { "phone", fld_garmin_phone_nr, STR_ANY },
  206. { "phone2", fld_garmin_phone_nr2, STR_ANY },
  207. { "fax", fld_garmin_fax_nr, STR_ANY },
  208. { "email", fld_garmin_email, STR_ANY },
  209. { "state", fld_garmin_state, STR_ANY },
  210. { "faci", fld_garmin_facility, STR_ANY },
  211. /* geocache details */
  212. { "gcid", fld_gc_id, STR_ANY },
  213. { "type", fld_gc_type, STR_ANY },
  214. { "cont", fld_gc_container, STR_ANY },
  215. { "terr", fld_gc_terr, STR_ANY },
  216. { "diff", fld_gc_diff, STR_ANY },
  217. { "arch", fld_gc_is_archived, STR_ANY },
  218. { "avail", fld_gc_is_available, STR_ANY },
  219. { "exported", fld_gc_exported, STR_ANY },
  220. { "found", fld_gc_last_found, STR_ANY },
  221. { "placer_id", fld_gc_placer_id, STR_ANY },
  222. { "placer", fld_gc_placer, STR_ANY },
  223. { "hint", fld_gc_hint, STR_ANY },
  224. { NULL, fld_terminator, 0 }
  225. };
  226. static QVector<field_e> unicsv_fields_tab;
  227. static double unicsv_altscale, unicsv_depthscale, unicsv_proximityscale
  228. ;
  229. static const char* unicsv_fieldsep;
  230. static gbfile* fin, *fout;
  231. static gpsdata_type unicsv_data_type;
  232. static route_head* unicsv_track, *unicsv_route;
  233. static char unicsv_outp_flags[(fld_terminator + 8) / 8];
  234. static grid_type unicsv_grid_idx;
  235. static int unicsv_datum_idx;
  236. static char* opt_datum;
  237. static char* opt_grid;
  238. static char* opt_utc;
  239. static char* opt_filename;
  240. static char* opt_format;
  241. static char* opt_prec;
  242. static char* opt_fields;
  243. static int unicsv_waypt_ct;
  244. static char unicsv_detect;
  245. static int llprec;
  246. static arglist_t unicsv_args[] = {
  247. {
  248. "datum", &opt_datum, "GPS datum (def. WGS 84)",
  249. "WGS 84", ARGTYPE_STRING, ARG_NOMINMAX
  250. },
  251. {
  252. "grid", &opt_grid, "Write position using this grid.",
  253. NULL, ARGTYPE_STRING, ARG_NOMINMAX
  254. },
  255. {
  256. "utc", &opt_utc, "Write timestamps with offset x to UTC time",
  257. NULL, ARGTYPE_INT, "-23", "+23"
  258. },
  259. {
  260. "format", &opt_format, "Write name(s) of format(s) from input session(s)",
  261. NULL, ARGTYPE_BOOL, ARG_NOMINMAX
  262. },
  263. {
  264. "filename", &opt_filename, "Write filename(s) from input session(s)",
  265. NULL, ARGTYPE_BOOL, ARG_NOMINMAX
  266. },
  267. {
  268. "prec", &opt_prec, "Precision of numerical coordinates (no grid set)",
  269. "6", ARGTYPE_INT | ARGTYPE_HIDDEN, "0", "15"
  270. },
  271. {
  272. "fields", &opt_fields, "Name and order of input fields, separated by '+'",
  273. NULL, ARGTYPE_STRING, ARG_NOMINMAX
  274. },
  275. ARG_TERMINATOR
  276. };
  277. /* helpers */
  278. /* here we only need a simple yes(0) or no(1) */
  279. static int
  280. unicsv_strrcmp(const char* s1, const char* s2)
  281. {
  282. int l1, l2;
  283. l1 = strlen(s1);
  284. l2 = strlen(s2);
  285. if ((l1 - l2) >= 0) {
  286. return strcmp(s1 + (l1 - l2), s2);
  287. } else {
  288. return 1; /* false */
  289. }
  290. }
  291. // There is no test coverage of this and it's been wrong for years and
  292. // nobody has noticed...
  293. static int
  294. unicsv_parse_gc_id(const QString& str)
  295. {
  296. int res = 0;
  297. const QString kBase35 = "0123456789ABCDEFGHJKMNPQRTVWXYZ"; // ILOSU are omitted.
  298. if (str.startsWith("GC")) {
  299. int base35 = str.size() > 6; // above GCFFFF?
  300. QString s = str.mid(2);
  301. while (!s.isEmpty()) {
  302. res = res * 16 + kBase35.indexOf(s[0]);
  303. s = str.mid(1);
  304. }
  305. if (base35) {
  306. res -= 411120;
  307. }
  308. }
  309. return res;
  310. }
  311. static time_t
  312. unicsv_parse_date(const char* str, int* consumed)
  313. {
  314. int p1, p2, p3, ct;
  315. char sep[2];
  316. struct tm tm;
  317. int lconsumed = 0;
  318. memset(&tm, 0, sizeof(tm));
  319. ct = sscanf(str, "%d%1[-.//]%d%1[-.//]%d%n", &p1, sep, &p2, sep, &p3, &lconsumed);
  320. if (consumed && lconsumed) {
  321. *consumed = lconsumed;
  322. }
  323. if (ct != 5) {
  324. if (consumed) { /* don't stop here; it's only sniffing */
  325. *consumed = 0; /* for a possible date */
  326. return 0;
  327. }
  328. Fatal() << MYNAME << ": Could not parse date string (" << str << ").\n";
  329. }
  330. if ((p1 > 99) || (sep[0] == '-')) { /* Y-M-D (iso like) */
  331. tm.tm_year = p1;
  332. tm.tm_mon = p2;
  333. tm.tm_mday = p3;
  334. } else if (sep[0] == '.') { /* Germany and any other countries */
  335. tm.tm_mday = p1; /* have a fixed D.M.Y format */
  336. tm.tm_mon = p2;
  337. tm.tm_year = p3;
  338. } else {
  339. tm.tm_mday = p2;
  340. tm.tm_mon = p1;
  341. tm.tm_year = p3;
  342. }
  343. if ((p1 < 100) && (p2 < 100) && (p3 < 100)) {
  344. if (tm.tm_year < 70) {
  345. tm.tm_year += 2000;
  346. } else {
  347. tm.tm_year += 1900;
  348. }
  349. }
  350. /* some low-level checks */
  351. if ((tm.tm_mon > 12) || (tm.tm_mon < 1) || (tm.tm_mday > 31) || (tm.tm_mday < 1)) {
  352. if (consumed) {
  353. *consumed = 0;
  354. return 0; /* don't stop here */
  355. }
  356. Fatal() << MYNAME << ": Could not parse date string (" << str << ").\n";
  357. }
  358. tm.tm_year -= 1900;
  359. tm.tm_mon -= 1;
  360. return mkgmtime(&tm);
  361. }
  362. static time_t
  363. unicsv_parse_time(const char* str, int* usec, time_t* date)
  364. {
  365. int hour, min, ct, sec;
  366. int consumed = 0;
  367. double us;
  368. char sep[2];
  369. time_t ldate;
  370. /* If we have somethine we're pretty sure is a date, parse that
  371. * first, skip over it, and pass that back to the caller)
  372. */
  373. ldate = unicsv_parse_date(str, &consumed);
  374. if (consumed && ldate) {
  375. str += consumed;
  376. if (date) {
  377. *date = ldate;
  378. }
  379. }
  380. ct = sscanf(str, "%d%1[.://]%d%1[.://]%d%lf", &hour, sep, &min, sep, &sec, &us);
  381. is_fatal(ct < 5, MYNAME ": Could not parse time string (%s).\n", str);
  382. if (ct == 6) {
  383. *usec = lround((us * 1000000));
  384. if (*usec > 999999) {
  385. *usec = 0;
  386. sec++;
  387. }
  388. } else {
  389. *usec = 0;
  390. }
  391. return ((hour * SECONDS_PER_HOUR) + (min * 60) + (int)sec);
  392. }
  393. static time_t
  394. unicsv_parse_time(const QString& str, int* msec, time_t* date)
  395. {
  396. return unicsv_parse_time(CSTR(str), msec, date);
  397. }
  398. static status_type
  399. unicsv_parse_status(const QString& str)
  400. {
  401. if (str.compare("true", Qt::CaseInsensitive) == 0 ||
  402. str.compare("yes", Qt::CaseInsensitive) == 0 ||
  403. str == "1") {
  404. return status_true;
  405. }
  406. if (str.compare("false", Qt::CaseInsensitive) == 0 ||
  407. str.compare("no", Qt::CaseInsensitive) == 0 ||
  408. str == "0") {
  409. return status_false;
  410. }
  411. return status_unknown;
  412. }
  413. static QDateTime
  414. unicsv_adjust_time(const time_t time, time_t* date)
  415. {
  416. time_t res = time;
  417. if (date) {
  418. res += *date;
  419. }
  420. if (opt_utc) {
  421. res += atoi(opt_utc) * SECONDS_PER_HOUR;
  422. } else {
  423. struct tm tm = *gmtime(&res);
  424. res = mklocaltime(&tm);
  425. }
  426. return QDateTime::fromTime_t(res);
  427. }
  428. static char
  429. unicsv_compare_fields(const char* s, const field_t* f)
  430. {
  431. char* name = (char*)f->name;
  432. const char* test = s;
  433. char result;
  434. if (!(f->options & STR_CASE)) {
  435. test = strupper(xstrdup(s));
  436. name = strupper(xstrdup(f->name));
  437. }
  438. if (f->options & STR_EQUAL) {
  439. result = (strcmp(test, name) == 0);
  440. } else if (f->options & STR_ANY) {
  441. result = (strstr(test, name) != NULL);
  442. } else {
  443. if (f->options & STR_LEFT) {
  444. result = (strncmp(test, name, strlen(name)) == 0);
  445. } else if (f->options & STR_RIGHT) {
  446. result = (unicsv_strrcmp(test, name) == 0);
  447. } else {
  448. result = 0; /* fallback to "FALSE" */
  449. }
  450. }
  451. if ((! result) && (strchr(test, ' ') != NULL)) {
  452. /* replace ' ' with '_' and try again */
  453. char* tmp = gstrsub(test, " ", "_");
  454. result = unicsv_compare_fields(tmp, f);
  455. xfree(tmp);
  456. }
  457. if ((! result) && (strchr(test, '-') != NULL)) {
  458. /* replace '-' with '_' and try again */
  459. char* tmp = gstrsub(test, "-", "_");
  460. result = unicsv_compare_fields(tmp, f);
  461. xfree(tmp);
  462. }
  463. if (name != f->name) {
  464. xfree(name);
  465. xfree(test);
  466. }
  467. return result;
  468. }
  469. static char
  470. unicsv_compare_fields(const QString& s, const field_t* f)
  471. {
  472. return unicsv_compare_fields(CSTR(s), f);
  473. }
  474. static void
  475. unicsv_fondle_header(QString s)
  476. {
  477. // TODO: clean up this back and forth between QString and char*.
  478. char* buf = NULL;
  479. char* cbuf_start = NULL;
  480. const cet_cs_vec_t* ascii = &cet_cs_vec_ansi_x3_4_1968; /* us-ascii */
  481. /* Convert the entire header to lower case for convenience.
  482. * If we see a tab in that header, we decree it to be tabsep.
  483. */
  484. unicsv_fieldsep = ",";
  485. if (s.contains('\t')) {
  486. unicsv_fieldsep = "\t";
  487. } else if (s.contains(';')) {
  488. unicsv_fieldsep = ";";
  489. } else if (s.contains('|')) {
  490. unicsv_fieldsep = "|";
  491. }
  492. cbuf_start = xstrdup(s.toLower());
  493. const char* cbuf = cbuf_start;
  494. /* convert the header line into native ascii */
  495. if (global_opts.charset != ascii) {
  496. buf = cet_str_any_to_any(cbuf, global_opts.charset, ascii);
  497. cbuf = buf;
  498. }
  499. while ((s = csv_lineparse(cbuf, unicsv_fieldsep, "\"", 0)) , !s.isEmpty()) {
  500. s = s.trimmed();
  501. field_t* f = &fields_def[0];
  502. cbuf = NULL;
  503. unicsv_fields_tab.append(fld_terminator);
  504. while (f->name) {
  505. if (unicsv_compare_fields(s, f)) {
  506. unicsv_fields_tab.last() = f->type;
  507. break;
  508. }
  509. f++;
  510. }
  511. if ((! f->name) && global_opts.debug_level) {
  512. warning(MYNAME ": Unhandled column \"%s\".\n", qPrintable(s));
  513. }
  514. /* handle some special items */
  515. if (f->type == fld_altitude) {
  516. if (s.contains("ft") || s.contains("feet")) {
  517. unicsv_altscale = FEET_TO_METERS(1);
  518. }
  519. }
  520. if (f->type == fld_depth) {
  521. if (s.contains("ft") || s.contains("feet")) {
  522. unicsv_depthscale = FEET_TO_METERS(1);
  523. }
  524. }
  525. if (f->type == fld_proximity) {
  526. if (s.contains("ft") || s.contains("feet")) {
  527. unicsv_proximityscale = FEET_TO_METERS(1);
  528. }
  529. }
  530. if ((f->type == fld_time) || (f->type == fld_date)) {
  531. if (s.contains("iso")) {
  532. f->type = fld_iso_time;
  533. }
  534. }
  535. }
  536. if (buf) {
  537. xfree(buf);
  538. }
  539. if (cbuf_start) {
  540. xfree(cbuf_start);
  541. }
  542. }
  543. static void
  544. unicsv_rd_init(const QString& fname)
  545. {
  546. char* c;
  547. unicsv_altscale = 1.0;
  548. unicsv_depthscale = 1.0;
  549. unicsv_proximityscale = 1.0;
  550. unicsv_fields_tab.clear();
  551. unicsv_data_type = global_opts.objective;
  552. unicsv_detect = (!(global_opts.masked_objective & (WPTDATAMASK | TRKDATAMASK | RTEDATAMASK | POSNDATAMASK)));
  553. unicsv_track = unicsv_route = NULL;
  554. unicsv_datum_idx = gt_lookup_datum_index(opt_datum, MYNAME);
  555. fin = gbfopen(fname, "rb", MYNAME);
  556. if (opt_fields) {
  557. QString fields = QString(opt_fields).replace("+", ",");
  558. unicsv_fondle_header(fields);
  559. } else if ((c = gbfgetstr(fin))) {
  560. unicsv_fondle_header(c);
  561. } else {
  562. unicsv_fieldsep = NULL;
  563. }
  564. if (fin->unicode) {
  565. cet_convert_init(CET_CHARSET_UTF8, 1);
  566. }
  567. }
  568. static void
  569. unicsv_rd_deinit(void)
  570. {
  571. gbfclose(fin);
  572. unicsv_fields_tab.clear();
  573. }
  574. static void
  575. unicsv_parse_one_line(char* ibuf)
  576. {
  577. Waypoint* wpt = NULL;
  578. int column;
  579. int utm_zone = -9999;
  580. double utm_easting = 0;
  581. double utm_northing = 0;
  582. char utm_zc = 'N';
  583. // Zones are always two bytes. Spare one for null termination..
  584. char bng_zone[3] = "";
  585. double bng_easting = 0;
  586. double bng_northing = 0;
  587. double swiss_easting = unicsv_unknown;
  588. double swiss_northing = unicsv_unknown;
  589. int checked = 0;
  590. time_t date = -1;
  591. time_t time = -1;
  592. int usec = -1;
  593. char is_localtime = 0;
  594. garmin_fs_t* gmsd;
  595. double d;
  596. struct tm ymd;
  597. int src_datum = unicsv_datum_idx;
  598. int ns = 1;
  599. int ew = 1;
  600. geocache_data* gc_data = NULL;
  601. wpt = new Waypoint;
  602. wpt->latitude = unicsv_unknown;
  603. wpt->longitude = unicsv_unknown;
  604. memset(&ymd, 0, sizeof(ymd));
  605. column = -1;
  606. QString s;
  607. while ((s = csv_lineparse(ibuf, unicsv_fieldsep, "\"", 0)), !s.isNull()) {
  608. if (++column >= unicsv_fields_tab.size()) {
  609. break; /* ignore extra fields on line */
  610. }
  611. ibuf = NULL;
  612. checked++;
  613. s = s.trimmed();
  614. if (s.isEmpty()) {
  615. continue; /* skip empty columns */
  616. }
  617. switch (unicsv_fields_tab[column]) {
  618. case fld_time:
  619. case fld_date:
  620. case fld_datetime:
  621. /* switch column type if it looks like an iso time string */
  622. if (s.contains('T')) {
  623. unicsv_fields_tab[column] = fld_iso_time;
  624. }
  625. break;
  626. default:
  627. ;
  628. }
  629. switch (unicsv_fields_tab[column]) {
  630. case fld_latitude:
  631. human_to_dec(CSTR(s), &wpt->latitude, NULL, 1);
  632. wpt->latitude = wpt->latitude * ns;
  633. break;
  634. case fld_longitude:
  635. human_to_dec(CSTR(s), NULL, &wpt->longitude, 2);
  636. wpt->longitude = wpt->longitude * ew;
  637. break;
  638. case fld_shortname:
  639. wpt->shortname = s;
  640. break;
  641. case fld_description:
  642. wpt->description = s;
  643. break;
  644. case fld_notes:
  645. wpt->notes = s;
  646. break;
  647. case fld_url: {
  648. wpt->AddUrlLink(s);
  649. }
  650. break;
  651. case fld_altitude:
  652. if (parse_distance(s, &d, unicsv_altscale, MYNAME)) {
  653. if (fabs(d) < fabs(unknown_alt)) {
  654. wpt->altitude = d;
  655. }
  656. }
  657. break;
  658. case fld_utm_zone:
  659. utm_zone = s.toInt();
  660. break;
  661. case fld_utm_easting:
  662. utm_easting = s.toDouble();
  663. break;
  664. case fld_utm_northing:
  665. utm_northing = s.toDouble();
  666. break;
  667. case fld_utm_zone_char:
  668. utm_zc = s[0].toUpper().toLatin1();
  669. break;
  670. case fld_utm:
  671. parse_coordinates(s, unicsv_datum_idx, grid_utm,
  672. &wpt->latitude, &wpt->longitude, MYNAME);
  673. /* coordinates from parse_coordinates are in WGS84
  674. don't convert a second time */
  675. src_datum = DATUM_WGS84;
  676. break;
  677. case fld_bng:
  678. parse_coordinates(s, DATUM_OSGB36, grid_bng,
  679. &wpt->latitude, &wpt->longitude, MYNAME);
  680. /* coordinates from parse_coordinates are in WGS84
  681. don't convert a second time */
  682. src_datum = DATUM_WGS84;
  683. break;
  684. case fld_bng_zone:
  685. strncpy(bng_zone, CSTR(s), sizeof(bng_zone) -1);
  686. strupper(bng_zone);
  687. break;
  688. case fld_bng_northing:
  689. bng_northing = s.toDouble();
  690. break;
  691. case fld_bng_easting:
  692. bng_easting = s.toDouble();
  693. break;
  694. case fld_swiss:
  695. parse_coordinates(s, DATUM_WGS84, grid_swiss,
  696. &wpt->latitude, &wpt->longitude, MYNAME);
  697. /* coordinates from parse_coordinates are in WGS84
  698. don't convert a second time */
  699. src_datum = DATUM_WGS84;
  700. break;
  701. case fld_swiss_easting:
  702. swiss_easting = s.toDouble();
  703. break;
  704. case fld_swiss_northing:
  705. swiss_northing = s.toDouble();
  706. break;
  707. case fld_hdop:
  708. wpt->hdop = s.toDouble();
  709. if (unicsv_detect) {
  710. unicsv_data_type = trkdata;
  711. }
  712. break;
  713. case fld_pdop:
  714. wpt->pdop = s.toDouble();
  715. if (unicsv_detect) {
  716. unicsv_data_type = trkdata;
  717. }
  718. break;
  719. case fld_vdop:
  720. wpt->vdop = s.toDouble();
  721. if (unicsv_detect) {
  722. unicsv_data_type = trkdata;
  723. }
  724. break;
  725. case fld_sat:
  726. wpt->sat = s.toInt();
  727. if (unicsv_detect) {
  728. unicsv_data_type = trkdata;
  729. }
  730. break;
  731. case fld_fix:
  732. if (unicsv_detect) {
  733. unicsv_data_type = trkdata;
  734. }
  735. if (case_ignore_strcmp(s, "none") == 0) {
  736. wpt->fix = fix_none;
  737. } else if (case_ignore_strcmp(s, "2d") == 0) {
  738. wpt->fix = fix_2d;
  739. } else if (case_ignore_strcmp(s, "3d") == 0) {
  740. wpt->fix = fix_3d;
  741. } else if (case_ignore_strcmp(s, "dgps") == 0) {
  742. wpt->fix = fix_dgps;
  743. } else if (case_ignore_strcmp(s, "pps") == 0) {
  744. wpt->fix = fix_pps;
  745. } else {
  746. wpt->fix = fix_unknown;
  747. }
  748. break;
  749. case fld_utc_date:
  750. if ((is_localtime < 2) && (date < 0)) {
  751. date = unicsv_parse_date(CSTR(s), NULL);
  752. is_localtime = 0;
  753. }
  754. break;
  755. case fld_utc_time:
  756. if ((is_localtime < 2) && (time < 0)) {
  757. time = unicsv_parse_time(s, &usec, &date);
  758. is_localtime = 0;
  759. }
  760. break;
  761. case fld_speed:
  762. if (parse_speed(s, &d, 1.0, MYNAME)) {
  763. WAYPT_SET(wpt, speed, d);
  764. if (unicsv_detect) {
  765. unicsv_data_type = trkdata;
  766. }
  767. }
  768. break;
  769. case fld_course:
  770. WAYPT_SET(wpt, course, s.toDouble());
  771. if (unicsv_detect) {
  772. unicsv_data_type = trkdata;
  773. }
  774. break;
  775. case fld_temperature:
  776. d = s.toDouble();
  777. if (fabs(d) < 999999) {
  778. WAYPT_SET(wpt, temperature, d);
  779. }
  780. break;
  781. case fld_temperature_f:
  782. d = s.toDouble();
  783. if (fabs(d) < 999999) {
  784. WAYPT_SET(wpt, temperature, FAHRENHEIT_TO_CELSIUS(d));
  785. }
  786. break;
  787. case fld_heartrate:
  788. wpt->heartrate = s.toInt();
  789. if (unicsv_detect) {
  790. unicsv_data_type = trkdata;
  791. }
  792. break;
  793. case fld_cadence:
  794. wpt->cadence = s.toInt();
  795. if (unicsv_detect) {
  796. unicsv_data_type = trkdata;
  797. }
  798. break;
  799. case fld_power:
  800. wpt->power = s.toDouble();
  801. if (unicsv_detect) {
  802. unicsv_data_type = trkdata;
  803. }
  804. break;
  805. case fld_proximity:
  806. if (parse_distance(s, &d, unicsv_proximityscale, MYNAME)) {
  807. WAYPT_SET(wpt, proximity, d);
  808. }
  809. break;
  810. case fld_depth:
  811. if (parse_distance(s, &d, unicsv_depthscale, MYNAME)) {
  812. WAYPT_SET(wpt, depth, d);
  813. }
  814. break;
  815. case fld_symbol:
  816. wpt->icon_descr = s;
  817. break;
  818. case fld_iso_time:
  819. is_localtime = 2; /* fix result */
  820. wpt->SetCreationTime(xml_parse_time(s));
  821. break;
  822. case fld_time:
  823. if ((is_localtime < 2) && (time < 0)) {
  824. time = unicsv_parse_time(s, &usec, &date);
  825. is_localtime = 1;
  826. }
  827. break;
  828. case fld_date:
  829. if ((is_localtime < 2) && (date < 0)) {
  830. date = unicsv_parse_date(CSTR(s), NULL);
  831. is_localtime = 1;
  832. }
  833. break;
  834. case fld_year:
  835. ymd.tm_year = s.toInt();
  836. break;
  837. case fld_month:
  838. ymd.tm_mon = s.toInt();
  839. break;
  840. case fld_day:
  841. ymd.tm_mday = s.toInt();
  842. break;
  843. case fld_hour:
  844. ymd.tm_hour = s.toInt();
  845. break;
  846. case fld_min:
  847. ymd.tm_min = s.toInt();
  848. break;
  849. case fld_sec:
  850. ymd.tm_sec = s.toInt();
  851. break;
  852. case fld_datetime:
  853. if ((is_localtime < 2) && (date < 0) && (time < 0)) {
  854. time = unicsv_parse_time(s, &usec, &date);
  855. is_localtime = 1;
  856. }
  857. break;
  858. case fld_ns:
  859. ns = s.startsWith('n', Qt::CaseInsensitive) ? 1 : -1;
  860. wpt->latitude *= ns;
  861. break;
  862. case fld_ew:
  863. ns = s.startsWith('e', Qt::CaseInsensitive) ? 1 : -1;
  864. wpt->longitude *= ew;
  865. break;
  866. case fld_garmin_city:
  867. case fld_garmin_postal_code:
  868. case fld_garmin_state:
  869. case fld_garmin_country:
  870. case fld_garmin_addr:
  871. case fld_garmin_phone_nr:
  872. case fld_garmin_phone_nr2:
  873. case fld_garmin_fax_nr:
  874. case fld_garmin_email:
  875. case fld_garmin_facility:
  876. gmsd = GMSD_FIND(wpt);
  877. if (! gmsd) {
  878. gmsd = garmin_fs_alloc(-1);
  879. fs_chain_add(&wpt->fs, (format_specific_data*) gmsd);
  880. }
  881. switch (unicsv_fields_tab[column]) {
  882. case fld_garmin_city:
  883. GMSD_SETQSTR(city, s);
  884. break;
  885. case fld_garmin_postal_code:
  886. GMSD_SETQSTR(postal_code, s);
  887. break;
  888. case fld_garmin_state:
  889. GMSD_SETQSTR(state, s);
  890. break;
  891. case fld_garmin_country:
  892. GMSD_SETQSTR(country, s);
  893. break;
  894. case fld_garmin_addr:
  895. GMSD_SETQSTR(addr, s);
  896. break;
  897. case fld_garmin_phone_nr:
  898. GMSD_SETQSTR(phone_nr, s);
  899. break;
  900. case fld_garmin_phone_nr2:
  901. GMSD_SETQSTR(phone_nr2, s);
  902. break;
  903. case fld_garmin_fax_nr:
  904. GMSD_SETQSTR(fax_nr, s);
  905. break;
  906. case fld_garmin_email:
  907. GMSD_SETQSTR(email, s);
  908. break;
  909. case fld_garmin_facility:
  910. GMSD_SETQSTR(facility, s);
  911. break;
  912. default:
  913. break;
  914. }
  915. break;
  916. case fld_gc_id:
  917. case fld_gc_type:
  918. case fld_gc_container:
  919. case fld_gc_terr:
  920. case fld_gc_diff:
  921. case fld_gc_is_archived:
  922. case fld_gc_is_available:
  923. case fld_gc_exported:
  924. case fld_gc_last_found:
  925. case fld_gc_placer:
  926. case fld_gc_placer_id:
  927. case fld_gc_hint:
  928. gc_data = wpt->AllocGCData();
  929. switch (unicsv_fields_tab[column]) {
  930. case fld_gc_id:
  931. gc_data->id = s.toInt();
  932. if (gc_data->id == 0) {
  933. gc_data->id = unicsv_parse_gc_id(s);
  934. }
  935. break;
  936. case fld_gc_type:
  937. gc_data->type = gs_mktype(s);
  938. break;
  939. case fld_gc_container:
  940. gc_data->container = gs_mkcont(s);
  941. break;
  942. case fld_gc_terr:
  943. gc_data->terr = s.toDouble() * 10;
  944. break;
  945. case fld_gc_diff:
  946. gc_data->diff = s.toDouble() * 10;
  947. break;
  948. case fld_gc_is_archived:
  949. gc_data->is_archived = unicsv_parse_status(s);
  950. break;
  951. case fld_gc_is_available:
  952. gc_data->is_available = unicsv_parse_status(s);
  953. break;
  954. case fld_gc_exported: {
  955. time_t time, date;
  956. int usec;
  957. time = unicsv_parse_time(s, &usec, &date);
  958. if (date || time) {
  959. gc_data->exported = unicsv_adjust_time(time, &date);
  960. }
  961. }
  962. break;
  963. case fld_gc_last_found: {
  964. time_t time, date;
  965. int usec;
  966. time = unicsv_parse_time(s, &usec, &date);
  967. if (date || time) {
  968. gc_data->last_found = unicsv_adjust_time(time, &date);
  969. }
  970. }
  971. break;
  972. case fld_gc_placer:
  973. gc_data->placer = s;
  974. break;
  975. case fld_gc_placer_id:
  976. gc_data->placer_id = s.toInt();
  977. break;
  978. case fld_gc_hint:
  979. gc_data->hint = s;
  980. break;
  981. default:
  982. break;
  983. }
  984. break;
  985. case fld_terminator: /* dummy */
  986. checked--;
  987. break;
  988. }
  989. }
  990. if (checked == 0) {
  991. delete wpt;
  992. return;
  993. }
  994. if (is_localtime < 2) { /* not fixed */
  995. if ((time >= 0) && (date >= 0)) {
  996. time_t t = date + time;
  997. if (is_localtime) {
  998. struct tm tm;
  999. tm = *gmtime(&t);
  1000. if (opt_utc) {
  1001. wpt->SetCreationTime(mkgmtime(&tm));
  1002. } else {
  1003. wpt->SetCreationTime(mklocaltime(&tm));
  1004. }
  1005. } else {
  1006. wpt->SetCreationTime(t);
  1007. }
  1008. } else if (time >= 0) {
  1009. wpt->SetCreationTime(time);
  1010. } else if (date >= 0) {
  1011. wpt->SetCreationTime(date);
  1012. } else if (ymd.tm_year || ymd.tm_mon || ymd.tm_mday) {
  1013. if (ymd.tm_year < 100) {
  1014. if (ymd.tm_year <= 70) {
  1015. ymd.tm_year += 2000;
  1016. } else {
  1017. ymd.tm_year += 1900;
  1018. }
  1019. }
  1020. ymd.tm_year -= 1900;
  1021. if (ymd.tm_mon == 0) {
  1022. ymd.tm_mon = 1;
  1023. }
  1024. if (ymd.tm_mday == 0) {
  1025. ymd.tm_mday = 1;
  1026. }
  1027. ymd.tm_mon--;
  1028. if (opt_utc) {
  1029. wpt->SetCreationTime(mkgmtime(&ymd));
  1030. } else {
  1031. wpt->SetCreationTime(mklocaltime(&ymd));
  1032. }
  1033. } else if (ymd.tm_hour || ymd.tm_min || ymd.tm_sec) {
  1034. if (opt_utc) {
  1035. wpt->SetCreationTime(mkgmtime(&ymd));
  1036. } else {
  1037. wpt->SetCreationTime(mklocaltime(&ymd));
  1038. }
  1039. }
  1040. if (usec >= 0) {
  1041. wpt->creation_time = wpt->creation_time.addMSecs(MICRO_TO_MILLI(usec));
  1042. }
  1043. if (opt_utc) {
  1044. wpt->creation_time += atoi(opt_utc) * SECONDS_PER_HOUR;
  1045. }
  1046. }
  1047. /* utm/bng/swiss can be optional */
  1048. if ((wpt->latitude == unicsv_unknown) && (wpt->longitude == unicsv_unknown)) {
  1049. if (utm_zone != -9999) {
  1050. GPS_Math_UTM_EN_To_Known_Datum(&wpt->latitude, &wpt->longitude,
  1051. utm_easting, utm_northing, utm_zone, utm_zc, unicsv_datum_idx);
  1052. } else if (bng_zone[0]) {
  1053. if (! GPS_Math_UKOSMap_To_WGS84_M(
  1054. bng_zone, bng_easting, bng_northing,
  1055. &wpt->latitude, &wpt->longitude))
  1056. fatal(MYNAME ": Unable to convert BNG coordinates (%s %.f %.f)!\n",
  1057. bng_zone, bng_easting, bng_northing);
  1058. src_datum = DATUM_WGS84; /* don't convert afterwards */
  1059. } else if ((swiss_easting != unicsv_unknown) && (swiss_northing != unicsv_unknown)) {
  1060. GPS_Math_Swiss_EN_To_WGS84(swiss_easting, swiss_northing,
  1061. &wpt->latitude, &wpt->longitude);
  1062. src_datum = DATUM_WGS84; /* don't convert afterwards */
  1063. }
  1064. }
  1065. if ((src_datum != DATUM_WGS84) &&
  1066. (wpt->latitude != unicsv_unknown) && (wpt->longitude != unicsv_unknown)) {
  1067. double alt;
  1068. GPS_Math_Known_Datum_To_WGS84_M(wpt->latitude, wpt->longitude, (double) 0.0,
  1069. &wpt->latitude, &wpt->longitude, &alt, src_datum);
  1070. }
  1071. switch (unicsv_data_type) {
  1072. case rtedata:
  1073. if (! unicsv_route) {
  1074. unicsv_route = route_head_alloc();
  1075. route_add_head(unicsv_route);
  1076. }
  1077. route_add_wpt(unicsv_route, wpt);
  1078. break;
  1079. case trkdata:
  1080. if (! unicsv_track) {
  1081. unicsv_track = route_head_alloc();
  1082. track_add_head(unicsv_track);
  1083. }
  1084. track_add_wpt(unicsv_track, wpt);
  1085. break;
  1086. default:
  1087. waypt_add(wpt);
  1088. }
  1089. }
  1090. static void
  1091. unicsv_rd(void)
  1092. {
  1093. char* buff;
  1094. if (unicsv_fieldsep == NULL) {
  1095. return;
  1096. }
  1097. while ((buff = gbfgetstr(fin))) {
  1098. buff = lrtrim(buff);
  1099. if ((*buff == '\0') || (*buff == '#')) {
  1100. continue;
  1101. }
  1102. unicsv_parse_one_line(buff);
  1103. }
  1104. }
  1105. /* =========================================================================== */
  1106. static void
  1107. unicsv_fatal_outside(const Waypoint* wpt)
  1108. {
  1109. gbfprintf(fout, "#####\n");
  1110. fatal(MYNAME ": %s (%s) is outside of convertable area of grid \"%s\"!\n",
  1111. wpt->shortname.isEmpty() ? "Waypoint" : qPrintable(wpt->shortname),
  1112. pretty_deg_format(wpt->latitude, wpt->longitude, 'd', NULL, 0),
  1113. gt_get_mps_grid_longname(unicsv_grid_idx, MYNAME));
  1114. }
  1115. static void
  1116. unicsv_print_str(const QString& s)
  1117. {
  1118. gbfputs(unicsv_fieldsep, fout);
  1119. QString t;
  1120. if (!s.isEmpty()) {
  1121. t = strenquote(s, UNICSV_QUOT_CHAR);
  1122. // I'm not sure these three replacements are necessary; they're just a
  1123. // slavish re-implementation of (what I think) the original C code
  1124. // was doing.
  1125. t.replace("\r\n", ",");
  1126. t.replace("\r", ",");
  1127. t.replace("\n", ",");
  1128. }
  1129. gbfputs(t.trimmed(), fout);
  1130. }
  1131. static void
  1132. unicsv_print_data_time(const QDateTime& idt)
  1133. {
  1134. if (!idt.isValid()) {
  1135. return;
  1136. }
  1137. QDateTime dt = idt;
  1138. if (opt_utc) {
  1139. //time += atoi(opt_utc) * SECONDS_PER_HOUR;
  1140. dt = dt.addSecs(atoi(opt_utc) * SECONDS_PER_HOUR);
  1141. dt = dt.toUTC();
  1142. }
  1143. unicsv_print_str(dt.toString("yyyy/MM/dd hh:mm:ss"));
  1144. }
  1145. #define FIELD_USED(a) (gb_getbit(&unicsv_outp_flags, a))
  1146. static void
  1147. unicsv_waypt_enum_cb(const Waypoint* wpt)
  1148. {
  1149. garmin_fs_t* gmsd;
  1150. const QString& shortname = wpt->shortname;
  1151. gmsd = GMSD_FIND(wpt);
  1152. if (!shortname.isEmpty()) {
  1153. gb_setbit(&unicsv_outp_flags, fld_shortname);
  1154. }
  1155. if (wpt->altitude != unknown_alt) {
  1156. gb_setbit(&unicsv_outp_flags, fld_altitude);
  1157. }
  1158. if (!wpt->icon_descr.isNull()) {
  1159. gb_setbit(&unicsv_outp_flags, fld_symbol);
  1160. }
  1161. if (!wpt->description.isEmpty() && shortname != wpt->description) {
  1162. gb_setbit(&unicsv_outp_flags, fld_description);
  1163. }
  1164. if (!wpt->notes.isEmpty() && shortname != wpt->notes) {
  1165. if ((wpt->description.isEmpty()) || (wpt->description != wpt->notes)) {
  1166. gb_setbit(&unicsv_outp_flags, fld_notes);
  1167. }
  1168. }
  1169. if (wpt->HasUrlLink()) {
  1170. gb_setbit(&unicsv_outp_flags, fld_url);
  1171. }
  1172. if (wpt->creation_time.isValid()) {
  1173. gb_setbit(&unicsv_outp_flags, fld_time);
  1174. if (wpt->creation_time.toTime_t() >= SECONDS_PER_DAY) {
  1175. gb_setbit(&unicsv_outp_flags, fld_date);
  1176. }
  1177. }
  1178. if (wpt->fix != fix_unknown) {
  1179. gb_setbit(&unicsv_outp_flags, fld_fix);
  1180. }
  1181. if (wpt->vdop > 0) {
  1182. gb_setbit(&unicsv_outp_flags, fld_vdop);
  1183. }
  1184. if (wpt->hdop > 0) {
  1185. gb_setbit(&unicsv_outp_flags, fld_hdop);
  1186. }
  1187. if (wpt->pdop > 0) {
  1188. gb_setbit(&unicsv_outp_flags, fld_pdop);
  1189. }
  1190. if (wpt->sat > 0) {
  1191. gb_setbit(&unicsv_outp_flags, fld_sat);
  1192. }
  1193. if (wpt->heartrate != 0) {
  1194. gb_setbit(&unicsv_outp_flags, fld_heartrate);
  1195. }
  1196. if (wpt->cadence != 0) {
  1197. gb_setbit(&unicsv_outp_flags, fld_cadence);
  1198. }
  1199. if (wpt->power > 0) {
  1200. gb_setbit(&unicsv_outp_flags, fld_power);
  1201. }
  1202. /* "flagged" waypoint members */
  1203. if WAYPT_HAS(wpt, course) {
  1204. gb_setbit(&unicsv_outp_flags, fld_course);
  1205. }
  1206. if WAYPT_HAS(wpt, depth) {
  1207. gb_setbit(&unicsv_outp_flags, fld_depth);
  1208. }
  1209. if WAYPT_HAS(wpt, speed) {
  1210. gb_setbit(&unicsv_outp_flags, fld_speed);
  1211. }
  1212. if WAYPT_HAS(wpt, proximity) {
  1213. gb_setbit(&unicsv_outp_flags, fld_proximity);
  1214. }
  1215. if WAYPT_HAS(wpt, temperature) {
  1216. gb_setbit(&unicsv_outp_flags, fld_temperature);
  1217. }
  1218. if (gmsd) {
  1219. if GMSD_HAS(addr) {
  1220. gb_setbit(&unicsv_outp_flags, fld_garmin_addr);
  1221. }
  1222. if GMSD_HAS(city) {
  1223. gb_setbit(&unicsv_outp_flags, fld_garmin_city);
  1224. }
  1225. if GMSD_HAS(country) {
  1226. gb_setbit(&unicsv_outp_flags, fld_garmin_country);
  1227. }
  1228. if GMSD_HAS(phone_nr) {
  1229. gb_setbit(&unicsv_outp_flags, fld_garmin_phone_nr);
  1230. }
  1231. if GMSD_HAS(phone_nr2) {
  1232. gb_setbit(&unicsv_outp_flags, fld_garmin_phone_nr2);
  1233. }
  1234. if GMSD_HAS(fax_nr) {
  1235. gb_setbit(&unicsv_outp_flags, fld_garmin_fax_nr);
  1236. }
  1237. if GMSD_HAS(email) {
  1238. gb_setbit(&unicsv_outp_flags, fld_garmin_email);
  1239. }
  1240. if GMSD_HAS(postal_code) {
  1241. gb_setbit(&unicsv_outp_flags, fld_garmin_postal_code);
  1242. }
  1243. if GMSD_HAS(state) {
  1244. gb_setbit(&unicsv_outp_flags, fld_garmin_state);
  1245. }
  1246. if GMSD_HAS(facility) {
  1247. gb_setbit(&unicsv_outp_flags, fld_garmin_facility);
  1248. }
  1249. }
  1250. if (! wpt->EmptyGCData()) {
  1251. const geocache_data* gc_data = wpt->gc_data;
  1252. if (gc_data->id) {
  1253. gb_setbit(&unicsv_outp_flags, fld_gc_id);
  1254. }
  1255. if (gc_data->type) {
  1256. gb_setbit(&unicsv_outp_flags, fld_gc_type);
  1257. }
  1258. if (gc_data->container) {
  1259. gb_setbit(&unicsv_outp_flags, fld_gc_container);
  1260. }
  1261. if (gc_data->terr) {
  1262. gb_setbit(&unicsv_outp_flags, fld_gc_terr);
  1263. }
  1264. if (gc_data->diff) {
  1265. gb_setbit(&unicsv_outp_flags, fld_gc_diff);
  1266. }
  1267. if (gc_data->is_archived) {
  1268. gb_setbit(&unicsv_outp_flags, fld_gc_is_archived);
  1269. }
  1270. if (gc_data->is_available) {
  1271. gb_setbit(&unicsv_outp_flags, fld_gc_is_available);
  1272. }
  1273. if (gc_data->exported.isValid()) {
  1274. gb_setbit(&unicsv_outp_flags, fld_gc_exported);
  1275. }
  1276. if (gc_data->last_found.isValid()) {
  1277. gb_setbit(&unicsv_outp_flags, fld_gc_last_found);
  1278. }
  1279. if (!gc_data->placer.isEmpty()) {
  1280. gb_setbit(&unicsv_outp_flags, fld_gc_placer);
  1281. }
  1282. if (gc_data->placer_id) {
  1283. gb_setbit(&unicsv_outp_flags, fld_gc_placer_id);
  1284. }
  1285. if (!gc_data->hint.isEmpty()) {
  1286. gb_setbit(&unicsv_outp_flags, fld_gc_hint);
  1287. }
  1288. }
  1289. }
  1290. static void
  1291. unicsv_waypt_disp_cb(const Waypoint* wpt)
  1292. {
  1293. double lat, lon, alt;
  1294. char* cout = NULL;
  1295. garmin_fs_t* gmsd;
  1296. const geocache_data* gc_data = NULL;
  1297. unicsv_waypt_ct++;
  1298. QString shortname = wpt->shortname;
  1299. gmsd = GMSD_FIND(wpt);
  1300. if (unicsv_datum_idx == DATUM_WGS84) {
  1301. lat = wpt->latitude;
  1302. lon = wpt->longitude;
  1303. alt = wpt->altitude;
  1304. } else {
  1305. GPS_Math_WGS84_To_Known_Datum_M(wpt->latitude, wpt->longitude, 0.0,
  1306. &lat, &lon, &alt, unicsv_datum_idx);
  1307. }
  1308. gbfprintf(fout, "%d%s", unicsv_waypt_ct, unicsv_fieldsep);
  1309. switch (unicsv_grid_idx) {
  1310. case grid_lat_lon_ddd:
  1311. cout = pretty_deg_format(lat, lon, 'd', unicsv_fieldsep, 0);
  1312. gbfputs(cout, fout);
  1313. break;
  1314. case grid_lat_lon_dmm:
  1315. cout = pretty_deg_format(lat, lon, 'm', unicsv_fieldsep, 0);
  1316. gbfputs(cout, fout);
  1317. break;
  1318. case grid_lat_lon_dms: {
  1319. char* sep;
  1320. QString tmp;
  1321. cout = pretty_deg_format(lat, lon, 's', unicsv_fieldsep, 0);
  1322. sep = strchr(cout, ',');
  1323. *sep = '\0';
  1324. tmp = strenquote(cout, UNICSV_QUOT_CHAR);
  1325. gbfprintf(fout, "%s%s", CSTR(tmp), unicsv_fieldsep);
  1326. tmp = strenquote(sep+1, UNICSV_QUOT_CHAR);
  1327. gbfputs(tmp, fout);
  1328. }
  1329. break;
  1330. case grid_bng: {
  1331. char map[3];
  1332. double north, east;
  1333. if (! GPS_Math_WGS84_To_UKOSMap_M(wpt->latitude, wpt->longitude, &east, &north, map)) {
  1334. unicsv_fatal_outside(wpt);
  1335. }
  1336. gbfprintf(fout, "%s%s%5.0f%s%5.0f",
  1337. map, unicsv_fieldsep,
  1338. east, unicsv_fieldsep,
  1339. north);
  1340. break;
  1341. }
  1342. case grid_utm: {
  1343. int zone;
  1344. char zonec;
  1345. double north, east;
  1346. if (! GPS_Math_Known_Datum_To_UTM_EN(lat, lon,
  1347. &east, &north, &zone, &zonec, unicsv_datum_idx)) {
  1348. unicsv_fatal_outside(wpt);
  1349. }
  1350. gbfprintf(fout, "%02d%s%c%s%.0f%s%.0f",
  1351. zone, unicsv_fieldsep,
  1352. zonec, unicsv_fieldsep,
  1353. east, unicsv_fieldsep,
  1354. north);
  1355. break;
  1356. }
  1357. case grid_swiss: {
  1358. double north, east;
  1359. if (! GPS_Math_WGS84_To_Swiss_EN(wpt->latitude, wpt->longitude, &east, &north)) {
  1360. unicsv_fatal_outside(wpt);
  1361. }
  1362. gbfprintf(fout, "%.f%s%.f",
  1363. east, unicsv_fieldsep, north);
  1364. break;
  1365. }
  1366. default:
  1367. gbfprintf(fout, "%.*f%s%.*f", llprec, lat, unicsv_fieldsep, llprec, lon);
  1368. break;
  1369. }
  1370. if (cout) {
  1371. xfree(cout);
  1372. }
  1373. if FIELD_USED(fld_shortname) {
  1374. unicsv_print_str(shortname);
  1375. }
  1376. if FIELD_USED(fld_altitude) {
  1377. if (wpt->altitude != unknown_alt) {
  1378. gbfprintf(fout, "%s%.1f", unicsv_fieldsep, wpt->altitude);
  1379. } else {
  1380. gbfputs(unicsv_fieldsep, fout);
  1381. }
  1382. }
  1383. if FIELD_USED(fld_description) {
  1384. unicsv_print_str(wpt->description);
  1385. }
  1386. if FIELD_USED(fld_notes) {
  1387. unicsv_print_str(wpt->notes);
  1388. }
  1389. if FIELD_USED(fld_symbol) {
  1390. unicsv_print_str(wpt->icon_descr.isNull() ? "Waypoint" : wpt->icon_descr);
  1391. }
  1392. if FIELD_USED(fld_depth) {
  1393. if WAYPT_HAS(wpt, depth) {
  1394. gbfprintf(fout, "%s%.3f", unicsv_fieldsep, wpt->depth);
  1395. } else {
  1396. gbfputs(unicsv_fieldsep, fout);
  1397. }
  1398. }
  1399. if FIELD_USED(fld_proximity) {
  1400. if WAYPT_HAS(wpt, proximity) {
  1401. gbfprintf(fout, "%s%.f", unicsv_fieldsep, wpt->proximity);
  1402. } else {
  1403. gbfputs(unicsv_fieldsep, fout);
  1404. }
  1405. }
  1406. if FIELD_USED(fld_temperature) {
  1407. if WAYPT_HAS(wpt, temperature) {
  1408. gbfprintf(fout, "%s%.3f", unicsv_fieldsep, wpt->temperature);
  1409. } else {
  1410. gbfputs(unicsv_fieldsep, fout);
  1411. }
  1412. }
  1413. if FIELD_USED(fld_speed) {
  1414. if WAYPT_HAS(wpt, speed) {
  1415. gbfprintf(fout, "%s%.2f", unicsv_fieldsep, wpt->speed);
  1416. } else {
  1417. gbfputs(unicsv_fieldsep, fout);
  1418. }
  1419. }
  1420. if FIELD_USED(fld_course) {
  1421. if WAYPT_HAS(wpt, course) {
  1422. gbfprintf(fout, "%s%.1f", unicsv_fieldsep, wpt->course);
  1423. } else {
  1424. gbfputs(unicsv_fieldsep, fout);
  1425. }
  1426. }
  1427. if FIELD_USED(fld_fix) {
  1428. const char* fix;
  1429. switch (wpt->fix) {
  1430. case fix_none:
  1431. fix = "none";
  1432. break;
  1433. case fix_2d:
  1434. fix = "2d";
  1435. break;
  1436. case fix_3d:
  1437. fix = "3d";
  1438. break;
  1439. case fix_dgps:
  1440. fix = "dgps";
  1441. break;
  1442. case fix_pps:
  1443. fix = "pps";
  1444. break;
  1445. default:
  1446. fix = NULL;
  1447. }
  1448. if (fix) {
  1449. unicsv_print_str(fix);
  1450. } else {
  1451. gbfputs(unicsv_fieldsep, fout);
  1452. }
  1453. }
  1454. if FIELD_USED(fld_hdop) {
  1455. if (wpt->hdop > 0) {
  1456. gbfprintf(fout, "%s%.2f", unicsv_fieldsep, wpt->hdop);
  1457. } else {
  1458. gbfputs(unicsv_fieldsep, fout);
  1459. }
  1460. }
  1461. if FIELD_USED(fld_vdop) {
  1462. if (wpt->vdop > 0) {
  1463. gbfprintf(fout, "%s%.2f", unicsv_fieldsep, wpt->vdop);
  1464. } else {
  1465. gbfputs(unicsv_fieldsep, fout);
  1466. }
  1467. }
  1468. if FIELD_USED(fld_pdop) {
  1469. if (wpt->pdop > 0) {
  1470. gbfprintf(fout, "%s%.2f", unicsv_fieldsep, wpt->pdop);
  1471. } else {
  1472. gbfputs(unicsv_fieldsep, fout);
  1473. }
  1474. }
  1475. if FIELD_USED(fld_sat) {
  1476. if (wpt->sat > 0) {
  1477. gbfprintf(fout, "%s%d", unicsv_fieldsep, wpt->sat);
  1478. } else {
  1479. gbfputs(unicsv_fieldsep, fout);
  1480. }
  1481. }
  1482. if FIELD_USED(fld_heartrate) {
  1483. if (wpt->heartrate != 0) {
  1484. gbfprintf(fout, "%s%u", unicsv_fieldsep, wpt->heartrate);
  1485. } else {
  1486. gbfputs(unicsv_fieldsep, fout);
  1487. }
  1488. }
  1489. if FIELD_USED(fld_cadence) {
  1490. if (wpt->cadence != 0) {
  1491. gbfprintf(fout, "%s%u", unicsv_fieldsep, wpt->cadence);
  1492. } else {
  1493. gbfputs(unicsv_fieldsep, fout);
  1494. }
  1495. }
  1496. if FIELD_USED(fld_power) {
  1497. if (wpt->power > 0) {
  1498. gbfprintf(fout, "%s%.1f", unicsv_fieldsep, wpt->power);
  1499. } else {
  1500. gbfputs(unicsv_fieldsep, fout);
  1501. }
  1502. }
  1503. if FIELD_USED(fld_date) {
  1504. if (wpt->creation_time.toTime_t() >= SECONDS_PER_DAY) {
  1505. QDateTime dt;
  1506. if (opt_utc) {
  1507. dt = wpt->GetCreationTime().toUTC();
  1508. // We might wrap to a different day by overriding the TZ offset.
  1509. dt = dt.addSecs(atoi(opt_utc) * SECONDS_PER_HOUR);
  1510. } else {
  1511. dt = wpt->GetCreationTime();
  1512. }
  1513. QString date = dt.toString("yyyy/MM/dd");
  1514. gbfputs(unicsv_fieldsep, fout);
  1515. gbfputs(date, fout);
  1516. } else {
  1517. gbfputs(unicsv_fieldsep, fout);
  1518. }
  1519. }
  1520. if FIELD_USED(fld_time) {
  1521. if (wpt->creation_time.isValid()) {
  1522. QTime t;
  1523. if (opt_utc) {
  1524. t = wpt->GetCreationTime().toUTC().time();
  1525. t = t.addSecs(atoi(opt_utc) * SECONDS_PER_HOUR);
  1526. } else {
  1527. t = wpt->GetCreationTime().time();
  1528. }
  1529. QString out;
  1530. if (t.msec() > 0) {
  1531. out = t.toString("hh:mm:ss.z");
  1532. } else {
  1533. out = t.toString("hh:mm:ss");
  1534. }
  1535. gbfputs(unicsv_fieldsep, fout);
  1536. gbfputs(out, fout);
  1537. } else {
  1538. gbfputs(unicsv_fieldsep, fout);
  1539. }
  1540. }
  1541. if (FIELD_USED(fld_url)) {
  1542. if (!wpt->HasUrlLink()) {
  1543. unicsv_print_str("");
  1544. } else {
  1545. UrlLink l = wpt->GetUrlLink();
  1546. unicsv_print_str(l.url_);
  1547. }
  1548. }
  1549. if FIELD_USED(fld_garmin_facility) {
  1550. unicsv_print_str(GMSD_GET(facility, NULL));
  1551. }
  1552. if FIELD_USED(fld_garmin_addr) {
  1553. unicsv_print_str(GMSD_GET(addr, NULL));
  1554. }
  1555. if FIELD_USED(fld_garmin_city) {
  1556. unicsv_print_str(GMSD_GET(city, NULL));
  1557. }
  1558. if FIELD_USED(fld_garmin_postal_code) {
  1559. unicsv_print_str(GMSD_GET(postal_code, NULL));
  1560. }
  1561. if FIELD_USED(fld_garmin_state) {
  1562. unicsv_print_str(GMSD_GET(state, NULL));
  1563. }
  1564. if FIELD_USED(fld_garmin_country) {
  1565. unicsv_print_str(GMSD_GET(country, NULL));
  1566. }
  1567. if FIELD_USED(fld_garmin_phone_nr) {
  1568. unicsv_print_str(GMSD_GET(phone_nr, NULL));
  1569. }
  1570. if FIELD_USED(fld_garmin_phone_nr2) {
  1571. unicsv_print_str(GMSD_GET(phone_nr2, NULL));
  1572. }
  1573. if FIELD_USED(fld_garmin_fax_nr) {
  1574. unicsv_print_str(GMSD_GET(fax_nr, NULL));
  1575. }
  1576. if FIELD_USED(fld_garmin_email) {
  1577. unicsv_print_str(GMSD_GET(email, NULL));
  1578. }
  1579. if (wpt->EmptyGCData()) {
  1580. gc_data = NULL;
  1581. } else {
  1582. gc_data = wpt->gc_data;
  1583. }
  1584. if FIELD_USED(fld_gc_id) {
  1585. gbfputs(unicsv_fieldsep, fout);
  1586. if (gc_data && gc_data->id) {
  1587. gbfprintf(fout, "%d", gc_data->id);
  1588. }
  1589. }
  1590. if FIELD_USED(fld_gc_type) {
  1591. if (gc_data) {
  1592. unicsv_print_str(gs_get_cachetype(gc_data->type));
  1593. } else {
  1594. gbfputs(unicsv_fieldsep, fout);
  1595. }
  1596. }
  1597. if FIELD_USED(fld_gc_container) {
  1598. if (gc_data) {
  1599. unicsv_print_str(gs_get_container(gc_data->container));
  1600. } else {
  1601. gbfputs(unicsv_fieldsep, fout);
  1602. }
  1603. }
  1604. if FIELD_USED(fld_gc_terr) {
  1605. gbfputs(unicsv_fieldsep, fout);
  1606. if (gc_data && gc_data->terr) {
  1607. gbfprintf(fout, "%.1f", (double)gc_data->terr / 10);
  1608. }
  1609. }
  1610. if FIELD_USED(fld_gc_diff) {
  1611. gbfputs(unicsv_fieldsep, fout);
  1612. if (gc_data && gc_data->diff) {
  1613. gbfprintf(fout, "%.1f", (double)gc_data->diff / 10);
  1614. }
  1615. }
  1616. if FIELD_USED(fld_gc_is_archived) {
  1617. if (gc_data && gc_data->is_archived) {
  1618. unicsv_print_str((gc_data->is_archived == status_true) ? "True" : "False");
  1619. } else {
  1620. gbfputs(unicsv_fieldsep, fout);
  1621. }
  1622. }
  1623. if FIELD_USED(fld_gc_is_available) {
  1624. if (gc_data && gc_data->is_available) {
  1625. unicsv_print_str((gc_data->is_available == status_true) ? "True" : "False");
  1626. } else {
  1627. gbfputs(unicsv_fieldsep, fout);
  1628. }
  1629. }
  1630. if FIELD_USED(fld_gc_exported) {
  1631. if (gc_data) {
  1632. unicsv_print_data_time(gc_data->exported);
  1633. } else {
  1634. gbfputs(unicsv_fieldsep, fout);
  1635. }
  1636. }
  1637. if FIELD_USED(fld_gc_last_found) {
  1638. if (gc_data) {
  1639. unicsv_print_data_time(gc_data->last_found);
  1640. } else {
  1641. gbfputs(unicsv_fieldsep, fout);
  1642. }
  1643. }
  1644. if FIELD_USED(fld_gc_placer) {
  1645. if (gc_data) {
  1646. unicsv_print_str(gc_data->placer);
  1647. } else {
  1648. gbfputs(unicsv_fieldsep, fout);
  1649. }
  1650. }
  1651. if FIELD_USED(fld_gc_placer_id) {
  1652. gbfputs(unicsv_fieldsep, fout);
  1653. if (gc_data && gc_data->placer_id) {
  1654. gbfprintf(fout, "%d", gc_data->placer_id);
  1655. }
  1656. }
  1657. if FIELD_USED(fld_gc_hint) {
  1658. if (gc_data) {
  1659. unicsv_print_str(gc_data->hint);
  1660. } else {
  1661. gbfputs(unicsv_fieldsep, fout);
  1662. }
  1663. }
  1664. if (opt_format) {
  1665. unicsv_print_str(wpt->session->name);
  1666. }
  1667. if (opt_filename) {
  1668. unicsv_print_str(wpt->session->filename);
  1669. }
  1670. gbfputs(UNICSV_LINE_SEP, fout);
  1671. }
  1672. /* --------------------------------------------------------------------------- */
  1673. static void
  1674. unicsv_wr_init(const QString& filename)
  1675. {
  1676. fout = gbfopen(filename, "wb", MYNAME);
  1677. memset(&unicsv_outp_flags, 0, sizeof(unicsv_outp_flags));
  1678. unicsv_grid_idx = grid_unknown;
  1679. unicsv_datum_idx = DATUM_WGS84;
  1680. unicsv_fieldsep = UNICSV_FIELD_SEP;
  1681. unicsv_waypt_ct = 0;
  1682. if (opt_grid != NULL) {
  1683. int i;
  1684. if (sscanf(opt_grid, "%d", &i)) {
  1685. unicsv_grid_idx = (grid_type) i;
  1686. if ((unicsv_grid_idx < GRID_INDEX_MIN) || (unicsv_grid_idx > GRID_INDEX_MAX))
  1687. fatal(MYNAME ": Grid index out of range (%d..%d)!\n",
  1688. (int)GRID_INDEX_MIN, (int)GRID_INDEX_MAX);
  1689. } else {
  1690. unicsv_grid_idx = gt_lookup_grid_type(opt_grid, MYNAME);
  1691. }
  1692. }
  1693. if (unicsv_grid_idx == grid_bng)
  1694. /* force datum to "Ord Srvy Grt Britn" / OSGB36 */
  1695. /* ! ignore parameter "Datum" ! */
  1696. {
  1697. unicsv_datum_idx = DATUM_OSGB36;
  1698. } else if (unicsv_grid_idx == grid_swiss)
  1699. /* ! ignore parameter "Datum" ! */
  1700. {
  1701. unicsv_datum_idx = DATUM_WGS84; /* internal, becomes CH1903 */
  1702. } else {
  1703. unicsv_datum_idx = gt_lookup_datum_index(opt_datum, MYNAME);
  1704. }
  1705. llprec = atoi(opt_prec);
  1706. }
  1707. static void
  1708. unicsv_wr_deinit(void)
  1709. {
  1710. gbfclose(fout);
  1711. }
  1712. // Waypoints are default-on and there's no way to turn them off. This is
  1713. // used to see if a user specified (-t OR -r) in addition to the default
  1714. // of -w. It's pretty weak, but it's better than letting the last flag
  1715. // 'win' which can result in no data silently being displayed.
  1716. static void
  1717. unicsv_check_modes(bool test) {
  1718. if (test) {
  1719. Fatal() << MYNAME <<
  1720. " : Invalid combination of -w, -t, -r selected. Use only one.";
  1721. }
  1722. }
  1723. static void
  1724. unicsv_wr(void)
  1725. {
  1726. switch (global_opts.objective) {
  1727. case wptdata:
  1728. case unknown_gpsdata:
  1729. unicsv_check_modes (doing_rtes || doing_trks);
  1730. waypt_disp_all(unicsv_waypt_enum_cb);
  1731. break;
  1732. case trkdata:
  1733. unicsv_check_modes (doing_rtes);
  1734. track_disp_all(NULL, NULL, unicsv_waypt_enum_cb);
  1735. break;
  1736. case rtedata:
  1737. unicsv_check_modes (doing_trks);
  1738. route_disp_all(NULL, NULL, unicsv_waypt_enum_cb);
  1739. break;
  1740. case posndata:
  1741. Fatal() << MYNAME << ": Realtime positioning not supported.";
  1742. }
  1743. gbfprintf(fout, "No%s", unicsv_fieldsep);
  1744. switch (unicsv_grid_idx) {
  1745. case grid_bng:
  1746. /* indexed parameters doesn't work under __win32__ (mingw)
  1747. gbfprintf(fout, "BNG-Zone%1$sBNG-East%1$sBNG-North", unicsv_fieldsep);
  1748. */
  1749. gbfprintf(fout, "BNG-Zone%sBNG-East%sBNG-North",
  1750. unicsv_fieldsep, unicsv_fieldsep);
  1751. break;
  1752. case grid_utm:
  1753. /* indexed parameters doesn't work under __win32__ (mingw)
  1754. gbfprintf(fout, "BNG-Zone%1$sBNG-East%1$sBNG-North", unicsv_fieldsep);
  1755. */
  1756. gbfprintf(fout, "UTM-Zone%sUTM-Ch%sUTM-East%sUTM-North",
  1757. unicsv_fieldsep, unicsv_fieldsep, unicsv_fieldsep);
  1758. break;
  1759. case grid_swiss:
  1760. gbfprintf(fout, "Swiss-East%sSwiss-North",
  1761. unicsv_fieldsep);
  1762. break;
  1763. default:
  1764. gbfprintf(fout, "Latitude%sLongitude", unicsv_fieldsep);
  1765. }
  1766. if FIELD_USED(fld_shortname) {
  1767. gbfprintf(fout, "%sName", unicsv_fieldsep);
  1768. }
  1769. if FIELD_USED(fld_altitude) {
  1770. gbfprintf(fout, "%sAltitude", unicsv_fieldsep);
  1771. }
  1772. if FIELD_USED(fld_description) {
  1773. gbfprintf(fout, "%sDescription", unicsv_fieldsep);
  1774. }
  1775. if FIELD_USED(fld_notes) {
  1776. gbfprintf(fout, "%sNotes", unicsv_fieldsep);
  1777. }
  1778. if FIELD_USED(fld_symbol) {
  1779. gbfprintf(fout, "%sSymbol", unicsv_fieldsep);
  1780. }
  1781. if FIELD_USED(fld_depth) {
  1782. gbfprintf(fout, "%sDepth", unicsv_fieldsep);
  1783. }
  1784. if FIELD_USED(fld_proximity) {
  1785. gbfprintf(fout, "%sProximity", unicsv_fieldsep);
  1786. }
  1787. if FIELD_USED(fld_temperature) {
  1788. gbfprintf(fout, "%sTemperature", unicsv_fieldsep);
  1789. }
  1790. if FIELD_USED(fld_speed) {
  1791. gbfprintf(fout, "%sSpeed", unicsv_fieldsep);
  1792. }
  1793. if FIELD_USED(fld_course) {
  1794. gbfprintf(fout, "%sCourse", unicsv_fieldsep);
  1795. }
  1796. if FIELD_USED(fld_fix) {
  1797. gbfprintf(fout, "%sFIX", unicsv_fieldsep);
  1798. }
  1799. if FIELD_USED(fld_hdop) {
  1800. gbfprintf(fout, "%sHDOP", unicsv_fieldsep);
  1801. }
  1802. if FIELD_USED(fld_vdop) {
  1803. gbfprintf(fout, "%sVDOP", unicsv_fieldsep);
  1804. }
  1805. if FIELD_USED(fld_pdop) {
  1806. gbfprintf(fout, "%sPDOP", unicsv_fieldsep);
  1807. }
  1808. if FIELD_USED(fld_sat) {
  1809. gbfprintf(fout, "%sSatellites", unicsv_fieldsep);
  1810. }
  1811. if FIELD_USED(fld_heartrate) {
  1812. gbfprintf(fout, "%sHeartrate", unicsv_fieldsep);
  1813. }
  1814. if FIELD_USED(fld_cadence) {
  1815. gbfprintf(fout, "%sCadence", unicsv_fieldsep);
  1816. }
  1817. if FIELD_USED(fld_power) {
  1818. gbfprintf(fout, "%sPower", unicsv_fieldsep);
  1819. }
  1820. if FIELD_USED(fld_date) {
  1821. gbfprintf(fout, "%sDate", unicsv_fieldsep);
  1822. }
  1823. if FIELD_USED(fld_time) {
  1824. gbfprintf(fout, "%sTime", unicsv_fieldsep);
  1825. }
  1826. if FIELD_USED(fld_url) {
  1827. gbfprintf(fout, "%sURL", unicsv_fieldsep);
  1828. }
  1829. if FIELD_USED(fld_garmin_facility) {
  1830. gbfprintf(fout, "%sFacility", unicsv_fieldsep);
  1831. }
  1832. if FIELD_USED(fld_garmin_addr) {
  1833. gbfprintf(fout, "%sAddress", unicsv_fieldsep);
  1834. }
  1835. if FIELD_USED(fld_garmin_city) {
  1836. gbfprintf(fout, "%sCity", unicsv_fieldsep);
  1837. }
  1838. if FIELD_USED(fld_garmin_postal_code) {
  1839. gbfprintf(fout, "%sPostalCode", unicsv_fieldsep);
  1840. }
  1841. if FIELD_USED(fld_garmin_state) {
  1842. gbfprintf(fout, "%sState", unicsv_fieldsep);
  1843. }
  1844. if FIELD_USED(fld_garmin_country) {
  1845. gbfprintf(fout, "%sCountry", unicsv_fieldsep);
  1846. }
  1847. if FIELD_USED(fld_garmin_phone_nr) {
  1848. gbfprintf(fout, "%sPhone", unicsv_fieldsep);
  1849. }
  1850. if FIELD_USED(fld_garmin_phone_nr2) {
  1851. gbfprintf(fout, "%sPhone2", unicsv_fieldsep);
  1852. }
  1853. if FIELD_USED(fld_garmin_fax_nr) {
  1854. gbfprintf(fout, "%sFax", unicsv_fieldsep);
  1855. }
  1856. if FIELD_USED(fld_garmin_email) {
  1857. gbfprintf(fout, "%sEmail", unicsv_fieldsep);
  1858. }
  1859. if FIELD_USED(fld_gc_id) {
  1860. gbfprintf(fout, "%sGCID", unicsv_fieldsep);
  1861. }
  1862. if FIELD_USED(fld_gc_type) {
  1863. gbfprintf(fout, "%sType", unicsv_fieldsep);
  1864. }
  1865. if FIELD_USED(fld_gc_container) {
  1866. gbfprintf(fout, "%sContainer", unicsv_fieldsep);
  1867. }
  1868. if FIELD_USED(fld_gc_terr) {
  1869. gbfprintf(fout, "%sTerrain", unicsv_fieldsep);
  1870. }
  1871. if FIELD_USED(fld_gc_diff) {
  1872. gbfprintf(fout, "%sDifficulty", unicsv_fieldsep);
  1873. }
  1874. if FIELD_USED(fld_gc_is_archived) {
  1875. gbfprintf(fout, "%sArchived", unicsv_fieldsep);
  1876. }
  1877. if FIELD_USED(fld_gc_is_available) {
  1878. gbfprintf(fout, "%sAvailable", unicsv_fieldsep);
  1879. }
  1880. if FIELD_USED(fld_gc_exported) {
  1881. gbfprintf(fout, "%sExported", unicsv_fieldsep);
  1882. }
  1883. if FIELD_USED(fld_gc_last_found) {
  1884. gbfprintf(fout, "%sLast Found", unicsv_fieldsep);
  1885. }
  1886. if FIELD_USED(fld_gc_placer) {
  1887. gbfprintf(fout, "%sPlacer", unicsv_fieldsep);
  1888. }
  1889. if FIELD_USED(fld_gc_placer_id) {
  1890. gbfprintf(fout, "%sPlacer ID", unicsv_fieldsep);
  1891. }
  1892. if FIELD_USED(fld_gc_hint) {
  1893. gbfprintf(fout, "%sHint", unicsv_fieldsep);
  1894. }
  1895. if (opt_format) {
  1896. gbfprintf(fout, "%sFormat", unicsv_fieldsep);
  1897. }
  1898. if (opt_filename) {
  1899. gbfprintf(fout, "%sFilename", unicsv_fieldsep);
  1900. }
  1901. gbfputs(UNICSV_LINE_SEP, fout);
  1902. switch (global_opts.objective) {
  1903. case wptdata:
  1904. waypt_disp_all(unicsv_waypt_disp_cb);
  1905. break;
  1906. case trkdata:
  1907. track_disp_all(NULL, NULL, unicsv_waypt_disp_cb);
  1908. break;
  1909. case rtedata:
  1910. route_disp_all(NULL, NULL, unicsv_waypt_disp_cb);
  1911. break;
  1912. default:
  1913. break;
  1914. }
  1915. }
  1916. /* --------------------------------------------------------------------------- */
  1917. ff_vecs_t unicsv_vecs = {
  1918. ff_type_file,
  1919. FF_CAP_RW_ALL,
  1920. unicsv_rd_init,
  1921. unicsv_wr_init,
  1922. unicsv_rd_deinit,
  1923. unicsv_wr_deinit,
  1924. unicsv_rd,
  1925. unicsv_wr,
  1926. NULL,
  1927. unicsv_args,
  1928. CET_CHARSET_ASCII, 0 /* can be changed with -c ... */
  1929. };