Contents of /trunk/src/gpx.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 137 - (show annotations)
Mon Oct 19 18:21:20 2009 UTC (14 years, 6 months ago) by harbaum
File MIME type: text/plain
File size: 38831 byte(s)
Links to gc.com everywhere
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",
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 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(" 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 printf(" owner: %s\n", cache->owner->name);
288 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 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 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 if(!xmlTextReaderIsEmptyElement(reader)) {
380
381 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 }
401 ret = xmlTextReaderRead(reader);
402 }
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 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 } 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 gpx_free_log(log);
490 return NULL;
491 }
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 /* 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 }
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 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id")))
565 tb->id = atoi(prop);
566
567 /* 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 gpx_free_tb(tb);
599 return NULL;
600 }
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 while(tb) {
641 tb_t *next = tb;
642 gpx_free_tb(tb);
643 tb = next;
644 }
645
646 return NULL;
647 }
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 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 } 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
698 /* 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 (strncasecmp(cache->id, "__", 2) != 0) &&
810 (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 gpx_free_cache(cache);
877 return NULL;
878 }
879
880 static gboolean process_gpx(xmlTextReaderPtr reader, gpx_dialog_t *dialog,
881 gpx_t *gpx) {
882
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 if(!name) return FALSE;
893
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 return TRUE;
923 break;
924
925 default:
926 break;
927 }
928 ret = xmlTextReaderRead(reader);
929 }
930
931 return FALSE;
932 }
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 if(isnan(p1.lat) || isnan(p1.lon)) {
1392 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 float cur_dist = -1;
1409 int cur_cnt = 0;
1410
1411 if(!isnan(cur->pos.lat) && !isnan(cur->pos.lon))
1412 cur_dist = gpx_pos_get_distance(*refpos, gpx_cache_pos(cur), 0);
1413
1414 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 }