Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


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