Contents of /trunk/src/gpx.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 137 - (hide annotations)
Mon Oct 19 18:21:20 2009 UTC (14 years, 7 months ago) by harbaum
File MIME type: text/plain
File size: 38831 byte(s)
Links to gc.com everywhere
1 harbaum 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     void gpx_free_wpt(wpt_t *wpt) {
39     if(wpt->id) xmlFree(wpt->id);
40     if(wpt->cmt) xmlFree(wpt->cmt);
41     if(wpt->desc) xmlFree(wpt->desc);
42     free(wpt);
43     }
44    
45 harbaum 134 void gpx_free_user(user_t *user) {
46     if(user->name) xmlFree(user->name);
47     free(user);
48     }
49    
50 harbaum 1 void gpx_free_log(log_t *log) {
51 harbaum 137 if(log->finder) gpx_free_user(log->finder);
52 harbaum 1 if(log->text) xmlFree(log->text);
53     free(log);
54     }
55    
56     void gpx_free_tb(tb_t *tb) {
57     if(tb->name) xmlFree(tb->name);
58     if(tb->ref) xmlFree(tb->ref);
59     free(tb);
60     }
61    
62     void gpx_free_cache(cache_t *cache) {
63     log_t *log = cache->log;
64     wpt_t *wpt = cache->wpt;
65     tb_t *tb = cache->tb;
66    
67     if(cache->id) xmlFree(cache->id);
68     if(cache->name) xmlFree(cache->name);
69 harbaum 134 if(cache->owner) gpx_free_user(cache->owner);
70 harbaum 1 if(cache->short_description) xmlFree(cache->short_description);
71     if(cache->long_description) xmlFree(cache->long_description);
72     if(cache->hint) xmlFree(cache->hint);
73     if(cache->url) xmlFree(cache->url);
74    
75     /* free all logs */
76     while(log) { log_t *next = log->next; gpx_free_log(log); log = next; }
77    
78     /* free all waypoints */
79     while(wpt) { wpt_t *next = wpt->next; gpx_free_wpt(wpt); wpt = next; }
80    
81     /* free all tbs */
82     while(tb) { tb_t *next = tb->next; gpx_free_tb(tb); tb = next; }
83    
84     if(cache->notes) notes_free(cache->notes);
85    
86     free(cache);
87     }
88    
89     void gpx_free_caches(gpx_t *gpx) {
90     cache_t *cache = gpx->cache;
91    
92     /* free all caches */
93     while(cache) {
94     cache_t *next = cache->next;
95     gpx_free_cache(cache);
96     cache = next;
97     }
98    
99     gpx->cache = NULL;
100     }
101    
102     void gpx_free(gpx_t *gpx) {
103    
104     if(gpx->name) xmlFree(gpx->name);
105     if(gpx->desc) xmlFree(gpx->desc);
106     if(gpx->filename) free(gpx->filename);
107    
108     gpx_free_caches(gpx);
109    
110     free(gpx);
111     }
112    
113     void gpx_free_all(gpx_t *gpx) {
114     while(gpx) {
115     gpx_t *next = gpx->next;
116     gpx_free(gpx);
117     gpx = next;
118     }
119     }
120    
121     static const char *cache_type_str[] = { "<Unknown>",
122     "Traditional Cache|Traditional|Geocache", "Multi-cache|Multi",
123     "Unknown Cache|Other",
124     "Virtual Cache|Virtual", "Webcam Cache|Webcam", "Event Cache|Event|Geocoins:",
125     "Letterbox Hybrid|Letterbox", "Earthcache", "Wherigo Cache",
126     "Mega-Event Cache", "Cache In Trash Out Event",
127     ""};
128    
129     static const char *cache_container_str[] = { "<Unknown>",
130     "Regular", "Small", "Micro", "Not chosen|Unknown",
131     "Other", "Large", "Virtual"
132     ""};
133    
134     static const char *log_type_str[] = { "<Unknown>",
135     "Found it|Found", "Didn't find it|Not Found", "Owner Maintenance",
136     "Write Note|Note|Other",
137     "Post Reviewer Note", "Enable Listing", "Publish Listing", "Will Attend",
138     "Attended", "Webcam Photo taken",
139     "Temporarily Disable Listing|Cache Disabled!",
140     "Needs Maintenance", "Update Coordinates", "Unarchive|Archive (show)",
141     "Needs Archived", "Archive",
142     ""};
143    
144     static const char *wpt_sym_str[] = { "<Unknown>",
145     "Stages of a Multicache", "Parking Area", "Final Location",
146     "Question to Answer", "Trailhead", "Reference Point",
147     ""};
148    
149     #define DLG_DIV 10
150    
151     /* create the dialog box shown while loading in progress */
152     gpx_dialog_t *gpx_busy_dialog_new(GtkWidget *parent) {
153     #ifdef USE_MAEMO
154     gpx_dialog_t *dialog = malloc(sizeof(gpx_dialog_t));
155     memset(dialog, 0, sizeof(gpx_dialog_t));
156    
157     dialog->dialog = gtk_dialog_new();
158    
159     gtk_dialog_set_has_separator(GTK_DIALOG(dialog->dialog), FALSE);
160 harbaum 7 gtk_window_set_title(GTK_WINDOW(dialog->dialog), _("Loading"));
161 harbaum 1 gtk_window_set_default_size(GTK_WINDOW(dialog->dialog), 300, 10);
162    
163 harbaum 30 gtk_window_set_modal(GTK_WINDOW(dialog->dialog), TRUE);
164 harbaum 1 gtk_window_set_transient_for(GTK_WINDOW(dialog->dialog), GTK_WINDOW(parent));
165    
166     dialog->label = gtk_label_new("---");
167     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog->dialog)->vbox),
168     dialog->label);
169    
170     dialog->pbar = gtk_progress_bar_new();
171     gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(dialog->pbar),
172     0.0025 * DLG_DIV);
173    
174     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog->dialog)->vbox),
175     dialog->pbar);
176    
177     gtk_widget_show_all(dialog->dialog);
178    
179     return dialog;
180     #else
181     return NULL;
182     #endif
183     }
184    
185     void gpx_busy_dialog_destroy(gpx_dialog_t *dialog) {
186     if(!dialog) return;
187    
188     gtk_widget_destroy(dialog->dialog);
189     free(dialog);
190     }
191    
192     static void gpx_busy_dialog_progress(gpx_dialog_t *dialog) {
193     static int sub_dlg = 0;
194    
195     if(!dialog) return;
196    
197     if(sub_dlg++ >= DLG_DIV) {
198     sub_dlg = 0;
199    
200     gtk_progress_bar_pulse(GTK_PROGRESS_BAR(dialog->pbar));
201    
202     /* wait for main gui to appear */
203     while(gtk_events_pending())
204     gtk_main_iteration();
205     }
206     }
207    
208     static void gpx_busy_dialog_set(gpx_dialog_t *dialog, char *name) {
209     if(!dialog) return;
210    
211     if(strrchr(name, '/'))
212     name = strrchr(name, '/')+1;
213    
214     gtk_label_set_text(GTK_LABEL(dialog->label), name);
215    
216     /* wait for main gui to appear */
217     while(gtk_events_pending())
218     gtk_main_iteration();
219     }
220    
221     static int str_search(const char *pstr[], char *str, char *type) {
222     int i=0;
223    
224     while(pstr[i+1][0]) {
225     char *p = (char*)pstr[i+1];
226    
227     /* multiple substrings in pattern? */
228     while(strchr(p, '|')) {
229     if(!strncasecmp(p, str, strchr(p, '|')-p))
230     return i;
231    
232     p = strchr(p, '|')+1;
233     }
234    
235     if(!strcasecmp(p, str))
236     return i;
237    
238     i++;
239     }
240    
241     fprintf(stderr, "ERROR parsing \"%s\": Unknown \"%s\"\n", type, str);
242     return -1;
243     }
244    
245     static int log_is_older(log_t *a, log_t *b) {
246     if(a->year < b->year) return TRUE;
247     else if(a->year == b->year) {
248     if(a->month < b->month) return TRUE;
249     else if(a->month == b->month) {
250     if(a->day < b->day) return TRUE;
251     }
252     }
253    
254     return FALSE;
255     }
256    
257     int is_white(char c) {
258     return((c==' ')||(c=='\r')||(c=='\n'));
259     }
260    
261     static int all_is_white(char *str) {
262     while(*str) {
263     if(!is_white(*str))
264     return FALSE;
265    
266     str++;
267     }
268     return TRUE;
269     }
270    
271     void gpx_display_log(log_t *log) {
272     printf(" Log:\n");
273     printf(" date: %d.%d.%d\n", log->day, log->month, log->year);
274     printf(" type: %s\n", log_type_str[log->type+1]);
275 harbaum 137 printf(" finder: %s\n", log->finder->name);
276 harbaum 1 // printf(" text: %s\n", log->text);
277     }
278    
279     void gpx_display_cache(cache_t *cache) {
280     log_t *log = cache->log;
281    
282     printf("\nCache:\n");
283     printf(" id: %s\n", cache->id);
284     printf(" name: %s\n", cache->name);
285     printf(" latitude: %f\n", cache->pos.lat);
286     printf(" longitude: %f\n", cache->pos.lon);
287 harbaum 134 printf(" owner: %s\n", cache->owner->name);
288 harbaum 1 printf(" type: %s\n", cache_type_str[cache->type+1]);
289     printf(" container: %s\n", cache_container_str[cache->container+1]);
290     printf(" difficulty: %.1f\n", cache->difficulty);
291     printf(" terrain: %.1f\n", cache->terrain);
292     // printf(" short: %s\n", cache->short_description);
293     // printf(" long: %s\n", cache->long_description);
294     // printf(" hint: %s\n", cache->hint);
295    
296     while(log) {
297     gpx_display_log(log);
298     log = log->next;
299     }
300     }
301    
302     void gpx_display_all(gpx_t *gpx) {
303     while(gpx) {
304     cache_t *cache = gpx->cache;
305    
306     printf("GPX name: %s\n", gpx->name);
307     printf("GPX desc: %s\n", gpx->desc);
308     printf("GPX date: %d.%d.%d\n", gpx->day, gpx->month, gpx->year);
309     while(cache) {
310     gpx_display_cache(cache);
311     cache = cache->next;
312     }
313     gpx = gpx->next;
314     }
315     }
316    
317     static gint my_strcmp(const xmlChar *a, const xmlChar *b) {
318     if(!a && !b) return 0;
319     if(!a) return -1;
320     if(!b) return +1;
321     return strcmp((char*)a,(char*)b);
322     }
323    
324     static float xml_get_prop_float(xmlTextReaderPtr reader, char *name) {
325     float ret = NAN;
326     char *prop;
327     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST name))) {
328     ret = g_ascii_strtod(prop, NULL);
329     xmlFree(prop);
330     }
331     return ret;
332     }
333    
334 harbaum 134 static unsigned int xml_get_prop_id(xmlTextReaderPtr reader) {
335     unsigned int ret = 0;
336     char *prop;
337     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
338     ret = atoi(prop);
339     xmlFree(prop);
340     }
341     return ret;
342     }
343    
344 harbaum 1 static int xml_prop_is(xmlTextReaderPtr reader, char *name, char *value,
345     int def_value) {
346     int match = def_value;
347     char *prop;
348     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST name))) {
349     match = (strcasecmp(prop, value) == 0);
350     xmlFree(prop);
351     }
352     return match;
353     }
354    
355     /* skip current element incl. everything below (mainly for testing) */
356     /* returns FALSE if something failed */
357     static gboolean skip_element(xmlTextReaderPtr reader) {
358     g_assert(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT);
359     const xmlChar *name = xmlTextReaderConstName(reader);
360     g_assert(name);
361     int depth = xmlTextReaderDepth(reader);
362    
363     if(xmlTextReaderIsEmptyElement(reader))
364     return TRUE;
365    
366     int ret = xmlTextReaderRead(reader);
367     while((ret == 1) &&
368     ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
369     (xmlTextReaderDepth(reader) > depth) ||
370     (my_strcmp(xmlTextReaderConstName(reader), name) != 0))) {
371     ret = xmlTextReaderRead(reader);
372     }
373     return(ret == 1);
374     }
375    
376     static char *process_text(xmlTextReaderPtr reader) {
377     char *text = NULL;
378    
379 harbaum 101 if(!xmlTextReaderIsEmptyElement(reader)) {
380 harbaum 1
381 harbaum 101 int depth = xmlTextReaderDepth(reader);
382     int ret = xmlTextReaderRead(reader);
383     while((ret == 1) &&
384     ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
385     (xmlTextReaderDepth(reader) != depth))) {
386    
387     /* found a text fragment */
388     if((xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT) ||
389     (xmlTextReaderNodeType(reader) == XML_READER_TYPE_CDATA)) {
390     char *frag = (char*)xmlTextReaderConstValue(reader);
391    
392     if(!text) text = strdup(frag);
393     else {
394     char *old = text;
395     text = malloc(strlen(old) + strlen(frag) + 1);
396     strcpy(text, old);
397     strcat(text, frag);
398     free(old);
399     }
400 harbaum 1 }
401 harbaum 101 ret = xmlTextReaderRead(reader);
402 harbaum 1 }
403     }
404    
405     return text;
406     }
407    
408     static int xml_str_search(xmlTextReaderPtr reader,
409     const char *pstr[], char *type, int def) {
410     char *text = process_text(reader);
411     int result = def;
412     if(text) {
413     result = str_search(pstr, text, type);
414     free(text);
415     }
416     return result;
417     }
418    
419     static float xml_float(xmlTextReaderPtr reader, float def) {
420     char *text = process_text(reader);
421     float result = def;
422     if(text) {
423     result = g_ascii_strtod(text, NULL);
424     free(text);
425     }
426     return result;
427     }
428    
429     static void xml_get_date(xmlTextReaderPtr reader, int *year, int *month, int *day) {
430     char *str = process_text(reader);
431     if(str) {
432     sscanf(str, "%d-%d-%d", year, month, day);
433     free(str);
434     }
435     }
436    
437     static log_t *process_gpx_wpt_gc_logs_log(xmlTextReaderPtr reader) {
438    
439     if(xmlTextReaderIsEmptyElement(reader))
440     return NULL;
441    
442     /* create a new log entry */
443     log_t *log = malloc(sizeof(log_t));
444     memset(log, 0, sizeof(log_t));
445    
446     /* process all sub-nodes */
447     int depth = xmlTextReaderDepth(reader);
448     int ret = xmlTextReaderRead(reader);
449     while(ret == 1) {
450    
451     switch(xmlTextReaderNodeType(reader)) {
452     case XML_READER_TYPE_ELEMENT:
453     g_assert(xmlTextReaderDepth(reader) == depth+1);
454     char *name = (char*)xmlTextReaderConstName(reader);
455     if(strrchr(name, ':')) name = strrchr(name, ':')+1;
456     if(name) {
457     if((strcasecmp(name, "date") == 0) ||
458     (strcasecmp(name, "time") == 0)) {
459     xml_get_date(reader, &log->year, &log->month, &log->day);
460     } else if(strcasecmp(name, "type") == 0) {
461     log->type = xml_str_search(reader, log_type_str, "log", 0);
462     } else if((strcasecmp(name, "finder") == 0) ||
463     (strcasecmp(name, "geocacher") == 0)) {
464 harbaum 137 if(!log->finder) {
465     log->finder = g_new0(user_t, 1);
466     log->finder->name = process_text(reader);
467     log->finder->id = xml_get_prop_id(reader);
468     }
469 harbaum 1 } else if(strcasecmp(name, "text") == 0) {
470     if(!log->text) log->text = process_text(reader);
471     } else
472     skip_element(reader);
473     } else
474     skip_element(reader);
475     break;
476    
477     case XML_READER_TYPE_END_ELEMENT:
478     /* end element must be for the current element */
479     g_assert(xmlTextReaderDepth(reader) == depth);
480     return log;
481     break;
482    
483     default:
484     break;
485     }
486     ret = xmlTextReaderRead(reader);
487     }
488    
489 harbaum 74 gpx_free_log(log);
490     return NULL;
491 harbaum 1 }
492    
493     static log_t *process_gpx_wpt_gc_logs(xmlTextReaderPtr reader) {
494     log_t *log_chain = NULL;
495    
496     if(xmlTextReaderIsEmptyElement(reader))
497     return NULL;
498    
499     /* process all sub-nodes */
500     int depth = xmlTextReaderDepth(reader);
501     int ret = xmlTextReaderRead(reader);
502     while(ret == 1) {
503    
504     switch(xmlTextReaderNodeType(reader)) {
505     case XML_READER_TYPE_ELEMENT:
506     g_assert(xmlTextReaderDepth(reader) == depth+1);
507     char *name = (char*)xmlTextReaderConstName(reader);
508     if(strrchr(name, ':')) name = strrchr(name, ':')+1;
509     if(name) {
510     if(strcasecmp(name, "log") == 0) {
511     log_t *log = process_gpx_wpt_gc_logs_log(reader);
512     if(log) {
513     /* add log to chain */
514     log_t **cur = &log_chain;
515     while(*cur && log_is_older(log, *cur))
516     cur = &((*cur)->next);
517    
518     log->next = *cur;
519     *cur = log;
520     }
521     } else
522     skip_element(reader);
523     } else
524     skip_element(reader);
525     break;
526    
527     case XML_READER_TYPE_END_ELEMENT:
528     /* end element must be for the current element */
529     g_assert(xmlTextReaderDepth(reader) == depth);
530     return log_chain;
531     break;
532    
533     default:
534     break;
535     }
536     ret = xmlTextReaderRead(reader);
537     }
538    
539 harbaum 74 /* free the entire log chain */
540     while(log_chain) {
541     log_t *next = log_chain->next;
542     gpx_free_log(log_chain);
543     log_chain = next;
544     }
545    
546     return NULL;
547 harbaum 1 }
548    
549     static tb_t *process_gpx_wpt_gc_tbs_travelbug(xmlTextReaderPtr reader) {
550    
551     if(xmlTextReaderIsEmptyElement(reader))
552     return NULL;
553    
554     /* create a new tb entry */
555     tb_t *tb = malloc(sizeof(tb_t));
556     memset(tb, 0, sizeof(tb_t));
557    
558     char *prop;
559     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref")))
560     tb->ref = strdup(prop);
561     else
562     tb->ref = strdup("<NONE>");
563    
564 harbaum 133 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id")))
565     tb->id = atoi(prop);
566    
567 harbaum 1 /* process all sub-nodes */
568     int depth = xmlTextReaderDepth(reader);
569     int ret = xmlTextReaderRead(reader);
570     while(ret == 1) {
571    
572     switch(xmlTextReaderNodeType(reader)) {
573     case XML_READER_TYPE_ELEMENT:
574     g_assert(xmlTextReaderDepth(reader) == depth+1);
575     char *name = (char*)xmlTextReaderConstName(reader);
576     if(strrchr(name, ':')) name = strrchr(name, ':')+1;
577     if(name) {
578     if(strcasecmp(name, "name") == 0) {
579     if(!tb->name) tb->name = process_text(reader);
580     } else
581     skip_element(reader);
582     } else
583     skip_element(reader);
584     break;
585    
586     case XML_READER_TYPE_END_ELEMENT:
587     /* end element must be for the current element */
588     g_assert(xmlTextReaderDepth(reader) == depth);
589     return tb;
590     break;
591    
592     default:
593     break;
594     }
595     ret = xmlTextReaderRead(reader);
596     }
597    
598 harbaum 74 gpx_free_tb(tb);
599     return NULL;
600 harbaum 1 }
601    
602     static tb_t *process_gpx_wpt_gc_tbs(xmlTextReaderPtr reader) {
603     tb_t *tb = NULL, **tbP = &tb;
604    
605     if(xmlTextReaderIsEmptyElement(reader))
606     return tb;
607    
608     /* process all sub-nodes */
609     int depth = xmlTextReaderDepth(reader);
610     int ret = xmlTextReaderRead(reader);
611     while(ret == 1) {
612    
613     switch(xmlTextReaderNodeType(reader)) {
614     case XML_READER_TYPE_ELEMENT:
615     g_assert(xmlTextReaderDepth(reader) == depth+1);
616     char *name = (char*)xmlTextReaderConstName(reader);
617     if(strrchr(name, ':')) name = strrchr(name, ':')+1;
618     if(name) {
619     if(strcasecmp(name, "travelbug") == 0) {
620     *tbP = process_gpx_wpt_gc_tbs_travelbug(reader);
621     if(*tbP) tbP = &(*tbP)->next;
622     } else
623     skip_element(reader);
624     } else
625     skip_element(reader);
626     break;
627    
628     case XML_READER_TYPE_END_ELEMENT:
629     /* end element must be for the current element */
630     g_assert(xmlTextReaderDepth(reader) == depth);
631     return tb;
632     break;
633    
634     default:
635     break;
636     }
637     ret = xmlTextReaderRead(reader);
638     }
639    
640 harbaum 74 while(tb) {
641     tb_t *next = tb;
642     gpx_free_tb(tb);
643     tb = next;
644     }
645    
646     return NULL;
647 harbaum 1 }
648    
649     static void process_gpx_wpt_gc(xmlTextReaderPtr reader, cache_t *cache) {
650     cache->available = xml_prop_is(reader, "available", "true", TRUE);
651     cache->archived = xml_prop_is(reader, "archived", "true", FALSE);
652    
653     if(xmlTextReaderIsEmptyElement(reader))
654     return;
655    
656     /* process all sub-nodes */
657     int depth = xmlTextReaderDepth(reader);
658     int ret = xmlTextReaderRead(reader);
659     while(ret == 1) {
660    
661     switch(xmlTextReaderNodeType(reader)) {
662     case XML_READER_TYPE_ELEMENT:
663     g_assert(xmlTextReaderDepth(reader) == depth+1);
664     char *name = (char*)xmlTextReaderConstName(reader);
665     if(strrchr(name, ':')) name = strrchr(name, ':')+1;
666     if(name) {
667     if(strcasecmp(name, "name") == 0) {
668     if(!cache->name) cache->name = process_text(reader);
669     } else if(strcasecmp(name, "owner") == 0) {
670 harbaum 134 if(!cache->owner) {
671     cache->owner = g_new0(user_t, 1);
672     cache->owner->name = process_text(reader);
673     cache->owner->id = xml_get_prop_id(reader);
674     }
675 harbaum 1 } else if(strcasecmp(name, "type") == 0) {
676     cache->type = xml_str_search(reader, cache_type_str,
677     "cache type", CACHE_TYPE_UNKNOWN);
678     } else if(strcasecmp(name, "container") == 0) {
679     cache->container = xml_str_search(reader, cache_container_str,
680     "container", CACHE_CONT_UNKNOWN);
681     } else if((strcasecmp(name, "short_description") == 0) ||
682     (strcasecmp(name, "summary") == 0)) {
683     if(!cache->short_description) {
684     cache->short_description = process_text(reader);
685     cache->short_is_html = xml_prop_is(reader, "html", "true", FALSE);
686     }
687     } else if((strcasecmp(name, "long_description") == 0) ||
688     (strcasecmp(name, "description") == 0)) {
689     if(!cache->long_description) {
690     cache->long_description = process_text(reader);
691     cache->long_is_html = xml_prop_is(reader, "html", "true", FALSE);
692     }
693     } else if((strcasecmp(name, "encoded_hints") == 0) ||
694     (strcasecmp(name, "hints") == 0)) {
695     if(!cache->hint) {
696     cache->hint = process_text(reader);
697 harbaum 101
698 harbaum 1 /* often hints aren't more than just a bunch of blanks ... */
699     if(cache->hint && all_is_white(cache->hint)) {
700     free(cache->hint);
701     cache->hint = NULL;
702     } else
703     cache->hint_is_html = xml_prop_is(reader, "html", "true", FALSE);
704     }
705     } else if(strcasecmp(name, "difficulty") == 0) {
706     cache->difficulty = xml_float(reader, 0.0);
707     } else if(strcasecmp(name, "terrain") == 0) {
708     cache->terrain = xml_float(reader, 0.0);
709     } else if(strcasecmp(name, "logs") == 0) {
710     if(!cache->log) cache->log = process_gpx_wpt_gc_logs(reader);
711     } else if(strcasecmp(name, "travelbugs") == 0) {
712     if(!cache->tb) cache->tb = process_gpx_wpt_gc_tbs(reader);
713     } else {
714     // printf("unhandled item found: gpx/wpt/cache/%s\n", name);
715     skip_element(reader);
716     }
717     } else
718     skip_element(reader);
719     break;
720    
721     case XML_READER_TYPE_END_ELEMENT:
722     /* end element must be for the current element */
723     g_assert(xmlTextReaderDepth(reader) == depth);
724     return;
725     break;
726    
727     default:
728     break;
729     }
730     ret = xmlTextReaderRead(reader);
731     }
732     }
733    
734     /* parse waypoint entry */
735     static cache_t *process_gpx_wpt(xmlTextReaderPtr reader, gpx_dialog_t *dialog,
736     gpx_t *gpx) {
737     char *cmt = NULL, *desc = NULL;
738     char *sym = NULL;
739    
740     gpx_busy_dialog_progress(dialog);
741    
742     if(xmlTextReaderIsEmptyElement(reader))
743     return NULL;
744    
745     cache_t *cache = malloc(sizeof(cache_t));
746     memset(cache, 0, sizeof(cache_t));
747    
748     /* set some defaults */
749     cache->type = CACHE_TYPE_UNKNOWN;
750     cache->container = CACHE_CONT_UNKNOWN;
751     cache->available = TRUE;
752    
753     /* parse attributes */
754     cache->pos.lat = xml_get_prop_float(reader, "lat");
755     cache->pos.lon = xml_get_prop_float(reader, "lon");
756    
757     /* process all sub-nodes */
758     int depth = xmlTextReaderDepth(reader);
759     int ret = xmlTextReaderRead(reader);
760     while(ret == 1) {
761    
762     switch(xmlTextReaderNodeType(reader)) {
763     case XML_READER_TYPE_ELEMENT:
764     g_assert(xmlTextReaderDepth(reader) == depth+1);
765     char *name = (char*)xmlTextReaderConstName(reader);
766    
767     if(strrchr(name, ':')) name = strrchr(name, ':')+1;
768    
769     if(name) {
770     if(strcasecmp(name, "name") == 0) {
771     if(!cache->id) cache->id = process_text(reader);
772     } else if(strcasecmp(name, "url") == 0) {
773     if(!cache->url) cache->url = process_text(reader);
774     } else if((strcasecmp(name, "cache") == 0) ||
775     (strcasecmp(name, "geocache") == 0)) {
776     process_gpx_wpt_gc(reader, cache);
777    
778     /* the following are used if the current entry is a waypoint */
779     } else if(strcasecmp(name, "cmt") == 0) {
780     if(!cmt) cmt = process_text(reader);
781     } else if(strcasecmp(name, "desc") == 0) {
782     if(!desc) desc = process_text(reader);
783     } else if(strcasecmp(name, "sym") == 0) {
784     if(!sym) sym = process_text(reader);
785     } else {
786     skip_element(reader);
787     }
788     } else
789     skip_element(reader);
790     break;
791    
792     case XML_READER_TYPE_END_ELEMENT:
793     /* end element must be for the current element */
794     g_assert(xmlTextReaderDepth(reader) == depth);
795    
796     /* ------------ cleanup -------------- */
797    
798     /* special handling for opencaching.de caches */
799     if(cache->id && strncasecmp(cache->id, "OC", 2) == 0) {
800     /* the html attributes are either missing or wrong on OC ... */
801     cache->long_is_html = TRUE;
802     cache->hint_is_html = TRUE;
803     cache->logs_are_html = TRUE;
804     }
805    
806     /* neither geocaching.com GC* nor opencaching.com OC* nor */
807     /* geocaching australia GA* waypoint */
808     if(cache->id &&
809 harbaum 14 (strncasecmp(cache->id, "__", 2) != 0) &&
810 harbaum 1 (strncasecmp(cache->id, "GC", 2) != 0) &&
811     (strncasecmp(cache->id, "OC", 2) != 0) &&
812     (strncasecmp(cache->id, "GA", 2) != 0)) {
813     cache_t *parent = gpx->cache;
814    
815     /* check if the gpx file contains a cache with matching name */
816     while(parent && strcasecmp(parent->id+2, cache->id+2))
817     parent = parent->next;
818    
819     if(parent && parent != cache) {
820     wpt_t **wpt = &parent->wpt;
821    
822     /* search end of list */
823     while(*wpt && (strcmp((*wpt)->id, cache->id)<0))
824     wpt = &(*wpt)->next;
825    
826     *wpt = malloc(sizeof(wpt_t));
827     memset(*wpt, 0, sizeof(wpt_t));
828    
829     /* transfer name to waypoint entry */
830     (*wpt)->id = cache->id;
831     cache->id = NULL;
832    
833     (*wpt)->pos.lat = cache->pos.lat;
834     (*wpt)->pos.lon = cache->pos.lon;
835    
836     (*wpt)->cmt = cmt; cmt = NULL;
837     (*wpt)->desc = desc; desc = NULL;
838     (*wpt)->sym = str_search(wpt_sym_str, sym, "wpt sym");
839    
840     /* and just free the current cache entry as we now have used */
841     /* the data for a caches waypoint */
842     gpx_free_cache(cache);
843     cache = NULL;
844     } else {
845     /* if it doesn't have a name etc, it's probably not a real */
846     /* cache, so drop it */
847     if(!cache->name || !cache->id) {
848     printf("Orphaned waypoint: %s\n", cache->id);
849     gpx_free_cache(cache);
850     cache = NULL;
851     }
852     }
853     } else {
854     if(!cache->id)
855     cache->id = g_strdup_printf("NO ID");
856    
857     /* this is known to be a geocache due to its waypoint name */
858     /* (gc*, oc*, ga*) and is thus forces to be an entry */
859     if(!cache->name)
860     cache->name = g_strdup_printf("Unnamed(%s)", cache->id);
861     }
862    
863     if(desc) free(desc);
864     if(cmt) free(cmt);
865     if(sym) free(sym);
866    
867     return cache;
868     break;
869    
870     default:
871     break;
872     }
873     ret = xmlTextReaderRead(reader);
874     }
875    
876 harbaum 74 gpx_free_cache(cache);
877     return NULL;
878 harbaum 1 }
879    
880 harbaum 74 static gboolean process_gpx(xmlTextReaderPtr reader, gpx_dialog_t *dialog,
881     gpx_t *gpx) {
882 harbaum 1
883     /* no attributes of interest */
884    
885     /* the following might be optimized for speed reasons! */
886    
887     /* find end of cache chain */
888     cache_t **cache = &gpx->cache;
889     while(*cache) cache = &(*cache)->next;
890    
891     const xmlChar *name = xmlTextReaderConstName(reader);
892 harbaum 74 if(!name) return FALSE;
893 harbaum 1
894     /* read next node */
895     int ret = xmlTextReaderRead(reader);
896     while(ret == 1) {
897    
898     switch(xmlTextReaderNodeType(reader)) {
899     case XML_READER_TYPE_ELEMENT:
900    
901     g_assert(xmlTextReaderDepth(reader) == 1);
902     char *name = (char*)xmlTextReaderConstName(reader);
903     if(name && !gpx->name && strcasecmp(name, "name") == 0) {
904     gpx->name = process_text(reader);
905     } else if(name && !gpx->desc && strcasecmp(name, "desc") == 0) {
906     gpx->desc = process_text(reader);
907     } else if(name && ((strcasecmp(name, "time") == 0) ||
908     (strcasecmp(name, "date") == 0))) {
909     xml_get_date(reader, &gpx->year, &gpx->month, &gpx->day);
910     } else if(name && strcasecmp(name, "wpt") == 0) {
911     *cache = process_gpx_wpt(reader, dialog, gpx);
912     if(*cache) cache = &(*cache)->next;
913     } else {
914     // printf("something unknown (%s) found\n", name);
915     skip_element(reader);
916     }
917     break;
918    
919     case XML_READER_TYPE_END_ELEMENT:
920     /* end element must be for the current element */
921     g_assert(xmlTextReaderDepth(reader) == 0);
922 harbaum 74 return TRUE;
923 harbaum 1 break;
924    
925     default:
926     break;
927     }
928     ret = xmlTextReaderRead(reader);
929     }
930    
931 harbaum 74 return FALSE;
932 harbaum 1 }
933    
934     /* parse loc waypoint entry */
935     static cache_t *process_loc_waypoint(xmlTextReaderPtr reader,
936     gpx_dialog_t *dialog) {
937    
938     gpx_busy_dialog_progress(dialog);
939    
940     if(xmlTextReaderIsEmptyElement(reader))
941     return NULL;
942    
943     cache_t *cache = malloc(sizeof(cache_t));
944     memset(cache, 0, sizeof(cache_t));
945    
946     /* set some defaults */
947     cache->type = CACHE_TYPE_TRADITIONAL;
948     cache->container = CACHE_CONT_UNKNOWN;
949     cache->available = TRUE;
950    
951     /* process all sub-nodes */
952     int depth = xmlTextReaderDepth(reader);
953     int ret = xmlTextReaderRead(reader);
954     while(ret == 1) {
955    
956     switch(xmlTextReaderNodeType(reader)) {
957     case XML_READER_TYPE_ELEMENT:
958     g_assert(xmlTextReaderDepth(reader) == depth+1);
959     char *name = (char*)xmlTextReaderConstName(reader);
960     if(strrchr(name, ':')) name = strrchr(name, ':')+1;
961    
962     if(name) {
963     if(strcasecmp(name, "name") == 0) {
964     cache->id = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id");
965     cache->name = process_text(reader);
966     } else if(strcasecmp(name, "link") == 0) {
967     cache->url = process_text(reader);
968     } else if(strcasecmp(name, "coord") == 0) {
969     cache->pos.lat = xml_get_prop_float(reader, "lat");
970     cache->pos.lon = xml_get_prop_float(reader, "lon");
971     skip_element(reader);
972     } else
973     skip_element(reader);
974     } else
975     skip_element(reader);
976     break;
977    
978     case XML_READER_TYPE_END_ELEMENT:
979     /* end element must be for the current element */
980     g_assert(xmlTextReaderDepth(reader) == depth);
981     return cache;
982     break;
983    
984     default:
985     break;
986     }
987     ret = xmlTextReaderRead(reader);
988     }
989    
990     g_assert(0);
991     return NULL;
992     }
993    
994     static void process_loc(xmlTextReaderPtr reader, gpx_dialog_t *dialog,
995     gpx_t *gpx) {
996    
997     /* find end of cache chain */
998     cache_t **cache = &gpx->cache;
999     while(*cache) cache = &(*cache)->next;
1000    
1001     const xmlChar *name = xmlTextReaderConstName(reader);
1002     g_assert(name);
1003    
1004     /* read next node */
1005     int ret = xmlTextReaderRead(reader);
1006     while(ret == 1) {
1007    
1008     switch(xmlTextReaderNodeType(reader)) {
1009     case XML_READER_TYPE_ELEMENT:
1010    
1011     g_assert(xmlTextReaderDepth(reader) == 1);
1012     char *name = (char*)xmlTextReaderConstName(reader);
1013    
1014     if(name && strcasecmp(name, "waypoint") == 0) {
1015     *cache = process_loc_waypoint(reader, dialog);
1016     if(*cache) cache = &(*cache)->next;
1017     } else
1018     skip_element(reader);
1019     break;
1020    
1021     case XML_READER_TYPE_END_ELEMENT:
1022     /* end element must be for the current element */
1023     g_assert(xmlTextReaderDepth(reader) == 0);
1024     return;
1025     break;
1026    
1027     default:
1028     break;
1029     }
1030     ret = xmlTextReaderRead(reader);
1031     }
1032    
1033     g_assert(0);
1034     return;
1035     }
1036    
1037     static gpx_t *process_root(xmlTextReaderPtr reader, gpx_dialog_t *dialog,
1038     char *fname, gpx_t *in) {
1039    
1040     /* no gpx entry given, create a new one */
1041     gpx_t *gpx = NULL;
1042     if(!in) {
1043     /* allocate memory to hold gpx file description */
1044     gpx = malloc(sizeof(gpx_t));
1045     memset(gpx, 0, sizeof(gpx_t));
1046     gpx->filename = strdup(fname);
1047     } else
1048     gpx = in;
1049    
1050     int ret = xmlTextReaderRead(reader);
1051     while(ret == 1) {
1052     switch(xmlTextReaderNodeType(reader)) {
1053     case XML_READER_TYPE_ELEMENT:
1054     g_assert(xmlTextReaderDepth(reader) == 0);
1055     char *name = (char*)xmlTextReaderConstName(reader);
1056     if(name && strcasecmp(name, "gpx") == 0) {
1057     process_gpx(reader, dialog, gpx);
1058     } else if(name && strcasecmp(name, "loc") == 0) {
1059     process_loc(reader, dialog, gpx);
1060     } else {
1061     printf("something unknown found\n");
1062     skip_element(reader);
1063     }
1064     break;
1065    
1066     case XML_READER_TYPE_END_ELEMENT:
1067     /* end element must be for the current element */
1068     g_assert(xmlTextReaderDepth(reader) == 0);
1069     ret = -1;
1070     break;
1071    
1072     default:
1073     break;
1074     }
1075    
1076     if(ret == 1)
1077     ret = xmlTextReaderRead(reader);
1078     }
1079    
1080     /* check if a name has been set and use filename if not */
1081     if(!in && !gpx->name) {
1082     if(!gpx->desc) {
1083     char *str = strrchr(fname, '/');
1084     if(str) gpx->name = strdup(str+1);
1085     else gpx->name = strdup(fname);
1086     } else
1087     gpx->name = strdup(gpx->desc);
1088     }
1089    
1090     return gpx;
1091     }
1092    
1093     static gpx_t *gpx_parse_file(gpx_dialog_t *dialog, char *filename) {
1094     gpx_t *gpx = NULL;
1095    
1096     LIBXML_TEST_VERSION;
1097    
1098     gpx_busy_dialog_set(dialog, filename);
1099    
1100     xmlTextReaderPtr reader = xmlReaderForFile(filename, NULL, 0);
1101     if (reader != NULL) {
1102     gpx = process_root(reader, dialog, filename, NULL);
1103     xmlFreeTextReader(reader);
1104     } else {
1105     fprintf(stderr, "Unable to open %s\n", filename);
1106     }
1107    
1108     /* check of there's a waypoints file (*-wpts.gpx) for this */
1109     if(strrchr(filename, '.')) {
1110     char *dot = strrchr(filename, '.');
1111     char wpts_name[128];
1112     *dot = 0;
1113     snprintf(wpts_name, sizeof(wpts_name), "%s-wpts.gpx", filename);
1114     *dot = '.';
1115     if(g_file_test(wpts_name, G_FILE_TEST_EXISTS)) {
1116     xmlTextReaderPtr reader = xmlReaderForFile(wpts_name, NULL, 0);
1117     if (reader != NULL) {
1118     gpx = process_root(reader, dialog, wpts_name, gpx);
1119     xmlFreeTextReader(reader);
1120     } else {
1121     fprintf(stderr, "Unable to open %s\n", filename);
1122     }
1123     }
1124     }
1125    
1126     return gpx;
1127     }
1128    
1129     static gpx_t *decompress_file(unzFile file, gpx_dialog_t *dialog,
1130     char *name, char *filename,
1131     gpx_t *gpx_in) {
1132     unz_file_info info;
1133     gpx_t *gpx = NULL;
1134    
1135     gpx_busy_dialog_set(dialog, name);
1136    
1137     if((unzLocateFile(file, name, FALSE) != Z_OK) ||
1138     (unzGetCurrentFileInfo(file, &info, NULL,0, NULL,0, NULL,0) != Z_OK) ||
1139     (unzOpenCurrentFile(file) != UNZ_OK)) {
1140    
1141     /* do not complain if we are processing a waypoints file */
1142     if(!gpx_in)
1143     errorf("Unable to locate/get info/open\n%s\ninside\n%s",
1144     name, filename);
1145     else
1146     printf("Unable to locate/get info/open %s inside %s\n",
1147     name, filename);
1148    
1149     return gpx_in;
1150     }
1151    
1152     printf("file size is %ld\n", info.uncompressed_size);
1153    
1154     char *buffer = malloc(info.uncompressed_size);
1155     if(!buffer) {
1156     errorf("Out of memory while uncompressing file");
1157     unzCloseCurrentFile(file);
1158     return gpx_in;
1159     }
1160    
1161     if(unzReadCurrentFile(file, buffer, info.uncompressed_size) < 0) {
1162     errorf("Read error on compressed file");
1163     free(buffer);
1164     unzCloseCurrentFile(file);
1165     return gpx_in;
1166     }
1167    
1168     /* fire up libxml */
1169     LIBXML_TEST_VERSION;
1170    
1171     xmlTextReaderPtr reader =
1172     xmlReaderForMemory(buffer, info.uncompressed_size,
1173     NULL, NULL, 0);
1174     if (reader != NULL) {
1175     gpx = process_root(reader, dialog, filename, gpx_in);
1176     xmlFreeTextReader(reader);
1177     } else {
1178     fprintf(stderr, "Unable to open %s\n", filename);
1179     }
1180    
1181     free(buffer);
1182     unzCloseCurrentFile(file);
1183     return gpx;
1184     }
1185    
1186     static gpx_t *decompress_zip(gpx_dialog_t *dialog, char *filename) {
1187     char *gpx_name, *fbase;
1188     gpx_t *gpx = NULL;
1189    
1190     /* extract base name and allocate space for file names */
1191     fbase = strrchr(filename, '/');
1192     if(!fbase) fbase = filename;
1193     else fbase++; /* skip '/' */
1194     gpx_name = malloc(strlen(fbase)+strlen("-wpts")+1);
1195    
1196     unzFile file = unzOpen(filename);
1197     if(!file) {
1198     errorf("Error opening file %s for unzip", filename);
1199     free(gpx_name);
1200     return NULL;
1201     }
1202    
1203     printf("ZIP file successfully opened\n");
1204    
1205     /* try to open gpx file inside */
1206     strcpy(gpx_name, fbase);
1207     strcpy(gpx_name+strlen(gpx_name)-4, ".gpx");
1208     printf("gpx file name is %s\n", gpx_name);
1209    
1210     gpx = decompress_file(file, dialog, gpx_name, filename, NULL);
1211    
1212     /* try to open -wpts.gpx file inside */
1213     strcpy(gpx_name, fbase);
1214     strcpy(gpx_name+strlen(gpx_name)-4, "-wpts.gpx");
1215     printf("gpx wpts file name is %s\n", gpx_name);
1216    
1217     gpx = decompress_file(file, dialog, gpx_name, filename, gpx);
1218    
1219     unzClose(file);
1220     free(gpx_name);
1221     return gpx;
1222     }
1223    
1224     gpx_t *gpx_parse(gpx_dialog_t *dialog, char *filename) {
1225     gpx_t *gpx = NULL;
1226    
1227     /* show busy dialog */
1228     printf("load file %s\n", filename);
1229    
1230     if((strlen(filename) > 4) &&
1231     !strcasecmp(filename+strlen(filename)-4, ".zip")) {
1232     printf("trying to load a zip file!\n");
1233    
1234     gpx = decompress_zip(dialog, filename);
1235     } else
1236     gpx = gpx_parse_file(dialog, filename);
1237    
1238     return gpx;
1239     }
1240    
1241     /* scan entire directory */
1242     gpx_t *gpx_parse_dir(gpx_dialog_t *dialog, char *dirname) {
1243     GnomeVFSResult result;
1244     GnomeVFSDirectoryHandle *handle;
1245     GnomeVFSFileInfo *finfo = gnome_vfs_file_info_new();;
1246    
1247     gpx_t *gpx = NULL;
1248    
1249     /* show busy dialog */
1250     printf("load dir %s\n", dirname);
1251     gpx_busy_dialog_set(dialog, dirname);
1252    
1253     LIBXML_TEST_VERSION;
1254    
1255     result = gnome_vfs_directory_open(&handle, dirname,
1256     GNOME_VFS_FILE_INFO_DEFAULT);
1257    
1258     if(result != GNOME_VFS_OK) {
1259     errorf("Unable to open directory \"%s\":\n%s",
1260     dirname, gnome_vfs_result_to_string(result));
1261     return NULL;
1262     }
1263    
1264     while(GNOME_VFS_OK == gnome_vfs_directory_read_next(handle, finfo)) {
1265     if(finfo->type == GNOME_VFS_FILE_TYPE_REGULAR) {
1266     char *ext = finfo->name+strlen(finfo->name)-4;
1267    
1268     /* check if file ends with .gpx or .loc */
1269     if((strcasecmp(ext, ".gpx") == 0) || (strcasecmp(ext, ".loc") == 0)) {
1270     char *filename = malloc(strlen(dirname)+strlen(finfo->name)+2);
1271    
1272     strcpy(filename, dirname);
1273     if(strlastchr(filename) != '/')
1274     strcat(filename, "/");
1275     strcat(filename, finfo->name);
1276    
1277     xmlTextReaderPtr reader = xmlReaderForFile(filename, NULL, 0);
1278     if (reader != NULL) {
1279     gpx = process_root(reader, dialog, filename, gpx);
1280     xmlFreeTextReader(reader);
1281     } else {
1282     fprintf(stderr, "Unable to open %s\n", filename);
1283     }
1284    
1285     free(filename);
1286     }
1287     }
1288     }
1289    
1290     if(gpx) {
1291     /* replace file name with directory name */
1292     free(gpx->filename);
1293     gpx->filename = strdup(dirname);
1294    
1295     /* replace gpx name with directory name */
1296     free(gpx->name);
1297    
1298     /* retrieve pure dirname if possible */
1299     char *n = strrchr(dirname, '/');
1300     if(!n) n = dirname;
1301     else n++;
1302    
1303     // gpx->name = malloc(strlen("<DIR> ")+strlen(n)+1);
1304     // strcpy(gpx->name, "<DIR> ");
1305     // strcat(gpx->name, n);
1306     gpx->name = strdup(n);
1307     }
1308    
1309     gnome_vfs_file_info_unref(finfo);
1310     gnome_vfs_directory_close(handle);
1311    
1312     return gpx;
1313     }
1314    
1315     /* return number of caches in given gpx file */
1316     int gpx_total_caches(gpx_t *gpx) {
1317     cache_t *cache = gpx->cache;
1318     int num = 0;
1319    
1320     while(cache) {
1321     num++;
1322     cache = cache->next;
1323     }
1324    
1325     return num;
1326     }
1327    
1328     int gpx_number_of_waypoints(wpt_t *wpt) {
1329     int num = 0;
1330    
1331     while(wpt) {
1332     num++;
1333     wpt = wpt->next;
1334     }
1335    
1336     return num;
1337     }
1338    
1339     int gpx_number_of_logs(log_t *log) {
1340     int num = 0;
1341    
1342     while(log) {
1343     num++;
1344     log = log->next;
1345     }
1346    
1347     return num;
1348     }
1349    
1350     int gpx_number_of_tbs(tb_t *tb) {
1351     int num = 0;
1352    
1353     while(tb) {
1354     num++;
1355     tb = tb->next;
1356     }
1357    
1358     return num;
1359     }
1360    
1361     /* http://mathforum.org/library/drmath/view/55417.html */
1362     float gpx_pos_get_bearing(pos_t p1, pos_t p2) {
1363     /* convert to radians */
1364     p1.lat *= (M_PI/180.0); p1.lon *= (M_PI/180.0);
1365     p2.lat *= (M_PI/180.0); p2.lon *= (M_PI/180.0);
1366    
1367     return fmodf(360.0 + (180.0/M_PI) *
1368     (atan2( sin(p2.lon - p1.lon) * cos(p2.lat),
1369     cos(p1.lat) * sin(p2.lat) -
1370     sin(p1.lat) * cos(p2.lat) * cos(p2.lon - p1.lon))),
1371     360.0);
1372     }
1373    
1374     /* http://mathforum.org/library/drmath/view/51722.html */
1375     float gpx_pos_get_distance(pos_t p1, pos_t p2, int miles) {
1376     /* convert to radians */
1377     p1.lat *= (M_PI/180.0); p1.lon *= (M_PI/180.0);
1378     p2.lat *= (M_PI/180.0); p2.lon *= (M_PI/180.0);
1379    
1380     float aob = acos(cos(p1.lat) * cos(p2.lat) * cos(p2.lon - p1.lon) +
1381     sin(p1.lat) * sin(p2.lat));
1382    
1383     if(miles)
1384     return(aob * 3959.0); /* great circle radius in miles */
1385    
1386     return(aob * 6371.0); /* great circle radius in kilometers */
1387     }
1388    
1389     void gpx_pos_get_distance_str(char *str, int len,
1390     pos_t p1, pos_t p2, int mil) {
1391 harbaum 34 if(isnan(p1.lat) || isnan(p1.lon)) {
1392 harbaum 1 snprintf(str, len, "---");
1393     return;
1394     }
1395    
1396     float dist = gpx_pos_get_distance(p1, p2, mil);
1397     distance_str(str, len, dist, mil);
1398     }
1399    
1400     void gpx_sort(gpx_t *gpx, int by, pos_t *refpos) {
1401     cache_t **new;
1402     cache_t *cur = gpx->cache;
1403     int total = gpx_total_caches(gpx);
1404     float *dist_cache = malloc(total * sizeof(float));
1405    
1406     gpx->cache = NULL; /* detach old chain */
1407     while(cur) {
1408 harbaum 13 float cur_dist = -1;
1409 harbaum 1 int cur_cnt = 0;
1410    
1411 harbaum 13 if(!isnan(cur->pos.lat) && !isnan(cur->pos.lon))
1412     cur_dist = gpx_pos_get_distance(*refpos, gpx_cache_pos(cur), 0);
1413    
1414 harbaum 1 new = &(gpx->cache);
1415    
1416     /* search for currect insertion point */
1417     while(*new && (dist_cache[cur_cnt] < cur_dist)) {
1418     new = &((*new)->next);
1419     cur_cnt++;
1420     }
1421    
1422     /* save distance for further comparisons */
1423     memmove(dist_cache+cur_cnt+1, dist_cache+cur_cnt,
1424     sizeof(float)*(total-cur_cnt-1));
1425     dist_cache[cur_cnt++] = cur_dist;
1426    
1427     cache_t *next = cur->next;
1428    
1429     /* insert into "new" chain */
1430     cur->next = *new;
1431     *new = cur;
1432    
1433     cur = next;
1434     }
1435    
1436     free(dist_cache);
1437     }
1438    
1439     gpx_t *gpx_cache2gpx(gpx_t *gpx, cache_t *search_cache) {
1440     while(gpx) {
1441     cache_t *cache = gpx->cache;
1442     while(cache) {
1443     if(cache == search_cache)
1444     return gpx;
1445    
1446     cache = cache->next;
1447     }
1448     gpx = gpx->next;
1449     }
1450    
1451     return NULL;
1452     }
1453    
1454     /* since the actual cache position may be overridden, we */
1455     /* always access the position through this function */
1456     pos_t gpx_cache_pos(cache_t *cache) {
1457     if(cache->notes && cache->notes->override)
1458     return cache->notes->pos;
1459    
1460     return cache->pos;
1461     }