Contents of /trunk/src/track.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 192 - (show annotations)
Tue Jul 7 11:07:11 2009 UTC (14 years, 10 months ago) by harbaum
File MIME type: text/plain
File size: 15816 byte(s)
Fremantle widget sensitivity cleanups
1 /*
2 * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3 *
4 * This file is part of OSM2Go.
5 *
6 * OSM2Go 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 3 of the License, or
9 * (at your option) any later version.
10 *
11 * OSM2Go 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 OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <libxml/parser.h>
21 #include <libxml/tree.h>
22
23 #define __USE_XOPEN
24 #include <time.h>
25
26 #include "appdata.h"
27
28 #ifndef LIBXML_TREE_ENABLED
29 #error "Tree not enabled in libxml"
30 #endif
31
32 // predecs
33 static void track_do_enable_gps(appdata_t *appdata);
34 static void track_do_disable_gps(appdata_t *appdata);
35
36 /* make menu represent the track state */
37 static void track_menu_set(appdata_t *appdata, gboolean present) {
38 if(!appdata->window) return;
39
40 /* if a track is present, then it can be cleared or exported */
41 gtk_widget_set_sensitive(appdata->track.menu_item_track_clear, present);
42 gtk_widget_set_sensitive(appdata->track.menu_item_track_export, present);
43 }
44
45 gint track_seg_points(track_seg_t *seg) {
46 gint points = 0;
47
48 track_point_t *point = seg->track_point;
49 while(point) {
50 points++;
51 point = point->next;
52 }
53 return points;
54 }
55
56 static gboolean track_get_prop_pos(xmlNode *node, pos_t *pos) {
57 char *str_lat = (char*)xmlGetProp(node, BAD_CAST "lat");
58 char *str_lon = (char*)xmlGetProp(node, BAD_CAST "lon");
59
60 if(!str_lon || !str_lat) {
61 if(!str_lon) xmlFree(str_lon);
62 if(!str_lat) xmlFree(str_lat);
63 return FALSE;
64 }
65
66 pos->lat = g_ascii_strtod(str_lat, NULL);
67 pos->lon = g_ascii_strtod(str_lon, NULL);
68
69 xmlFree(str_lon);
70 xmlFree(str_lat);
71
72 return TRUE;
73 }
74
75 static track_point_t *track_parse_trkpt(bounds_t *bounds, xmlDocPtr doc,
76 xmlNode *a_node) {
77 track_point_t *point = g_new0(track_point_t, 1);
78 point->altitude = NAN;
79
80 /* parse position */
81 if(!track_get_prop_pos(a_node, &point->pos)) {
82 g_free(point);
83 return NULL;
84 }
85
86 /* scan for children */
87 xmlNode *cur_node = NULL;
88 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
89 if (cur_node->type == XML_ELEMENT_NODE) {
90
91 /* elevation (altitude) */
92 if(strcasecmp((char*)cur_node->name, "ele") == 0) {
93 char *str = (char*)xmlNodeGetContent(cur_node);
94 point->altitude = g_ascii_strtod(str, NULL);
95 xmlFree(str);
96 }
97
98 /* time */
99 if(strcasecmp((char*)cur_node->name, "time") == 0) {
100 struct tm time;
101 char *str = (char*)xmlNodeGetContent(cur_node);
102 char *ptr = strptime(str, DATE_FORMAT, &time);
103 if(ptr) point->time = mktime(&time);
104 xmlFree(str);
105 }
106 }
107 }
108
109 return point;
110 }
111
112 static void track_parse_trkseg(track_t *track, bounds_t *bounds,
113 xmlDocPtr doc, xmlNode *a_node) {
114 xmlNode *cur_node = NULL;
115 track_point_t **point = NULL;
116 track_seg_t **seg = &(track->track_seg);
117
118 /* search end of track_seg list */
119 while(*seg) seg = &((*seg)->next);
120
121 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
122 if (cur_node->type == XML_ELEMENT_NODE) {
123 if(strcasecmp((char*)cur_node->name, "trkpt") == 0) {
124 track_point_t *cpnt = track_parse_trkpt(bounds, doc, cur_node);
125 if(cpnt) {
126 if(!point) {
127 /* start a new segment */
128 *seg = g_new0(track_seg_t, 1);
129 point = &((*seg)->track_point);
130 }
131 /* attach point to chain */
132 *point = cpnt;
133 point = &((*point)->next);
134 } else {
135 /* end segment if point could not be parsed and start a new one */
136 /* close segment if there is one */
137 if(point) {
138 printf("ending track segment leaving bounds\n");
139 seg = &((*seg)->next);
140 point = NULL;
141 }
142 }
143 } else
144 printf("found unhandled gpx/trk/trkseg/%s\n", cur_node->name);
145
146 }
147 }
148 }
149
150 static track_t *track_parse_trk(bounds_t *bounds,
151 xmlDocPtr doc, xmlNode *a_node) {
152 track_t *track = g_new0(track_t, 1);
153 xmlNode *cur_node = NULL;
154
155 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
156 if (cur_node->type == XML_ELEMENT_NODE) {
157 if(strcasecmp((char*)cur_node->name, "trkseg") == 0) {
158 track_parse_trkseg(track, bounds, doc, cur_node);
159 } else
160 printf("found unhandled gpx/trk/%s\n", cur_node->name);
161
162 }
163 }
164 return track;
165 }
166
167 static track_t *track_parse_gpx(bounds_t *bounds,
168 xmlDocPtr doc, xmlNode *a_node) {
169 track_t *track = NULL;
170 xmlNode *cur_node = NULL;
171
172 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
173 if (cur_node->type == XML_ELEMENT_NODE) {
174 if(strcasecmp((char*)cur_node->name, "trk") == 0) {
175 if(!track)
176 track = track_parse_trk(bounds, doc, cur_node);
177 else
178 printf("ignoring additional track\n");
179 } else
180 printf("found unhandled gpx/%s\n", cur_node->name);
181 }
182 }
183 return track;
184 }
185
186 /* parse root element and search for "track" */
187 static track_t *track_parse_root(bounds_t *bounds,
188 xmlDocPtr doc, xmlNode *a_node) {
189 track_t *track = NULL;
190 xmlNode *cur_node = NULL;
191
192 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
193 if (cur_node->type == XML_ELEMENT_NODE) {
194 /* parse track file ... */
195 if(strcasecmp((char*)cur_node->name, "gpx") == 0)
196 track = track_parse_gpx(bounds, doc, cur_node);
197 else
198 printf("found unhandled %s\n", cur_node->name);
199 }
200 }
201 return track;
202 }
203
204 static track_t *track_parse_doc(bounds_t *bounds, xmlDocPtr doc) {
205 track_t *track;
206
207 /* Get the root element node */
208 xmlNode *root_element = xmlDocGetRootElement(doc);
209
210 track = track_parse_root(bounds, doc, root_element);
211
212 /*free the document */
213 xmlFreeDoc(doc);
214
215 /*
216 * Free the global variables that may
217 * have been allocated by the parser.
218 */
219 xmlCleanupParser();
220
221 return track;
222 }
223
224 void track_info(track_t *track) {
225 printf("Track is %sdirty.\n", track->dirty?"":"not ");
226
227 gint segs = 0, points = 0;
228 track_seg_t *seg = track->track_seg;
229 while(seg) {
230 points += track_seg_points(seg);
231 segs++;
232 seg = seg->next;
233 }
234
235 printf("%d points in %d segments\n", points, segs);
236
237 }
238
239 static track_t *track_read(osm_t *osm, char *filename) {
240 printf("============================================================\n");
241 printf("loading track %s\n", filename);
242
243 xmlDoc *doc = NULL;
244
245 LIBXML_TEST_VERSION;
246
247 /* parse the file and get the DOM */
248 if((doc = xmlReadFile(filename, NULL, 0)) == NULL) {
249 xmlErrorPtr errP = xmlGetLastError();
250 errorf(NULL, "While parsing \"%s\":\n\n%s", filename, errP->message);
251 return NULL;
252 }
253
254 track_t *track = track_parse_doc(osm->bounds, doc);
255
256 if(!track || !track->track_seg) {
257 printf("track was empty/invalid track\n");
258 return NULL;
259 }
260
261 track->dirty = TRUE;
262 track_info(track);
263
264 return track;
265 }
266
267 void track_point_free(track_point_t *point) {
268 g_free(point);
269 }
270
271 void track_seg_free(track_seg_t *seg) {
272 track_point_t *point = seg->track_point;
273 while(point) {
274 track_point_t *next = point->next;
275 track_point_free(point);
276 point = next;
277 }
278
279 g_free(seg);
280 }
281
282 /* --------------------------------------------------------------- */
283
284 void track_clear(appdata_t *appdata, track_t *track) {
285 if (! track) return;
286
287 printf("clearing track\n");
288
289 if(appdata && appdata->map)
290 map_track_remove(appdata);
291
292 track_seg_t *seg = track->track_seg;
293 while(seg) {
294 track_seg_t *next = seg->next;
295 track_seg_free(seg);
296 seg = next;
297 }
298
299 g_free(track);
300
301 track_menu_set(appdata, FALSE);
302 }
303
304 /* ---------------------- saving track --------------------------- */
305
306 void track_save_points(track_point_t *point, xmlNodePtr node) {
307 while(point) {
308 char str[G_ASCII_DTOSTR_BUF_SIZE];
309
310 xmlNodePtr node_point = xmlNewChild(node, NULL, BAD_CAST "trkpt", NULL);
311
312 g_ascii_formatd(str, sizeof(str), LL_FORMAT, point->pos.lat);
313 xmlNewProp(node_point, BAD_CAST "lat", BAD_CAST str);
314
315 g_ascii_formatd(str, sizeof(str), LL_FORMAT, point->pos.lon);
316 xmlNewProp(node_point, BAD_CAST "lon", BAD_CAST str);
317
318 if(!isnan(point->altitude)) {
319 g_ascii_formatd(str, sizeof(str), ALT_FORMAT, point->altitude);
320 xmlNewTextChild(node_point, NULL, BAD_CAST "ele", BAD_CAST str);
321 }
322
323 if(point->time) {
324 strftime(str, sizeof(str), DATE_FORMAT, localtime(&point->time));
325 xmlNewTextChild(node_point, NULL, BAD_CAST "time", BAD_CAST str);
326 }
327
328 point = point->next;
329 }
330 }
331
332 void track_save_segs(track_seg_t *seg, xmlNodePtr node) {
333 while(seg) {
334 xmlNodePtr node_seg = xmlNewChild(node, NULL, BAD_CAST "trkseg", NULL);
335 track_save_points(seg->track_point, node_seg);
336 seg = seg->next;
337 }
338 }
339
340 void track_write(char *name, track_t *track) {
341 printf("writing track to %s\n", name);
342
343 LIBXML_TEST_VERSION;
344
345 xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
346 xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "gpx");
347 xmlNewProp(root_node, BAD_CAST "creator", BAD_CAST PACKAGE " v" VERSION);
348 xmlNewProp(root_node, BAD_CAST "xmlns", BAD_CAST
349 "http://www.topografix.com/GPX/1/0");
350
351 xmlNodePtr trk_node = xmlNewChild(root_node, NULL, BAD_CAST "trk", NULL);
352 xmlDocSetRootElement(doc, root_node);
353
354 track_save_segs(track->track_seg, trk_node);
355
356 xmlSaveFormatFileEnc(name, doc, "UTF-8", 1);
357 xmlFreeDoc(doc);
358 xmlCleanupParser();
359
360 track->dirty = FALSE;
361 }
362
363 /* save track in project */
364 void track_save(project_t *project, track_t *track) {
365 if(!project) return;
366
367 char *trk_name = g_strdup_printf("%s/%s.trk", project->path, project->name);
368
369 if(!track) {
370 g_remove(trk_name);
371 g_free(trk_name);
372 return;
373 }
374
375 /* no need to save again if it has already been saved */
376 if(!track->dirty) {
377 printf("track is not dirty, no need to save it (again)\n");
378 g_free(trk_name);
379 return;
380 }
381
382 track_write(trk_name, track);
383
384 g_free(trk_name);
385 }
386
387 void track_export(appdata_t *appdata, char *filename) {
388 g_assert(appdata->track.track);
389 track_write(filename, appdata->track.track);
390 }
391
392 /* ---------------------- loading track --------------------------- */
393
394 track_t *track_restore(appdata_t *appdata, project_t *project) {
395 char *trk_name = g_strdup_printf("%s/%s.trk", project->path, project->name);
396 track_t *track = NULL;
397
398 LIBXML_TEST_VERSION;
399
400 if(!g_file_test(trk_name, G_FILE_TEST_EXISTS)) {
401 printf("no track present!\n");
402 g_free(trk_name);
403 return NULL;
404 }
405
406 printf("track found, loading ...\n");
407
408 track = track_read(appdata->osm, trk_name);
409 g_free(trk_name);
410
411 track_menu_set(appdata, track != NULL);
412
413 printf("restored track\n");
414 if(track) {
415 track->dirty = FALSE;
416 track_info(track);
417 }
418
419 return track;
420 }
421
422 static void track_end_segment(track_t *track) {
423 if(!track) return;
424
425 if(track->cur_seg) {
426 printf("ending a segment\n");
427
428 /* todo: check if segment only has 1 point */
429
430 track->cur_seg = NULL;
431 }
432 }
433
434 static void track_append_position(appdata_t *appdata, pos_t *pos, float alt) {
435 track_t *track = appdata->track.track;
436
437 track_menu_set(appdata, TRUE);
438
439 /* no track at all? might be due to a "clear track" while running */
440 if(!track) {
441 printf("restarting after \"clear\"\n");
442 track = appdata->track.track = g_new0(track_t, 1);
443 }
444
445 if(!track->cur_seg) {
446 printf("starting new segment\n");
447
448 track_seg_t **seg = &(track->track_seg);
449 while(*seg) seg = &((*seg)->next);
450
451 *seg = track->cur_seg = g_new0(track_seg_t, 1);
452 } else
453 printf("appending to current segment\n");
454
455 gint seg_len = 0;
456 track_point_t **point = &(track->cur_seg->track_point);
457 while(*point) { seg_len++; point = &((*point)->next); }
458
459 map_track_pos(appdata, pos);
460
461 /* don't append if point is the same as last time */
462 track_point_t *prev = track->cur_seg->track_point;
463 while(prev && prev->next) prev = prev->next;
464
465 if(prev && prev->pos.lat == pos->lat &&
466 prev->pos.lon == pos->lon) {
467 printf("same value as last point -> ignore\n");
468 } else {
469
470 *point = g_new0(track_point_t, 1);
471 (*point)->altitude = alt;
472 (*point)->time = time(NULL);
473 (*point)->pos = *pos;
474 track->dirty = TRUE;
475
476 /* if segment length was 1 the segment can now be drawn */
477 /* for the first time */
478 if(!seg_len) {
479 printf("initial draw\n");
480 g_assert(!track->cur_seg->item_chain);
481 map_track_draw_seg(appdata->map, track->cur_seg);
482 }
483
484 /* if segment length was > 0 the segment has to be updated */
485 if(seg_len > 0) {
486 printf("update draw with seg_len %d\n", seg_len+1);
487
488 g_assert(track->cur_seg->item_chain);
489 map_track_update_seg(appdata->map, track->cur_seg);
490 }
491 }
492
493 #ifdef USE_GOOCANVAS
494 if(appdata->settings && appdata->settings->follow_gps) {
495 lpos_t lpos;
496 pos2lpos(appdata->osm->bounds, pos, &lpos);
497 map_scroll_to_if_offscreen(appdata->map, &lpos);
498 }
499 #endif
500 }
501
502 static gboolean update(gpointer data) {
503 appdata_t *appdata = (appdata_t*)data;
504
505 /* ignore updates while no valid osm file is loaded, e.g. when switching */
506 /* projects */
507 if(!appdata->osm)
508 return TRUE;
509
510 /* the map is only gone of the main screen is being closed */
511 if(!appdata->map) {
512 printf("map has gone while tracking was active, stopping tracker\n");
513
514 if(appdata->track.handler_id) {
515 gtk_timeout_remove(appdata->track.handler_id);
516 appdata->track.handler_id = 0;
517 }
518
519 return FALSE;
520 }
521
522 if(!appdata->settings || !appdata->settings->enable_gps) {
523 // Turn myself off gracefully.
524 track_do_disable_gps(appdata);
525 return FALSE;
526 }
527
528 pos_t pos;
529 float alt;
530 if(gps_get_pos(appdata, &pos, &alt)) {
531 printf("valid position %.6f/%.6f alt %.2f\n", pos.lat, pos.lon, alt);
532 track_append_position(appdata, &pos, alt);
533 } else {
534 printf("no valid position\n");
535 /* end segment */
536 track_end_segment(appdata->track.track);
537 map_track_pos(appdata, NULL);
538 }
539
540 return TRUE;
541 }
542
543 static void track_do_enable_gps(appdata_t *appdata) {
544 gps_enable(appdata, TRUE);
545
546 if(!appdata->track.handler_id) {
547 appdata->track.handler_id = gtk_timeout_add(1000, update, appdata);
548
549 if(!appdata->track.track) {
550 printf("GPS: no track yet, starting new one\n");
551 appdata->track.track = g_new0(track_t, 1);
552 appdata->track.track->dirty = FALSE;
553 } else
554 printf("GPS: extending existing track\n");
555 }
556 }
557
558 static void track_do_disable_gps(appdata_t *appdata) {
559 gps_enable(appdata, FALSE);
560
561 if(appdata->track.handler_id) {
562 gtk_timeout_remove(appdata->track.handler_id);
563 appdata->track.handler_id = 0;
564 }
565
566 /* stopping the GPS removes the marker ... */
567 map_track_pos(appdata, NULL);
568
569 /* ... and terminates the current segment if present */
570 if(appdata->track.track)
571 appdata->track.track->cur_seg = NULL;
572 }
573
574 void track_enable_gps(appdata_t *appdata, gboolean enable) {
575 printf("request to %sable gps\n", enable?"en":"dis");
576
577 if(appdata->settings)
578 appdata->settings->enable_gps = enable;
579
580 gtk_widget_set_sensitive(appdata->track.menu_item_track_follow_gps, enable);
581
582 if(enable) track_do_enable_gps(appdata);
583 else track_do_disable_gps(appdata);
584 }
585
586 track_t *track_import(appdata_t *appdata, char *name) {
587 printf("import %s\n", name);
588
589 /* remove any existing track */
590 if(appdata->track.track) {
591 track_clear(appdata, appdata->track.track);
592 appdata->track.track = NULL;
593 }
594
595 track_t *track = track_read(appdata->osm, name);
596 track_menu_set(appdata, track != NULL);
597
598 if(track) {
599 map_track_draw(appdata->map, track);
600 track->dirty = TRUE;
601 }
602
603 return track;
604 }
605
606 // vim:et:ts=8:sw=2:sts=2:ai