Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


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