Parent Directory | Revision Log
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 | } |