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.

compegps.cc 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. /*
  2. Support for CompeGPS waypoint (.wpt), route (.rte) and track (.trk) files,
  3. Copyright (C) 2005 Olaf Klein, o.b.klein@gpsbabel.org
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
  15. */
  16. /*
  17. History:
  18. 10/23/2005: First release; only a reader
  19. 10/25/2005: becomes a writer too
  20. 10/26/2005: received documention from CompeGPS team
  21. added fatals for "G" and "U" if not WGS84 and lat/lon
  22. 08/13/2006: switch to gbfile api
  23. */
  24. /*
  25. the meaning of leading characters in CompeGPS data lines (enhanced PCX):
  26. header lines:
  27. "G": WGS 84 - Datum of the map
  28. "N": Anybody - Name of the user
  29. "L": -02:00:00 - Difference to UTC
  30. "M": ... - Any comments
  31. "R": 16711680 , xxxx , 1 - Route header
  32. "U": 1 - System of coordinates (0=UTM 1=Latitude/Longitude)
  33. "C": 0 0 255 2 -1.000000 - ???
  34. "V": 0.0 0.0 0 0 0 0 0.0 - ???
  35. "E": 0|1|00-NUL-00 00:00:00|00:00:00|0 - ???
  36. data lines:
  37. "W": if(route) routepoint; else waypoint
  38. "T": trackpoint
  39. "t": if(track) additionally track info
  40. if(!track) additionally trackpoint info
  41. "a": link to ...
  42. "w": waypoint additional info
  43. */
  44. #include "defs.h"
  45. #include "cet_util.h"
  46. #include "csv_util.h"
  47. #if CSVFMTS_ENABLED
  48. #include <math.h>
  49. #include "jeeps/gpsmath.h"
  50. #include <stdlib.h>
  51. #include <stdio.h>
  52. #define MYNAME "CompeGPS"
  53. #define SHORT_NAME_LENGTH 16
  54. static gbfile* fin, *fout;
  55. static int target_index, curr_index;
  56. static int track_info_flag;
  57. static short_handle sh;
  58. static int snlen;
  59. static int radius;
  60. static int input_datum;
  61. static route_head* curr_track;
  62. static route_head* curr_route;
  63. /* placeholders for options */
  64. static char* option_icon;
  65. static char* option_index;
  66. static char* option_radius;
  67. static char* option_snlen;
  68. static
  69. arglist_t compegps_args[] = {
  70. {
  71. "deficon", &option_icon, "Default icon name",
  72. NULL, ARGTYPE_STRING, ARG_NOMINMAX
  73. },
  74. {
  75. "index", &option_index, "Index of route/track to write (if more than one in source)",
  76. NULL, ARGTYPE_INT, "1", NULL
  77. },
  78. {
  79. "radius", &option_radius, "Give points (waypoints/route points) a default radius (proximity)",
  80. NULL, ARGTYPE_FLOAT, "0", NULL
  81. },
  82. {
  83. "snlen", &option_snlen, "Length of generated shortnames (default 16)",
  84. "16", ARGTYPE_INT, "1", NULL
  85. },
  86. ARG_TERMINATOR
  87. };
  88. static
  89. void fix_datum(double* lat, double* lon)
  90. {
  91. double amt;
  92. /*
  93. * Avoid FP jitter in the common case.
  94. */
  95. if (input_datum != DATUM_WGS84) {
  96. GPS_Math_Known_Datum_To_WGS84_M(*lat, *lon, 0.0, lat, lon,
  97. &amt, input_datum);
  98. }
  99. }
  100. static void
  101. compegps_parse_date(const char* c, struct tm* tm)
  102. {
  103. char month[4];
  104. int year;
  105. tm->tm_mday = atoi(c);
  106. strncpy(month, c+3, 3);
  107. month[3] = 0;
  108. tm->tm_mon = month_lookup(month);
  109. year = atoi(c + 7);
  110. if (year < 70) {
  111. year += 100;
  112. }
  113. if (year > 1900) {
  114. year -= 1900;
  115. }
  116. tm->tm_year = year;
  117. // if (tm->tm_year < 70) tm->tm_year += 100;
  118. }
  119. static void
  120. compegps_parse_time(const char* c, struct tm* tm)
  121. {
  122. tm->tm_hour = atoi(c);
  123. tm->tm_min = atoi(c+3);
  124. tm->tm_sec = atoi(c+6);
  125. }
  126. /* specialized readers */
  127. static Waypoint*
  128. parse_wpt(char* buff)
  129. {
  130. int col = -1;
  131. char* c, *cx;
  132. Waypoint* wpt = new Waypoint;
  133. struct tm tm;
  134. int has_time = 0;
  135. memset(&tm, 0, sizeof(tm));
  136. c = strstr(buff, "A ");
  137. if (c == buff) {
  138. col++;
  139. }
  140. c = csv_lineparse(buff, " ", "", col++);
  141. while (c != NULL) {
  142. c = lrtrim(c);
  143. if (*c != '\0') {
  144. #if 0
  145. printf(MYNAME "_read_wpt: col(%d)=%s\n", col, c);
  146. #endif
  147. switch (col) {
  148. case 0:
  149. cx = c + strlen(c) - 1; /* trim trailing underscores */
  150. while ((cx >= c) && (*cx == '_')) {
  151. *cx-- = '\0';
  152. }
  153. if (*c != '\0') {
  154. wpt->shortname = c;
  155. }
  156. break;
  157. case 2:
  158. human_to_dec(c, &wpt->latitude, NULL, 1);
  159. break;
  160. case 3:
  161. human_to_dec(c, NULL, &wpt->longitude, 2);
  162. break;
  163. // Older compegps used a dumb constant.
  164. // Report are that 2010-era writes a sensible
  165. // value here.
  166. /* always "27-MAR-62 00:00:00" */
  167. case 4:
  168. if (strcmp(c, "27-MAR-62")) {
  169. has_time = 1;
  170. compegps_parse_date(c, &tm);
  171. }
  172. break;
  173. case 5:
  174. if (has_time) {
  175. compegps_parse_time(c, &tm);
  176. wpt->SetCreationTime(mkgmtime(&tm));
  177. }
  178. break;
  179. case 6:
  180. wpt->altitude = atof(c);
  181. break;
  182. case 7:
  183. wpt->description = c;
  184. break;
  185. default:
  186. if (col > 7) {
  187. wpt->description += " ";
  188. wpt->description += c;
  189. }
  190. }
  191. }
  192. c = csv_lineparse(NULL, " ", "", col++);
  193. }
  194. fix_datum(&wpt->latitude, &wpt->longitude);
  195. return wpt;
  196. }
  197. static void
  198. parse_wpt_info(const char* buff, Waypoint* wpt) /* "w" */
  199. {
  200. char* c;
  201. int col = -1;
  202. double fx;
  203. c = csv_lineparse(buff, ",", "", col++);
  204. while (c != NULL) {
  205. c = lrtrim(c);
  206. if (*c != '\0') {
  207. #if 0
  208. printf(MYNAME "_read_wpt_info: col(%d)=%s\n", col, c);
  209. #endif
  210. switch (col) {
  211. case 0:
  212. wpt->icon_descr = c;
  213. break;
  214. case 1:
  215. break; /* Text postion */
  216. case 2:
  217. break; /* Lens zoom level */
  218. case 3:
  219. break; /* Text colour */
  220. case 4:
  221. break; /* Background colour */
  222. case 5:
  223. break; /* Transparent text  (0=transparent, 1=no transparent) */
  224. case 6:
  225. break; /* ??? */
  226. case 7:
  227. break; /* ??? */
  228. case 8: /* radius */
  229. fx = atof(c);
  230. if (fx > 0) {
  231. WAYPT_SET(wpt, proximity, fx);
  232. }
  233. break;
  234. }
  235. }
  236. c = csv_lineparse(NULL, ",", "", col++);
  237. }
  238. }
  239. static Waypoint*
  240. parse_trkpt(char* buff)
  241. {
  242. int col = -1;
  243. char* c;
  244. struct tm tm;
  245. Waypoint* wpt = new Waypoint;
  246. c = strstr(buff, "A ");
  247. if (c == buff) {
  248. col++;
  249. }
  250. memset(&tm, 0, sizeof(tm));
  251. c = csv_lineparse(buff, " ", "", col++);
  252. while (c != NULL) {
  253. c = lrtrim(c);
  254. if (*c != '\0') {
  255. #if 0
  256. printf(MYNAME "_read_trkpt: col(%d)=%s\n", col, c);
  257. #endif
  258. switch (col) {
  259. case 2:
  260. human_to_dec(c, &wpt->latitude, NULL, 1);
  261. break;
  262. case 3:
  263. human_to_dec(c, NULL, &wpt->longitude, 2);
  264. break;
  265. case 4:
  266. compegps_parse_date(c, &tm);
  267. break;
  268. case 5:
  269. compegps_parse_time(c, &tm);
  270. wpt->SetCreationTime(mkgmtime(&tm));
  271. break;
  272. case 7:
  273. wpt->altitude = atof(c);
  274. break;
  275. }
  276. }
  277. c = csv_lineparse(NULL, " ", "", col++);
  278. }
  279. fix_datum(&wpt->latitude, &wpt->longitude);
  280. return wpt;
  281. }
  282. static void
  283. parse_track_info(const char* buff, route_head* track) /* "t" */
  284. {
  285. char* c;
  286. int col = -1;
  287. c = csv_lineparse(buff, "|", "", col++);
  288. while (c != NULL) {
  289. c = lrtrim(c);
  290. if (*c != '\0') {
  291. #if 0
  292. printf(MYNAME "_read_track_info: col(%d)=%s\n", col, c);
  293. #endif
  294. switch (col) {
  295. case 0:
  296. break; /* unknown field */
  297. case 1:
  298. track->rte_name = c;
  299. break;
  300. case 2:
  301. break; /* unknown field */
  302. case 3:
  303. break; /* unknown field */
  304. }
  305. }
  306. c = csv_lineparse(NULL, "|", "", col++);
  307. }
  308. }
  309. static void
  310. parse_rte_info(const char* buff, route_head* route) /* "R" */
  311. {
  312. char* c;
  313. int col = -1;
  314. c = csv_lineparse(buff, ",", "", col++);
  315. while (c != NULL) {
  316. c = lrtrim(c);
  317. if (*c != '\0') {
  318. #if 0
  319. printf(MYNAME "_read_rte_info: col(%d)=%s\n", col, c);
  320. #endif
  321. switch (col) {
  322. case 0:
  323. break; /* unknown field (colour?) */
  324. case 1:
  325. route->rte_name = c;
  326. break;
  327. case 2:
  328. break; /* unknown field */
  329. }
  330. }
  331. c = csv_lineparse(NULL, ",", "", col++);
  332. }
  333. }
  334. /* main functions */
  335. static void
  336. compegps_rd_init(const QString& fname)
  337. {
  338. fin = gbfopen(fname, "rb", MYNAME);
  339. input_datum = DATUM_WGS84;
  340. }
  341. static void
  342. compegps_rd_deinit(void)
  343. {
  344. gbfclose(fin);
  345. }
  346. static void
  347. compegps_data_read(void)
  348. {
  349. char* buff;
  350. int line = 0;
  351. int input_datum;
  352. Waypoint* wpt = NULL;
  353. route_head* route = NULL;
  354. route_head* track = NULL;
  355. while ((buff = gbfgetstr(fin))) {
  356. char* cin = buff;
  357. char* ctail;
  358. if ((line++ == 0) && fin->unicode) {
  359. cet_convert_init(CET_CHARSET_UTF8, 1);
  360. }
  361. cin = lrtrim(buff);
  362. if (strlen(cin) == 0) {
  363. continue;
  364. }
  365. ctail = strchr(cin, ' ');
  366. if (ctail == NULL) {
  367. continue;
  368. }
  369. ctail = lrtrim(ctail);
  370. switch (*cin) {
  371. case 'G':
  372. input_datum = GPS_Lookup_Datum_Index(ctail);
  373. if (input_datum < 0) {
  374. fatal(MYNAME ": Unsupported datum \"%s\"!", ctail);
  375. }
  376. break;
  377. case 'U':
  378. switch (*ctail) {
  379. case '1': /* lat/lon, that's we want to see */
  380. break;
  381. case '0': /* UTM not supported yet */
  382. fatal(MYNAME "Sorry, UTM is not supported yet!\n");
  383. default:
  384. fatal(MYNAME "Invalid system of coordinates (%s)!\n", cin);
  385. }
  386. break;
  387. case 'R':
  388. route = route_head_alloc();
  389. route_add_head(route);
  390. parse_rte_info(ctail, route);
  391. break;
  392. case 'M':
  393. break;
  394. case 'W':
  395. wpt = parse_wpt(ctail);
  396. if (wpt != NULL) {
  397. if (route != NULL) {
  398. route_add_wpt(route, wpt);
  399. } else {
  400. waypt_add(wpt);
  401. }
  402. }
  403. break;
  404. case 'w':
  405. is_fatal((wpt == NULL), MYNAME ": No waypoint data before \"%s\"!", cin);
  406. parse_wpt_info(ctail, wpt);
  407. break;
  408. case 'T':
  409. wpt = parse_trkpt(ctail);
  410. if (wpt != NULL) {
  411. if (track == NULL) {
  412. track = route_head_alloc();
  413. track_add_head(track);
  414. }
  415. track_add_wpt(track, wpt);
  416. }
  417. break;
  418. case 't':
  419. if (track != NULL) {
  420. parse_track_info(ctail, track);
  421. }
  422. break;
  423. }
  424. }
  425. }
  426. /* ----------------------------------------------------------- */
  427. static void
  428. write_waypt_cb(const Waypoint* wpt)
  429. {
  430. QString name;
  431. if (curr_index != target_index) {
  432. return;
  433. }
  434. // Our only output cleansing is to replace
  435. QString cleaned_name(wpt->shortname);
  436. cleaned_name.replace(' ', '_');
  437. name = (snlen > 0) ? mkshort(sh, cleaned_name) : cleaned_name;
  438. gbfprintf(fout, "W %s A ", CSTR(name));
  439. gbfprintf(fout, "%.10f%c%c ",
  440. fabs(wpt->latitude), 0xBA, (wpt->latitude >= 0) ? 'N' : 'S');
  441. gbfprintf(fout, "%.10f%c%c ",
  442. fabs(wpt->longitude), 0xBA, (wpt->longitude >= 0) ? 'E' : 'W');
  443. gbfprintf(fout, "27-MAR-62 00:00:00 %.6f",
  444. (wpt->altitude != unknown_alt) ? wpt->altitude : 0.0);
  445. if (wpt->description != NULL) {
  446. gbfprintf(fout, " %s", CSTRc(wpt->description));
  447. }
  448. gbfprintf(fout, "\n");
  449. if ((!wpt->icon_descr.isNull()) || (wpt->wpt_flags.proximity) || \
  450. (option_icon != NULL)) {
  451. gbfprintf(fout, "w %s,0,0.0,16777215,255,1,7,,%.1f\n",
  452. wpt->icon_descr.isNull() ? "Waypoint" : CSTR(wpt->icon_descr),
  453. WAYPT_GET(wpt, proximity, 0));
  454. }
  455. }
  456. static void
  457. write_route_hdr_cb(const route_head* rte)
  458. {
  459. curr_route = (route_head*) rte;
  460. curr_index++;
  461. if (curr_index != target_index) {
  462. return;
  463. }
  464. QString name = rte->rte_name;
  465. if (name != NULL) {
  466. name = csv_stringclean(name, ",");
  467. } else {
  468. name = " ";
  469. }
  470. gbfprintf(fout, "R 16711680,%s,1,-1\n", CSTR(name));
  471. }
  472. static void
  473. write_route(void)
  474. {
  475. curr_index = 0;
  476. route_disp_all(write_route_hdr_cb, NULL, write_waypt_cb);
  477. }
  478. static void
  479. write_track_hdr_cb(const route_head* trk)
  480. {
  481. track_info_flag = 0;
  482. curr_track = (route_head*) trk;
  483. curr_index++;
  484. if (curr_index != target_index) {
  485. return;
  486. }
  487. track_info_flag = 1;
  488. }
  489. static void
  490. write_trkpt_cb(const Waypoint* wpt)
  491. {
  492. char buff[128];
  493. if ((curr_index != target_index) || (wpt == NULL)) {
  494. return;
  495. }
  496. buff[0] = '\0';
  497. // TOOD: This should probably attempt a gmtime and then fall back to the 1-1-1970
  498. // case or bypass the time_t completely and build string representations directly.
  499. if (wpt->creation_time.isValid()) {
  500. const time_t tt = wpt->GetCreationTime().toTime_t();
  501. struct tm tm = *gmtime(&tt);
  502. strftime(buff, sizeof(buff), "%d-%b-%y %H:%M:%S", &tm);
  503. strupper(buff);
  504. } else {
  505. strncpy(buff, "01-JAN-70 00:00:00", sizeof(buff));
  506. }
  507. gbfprintf(fout, "T A %.10f%c%c %.10f%c%c ",
  508. fabs(wpt->latitude), 0xBA, (wpt->latitude >= 0) ? 'N' : 'S',
  509. fabs(wpt->longitude), 0xBA, (wpt->longitude >= 0) ? 'E' : 'W');
  510. gbfprintf(fout, "%s s %.1f %.1f %.1f %.1f %d ",
  511. buff,
  512. wpt->altitude,
  513. 0.0,
  514. 0.0,
  515. 0.0,
  516. 0);
  517. gbfprintf(fout, "%.1f %.1f %.1f %.1f %.1f\n",
  518. -1000.0,
  519. -1.0,
  520. -1.0,
  521. -1.0,
  522. -1.0);
  523. if (track_info_flag != 0) {
  524. track_info_flag = 0;
  525. if (curr_track->rte_name != NULL) {
  526. QString name = csv_stringclean(curr_track->rte_name, "|");
  527. gbfprintf(fout, "t 4294967295|%s|-1|-1\n", CSTR(name));
  528. }
  529. }
  530. }
  531. static void
  532. write_track(void)
  533. {
  534. curr_index = 0;
  535. // gbfprintf(fout, "L +02:00:00\n");
  536. track_disp_all(write_track_hdr_cb, NULL, write_trkpt_cb);
  537. gbfprintf(fout, "F 1234\n");
  538. }
  539. static void
  540. write_waypoints(void)
  541. {
  542. waypt_disp_all(write_waypt_cb);
  543. }
  544. /* --------------------------------------------------------------------------- */
  545. static void
  546. compegps_wr_init(const QString& fname)
  547. {
  548. fout = gbfopen(fname, "w", MYNAME);
  549. sh = mkshort_new_handle();
  550. }
  551. static void
  552. compegps_wr_deinit(void)
  553. {
  554. mkshort_del_handle(&sh);
  555. gbfclose(fout);
  556. }
  557. static void
  558. compegps_data_write(void)
  559. {
  560. /* because of different file extensions we can only write one GPS data type at time */
  561. gbfprintf(fout, "G WGS 84\n");
  562. gbfprintf(fout, "U 1\n");
  563. /* process options */
  564. target_index = 1;
  565. if (option_index != NULL) {
  566. target_index = atoi(option_index);
  567. }
  568. snlen = 0;
  569. if (global_opts.synthesize_shortnames != 0) {
  570. if (option_snlen != NULL) {
  571. snlen = atoi(option_snlen);
  572. } else {
  573. snlen = SHORT_NAME_LENGTH;
  574. }
  575. is_fatal((snlen < 1), MYNAME "Invalid length for generated shortnames!");
  576. setshort_whitespace_ok(sh, 0);
  577. setshort_length(sh, snlen);
  578. }
  579. radius = -1;
  580. if (option_radius != 0) {
  581. radius = atof(option_radius);
  582. is_fatal((radius <= 0.0), MYNAME "Invalid value for radius!");
  583. }
  584. if (option_icon != NULL) {
  585. if (*option_icon == '\0') {
  586. option_icon = NULL;
  587. } else if (case_ignore_strcmp(option_icon, "deficon") == 0) {
  588. option_icon = NULL;
  589. }
  590. }
  591. switch (global_opts.objective) {
  592. case wptdata:
  593. case unknown_gpsdata:
  594. curr_index = target_index = 0;
  595. write_waypoints();
  596. break;
  597. case trkdata:
  598. write_track();
  599. break;
  600. case rtedata:
  601. write_route();
  602. break;
  603. case posndata:
  604. fatal(MYNAME ": Realtime positioning not supported.\n");
  605. break;
  606. }
  607. }
  608. /* --------------------------------------------------------------------------- */
  609. ff_vecs_t compegps_vecs = {
  610. ff_type_file,
  611. FF_CAP_RW_ALL,
  612. compegps_rd_init,
  613. compegps_wr_init,
  614. compegps_rd_deinit,
  615. compegps_wr_deinit,
  616. compegps_data_read,
  617. compegps_data_write,
  618. NULL,
  619. compegps_args,
  620. CET_CHARSET_MS_ANSI, 1
  621. };
  622. #endif /* CSVFMTS_ENABLED */