Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


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