Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


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