Contents of /branches/ports/maemo/src/track.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 137 - (show annotations)
Sat Mar 21 22:37:16 2009 UTC (15 years, 3 months ago) by achadwick
File MIME type: text/plain
File size: 16707 byte(s)
Merge to tags/trunk-0.6.14 (-r89:HEAD). Will commit build fixes separately.
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 #include "appdata.h"
24
25 #ifndef LIBXML_TREE_ENABLED
26 #error "Tree not enabled in libxml"
27 #endif
28
29 // predecs
30 static void track_enable_gps(appdata_t *appdata);
31 static void track_disable_gps(appdata_t *appdata);
32
33 /* enable/disable menu with respect to mode */
34 void track_set_mode(appdata_t *appdata, track_t *track, track_mode_t mode) {
35 /* import and gps are always enabled */
36 const gboolean clear[] = { FALSE, TRUE, TRUE };
37 const gboolean export[] = { FALSE, FALSE, TRUE };
38
39 gtk_widget_set_sensitive(appdata->track.menu_item_clear, clear[mode]);
40 gtk_widget_set_sensitive(appdata->track.menu_item_export, export[mode]);
41
42 /* adjust menu item if required */
43 if((mode == TRACK_GPS) &&
44 !gtk_check_menu_item_get_active(
45 GTK_CHECK_MENU_ITEM(appdata->track.menu_item_gps)))
46 gtk_check_menu_item_set_active(
47 GTK_CHECK_MENU_ITEM(appdata->track.menu_item_gps), TRUE);
48
49 if((mode != TRACK_GPS) &&
50 gtk_check_menu_item_get_active(
51 GTK_CHECK_MENU_ITEM(appdata->track.menu_item_gps)))
52 gtk_check_menu_item_set_active(
53 GTK_CHECK_MENU_ITEM(appdata->track.menu_item_gps), FALSE);
54
55 if(track)
56 track->mode = mode;
57 }
58
59 gint track_seg_points(track_seg_t *seg) {
60 gint points = 0;
61
62 track_point_t *point = seg->track_point;
63 while(point) {
64 points++;
65 point = point->next;
66 }
67 return points;
68 }
69
70 static gboolean track_get_prop_pos(xmlNode *node, pos_t *pos) {
71 char *str_lat = (char*)xmlGetProp(node, BAD_CAST "lat");
72 char *str_lon = (char*)xmlGetProp(node, BAD_CAST "lon");
73
74 if(!str_lon || !str_lat) {
75 if(!str_lon) xmlFree(str_lon);
76 if(!str_lat) xmlFree(str_lat);
77 return FALSE;
78 }
79
80 pos->lat = g_ascii_strtod(str_lat, NULL);
81 pos->lon = g_ascii_strtod(str_lon, NULL);
82
83 xmlFree(str_lon);
84 xmlFree(str_lat);
85
86 return TRUE;
87 }
88
89 static track_point_t *track_parse_trkpt(bounds_t *bounds, xmlDocPtr doc,
90 xmlNode *a_node) {
91 track_point_t *point = NULL;
92 pos_t pos;
93
94 /* parse position */
95 if(!track_get_prop_pos(a_node, &pos))
96 return NULL;
97
98 point = g_new0(track_point_t, 1);
99
100 pos2lpos(bounds, &pos, &point->lpos);
101
102 /* check if point is within bounds */
103 if((point->lpos.x < bounds->min.x) || (point->lpos.x > bounds->max.x) ||
104 (point->lpos.y < bounds->min.y) || (point->lpos.y > bounds->max.y)) {
105 g_free(point);
106 point = NULL;
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("Loaded track: %s\n", track->filename);
226 printf("Track has %sbeen saved in project.\n", track->saved?"":"not ");
227
228 gint segs = 0, points = 0;
229 track_seg_t *seg = track->track_seg;
230 while(seg) {
231 points += track_seg_points(seg);
232 segs++;
233 seg = seg->next;
234 }
235
236 printf("%d points in %d segments\n", points, segs);
237
238 }
239
240 static track_t *track_import(osm_t *osm, char *filename) {
241 printf("============================================================\n");
242 printf("loading track %s\n", filename);
243
244 xmlDoc *doc = NULL;
245
246 LIBXML_TEST_VERSION;
247
248 /* parse the file and get the DOM */
249 if((doc = xmlReadFile(filename, NULL, 0)) == NULL) {
250 xmlErrorPtr errP = xmlGetLastError();
251 errorf(NULL, "While parsing \"%s\":\n\n%s", filename, errP->message);
252 return NULL;
253 }
254
255 track_t *track = track_parse_doc(osm->bounds, doc);
256 track->filename = g_strdup(filename);
257 track->saved = FALSE;
258
259 track_info(track);
260
261 return track;
262 }
263
264 void track_point_free(track_point_t *point) {
265 g_free(point);
266 }
267
268 void track_seg_free(track_seg_t *seg) {
269 track_point_t *point = seg->track_point;
270 while(point) {
271 track_point_t *next = point->next;
272 track_point_free(point);
273 point = next;
274 }
275
276 g_free(seg);
277 }
278
279 /* --------------------------------------------------------------- */
280
281 void track_clear(appdata_t *appdata, track_t *track) {
282 if (! track)
283 return;
284 printf("clearing track\n");
285
286 if(appdata->map)
287 map_track_remove(appdata);
288
289 track_seg_t *seg = track->track_seg;
290 while(seg) {
291 track_seg_t *next = seg->next;
292 track_seg_free(seg);
293 seg = next;
294 }
295
296 g_free(track->filename);
297 g_free(track);
298 }
299
300 /* ---------------------- saving track --------------------------- */
301
302 void track_save_points(track_point_t *point, xmlNodePtr node) {
303 while(point) {
304 xmlNodePtr node_point = xmlNewChild(node, NULL, BAD_CAST "point", NULL);
305
306 char *str = g_strdup_printf("%d", point->lpos.x);
307 xmlNewProp(node_point, BAD_CAST "x", BAD_CAST str);
308 g_free(str);
309
310 str = g_strdup_printf("%d", point->lpos.y);
311 xmlNewProp(node_point, BAD_CAST "y", BAD_CAST str);
312 g_free(str);
313
314 point = point->next;
315 }
316 }
317
318 void track_save_segs(track_seg_t *seg, xmlNodePtr node) {
319 while(seg) {
320 xmlNodePtr node_seg = xmlNewChild(node, NULL, BAD_CAST "seg", NULL);
321 track_save_points(seg->track_point, node_seg);
322 seg = seg->next;
323 }
324 }
325
326 /* save track in project */
327 void track_save(project_t *project, track_t *track) {
328 if(!project) return;
329
330 char *trk_name = g_strdup_printf("%s/%s.trk", project->path, project->name);
331
332 if(!track) {
333 g_remove(trk_name);
334 g_free(trk_name);
335 return;
336 }
337
338 /* no need to save again if it has already been saved */
339 if(track->saved) {
340 printf("track already saved, don't save it again\n");
341 g_free(trk_name);
342 return;
343 }
344
345 printf("saving track\n");
346
347 LIBXML_TEST_VERSION;
348
349 xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
350 xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "trk");
351 xmlNewProp(root_node, BAD_CAST "filename", BAD_CAST track->filename);
352 char *mode_str = g_strdup_printf("%d", track->mode);
353 xmlNewProp(root_node, BAD_CAST "mode", BAD_CAST mode_str);
354 g_free(mode_str);
355 xmlDocSetRootElement(doc, root_node);
356
357 track_save_segs(track->track_seg, root_node);
358
359 xmlSaveFormatFileEnc(trk_name, doc, "UTF-8", 1);
360 xmlFreeDoc(doc);
361 xmlCleanupParser();
362
363 g_free(trk_name);
364
365 track->saved = TRUE;
366 }
367
368 /* ---------------------- loading track --------------------------- */
369
370 static int xml_get_prop_int(xmlNode *node, char *prop) {
371 char *str = (char*)xmlGetProp(node, BAD_CAST prop);
372 int value = 0;
373
374 if(str) {
375 value = strtoul(str, NULL, 10);
376 xmlFree(str);
377 }
378
379 return value;
380 }
381
382 track_t *track_restore(appdata_t *appdata, project_t *project) {
383 char *trk_name = g_strdup_printf("%s/%s.trk", project->path, project->name);
384 track_t *track = NULL;
385
386 LIBXML_TEST_VERSION;
387
388 if(!g_file_test(trk_name, G_FILE_TEST_EXISTS)) {
389 printf("no track present!\n");
390 g_free(trk_name);
391 return NULL;
392 }
393
394 printf("track found, loading ...\n");
395
396 xmlDoc *doc = NULL;
397 xmlNode *root_element = NULL;
398
399 /* parse the file and get the DOM */
400 if((doc = xmlReadFile(trk_name, NULL, 0)) == NULL) {
401 errorf(GTK_WIDGET(appdata->window),
402 "Error: could not parse file %s\n", trk_name);
403 g_free(trk_name);
404 return NULL;
405 }
406
407 /* Get the root element node */
408 root_element = xmlDocGetRootElement(doc);
409
410 xmlNode *cur_node = NULL;
411 for (cur_node = root_element; cur_node; cur_node = cur_node->next) {
412 if (cur_node->type == XML_ELEMENT_NODE) {
413 if(strcasecmp((char*)cur_node->name, "trk") == 0) {
414 printf("found track\n");
415
416 track = g_new0(track_t, 1);
417 track->mode = xml_get_prop_int(cur_node, "mode");
418
419 char *str = (char*)xmlGetProp(cur_node, BAD_CAST "filename");
420 if(str) {
421 track->filename = g_strdup(str);
422 xmlFree(str);
423 }
424
425 xmlNodePtr seg_node = cur_node->children;
426 track_seg_t **seg = &track->track_seg;
427 while(seg_node) {
428 if(seg_node->type == XML_ELEMENT_NODE) {
429
430 if(strcasecmp((char*)seg_node->name, "seg") == 0) {
431 *seg = g_new0(track_seg_t, 1);
432
433 xmlNodePtr point_node = seg_node->children;
434 track_point_t **point = &(*seg)->track_point;
435 while(point_node) {
436 if(point_node->type == XML_ELEMENT_NODE) {
437
438 if(strcasecmp((char*)point_node->name, "point") == 0) {
439 *point = g_new0(track_point_t, 1);
440 (*point)->lpos.x = xml_get_prop_int(point_node, "x");
441 (*point)->lpos.y = xml_get_prop_int(point_node, "y");
442
443 point = &((*point)->next);
444 }
445 }
446 point_node = point_node->next;
447 }
448
449 seg = &((*seg)->next);
450 }
451 }
452 seg_node = seg_node->next;
453 }
454 }
455 }
456 }
457
458 g_free(trk_name);
459
460 printf("restoring track mode %d\n", track->mode);
461 track_set_mode(appdata, track, track->mode);
462 track->saved = TRUE;
463 track_info(track);
464
465 return track;
466 }
467
468 static void track_end_segment(track_t *track) {
469 if(track->cur_seg) {
470 printf("ending a segment\n");
471
472 /* todo: check if segment only has 1 point */
473
474 track->cur_seg = NULL;
475 }
476 }
477
478 static void track_append_position(appdata_t *appdata, pos_t *pos) {
479 track_t *track = appdata->track.track;
480
481 if(!track->cur_seg) {
482 printf("starting new segment\n");
483
484 track_seg_t **seg = &(track->track_seg);
485 while(*seg) seg = &((*seg)->next);
486
487 *seg = track->cur_seg = g_new0(track_seg_t, 1);
488 } else
489 printf("appending to current segment\n");
490
491 gint seg_len = 0;
492 track_point_t **point = &(track->cur_seg->track_point);
493 while(*point) { seg_len++; point = &((*point)->next); }
494
495 /* create utm coordinates */
496 lpos_t lpos;
497 bounds_t *bounds = appdata->osm->bounds;
498 pos2lpos(bounds, pos, &lpos);
499
500 /* check if point is within bounds */
501 if((lpos.x < bounds->min.x) || (lpos.x > bounds->max.x) ||
502 (lpos.y < bounds->min.y) || (lpos.y > bounds->max.y)) {
503 printf("position out of bounds\n");
504
505 /* end segment */
506 track_end_segment(track);
507 map_track_pos(appdata, NULL);
508 } else {
509 map_track_pos(appdata, &lpos);
510
511 /* don't append if point is the same as last time */
512 track_point_t *prev = track->cur_seg->track_point;
513 while(prev && prev->next) prev = prev->next;
514
515 if(prev && prev->lpos.x == lpos.x && prev->lpos.y == lpos.y) {
516 printf("same value as last point -> ignore\n");
517 } else {
518
519 *point = g_new0(track_point_t, 1);
520 (*point)->lpos.x = lpos.x;
521 (*point)->lpos.y = lpos.y;
522 track->saved = FALSE;
523
524 /* if segment length was 1 the segment can now be drawn for the first time */
525 if(seg_len <= 1) {
526 printf("initial/second draw with seg_len %d\n", seg_len);
527
528 if(seg_len == 0) g_assert(!track->cur_seg->item);
529 else g_assert(track->cur_seg->item);
530
531 map_track_draw_seg(appdata->map, track->cur_seg);
532 }
533
534 /* if segment length was > 1 the segment has to be updated */
535 if(seg_len > 1) {
536 printf("update draw\n");
537
538 g_assert(track->cur_seg->item);
539 map_track_update_seg(appdata->map, track->cur_seg);
540 }
541 }
542
543 #ifdef USE_GOOCANVAS
544 map_scroll_to_if_offscreen(appdata->map, &lpos);
545 #endif
546 }
547 }
548
549 static gboolean update(gpointer data) {
550 appdata_t *appdata = (appdata_t*)data;
551
552 if(! appdata->map) {
553 printf("map has gone while tracking was active, stopping tracker\n");
554
555 if(appdata->track.handler_id) {
556 gtk_timeout_remove(appdata->track.handler_id);
557 appdata->track.handler_id = 0;
558 }
559
560 return FALSE;
561 }
562
563 if (! appdata->gps_enabled) {
564 // Turn myself off gracefully.
565 track_disable_gps(appdata);
566 return FALSE;
567 }
568
569 pos_t *pos = gps_get_pos(appdata);
570 if(pos) {
571 printf("valid position %f/%f\n", pos->lat, pos->lon);
572 track_append_position(appdata, pos);
573 } else {
574 printf("no valid position\n");
575 /* end segment */
576 track_end_segment(appdata->track.track);
577 map_track_pos(appdata, NULL);
578 }
579
580 return TRUE;
581 }
582
583 static void track_enable_gps(appdata_t *appdata) {
584 appdata->gps_enabled = TRUE;
585
586 if(!appdata->track.handler_id) {
587 appdata->track.handler_id = gtk_timeout_add(1000, update, appdata);
588
589 if(appdata->track.track) {
590 printf("there's already a track -> restored!!\n");
591 map_track_draw(appdata->map, appdata->track.track);
592 } else
593 appdata->track.track = g_new0(track_t, 1);
594 }
595 }
596
597 static void track_disable_gps(appdata_t *appdata) {
598 appdata->gps_enabled = FALSE;
599
600 if(appdata->track.handler_id) {
601 gtk_timeout_remove(appdata->track.handler_id);
602 appdata->track.handler_id = 0;
603 }
604 }
605
606 void track_do(appdata_t *appdata, track_mode_t mode, char *name) {
607
608 printf("track do %d\n", mode);
609
610 /* remove existing track */
611 if(appdata->track.track) {
612 track_clear(appdata, appdata->track.track);
613 appdata->track.track = NULL;
614 map_track_pos(appdata, NULL);
615 }
616
617 switch(mode) {
618 case TRACK_NONE:
619 /* disable gps if it was on */
620 track_disable_gps(appdata);
621
622 track_set_mode(appdata, appdata->track.track, TRACK_NONE);
623 break;
624
625 case TRACK_IMPORT:
626 /* disable gps if it was on */
627 track_disable_gps(appdata);
628
629 appdata->track.track = track_import(appdata->osm, name);
630 map_track_draw(appdata->map, appdata->track.track);
631 track_set_mode(appdata, appdata->track.track, TRACK_IMPORT);
632 break;
633
634 case TRACK_GPS:
635 track_enable_gps(appdata);
636
637 track_set_mode(appdata, appdata->track.track, TRACK_GPS);
638 break;
639 }
640 }
641 // vim:et:ts=8:sw=2:sts=2:ai