Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2 - (hide annotations)
Wed Dec 10 00:00:05 2008 UTC (15 years, 5 months ago) by achadwick
File MIME type: text/plain
File size: 40675 byte(s)
Begin trunk. No code changes.
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     #include <stdio.h>
21     #include <stdlib.h>
22     #include <string.h>
23     #include <math.h>
24    
25     #define __USE_XOPEN
26     #include <time.h>
27    
28     #include <libxml/parser.h>
29     #include <libxml/tree.h>
30    
31     #include "appdata.h"
32    
33     #ifndef LIBXML_TREE_ENABLED
34     #error "Tree not enabled in libxml"
35     #endif
36    
37     /* determine where a node/way/relation read from the osm file */
38     /* is inserted into the internal database */
39     // #define OSM_SORT_ID
40     #define OSM_SORT_LAST
41     // #define OSM_SORT_FIRST
42    
43     /* ------------------------- user handling --------------------- */
44    
45     static void osm_bounds_free(bounds_t *bounds) {
46     free(bounds);
47     }
48    
49     static void osm_bounds_dump(bounds_t *bounds) {
50     printf("\nBounds: %f->%f %f->%f\n",
51     bounds->ll_min.lat, bounds->ll_max.lat,
52     bounds->ll_min.lon, bounds->ll_max.lon);
53     }
54    
55     static bounds_t *osm_parse_osm_bounds(osm_t *osm,
56     xmlDocPtr doc, xmlNode *a_node) {
57     char *prop;
58    
59     if(osm->bounds) {
60     errorf(NULL, "Doubly defined bounds");
61     return NULL;
62     }
63    
64     bounds_t *bounds = g_new0(bounds_t, 1);
65    
66     bounds->ll_min.lat = bounds->ll_min.lon = NAN;
67     bounds->ll_max.lat = bounds->ll_max.lon = NAN;
68    
69     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"minlat"))) {
70     bounds->ll_min.lat = g_ascii_strtod(prop, NULL);
71     xmlFree(prop);
72     }
73    
74     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"maxlat"))) {
75     bounds->ll_max.lat = g_ascii_strtod(prop, NULL);
76     xmlFree(prop);
77     }
78    
79     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"minlon"))) {
80     bounds->ll_min.lon = g_ascii_strtod(prop, NULL);
81     xmlFree(prop);
82     }
83    
84     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"maxlon"))) {
85     bounds->ll_max.lon = g_ascii_strtod(prop, NULL);
86     xmlFree(prop);
87     }
88    
89     if(isnan(bounds->ll_min.lat) || isnan(bounds->ll_min.lon) ||
90     isnan(bounds->ll_max.lat) || isnan(bounds->ll_max.lon)) {
91     errorf(NULL, "Invalid coordinate in bounds (%f/%f/%f/%f)",
92     bounds->ll_min.lat, bounds->ll_min.lon,
93     bounds->ll_max.lat, bounds->ll_max.lon);
94    
95     g_free(bounds);
96     return NULL;
97     }
98    
99    
100     /* calculate map zone which will be used as a reference for all */
101     /* drawing/projection later on */
102     pos_t center = { (bounds->ll_max.lat + bounds->ll_min.lat)/2,
103     (bounds->ll_max.lon + bounds->ll_min.lon)/2 };
104    
105     pos2lpos_center(&center, &bounds->center);
106    
107     /* the scale is needed to accomodate for "streching" */
108     /* by the mercartor projection */
109     bounds->scale = cos(DEG2RAD(center.lat));
110    
111     pos2lpos_center(&bounds->ll_min, &bounds->min);
112     bounds->min.x -= bounds->center.x;
113     bounds->min.y -= bounds->center.y;
114     bounds->min.x *= bounds->scale;
115     bounds->min.y *= bounds->scale;
116    
117     pos2lpos_center(&bounds->ll_max, &bounds->max);
118     bounds->max.x -= bounds->center.x;
119     bounds->max.y -= bounds->center.y;
120     bounds->max.x *= bounds->scale;
121     bounds->max.y *= bounds->scale;
122    
123     return bounds;
124     }
125    
126     /* ------------------------- user handling --------------------- */
127    
128     void osm_users_free(user_t *user) {
129     while(user) {
130     user_t *next = user->next;
131    
132     if(user->name) g_free(user->name);
133     g_free(user);
134    
135     user = next;
136     }
137     }
138    
139     void osm_users_dump(user_t *user) {
140     printf("\nUser list:\n");
141     while(user) {
142     printf("Name: %s\n", user->name);
143     user = user->next;
144     }
145     }
146    
147     static user_t *osm_user(osm_t *osm, char *name) {
148    
149     /* search through user list */
150     user_t **user = &osm->user;
151     while(*user && strcasecmp((*user)->name, name) < 0)
152     user = &(*user)->next;
153    
154     /* end of list or inexact match? create new user entry! */
155     if(!*user || strcasecmp((*user)->name, name)) {
156     user_t *new = g_new0(user_t, 1);
157     new->name = g_strdup(name);
158     new->next = *user;
159     *user = new;
160    
161     return new;
162     }
163    
164     return *user;
165     }
166    
167     static
168     time_t convert_iso8601(const char *str) {
169     tzset();
170    
171     struct tm ctime;
172     memset(&ctime, 0, sizeof(struct tm));
173     strptime(str, "%FT%T%z", &ctime);
174    
175     return mktime(&ctime) - timezone;
176     }
177    
178     /* -------------------- tag handling ----------------------- */
179    
180     void osm_tag_free(tag_t *tag) {
181     if(tag->key) g_free(tag->key);
182     if(tag->value) g_free(tag->value);
183     g_free(tag);
184     }
185    
186     void osm_tags_free(tag_t *tag) {
187     while(tag) {
188     tag_t *next = tag->next;
189     osm_tag_free(tag);
190     tag = next;
191     }
192     }
193    
194     static void osm_tags_dump(tag_t *tag) {
195     while(tag) {
196     printf("Key/Val: %s/%s\n", tag->key, tag->value);
197     tag = tag->next;
198     }
199     }
200    
201     tag_t *osm_parse_osm_tag(osm_t *osm, xmlDocPtr doc, xmlNode *a_node) {
202     xmlNode *cur_node = NULL;
203    
204     /* allocate a new tag structure */
205     tag_t *tag = g_new0(tag_t, 1);
206    
207     char *prop;
208     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"k"))) {
209     if(strlen(prop) > 0) tag->key = g_strdup(prop);
210     xmlFree(prop);
211     }
212    
213     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"v"))) {
214     if(strlen(prop) > 0) tag->value = g_strdup(prop);
215     xmlFree(prop);
216     }
217    
218     if(!tag->key || !tag->value) {
219     printf("incomplete tag key/value %s/%s\n", tag->key, tag->value);
220     osm_tags_free(tag);
221     return NULL;
222     }
223    
224     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next)
225     if (cur_node->type == XML_ELEMENT_NODE)
226     printf("found unhandled osm/node/tag/%s\n", cur_node->name);
227    
228     return tag;
229     }
230    
231     gboolean osm_is_creator_tag(tag_t *tag) {
232     if(strcasecmp(tag->key, "created_by") == 0) return TRUE;
233     if(strcasecmp(tag->key, "source") == 0) return TRUE;
234    
235     return FALSE;
236     }
237    
238     gboolean osm_tag_key_and_value_present(tag_t *haystack, tag_t *tag) {
239     while(haystack) {
240     if((strcasecmp(haystack->key, tag->key) == 0) &&
241     (strcasecmp(haystack->value, tag->value) == 0))
242     return TRUE;
243    
244     haystack = haystack->next;
245     }
246     return FALSE;
247     }
248    
249     gboolean osm_tag_key_other_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_way_ends_with_node(way_t *way, node_t *node) {
261     /* and deleted way may even not contain any nodes at all */
262     /* so ignore it */
263     if(way->flags & OSM_FLAG_DELETED)
264     return FALSE;
265    
266     /* any valid way must have at least two nodes */
267     g_assert(way->node_chain && way->node_chain->next);
268    
269     node_chain_t *chain = way->node_chain;
270     if(chain->node == node) return TRUE;
271    
272     while(chain->next) chain = chain->next;
273     if(chain->node == node) return TRUE;
274    
275     return FALSE;
276     }
277    
278     /* ------------------- node handling ------------------- */
279    
280     void osm_node_free(icon_t **icon, node_t *node) {
281     if(node->icon_buf)
282     icon_free(icon, node->icon_buf);
283    
284     /* there must not be anything left in this chain */
285     g_assert(!node->map_item_chain);
286    
287     osm_tags_free(node->tag);
288     g_free(node);
289     }
290    
291     static void osm_nodes_free(icon_t **icon, node_t *node) {
292     while(node) {
293     node_t *next = node->next;
294     osm_node_free(icon, node);
295     node = next;
296     }
297     }
298    
299     void osm_node_dump(node_t *node) {
300     char buf[64];
301     struct tm tm;
302    
303     printf("Id: %lu\n", node->id);
304     printf("User: %s\n", node->user?node->user->name:"<unspecified>");
305     printf("Visible: %s\n", node->visible?"yes":"no");
306    
307     localtime_r(&node->time, &tm);
308     strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", &tm);
309     printf("Time: %s\n", buf);
310     osm_tags_dump(node->tag);
311     }
312    
313     void osm_nodes_dump(node_t *node) {
314     printf("\nNode list:\n");
315     while(node) {
316     osm_node_dump(node);
317     printf("\n");
318     node = node->next;
319     }
320     }
321    
322     static node_t *osm_parse_osm_node(osm_t *osm,
323     xmlDocPtr doc, xmlNode *a_node) {
324     xmlNode *cur_node = NULL;
325    
326     /* allocate a new node structure */
327     node_t *node = g_new0(node_t, 1);
328     node->pos.lat = node->pos.lon = NAN;
329    
330     char *prop;
331     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"id"))) {
332     node->id = strtoul(prop, NULL, 10);
333     xmlFree(prop);
334     }
335    
336     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"lat"))) {
337     node->pos.lat = g_ascii_strtod(prop, NULL);
338     xmlFree(prop);
339     }
340    
341     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"lon"))) {
342     node->pos.lon = g_ascii_strtod(prop, NULL);
343     xmlFree(prop);
344     }
345    
346     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"user"))) {
347     node->user = osm_user(osm, prop);
348     xmlFree(prop);
349     }
350    
351     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"visible"))) {
352     node->visible = (strcasecmp(prop, "true") == 0);
353     xmlFree(prop);
354     }
355    
356     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"timestamp"))) {
357     node->time = convert_iso8601(prop);
358     xmlFree(prop);
359     }
360    
361     /* scan for tags and attach a list of tags */
362     tag_t **tag = &node->tag;
363     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
364     if (cur_node->type == XML_ELEMENT_NODE) {
365     if(strcasecmp((char*)cur_node->name, "tag") == 0) {
366     /* attach tag to node */
367     *tag = osm_parse_osm_tag(osm, doc, cur_node);
368     if(*tag) tag = &((*tag)->next);
369     } else
370     printf("found unhandled osm/node/%s\n", cur_node->name);
371     }
372     }
373    
374     pos2lpos(osm->bounds, &node->pos, &node->lpos);
375    
376     return node;
377     }
378    
379     /* ------------------- way handling ------------------- */
380    
381     void osm_node_chain_free(node_chain_t *node_chain) {
382     while(node_chain) {
383     g_assert(node_chain->node->ways);
384    
385     node_chain_t *next = node_chain->next;
386     node_chain->node->ways--;
387     g_free(node_chain);
388     node_chain = next;
389     }
390     }
391    
392     void osm_way_free(way_t *way) {
393     // printf("freeing way #%ld\n", way->id);
394    
395     osm_node_chain_free(way->node_chain);
396     osm_tags_free(way->tag);
397    
398     /* there must not be anything left in this chain */
399     g_assert(!way->map_item_chain);
400    
401     g_free(way);
402     }
403    
404     static void osm_ways_free(way_t *way) {
405     while(way) {
406     way_t *next = way->next;
407     osm_way_free(way);
408     way = next;
409     }
410     }
411    
412     void osm_way_append_node(way_t *way, node_t *node) {
413     node_chain_t **node_chain = &way->node_chain;
414    
415     while(*node_chain)
416     node_chain = &((*node_chain)->next);
417    
418     *node_chain = g_new0(node_chain_t, 1);
419     (*node_chain)->node = node;
420    
421     node->ways++;
422     }
423    
424     int osm_node_chain_length(node_chain_t *node_chain) {
425     int cnt = 0;
426     while(node_chain) {
427     cnt++;
428     node_chain = node_chain->next;
429     }
430    
431     return cnt;
432     }
433    
434     void osm_way_dump(way_t *way) {
435     char buf[64];
436     struct tm tm;
437    
438     printf("Id: %lu\n", way->id);
439     printf("User: %s\n", way->user?way->user->name:"<unspecified>");
440     printf("Visible: %s\n", way->visible?"yes":"no");
441     node_chain_t *node_chain = way->node_chain;
442     while(node_chain) {
443     printf(" Node: %lu\n", node_chain->node->id);
444     node_chain = node_chain->next;
445     }
446    
447     localtime_r(&way->time, &tm);
448     strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", &tm);
449     printf("Time: %s\n", buf);
450     osm_tags_dump(way->tag);
451     }
452    
453     void osm_ways_dump(way_t *way) {
454     printf("\nWay list:\n");
455     while(way) {
456     osm_way_dump(way);
457     printf("\n");
458     way = way->next;
459     }
460     }
461    
462     node_chain_t *osm_parse_osm_way_nd(osm_t *osm,
463     xmlDocPtr doc, xmlNode *a_node) {
464     char *prop;
465    
466     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"ref"))) {
467     item_id_t id = strtoul(prop, NULL, 10);
468     node_chain_t *node_chain = g_new0(node_chain_t, 1);
469    
470     /* search matching node */
471     node_chain->node = osm->node;
472     while(node_chain->node && node_chain->node->id != id)
473     node_chain->node = node_chain->node->next;
474    
475     if(!node_chain->node) printf("Node id %lu not found\n", id);
476    
477     if(node_chain->node)
478     node_chain->node->ways++;
479    
480     xmlFree(prop);
481    
482     return node_chain;
483     }
484    
485     return NULL;
486     }
487    
488     static way_t *osm_parse_osm_way(osm_t *osm,
489     xmlDocPtr doc, xmlNode *a_node) {
490     xmlNode *cur_node = NULL;
491    
492     /* allocate a new way structure */
493     way_t *way = g_new0(way_t, 1);
494    
495     char *prop;
496     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"id"))) {
497     way->id = strtoul(prop, NULL, 10);
498     xmlFree(prop);
499     }
500    
501     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"user"))) {
502     way->user = osm_user(osm, prop);
503     xmlFree(prop);
504     }
505    
506     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"visible"))) {
507     way->visible = (strcasecmp(prop, "true") == 0);
508     xmlFree(prop);
509     }
510    
511     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"timestamp"))) {
512     way->time = convert_iso8601(prop);
513     xmlFree(prop);
514     }
515    
516     /* scan for tags/nodes and attach their lists */
517     tag_t **tag = &way->tag;
518     node_chain_t **node_chain = &way->node_chain;
519    
520     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
521     if (cur_node->type == XML_ELEMENT_NODE) {
522     if(strcasecmp((char*)cur_node->name, "tag") == 0) {
523     /* attach tag to node */
524     *tag = osm_parse_osm_tag(osm, doc, cur_node);
525     if(*tag) tag = &((*tag)->next);
526     } else if(strcasecmp((char*)cur_node->name, "nd") == 0) {
527     *node_chain = osm_parse_osm_way_nd(osm, doc, cur_node);
528     if(*node_chain)
529     node_chain = &((*node_chain)->next);
530     } else
531     printf("found unhandled osm/node/%s\n", cur_node->name);
532     }
533     }
534    
535     return way;
536     }
537    
538     /* ------------------- relation handling ------------------- */
539    
540     void osm_member_free(member_t *member) {
541     if(member->role) g_free(member->role);
542     g_free(member);
543     }
544    
545     void osm_members_free(member_t *member) {
546     while(member) {
547     member_t *next = member->next;
548     osm_member_free(member);
549     member = next;
550     }
551     }
552    
553     static void osm_relations_free(relation_t *relation) {
554     while(relation) {
555     relation_t *next = relation->next;
556    
557     osm_tags_free(relation->tag);
558     osm_members_free(relation->member);
559    
560     g_free(relation);
561     relation = next;
562     }
563     }
564    
565     void osm_relations_dump(relation_t *relation) {
566     printf("\nRelation list:\n");
567     while(relation) {
568     char buf[64];
569     struct tm tm;
570    
571     printf("Id: %lu\n", relation->id);
572     printf("User: %s\n",
573     relation->user?relation->user->name:"<unspecified>");
574     printf("Visible: %s\n", relation->visible?"yes":"no");
575    
576     member_t *member = relation->member;
577     while(member) {
578     switch(member->type) {
579     case ILLEGAL:
580     case NODE_ID:
581     case WAY_ID:
582     case RELATION_ID:
583     break;
584    
585     case NODE:
586     if(member->node)
587     printf(" Member: Node, id = %lu, role = %s\n",
588     member->node->id, member->role);
589     break;
590    
591     case WAY:
592     if(member->way)
593     printf(" Member: Way, id = %lu, role = %s\n",
594     member->way->id, member->role);
595     break;
596    
597     case RELATION:
598     if(member->relation)
599     printf(" Member: Relation, id = %lu, role = %s\n",
600     member->relation->id, member->role);
601     break;
602     }
603    
604     member = member->next;
605     }
606    
607     localtime_r(&relation->time, &tm);
608     strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", &tm);
609     printf("Time: %s\n", buf);
610     osm_tags_dump(relation->tag);
611    
612     printf("\n");
613     relation = relation->next;
614     }
615     }
616    
617     member_t *osm_parse_osm_relation_member(osm_t *osm,
618     xmlDocPtr doc, xmlNode *a_node) {
619     char *prop;
620     member_t *member = g_new0(member_t, 1);
621     member->type = ILLEGAL;
622    
623     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"type"))) {
624     if(strcasecmp(prop, "way") == 0) member->type = WAY;
625     else if(strcasecmp(prop, "node") == 0) member->type = NODE;
626     else if(strcasecmp(prop, "relation") == 0) member->type = RELATION;
627     xmlFree(prop);
628     }
629    
630     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"ref"))) {
631     item_id_t id = strtoul(prop, NULL, 10);
632    
633     switch(member->type) {
634     case ILLEGAL:
635     printf("Unable to store illegal type\n");
636     break;
637    
638     case WAY:
639     /* search matching way */
640     member->way = osm->way;
641     while(member->way && member->way->id != id)
642     member->way = member->way->next;
643    
644     if(!member->way) {
645     member->type = WAY_ID;
646     member->id = id;
647     }
648     break;
649    
650     case NODE:
651     /* search matching node */
652     member->node = osm->node;
653     while(member->node && member->node->id != id)
654     member->node = member->node->next;
655    
656     if(!member->node) {
657     member->type = NODE_ID;
658     member->id = id;
659     }
660     break;
661    
662     case RELATION:
663     /* search matching relation */
664     member->relation = osm->relation;
665     while(member->relation && member->relation->id != id)
666     member->relation = member->relation->next;
667    
668     if(!member->relation) {
669     member->type = NODE_ID;
670     member->id = id;
671     }
672     break;
673    
674     case WAY_ID:
675     case NODE_ID:
676     case RELATION_ID:
677     break;
678     }
679    
680     xmlFree(prop);
681     }
682    
683     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"role"))) {
684     if(strlen(prop) > 0) member->role = g_strdup(prop);
685     xmlFree(prop);
686     }
687    
688     return member;
689     }
690    
691     static relation_t *osm_parse_osm_relation(osm_t *osm,
692     xmlDocPtr doc, xmlNode *a_node) {
693     xmlNode *cur_node = NULL;
694    
695     /* allocate a new relation structure */
696     relation_t *relation = g_new0(relation_t, 1);
697    
698     char *prop;
699     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"id"))) {
700     relation->id = strtoul(prop, NULL, 10);
701     xmlFree(prop);
702     }
703    
704     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"user"))) {
705     relation->user = osm_user(osm, prop);
706     xmlFree(prop);
707     }
708    
709     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"visible"))) {
710     relation->visible = (strcasecmp(prop, "true") == 0);
711     xmlFree(prop);
712     }
713    
714     if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"timestamp"))) {
715     relation->time = convert_iso8601(prop);
716     xmlFree(prop);
717     }
718    
719     /* scan for tags and attach a list of tags */
720     tag_t **tag = &relation->tag;
721     member_t **member = &relation->member;
722    
723     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
724     if (cur_node->type == XML_ELEMENT_NODE) {
725     if(strcasecmp((char*)cur_node->name, "tag") == 0) {
726     /* attach tag to node */
727     *tag = osm_parse_osm_tag(osm, doc, cur_node);
728     if(*tag) tag = &((*tag)->next);
729     } else if(strcasecmp((char*)cur_node->name, "member") == 0) {
730     *member = osm_parse_osm_relation_member(osm, doc, cur_node);
731     if(*member) member = &((*member)->next);
732     } else
733     printf("found unhandled osm/node/%s\n", cur_node->name);
734     }
735     }
736    
737     return relation;
738     }
739    
740     /* ----------------------- generic xml handling -------------------------- */
741    
742     /* parse loc entry */
743     static void osm_parse_osm(osm_t *osm, xmlDocPtr doc, xmlNode * a_node) {
744     xmlNode *cur_node = NULL;
745    
746     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
747     if (cur_node->type == XML_ELEMENT_NODE) {
748     if(strcasecmp((char*)cur_node->name, "bounds") == 0)
749     osm->bounds = osm_parse_osm_bounds(osm, doc, cur_node);
750     else if(strcasecmp((char*)cur_node->name, "node") == 0) {
751     /* parse node and attach it to chain */
752     node_t *new = osm_parse_osm_node(osm, doc, cur_node);
753     if(new) {
754     node_t **node = &osm->node;
755    
756     #ifdef OSM_SORT_ID
757     /* search chain of nodes */
758     while(*node && ((*node)->id < new->id))
759     node = &(*node)->next;
760     #endif
761    
762     #ifdef OSM_SORT_LAST
763     while(*node) node = &(*node)->next;
764     #endif
765    
766     /* insert into chain */
767     new->next = *node;
768     *node = new;
769     }
770     } else if(strcasecmp((char*)cur_node->name, "way") == 0) {
771     /* parse way and attach it to chain */
772     way_t *new = osm_parse_osm_way(osm, doc, cur_node);
773     if(new) {
774     way_t **way = &osm->way;
775    
776     #ifdef OSM_SORT_ID
777     /* insert into chain */
778     while(*way && ((*way)->id < new->id))
779     way = &(*way)->next;
780     #endif
781    
782     #ifdef OSM_SORT_LAST
783     while(*way) way = &(*way)->next;
784     #endif
785    
786     /* insert into chain */
787     new->next = *way;
788     *way = new;
789     }
790     } else if(strcasecmp((char*)cur_node->name, "relation") == 0) {
791     /* parse relation and attach it to chain */
792     relation_t *new = osm_parse_osm_relation(osm, doc, cur_node);
793     if(new) {
794     relation_t **relation = &osm->relation;
795    
796     #ifdef OSM_SORT_ID
797     /* search chain of ways */
798     while(*relation && ((*relation)->id < new->id))
799     relation = &(*relation)->next;
800     #endif
801    
802     #ifdef OSM_SORT_LAST
803     while(*relation) relation = &(*relation)->next;
804     #endif
805    
806     /* insert into chain */
807     new->next = *relation;
808     *relation = new;
809     }
810     } else
811     printf("found unhandled osm/%s\n", cur_node->name);
812    
813     }
814     }
815     }
816    
817     /* parse root element and search for "osm" */
818     static osm_t *osm_parse_root(xmlDocPtr doc, xmlNode * a_node) {
819     osm_t *osm;
820     xmlNode *cur_node = NULL;
821    
822     /* allocate memory to hold osm file description */
823     osm = g_new0(osm_t, 1);
824    
825     for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
826     if (cur_node->type == XML_ELEMENT_NODE) {
827     /* parse osm osm file ... */
828     if(strcasecmp((char*)cur_node->name, "osm") == 0)
829     osm_parse_osm(osm, doc, cur_node);
830     else
831     printf("found unhandled %s\n", cur_node->name);
832     }
833     }
834    
835     return osm;
836     }
837    
838     static osm_t *osm_parse_doc(xmlDocPtr doc) {
839     osm_t *osm;
840    
841     /* Get the root element node */
842     xmlNode *root_element = xmlDocGetRootElement(doc);
843    
844     osm = osm_parse_root(doc, root_element);
845    
846     /*free the document */
847     xmlFreeDoc(doc);
848    
849     /*
850     * Free the global variables that may
851     * have been allocated by the parser.
852     */
853     xmlCleanupParser();
854    
855     return osm;
856     }
857    
858     /* ------------------ osm handling ----------------- */
859    
860     void osm_free(icon_t **icon, osm_t *osm) {
861     if(!osm) return;
862    
863     if(osm->bounds) osm_bounds_free(osm->bounds);
864     if(osm->user) osm_users_free(osm->user);
865     if(osm->way) osm_ways_free(osm->way);
866     if(osm->node) osm_nodes_free(icon, osm->node);
867     if(osm->relation) osm_relations_free(osm->relation);
868     g_free(osm);
869     }
870    
871     void osm_dump(osm_t *osm) {
872     osm_bounds_dump(osm->bounds);
873     osm_users_dump(osm->user);
874     osm_nodes_dump(osm->node);
875     osm_ways_dump(osm->way);
876     osm_relations_dump(osm->relation);
877     }
878    
879     osm_t *osm_parse(char *filename) {
880     xmlDoc *doc = NULL;
881    
882     LIBXML_TEST_VERSION;
883    
884     /* parse the file and get the DOM */
885     if ((doc = xmlReadFile(filename, NULL, 0)) == NULL) {
886     xmlErrorPtr errP = xmlGetLastError();
887     errorf(NULL, "While parsing \"%s\":\n\n%s", filename, errP->message);
888     return NULL;
889     }
890    
891     return osm_parse_doc(doc);
892     }
893    
894     gboolean osm_sanity_check(GtkWidget *parent, osm_t *osm) {
895     if(!osm->bounds) {
896     errorf(parent, _("Ivalid data in OSM file:\n"
897     "Boundary box missing!"));
898     return FALSE;
899     }
900     if(!osm->node) {
901     errorf(parent, _("Ivalid data in OSM file:\n"
902     "No drawable content found!"));
903     return FALSE;
904     }
905     return TRUE;
906     }
907    
908     /* ------------------------- misc access functions -------------- */
909    
910     char *osm_tag_get_by_key(tag_t *tag, char *key) {
911     if(!tag || !key) return NULL;
912    
913     while(tag) {
914     if(strcasecmp(tag->key, key) == 0)
915     return tag->value;
916    
917     tag = tag->next;
918     }
919    
920     return NULL;
921     }
922    
923     char *osm_way_get_value(way_t *way, char *key) {
924     tag_t *tag = way->tag;
925    
926     while(tag) {
927     if(strcasecmp(tag->key, key) == 0)
928     return tag->value;
929    
930     tag = tag->next;
931     }
932    
933     return NULL;
934     }
935    
936     char *osm_node_get_value(node_t *node, char *key) {
937     tag_t *tag = node->tag;
938    
939     while(tag) {
940     if(strcasecmp(tag->key, key) == 0)
941     return tag->value;
942    
943     tag = tag->next;
944     }
945    
946     return NULL;
947     }
948    
949     gboolean osm_way_has_value(way_t *way, char *str) {
950     tag_t *tag = way->tag;
951    
952     while(tag) {
953     if(tag->value && strcasecmp(tag->value, str) == 0)
954     return TRUE;
955    
956     tag = tag->next;
957     }
958     return FALSE;
959     }
960    
961     gboolean osm_node_has_value(node_t *node, char *str) {
962     tag_t *tag = node->tag;
963    
964     while(tag) {
965     if(tag->value && strcasecmp(tag->value, str) == 0)
966     return TRUE;
967    
968     tag = tag->next;
969     }
970     return FALSE;
971     }
972    
973     gboolean osm_node_has_tag(node_t *node) {
974     tag_t *tag = node->tag;
975    
976     if(tag && strcasecmp(tag->key, "created_by") == 0)
977     tag = tag->next;
978    
979     return tag != NULL;
980     }
981    
982     /* return true if node is part of way */
983     gboolean osm_node_in_way(way_t *way, node_t *node) {
984     node_chain_t *node_chain = way->node_chain;
985     while(node_chain) {
986     if(node_chain->node == node)
987     return TRUE;
988    
989     node_chain = node_chain->next;
990     }
991     return FALSE;
992     }
993    
994     static void osm_generate_tags(tag_t *tag, xmlNodePtr node) {
995     while(tag) {
996     /* make sure "created_by" tag contains our id */
997     if(strcasecmp(tag->key, "created_by") == 0) {
998     g_free(tag->value);
999     tag->value = g_strdup(PACKAGE " v" VERSION);
1000     }
1001    
1002     xmlNodePtr tag_node = xmlNewChild(node, NULL, BAD_CAST "tag", NULL);
1003     xmlNewProp(tag_node, BAD_CAST "k", BAD_CAST tag->key);
1004     xmlNewProp(tag_node, BAD_CAST "v", BAD_CAST tag->value);
1005     tag = tag->next;
1006     }
1007     }
1008    
1009     /* build xml representation for a way */
1010     char *osm_generate_xml(osm_t *osm, type_t type, void *item) {
1011     char str[32];
1012     xmlChar *result = NULL;
1013     int len = 0;
1014    
1015     LIBXML_TEST_VERSION;
1016    
1017     xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
1018     xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "osm");
1019     xmlNewProp(root_node, BAD_CAST "version", BAD_CAST "0.5");
1020     xmlNewProp(root_node, BAD_CAST "generator", BAD_CAST PACKAGE " V" VERSION);
1021     xmlDocSetRootElement(doc, root_node);
1022    
1023     switch(type) {
1024     case NODE:
1025     {
1026     node_t *node = (node_t*)item;
1027     xmlNodePtr node_node = xmlNewChild(root_node, NULL,
1028     BAD_CAST "node", NULL);
1029     /* new nodes don't have an id, but get one after the upload */
1030     if(!(node->flags & OSM_FLAG_NEW)) {
1031     snprintf(str, sizeof(str), "%u", (unsigned)node->id);
1032     xmlNewProp(node_node, BAD_CAST "id", BAD_CAST str);
1033     }
1034     g_ascii_dtostr(str, sizeof(str), node->pos.lat);
1035     xmlNewProp(node_node, BAD_CAST "lat", BAD_CAST str);
1036     g_ascii_dtostr(str, sizeof(str), node->pos.lon);
1037     xmlNewProp(node_node, BAD_CAST "lon", BAD_CAST str);
1038     osm_generate_tags(node->tag, node_node);
1039     }
1040     break;
1041    
1042     case WAY:
1043     {
1044     way_t *way = (way_t*)item;
1045     xmlNodePtr way_node = xmlNewChild(root_node, NULL, BAD_CAST "way", NULL);
1046     snprintf(str, sizeof(str), "%u", (unsigned)way->id);
1047     xmlNewProp(way_node, BAD_CAST "id", BAD_CAST str);
1048    
1049     node_chain_t *node_chain = way->node_chain;
1050     while(node_chain) {
1051     xmlNodePtr nd_node = xmlNewChild(way_node, NULL, BAD_CAST "nd", NULL);
1052     char *str = g_strdup_printf("%ld", node_chain->node->id);
1053     xmlNewProp(nd_node, BAD_CAST "ref", BAD_CAST str);
1054     g_free(str);
1055     node_chain = node_chain->next;
1056     }
1057    
1058     osm_generate_tags(way->tag, way_node);
1059     }
1060     break;
1061    
1062     case RELATION:
1063     {
1064     relation_t *relation = (relation_t*)item;
1065     xmlNodePtr rel_node = xmlNewChild(root_node, NULL,
1066     BAD_CAST "relation", NULL);
1067     snprintf(str, sizeof(str), "%u", (unsigned)relation->id);
1068     xmlNewProp(rel_node, BAD_CAST "id", BAD_CAST str);
1069    
1070     member_t *member = relation->member;
1071     while(member) {
1072     xmlNodePtr m_node = xmlNewChild(rel_node,NULL,BAD_CAST "member", NULL);
1073     char *str = NULL;
1074    
1075     switch(member->type) {
1076     case NODE:
1077     xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "node");
1078     str = g_strdup_printf("%ld", member->node->id);
1079     break;
1080    
1081     case WAY:
1082     xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "way");
1083     str = g_strdup_printf("%ld", member->way->id);
1084     break;
1085    
1086     case RELATION:
1087     xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "relation");
1088     str = g_strdup_printf("%ld", member->relation->id);
1089     break;
1090    
1091     default:
1092     break;
1093     }
1094    
1095     if(str) {
1096     xmlNewProp(m_node, BAD_CAST "ref", BAD_CAST str);
1097     g_free(str);
1098     }
1099    
1100     if(member->role)
1101     xmlNewProp(m_node, BAD_CAST "role", BAD_CAST member->role);
1102     else
1103     xmlNewProp(m_node, BAD_CAST "role", BAD_CAST "");
1104    
1105     member = member->next;
1106     }
1107     osm_generate_tags(relation->tag, rel_node);
1108     }
1109     break;
1110    
1111     default:
1112     printf("neither NODE nor WAY nor RELATION\n");
1113     g_assert(0);
1114     break;
1115     }
1116    
1117     xmlDocDumpFormatMemoryEnc(doc, &result, &len, "UTF-8", 1);
1118     xmlFreeDoc(doc);
1119     xmlCleanupParser();
1120    
1121     // puts("xml encoding result:");
1122     // puts((char*)result);
1123    
1124     return (char*)result;
1125     }
1126    
1127     /* build xml representation for a node */
1128     char *osm_generate_xml_node(osm_t *osm, node_t *node) {
1129     return osm_generate_xml(osm, NODE, node);
1130     }
1131    
1132     /* build xml representation for a way */
1133     char *osm_generate_xml_way(osm_t *osm, way_t *way) {
1134     return osm_generate_xml(osm, WAY, way);
1135     }
1136    
1137     /* build xml representation for a relation */
1138     char *osm_generate_xml_relation(osm_t *osm, relation_t *relation) {
1139     return osm_generate_xml(osm, RELATION, relation);
1140     }
1141    
1142     node_t *osm_get_node_by_id(osm_t *osm, item_id_t id) {
1143     node_t *node = osm->node;
1144     while(node) {
1145     if(node->id == id)
1146     return node;
1147    
1148     node = node->next;
1149     }
1150     return NULL;
1151     }
1152    
1153     way_t *osm_get_way_by_id(osm_t *osm, item_id_t id) {
1154     way_t *way = osm->way;
1155     while(way) {
1156     if(way->id == id)
1157     return way;
1158    
1159     way = way->next;
1160     }
1161     return NULL;
1162     }
1163    
1164     relation_t *osm_get_relation_by_id(osm_t *osm, item_id_t id) {
1165     relation_t *relation = osm->relation;
1166     while(relation) {
1167     if(relation->id == id)
1168     return relation;
1169    
1170     relation = relation->next;
1171     }
1172    
1173     return NULL;
1174     }
1175    
1176     /* ---------- edit functions ------------- */
1177    
1178     item_id_t osm_new_way_id(osm_t *osm) {
1179     item_id_t id = -1;
1180    
1181     while(TRUE) {
1182     gboolean found = FALSE;
1183     way_t *way = osm->way;
1184     while(way) {
1185     if(way->id == id)
1186     found = TRUE;
1187    
1188     way = way->next;
1189     }
1190    
1191     /* no such id so far -> use it */
1192     if(!found) return id;
1193    
1194     id--;
1195     }
1196     g_assert(0);
1197     return 0;
1198     }
1199    
1200     item_id_t osm_new_node_id(osm_t *osm) {
1201     item_id_t id = -1;
1202    
1203     while(TRUE) {
1204     gboolean found = FALSE;
1205     node_t *node = osm->node;
1206     while(node) {
1207     if(node->id == id)
1208     found = TRUE;
1209    
1210     node = node->next;
1211     }
1212    
1213     /* no such id so far -> use it */
1214     if(!found) return id;
1215    
1216     id--;
1217     }
1218     g_assert(0);
1219     return 0;
1220     }
1221    
1222     node_t *osm_node_new(osm_t *osm, gint x, gint y) {
1223     printf("Creating new node\n");
1224    
1225     node_t *node = g_new0(node_t, 1);
1226     node->lpos.x = x;
1227     node->lpos.y = y;
1228     node->visible = TRUE;
1229     node->time = time(NULL);
1230    
1231     /* add created_by tag */
1232     node->tag = g_new0(tag_t, 1);
1233     node->tag->key = g_strdup("created_by");
1234     node->tag->value = g_strdup(PACKAGE " v" VERSION);
1235    
1236     /* convert screen position back to ll */
1237     lpos2pos(osm->bounds, &node->lpos, &node->pos);
1238    
1239     printf(" new at %d %d (%f %f)\n",
1240     node->lpos.x, node->lpos.y, node->pos.lat, node->pos.lon);
1241    
1242     return node;
1243     }
1244    
1245    
1246     void osm_node_attach(osm_t *osm, node_t *node) {
1247     printf("Attaching node\n");
1248    
1249     node->id = osm_new_node_id(osm);
1250     node->flags = OSM_FLAG_NEW;
1251    
1252     /* attach to end of node list */
1253     node_t **lnode = &osm->node;
1254     while(*lnode) lnode = &(*lnode)->next;
1255     *lnode = node;
1256     }
1257    
1258     way_t *osm_way_new(void) {
1259     printf("Creating new way\n");
1260    
1261     way_t *way = g_new0(way_t, 1);
1262     way->visible = TRUE;
1263     way->flags = OSM_FLAG_NEW;
1264     way->time = time(NULL);
1265    
1266     /* add created_by tag */
1267     way->tag = g_new0(tag_t, 1);
1268     way->tag->key = g_strdup("created_by");
1269     way->tag->value = g_strdup(PACKAGE " v" VERSION);
1270    
1271     return way;
1272     }
1273    
1274     void osm_way_attach(osm_t *osm, way_t *way) {
1275     printf("Attaching way\n");
1276    
1277     way->id = osm_new_way_id(osm);
1278     way->flags = OSM_FLAG_NEW;
1279    
1280     /* attach to end of way list */
1281     way_t **lway = &osm->way;
1282     while(*lway) lway = &(*lway)->next;
1283     *lway = way;
1284     }
1285    
1286     /* returns pointer to chain of ways affected by this deletion */
1287     way_chain_t *osm_node_delete(osm_t *osm, icon_t **icon,
1288     node_t *node, gboolean permanently,
1289     gboolean affect_ways) {
1290     way_chain_t *way_chain = NULL, **cur_way_chain = &way_chain;
1291    
1292     /* new nodes aren't stored on the server and are just deleted permanently */
1293     if(node->flags & OSM_FLAG_NEW) {
1294     printf("About to delete NEW node #%ld -> force permanent delete\n",
1295     node->id);
1296     permanently = TRUE;
1297     }
1298    
1299     /* first remove node from all ways using it */
1300     way_t *way = osm->way;
1301     while(way) {
1302     node_chain_t **chain = &(way->node_chain);
1303     gboolean modified = FALSE;
1304     while(*chain) {
1305     /* remove node from chain */
1306     if(node == (*chain)->node) {
1307     modified = TRUE;
1308     if(affect_ways) {
1309     node_chain_t *next = (*chain)->next;
1310     g_free(*chain);
1311     *chain = next;
1312     } else
1313     chain = &((*chain)->next);
1314     } else
1315     chain = &((*chain)->next);
1316     }
1317    
1318     if(modified) {
1319     way->flags |= OSM_FLAG_DIRTY;
1320    
1321     /* and add the way to the list of affected ways */
1322     *cur_way_chain = g_new0(way_chain_t, 1);
1323     (*cur_way_chain)->way = way;
1324     cur_way_chain = &((*cur_way_chain)->next);
1325     }
1326    
1327     way = way->next;
1328     }
1329    
1330     if(!permanently) {
1331     printf("mark node #%ld as deleted\n", node->id);
1332     node->flags |= OSM_FLAG_DELETED;
1333     } else {
1334     printf("permanently delete node #%ld\n", node->id);
1335    
1336     /* remove it from the chain */
1337     node_t **cnode = &osm->node;
1338     int found = 0;
1339    
1340     while(*cnode) {
1341     if(*cnode == node) {
1342     found++;
1343     *cnode = (*cnode)->next;
1344    
1345     osm_node_free(icon, node);
1346     } else
1347     cnode = &((*cnode)->next);
1348     }
1349     g_assert(found == 1);
1350     }
1351    
1352     return way_chain;
1353     }
1354    
1355     guint osm_way_number_of_nodes(way_t *way) {
1356     guint nodes = 0;
1357     node_chain_t *chain = way->node_chain;
1358     while(chain) {
1359     nodes++;
1360     chain = chain->next;
1361     }
1362     return nodes;
1363     }
1364    
1365     /* return all relations a node is in */
1366     relation_chain_t *osm_node_to_relation(osm_t *osm, node_t *node) {
1367     relation_chain_t *rel_chain = NULL, **cur_rel_chain = &rel_chain;
1368    
1369     relation_t *relation = osm->relation;
1370     while(relation) {
1371     gboolean is_member = FALSE;
1372    
1373     member_t *member = relation->member;
1374     while(member) {
1375     switch(member->type) {
1376     case NODE:
1377     /* nodes are checked directly */
1378     if(member->node == node)
1379     is_member = TRUE;
1380     break;
1381    
1382     case WAY: {
1383     /* ways have to be checked for the nodes they consist of */
1384     node_chain_t *chain = member->way->node_chain;
1385     while(chain && !is_member) {
1386     if(chain->node == node)
1387     is_member = TRUE;
1388    
1389     chain = chain->next;
1390     }
1391     } break;
1392    
1393     default:
1394     break;
1395     }
1396     member = member->next;
1397     }
1398    
1399     /* node is a member of this relation, so move it to the member chain */
1400     if(is_member) {
1401     *cur_rel_chain = g_new0(relation_chain_t, 1);
1402     (*cur_rel_chain)->relation = relation;
1403     cur_rel_chain = &((*cur_rel_chain)->next);
1404     }
1405    
1406     relation = relation->next;
1407     }
1408    
1409     return rel_chain;
1410     }
1411    
1412     /* return all relations a way is in */
1413     relation_chain_t *osm_way_to_relation(osm_t *osm, way_t *way) {
1414     relation_chain_t *rel_chain = NULL, **cur_rel_chain = &rel_chain;
1415    
1416     relation_t *relation = osm->relation;
1417     while(relation) {
1418     gboolean is_member = FALSE;
1419    
1420     member_t *member = relation->member;
1421     while(member) {
1422     switch(member->type) {
1423     case WAY: {
1424     /* ways can be check directly */
1425     if(member->way == way)
1426     is_member = TRUE;
1427     } break;
1428    
1429     default:
1430     break;
1431     }
1432     member = member->next;
1433     }
1434    
1435     /* way is a member of this relation, so move it to the member chain */
1436     if(is_member) {
1437     *cur_rel_chain = g_new0(relation_chain_t, 1);
1438     (*cur_rel_chain)->relation = relation;
1439     cur_rel_chain = &((*cur_rel_chain)->next);
1440     }
1441    
1442     relation = relation->next;
1443     }
1444    
1445     return rel_chain;
1446     }
1447    
1448     /* return all ways a node is in */
1449     way_chain_t *osm_node_to_way(osm_t *osm, node_t *node) {
1450     way_chain_t *chain = NULL, **cur_chain = &chain;
1451    
1452     way_t *way = osm->way;
1453     while(way) {
1454     gboolean is_member = FALSE;
1455    
1456     node_chain_t *node_chain = way->node_chain;
1457     while(node_chain) {
1458     if(node_chain->node == node)
1459     is_member = TRUE;
1460    
1461     node_chain = node_chain->next;
1462     }
1463    
1464     /* node is a member of this relation, so move it to the member chain */
1465     if(is_member) {
1466     *cur_chain = g_new0(way_chain_t, 1);
1467     (*cur_chain)->way = way;
1468     cur_chain = &((*cur_chain)->next);
1469     }
1470    
1471     way = way->next;
1472     }
1473    
1474     return chain;
1475     }
1476    
1477     gboolean osm_position_within_bounds(osm_t *osm, gint x, gint y) {
1478     if((x < osm->bounds->min.x) || (x > osm->bounds->max.x)) return FALSE;
1479     if((y < osm->bounds->min.y) || (y > osm->bounds->max.y)) return FALSE;
1480     return TRUE;
1481     }
1482    
1483     /* remove the given node from all relations. used if the node is to */
1484     /* be deleted */
1485     void osm_node_remove_from_relation(osm_t *osm, node_t *node) {
1486     relation_t *relation = osm->relation;
1487     printf("removing node #%ld from all relations:\n", node->id);
1488    
1489     while(relation) {
1490     member_t **member = &relation->member;
1491     while(*member) {
1492     if(((*member)->type == NODE) &&
1493     ((*member)->node == node)) {
1494    
1495     printf(" from relation #%ld\n", relation->id);
1496    
1497     member_t *cur = *member;
1498     *member = (*member)->next;
1499     osm_member_free(cur);
1500    
1501     relation->flags |= OSM_FLAG_DIRTY;
1502     } else
1503     member = &(*member)->next;
1504     }
1505     relation = relation->next;
1506     }
1507     }
1508    
1509     /* remove the given way from all relations */
1510     void osm_way_remove_from_relation(osm_t *osm, way_t *way) {
1511     relation_t *relation = osm->relation;
1512     printf("removing way #%ld from all relations:\n", way->id);
1513    
1514     while(relation) {
1515     member_t **member = &relation->member;
1516     while(*member) {
1517     if(((*member)->type == WAY) &&
1518     ((*member)->way == way)) {
1519    
1520     printf(" from relation #%ld\n", relation->id);
1521    
1522     member_t *cur = *member;
1523     *member = (*member)->next;
1524     osm_member_free(cur);
1525    
1526     relation->flags |= OSM_FLAG_DIRTY;
1527     } else
1528     member = &(*member)->next;
1529     }
1530     relation = relation->next;
1531     }
1532     }
1533    
1534     void osm_way_delete(osm_t *osm, icon_t **icon,
1535     way_t *way, gboolean permanently) {
1536    
1537     /* new ways aren't stored on the server and are just deleted permanently */
1538     if(way->flags & OSM_FLAG_NEW) {
1539     printf("About to delete NEW way #%ld -> force permanent delete\n",
1540     way->id);
1541     permanently = TRUE;
1542     }
1543    
1544     /* delete all nodes that aren't in other use now */
1545     node_chain_t **chain = &way->node_chain;
1546     while(*chain) {
1547    
1548     (*chain)->node->ways--;
1549     printf("checking node #%ld (still used by %d)\n",
1550     (*chain)->node->id, (*chain)->node->ways);
1551    
1552     /* this node must only be part of this way */
1553     if(!(*chain)->node->ways) {
1554     /* delete this node, but don't let this actually affect the */
1555     /* associated ways as the only such way is the oen we are currently */
1556     /* deleting */
1557     way_chain_t *way_chain =
1558     osm_node_delete(osm, icon, (*chain)->node, FALSE, FALSE);
1559     g_assert(way_chain);
1560     while(way_chain) {
1561     way_chain_t *way_next = way_chain->next;
1562     g_assert(way_chain->way == way);
1563     g_free(way_chain);
1564     way_chain = way_next;
1565     }
1566     }
1567    
1568     node_chain_t *cur = (*chain);
1569     *chain = cur->next;
1570     g_free(cur);
1571     }
1572     way->node_chain = NULL;
1573    
1574     if(!permanently) {
1575     printf("mark way #%ld as deleted\n", way->id);
1576     way->flags |= OSM_FLAG_DELETED;
1577     } else {
1578     printf("permanently delete way #%ld\n", way->id);
1579    
1580     /* remove it from the chain */
1581     way_t **cway = &osm->way;
1582     int found = 0;
1583    
1584     while(*cway) {
1585     if(*cway == way) {
1586     found++;
1587     *cway = (*cway)->next;
1588    
1589     osm_way_free(way);
1590     } else
1591     cway = &((*cway)->next);
1592     }
1593     g_assert(found == 1);
1594     }
1595     }
1596    
1597     void osm_way_revert(way_t *way) {
1598     node_chain_t *new = NULL;
1599    
1600     /* walk old chain first to last */
1601     node_chain_t *old = way->node_chain;
1602     while(old) {
1603     node_chain_t *next = old->next;
1604    
1605     /* and prepend each node to the new chain */
1606     old->next = new;
1607     new = old;
1608    
1609     old = next;
1610     }
1611    
1612     way->node_chain = new;
1613     }
1614    
1615     node_t *osm_way_get_first_node(way_t *way) {
1616     node_chain_t *chain = way->node_chain;
1617     if(!chain) return NULL;
1618     return chain->node;
1619     }
1620    
1621     node_t *osm_way_get_last_node(way_t *way) {
1622     node_chain_t *chain = way->node_chain;
1623    
1624     while(chain && chain->next) chain=chain->next;
1625    
1626     if(!chain) return NULL;
1627    
1628     return chain->node;
1629     }
1630    
1631     void osm_way_rotate(way_t *way, gint offset) {
1632     if(!offset) return;
1633    
1634     /* needs at least two nodes to work properly */
1635     g_assert(way->node_chain);
1636     g_assert(way->node_chain->next);
1637    
1638     while(offset--) {
1639     node_chain_t *chain = way->node_chain;
1640     chain->node->ways--; // reduce way count of old start/end node
1641    
1642     /* move all nodes ahead one chain element ... */
1643     while(chain->next) {
1644     chain->node = chain->next->node;
1645     chain = chain->next;
1646     }
1647    
1648     /* ... and make last one same as first one */
1649     chain->node = way->node_chain->node;
1650     chain->node->ways++; // increase way count of new start/end node
1651     }
1652     }
1653    
1654     tag_t *osm_tags_copy(tag_t *src_tag, gboolean update_creator) {
1655     tag_t *new_tags = NULL;
1656     tag_t **dst_tag = &new_tags;
1657    
1658     while(src_tag) {
1659     *dst_tag = g_new0(tag_t, 1);
1660     (*dst_tag)->key = g_strdup(src_tag->key);
1661     if(update_creator && (strcasecmp(src_tag->key, "created_by") == 0))
1662     (*dst_tag)->value = g_strdup(PACKAGE " v" VERSION);
1663     else
1664     (*dst_tag)->value = g_strdup(src_tag->value);
1665    
1666     dst_tag = &(*dst_tag)->next;
1667     src_tag = src_tag->next;
1668     }
1669    
1670     return new_tags;
1671     }