Contents of /trunk/src/track.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2 - (show annotations)
Wed Dec 10 00:00:05 2008 UTC (15 years, 5 months ago) by achadwick
File MIME type: text/plain
File size: 16083 byte(s)
Begin trunk. No code changes.
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 /* enable/disable menu with respect to mode */
30 void track_set_mode(appdata_t *appdata, track_t *track, track_mode_t mode) {
31 /* import and gps are always enabled */
32 const gboolean clear[] = { FALSE, TRUE, TRUE };
33 const gboolean export[] = { FALSE, FALSE, TRUE };
34
35 gtk_widget_set_sensitive(appdata->track.menu_item_clear, clear[mode]);
36 gtk_widget_set_sensitive(appdata->track.menu_item_export, export[mode]);
37
38 /* adjust menu item if required */
39 if((mode == TRACK_GPS) &&
40 !gtk_check_menu_item_get_active(
41 GTK_CHECK_MENU_ITEM(appdata->track.menu_item_gps)))
42 gtk_check_menu_item_set_active(
43 GTK_CHECK_MENU_ITEM(appdata->track.menu_item_gps), TRUE);
44
45 if((mode != TRACK_GPS) &&
46 gtk_check_menu_item_get_active(
47 GTK_CHECK_MENU_ITEM(appdata->track.menu_item_gps)))
48 gtk_check_menu_item_set_active(
49 GTK_CHECK_MENU_ITEM(appdata->track.menu_item_gps), FALSE);
50
51 if(track)
52 track->mode = mode;
53 }
54
55 gint track_seg_points(track_seg_t *seg) {
56 gint points = 0;
57
58 track_point_t *point = seg->track_point;
59 while(point) {
60 points++;
61 point = point->next;
62 }
63 return points;
64 }
65
66 static gboolean track_get_prop_pos(xmlNode *node, pos_t *pos) {
67 char *str_lat = (char*)xmlGetProp(node, BAD_CAST "lat");
68 char *str_lon = (char*)xmlGetProp(node, BAD_CAST "lon");
69
70 if(!str_lon || !str_lat) {
71 if(!str_lon) xmlFree(str_lon);
72 if(!str_lat) xmlFree(str_lat);
73 return FALSE;
74 }
75
76 pos->lat = g_ascii_strtod(str_lat, NULL);
77 pos->lon = g_ascii_strtod(str_lon, NULL);
78
79 xmlFree(str_lon);
80 xmlFree(str_lat);
81
82 return TRUE;
83 }
84
85 static track_point_t *track_parse_trkpt(bounds_t *bounds, xmlDocPtr doc,
86 xmlNode *a_node) {
87 track_point_t *point = NULL;
88 pos_t pos;
89
90 /* parse position */
91 if(!track_get_prop_pos(a_node, &pos))
92 return NULL;
93
94 point = g_new0(track_point_t, 1);
95
96 pos2lpos(bounds, &pos, &point->lpos);
97
98 /* check if point is within bounds */
99 if((point->lpos.x < bounds->min.x) || (point->lpos.x > bounds->max.x) ||
100 (point->lpos.y < bounds->min.y) || (point->lpos.y > bounds->max.y)) {
101 g_free(point);
102 point = NULL;
103 }
104
105 return point;
106 }
107
108 static void track_parse_trkseg(track_t *track, bounds_t *bounds,
109 xmlDocPtr doc, xmlNode *a_node) {
110 xmlNode *cur_node = NULL;
111 track_point_t **point = NULL;
112 track_seg_t **seg = &(track->track_seg);
113
114 /* search end of track_seg list */
115 while(*seg) seg = &((*seg)->next);
116
117 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
118 if (cur_node->type == XML_ELEMENT_NODE) {
119 if(strcasecmp((char*)cur_node->name, "trkpt") == 0) {
120 track_point_t *cpnt = track_parse_trkpt(bounds, doc, cur_node);
121 if(cpnt) {
122 if(!point) {
123 /* start a new segment */
124 *seg = g_new0(track_seg_t, 1);
125 point = &((*seg)->track_point);
126 }
127 /* attach point to chain */
128 *point = cpnt;
129 point = &((*point)->next);
130 } else {
131 /* end segment if point could not be parsed and start a new one */
132 /* close segment if there is one */
133 if(point) {
134 printf("ending track segment leaving bounds\n");
135 seg = &((*seg)->next);
136 point = NULL;
137 }
138 }
139 } else
140 printf("found unhandled gpx/trk/trkseg/%s\n", cur_node->name);
141
142 }
143 }
144 }
145
146 static track_t *track_parse_trk(bounds_t *bounds,
147 xmlDocPtr doc, xmlNode *a_node) {
148 track_t *track = g_new0(track_t, 1);
149 xmlNode *cur_node = NULL;
150
151 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
152 if (cur_node->type == XML_ELEMENT_NODE) {
153 if(strcasecmp((char*)cur_node->name, "trkseg") == 0) {
154 track_parse_trkseg(track, bounds, doc, cur_node);
155 } else
156 printf("found unhandled gpx/trk/%s\n", cur_node->name);
157
158 }
159 }
160 return track;
161 }
162
163 static track_t *track_parse_gpx(bounds_t *bounds,
164 xmlDocPtr doc, xmlNode *a_node) {
165 track_t *track = NULL;
166 xmlNode *cur_node = NULL;
167
168 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
169 if (cur_node->type == XML_ELEMENT_NODE) {
170 if(strcasecmp((char*)cur_node->name, "trk") == 0) {
171 if(!track)
172 track = track_parse_trk(bounds, doc, cur_node);
173 else
174 printf("ignoring additional track\n");
175 } else
176 printf("found unhandled gpx/%s\n", cur_node->name);
177 }
178 }
179 return track;
180 }
181
182 /* parse root element and search for "track" */
183 static track_t *track_parse_root(bounds_t *bounds,
184 xmlDocPtr doc, xmlNode *a_node) {
185 track_t *track = NULL;
186 xmlNode *cur_node = NULL;
187
188 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
189 if (cur_node->type == XML_ELEMENT_NODE) {
190 /* parse track file ... */
191 if(strcasecmp((char*)cur_node->name, "gpx") == 0)
192 track = track_parse_gpx(bounds, doc, cur_node);
193 else
194 printf("found unhandled %s\n", cur_node->name);
195 }
196 }
197 return track;
198 }
199
200 static track_t *track_parse_doc(bounds_t *bounds, xmlDocPtr doc) {
201 track_t *track;
202
203 /* Get the root element node */
204 xmlNode *root_element = xmlDocGetRootElement(doc);
205
206 track = track_parse_root(bounds, doc, root_element);
207
208 /*free the document */
209 xmlFreeDoc(doc);
210
211 /*
212 * Free the global variables that may
213 * have been allocated by the parser.
214 */
215 xmlCleanupParser();
216
217 return track;
218 }
219
220 void track_info(track_t *track) {
221 printf("Loaded track: %s\n", track->filename);
222 printf("Track has %sbeen saved in project.\n", track->saved?"":"not ");
223
224 gint segs = 0, points = 0;
225 track_seg_t *seg = track->track_seg;
226 while(seg) {
227 points += track_seg_points(seg);
228 segs++;
229 seg = seg->next;
230 }
231
232 printf("%d points in %d segments\n", points, segs);
233
234 }
235
236 static track_t *track_import(osm_t *osm, char *filename) {
237 printf("============================================================\n");
238 printf("loading track %s\n", filename);
239
240 xmlDoc *doc = NULL;
241
242 LIBXML_TEST_VERSION;
243
244 /* parse the file and get the DOM */
245 if((doc = xmlReadFile(filename, NULL, 0)) == NULL) {
246 xmlErrorPtr errP = xmlGetLastError();
247 errorf(NULL, "While parsing \"%s\":\n\n%s", filename, errP->message);
248 return NULL;
249 }
250
251 track_t *track = track_parse_doc(osm->bounds, doc);
252 track->filename = g_strdup(filename);
253 track->saved = FALSE;
254
255 track_info(track);
256
257 return track;
258 }
259
260 void track_point_free(track_point_t *point) {
261 g_free(point);
262 }
263
264 void track_seg_free(track_seg_t *seg) {
265 track_point_t *point = seg->track_point;
266 while(point) {
267 track_point_t *next = point->next;
268 track_point_free(point);
269 point = next;
270 }
271
272 g_free(seg);
273 }
274
275 /* --------------------------------------------------------------- */
276
277 static void track_clear(appdata_t *appdata, track_t *track) {
278 printf("clearing track\n");
279
280 if(appdata->map)
281 map_track_remove(appdata);
282
283 track_seg_t *seg = track->track_seg;
284 while(seg) {
285 track_seg_t *next = seg->next;
286 track_seg_free(seg);
287 seg = next;
288 }
289
290 g_free(track->filename);
291 g_free(track);
292 }
293
294 /* ---------------------- saving track --------------------------- */
295
296 void track_save_points(track_point_t *point, xmlNodePtr node) {
297 while(point) {
298 xmlNodePtr node_point = xmlNewChild(node, NULL, BAD_CAST "point", NULL);
299
300 char *str = g_strdup_printf("%d", point->lpos.x);
301 xmlNewProp(node_point, BAD_CAST "x", BAD_CAST str);
302 g_free(str);
303
304 str = g_strdup_printf("%d", point->lpos.y);
305 xmlNewProp(node_point, BAD_CAST "y", BAD_CAST str);
306 g_free(str);
307
308 point = point->next;
309 }
310 }
311
312 void track_save_segs(track_seg_t *seg, xmlNodePtr node) {
313 while(seg) {
314 xmlNodePtr node_seg = xmlNewChild(node, NULL, BAD_CAST "seg", NULL);
315 track_save_points(seg->track_point, node_seg);
316 seg = seg->next;
317 }
318 }
319
320 /* save track in project */
321 void track_save(project_t *project, track_t *track) {
322 if(!project) return;
323
324 char *trk_name = g_strdup_printf("%s/%s.trk", project->path, project->name);
325
326 if(!track) {
327 g_remove(trk_name);
328 g_free(trk_name);
329 return;
330 }
331
332 /* no need to save again if it has already been saved */
333 if(track->saved) {
334 printf("track already saved, don't save it again\n");
335 g_free(trk_name);
336 return;
337 }
338
339 printf("saving track\n");
340
341 LIBXML_TEST_VERSION;
342
343 xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
344 xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "trk");
345 xmlNewProp(root_node, BAD_CAST "filename", BAD_CAST track->filename);
346 char *mode_str = g_strdup_printf("%d", track->mode);
347 xmlNewProp(root_node, BAD_CAST "mode", BAD_CAST mode_str);
348 g_free(mode_str);
349 xmlDocSetRootElement(doc, root_node);
350
351 track_save_segs(track->track_seg, root_node);
352
353 xmlSaveFormatFileEnc(trk_name, doc, "UTF-8", 1);
354 xmlFreeDoc(doc);
355 xmlCleanupParser();
356
357 g_free(trk_name);
358
359 track->saved = TRUE;
360 }
361
362 /* ---------------------- loading track --------------------------- */
363
364 static int xml_get_prop_int(xmlNode *node, char *prop) {
365 char *str = (char*)xmlGetProp(node, BAD_CAST prop);
366 int value = 0;
367
368 if(str) {
369 value = strtoul(str, NULL, 10);
370 xmlFree(str);
371 }
372
373 return value;
374 }
375
376 track_t *track_restore(appdata_t *appdata, project_t *project) {
377 char *trk_name = g_strdup_printf("%s/%s.trk", project->path, project->name);
378 track_t *track = NULL;
379
380 LIBXML_TEST_VERSION;
381
382 if(!g_file_test(trk_name, G_FILE_TEST_EXISTS)) {
383 printf("no track present!\n");
384 g_free(trk_name);
385 return NULL;
386 }
387
388 printf("track found, loading ...\n");
389
390 xmlDoc *doc = NULL;
391 xmlNode *root_element = NULL;
392
393 /* parse the file and get the DOM */
394 if((doc = xmlReadFile(trk_name, NULL, 0)) == NULL) {
395 errorf(GTK_WIDGET(appdata->window),
396 "Error: could not parse file %s\n", trk_name);
397 g_free(trk_name);
398 return NULL;
399 }
400
401 /* Get the root element node */
402 root_element = xmlDocGetRootElement(doc);
403
404 xmlNode *cur_node = NULL;
405 for (cur_node = root_element; cur_node; cur_node = cur_node->next) {
406 if (cur_node->type == XML_ELEMENT_NODE) {
407 if(strcasecmp((char*)cur_node->name, "trk") == 0) {
408 printf("found track\n");
409
410 track = g_new0(track_t, 1);
411 track->mode = xml_get_prop_int(cur_node, "mode");
412
413 char *str = (char*)xmlGetProp(cur_node, BAD_CAST "filename");
414 if(str) {
415 track->filename = g_strdup(str);
416 xmlFree(str);
417 }
418
419 xmlNodePtr seg_node = cur_node->children;
420 track_seg_t **seg = &track->track_seg;
421 while(seg_node) {
422 if(seg_node->type == XML_ELEMENT_NODE) {
423
424 if(strcasecmp((char*)seg_node->name, "seg") == 0) {
425 *seg = g_new0(track_seg_t, 1);
426
427 xmlNodePtr point_node = seg_node->children;
428 track_point_t **point = &(*seg)->track_point;
429 while(point_node) {
430 if(point_node->type == XML_ELEMENT_NODE) {
431
432 if(strcasecmp((char*)point_node->name, "point") == 0) {
433 *point = g_new0(track_point_t, 1);
434 (*point)->lpos.x = xml_get_prop_int(point_node, "x");
435 (*point)->lpos.y = xml_get_prop_int(point_node, "y");
436
437 point = &((*point)->next);
438 }
439 }
440 point_node = point_node->next;
441 }
442
443 seg = &((*seg)->next);
444 }
445 }
446 seg_node = seg_node->next;
447 }
448 }
449 }
450 }
451
452 g_free(trk_name);
453
454 printf("restoring track mode %d\n", track->mode);
455 track_set_mode(appdata, track, track->mode);
456 track->saved = TRUE;
457 track_info(track);
458
459 return track;
460 }
461
462 static void track_end_segment(track_t *track) {
463 if(track->cur_seg) {
464 printf("ending a segment\n");
465
466 /* todo: check if segment only has 1 point */
467
468 track->cur_seg = NULL;
469 }
470 }
471
472 static void track_append_position(appdata_t *appdata, pos_t *pos) {
473 track_t *track = appdata->track.track;
474
475 if(!track->cur_seg) {
476 printf("starting new segment\n");
477
478 track_seg_t **seg = &(track->track_seg);
479 while(*seg) seg = &((*seg)->next);
480
481 *seg = track->cur_seg = g_new0(track_seg_t, 1);
482 } else
483 printf("appending to current segment\n");
484
485 gint seg_len = 0;
486 track_point_t **point = &(track->cur_seg->track_point);
487 while(*point) { seg_len++; point = &((*point)->next); }
488
489 /* create utm coordinates */
490 lpos_t lpos;
491 bounds_t *bounds = appdata->osm->bounds;
492 pos2lpos(bounds, pos, &lpos);
493
494 /* check if point is within bounds */
495 if((lpos.x < bounds->min.x) || (lpos.x > bounds->max.x) ||
496 (lpos.y < bounds->min.y) || (lpos.y > bounds->max.y)) {
497 printf("position out of bounds\n");
498
499 /* end segment */
500 track_end_segment(track);
501 map_track_pos(appdata, NULL);
502 } else {
503 map_track_pos(appdata, &lpos);
504
505 /* don't append if point is the same as last time */
506 track_point_t *prev = track->cur_seg->track_point;
507 while(prev && prev->next) prev = prev->next;
508
509 if(prev && prev->lpos.x == lpos.x && prev->lpos.y == lpos.y) {
510 printf("same value as last point -> ignore\n");
511 } else {
512
513 *point = g_new0(track_point_t, 1);
514 (*point)->lpos.x = lpos.x;
515 (*point)->lpos.y = lpos.y;
516 track->saved = FALSE;
517
518 /* if segment length was 1 the segment can now be drawn for the first time */
519 if(seg_len <= 1) {
520 printf("initial/second draw with seg_len %d\n", seg_len);
521
522 if(seg_len == 0) g_assert(!track->cur_seg->item);
523 else g_assert(track->cur_seg->item);
524
525 map_track_draw_seg(appdata->map, track->cur_seg);
526 }
527
528 /* if segment length was > 1 the segment has to be updated */
529 if(seg_len > 1) {
530 printf("update draw\n");
531
532 g_assert(track->cur_seg->item);
533 map_track_update_seg(appdata->map, track->cur_seg);
534 }
535 }
536 }
537 }
538
539 static gboolean update(gpointer data) {
540 appdata_t *appdata = (appdata_t*)data;
541
542 pos_t *pos = gps_get_pos(appdata);
543 if(pos) {
544 printf("valid position %f/%f\n", pos->lat, pos->lon);
545 track_append_position(appdata, pos);
546 } else {
547 printf("no valid position\n");
548 /* end segment */
549 track_end_segment(appdata->track.track);
550 map_track_pos(appdata, NULL);
551 }
552
553 return TRUE;
554 }
555
556 static void track_enable_gps(appdata_t *appdata) {
557 appdata->gps_enabled = TRUE;
558
559 if(!appdata->track.handler_id) {
560 appdata->track.handler_id = gtk_timeout_add(1000, update, appdata);
561
562 if(appdata->track.track) {
563 printf("there's already a track -> restored!!\n");
564 map_track_draw(appdata->map, appdata->track.track);
565 } else
566 appdata->track.track = g_new0(track_t, 1);
567 }
568 }
569
570 static void track_disable_gps(appdata_t *appdata) {
571 appdata->gps_enabled = FALSE;
572
573 if(appdata->track.handler_id) {
574 gtk_timeout_remove(appdata->track.handler_id);
575 appdata->track.handler_id = 0;
576 }
577 }
578
579 void track_do(appdata_t *appdata, track_mode_t mode, char *name) {
580
581 printf("track do %d\n", mode);
582
583 /* remove existing track */
584 if(appdata->track.track) {
585 track_clear(appdata, appdata->track.track);
586 appdata->track.track = NULL;
587 map_track_pos(appdata, NULL);
588 }
589
590 switch(mode) {
591 case TRACK_NONE:
592 /* disable gps if it was on */
593 track_disable_gps(appdata);
594
595 track_set_mode(appdata, appdata->track.track, TRACK_NONE);
596 break;
597
598 case TRACK_IMPORT:
599 /* disable gps if it was on */
600 track_disable_gps(appdata);
601
602 appdata->track.track = track_import(appdata->osm, name);
603 map_track_draw(appdata->map, appdata->track.track);
604 track_set_mode(appdata, appdata->track.track, TRACK_IMPORT);
605 break;
606
607 case TRACK_GPS:
608 track_enable_gps(appdata);
609
610 track_set_mode(appdata, appdata->track.track, TRACK_GPS);
611 break;
612 }
613 }