Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 70 - (hide annotations)
Wed Feb 11 19:43:03 2009 UTC (15 years, 3 months ago) by harbaum
File MIME type: text/plain
File size: 69179 byte(s)
Removed qnd_xml parser
1 harbaum 1 /*
2     * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3     *
4     * This file is part of OSM2Go.
5     *
6     * OSM2Go 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     * OSM2Go 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 OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18     */
19    
20 harbaum 42 /* these defines select one of three possible xml parsers */
21     /* this is in fact selected depending on the plattform in the Makefile */
22 harbaum 39 // #define OSM_DOM_PARSER
23     // #define OSM_STREAM_PARSER
24 harbaum 8
25 harbaum 1 #include <stdio.h>
26     #include <stdlib.h>
27     #include <string.h>
28     #include <math.h>
29    
30     #define __USE_XOPEN
31     #include <time.h>
32    
33     #include <libxml/parser.h>
34     #include <libxml/tree.h>
35    
36     #include "appdata.h"
37 achadwick 28 #include "banner.h"
38 harbaum 1
39     #ifndef LIBXML_TREE_ENABLED
40     #error "Tree not enabled in libxml"
41     #endif
42    
43     /* determine where a node/way/relation read from the osm file */
44     /* is inserted into the internal database */
45     // #define OSM_SORT_ID
46     #define OSM_SORT_LAST
47     // #define OSM_SORT_FIRST
48    
49 harbaum 9 /* ------------------------- bounds handling --------------------- */
50 harbaum 1
51     static void osm_bounds_free(bounds_t *bounds) {
52     free(bounds);
53     }
54    
55     static void osm_bounds_dump(bounds_t *bounds) {
56     printf("\nBounds: %f->%f %f->%f\n",
57     bounds->ll_min.lat, bounds->ll_max.lat,
58     bounds->ll_min.lon, bounds->ll_max.lon);
59     }
60    
61 harbaum 39 #ifdef OSM_DOM_PARSER
62 harbaum 1 static bounds_t *osm_parse_osm_bounds(osm_t *osm,
63     xmlDocPtr doc, xmlNode *a_node) {
64     char *prop;
65    
66     if(osm->bounds) {
67     errorf(NULL, "Doubly defined bounds");
68     return NULL;
69     }
70    
71     bounds_t *bounds = g_new0(bounds_t, 1);
72    
73     bounds->ll_min.lat = bounds->ll_min.lon = NAN;
74     bounds->ll_max.lat = bounds->ll_max.lon = NAN;
75    
76     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"minlat"))) {
77     bounds->ll_min.lat = g_ascii_strtod(prop, NULL);
78     xmlFree(prop);
79     }
80    
81     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"maxlat"))) {
82     bounds->ll_max.lat = g_ascii_strtod(prop, NULL);
83     xmlFree(prop);
84     }
85    
86     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"minlon"))) {
87     bounds->ll_min.lon = g_ascii_strtod(prop, NULL);
88     xmlFree(prop);
89     }
90    
91     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"maxlon"))) {
92     bounds->ll_max.lon = g_ascii_strtod(prop, NULL);
93     xmlFree(prop);
94     }
95    
96     if(isnan(bounds->ll_min.lat) || isnan(bounds->ll_min.lon) ||
97     isnan(bounds->ll_max.lat) || isnan(bounds->ll_max.lon)) {
98     errorf(NULL, "Invalid coordinate in bounds (%f/%f/%f/%f)",
99     bounds->ll_min.lat, bounds->ll_min.lon,
100     bounds->ll_max.lat, bounds->ll_max.lon);
101    
102 harbaum 8 osm_bounds_free(bounds);
103 harbaum 1 return NULL;
104     }
105    
106    
107     /* calculate map zone which will be used as a reference for all */
108     /* drawing/projection later on */
109     pos_t center = { (bounds->ll_max.lat + bounds->ll_min.lat)/2,
110     (bounds->ll_max.lon + bounds->ll_min.lon)/2 };
111    
112     pos2lpos_center(&center, &bounds->center);
113    
114     /* the scale is needed to accomodate for "streching" */
115     /* by the mercartor projection */
116     bounds->scale = cos(DEG2RAD(center.lat));
117    
118     pos2lpos_center(&bounds->ll_min, &bounds->min);
119     bounds->min.x -= bounds->center.x;
120     bounds->min.y -= bounds->center.y;
121     bounds->min.x *= bounds->scale;
122     bounds->min.y *= bounds->scale;
123    
124     pos2lpos_center(&bounds->ll_max, &bounds->max);
125     bounds->max.x -= bounds->center.x;
126     bounds->max.y -= bounds->center.y;
127     bounds->max.x *= bounds->scale;
128     bounds->max.y *= bounds->scale;
129    
130     return bounds;
131     }
132 harbaum 8 #endif
133 harbaum 1
134     /* ------------------------- user handling --------------------- */
135    
136     void osm_users_free(user_t *user) {
137     while(user) {
138     user_t *next = user->next;
139    
140     if(user->name) g_free(user->name);
141     g_free(user);
142    
143     user = next;
144     }
145     }
146    
147     void osm_users_dump(user_t *user) {
148     printf("\nUser list:\n");
149     while(user) {
150     printf("Name: %s\n", user->name);
151     user = user->next;
152     }
153     }
154    
155     static user_t *osm_user(osm_t *osm, char *name) {
156 harbaum 39 if(!name) return NULL;
157 harbaum 1
158     /* search through user list */
159     user_t **user = &osm->user;
160     while(*user && strcasecmp((*user)->name, name) < 0)
161     user = &(*user)->next;
162    
163     /* end of list or inexact match? create new user entry! */
164     if(!*user || strcasecmp((*user)->name, name)) {
165     user_t *new = g_new0(user_t, 1);
166     new->name = g_strdup(name);
167     new->next = *user;
168     *user = new;
169    
170     return new;
171     }
172    
173     return *user;
174     }
175    
176     static
177     time_t convert_iso8601(const char *str) {
178 harbaum 39 if(!str) return 0;
179    
180 harbaum 1 tzset();
181    
182     struct tm ctime;
183     memset(&ctime, 0, sizeof(struct tm));
184     strptime(str, "%FT%T%z", &ctime);
185    
186     return mktime(&ctime) - timezone;
187     }
188    
189     /* -------------------- tag handling ----------------------- */
190    
191     void osm_tag_free(tag_t *tag) {
192     if(tag->key) g_free(tag->key);
193     if(tag->value) g_free(tag->value);
194     g_free(tag);
195     }
196    
197     void osm_tags_free(tag_t *tag) {
198     while(tag) {
199     tag_t *next = tag->next;
200     osm_tag_free(tag);
201     tag = next;
202     }
203     }
204    
205     static void osm_tags_dump(tag_t *tag) {
206     while(tag) {
207     printf("Key/Val: %s/%s\n", tag->key, tag->value);
208     tag = tag->next;
209     }
210     }
211    
212     tag_t *osm_parse_osm_tag(osm_t *osm, xmlDocPtr doc, xmlNode *a_node) {
213     xmlNode *cur_node = NULL;
214    
215     /* allocate a new tag structure */
216     tag_t *tag = g_new0(tag_t, 1);
217    
218     char *prop;
219     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"k"))) {
220     if(strlen(prop) > 0) tag->key = g_strdup(prop);
221     xmlFree(prop);
222     }
223    
224     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"v"))) {
225     if(strlen(prop) > 0) tag->value = g_strdup(prop);
226     xmlFree(prop);
227     }
228    
229     if(!tag->key || !tag->value) {
230     printf("incomplete tag key/value %s/%s\n", tag->key, tag->value);
231     osm_tags_free(tag);
232     return NULL;
233     }
234    
235     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next)
236     if (cur_node->type == XML_ELEMENT_NODE)
237     printf("found unhandled osm/node/tag/%s\n", cur_node->name);
238    
239     return tag;
240     }
241    
242     gboolean osm_is_creator_tag(tag_t *tag) {
243     if(strcasecmp(tag->key, "created_by") == 0) return TRUE;
244     if(strcasecmp(tag->key, "source") == 0) return TRUE;
245    
246     return FALSE;
247     }
248    
249     gboolean osm_tag_key_and_value_present(tag_t *haystack, tag_t *tag) {
250     while(haystack) {
251     if((strcasecmp(haystack->key, tag->key) == 0) &&
252     (strcasecmp(haystack->value, tag->value) == 0))
253     return TRUE;
254    
255     haystack = haystack->next;
256     }
257     return FALSE;
258     }
259    
260     gboolean osm_tag_key_other_value_present(tag_t *haystack, tag_t *tag) {
261     while(haystack) {
262     if((strcasecmp(haystack->key, tag->key) == 0) &&
263     (strcasecmp(haystack->value, tag->value) != 0))
264     return TRUE;
265    
266     haystack = haystack->next;
267     }
268     return FALSE;
269     }
270    
271     gboolean osm_way_ends_with_node(way_t *way, node_t *node) {
272     /* and deleted way may even not contain any nodes at all */
273     /* so ignore it */
274     if(way->flags & OSM_FLAG_DELETED)
275     return FALSE;
276    
277     /* any valid way must have at least two nodes */
278     g_assert(way->node_chain && way->node_chain->next);
279    
280     node_chain_t *chain = way->node_chain;
281     if(chain->node == node) return TRUE;
282    
283     while(chain->next) chain = chain->next;
284     if(chain->node == node) return TRUE;
285    
286     return FALSE;
287     }
288    
289     /* ------------------- node handling ------------------- */
290    
291     void osm_node_free(icon_t **icon, node_t *node) {
292     if(node->icon_buf)
293     icon_free(icon, node->icon_buf);
294    
295     /* there must not be anything left in this chain */
296     g_assert(!node->map_item_chain);
297    
298     osm_tags_free(node->tag);
299     g_free(node);
300     }
301    
302     static void osm_nodes_free(icon_t **icon, node_t *node) {
303     while(node) {
304     node_t *next = node->next;
305     osm_node_free(icon, node);
306     node = next;
307     }
308     }
309    
310     void osm_node_dump(node_t *node) {
311     char buf[64];
312     struct tm tm;
313    
314     printf("Id: %lu\n", node->id);
315     printf("User: %s\n", node->user?node->user->name:"<unspecified>");
316     printf("Visible: %s\n", node->visible?"yes":"no");
317    
318     localtime_r(&node->time, &tm);
319     strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", &tm);
320     printf("Time: %s\n", buf);
321     osm_tags_dump(node->tag);
322     }
323    
324     void osm_nodes_dump(node_t *node) {
325     printf("\nNode list:\n");
326     while(node) {
327     osm_node_dump(node);
328     printf("\n");
329     node = node->next;
330     }
331     }
332    
333 harbaum 39 #ifdef OSM_DOM_PARSER
334 harbaum 1 static node_t *osm_parse_osm_node(osm_t *osm,
335     xmlDocPtr doc, xmlNode *a_node) {
336     xmlNode *cur_node = NULL;
337    
338     /* allocate a new node structure */
339     node_t *node = g_new0(node_t, 1);
340     node->pos.lat = node->pos.lon = NAN;
341    
342     char *prop;
343     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"id"))) {
344     node->id = strtoul(prop, NULL, 10);
345     xmlFree(prop);
346     }
347    
348     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"lat"))) {
349     node->pos.lat = g_ascii_strtod(prop, NULL);
350     xmlFree(prop);
351     }
352    
353     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"lon"))) {
354     node->pos.lon = g_ascii_strtod(prop, NULL);
355     xmlFree(prop);
356     }
357    
358     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"user"))) {
359     node->user = osm_user(osm, prop);
360     xmlFree(prop);
361     }
362    
363     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"visible"))) {
364     node->visible = (strcasecmp(prop, "true") == 0);
365     xmlFree(prop);
366     }
367    
368     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"timestamp"))) {
369     node->time = convert_iso8601(prop);
370     xmlFree(prop);
371     }
372    
373 harbaum 42 /* append node to end of hash table if present */
374     if(osm->node_hash) {
375     hash_item_t **item = &osm->node_hash->hash[ID2HASH(node->id)];
376     while(*item) item = &(*item)->next;
377    
378     *item = g_new0(hash_item_t, 1);
379     (*item)->data.node = node;
380     }
381    
382 harbaum 1 /* scan for tags and attach a list of tags */
383     tag_t **tag = &node->tag;
384     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
385     if (cur_node->type == XML_ELEMENT_NODE) {
386     if(strcasecmp((char*)cur_node->name, "tag") == 0) {
387     /* attach tag to node */
388     *tag = osm_parse_osm_tag(osm, doc, cur_node);
389     if(*tag) tag = &((*tag)->next);
390     } else
391     printf("found unhandled osm/node/%s\n", cur_node->name);
392     }
393     }
394    
395     pos2lpos(osm->bounds, &node->pos, &node->lpos);
396    
397     return node;
398     }
399 harbaum 8 #endif
400 harbaum 1
401     /* ------------------- way handling ------------------- */
402    
403     void osm_node_chain_free(node_chain_t *node_chain) {
404     while(node_chain) {
405     g_assert(node_chain->node->ways);
406    
407     node_chain_t *next = node_chain->next;
408     node_chain->node->ways--;
409     g_free(node_chain);
410     node_chain = next;
411     }
412     }
413    
414     void osm_way_free(way_t *way) {
415     // printf("freeing way #%ld\n", way->id);
416    
417     osm_node_chain_free(way->node_chain);
418     osm_tags_free(way->tag);
419    
420     /* there must not be anything left in this chain */
421     g_assert(!way->map_item_chain);
422    
423     g_free(way);
424     }
425    
426     static void osm_ways_free(way_t *way) {
427     while(way) {
428     way_t *next = way->next;
429     osm_way_free(way);
430     way = next;
431     }
432     }
433    
434     void osm_way_append_node(way_t *way, node_t *node) {
435     node_chain_t **node_chain = &way->node_chain;
436    
437     while(*node_chain)
438     node_chain = &((*node_chain)->next);
439    
440     *node_chain = g_new0(node_chain_t, 1);
441     (*node_chain)->node = node;
442    
443     node->ways++;
444     }
445    
446     int osm_node_chain_length(node_chain_t *node_chain) {
447     int cnt = 0;
448     while(node_chain) {
449     cnt++;
450     node_chain = node_chain->next;
451     }
452    
453     return cnt;
454     }
455    
456     void osm_way_dump(way_t *way) {
457     char buf[64];
458     struct tm tm;
459    
460     printf("Id: %lu\n", way->id);
461     printf("User: %s\n", way->user?way->user->name:"<unspecified>");
462     printf("Visible: %s\n", way->visible?"yes":"no");
463     node_chain_t *node_chain = way->node_chain;
464     while(node_chain) {
465     printf(" Node: %lu\n", node_chain->node->id);
466     node_chain = node_chain->next;
467     }
468    
469     localtime_r(&way->time, &tm);
470     strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", &tm);
471     printf("Time: %s\n", buf);
472     osm_tags_dump(way->tag);
473     }
474    
475     void osm_ways_dump(way_t *way) {
476     printf("\nWay list:\n");
477     while(way) {
478     osm_way_dump(way);
479     printf("\n");
480     way = way->next;
481     }
482     }
483    
484     node_chain_t *osm_parse_osm_way_nd(osm_t *osm,
485     xmlDocPtr doc, xmlNode *a_node) {
486     char *prop;
487    
488     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"ref"))) {
489     item_id_t id = strtoul(prop, NULL, 10);
490     node_chain_t *node_chain = g_new0(node_chain_t, 1);
491    
492     /* search matching node */
493 harbaum 42 node_chain->node = osm_get_node_by_id(osm, id);
494 harbaum 1 if(!node_chain->node) printf("Node id %lu not found\n", id);
495 harbaum 42 else node_chain->node->ways++;
496 harbaum 1
497     xmlFree(prop);
498    
499     return node_chain;
500     }
501    
502     return NULL;
503     }
504    
505 harbaum 39 #ifdef OSM_DOM_PARSER
506 harbaum 1 static way_t *osm_parse_osm_way(osm_t *osm,
507     xmlDocPtr doc, xmlNode *a_node) {
508     xmlNode *cur_node = NULL;
509    
510     /* allocate a new way structure */
511     way_t *way = g_new0(way_t, 1);
512    
513     char *prop;
514     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"id"))) {
515     way->id = strtoul(prop, NULL, 10);
516     xmlFree(prop);
517     }
518    
519     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"user"))) {
520     way->user = osm_user(osm, prop);
521     xmlFree(prop);
522     }
523    
524     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"visible"))) {
525     way->visible = (strcasecmp(prop, "true") == 0);
526     xmlFree(prop);
527     }
528    
529     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"timestamp"))) {
530     way->time = convert_iso8601(prop);
531     xmlFree(prop);
532     }
533    
534 harbaum 42 /* append way to end of hash table if present */
535     if(osm->way_hash) {
536     hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)];
537     while(*item) item = &(*item)->next;
538    
539     *item = g_new0(hash_item_t, 1);
540     (*item)->data.way = way;
541     }
542    
543 harbaum 1 /* scan for tags/nodes and attach their lists */
544     tag_t **tag = &way->tag;
545     node_chain_t **node_chain = &way->node_chain;
546    
547     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
548     if (cur_node->type == XML_ELEMENT_NODE) {
549     if(strcasecmp((char*)cur_node->name, "tag") == 0) {
550     /* attach tag to node */
551     *tag = osm_parse_osm_tag(osm, doc, cur_node);
552     if(*tag) tag = &((*tag)->next);
553     } else if(strcasecmp((char*)cur_node->name, "nd") == 0) {
554     *node_chain = osm_parse_osm_way_nd(osm, doc, cur_node);
555     if(*node_chain)
556     node_chain = &((*node_chain)->next);
557     } else
558     printf("found unhandled osm/node/%s\n", cur_node->name);
559     }
560     }
561    
562     return way;
563     }
564 harbaum 8 #endif
565 harbaum 1
566     /* ------------------- relation handling ------------------- */
567    
568     void osm_member_free(member_t *member) {
569     if(member->role) g_free(member->role);
570     g_free(member);
571     }
572    
573     void osm_members_free(member_t *member) {
574     while(member) {
575     member_t *next = member->next;
576     osm_member_free(member);
577     member = next;
578     }
579     }
580    
581     static void osm_relations_free(relation_t *relation) {
582     while(relation) {
583     relation_t *next = relation->next;
584    
585     osm_tags_free(relation->tag);
586     osm_members_free(relation->member);
587    
588     g_free(relation);
589     relation = next;
590     }
591     }
592    
593     void osm_relations_dump(relation_t *relation) {
594     printf("\nRelation list:\n");
595     while(relation) {
596     char buf[64];
597     struct tm tm;
598    
599     printf("Id: %lu\n", relation->id);
600     printf("User: %s\n",
601     relation->user?relation->user->name:"<unspecified>");
602     printf("Visible: %s\n", relation->visible?"yes":"no");
603    
604     member_t *member = relation->member;
605     while(member) {
606     switch(member->type) {
607     case ILLEGAL:
608     case NODE_ID:
609     case WAY_ID:
610     case RELATION_ID:
611     break;
612    
613     case NODE:
614     if(member->node)
615     printf(" Member: Node, id = %lu, role = %s\n",
616     member->node->id, member->role);
617     break;
618    
619     case WAY:
620     if(member->way)
621     printf(" Member: Way, id = %lu, role = %s\n",
622     member->way->id, member->role);
623     break;
624    
625     case RELATION:
626     if(member->relation)
627     printf(" Member: Relation, id = %lu, role = %s\n",
628     member->relation->id, member->role);
629     break;
630     }
631    
632     member = member->next;
633     }
634    
635     localtime_r(&relation->time, &tm);
636     strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", &tm);
637     printf("Time: %s\n", buf);
638     osm_tags_dump(relation->tag);
639    
640     printf("\n");
641     relation = relation->next;
642     }
643     }
644    
645     member_t *osm_parse_osm_relation_member(osm_t *osm,
646     xmlDocPtr doc, xmlNode *a_node) {
647     char *prop;
648     member_t *member = g_new0(member_t, 1);
649     member->type = ILLEGAL;
650    
651     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"type"))) {
652     if(strcasecmp(prop, "way") == 0) member->type = WAY;
653     else if(strcasecmp(prop, "node") == 0) member->type = NODE;
654     else if(strcasecmp(prop, "relation") == 0) member->type = RELATION;
655     xmlFree(prop);
656     }
657    
658     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"ref"))) {
659     item_id_t id = strtoul(prop, NULL, 10);
660    
661     switch(member->type) {
662     case ILLEGAL:
663     printf("Unable to store illegal type\n");
664     break;
665    
666     case WAY:
667     /* search matching way */
668 harbaum 42 member->way = osm_get_way_by_id(osm, id);
669 harbaum 1 if(!member->way) {
670     member->type = WAY_ID;
671     member->id = id;
672     }
673     break;
674    
675     case NODE:
676     /* search matching node */
677 harbaum 42 member->node = osm_get_node_by_id(osm, id);
678 harbaum 1 if(!member->node) {
679     member->type = NODE_ID;
680     member->id = id;
681     }
682     break;
683    
684     case RELATION:
685     /* search matching relation */
686 harbaum 42 member->relation = osm_get_relation_by_id(osm, id);
687 harbaum 1 if(!member->relation) {
688     member->type = NODE_ID;
689     member->id = id;
690     }
691     break;
692    
693     case WAY_ID:
694     case NODE_ID:
695     case RELATION_ID:
696     break;
697     }
698    
699     xmlFree(prop);
700     }
701    
702     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"role"))) {
703     if(strlen(prop) > 0) member->role = g_strdup(prop);
704     xmlFree(prop);
705     }
706    
707     return member;
708     }
709    
710 harbaum 39 #ifdef OSM_DOM_PARSER
711 harbaum 1 static relation_t *osm_parse_osm_relation(osm_t *osm,
712     xmlDocPtr doc, xmlNode *a_node) {
713     xmlNode *cur_node = NULL;
714    
715     /* allocate a new relation structure */
716     relation_t *relation = g_new0(relation_t, 1);
717    
718     char *prop;
719     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"id"))) {
720     relation->id = strtoul(prop, NULL, 10);
721     xmlFree(prop);
722     }
723    
724     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"user"))) {
725     relation->user = osm_user(osm, prop);
726     xmlFree(prop);
727     }
728    
729     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"visible"))) {
730     relation->visible = (strcasecmp(prop, "true") == 0);
731     xmlFree(prop);
732     }
733    
734     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"timestamp"))) {
735     relation->time = convert_iso8601(prop);
736     xmlFree(prop);
737     }
738    
739     /* scan for tags and attach a list of tags */
740     tag_t **tag = &relation->tag;
741     member_t **member = &relation->member;
742    
743     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
744     if (cur_node->type == XML_ELEMENT_NODE) {
745     if(strcasecmp((char*)cur_node->name, "tag") == 0) {
746     /* attach tag to node */
747     *tag = osm_parse_osm_tag(osm, doc, cur_node);
748     if(*tag) tag = &((*tag)->next);
749     } else if(strcasecmp((char*)cur_node->name, "member") == 0) {
750     *member = osm_parse_osm_relation_member(osm, doc, cur_node);
751     if(*member) member = &((*member)->next);
752     } else
753     printf("found unhandled osm/node/%s\n", cur_node->name);
754     }
755     }
756    
757     return relation;
758     }
759    
760     /* ----------------------- generic xml handling -------------------------- */
761    
762 harbaum 42 /* parse osm entry */
763 harbaum 1 static void osm_parse_osm(osm_t *osm, xmlDocPtr doc, xmlNode * a_node) {
764     xmlNode *cur_node = NULL;
765    
766     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
767     if (cur_node->type == XML_ELEMENT_NODE) {
768     if(strcasecmp((char*)cur_node->name, "bounds") == 0)
769     osm->bounds = osm_parse_osm_bounds(osm, doc, cur_node);
770     else if(strcasecmp((char*)cur_node->name, "node") == 0) {
771     /* parse node and attach it to chain */
772     node_t *new = osm_parse_osm_node(osm, doc, cur_node);
773     if(new) {
774     node_t **node = &osm->node;
775    
776     #ifdef OSM_SORT_ID
777     /* search chain of nodes */
778     while(*node && ((*node)->id < new->id))
779     node = &(*node)->next;
780     #endif
781    
782     #ifdef OSM_SORT_LAST
783     while(*node) node = &(*node)->next;
784     #endif
785    
786     /* insert into chain */
787     new->next = *node;
788     *node = new;
789     }
790     } else if(strcasecmp((char*)cur_node->name, "way") == 0) {
791     /* parse way and attach it to chain */
792     way_t *new = osm_parse_osm_way(osm, doc, cur_node);
793     if(new) {
794     way_t **way = &osm->way;
795    
796     #ifdef OSM_SORT_ID
797     /* insert into chain */
798     while(*way && ((*way)->id < new->id))
799     way = &(*way)->next;
800     #endif
801    
802     #ifdef OSM_SORT_LAST
803     while(*way) way = &(*way)->next;
804     #endif
805    
806     /* insert into chain */
807     new->next = *way;
808     *way = new;
809     }
810     } else if(strcasecmp((char*)cur_node->name, "relation") == 0) {
811     /* parse relation and attach it to chain */
812     relation_t *new = osm_parse_osm_relation(osm, doc, cur_node);
813     if(new) {
814     relation_t **relation = &osm->relation;
815    
816     #ifdef OSM_SORT_ID
817     /* search chain of ways */
818     while(*relation && ((*relation)->id < new->id))
819     relation = &(*relation)->next;
820     #endif
821    
822     #ifdef OSM_SORT_LAST
823     while(*relation) relation = &(*relation)->next;
824     #endif
825    
826     /* insert into chain */
827     new->next = *relation;
828     *relation = new;
829     }
830     } else
831     printf("found unhandled osm/%s\n", cur_node->name);
832    
833     }
834     }
835     }
836    
837     /* parse root element and search for "osm" */
838     static osm_t *osm_parse_root(xmlDocPtr doc, xmlNode * a_node) {
839     osm_t *osm;
840     xmlNode *cur_node = NULL;
841    
842     /* allocate memory to hold osm file description */
843     osm = g_new0(osm_t, 1);
844 harbaum 42 osm->node_hash = g_new0(hash_table_t, 1);
845     osm->way_hash = g_new0(hash_table_t, 1);
846 harbaum 1
847     for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
848     if (cur_node->type == XML_ELEMENT_NODE) {
849     /* parse osm osm file ... */
850     if(strcasecmp((char*)cur_node->name, "osm") == 0)
851     osm_parse_osm(osm, doc, cur_node);
852     else
853     printf("found unhandled %s\n", cur_node->name);
854     }
855     }
856    
857     return osm;
858     }
859    
860     static osm_t *osm_parse_doc(xmlDocPtr doc) {
861     osm_t *osm;
862    
863     /* Get the root element node */
864     xmlNode *root_element = xmlDocGetRootElement(doc);
865    
866     osm = osm_parse_root(doc, root_element);
867    
868     /*free the document */
869     xmlFreeDoc(doc);
870    
871     /*
872     * Free the global variables that may
873     * have been allocated by the parser.
874     */
875     xmlCleanupParser();
876    
877     return osm;
878     }
879 harbaum 8 #endif
880 harbaum 1
881     /* ------------------ osm handling ----------------- */
882    
883 harbaum 42 /* the two hash tables eat over 512kBytes memory and may thus be */
884     /* freed at any time. osm2go can work without them (albeit slower) */
885     static void hash_table_free(hash_table_t *table) {
886     if(!table) return;
887    
888     int i;
889     for(i=0;i<65536;i++) {
890     hash_item_t *item = table->hash[i];
891     while(item) {
892     hash_item_t *next = item->next;
893     g_free(item);
894     item = next;
895     }
896     }
897     }
898    
899     void osm_hash_tables_free(osm_t *osm) {
900     hash_table_free(osm->node_hash);
901     osm->node_hash = NULL;
902     hash_table_free(osm->way_hash);
903     osm->way_hash = NULL;
904     }
905    
906 harbaum 1 void osm_free(icon_t **icon, osm_t *osm) {
907     if(!osm) return;
908    
909 harbaum 42 osm_hash_tables_free(osm);
910    
911 harbaum 1 if(osm->bounds) osm_bounds_free(osm->bounds);
912     if(osm->user) osm_users_free(osm->user);
913     if(osm->way) osm_ways_free(osm->way);
914     if(osm->node) osm_nodes_free(icon, osm->node);
915     if(osm->relation) osm_relations_free(osm->relation);
916     g_free(osm);
917     }
918    
919     void osm_dump(osm_t *osm) {
920     osm_bounds_dump(osm->bounds);
921     osm_users_dump(osm->user);
922     osm_nodes_dump(osm->node);
923     osm_ways_dump(osm->way);
924     osm_relations_dump(osm->relation);
925     }
926    
927 harbaum 8 #ifdef OSM_STREAM_PARSER
928     /* -------------------------- stream parser tests ------------------- */
929    
930     #include <libxml/xmlreader.h>
931    
932     static gint my_strcmp(const xmlChar *a, const xmlChar *b) {
933     if(!a && !b) return 0;
934     if(!a) return -1;
935     if(!b) return +1;
936     return strcmp((char*)a,(char*)b);
937     }
938    
939     /* skip current element incl. everything below (mainly for testing) */
940     /* returns FALSE if something failed */
941     static gboolean skip_element(xmlTextReaderPtr reader) {
942     g_assert(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT);
943     const xmlChar *name = xmlTextReaderConstName(reader);
944     g_assert(name);
945     int depth = xmlTextReaderDepth(reader);
946    
947     if(xmlTextReaderIsEmptyElement(reader))
948     return TRUE;
949    
950     int ret = xmlTextReaderRead(reader);
951     while((ret == 1) &&
952     ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
953     (xmlTextReaderDepth(reader) > depth) ||
954     (my_strcmp(xmlTextReaderConstName(reader), name) != 0))) {
955     ret = xmlTextReaderRead(reader);
956     }
957     return(ret == 1);
958     }
959    
960     /* parse bounds */
961     static bounds_t *process_bounds(xmlTextReaderPtr reader) {
962     char *prop = NULL;
963     bounds_t *bounds = g_new0(bounds_t, 1);
964    
965     bounds->ll_min.lat = bounds->ll_min.lon = NAN;
966     bounds->ll_max.lat = bounds->ll_max.lon = NAN;
967    
968     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "minlat"))) {
969     bounds->ll_min.lat = g_ascii_strtod(prop, NULL);
970     xmlFree(prop);
971     }
972    
973     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "maxlat"))) {
974     bounds->ll_max.lat = g_ascii_strtod(prop, NULL);
975     xmlFree(prop);
976     }
977    
978     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "minlon"))) {
979     bounds->ll_min.lon = g_ascii_strtod(prop, NULL);
980     xmlFree(prop);
981     }
982    
983     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "maxlon"))) {
984     bounds->ll_max.lon = g_ascii_strtod(prop, NULL);
985     xmlFree(prop);
986     }
987    
988     if(isnan(bounds->ll_min.lat) || isnan(bounds->ll_min.lon) ||
989     isnan(bounds->ll_max.lat) || isnan(bounds->ll_max.lon)) {
990     errorf(NULL, "Invalid coordinate in bounds (%f/%f/%f/%f)",
991     bounds->ll_min.lat, bounds->ll_min.lon,
992     bounds->ll_max.lat, bounds->ll_max.lon);
993    
994     osm_bounds_free(bounds);
995     return NULL;
996     }
997    
998     /* skip everything below */
999     skip_element(reader);
1000    
1001     /* calculate map zone which will be used as a reference for all */
1002     /* drawing/projection later on */
1003     pos_t center = { (bounds->ll_max.lat + bounds->ll_min.lat)/2,
1004     (bounds->ll_max.lon + bounds->ll_min.lon)/2 };
1005    
1006     pos2lpos_center(&center, &bounds->center);
1007    
1008     /* the scale is needed to accomodate for "streching" */
1009     /* by the mercartor projection */
1010     bounds->scale = cos(DEG2RAD(center.lat));
1011    
1012     pos2lpos_center(&bounds->ll_min, &bounds->min);
1013     bounds->min.x -= bounds->center.x;
1014     bounds->min.y -= bounds->center.y;
1015     bounds->min.x *= bounds->scale;
1016     bounds->min.y *= bounds->scale;
1017    
1018     pos2lpos_center(&bounds->ll_max, &bounds->max);
1019     bounds->max.x -= bounds->center.x;
1020     bounds->max.y -= bounds->center.y;
1021     bounds->max.x *= bounds->scale;
1022     bounds->max.y *= bounds->scale;
1023    
1024     return bounds;
1025     }
1026    
1027     static tag_t *process_tag(xmlTextReaderPtr reader) {
1028     /* allocate a new tag structure */
1029     tag_t *tag = g_new0(tag_t, 1);
1030    
1031     char *prop;
1032     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "k"))) {
1033     if(strlen(prop) > 0) tag->key = g_strdup(prop);
1034     xmlFree(prop);
1035     }
1036    
1037     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "v"))) {
1038     if(strlen(prop) > 0) tag->value = g_strdup(prop);
1039     xmlFree(prop);
1040     }
1041    
1042     if(!tag->key || !tag->value) {
1043     printf("incomplete tag key/value %s/%s\n", tag->key, tag->value);
1044     osm_tags_free(tag);
1045     tag = NULL;
1046     }
1047    
1048     skip_element(reader);
1049     return tag;
1050     }
1051    
1052     static node_t *process_node(xmlTextReaderPtr reader, osm_t *osm) {
1053    
1054     /* allocate a new node structure */
1055     node_t *node = g_new0(node_t, 1);
1056     node->pos.lat = node->pos.lon = NAN;
1057    
1058     char *prop;
1059     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
1060     node->id = strtoul(prop, NULL, 10);
1061     xmlFree(prop);
1062     }
1063    
1064     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "lat"))) {
1065     node->pos.lat = g_ascii_strtod(prop, NULL);
1066     xmlFree(prop);
1067     }
1068    
1069     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "lon"))) {
1070     node->pos.lon = g_ascii_strtod(prop, NULL);
1071     xmlFree(prop);
1072     }
1073    
1074     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) {
1075     node->user = osm_user(osm, prop);
1076     xmlFree(prop);
1077     }
1078    
1079     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) {
1080     node->visible = (strcasecmp(prop, "true") == 0);
1081     xmlFree(prop);
1082     }
1083    
1084     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) {
1085     node->time = convert_iso8601(prop);
1086     xmlFree(prop);
1087     }
1088    
1089     pos2lpos(osm->bounds, &node->pos, &node->lpos);
1090    
1091 harbaum 42 /* append node to end of hash table if present */
1092     if(osm->node_hash) {
1093     hash_item_t **item = &osm->node_hash->hash[ID2HASH(node->id)];
1094     while(*item) item = &(*item)->next;
1095    
1096     *item = g_new0(hash_item_t, 1);
1097     (*item)->data.node = node;
1098     }
1099    
1100 harbaum 8 /* just an empty element? then return the node as it is */
1101     if(xmlTextReaderIsEmptyElement(reader))
1102     return node;
1103    
1104     /* parse tags if present */
1105     int depth = xmlTextReaderDepth(reader);
1106    
1107     /* scan all elements on same level or its children */
1108     tag_t **tag = &node->tag;
1109     int ret = xmlTextReaderRead(reader);
1110     while((ret == 1) &&
1111     ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
1112     (xmlTextReaderDepth(reader) != depth))) {
1113    
1114     if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
1115     char *subname = (char*)xmlTextReaderConstName(reader);
1116     if(strcasecmp(subname, "tag") == 0) {
1117     *tag = process_tag(reader);
1118     if(*tag) tag = &(*tag)->next;
1119     } else
1120     skip_element(reader);
1121     }
1122    
1123     ret = xmlTextReaderRead(reader);
1124     }
1125    
1126     return node;
1127     }
1128    
1129     static node_chain_t *process_nd(xmlTextReaderPtr reader, osm_t *osm) {
1130     char *prop;
1131    
1132     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref"))) {
1133     item_id_t id = strtoul(prop, NULL, 10);
1134     node_chain_t *node_chain = g_new0(node_chain_t, 1);
1135    
1136     /* search matching node */
1137 harbaum 42 node_chain->node = osm_get_node_by_id(osm, id);
1138 harbaum 8 if(!node_chain->node) printf("Node id %lu not found\n", id);
1139 harbaum 42 else node_chain->node->ways++;
1140 harbaum 8
1141     xmlFree(prop);
1142    
1143     skip_element(reader);
1144     return node_chain;
1145     }
1146    
1147     skip_element(reader);
1148     return NULL;
1149     }
1150    
1151     static way_t *process_way(xmlTextReaderPtr reader, osm_t *osm) {
1152     /* allocate a new way structure */
1153     way_t *way = g_new0(way_t, 1);
1154    
1155     char *prop;
1156     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
1157     way->id = strtoul(prop, NULL, 10);
1158     xmlFree(prop);
1159     }
1160    
1161     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) {
1162     way->user = osm_user(osm, prop);
1163     xmlFree(prop);
1164     }
1165    
1166     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) {
1167     way->visible = (strcasecmp(prop, "true") == 0);
1168     xmlFree(prop);
1169     }
1170    
1171     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) {
1172     way->time = convert_iso8601(prop);
1173     xmlFree(prop);
1174     }
1175    
1176 harbaum 42 /* append way to end of hash table if present */
1177     if(osm->way_hash) {
1178     hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)];
1179     while(*item) item = &(*item)->next;
1180    
1181     *item = g_new0(hash_item_t, 1);
1182     (*item)->data.way = way;
1183     }
1184    
1185 harbaum 8 /* just an empty element? then return the way as it is */
1186     /* (this should in fact never happen as this would be a way without nodes) */
1187     if(xmlTextReaderIsEmptyElement(reader))
1188     return way;
1189    
1190     /* parse tags/nodes if present */
1191     int depth = xmlTextReaderDepth(reader);
1192    
1193     /* scan all elements on same level or its children */
1194     tag_t **tag = &way->tag;
1195     node_chain_t **node_chain = &way->node_chain;
1196     int ret = xmlTextReaderRead(reader);
1197     while((ret == 1) &&
1198     ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
1199     (xmlTextReaderDepth(reader) != depth))) {
1200    
1201     if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
1202     char *subname = (char*)xmlTextReaderConstName(reader);
1203     if(strcasecmp(subname, "nd") == 0) {
1204     *node_chain = process_nd(reader, osm);
1205     if(*node_chain) node_chain = &(*node_chain)->next;
1206     } else if(strcasecmp(subname, "tag") == 0) {
1207     *tag = process_tag(reader);
1208     if(*tag) tag = &(*tag)->next;
1209     } else
1210     skip_element(reader);
1211     }
1212     ret = xmlTextReaderRead(reader);
1213     }
1214    
1215     return way;
1216     }
1217    
1218     static member_t *process_member(xmlTextReaderPtr reader, osm_t *osm) {
1219     char *prop;
1220     member_t *member = g_new0(member_t, 1);
1221     member->type = ILLEGAL;
1222    
1223     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "type"))) {
1224     if(strcasecmp(prop, "way") == 0) member->type = WAY;
1225     else if(strcasecmp(prop, "node") == 0) member->type = NODE;
1226     else if(strcasecmp(prop, "relation") == 0) member->type = RELATION;
1227     xmlFree(prop);
1228     }
1229    
1230     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref"))) {
1231     item_id_t id = strtoul(prop, NULL, 10);
1232    
1233     switch(member->type) {
1234     case ILLEGAL:
1235     printf("Unable to store illegal type\n");
1236     break;
1237    
1238     case WAY:
1239     /* search matching way */
1240 harbaum 42 member->way = osm_get_way_by_id(osm, id);
1241 harbaum 8 if(!member->way) {
1242     member->type = WAY_ID;
1243     member->id = id;
1244     }
1245     break;
1246    
1247     case NODE:
1248     /* search matching node */
1249 harbaum 42 member->node = osm_get_node_by_id(osm, id);
1250 harbaum 8 if(!member->node) {
1251     member->type = NODE_ID;
1252     member->id = id;
1253     }
1254     break;
1255    
1256     case RELATION:
1257     /* search matching relation */
1258 harbaum 42 member->relation = osm_get_relation_by_id(osm, id);
1259 harbaum 8 if(!member->relation) {
1260     member->type = NODE_ID;
1261     member->id = id;
1262     }
1263     break;
1264    
1265     case WAY_ID:
1266     case NODE_ID:
1267     case RELATION_ID:
1268     break;
1269     }
1270    
1271     xmlFree(prop);
1272     }
1273    
1274     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "role"))) {
1275     if(strlen(prop) > 0) member->role = g_strdup(prop);
1276     xmlFree(prop);
1277     }
1278    
1279     return member;
1280     }
1281    
1282     static relation_t *process_relation(xmlTextReaderPtr reader, osm_t *osm) {
1283     /* allocate a new relation structure */
1284     relation_t *relation = g_new0(relation_t, 1);
1285    
1286     char *prop;
1287     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
1288     relation->id = strtoul(prop, NULL, 10);
1289     xmlFree(prop);
1290     }
1291    
1292     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) {
1293     relation->user = osm_user(osm, prop);
1294     xmlFree(prop);
1295     }
1296    
1297     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) {
1298     relation->visible = (strcasecmp(prop, "true") == 0);
1299     xmlFree(prop);
1300     }
1301    
1302     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) {
1303     relation->time = convert_iso8601(prop);
1304     xmlFree(prop);
1305     }
1306    
1307     /* just an empty element? then return the relation as it is */
1308     /* (this should in fact never happen as this would be a relation */
1309     /* without members) */
1310     if(xmlTextReaderIsEmptyElement(reader))
1311     return relation;
1312    
1313     /* parse tags/member if present */
1314     int depth = xmlTextReaderDepth(reader);
1315    
1316     /* scan all elements on same level or its children */
1317     tag_t **tag = &relation->tag;
1318     member_t **member = &relation->member;
1319     int ret = xmlTextReaderRead(reader);
1320     while((ret == 1) &&
1321     ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
1322     (xmlTextReaderDepth(reader) != depth))) {
1323    
1324     if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
1325     char *subname = (char*)xmlTextReaderConstName(reader);
1326 achadwick 35 if(strcasecmp(subname, "member") == 0) {
1327 harbaum 8 *member = process_member(reader, osm);
1328     if(*member) member = &(*member)->next;
1329     } else if(strcasecmp(subname, "tag") == 0) {
1330     *tag = process_tag(reader);
1331     if(*tag) tag = &(*tag)->next;
1332     } else
1333     skip_element(reader);
1334     }
1335     ret = xmlTextReaderRead(reader);
1336     }
1337    
1338     return relation;
1339     }
1340    
1341     static osm_t *process_osm(xmlTextReaderPtr reader) {
1342     /* alloc osm structure */
1343     osm_t *osm = g_new0(osm_t, 1);
1344 harbaum 42 osm->node_hash = g_new0(hash_table_t, 1);
1345     osm->way_hash = g_new0(hash_table_t, 1);
1346 harbaum 8
1347     node_t **node = &osm->node;
1348     way_t **way = &osm->way;
1349     relation_t **relation = &osm->relation;
1350    
1351     /* no attributes of interest */
1352    
1353     const xmlChar *name = xmlTextReaderConstName(reader);
1354     g_assert(name);
1355    
1356     /* read next node */
1357 achadwick 28 int num_elems = 0;
1358     const int tick_every = 50; // Balance responsive appearance with performance.
1359 harbaum 8 int ret = xmlTextReaderRead(reader);
1360     while(ret == 1) {
1361    
1362     switch(xmlTextReaderNodeType(reader)) {
1363     case XML_READER_TYPE_ELEMENT:
1364    
1365     g_assert(xmlTextReaderDepth(reader) == 1);
1366     char *name = (char*)xmlTextReaderConstName(reader);
1367     if(strcasecmp(name, "bounds") == 0) {
1368     osm->bounds = process_bounds(reader);
1369     } else if(strcasecmp(name, "node") == 0) {
1370     *node = process_node(reader, osm);
1371     if(*node) node = &(*node)->next;
1372     } else if(strcasecmp(name, "way") == 0) {
1373     *way = process_way(reader, osm);
1374     if(*way) way = &(*way)->next;
1375     } else if(strcasecmp(name, "relation") == 0) {
1376     *relation = process_relation(reader, osm);
1377     if(*relation) relation = &(*relation)->next;
1378     } else {
1379     printf("something unknown found\n");
1380     g_assert(0);
1381     skip_element(reader);
1382     }
1383     break;
1384    
1385     case XML_READER_TYPE_END_ELEMENT:
1386     /* end element must be for the current element */
1387     g_assert(xmlTextReaderDepth(reader) == 0);
1388     return osm;
1389     break;
1390    
1391     default:
1392     break;
1393     }
1394     ret = xmlTextReaderRead(reader);
1395 achadwick 28
1396     if (num_elems++ > tick_every) {
1397     num_elems = 0;
1398     banner_busy_tick();
1399     }
1400 harbaum 8 }
1401    
1402     g_assert(0);
1403     return NULL;
1404     }
1405    
1406     static osm_t *process_file(const char *filename) {
1407     osm_t *osm = NULL;
1408     xmlTextReaderPtr reader;
1409     int ret;
1410    
1411     reader = xmlReaderForFile(filename, NULL, 0);
1412     if (reader != NULL) {
1413     ret = xmlTextReaderRead(reader);
1414     if(ret == 1) {
1415     char *name = (char*)xmlTextReaderConstName(reader);
1416     if(name && strcasecmp(name, "osm") == 0)
1417     osm = process_osm(reader);
1418     } else
1419     printf("file empty\n");
1420    
1421     xmlFreeTextReader(reader);
1422     } else {
1423     fprintf(stderr, "Unable to open %s\n", filename);
1424     }
1425     return osm;
1426     }
1427    
1428     /* ----------------------- end of stream parser tests ------------------- */
1429     #endif
1430    
1431 harbaum 39 #ifdef OSM_QND_XML_PARSER
1432     /* -------------------------- qnd-xml parser tests ------------------- */
1433    
1434 harbaum 40 #ifdef USE_FLOAT
1435     #define GET_PROP_POS(a,b,c) qnd_xml_get_prop_float(a, b, c)
1436     #else
1437     #define GET_PROP_POS(a,b,c) qnd_xml_get_prop_double(a, b, c)
1438     #endif
1439    
1440 harbaum 39 gboolean osm_bounds_cb(qnd_xml_stack_t *stack,
1441     qnd_xml_attribute_t *attributes, gpointer data) {
1442    
1443     /* get parent pointer */
1444     osm_t *osm = (osm_t*)stack->prev->userdata[0];
1445    
1446     if(osm->bounds) {
1447     errorf(NULL, "Doubly defined bounds");
1448     return FALSE;
1449     }
1450    
1451     bounds_t *bounds = osm->bounds = g_new0(bounds_t, 1);
1452    
1453     bounds->ll_min.lat = bounds->ll_min.lon = NAN;
1454     bounds->ll_max.lat = bounds->ll_max.lon = NAN;
1455    
1456 harbaum 40 GET_PROP_POS(attributes, "minlat", &bounds->ll_min.lat);
1457     GET_PROP_POS(attributes, "minlon", &bounds->ll_min.lon);
1458     GET_PROP_POS(attributes, "maxlat", &bounds->ll_max.lat);
1459     GET_PROP_POS(attributes, "maxlon", &bounds->ll_max.lon);
1460 harbaum 39
1461     if(isnan(bounds->ll_min.lat) || isnan(bounds->ll_min.lon) ||
1462     isnan(bounds->ll_max.lat) || isnan(bounds->ll_max.lon)) {
1463     errorf(NULL, "Invalid coordinate in bounds (%f/%f/%f/%f)",
1464     bounds->ll_min.lat, bounds->ll_min.lon,
1465     bounds->ll_max.lat, bounds->ll_max.lon);
1466    
1467     osm_bounds_free(bounds);
1468     osm->bounds = NULL;
1469     return FALSE;
1470     }
1471    
1472    
1473     /* calculate map zone which will be used as a reference for all */
1474     /* drawing/projection later on */
1475     pos_t center = { (bounds->ll_max.lat + bounds->ll_min.lat)/2,
1476     (bounds->ll_max.lon + bounds->ll_min.lon)/2 };
1477    
1478     pos2lpos_center(&center, &bounds->center);
1479    
1480     /* the scale is needed to accomodate for "streching" */
1481     /* by the mercartor projection */
1482     bounds->scale = cos(DEG2RAD(center.lat));
1483    
1484     pos2lpos_center(&bounds->ll_min, &bounds->min);
1485     bounds->min.x -= bounds->center.x;
1486     bounds->min.y -= bounds->center.y;
1487     bounds->min.x *= bounds->scale;
1488     bounds->min.y *= bounds->scale;
1489    
1490     pos2lpos_center(&bounds->ll_max, &bounds->max);
1491     bounds->max.x -= bounds->center.x;
1492     bounds->max.y -= bounds->center.y;
1493     bounds->max.x *= bounds->scale;
1494     bounds->max.y *= bounds->scale;
1495    
1496     return TRUE;
1497     }
1498    
1499     static gboolean osm_tag_cb(qnd_xml_stack_t *stack,
1500     qnd_xml_attribute_t *attributes, gpointer data) {
1501    
1502     tag_t *tag = *(tag_t**)stack->prev->userdata[1] = g_new0(tag_t, 1);
1503    
1504     tag->key = qnd_xml_get_prop_str(attributes, "k");
1505     tag->value = qnd_xml_get_prop_str(attributes, "v");
1506    
1507     if(!tag->key || !tag->value) {
1508     printf("incomplete tag key/value %s/%s\n", tag->key, tag->value);
1509     osm_tags_free(tag);
1510     tag = NULL;
1511     } else
1512     stack->prev->userdata[1] = &tag->next;
1513    
1514     return TRUE;
1515     }
1516    
1517     static gboolean osm_node_cb(qnd_xml_stack_t *stack,
1518     qnd_xml_attribute_t *attributes, gpointer data) {
1519    
1520     osm_t *osm = (osm_t*)stack->prev->userdata[0];
1521    
1522     /* allocate a new node structure. userdata[1] points to the current */
1523     /* position a new node is to be stored */
1524     node_t *node = *(node_t**)stack->prev->userdata[1] =
1525     stack->userdata[0] = g_new0(node_t, 1);
1526     stack->prev->userdata[1] = &node->next;
1527    
1528     qnd_xml_get_prop_gulong(attributes, "id", &node->id);
1529 harbaum 40 GET_PROP_POS(attributes, "lat", &node->pos.lat);
1530     GET_PROP_POS(attributes, "lon", &node->pos.lon);
1531 harbaum 39 node->user = osm_user(osm, qnd_xml_get_prop(attributes, "user"));
1532     node->visible = qnd_xml_get_prop_is(attributes, "visible", "true");
1533     node->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp"));
1534    
1535     pos2lpos(osm->bounds, &node->pos, &node->lpos);
1536    
1537     /* store current tag pointer in userdata for fast access to current tag */
1538     stack->userdata[1] = &node->tag;
1539    
1540 harbaum 42 /* append node to end of hash table if present */
1541     if(osm->node_hash) {
1542     hash_item_t **item = &osm->node_hash->hash[ID2HASH(node->id)];
1543     while(*item) item = &(*item)->next;
1544    
1545     *item = g_new0(hash_item_t, 1);
1546     (*item)->data.node = node;
1547     }
1548    
1549 harbaum 39 return TRUE;
1550     }
1551    
1552     static gboolean osm_way_nd_cb(qnd_xml_stack_t *stack,
1553     qnd_xml_attribute_t *attributes, gpointer data) {
1554    
1555     osm_t *osm = (osm_t*)stack->prev->prev->userdata[0];
1556    
1557     item_id_t id;
1558     if(qnd_xml_get_prop_gulong(attributes, "ref", &id)) {
1559     /* allocate a new node_chain structure */
1560     node_chain_t *node_chain = *(node_chain_t**)stack->prev->userdata[2] =
1561     g_new0(node_chain_t, 1);
1562    
1563     /* search matching node */
1564 harbaum 42 node_chain->node = osm_get_node_by_id(osm, id);
1565 harbaum 39 if(!node_chain->node) printf("Node id %lu not found\n", id);
1566 harbaum 42 else node_chain->node->ways++;
1567 harbaum 39
1568     stack->prev->userdata[2] = &node_chain->next;
1569     }
1570    
1571     return TRUE;
1572     }
1573    
1574     gboolean osm_way_cb(qnd_xml_stack_t *stack,
1575     qnd_xml_attribute_t *attributes, gpointer data) {
1576    
1577     osm_t *osm = (osm_t*)stack->prev->userdata[0];
1578    
1579     /* allocate a new way structure. userdata[2] points to the current */
1580     /* position a new way is to be stored in the way list */
1581     way_t *way = *(way_t**)stack->prev->userdata[2] =
1582     stack->userdata[0] = g_new0(way_t, 1);
1583     stack->prev->userdata[2] = &way->next;
1584    
1585     qnd_xml_get_prop_gulong(attributes, "id", &way->id);
1586     way->user = osm_user(osm, qnd_xml_get_prop(attributes, "user"));
1587     way->visible = qnd_xml_get_prop_is(attributes, "visible", "true");
1588     way->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp"));
1589    
1590     /* store current tag and node_chain pointers in userdata for fast */
1591     /* access to current tag/node_chain entry */
1592     stack->userdata[1] = &way->tag;
1593     stack->userdata[2] = &way->node_chain;
1594    
1595 harbaum 42 /* append way to end of hash table if present */
1596     if(osm->way_hash) {
1597     hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)];
1598     while(*item) item = &(*item)->next;
1599    
1600     *item = g_new0(hash_item_t, 1);
1601     (*item)->data.way = way;
1602     }
1603    
1604 harbaum 39 return TRUE;
1605     }
1606    
1607     static gboolean osm_rel_member_cb(qnd_xml_stack_t *stack,
1608     qnd_xml_attribute_t *attributes, gpointer data) {
1609    
1610     osm_t *osm = (osm_t*)stack->prev->prev->userdata[0];
1611    
1612     member_t *member = *(member_t**)stack->prev->userdata[2] =
1613     g_new0(member_t, 1);
1614     stack->prev->userdata[2] = &member->next;
1615     member->type = ILLEGAL;
1616    
1617     char *type = qnd_xml_get_prop(attributes, "type");
1618     if(type) {
1619     if(strcasecmp(type, "way") == 0) member->type = WAY;
1620     else if(strcasecmp(type, "node") == 0) member->type = NODE;
1621     else if(strcasecmp(type, "relation") == 0) member->type = RELATION;
1622     }
1623    
1624     item_id_t id;
1625     if(qnd_xml_get_prop_gulong(attributes, "ref", &id)) {
1626     switch(member->type) {
1627     case ILLEGAL:
1628     printf("Unable to store illegal type\n");
1629     break;
1630    
1631     case WAY:
1632     /* search matching way */
1633 harbaum 42 member->way = osm_get_way_by_id(osm, id);
1634 harbaum 39 if(!member->way) {
1635     member->type = WAY_ID;
1636     member->id = id;
1637     }
1638     break;
1639    
1640     case NODE:
1641     /* search matching node */
1642 harbaum 42 member->node = osm_get_node_by_id(osm, id);
1643 harbaum 39 if(!member->node) {
1644     member->type = NODE_ID;
1645     member->id = id;
1646     }
1647     break;
1648    
1649     case RELATION:
1650     /* search matching relation */
1651 harbaum 42 member->relation = osm_get_relation_by_id(osm, id);
1652 harbaum 39 if(!member->relation) {
1653     member->type = NODE_ID;
1654     member->id = id;
1655     }
1656     break;
1657    
1658     case WAY_ID:
1659     case NODE_ID:
1660     case RELATION_ID:
1661     break;
1662     }
1663     }
1664    
1665     return TRUE;
1666     }
1667    
1668     gboolean osm_rel_cb(qnd_xml_stack_t *stack,
1669     qnd_xml_attribute_t *attributes, gpointer data) {
1670    
1671     osm_t *osm = (osm_t*)stack->prev->userdata[0];
1672    
1673     /* allocate a new relation structure. userdata[3] points to the current */
1674     /* position a new relation is to be stored at in the relation list */
1675     relation_t *relation = *(relation_t**)stack->prev->userdata[3] =
1676     stack->userdata[0] = g_new0(relation_t, 1);
1677     stack->prev->userdata[3] = &relation->next;
1678    
1679     qnd_xml_get_prop_gulong(attributes, "id", &relation->id);
1680     relation->user = osm_user(osm, qnd_xml_get_prop(attributes, "user"));
1681     relation->visible = qnd_xml_get_prop_is(attributes, "visible", "true");
1682     relation->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp"));
1683    
1684     /* store current tag and member pointers in userdata for fast access */
1685     /* to current tag and members in their chains */
1686     stack->userdata[1] = &relation->tag;
1687     stack->userdata[2] = &relation->member;
1688    
1689     return TRUE;
1690     }
1691    
1692     gboolean osm_cb(qnd_xml_stack_t *stack,
1693     qnd_xml_attribute_t *attributes, gpointer data) {
1694    
1695     g_assert(!stack->userdata[0]);
1696    
1697     /* also set parents (roots) userdata as it's the parsers return value */
1698     osm_t *osm = stack->prev->userdata[0] =
1699     stack->userdata[0] = g_new0(osm_t, 1);
1700    
1701 harbaum 42 osm->node_hash = g_new0(hash_table_t, 1);
1702     osm->way_hash = g_new0(hash_table_t, 1);
1703    
1704 harbaum 39 /* store direct pointers for faster list access */
1705     /* (otherwise we'd have to search the end of the lists for every item */
1706     /* to be attached) */
1707     stack->userdata[1] = &osm->node;
1708     stack->userdata[2] = &osm->way;
1709     stack->userdata[3] = &osm->relation;
1710    
1711     return TRUE;
1712     }
1713    
1714    
1715     /* these structures describe the content qnd_xml expects while parsing */
1716     qnd_xml_entry_t osm_node_tag = { "tag", osm_tag_cb, QND_XML_LEAF };
1717    
1718     qnd_xml_entry_t osm_way_tag = { "tag", osm_tag_cb, QND_XML_LEAF };
1719     qnd_xml_entry_t osm_way_nd = { "nd", osm_way_nd_cb, QND_XML_LEAF };
1720    
1721     qnd_xml_entry_t osm_rel_tag = { "tag", osm_tag_cb, QND_XML_LEAF };
1722     qnd_xml_entry_t osm_rel_member = { "member", osm_rel_member_cb, QND_XML_LEAF };
1723    
1724     qnd_xml_entry_t osm_bounds = { "bounds", osm_bounds_cb, QND_XML_LEAF };
1725    
1726     qnd_xml_entry_t *node_children[] = { &osm_node_tag },
1727     osm_node = { "node", osm_node_cb, QND_XML_CHILDREN(node_children) };
1728    
1729     qnd_xml_entry_t *way_children[] = { &osm_way_tag, &osm_way_nd },
1730     osm_way = { "way", osm_way_cb, QND_XML_CHILDREN(way_children) };
1731    
1732     qnd_xml_entry_t *rel_children[] = { &osm_rel_tag, &osm_rel_member },
1733     osm_rel = { "rel", osm_rel_cb, QND_XML_CHILDREN(rel_children) };
1734    
1735     /* the osm element */
1736     qnd_xml_entry_t *osm_children[] = {
1737     &osm_bounds, &osm_node, &osm_way, &osm_rel };
1738     qnd_xml_entry_t osm = { "osm", osm_cb, QND_XML_CHILDREN(osm_children) };
1739    
1740     /* the root element */
1741     qnd_xml_entry_t *root_children[] = { &osm };
1742     qnd_xml_entry_t root = { "<root>", NULL, QND_XML_CHILDREN(root_children) };
1743    
1744     // gcc `pkg-config --cflags --libs glib-2.0` -o qnd_xml qnd_xml.c
1745    
1746    
1747    
1748     /* ----------------------- end of qnd-xml parser tests ------------------- */
1749     #endif
1750    
1751    
1752 harbaum 8 #include <sys/time.h>
1753    
1754 harbaum 1 osm_t *osm_parse(char *filename) {
1755    
1756 harbaum 8 struct timeval start;
1757     gettimeofday(&start, NULL);
1758    
1759 harbaum 39 #ifdef OSM_STREAM_PARSER
1760 harbaum 1 LIBXML_TEST_VERSION;
1761    
1762 harbaum 8 // use stream parser
1763     osm_t *osm = process_file(filename);
1764     xmlCleanupParser();
1765 harbaum 39 #endif
1766 harbaum 8
1767 harbaum 39 #ifdef OSM_DOM_PARSER
1768     LIBXML_TEST_VERSION;
1769    
1770 harbaum 8 // parse into a tree
1771 harbaum 1 /* parse the file and get the DOM */
1772 harbaum 8 xmlDoc *doc = NULL;
1773 harbaum 1 if ((doc = xmlReadFile(filename, NULL, 0)) == NULL) {
1774     xmlErrorPtr errP = xmlGetLastError();
1775     errorf(NULL, "While parsing \"%s\":\n\n%s", filename, errP->message);
1776     return NULL;
1777     }
1778    
1779 harbaum 8 osm_t *osm = osm_parse_doc(doc);
1780     #endif
1781    
1782 harbaum 39 #ifdef OSM_QND_XML_PARSER
1783     osm_t *osm = NULL;
1784     if(!(osm = qnd_xml_parse(filename, &root, NULL))) {
1785     errorf(NULL, "While parsing \"%s\"", filename);
1786     return NULL;
1787     }
1788     #endif
1789    
1790 harbaum 8 struct timeval end;
1791     gettimeofday(&end, NULL);
1792    
1793     printf("total parse time: %ldms\n",
1794     (end.tv_usec - start.tv_usec)/1000 +
1795     (end.tv_sec - start.tv_sec)*1000);
1796    
1797     return osm;
1798 harbaum 1 }
1799    
1800     gboolean osm_sanity_check(GtkWidget *parent, osm_t *osm) {
1801     if(!osm->bounds) {
1802 achadwick 6 errorf(parent, _("Invalid data in OSM file:\n"
1803 harbaum 1 "Boundary box missing!"));
1804     return FALSE;
1805     }
1806     if(!osm->node) {
1807 achadwick 6 errorf(parent, _("Invalid data in OSM file:\n"
1808 harbaum 1 "No drawable content found!"));
1809     return FALSE;
1810     }
1811     return TRUE;
1812     }
1813    
1814     /* ------------------------- misc access functions -------------- */
1815    
1816     char *osm_tag_get_by_key(tag_t *tag, char *key) {
1817     if(!tag || !key) return NULL;
1818    
1819     while(tag) {
1820     if(strcasecmp(tag->key, key) == 0)
1821     return tag->value;
1822    
1823     tag = tag->next;
1824     }
1825    
1826     return NULL;
1827     }
1828    
1829     char *osm_way_get_value(way_t *way, char *key) {
1830     tag_t *tag = way->tag;
1831    
1832     while(tag) {
1833     if(strcasecmp(tag->key, key) == 0)
1834     return tag->value;
1835    
1836     tag = tag->next;
1837     }
1838    
1839     return NULL;
1840     }
1841    
1842     char *osm_node_get_value(node_t *node, char *key) {
1843     tag_t *tag = node->tag;
1844    
1845     while(tag) {
1846     if(strcasecmp(tag->key, key) == 0)
1847     return tag->value;
1848    
1849     tag = tag->next;
1850     }
1851    
1852     return NULL;
1853     }
1854    
1855     gboolean osm_way_has_value(way_t *way, char *str) {
1856     tag_t *tag = way->tag;
1857    
1858     while(tag) {
1859     if(tag->value && strcasecmp(tag->value, str) == 0)
1860     return TRUE;
1861    
1862     tag = tag->next;
1863     }
1864     return FALSE;
1865     }
1866    
1867     gboolean osm_node_has_value(node_t *node, char *str) {
1868     tag_t *tag = node->tag;
1869    
1870     while(tag) {
1871     if(tag->value && strcasecmp(tag->value, str) == 0)
1872     return TRUE;
1873    
1874     tag = tag->next;
1875     }
1876     return FALSE;
1877     }
1878    
1879     gboolean osm_node_has_tag(node_t *node) {
1880     tag_t *tag = node->tag;
1881    
1882     if(tag && strcasecmp(tag->key, "created_by") == 0)
1883     tag = tag->next;
1884    
1885     return tag != NULL;
1886     }
1887    
1888     /* return true if node is part of way */
1889     gboolean osm_node_in_way(way_t *way, node_t *node) {
1890     node_chain_t *node_chain = way->node_chain;
1891     while(node_chain) {
1892     if(node_chain->node == node)
1893     return TRUE;
1894    
1895     node_chain = node_chain->next;
1896     }
1897     return FALSE;
1898     }
1899    
1900     static void osm_generate_tags(tag_t *tag, xmlNodePtr node) {
1901     while(tag) {
1902     /* make sure "created_by" tag contains our id */
1903     if(strcasecmp(tag->key, "created_by") == 0) {
1904     g_free(tag->value);
1905     tag->value = g_strdup(PACKAGE " v" VERSION);
1906     }
1907    
1908     xmlNodePtr tag_node = xmlNewChild(node, NULL, BAD_CAST "tag", NULL);
1909     xmlNewProp(tag_node, BAD_CAST "k", BAD_CAST tag->key);
1910     xmlNewProp(tag_node, BAD_CAST "v", BAD_CAST tag->value);
1911     tag = tag->next;
1912     }
1913     }
1914    
1915     /* build xml representation for a way */
1916     char *osm_generate_xml(osm_t *osm, type_t type, void *item) {
1917     char str[32];
1918     xmlChar *result = NULL;
1919     int len = 0;
1920    
1921     LIBXML_TEST_VERSION;
1922    
1923     xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
1924     xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "osm");
1925     xmlNewProp(root_node, BAD_CAST "version", BAD_CAST "0.5");
1926     xmlNewProp(root_node, BAD_CAST "generator", BAD_CAST PACKAGE " V" VERSION);
1927     xmlDocSetRootElement(doc, root_node);
1928    
1929     switch(type) {
1930     case NODE:
1931     {
1932     node_t *node = (node_t*)item;
1933     xmlNodePtr node_node = xmlNewChild(root_node, NULL,
1934     BAD_CAST "node", NULL);
1935     /* new nodes don't have an id, but get one after the upload */
1936     if(!(node->flags & OSM_FLAG_NEW)) {
1937     snprintf(str, sizeof(str), "%u", (unsigned)node->id);
1938     xmlNewProp(node_node, BAD_CAST "id", BAD_CAST str);
1939     }
1940     g_ascii_dtostr(str, sizeof(str), node->pos.lat);
1941     xmlNewProp(node_node, BAD_CAST "lat", BAD_CAST str);
1942     g_ascii_dtostr(str, sizeof(str), node->pos.lon);
1943     xmlNewProp(node_node, BAD_CAST "lon", BAD_CAST str);
1944     osm_generate_tags(node->tag, node_node);
1945     }
1946     break;
1947    
1948     case WAY:
1949     {
1950     way_t *way = (way_t*)item;
1951     xmlNodePtr way_node = xmlNewChild(root_node, NULL, BAD_CAST "way", NULL);
1952     snprintf(str, sizeof(str), "%u", (unsigned)way->id);
1953     xmlNewProp(way_node, BAD_CAST "id", BAD_CAST str);
1954    
1955     node_chain_t *node_chain = way->node_chain;
1956     while(node_chain) {
1957     xmlNodePtr nd_node = xmlNewChild(way_node, NULL, BAD_CAST "nd", NULL);
1958     char *str = g_strdup_printf("%ld", node_chain->node->id);
1959     xmlNewProp(nd_node, BAD_CAST "ref", BAD_CAST str);
1960     g_free(str);
1961     node_chain = node_chain->next;
1962     }
1963    
1964     osm_generate_tags(way->tag, way_node);
1965     }
1966     break;
1967    
1968     case RELATION:
1969     {
1970     relation_t *relation = (relation_t*)item;
1971     xmlNodePtr rel_node = xmlNewChild(root_node, NULL,
1972     BAD_CAST "relation", NULL);
1973     snprintf(str, sizeof(str), "%u", (unsigned)relation->id);
1974     xmlNewProp(rel_node, BAD_CAST "id", BAD_CAST str);
1975    
1976     member_t *member = relation->member;
1977     while(member) {
1978     xmlNodePtr m_node = xmlNewChild(rel_node,NULL,BAD_CAST "member", NULL);
1979     char *str = NULL;
1980    
1981     switch(member->type) {
1982     case NODE:
1983     xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "node");
1984     str = g_strdup_printf("%ld", member->node->id);
1985     break;
1986    
1987     case WAY:
1988     xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "way");
1989     str = g_strdup_printf("%ld", member->way->id);
1990     break;
1991    
1992     case RELATION:
1993     xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "relation");
1994     str = g_strdup_printf("%ld", member->relation->id);
1995     break;
1996    
1997     default:
1998     break;
1999     }
2000    
2001     if(str) {
2002     xmlNewProp(m_node, BAD_CAST "ref", BAD_CAST str);
2003     g_free(str);
2004     }
2005    
2006     if(member->role)
2007     xmlNewProp(m_node, BAD_CAST "role", BAD_CAST member->role);
2008     else
2009     xmlNewProp(m_node, BAD_CAST "role", BAD_CAST "");
2010    
2011     member = member->next;
2012     }
2013     osm_generate_tags(relation->tag, rel_node);
2014     }
2015     break;
2016    
2017     default:
2018     printf("neither NODE nor WAY nor RELATION\n");
2019     g_assert(0);
2020     break;
2021     }
2022    
2023     xmlDocDumpFormatMemoryEnc(doc, &result, &len, "UTF-8", 1);
2024     xmlFreeDoc(doc);
2025     xmlCleanupParser();
2026    
2027     // puts("xml encoding result:");
2028     // puts((char*)result);
2029    
2030     return (char*)result;
2031     }
2032    
2033     /* build xml representation for a node */
2034     char *osm_generate_xml_node(osm_t *osm, node_t *node) {
2035     return osm_generate_xml(osm, NODE, node);
2036     }
2037    
2038     /* build xml representation for a way */
2039     char *osm_generate_xml_way(osm_t *osm, way_t *way) {
2040     return osm_generate_xml(osm, WAY, way);
2041     }
2042    
2043     /* build xml representation for a relation */
2044     char *osm_generate_xml_relation(osm_t *osm, relation_t *relation) {
2045     return osm_generate_xml(osm, RELATION, relation);
2046     }
2047    
2048 harbaum 42 /* the following three functions are eating much CPU power */
2049     /* as they search the objects lists. Hashing is supposed to help */
2050 harbaum 1 node_t *osm_get_node_by_id(osm_t *osm, item_id_t id) {
2051 harbaum 42 if(id > 0 && osm->node_hash) {
2052     // use hash table if present
2053     hash_item_t *item = osm->node_hash->hash[ID2HASH(id)];
2054     while(item) {
2055     if(item->data.node->id == id)
2056     return item->data.node;
2057    
2058     item = item->next;
2059     }
2060     }
2061    
2062     /* use linear search if no hash tables are present or search in hash table failed */
2063 harbaum 1 node_t *node = osm->node;
2064     while(node) {
2065     if(node->id == id)
2066     return node;
2067 harbaum 42
2068 harbaum 1 node = node->next;
2069     }
2070 harbaum 42
2071 harbaum 1 return NULL;
2072     }
2073    
2074     way_t *osm_get_way_by_id(osm_t *osm, item_id_t id) {
2075 harbaum 42 if(id > 0 && osm->way_hash) {
2076     // use hash table if present
2077     hash_item_t *item = osm->way_hash->hash[ID2HASH(id)];
2078     while(item) {
2079     if(item->data.way->id == id)
2080     return item->data.way;
2081    
2082     item = item->next;
2083     }
2084     }
2085    
2086     /* use linear search if no hash tables are present or search on hash table failed */
2087 harbaum 1 way_t *way = osm->way;
2088     while(way) {
2089     if(way->id == id)
2090     return way;
2091 harbaum 42
2092 harbaum 1 way = way->next;
2093     }
2094 harbaum 42
2095 harbaum 1 return NULL;
2096     }
2097    
2098     relation_t *osm_get_relation_by_id(osm_t *osm, item_id_t id) {
2099 harbaum 42 // use linear search
2100 harbaum 1 relation_t *relation = osm->relation;
2101     while(relation) {
2102     if(relation->id == id)
2103     return relation;
2104    
2105     relation = relation->next;
2106     }
2107    
2108     return NULL;
2109     }
2110    
2111     /* ---------- edit functions ------------- */
2112    
2113     item_id_t osm_new_way_id(osm_t *osm) {
2114     item_id_t id = -1;
2115    
2116     while(TRUE) {
2117     gboolean found = FALSE;
2118     way_t *way = osm->way;
2119     while(way) {
2120     if(way->id == id)
2121     found = TRUE;
2122    
2123     way = way->next;
2124     }
2125    
2126     /* no such id so far -> use it */
2127     if(!found) return id;
2128    
2129     id--;
2130     }
2131     g_assert(0);
2132     return 0;
2133     }
2134    
2135     item_id_t osm_new_node_id(osm_t *osm) {
2136     item_id_t id = -1;
2137    
2138     while(TRUE) {
2139     gboolean found = FALSE;
2140     node_t *node = osm->node;
2141     while(node) {
2142     if(node->id == id)
2143     found = TRUE;
2144    
2145     node = node->next;
2146     }
2147    
2148     /* no such id so far -> use it */
2149     if(!found) return id;
2150    
2151     id--;
2152     }
2153     g_assert(0);
2154     return 0;
2155     }
2156    
2157     node_t *osm_node_new(osm_t *osm, gint x, gint y) {
2158     printf("Creating new node\n");
2159    
2160     node_t *node = g_new0(node_t, 1);
2161     node->lpos.x = x;
2162     node->lpos.y = y;
2163     node->visible = TRUE;
2164     node->time = time(NULL);
2165    
2166     /* add created_by tag */
2167     node->tag = g_new0(tag_t, 1);
2168     node->tag->key = g_strdup("created_by");
2169     node->tag->value = g_strdup(PACKAGE " v" VERSION);
2170    
2171     /* convert screen position back to ll */
2172     lpos2pos(osm->bounds, &node->lpos, &node->pos);
2173    
2174     printf(" new at %d %d (%f %f)\n",
2175     node->lpos.x, node->lpos.y, node->pos.lat, node->pos.lon);
2176    
2177     return node;
2178     }
2179    
2180    
2181     void osm_node_attach(osm_t *osm, node_t *node) {
2182     printf("Attaching node\n");
2183    
2184     node->id = osm_new_node_id(osm);
2185     node->flags = OSM_FLAG_NEW;
2186    
2187     /* attach to end of node list */
2188     node_t **lnode = &osm->node;
2189     while(*lnode) lnode = &(*lnode)->next;
2190     *lnode = node;
2191     }
2192    
2193 harbaum 64 void osm_node_restore(osm_t *osm, node_t *node) {
2194     printf("Restoring node\n");
2195    
2196     /* attach to end of node list */
2197     node_t **lnode = &osm->node;
2198     while(*lnode) lnode = &(*lnode)->next;
2199     *lnode = node;
2200     }
2201    
2202 harbaum 1 way_t *osm_way_new(void) {
2203     printf("Creating new way\n");
2204    
2205     way_t *way = g_new0(way_t, 1);
2206     way->visible = TRUE;
2207     way->flags = OSM_FLAG_NEW;
2208     way->time = time(NULL);
2209    
2210     /* add created_by tag */
2211     way->tag = g_new0(tag_t, 1);
2212     way->tag->key = g_strdup("created_by");
2213     way->tag->value = g_strdup(PACKAGE " v" VERSION);
2214    
2215     return way;
2216     }
2217    
2218     void osm_way_attach(osm_t *osm, way_t *way) {
2219     printf("Attaching way\n");
2220    
2221     way->id = osm_new_way_id(osm);
2222     way->flags = OSM_FLAG_NEW;
2223    
2224     /* attach to end of way list */
2225     way_t **lway = &osm->way;
2226     while(*lway) lway = &(*lway)->next;
2227     *lway = way;
2228     }
2229    
2230     /* returns pointer to chain of ways affected by this deletion */
2231     way_chain_t *osm_node_delete(osm_t *osm, icon_t **icon,
2232     node_t *node, gboolean permanently,
2233     gboolean affect_ways) {
2234     way_chain_t *way_chain = NULL, **cur_way_chain = &way_chain;
2235    
2236     /* new nodes aren't stored on the server and are just deleted permanently */
2237     if(node->flags & OSM_FLAG_NEW) {
2238     printf("About to delete NEW node #%ld -> force permanent delete\n",
2239     node->id);
2240     permanently = TRUE;
2241     }
2242    
2243     /* first remove node from all ways using it */
2244     way_t *way = osm->way;
2245     while(way) {
2246     node_chain_t **chain = &(way->node_chain);
2247     gboolean modified = FALSE;
2248     while(*chain) {
2249     /* remove node from chain */
2250     if(node == (*chain)->node) {
2251     modified = TRUE;
2252     if(affect_ways) {
2253     node_chain_t *next = (*chain)->next;
2254     g_free(*chain);
2255     *chain = next;
2256     } else
2257     chain = &((*chain)->next);
2258     } else
2259     chain = &((*chain)->next);
2260     }
2261    
2262     if(modified) {
2263     way->flags |= OSM_FLAG_DIRTY;
2264    
2265     /* and add the way to the list of affected ways */
2266     *cur_way_chain = g_new0(way_chain_t, 1);
2267     (*cur_way_chain)->way = way;
2268     cur_way_chain = &((*cur_way_chain)->next);
2269     }
2270    
2271     way = way->next;
2272     }
2273    
2274     if(!permanently) {
2275     printf("mark node #%ld as deleted\n", node->id);
2276     node->flags |= OSM_FLAG_DELETED;
2277     } else {
2278     printf("permanently delete node #%ld\n", node->id);
2279    
2280     /* remove it from the chain */
2281     node_t **cnode = &osm->node;
2282     int found = 0;
2283    
2284     while(*cnode) {
2285     if(*cnode == node) {
2286     found++;
2287     *cnode = (*cnode)->next;
2288    
2289     osm_node_free(icon, node);
2290     } else
2291     cnode = &((*cnode)->next);
2292     }
2293     g_assert(found == 1);
2294     }
2295    
2296     return way_chain;
2297     }
2298    
2299     guint osm_way_number_of_nodes(way_t *way) {
2300     guint nodes = 0;
2301     node_chain_t *chain = way->node_chain;
2302     while(chain) {
2303     nodes++;
2304     chain = chain->next;
2305     }
2306     return nodes;
2307     }
2308    
2309     /* return all relations a node is in */
2310     relation_chain_t *osm_node_to_relation(osm_t *osm, node_t *node) {
2311     relation_chain_t *rel_chain = NULL, **cur_rel_chain = &rel_chain;
2312    
2313     relation_t *relation = osm->relation;
2314     while(relation) {
2315     gboolean is_member = FALSE;
2316    
2317     member_t *member = relation->member;
2318     while(member) {
2319     switch(member->type) {
2320     case NODE:
2321     /* nodes are checked directly */
2322     if(member->node == node)
2323     is_member = TRUE;
2324     break;
2325    
2326     case WAY: {
2327     /* ways have to be checked for the nodes they consist of */
2328     node_chain_t *chain = member->way->node_chain;
2329     while(chain && !is_member) {
2330     if(chain->node == node)
2331     is_member = TRUE;
2332    
2333     chain = chain->next;
2334     }
2335     } break;
2336    
2337     default:
2338     break;
2339     }
2340     member = member->next;
2341     }
2342    
2343     /* node is a member of this relation, so move it to the member chain */
2344     if(is_member) {
2345     *cur_rel_chain = g_new0(relation_chain_t, 1);
2346     (*cur_rel_chain)->relation = relation;
2347     cur_rel_chain = &((*cur_rel_chain)->next);
2348     }
2349    
2350     relation = relation->next;
2351     }
2352    
2353     return rel_chain;
2354     }
2355    
2356     /* return all relations a way is in */
2357     relation_chain_t *osm_way_to_relation(osm_t *osm, way_t *way) {
2358     relation_chain_t *rel_chain = NULL, **cur_rel_chain = &rel_chain;
2359    
2360     relation_t *relation = osm->relation;
2361     while(relation) {
2362     gboolean is_member = FALSE;
2363    
2364     member_t *member = relation->member;
2365     while(member) {
2366     switch(member->type) {
2367     case WAY: {
2368     /* ways can be check directly */
2369     if(member->way == way)
2370     is_member = TRUE;
2371     } break;
2372    
2373     default:
2374     break;
2375     }
2376     member = member->next;
2377     }
2378    
2379     /* way is a member of this relation, so move it to the member chain */
2380     if(is_member) {
2381     *cur_rel_chain = g_new0(relation_chain_t, 1);
2382     (*cur_rel_chain)->relation = relation;
2383     cur_rel_chain = &((*cur_rel_chain)->next);
2384     }
2385    
2386     relation = relation->next;
2387     }
2388    
2389     return rel_chain;
2390     }
2391    
2392     /* return all ways a node is in */
2393     way_chain_t *osm_node_to_way(osm_t *osm, node_t *node) {
2394     way_chain_t *chain = NULL, **cur_chain = &chain;
2395    
2396     way_t *way = osm->way;
2397     while(way) {
2398     gboolean is_member = FALSE;
2399    
2400     node_chain_t *node_chain = way->node_chain;
2401     while(node_chain) {
2402     if(node_chain->node == node)
2403     is_member = TRUE;
2404    
2405     node_chain = node_chain->next;
2406     }
2407    
2408     /* node is a member of this relation, so move it to the member chain */
2409     if(is_member) {
2410     *cur_chain = g_new0(way_chain_t, 1);
2411     (*cur_chain)->way = way;
2412     cur_chain = &((*cur_chain)->next);
2413     }
2414    
2415 harbaum 8 way = way->next;
2416 harbaum 1 }
2417    
2418     return chain;
2419     }
2420    
2421     gboolean osm_position_within_bounds(osm_t *osm, gint x, gint y) {
2422     if((x < osm->bounds->min.x) || (x > osm->bounds->max.x)) return FALSE;
2423     if((y < osm->bounds->min.y) || (y > osm->bounds->max.y)) return FALSE;
2424     return TRUE;
2425     }
2426    
2427     /* remove the given node from all relations. used if the node is to */
2428     /* be deleted */
2429     void osm_node_remove_from_relation(osm_t *osm, node_t *node) {
2430     relation_t *relation = osm->relation;
2431     printf("removing node #%ld from all relations:\n", node->id);
2432    
2433     while(relation) {
2434     member_t **member = &relation->member;
2435     while(*member) {
2436     if(((*member)->type == NODE) &&
2437     ((*member)->node == node)) {
2438    
2439     printf(" from relation #%ld\n", relation->id);
2440    
2441     member_t *cur = *member;
2442     *member = (*member)->next;
2443     osm_member_free(cur);
2444    
2445     relation->flags |= OSM_FLAG_DIRTY;
2446     } else
2447     member = &(*member)->next;
2448     }
2449     relation = relation->next;
2450     }
2451     }
2452    
2453     /* remove the given way from all relations */
2454     void osm_way_remove_from_relation(osm_t *osm, way_t *way) {
2455     relation_t *relation = osm->relation;
2456     printf("removing way #%ld from all relations:\n", way->id);
2457    
2458     while(relation) {
2459     member_t **member = &relation->member;
2460     while(*member) {
2461     if(((*member)->type == WAY) &&
2462     ((*member)->way == way)) {
2463    
2464     printf(" from relation #%ld\n", relation->id);
2465    
2466     member_t *cur = *member;
2467     *member = (*member)->next;
2468     osm_member_free(cur);
2469    
2470     relation->flags |= OSM_FLAG_DIRTY;
2471     } else
2472     member = &(*member)->next;
2473     }
2474     relation = relation->next;
2475     }
2476     }
2477    
2478     void osm_way_delete(osm_t *osm, icon_t **icon,
2479     way_t *way, gboolean permanently) {
2480    
2481     /* new ways aren't stored on the server and are just deleted permanently */
2482     if(way->flags & OSM_FLAG_NEW) {
2483     printf("About to delete NEW way #%ld -> force permanent delete\n",
2484     way->id);
2485     permanently = TRUE;
2486     }
2487    
2488     /* delete all nodes that aren't in other use now */
2489     node_chain_t **chain = &way->node_chain;
2490     while(*chain) {
2491    
2492     (*chain)->node->ways--;
2493     printf("checking node #%ld (still used by %d)\n",
2494     (*chain)->node->id, (*chain)->node->ways);
2495    
2496     /* this node must only be part of this way */
2497     if(!(*chain)->node->ways) {
2498     /* delete this node, but don't let this actually affect the */
2499     /* associated ways as the only such way is the oen we are currently */
2500     /* deleting */
2501     way_chain_t *way_chain =
2502     osm_node_delete(osm, icon, (*chain)->node, FALSE, FALSE);
2503     g_assert(way_chain);
2504     while(way_chain) {
2505     way_chain_t *way_next = way_chain->next;
2506     g_assert(way_chain->way == way);
2507     g_free(way_chain);
2508     way_chain = way_next;
2509     }
2510     }
2511    
2512     node_chain_t *cur = (*chain);
2513     *chain = cur->next;
2514     g_free(cur);
2515     }
2516     way->node_chain = NULL;
2517    
2518     if(!permanently) {
2519     printf("mark way #%ld as deleted\n", way->id);
2520     way->flags |= OSM_FLAG_DELETED;
2521     } else {
2522     printf("permanently delete way #%ld\n", way->id);
2523    
2524     /* remove it from the chain */
2525     way_t **cway = &osm->way;
2526     int found = 0;
2527    
2528     while(*cway) {
2529     if(*cway == way) {
2530     found++;
2531     *cway = (*cway)->next;
2532    
2533     osm_way_free(way);
2534     } else
2535     cway = &((*cway)->next);
2536     }
2537     g_assert(found == 1);
2538     }
2539     }
2540    
2541     void osm_way_revert(way_t *way) {
2542     node_chain_t *new = NULL;
2543    
2544     /* walk old chain first to last */
2545     node_chain_t *old = way->node_chain;
2546     while(old) {
2547     node_chain_t *next = old->next;
2548    
2549     /* and prepend each node to the new chain */
2550     old->next = new;
2551     new = old;
2552    
2553     old = next;
2554     }
2555    
2556     way->node_chain = new;
2557     }
2558    
2559     node_t *osm_way_get_first_node(way_t *way) {
2560     node_chain_t *chain = way->node_chain;
2561     if(!chain) return NULL;
2562     return chain->node;
2563     }
2564    
2565     node_t *osm_way_get_last_node(way_t *way) {
2566     node_chain_t *chain = way->node_chain;
2567    
2568     while(chain && chain->next) chain=chain->next;
2569    
2570     if(!chain) return NULL;
2571    
2572     return chain->node;
2573     }
2574    
2575     void osm_way_rotate(way_t *way, gint offset) {
2576     if(!offset) return;
2577    
2578     /* needs at least two nodes to work properly */
2579     g_assert(way->node_chain);
2580     g_assert(way->node_chain->next);
2581    
2582     while(offset--) {
2583     node_chain_t *chain = way->node_chain;
2584     chain->node->ways--; // reduce way count of old start/end node
2585    
2586     /* move all nodes ahead one chain element ... */
2587     while(chain->next) {
2588     chain->node = chain->next->node;
2589     chain = chain->next;
2590     }
2591    
2592     /* ... and make last one same as first one */
2593     chain->node = way->node_chain->node;
2594     chain->node->ways++; // increase way count of new start/end node
2595     }
2596     }
2597    
2598     tag_t *osm_tags_copy(tag_t *src_tag, gboolean update_creator) {
2599     tag_t *new_tags = NULL;
2600     tag_t **dst_tag = &new_tags;
2601    
2602     while(src_tag) {
2603     *dst_tag = g_new0(tag_t, 1);
2604     (*dst_tag)->key = g_strdup(src_tag->key);
2605     if(update_creator && (strcasecmp(src_tag->key, "created_by") == 0))
2606     (*dst_tag)->value = g_strdup(PACKAGE " v" VERSION);
2607     else
2608     (*dst_tag)->value = g_strdup(src_tag->value);
2609    
2610     dst_tag = &(*dst_tag)->next;
2611     src_tag = src_tag->next;
2612     }
2613    
2614     return new_tags;
2615     }
2616 harbaum 54
2617     /* return plain text of type */
2618     char *osm_type_string(type_t type) {
2619     const struct { type_t type; char *name; } types[] = {
2620     { ILLEGAL, "illegal" },
2621     { NODE, "node" },
2622     { WAY, "way" },
2623     { RELATION, "relation" },
2624     { NODE_ID, "node id" },
2625     { WAY_ID, "way id" },
2626     { RELATION_ID, "relation id" },
2627     { 0, NULL }
2628     };
2629    
2630     int i;
2631     for(i=0;types[i].name;i++)
2632     if(type == types[i].type)
2633     return types[i].name;
2634    
2635     return NULL;
2636     }
2637    
2638 harbaum 64 char *osm_object_string(type_t type, void *object) {
2639     char *type_str = osm_type_string(type);
2640 harbaum 54
2641 harbaum 64 if(!object)
2642     return g_strdup_printf("%s #<invalid>", type_str);
2643    
2644     switch(type) {
2645     case ILLEGAL:
2646     return g_strdup_printf("%s #<unspec>", type_str);
2647     break;
2648     case NODE:
2649     return g_strdup_printf("%s #%ld", type_str, ((node_t*)object)->id);
2650     break;
2651     case WAY:
2652     return g_strdup_printf("%s #%ld", type_str, ((way_t*)object)->id);
2653     break;
2654     case RELATION:
2655     return g_strdup_printf("%s #%ld", type_str, ((relation_t*)object)->id);
2656     break;
2657     case NODE_ID:
2658     case WAY_ID:
2659     case RELATION_ID:
2660     return g_strdup_printf("%s #%ld", type_str, *((item_id_t*)object));
2661     break;
2662     }
2663     return NULL;
2664     }
2665    
2666 achadwick 28 // vim:et:ts=8:sw=2:sts=2:ai