Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


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