Contents of /trunk/src/gpx.c

Parent Directory Parent Directory | Revision Log Revision Log


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