1 /*
2
3 Support for G7ToWin data files (.g7t),
4 Copyright (C) 2007 Olaf Klein, o.b.klein@gpsbabel.org
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
19 */
20
21 /*
22 History:
23 04/07/2007: start programming
24 04/15/2007: added to gpsbabel
25 */
26
27 #include "defs.h"
28 #include "csv_util.h"
29 #include "garmin_fs.h"
30 #include "garmin_tables.h"
31 #include "jeeps/gpsmath.h"
32 #include "strptime.h"
33
34 #include <time.h>
35
36 #if CSVFMTS_ENABLED
37
38 #define MYNAME "g7towin"
39
40 #define G7T_HEADER "Version 2:G7T"
41
42 static gbfile* fin;
43 static grid_type grid;
44 static int datum;
45 static gpsdata_type mode;
46 static double altf;
47 static int gardown;
48 static int event_ct;
49
50 static
51 arglist_t g7towin_args[] = {
52 ARG_TERMINATOR
53 };
54
55 #define WAYPT__OFS 0x00000
56 #define TRKPT__OFS 0x01000
57
58 #define WPT_c0_OFS 0x0c000
59 #define WPT_c1_OFS 0x0c100
60 #define WPT_c2_OFS 0x0c200
61 #define WPT_c3_OFS 0x0c300
62 #define WPT_c4_OFS 0x0c400
63 #define WPT_c5_OFS 0x0c500
64 #define WPT_c6_OFS 0x0c600
65 #define WPT_c7_OFS 0x0c700
66 #define WPT_c8_OFS 0x0c800
67 #define WPT_cA_OFS 0x0cA00
68 #define WPT_cB_OFS 0x0cB00
69 #define WPT_cC_OFS 0x0cC00
70 #define WPT_cD_OFS 0x0cD00
71
72 static void
parse_line(char * buff,int index,const char * delimiter,waypoint * wpt)73 parse_line(char* buff, int index, const char* delimiter, waypoint* wpt)
74 {
75 char* cin;
76 garmin_fs_p gmsd = GMSD_FIND(wpt);
77
78 while ((cin = csv_lineparse(buff, delimiter, "", index++))) {
79
80 buff = NULL;
81 cin = lrtrim(cin);
82
83 if ((*cin == '\0') ||
84 (strcmp(cin, "INF") == 0) ||
85 (strcmp(cin, "1e25") == 0) ||
86 (strcmp(cin, "1.0e25") == 0)) {
87 continue;
88 }
89
90 switch (index) {
91
92 int categories, dyn;
93 struct tm tm;
94 char* cerr;
95
96 case TRKPT__OFS + 1:
97 cin += parse_coordinates(cin, datum, grid,
98 &wpt->latitude, &wpt->longitude, MYNAME);
99 while (isspace(*cin)) {
100 cin++;
101 }
102
103 memset(&tm, 0, sizeof(tm));
104 cerr = strptime(cin, "%a %b %d %H:%M:%S %Y", &tm);
105 if (cerr == NULL) {
106 fatal(MYNAME ": Unable to convert date (%s)!\n", cin);
107 }
108 wpt->creation_time = mkgmtime(&tm);
109 break;
110
111 case WAYPT__OFS + 1:
112 wpt->description = xstrdup(cin);
113 break;
114
115 case WAYPT__OFS + 2:
116 wpt->icon_descr = gt_find_desc_from_icon_number(
117 atoi(cin), PCX, &dyn);
118 wpt->wpt_flags.icon_descr_is_dynamic = dyn;
119 break;
120
121 case WAYPT__OFS + 4:
122 if (strcmp(cin, "S+C") == 0) {
123 GMSD_SET(display, gt_display_mode_symbol_and_comment);
124 } else if (strcmp(cin, "S") == 0) {
125 GMSD_SET(display, gt_display_mode_symbol);
126 } else if (strcmp(cin, "S+N") == 0) {
127 GMSD_SET(display, gt_display_mode_symbol_and_name);
128 }
129 break;
130
131 case WPT_cA_OFS + 1:
132 case WPT_c1_OFS + 1:
133 if (wpt->shortname) {
134 xfree(wpt->shortname);
135 }
136 wpt->shortname = xstrdup(cin);
137 break;
138
139 case WPT_cA_OFS + 4:
140 case WPT_c4_OFS + 2:
141 GMSD_SETSTR(city, cin);
142 break;
143
144 case WPT_cA_OFS + 5:
145 case WPT_c4_OFS + 3:
146 GMSD_SETSTR(state, cin);
147 break;
148
149 case WPT_cA_OFS + 6:
150 case WPT_c4_OFS + 4:
151 GMSD_SETSTR(cc, cin);
152 break;
153
154 case WPT_cB_OFS + 1:
155 case WPT_c6_OFS + 2:
156 GMSD_SETSTR(facility, cin);
157 break;
158
159 case WPT_cB_OFS + 2:
160 case WPT_c6_OFS + 3:
161 GMSD_SETSTR(addr, cin);
162 break;
163
164 case WPT_cB_OFS + 3: /*cross road */
165 case WPT_c6_OFS + 4:
166 GMSD_SETSTR(cross_road, cin);
167 break;
168
169 case TRKPT__OFS + 2: /* altitude */
170 case WPT_cC_OFS + 1:
171 case WPT_c5_OFS + 1:
172 case WPT_c8_OFS + 1:
173 wpt->altitude = altf * atof(cin);
174 break;
175
176 case TRKPT__OFS + 3: /* depth */
177 case WPT_cC_OFS + 2:
178 case WPT_c5_OFS + 2:
179 case WPT_c8_OFS + 2:
180 WAYPT_SET(wpt, depth, altf * atof(cin));
181 break;
182
183 case TRKPT__OFS + 10: /* temperature */
184 if (*cin == '|') {
185 cin++; /* in track points */
186 }
187 if (strcmp(cin, "1e25") == 0) {
188 break;
189 }
190 if (strcmp(cin, "1.0e25") == 0) {
191 break;
192 }
193 /* !!! NO BREAK !!! */
194 case WPT_cD_OFS + 1:
195 case WPT_cB_OFS + 6:
196 WAYPT_SET(wpt, temperature, atof(cin));
197 break;
198
199 case WAYPT__OFS + 6: /* proximity */
200 case WPT_cD_OFS + 2:
201 WAYPT_SET(wpt, proximity, atof(cin));
202 break;
203
204 case WPT_cB_OFS + 5:
205 case WPT_cD_OFS + 3:
206 categories = atoi(cin);
207 if (categories != 0) {
208 GMSD_SET(category, atoi(cin));
209 }
210 break;
211
212 #if 0
213
214 /* currently unused */
215
216 case TRKPT__OFS + 5: /* distance from previous point */
217 case TRKPT__OFS + 6: /* distance from segment start */
218 case TRKPT__OFS + 7: /* distance from start */
219 case TRKPT__OFS + 8: /* velocity from previous point */
220 case TRKPT__OFS + 9: /* time (in seconds) from previous point */
221 break;
222
223 case WAYPT__OFS + 3: /* ignore color */
224 break;
225
226 case WAYPT__OFS + 5: /* always '0' */
227 break;
228
229 case TRKPT__OFS + 4:
230 if (case_ignore_strcmp(cin, "FT") == 0) ;
231 else if (case_ignore_strcmp(cin, "M") == 0) ;
232 else if (case_ignore_strcmp(cin, "SM") == 0) ;
233 else if (case_ignore_strcmp(cin, "NM") == 0) ;
234 else if (case_ignore_strcmp(cin, "KM") == 0) ;
235 break;
236
237 case WPT_cB_OFS + 4: /* unknown (datatype) */
238 break;
239
240 case WPT_cC_OFS + 3: /* waypt_class (always FF) */
241 break;
242
243 case WPT_cC_OFS + 4: /* class & subclass */
244 case WPT_cC_OFS + 5:
245 case WPT_cC_OFS + 6:
246 case WPT_cC_OFS + 7:
247 case WPT_cC_OFS + 8:
248 case WPT_cC_OFS + 9:
249 case WPT_cC_OFS + 10:
250 case WPT_cC_OFS + 11:
251 case WPT_cC_OFS + 12:
252 case WPT_cC_OFS + 13:
253 case WPT_cC_OFS + 14:
254 case WPT_cC_OFS + 15:
255 case WPT_cC_OFS + 16:
256 case WPT_cC_OFS + 17:
257 case WPT_cC_OFS + 18:
258 case WPT_cC_OFS + 19:
259 case WPT_cC_OFS + 20:
260 case WPT_cC_OFS + 21:
261 break;
262
263 case WPT_cC_OFS + 22:
264 /* distance */
265 break;
266 #endif
267 }
268 }
269 }
270
271 static waypoint*
parse_waypt(char * buff)272 parse_waypt(char* buff)
273 {
274 char* cin, *cerr;
275 int i;
276 struct tm tm;
277 waypoint* wpt;
278 garmin_fs_p gmsd;
279
280 wpt = waypt_new();
281 gmsd = garmin_fs_alloc(-1);
282 fs_chain_add(&wpt->fs, (format_specific_data*) gmsd);
283
284 if (gardown) {
285 cin = buff + 6;
286 } else {
287 /* We've seen waypoints with length of 14 and 15 !!! */
288 cin = buff + 15;
289 while ((cin > buff) && (! isspace(*cin))) {
290 cin--;
291 }
292 }
293
294 while (isspace(*cin)) {
295 cin--;
296 }
297 if (cin >= buff) {
298 wpt->shortname = xstrndup(buff, cin - buff + 1);
299 }
300
301 if (gardown) {
302 buff += 6;
303 } else {
304 buff += 15;
305 }
306 while (isspace(*buff)) {
307 buff++;
308 }
309
310 buff += parse_coordinates(buff, datum, grid,
311 &wpt->latitude, &wpt->longitude, MYNAME);
312 while (isspace(*buff)) {
313 buff++;
314 }
315
316 memset(&tm, 0, sizeof(tm));
317 cerr = strptime(buff, "%a %b %d %H:%M:%S %Y", &tm);
318 if (cerr == NULL) {
319 fatal(MYNAME ": Unable to convert date (%s)!\n", buff);
320 }
321 wpt->creation_time = mkgmtime(&tm);
322
323 /* go over time stamp */
324 i = 5;
325 while (buff && i) {
326 i--;
327 buff = strchr(buff, ' ');
328 if (buff) {
329 buff++;
330 }
331 }
332 if (gardown && (buff == NULL)) {
333 return wpt;
334 }
335 is_fatal((buff == NULL), MYNAME ": Incomplete waypoint line!");
336
337 while (isspace(*buff)) {
338 buff++;
339 }
340
341 parse_line(buff, WAYPT__OFS, "^", wpt);
342
343 return wpt;
344 }
345
346 static waypoint*
parse_trkpt(char * buff)347 parse_trkpt(char* buff)
348 {
349 garmin_fs_p gmsd;
350 waypoint* wpt;
351
352 wpt = waypt_new();
353 gmsd = garmin_fs_alloc(-1);
354 fs_chain_add(&wpt->fs, (format_specific_data*) gmsd);
355
356 parse_line(buff, TRKPT__OFS, ";", wpt);
357
358 return wpt;
359 }
360
361 /*
362 * parse_categories is currently only a dummy procedure.
363 * w'll need a central storage with binding to the module
364 * which has established a list of category names.
365 */
366
367 static void
parse_categories(char * buff)368 parse_categories(char* buff)
369 {
370 char* cin;
371 int cat = 0;
372
373 while ((cin = csv_lineparse(buff, ",", "", cat++))) {
374 gbuint16 cx;
375
376 buff = NULL;
377
378 cin = lrtrim(cin);
379 if (*cin == 0) {
380 continue;
381 }
382
383 garmin_fs_convert_category(cin, &cx);
384 }
385 }
386
387
388 /* main functions */
389
390 static void
rd_init(const char * fname)391 rd_init(const char* fname)
392 {
393 fin = gbfopen(fname, "rb", MYNAME);
394
395 gardown = 1;
396 mode = wptdata;
397 grid = grid_lat_lon_dmm;
398 datum = DATUM_WGS84;
399 altf = 1;
400 event_ct = 0;
401 }
402
403 static void
rd_deinit(void)404 rd_deinit(void)
405 {
406 gbfclose(fin);
407 }
408
409 static void
data_read(void)410 data_read(void)
411 {
412 char* buff;
413 int line = 0;
414 waypoint* wpt = NULL;
415 waypoint* prev = NULL;
416 route_head* head = NULL;
417
418 while ((buff = gbfgetstr(fin))) {
419 char* cin = buff;
420 char* cdata;
421
422 if ((line++ == 0) && fin->unicode) {
423 cet_convert_init(CET_CHARSET_UTF8, 1);
424 }
425
426 cin = lrtrim(buff);
427 if (!*cin) {
428 continue;
429 }
430
431 cdata = cin+1;
432 while (! isspace(*cdata)) {
433 cdata++;
434 }
435 while (isspace(*cdata)) {
436 cdata++;
437 }
438 if (! *cdata) {
439 continue;
440 }
441
442 switch (*cin) {
443
444 case '#': /* comment */
445 break;
446
447 case 'A':
448 if (case_ignore_strncmp(cdata, "Meter", 5) == 0) {
449 altf = 1.0;
450 } else if (case_ignore_strncmp(cdata, "Feet", 4) == 0) {
451 altf = FEET_TO_METERS(1.0);
452 }
453 break;
454
455 case 'C': /* categories */
456 parse_categories(cdata);
457 break;
458
459 case 'D':
460 datum = gt_lookup_datum_index(cdata, MYNAME);
461 break;
462
463 case 'I': /* event point */
464 wpt = waypt_new();
465 cdata += parse_coordinates(cdata, datum, grid,
466 &wpt->latitude, &wpt->longitude, MYNAME);
467 xasprintf(&wpt->shortname, "Event%d", ++event_ct);
468 while (isspace(*cdata)) {
469 cdata++;
470 }
471 if (*cdata == ';') {
472 int dyn;
473
474 cdata++;
475 wpt->icon_descr = gt_find_desc_from_icon_number(
476 atoi(cdata), PCX, &dyn);
477 wpt->wpt_flags.icon_descr_is_dynamic = dyn;
478 }
479 waypt_add(wpt);
480 break;
481
482 case 'M':
483 grid = gt_lookup_grid_type(cdata, MYNAME);
484 break;
485
486 case 'P': /* proximity waypoint */
487 case 'W': /* normal waypoint */
488 wpt = parse_waypt(cin + 3);
489 prev = wpt;
490 if (wpt) {
491 if (mode == rtedata) {
492 route_add_wpt(head, wpt);
493 } else {
494 waypt_add(wpt);
495 }
496 }
497 break;
498
499 case 'c': /* additional lines */
500 switch (*(cin+1)) {
501 int index;
502
503 case 'A':
504 case 'B':
505 case 'C':
506 case 'D':
507
508 index = WPT_cA_OFS + ((*(cin+1) - 'A') * 256);
509 parse_line(cdata, index, "|", wpt);
510 break;
511
512 case '1':
513 case '2':
514 case '3':
515 case '4':
516 case '5':
517 case '6':
518 case '7':
519 case '8':
520
521 index = WPT_c0_OFS + ((*(cin+1) - '0') * 256);
522 parse_line(cdata, index, ";", wpt);
523 break;
524
525 case 'L':
526 waypt_add_url(wpt, xstrdup(cdata), NULL);
527 break;
528
529 default:
530 break;
531 }
532 break;
533
534 case 'N': /* track log header */
535 mode = trkdata;
536 head = route_head_alloc();
537 cdata = strchr(cdata, '-');
538 if (cdata) {
539 while (isspace(*cdata)) {
540 cdata++;
541 }
542 if (*cdata) {
543 char* s;
544 s = strrchr(cdata, ',');
545 if (s) {
546 *s = '\0';
547 s = strrchr(cdata, ',');
548 if (s) {
549 *s = '\0';
550 head->rte_name = xstrdup(cdata);
551 }
552 }
553 }
554 }
555 track_add_head(head);
556 break;
557
558 case 'R': /* route header */
559 mode = rtedata;
560 head = route_head_alloc();
561 cdata += 3; /*skip route number */
562 if (*cdata) {
563 head->rte_name = xstrdup(cdata);
564 }
565 route_add_head(head);
566 break;
567
568 case 'T':
569 wpt = parse_trkpt(cdata);
570 if (wpt) {
571 track_add_wpt(head, wpt);
572 }
573 break;
574
575 case 'V':
576 if (strcmp(cin, G7T_HEADER) != 0) {
577 fatal(MYNAME ": Invalid version or invalid file!\n");
578 }
579 gardown = 0;
580 break;
581
582 default:
583 break;
584 }
585 }
586 }
587
588 /* --------------------------------------------------------------------------- */
589
590 ff_vecs_t g7towin_vecs = {
591 ff_type_file,
592 { ff_cap_read, ff_cap_read, ff_cap_read },
593 rd_init,
594 NULL,
595 rd_deinit,
596 NULL,
597 data_read,
598 NULL,
599 NULL,
600 g7towin_args,
601 CET_CHARSET_MS_ANSI, 0
602 };
603
604 #endif /* CSVFMTS_ENABLED */
605