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