Contents of /trunk/src/gpx_dom.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations)
Sat Jun 20 11:08:47 2009 UTC (14 years, 10 months ago) by harbaum
File MIME type: text/plain
File size: 30777 byte(s)
Initial import
1 /*
2 * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3 *
4 * This file is part of GPXView.
5 *
6 * GPXView 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 * GPXView 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 GPXView. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <math.h>
24
25 #include <libxml/parser.h>
26 #include <libxml/tree.h>
27
28 #include <libxml/xmlreader.h>
29
30 #include <glib.h>
31 #include <glib/gstdio.h>
32
33 #include <zlib.h>
34
35 #include "gpxview.h"
36 #include "unzip.h"
37
38 #ifndef LIBXML_TREE_ENABLED
39 #error "Tree not enabled in libxml"
40 #endif
41
42 void gpx_free_wpt(wpt_t *wpt) {
43 if(wpt->id) xmlFree(wpt->id);
44 if(wpt->cmt) xmlFree(wpt->cmt);
45 if(wpt->desc) xmlFree(wpt->desc);
46 free(wpt);
47 }
48
49 void gpx_free_log(log_t *log) {
50 if(log->finder) xmlFree(log->finder);
51 if(log->text) xmlFree(log->text);
52 free(log);
53 }
54
55 void gpx_free_tb(tb_t *tb) {
56 if(tb->name) xmlFree(tb->name);
57 if(tb->ref) xmlFree(tb->ref);
58 free(tb);
59 }
60
61 void gpx_free_cache(cache_t *cache) {
62 log_t *log = cache->log;
63 wpt_t *wpt = cache->wpt;
64 tb_t *tb = cache->tb;
65
66 if(cache->id) xmlFree(cache->id);
67 if(cache->name) xmlFree(cache->name);
68 if(cache->owner) xmlFree(cache->owner);
69 if(cache->short_description) xmlFree(cache->short_description);
70 if(cache->long_description) xmlFree(cache->long_description);
71 if(cache->hint) xmlFree(cache->hint);
72 if(cache->url) xmlFree(cache->url);
73
74 /* free all logs */
75 while(log) { log_t *next = log->next; gpx_free_log(log); log = next; }
76
77 /* free all waypoints */
78 while(wpt) { wpt_t *next = wpt->next; gpx_free_wpt(wpt); wpt = next; }
79
80 /* free all tbs */
81 while(tb) { tb_t *next = tb->next; gpx_free_tb(tb); tb = next; }
82
83 if(cache->notes) notes_free(cache->notes);
84
85 free(cache);
86 }
87
88 void gpx_free(gpx_t *gpx) {
89 cache_t *cache = gpx->cache;
90
91 if(gpx->name) xmlFree(gpx->name);
92 if(gpx->desc) xmlFree(gpx->desc);
93 if(gpx->filename) free(gpx->filename);
94
95 /* free all caches */
96 while(cache) {
97 cache_t *next = cache->next;
98 gpx_free_cache(cache);
99 cache = next;
100 }
101
102 free(gpx);
103 }
104
105 void gpx_free_all(gpx_t *gpx) {
106 while(gpx) {
107 gpx_t *next = gpx->next;
108 gpx_free(gpx);
109 gpx = next;
110 }
111 }
112
113 static const char *cache_type_str[] = { "<Unknown>",
114 "Traditional Cache|Traditional|Geocache", "Multi-cache|Multi",
115 "Unknown Cache|Other",
116 "Virtual Cache|Virtual", "Webcam Cache|Webcam", "Event Cache|Event",
117 "Letterbox Hybrid", "Earthcache", "Wherigo Cache",
118 "Mega-Event Cache", "Cache In Trash Out Event",
119 ""};
120
121 static const char *cache_container_str[] = { "<Unknown>",
122 "Regular", "Small", "Micro", "Not chosen|Unknown",
123 "Other", "Large", "Virtual"
124 ""};
125
126 static const char *log_type_str[] = { "<Unknown>",
127 "Found it|Found", "Didn't find it|Not Found", "Owner Maintenance",
128 "Write Note|Note|Other",
129 "Post Reviewer Note", "Enable Listing", "Publish Listing", "Will Attend",
130 "Attended", "Webcam Photo taken", "Temporarily Disable Listing",
131 "Needs Maintenance", "Update Coordinates", "Unarchive",
132 "Needs Archived", "Archive",
133 ""};
134
135 static const char *wpt_sym_str[] = { "<Unknown>",
136 "Stages of a Multicache", "Parking Area", "Final Location",
137 "Question to Answer", "Trailhead", "Reference Point",
138 ""};
139
140 static int str_search(const char *pstr[], char *str, char *type) {
141 int i=0, ret = -1;
142
143 while(pstr[i+1][0]) {
144 char *p = (char*)pstr[i+1];
145
146 /* multiple substrings in pattern? */
147 while(strchr(p, '|')) {
148 if(!strncasecmp(p, str, strchr(p, '|')-p))
149 return i;
150
151 p = strchr(p, '|')+1;
152 }
153
154 if(!strcasecmp(p, str))
155 return i;
156
157 i++;
158 }
159
160 fprintf(stderr, "ERROR parsing \"%s\": Unknown \"%s\"\n", type, str);
161 return -1;
162 }
163
164 static tb_t *gpx_parse_gpx_wpt_tbs(xmlDocPtr doc, xmlNode *a_node) {
165 tb_t *tb_chain = NULL, *tb;
166 xmlNode *cur_node = NULL;
167
168 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
169 if (cur_node->type == XML_ELEMENT_NODE) {
170 if(strcasecmp(cur_node->name, "travelbug") == 0) {
171 xmlNode *sub_node = cur_node->children;
172
173 /* create a new log entry */
174 tb = malloc(sizeof(tb_t));
175 memset(tb, 0, sizeof(tb_t));
176 tb->ref = xmlGetProp(cur_node, "ref");
177 if(!tb->ref) tb->ref = strdup("<NONE>");
178
179 while (sub_node != NULL) {
180 if (sub_node->type == XML_ELEMENT_NODE) {
181 if(strcasecmp(sub_node->name, "name") == 0)
182 tb->name = xmlNodeListGetString(doc, sub_node->children, 1);
183 else
184 printf(" tb unused %s\n", sub_node->name);
185 }
186 sub_node = sub_node->next;
187 }
188
189 /* add tb to chain */
190 tb_t **cur = &tb_chain;
191 while(*cur && (strcmp(tb->ref, (*cur)->ref)<0))
192 cur = &((*cur)->next);
193
194 tb->next = *cur;
195 *cur = tb;
196 }
197 }
198 }
199 return tb_chain;
200 }
201
202 static int log_is_older(log_t *a, log_t *b) {
203 if(a->year < b->year) return TRUE;
204 else if(a->year == b->year) {
205 if(a->month < b->month) return TRUE;
206 else if(a->month == b->month) {
207 if(a->day < b->day) return TRUE;
208 }
209 }
210
211 return FALSE;
212 }
213
214 /* parse log entry */
215 static log_t *gpx_parse_gpx_wpt_logs(xmlDocPtr doc, xmlNode *a_node) {
216 log_t *log_chain = NULL, *log;
217 xmlNode *cur_node = NULL;
218
219 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
220 if (cur_node->type == XML_ELEMENT_NODE) {
221
222 if(strcasecmp(cur_node->name, "log") == 0) {
223 xmlNode *sub_node = cur_node->children;
224
225 /* create a new log entry */
226 log = malloc(sizeof(log_t));
227 memset(log, 0, sizeof(log_t));
228
229 while (sub_node != NULL) {
230 if (sub_node->type == XML_ELEMENT_NODE) {
231 char *str;
232
233 if((strcasecmp(sub_node->name, "date") == 0) ||
234 (strcasecmp(sub_node->name, "time") == 0)) {
235 if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
236 sscanf(str, "%d-%d-%d", &log->year, &log->month, &log->day);
237 xmlFree(str);
238 }
239 } else if(strcasecmp(sub_node->name, "type") == 0) {
240 if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
241 log->type = str_search(log_type_str, str, "log");
242 xmlFree(str);
243 }
244 } else if((strcasecmp(sub_node->name, "finder") == 0) ||
245 (strcasecmp(sub_node->name, "geocacher") == 0)) {
246 log->finder = xmlNodeListGetString(doc, sub_node->children, 1);
247 } else if(strcasecmp(sub_node->name, "text") == 0) {
248 log->text = xmlNodeListGetString(doc, sub_node->children, 1);
249 } else {
250 // printf(" log unused = %s\n", sub_node->name);
251 }
252 }
253 sub_node = sub_node->next;
254 }
255
256 /* add log to chain */
257 log_t **cur = &log_chain;
258 while(*cur && log_is_older(log, *cur))
259 cur = &((*cur)->next);
260
261 log->next = *cur;
262 *cur = log;
263 }
264 }
265 }
266 return log_chain;
267 }
268
269 int is_white(char c) {
270 return((c==' ')||(c=='\r')||(c=='\n'));
271 }
272
273 static int all_is_white(char *str) {
274 while(*str) {
275 if(!is_white(*str))
276 return FALSE;
277
278 str++;
279 }
280 return TRUE;
281 }
282
283 /* parse waypoint entry */
284 static void gpx_parse_gpx_wpt(gpx_t *gpx, xmlDocPtr doc, xmlNode *a_node,
285 pos_t *pos) {
286 xmlNode *cur_node = NULL;
287 cache_t **cache = &gpx->cache;
288
289 /* skip to end of list and create a new entry */
290 while(*cache)
291 cache = &(*cache)->next;
292
293 *cache = malloc(sizeof(cache_t));
294 memset(*cache, 0, sizeof(cache_t));
295
296 (*cache)->pos.lat = pos->lat;
297 (*cache)->pos.lon = pos->lon;
298
299 /* set some defaults */
300 (*cache)->type = CACHE_TYPE_UNKNOWN;
301 (*cache)->container = CACHE_CONT_UNKNOWN;
302 (*cache)->available = TRUE;
303
304 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
305 if (cur_node->type == XML_ELEMENT_NODE) {
306 char *prop, *str;
307
308 if(strcasecmp(cur_node->name, "name") == 0) {
309 (*cache)->id = xmlNodeListGetString(doc, cur_node->children, 1);
310 } else if(strcasecmp(cur_node->name, "url") == 0) {
311 (*cache)->url = xmlNodeListGetString(doc, cur_node->children, 1);
312
313 /* GC names them cache, OC names them geocache */
314 } else if((strcasecmp(cur_node->name, "cache") == 0) ||
315 (strcasecmp(cur_node->name, "geocache") == 0)) {
316
317 /* try to read "available" and "archived" marker */
318 if((prop = xmlGetProp(cur_node, "available"))) {
319 (*cache)->available = (strcasecmp(prop, "true") == 0);
320 xmlFree(prop);
321 }
322
323 if((prop = xmlGetProp(cur_node, "archived"))) {
324 (*cache)->archived = (strcasecmp(prop, "true") == 0);
325 xmlFree(prop);
326 }
327
328 /* think about the children only */
329 xmlNode *sub_node = cur_node->children;
330 while (sub_node != NULL) {
331 if (sub_node->type == XML_ELEMENT_NODE) {
332
333 if(strcasecmp(sub_node->name, "name") == 0) {
334 (*cache)->name =
335 xmlNodeListGetString(doc, sub_node->children, 1);
336 } else if(strcasecmp(sub_node->name, "owner") == 0) {
337 (*cache)->owner =
338 xmlNodeListGetString(doc, sub_node->children, 1);
339 } else if(strcasecmp(sub_node->name, "type") == 0) {
340 if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
341 (*cache)->type = str_search(cache_type_str, str, "cache type");
342 xmlFree(str);
343 }
344 } else if(strcasecmp(sub_node->name, "container") == 0) {
345 if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
346 (*cache)->container = str_search(cache_container_str, str,
347 "container");
348 xmlFree(str);
349 }
350 } else if((strcasecmp(sub_node->name, "short_description") == 0) ||
351 (strcasecmp(sub_node->name, "summary") == 0)) {
352 (*cache)->short_description =
353 xmlNodeListGetString(doc, sub_node->children, 1);
354 if((prop = xmlGetProp(sub_node, "html"))) {
355 (*cache)->short_is_html = (strcasecmp(prop, "true") == 0);
356 xmlFree(prop);
357 }
358 } else if((strcasecmp(sub_node->name, "long_description") == 0) ||
359 (strcasecmp(sub_node->name, "description") == 0)) {
360 (*cache)->long_description =
361 xmlNodeListGetString(doc, sub_node->children, 1);
362 if((prop = xmlGetProp(sub_node, "html"))) {
363 (*cache)->long_is_html = (strcasecmp(prop, "true") == 0);
364 xmlFree(prop);
365 }
366 } else if((strcasecmp(sub_node->name, "encoded_hints") == 0) ||
367 (strcasecmp(sub_node->name, "hints") == 0)) {
368 if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
369 /* often hints aren't more than just a bunch of blanks ... */
370 if(!all_is_white(str)) {
371 (*cache)->hint = str;
372 if((prop = xmlGetProp(sub_node, "html"))) {
373 (*cache)->hint_is_html = (strcasecmp(prop, "true") == 0);
374 xmlFree(prop);
375 }
376 } else
377 xmlFree(str);
378 }
379 } else if(strcasecmp(sub_node->name, "difficulty") == 0) {
380 if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
381 (*cache)->difficulty = g_ascii_strtod(str, NULL);
382 xmlFree(str);
383 }
384 } else if(strcasecmp(sub_node->name, "terrain") == 0) {
385 if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
386 (*cache)->terrain = g_ascii_strtod(str, NULL);
387 xmlFree(str);
388 }
389 } else if(strcasecmp(sub_node->name, "logs") == 0) {
390 (*cache)->log = gpx_parse_gpx_wpt_logs(doc, sub_node->children);
391 } else if(strcasecmp(sub_node->name, "travelbugs") == 0) {
392 (*cache)->tb = gpx_parse_gpx_wpt_tbs(doc, sub_node->children);
393 } else {
394 if((strcasecmp(sub_node->name, "country") != 0) &&
395 (strcasecmp(sub_node->name, "licence" /* OC */) != 0) &&
396 (strcasecmp(sub_node->name, "state") != 0) &&
397 (strcasecmp(sub_node->name, "locale" /* OC */) != 0) &&
398 (strcasecmp(sub_node->name, "placed_by" /* OC */) != 0)) {
399 if(str = xmlNodeListGetString(doc, sub_node->children, 1)) {
400 printf(" cache unused %s: %s\n", sub_node->name, str);
401 xmlFree(str);
402 }
403 }
404 }
405 }
406 sub_node = sub_node->next;
407 }
408 } else {
409 /* ignore some by purpose (some are parsed seperately below) */
410 if((strcasecmp(cur_node->name, "time") != 0) &&
411 (strcasecmp(cur_node->name, "desc") != 0) &&
412 (strcasecmp(cur_node->name, "urlname") != 0) &&
413 (strcasecmp(cur_node->name, "sym") != 0) &&
414 (strcasecmp(cur_node->name, "cmt") != 0) &&
415 (strcasecmp(cur_node->name, "src") != 0) &&
416 (strcasecmp(cur_node->name, "wptExtension") != 0) &&
417 (strcasecmp(cur_node->name, "type") != 0)) {
418
419 if(str = xmlNodeListGetString(doc, cur_node->children, 1)) {
420 printf(" wpt unused %s: %s\n", cur_node->name, str);
421 xmlFree(str);
422 }
423 }
424 }
425 }
426 }
427
428 /* special handling for opencaching.de caches */
429 if(strncasecmp((*cache)->id, "OC", 2) == 0) {
430 /* the html attributes are either missing or wrong on OC ... */
431 (*cache)->long_is_html = TRUE;
432 (*cache)->hint_is_html = TRUE;
433 // (*cache)->logs_are_html = TRUE;
434 }
435
436 /* neither geocaching.com GC* nor opencaching.com OC* nor */
437 /* geocaching australia GA* waypoint */
438 if((strncasecmp((*cache)->id, "GC", 2) != 0) &&
439 (strncasecmp((*cache)->id, "OC", 2) != 0) &&
440 (strncasecmp((*cache)->id, "GA", 2) != 0)) {
441 cache_t *parent = gpx->cache;
442
443 /* check if the gpx file contains a cache with matching name */
444 while(parent && strcasecmp(parent->id+2, (*cache)->id+2))
445 parent = parent->next;
446
447 if(parent && parent != *cache) {
448 wpt_t **wpt = &parent->wpt;
449 char *str;
450
451 /* search end of list */
452 while(*wpt && (strcmp((*wpt)->id, (*cache)->id)<0))
453 wpt = &(*wpt)->next;
454
455 *wpt = malloc(sizeof(wpt_t));
456 memset(*wpt, 0, sizeof(wpt_t));
457
458 /* transfer name to waypoint entry */
459 (*wpt)->id = (*cache)->id;
460 (*cache)->id = NULL;
461
462 (*wpt)->pos.lat = pos->lat;
463 (*wpt)->pos.lon = pos->lon;
464
465 /* re-parse entry */
466 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
467 if (cur_node->type == XML_ELEMENT_NODE) {
468 /* pos_t pos; */
469 if(strcasecmp(cur_node->name, "cmt") == 0) {
470 (*wpt)->cmt = xmlNodeListGetString(doc, cur_node->children, 1);
471 } else if(strcasecmp(cur_node->name, "desc") == 0) {
472 (*wpt)->desc = xmlNodeListGetString(doc, cur_node->children, 1);
473 } else if(strcasecmp(cur_node->name, "sym") == 0) {
474 if(str = xmlNodeListGetString(doc, cur_node->children, 1)) {
475 (*wpt)->sym = str_search(wpt_sym_str, str, "wpt sym");
476 xmlFree(str);
477 }
478 }
479 }
480 }
481
482 /* and just free the current cache entry as we now have used */
483 /* the data for a caches waypoint */
484 gpx_free_cache(*cache);
485 *cache = NULL;
486 } else {
487 /* if it doesn't have a name etc, it's probably not a real */
488 /* cache, so drop it */
489 if(!(*cache)->name || !(*cache)->id) {
490 printf("Orphaned waypoint: %s\n", (*cache)->id);
491 gpx_free_cache(*cache);
492 *cache = NULL;
493 }
494 }
495 } else {
496 /* this is known to be a geocache due to its waypoint name */
497 /* (gc*, oc*, ga*) and is thus forces to be an entry */
498 if(!(*cache)->name) {
499 (*cache)->name = malloc(strlen((*cache)->id) + sizeof("Unnamed ()")+1);
500 snprintf((*cache)->name, strlen((*cache)->id) + sizeof("Unnamed ()")+1,
501 "Unnamed(%s)", (*cache)->id);
502 }
503 }
504 }
505
506 void gpx_display_log(log_t *log) {
507 printf(" Log:\n");
508 printf(" date: %d.%d.%d\n", log->day, log->month, log->year);
509 printf(" type: %s\n", log_type_str[log->type+1]);
510 printf(" finder: %s\n", log->finder);
511 // printf(" text: %s\n", log->text);
512 }
513
514 void gpx_display_cache(cache_t *cache) {
515 log_t *log = cache->log;
516
517 printf("\nCache:\n");
518 printf(" id: %s\n", cache->id);
519 printf(" name: %s\n", cache->name);
520 printf(" latitude: %f\n", cache->pos.lat);
521 printf(" longitude: %f\n", cache->pos.lon);
522 printf(" owner: %s\n", cache->owner);
523 printf(" type: %s\n", cache_type_str[cache->type+1]);
524 printf(" container: %s\n", cache_container_str[cache->container+1]);
525 printf(" difficulty: %.1f\n", cache->difficulty);
526 printf(" terrain: %.1f\n", cache->terrain);
527 // printf(" short: %s\n", cache->short_description);
528 // printf(" long: %s\n", cache->long_description);
529 // printf(" hint: %s\n", cache->hint);
530
531 while(log) {
532 gpx_display_log(log);
533 log = log->next;
534 }
535 }
536
537 void gpx_display_all(gpx_t *gpx) {
538 while(gpx) {
539 cache_t *cache = gpx->cache;
540
541 printf("GPX name: %s\n", gpx->name);
542 printf("GPX desc: %s\n", gpx->desc);
543 printf("GPX date: %d.%d.%d\n", gpx->day, gpx->month, gpx->year);
544 while(cache) {
545 gpx_display_cache(cache);
546 cache = cache->next;
547 }
548 gpx = gpx->next;
549 }
550 }
551
552 /* parse gpx entry */
553 static void gpx_parse_gpx(gpx_t *gpx, xmlDocPtr doc, xmlNode * a_node) {
554 xmlNode *cur_node = NULL;
555
556 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
557 if (cur_node->type == XML_ELEMENT_NODE) {
558 /* some global information */
559 if(!gpx->name && strcasecmp(cur_node->name, "name") == 0) {
560 gpx->name = xmlNodeListGetString(doc, cur_node->children, 1);
561 } else if(!gpx->desc && strcasecmp(cur_node->name, "desc") == 0) {
562 gpx->desc = xmlNodeListGetString(doc, cur_node->children, 1);
563 } else if((strcasecmp(cur_node->name, "date") == 0) ||
564 (strcasecmp(cur_node->name, "time") == 0)) {
565 char *str;
566 if(str = xmlNodeListGetString(doc, cur_node->children, 1)) {
567 sscanf(str, "%d-%d-%d", &gpx->year, &gpx->month, &gpx->day);
568 xmlFree(str);
569 }
570 } else if(strcasecmp(cur_node->name, "wpt") == 0) {
571 char *str;
572 pos_t pos = { 0.0, 0.0 };
573
574 if((str = xmlGetProp(cur_node, "lat"))) {
575 pos.lat = g_ascii_strtod(str, NULL);
576 xmlFree(str);
577 }
578
579 if((str = xmlGetProp(cur_node, "lon"))) {
580 pos.lon = g_ascii_strtod(str, NULL);
581 xmlFree(str);
582 }
583
584 gpx_parse_gpx_wpt(gpx, doc, cur_node->children, &pos);
585 }
586 }
587 }
588 }
589
590 /* parse loc waypoint entry */
591 static void gpx_parse_loc_wpt(gpx_t *gpx, xmlDocPtr doc, xmlNode *a_node) {
592 xmlNode *cur_node = NULL;
593 cache_t **cache = &gpx->cache;
594
595 /* skip to end of list and create a new entry */
596 while(*cache)
597 cache = &(*cache)->next;
598
599 *cache = malloc(sizeof(cache_t));
600 memset(*cache, 0, sizeof(cache_t));
601
602 /* set some defaults */
603 (*cache)->type = CACHE_TYPE_UNKNOWN;
604 (*cache)->container = CACHE_CONT_UNKNOWN;
605 (*cache)->available = TRUE;
606
607 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
608 if (cur_node->type == XML_ELEMENT_NODE) {
609 char *str;
610
611 if(strcasecmp(cur_node->name, "name") == 0) {
612 (*cache)->name = xmlNodeListGetString(doc, cur_node->children, 1);
613 (*cache)->id = xmlGetProp(cur_node, "id");
614 } else if(strcasecmp(cur_node->name, "link") == 0) {
615 (*cache)->url =
616 xmlNodeListGetString(doc, cur_node->children, 1);
617 } else if(strcasecmp(cur_node->name, "type") == 0) {
618 if(str = xmlNodeListGetString(doc, cur_node->children, 1)) {
619 (*cache)->type = str_search(cache_type_str, str, "cache type");
620 xmlFree(str);
621 }
622 } else if(strcasecmp(cur_node->name, "coord") == 0) {
623 if((str = xmlGetProp(cur_node, "lat"))) {
624 (*cache)->pos.lat = g_ascii_strtod(str, NULL);
625 xmlFree(str);
626 }
627 if((str = xmlGetProp(cur_node, "lon"))) {
628 (*cache)->pos.lon = g_ascii_strtod(str, NULL);
629 xmlFree(str);
630 }
631 }
632 }
633 }
634 }
635
636 /* parse loc entry */
637 static void gpx_parse_loc(gpx_t *gpx, xmlDocPtr doc, xmlNode * a_node) {
638 xmlNode *cur_node = NULL;
639
640 for (cur_node = a_node; cur_node; cur_node = cur_node->next)
641 if (cur_node->type == XML_ELEMENT_NODE)
642 if(strcasecmp(cur_node->name, "waypoint") == 0)
643 gpx_parse_loc_wpt(gpx, doc, cur_node->children);
644 }
645
646 /* parse root element and search for "gpx" */
647 static gpx_t *gpx_parse_root(xmlDocPtr doc, xmlNode * a_node,
648 char *filename, gpx_t *in) {
649 gpx_t *gpx;
650 xmlNode *cur_node = NULL;
651
652 /* no gpx entry given, create a new one */
653 if(!in) {
654 /* allocate memory to hold gpx file description */
655 gpx = malloc(sizeof(gpx_t));
656 memset(gpx, 0, sizeof(gpx_t));
657 gpx->filename = strdup(filename);
658 } else
659 gpx = in;
660
661 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
662 if (cur_node->type == XML_ELEMENT_NODE) {
663 /* parse gpx file ... */
664 if(strcasecmp(cur_node->name, "gpx") == 0)
665 gpx_parse_gpx(gpx, doc, cur_node->children);
666 /* ... or loc file */
667 else if(strcasecmp(cur_node->name, "loc") == 0)
668 gpx_parse_loc(gpx, doc, cur_node->children);
669 }
670 }
671
672 /* check if a name has been set and use filename if not */
673 if(!in && !gpx->name) {
674 if(!gpx->desc) {
675 char *str = strrchr(filename, '/');
676 if(str) gpx->name = strdup(str+1);
677 else gpx->name = strdup(filename);
678 } else
679 gpx->name = strdup(gpx->desc);
680 }
681
682 return gpx;
683 }
684
685 static gpx_t *gpx_parse_doc(xmlDocPtr doc, char *filename, gpx_t *gpx_in) {
686 gpx_t *gpx;
687
688 /* Get the root element node */
689 xmlNode *root_element = xmlDocGetRootElement(doc);
690
691 gpx = gpx_parse_root(doc, root_element, filename, gpx_in);
692
693 /*free the document */
694 xmlFreeDoc(doc);
695
696 /*
697 * Free the global variables that may
698 * have been allocated by the parser.
699 */
700 xmlCleanupParser();
701
702 if(!gpx) return gpx_in;
703
704 return gpx;
705 }
706
707 static gpx_t *gpx_parse_file(char *filename) {
708 gpx_t *gpx = NULL;
709 xmlDoc *doc = NULL;
710
711 struct timeval start;
712 gettimeofday(&start, NULL);
713
714 LIBXML_TEST_VERSION;
715 #if 1
716 /* DOM parser */
717
718 /* parse the file and get the DOM */
719 if ((doc = xmlReadFile(filename, NULL, 0)) == NULL) {
720 xmlErrorPtr errP = xmlGetLastError();
721 errorf("While parsing \"%s\":\n\n%s", filename, errP->message);
722 return NULL;
723 }
724
725 gpx = gpx_parse_doc(doc, filename, NULL);
726 #else
727
728 #endif
729
730 struct timeval end;
731 gettimeofday(&end, NULL);
732
733 printf("total parse time for %s: %ldms\n", filename,
734 (end.tv_usec - start.tv_usec)/1000 +
735 (end.tv_sec - start.tv_sec)*1000);
736
737 /* check of there's a waypoints file (*-wpts.gpx) for this */
738 if(strrchr(filename, '.')) {
739 char *dot = strrchr(filename, '.');
740 char wpts_name[128];
741 *dot = 0;
742 snprintf(wpts_name, sizeof(wpts_name), "%s-wpts.gpx", filename);
743 *dot = '.';
744
745 if(g_file_test(wpts_name, G_FILE_TEST_EXISTS)) {
746
747 /* and do the same for this file again */
748 if((doc = xmlReadFile(wpts_name, NULL, 0)) == NULL) {
749 xmlErrorPtr errP = xmlGetLastError();
750 errorf("While parsing \"%s\":\n\n%s", wpts_name, errP->message);
751 } else
752 gpx = gpx_parse_doc(doc, filename, gpx);
753 }
754 }
755
756 return gpx;
757 }
758
759 static gpx_t *decompress_file(unzFile file, char *name, char *filename,
760 gpx_t *gpx_in) {
761 unz_file_info info;
762 gpx_t *gpx;
763
764 if((unzLocateFile(file, name, FALSE) != Z_OK) ||
765 (unzGetCurrentFileInfo(file, &info, NULL,0, NULL,0, NULL,0) != Z_OK) ||
766 (unzOpenCurrentFile(file) != UNZ_OK)) {
767
768 /* do not complain if we are processing a waypoints file */
769 if(!gpx_in)
770 errorf("Unable to locate/get info/open\n%s\ninside\n%s",
771 name, filename);
772 else
773 printf("Unable to locate/get info/open %s inside %s\n",
774 name, filename);
775
776 return gpx_in;
777 }
778
779 printf("file size is %d\n", info.uncompressed_size);
780
781 char *buffer = malloc(info.uncompressed_size);
782 if(!buffer) {
783 errorf("Out of memory while uncompressing file");
784 unzCloseCurrentFile(file);
785 return gpx_in;
786 }
787
788 if(unzReadCurrentFile(file, buffer, info.uncompressed_size) < 0) {
789 errorf("Read error on compressed file");
790 free(buffer);
791 unzCloseCurrentFile(file);
792 return gpx_in;
793 }
794
795 struct timeval start;
796 gettimeofday(&start, NULL);
797
798 /* fire up libxml */
799 LIBXML_TEST_VERSION;
800
801 xmlDoc *doc = NULL;
802 /* parse the file and get the DOM */
803 if ((doc = xmlReadMemory(buffer, info.uncompressed_size,
804 NULL, NULL, 0)) == NULL) {
805 xmlErrorPtr errP = xmlGetLastError();
806 errorf("While parsing \"%s\":\n\n%s", filename, errP->message);
807 free(buffer);
808 unzCloseCurrentFile(file);
809 return gpx_in;
810 }
811
812 printf("loaded successfully, parse ...\n");
813
814 gpx = gpx_parse_doc(doc, filename, gpx_in);
815
816 struct timeval end;
817 gettimeofday(&end, NULL);
818
819 printf("total parse time for %s: %ldms\n", filename,
820 (end.tv_usec - start.tv_usec)/1000 +
821 (end.tv_sec - start.tv_sec)*1000);
822
823 free(buffer);
824 unzCloseCurrentFile(file);
825 return gpx;
826 }
827
828 static gpx_t *decompress_zip(char *filename) {
829 char *gpx_name, *fbase;
830 gpx_t *gpx = NULL;
831
832 /* extract base name and allocate space for file names */
833 fbase = strrchr(filename, '/');
834 if(!fbase) fbase = filename;
835 else fbase++; /* skip '/' */
836 gpx_name = malloc(strlen(fbase)+strlen("-wpts")+1);
837
838 unzFile file = unzOpen(filename);
839 if(!file) {
840 errorf("Error opening file %s for unzip", filename);
841 free(gpx_name);
842 return NULL;
843 }
844
845 printf("ZIP file successfully opened\n");
846
847 /* try to open gpx file inside */
848 strcpy(gpx_name, fbase);
849 strcpy(gpx_name+strlen(gpx_name)-4, ".gpx");
850 printf("gpx file name is %s\n", gpx_name);
851
852 gpx = decompress_file(file, gpx_name, filename, NULL);
853
854 /* try to open -wpts.gpx file inside */
855 strcpy(gpx_name, fbase);
856 strcpy(gpx_name+strlen(gpx_name)-4, "-wpts.gpx");
857 printf("gpx wpts file name is %s\n", gpx_name);
858
859 gpx = decompress_file(file, gpx_name, filename, gpx);
860
861 unzClose(file);
862 free(gpx_name);
863 return gpx;
864 }
865
866 gpx_t *gpx_parse(char *filename) {
867 if((strlen(filename) > 4) &&
868 !strcasecmp(filename+strlen(filename)-4, ".zip")) {
869 printf("trying to load a zip file!\n");
870
871 return decompress_zip(filename);
872 }
873
874 return gpx_parse_file(filename);
875 }
876
877 /* scan entire directory */
878 gpx_t *gpx_parse_dir(char *dirname) {
879 GnomeVFSResult result;
880 GnomeVFSDirectoryHandle *handle;
881 GnomeVFSFileInfo *finfo = gnome_vfs_file_info_new();;
882
883 gpx_t *gpx = NULL;
884 xmlDoc *doc = NULL;
885 xmlNode *root_element = NULL;
886
887 LIBXML_TEST_VERSION;
888
889 result = gnome_vfs_directory_open(&handle, dirname,
890 GNOME_VFS_FILE_INFO_DEFAULT);
891
892 if(result != GNOME_VFS_OK) {
893 errorf("Unable to open directory \"%s\":\n%s",
894 dirname, gnome_vfs_result_to_string(result));
895
896 return NULL;
897 }
898
899 while(GNOME_VFS_OK == gnome_vfs_directory_read_next(handle, finfo)) {
900 if(finfo->type == GNOME_VFS_FILE_TYPE_REGULAR) {
901 char *ext = finfo->name+strlen(finfo->name)-4;
902
903 /* check if file ends with .gpx or .loc */
904 if((strcasecmp(ext, ".gpx") == 0) || (strcasecmp(ext, ".loc") == 0)) {
905 char *filename = malloc(strlen(dirname)+strlen(finfo->name)+2);
906
907 strcpy(filename, dirname);
908 if(strlastchr(filename) != '/')
909 strcat(filename, "/");
910 strcat(filename, finfo->name);
911
912 /* parse the file and get the DOM */
913 doc = xmlReadFile(filename, NULL, 0);
914
915 if (doc == NULL) {
916 xmlErrorPtr errP = xmlGetLastError();
917 errorf("While parsing \"%s\":\n\n%s", filename, errP->message);
918 } else {
919 /*Get the root element node */
920 root_element = xmlDocGetRootElement(doc);
921
922 gpx = gpx_parse_root(doc, root_element, filename, gpx);
923
924 xmlFreeDoc(doc);
925 xmlCleanupParser();
926 }
927
928 free(filename);
929 }
930 }
931 }
932
933 if(gpx) {
934 /* replace file name with directory name */
935 free(gpx->filename);
936 gpx->filename = strdup(dirname);
937
938 /* replace gpx name with directory name */
939 free(gpx->name);
940
941 /* retrieve pure dirname if possible */
942 char *n = strrchr(dirname, '/');
943 if(!n) n = dirname;
944 else n++;
945
946 // gpx->name = malloc(strlen("<DIR> ")+strlen(n)+1);
947 // strcpy(gpx->name, "<DIR> ");
948 // strcat(gpx->name, n);
949 gpx->name = strdup(n);
950 }
951
952 gnome_vfs_file_info_unref(finfo);
953 gnome_vfs_directory_close(handle);
954
955 return gpx;
956 }
957
958 /* return number of caches in given gpx file */
959 int gpx_total_caches(gpx_t *gpx) {
960 cache_t *cache = gpx->cache;
961 int num = 0;
962
963 while(cache) {
964 num++;
965 cache = cache->next;
966 }
967
968 return num;
969 }
970
971 int gpx_number_of_waypoints(wpt_t *wpt) {
972 int num = 0;
973
974 while(wpt) {
975 num++;
976 wpt = wpt->next;
977 }
978
979 return num;
980 }
981
982 int gpx_number_of_logs(log_t *log) {
983 int num = 0;
984
985 while(log) {
986 num++;
987 log = log->next;
988 }
989
990 return num;
991 }
992
993 int gpx_number_of_tbs(tb_t *tb) {
994 int num = 0;
995
996 while(tb) {
997 num++;
998 tb = tb->next;
999 }
1000
1001 return num;
1002 }
1003
1004 /* http://mathforum.org/library/drmath/view/55417.html */
1005 float gpx_pos_get_bearing(pos_t p1, pos_t p2) {
1006 /* convert to radians */
1007 p1.lat *= (M_PI/180.0); p1.lon *= (M_PI/180.0);
1008 p2.lat *= (M_PI/180.0); p2.lon *= (M_PI/180.0);
1009
1010 return fmodf(360.0 + (180.0/M_PI) *
1011 (atan2( sin(p2.lon - p1.lon) * cos(p2.lat),
1012 cos(p1.lat) * sin(p2.lat) -
1013 sin(p1.lat) * cos(p2.lat) * cos(p2.lon - p1.lon))),
1014 360.0);
1015 }
1016
1017 /* http://mathforum.org/library/drmath/view/51722.html */
1018 float gpx_pos_get_distance(pos_t p1, pos_t p2, int miles) {
1019 /* convert to radians */
1020 p1.lat *= (M_PI/180.0); p1.lon *= (M_PI/180.0);
1021 p2.lat *= (M_PI/180.0); p2.lon *= (M_PI/180.0);
1022
1023 float aob = acos(cos(p1.lat) * cos(p2.lat) * cos(p2.lon - p1.lon) +
1024 sin(p1.lat) * sin(p2.lat));
1025
1026 if(miles)
1027 return(aob * 3959.0); /* great circle radius in miles */
1028
1029 return(aob * 6371.0); /* great circle radius in kilometers */
1030 }
1031
1032 void gpx_pos_get_distance_str(char *str, int len,
1033 pos_t p1, pos_t p2, int mil) {
1034
1035 float dist = gpx_pos_get_distance(p1, p2, mil);
1036 distance_str(str, len, dist, mil);
1037 }
1038
1039 /* http://library.gnome.org/devel/gtk/unstable/GtkRadioButton.html */
1040 void gpx_sort(gpx_t *gpx, int by, pos_t *refpos) {
1041 cache_t **new;
1042 cache_t *cur = gpx->cache;
1043 int total = gpx_total_caches(gpx);
1044 float *dist_cache = malloc(total * sizeof(float));
1045
1046 gpx->cache = NULL; /* detach old chain */
1047 while(cur) {
1048 float cur_dist = gpx_pos_get_distance(*refpos, gpx_cache_pos(cur), 0);
1049 int cur_cnt = 0;
1050
1051 new = &(gpx->cache);
1052
1053 /* search for currect insertion point */
1054 while(*new && (dist_cache[cur_cnt] < cur_dist)) {
1055 new = &((*new)->next);
1056 cur_cnt++;
1057 }
1058
1059 /* save distance for further comparisons */
1060 memmove(dist_cache+cur_cnt+1, dist_cache+cur_cnt,
1061 sizeof(float)*(total-cur_cnt-1));
1062 dist_cache[cur_cnt++] = cur_dist;
1063
1064 cache_t *next = cur->next;
1065
1066 /* insert into "new" chain */
1067 cur->next = *new;
1068 *new = cur;
1069
1070 cur = next;
1071 }
1072
1073 free(dist_cache);
1074 }
1075
1076 gpx_t *gpx_cache2gpx(gpx_t *gpx, cache_t *search_cache) {
1077 while(gpx) {
1078 cache_t *cache = gpx->cache;
1079 while(cache) {
1080 if(cache == search_cache)
1081 return gpx;
1082
1083 cache = cache->next;
1084 }
1085 gpx = gpx->next;
1086 }
1087
1088 return NULL;
1089 }
1090
1091 /* since the actual cache position may be overridden, we */
1092 /* always access the position through this function */
1093 pos_t gpx_cache_pos(cache_t *cache) {
1094 if(cache->notes && cache->notes->override)
1095 return cache->notes->pos;
1096
1097 return cache->pos;
1098 }