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