Contents of /trunk/src/gpx.c

Parent Directory Parent Directory | Revision Log Revision Log


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