Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 191 - (hide annotations)
Tue Jul 7 07:36:27 2009 UTC (14 years, 10 months ago) by harbaum
File MIME type: text/plain
File size: 60183 byte(s)
Full fremantle menues and per object relation handling
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 harbaum 161 printf("Id: "ITEM_ID_FORMAT"\n", node->id);
231 harbaum 1 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 harbaum 161 // printf("freeing way #" ITEM_ID_FORMAT "\n", way->id);
264 harbaum 1
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 harbaum 161 printf("Id: "ITEM_ID_FORMAT"\n", way->id);
309 harbaum 1 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 harbaum 161 printf(" Node: "ITEM_ID_FORMAT"\n", node_chain->node->id);
314 harbaum 1 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 161 if(!node_chain->node) printf("Node id " ITEM_ID_FORMAT " 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 harbaum 161 printf("Id: "ITEM_ID_FORMAT"\n", relation->id);
390 harbaum 1 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 161 printf(" Member: Node, id = " ITEM_ID_FORMAT ", 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 161 printf(" Member: Way, id = " ITEM_ID_FORMAT ", role = %s\n",
412     member->object.way->id, member->role);
413 harbaum 1 break;
414 harbaum 161
415 harbaum 1 case RELATION:
416 harbaum 155 if(member->object.relation)
417 harbaum 161 printf(" Member: Relation, id = " ITEM_ID_FORMAT ", role = %s\n",
418     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 harbaum 158 /* new in api 0.6: */
683     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "version"))) {
684     node->version = strtoul(prop, NULL, 10);
685     xmlFree(prop);
686     }
687    
688 harbaum 8 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 161 if(!node_chain->node) printf("Node id " ITEM_ID_FORMAT " 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 harbaum 158 /* new in api 0.6: */
786     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "version"))) {
787     way->version = strtoul(prop, NULL, 10);
788     xmlFree(prop);
789     }
790    
791 harbaum 8 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) {
792     way->user = osm_user(osm, prop);
793     xmlFree(prop);
794     }
795    
796     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) {
797     way->visible = (strcasecmp(prop, "true") == 0);
798     xmlFree(prop);
799     }
800    
801     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) {
802     way->time = convert_iso8601(prop);
803     xmlFree(prop);
804     }
805    
806 harbaum 42 /* append way to end of hash table if present */
807     if(osm->way_hash) {
808     hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)];
809     while(*item) item = &(*item)->next;
810    
811     *item = g_new0(hash_item_t, 1);
812     (*item)->data.way = way;
813     }
814    
815 harbaum 8 /* just an empty element? then return the way as it is */
816     /* (this should in fact never happen as this would be a way without nodes) */
817     if(xmlTextReaderIsEmptyElement(reader))
818     return way;
819    
820     /* parse tags/nodes if present */
821     int depth = xmlTextReaderDepth(reader);
822    
823     /* scan all elements on same level or its children */
824     tag_t **tag = &way->tag;
825     node_chain_t **node_chain = &way->node_chain;
826     int ret = xmlTextReaderRead(reader);
827     while((ret == 1) &&
828     ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
829     (xmlTextReaderDepth(reader) != depth))) {
830    
831     if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
832     char *subname = (char*)xmlTextReaderConstName(reader);
833     if(strcasecmp(subname, "nd") == 0) {
834     *node_chain = process_nd(reader, osm);
835     if(*node_chain) node_chain = &(*node_chain)->next;
836     } else if(strcasecmp(subname, "tag") == 0) {
837     *tag = process_tag(reader);
838     if(*tag) tag = &(*tag)->next;
839     } else
840     skip_element(reader);
841     }
842     ret = xmlTextReaderRead(reader);
843     }
844    
845     return way;
846     }
847    
848     static member_t *process_member(xmlTextReaderPtr reader, osm_t *osm) {
849     char *prop;
850     member_t *member = g_new0(member_t, 1);
851 harbaum 155 member->object.type = ILLEGAL;
852 harbaum 8
853     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "type"))) {
854 harbaum 155 if(strcasecmp(prop, "way") == 0) member->object.type = WAY;
855     else if(strcasecmp(prop, "node") == 0) member->object.type = NODE;
856     else if(strcasecmp(prop, "relation") == 0) member->object.type = RELATION;
857 harbaum 8 xmlFree(prop);
858     }
859    
860     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref"))) {
861     item_id_t id = strtoul(prop, NULL, 10);
862    
863 harbaum 155 switch(member->object.type) {
864 harbaum 8 case ILLEGAL:
865     printf("Unable to store illegal type\n");
866     break;
867    
868     case WAY:
869     /* search matching way */
870 harbaum 155 member->object.way = osm_get_way_by_id(osm, id);
871     if(!member->object.way) {
872     member->object.type = WAY_ID;
873     member->object.id = id;
874 harbaum 8 }
875     break;
876    
877     case NODE:
878     /* search matching node */
879 harbaum 155 member->object.node = osm_get_node_by_id(osm, id);
880     if(!member->object.node) {
881     member->object.type = NODE_ID;
882     member->object.id = id;
883 harbaum 8 }
884     break;
885    
886     case RELATION:
887     /* search matching relation */
888 harbaum 155 member->object.relation = osm_get_relation_by_id(osm, id);
889     if(!member->object.relation) {
890     member->object.type = NODE_ID;
891     member->object.id = id;
892 harbaum 8 }
893     break;
894    
895     case WAY_ID:
896     case NODE_ID:
897     case RELATION_ID:
898     break;
899     }
900    
901     xmlFree(prop);
902     }
903    
904     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "role"))) {
905     if(strlen(prop) > 0) member->role = g_strdup(prop);
906     xmlFree(prop);
907     }
908    
909     return member;
910     }
911    
912     static relation_t *process_relation(xmlTextReaderPtr reader, osm_t *osm) {
913     /* allocate a new relation structure */
914     relation_t *relation = g_new0(relation_t, 1);
915    
916     char *prop;
917     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
918     relation->id = strtoul(prop, NULL, 10);
919     xmlFree(prop);
920     }
921    
922 harbaum 158 /* new in api 0.6: */
923     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "version"))) {
924     relation->version = strtoul(prop, NULL, 10);
925     xmlFree(prop);
926     }
927    
928 harbaum 8 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) {
929     relation->user = osm_user(osm, prop);
930     xmlFree(prop);
931     }
932    
933     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) {
934     relation->visible = (strcasecmp(prop, "true") == 0);
935     xmlFree(prop);
936     }
937    
938     if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) {
939     relation->time = convert_iso8601(prop);
940     xmlFree(prop);
941     }
942    
943     /* just an empty element? then return the relation as it is */
944     /* (this should in fact never happen as this would be a relation */
945     /* without members) */
946     if(xmlTextReaderIsEmptyElement(reader))
947     return relation;
948    
949     /* parse tags/member if present */
950     int depth = xmlTextReaderDepth(reader);
951    
952     /* scan all elements on same level or its children */
953     tag_t **tag = &relation->tag;
954     member_t **member = &relation->member;
955     int ret = xmlTextReaderRead(reader);
956     while((ret == 1) &&
957     ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
958     (xmlTextReaderDepth(reader) != depth))) {
959    
960     if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
961     char *subname = (char*)xmlTextReaderConstName(reader);
962 achadwick 35 if(strcasecmp(subname, "member") == 0) {
963 harbaum 8 *member = process_member(reader, osm);
964     if(*member) member = &(*member)->next;
965     } else if(strcasecmp(subname, "tag") == 0) {
966     *tag = process_tag(reader);
967     if(*tag) tag = &(*tag)->next;
968     } else
969     skip_element(reader);
970     }
971     ret = xmlTextReaderRead(reader);
972     }
973    
974     return relation;
975     }
976    
977     static osm_t *process_osm(xmlTextReaderPtr reader) {
978     /* alloc osm structure */
979     osm_t *osm = g_new0(osm_t, 1);
980 harbaum 42 osm->node_hash = g_new0(hash_table_t, 1);
981     osm->way_hash = g_new0(hash_table_t, 1);
982 harbaum 8
983     node_t **node = &osm->node;
984     way_t **way = &osm->way;
985     relation_t **relation = &osm->relation;
986    
987     /* no attributes of interest */
988    
989     const xmlChar *name = xmlTextReaderConstName(reader);
990     g_assert(name);
991    
992     /* read next node */
993 achadwick 28 int num_elems = 0;
994     const int tick_every = 50; // Balance responsive appearance with performance.
995 harbaum 8 int ret = xmlTextReaderRead(reader);
996     while(ret == 1) {
997    
998     switch(xmlTextReaderNodeType(reader)) {
999     case XML_READER_TYPE_ELEMENT:
1000    
1001     g_assert(xmlTextReaderDepth(reader) == 1);
1002     char *name = (char*)xmlTextReaderConstName(reader);
1003     if(strcasecmp(name, "bounds") == 0) {
1004     osm->bounds = process_bounds(reader);
1005     } else if(strcasecmp(name, "node") == 0) {
1006     *node = process_node(reader, osm);
1007     if(*node) node = &(*node)->next;
1008     } else if(strcasecmp(name, "way") == 0) {
1009     *way = process_way(reader, osm);
1010     if(*way) way = &(*way)->next;
1011     } else if(strcasecmp(name, "relation") == 0) {
1012     *relation = process_relation(reader, osm);
1013     if(*relation) relation = &(*relation)->next;
1014     } else {
1015     printf("something unknown found\n");
1016     g_assert(0);
1017     skip_element(reader);
1018     }
1019     break;
1020    
1021     case XML_READER_TYPE_END_ELEMENT:
1022     /* end element must be for the current element */
1023     g_assert(xmlTextReaderDepth(reader) == 0);
1024     return osm;
1025     break;
1026    
1027     default:
1028     break;
1029     }
1030     ret = xmlTextReaderRead(reader);
1031 achadwick 28
1032     if (num_elems++ > tick_every) {
1033     num_elems = 0;
1034     banner_busy_tick();
1035     }
1036 harbaum 8 }
1037    
1038     g_assert(0);
1039     return NULL;
1040     }
1041    
1042     static osm_t *process_file(const char *filename) {
1043     osm_t *osm = NULL;
1044     xmlTextReaderPtr reader;
1045     int ret;
1046    
1047     reader = xmlReaderForFile(filename, NULL, 0);
1048     if (reader != NULL) {
1049     ret = xmlTextReaderRead(reader);
1050     if(ret == 1) {
1051     char *name = (char*)xmlTextReaderConstName(reader);
1052     if(name && strcasecmp(name, "osm") == 0)
1053     osm = process_osm(reader);
1054     } else
1055     printf("file empty\n");
1056    
1057     xmlFreeTextReader(reader);
1058     } else {
1059     fprintf(stderr, "Unable to open %s\n", filename);
1060     }
1061     return osm;
1062     }
1063    
1064 harbaum 78 /* ----------------------- end of stream parser ------------------- */
1065 harbaum 8
1066     #include <sys/time.h>
1067    
1068 harbaum 175 osm_t *osm_parse(char *path, char *filename) {
1069 harbaum 1
1070 harbaum 8 struct timeval start;
1071     gettimeofday(&start, NULL);
1072    
1073 harbaum 1 LIBXML_TEST_VERSION;
1074    
1075 harbaum 8 // use stream parser
1076 harbaum 175 osm_t *osm = NULL;
1077     if(filename[0] == '/')
1078     osm = process_file(filename);
1079     else {
1080     char *full = g_strjoin(NULL, path, filename, NULL);
1081     osm = process_file(full);
1082     g_free(full);
1083     }
1084    
1085 harbaum 8 xmlCleanupParser();
1086    
1087     struct timeval end;
1088     gettimeofday(&end, NULL);
1089    
1090     printf("total parse time: %ldms\n",
1091     (end.tv_usec - start.tv_usec)/1000 +
1092     (end.tv_sec - start.tv_sec)*1000);
1093    
1094     return osm;
1095 harbaum 1 }
1096    
1097     gboolean osm_sanity_check(GtkWidget *parent, osm_t *osm) {
1098     if(!osm->bounds) {
1099 achadwick 6 errorf(parent, _("Invalid data in OSM file:\n"
1100 harbaum 1 "Boundary box missing!"));
1101     return FALSE;
1102     }
1103     if(!osm->node) {
1104 achadwick 6 errorf(parent, _("Invalid data in OSM file:\n"
1105 harbaum 1 "No drawable content found!"));
1106     return FALSE;
1107     }
1108     return TRUE;
1109     }
1110    
1111     /* ------------------------- misc access functions -------------- */
1112    
1113     char *osm_tag_get_by_key(tag_t *tag, char *key) {
1114     if(!tag || !key) return NULL;
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     char *osm_way_get_value(way_t *way, char *key) {
1127     tag_t *tag = way->tag;
1128    
1129     while(tag) {
1130     if(strcasecmp(tag->key, key) == 0)
1131     return tag->value;
1132    
1133     tag = tag->next;
1134     }
1135    
1136     return NULL;
1137     }
1138    
1139     char *osm_node_get_value(node_t *node, char *key) {
1140     tag_t *tag = node->tag;
1141    
1142     while(tag) {
1143     if(strcasecmp(tag->key, key) == 0)
1144     return tag->value;
1145    
1146     tag = tag->next;
1147     }
1148    
1149     return NULL;
1150     }
1151    
1152     gboolean osm_way_has_value(way_t *way, char *str) {
1153     tag_t *tag = way->tag;
1154    
1155     while(tag) {
1156     if(tag->value && strcasecmp(tag->value, str) == 0)
1157     return TRUE;
1158    
1159     tag = tag->next;
1160     }
1161     return FALSE;
1162     }
1163    
1164     gboolean osm_node_has_value(node_t *node, char *str) {
1165     tag_t *tag = node->tag;
1166    
1167     while(tag) {
1168     if(tag->value && strcasecmp(tag->value, str) == 0)
1169     return TRUE;
1170    
1171     tag = tag->next;
1172     }
1173     return FALSE;
1174     }
1175    
1176     gboolean osm_node_has_tag(node_t *node) {
1177     tag_t *tag = node->tag;
1178    
1179 harbaum 158 /* created_by tags don't count as real tags */
1180 harbaum 1 if(tag && strcasecmp(tag->key, "created_by") == 0)
1181     tag = tag->next;
1182    
1183     return tag != NULL;
1184     }
1185    
1186     /* return true if node is part of way */
1187     gboolean osm_node_in_way(way_t *way, node_t *node) {
1188     node_chain_t *node_chain = way->node_chain;
1189     while(node_chain) {
1190     if(node_chain->node == node)
1191     return TRUE;
1192    
1193     node_chain = node_chain->next;
1194     }
1195     return FALSE;
1196     }
1197    
1198     static void osm_generate_tags(tag_t *tag, xmlNodePtr node) {
1199     while(tag) {
1200     /* make sure "created_by" tag contains our id */
1201     if(strcasecmp(tag->key, "created_by") == 0) {
1202 harbaum 158 if(strcasecmp(tag->value, PACKAGE " v" VERSION) != 0) {
1203     g_free(tag->value);
1204     tag->value = g_strdup(PACKAGE " v" VERSION);
1205     }
1206 harbaum 1 }
1207    
1208     xmlNodePtr tag_node = xmlNewChild(node, NULL, BAD_CAST "tag", NULL);
1209     xmlNewProp(tag_node, BAD_CAST "k", BAD_CAST tag->key);
1210     xmlNewProp(tag_node, BAD_CAST "v", BAD_CAST tag->value);
1211     tag = tag->next;
1212     }
1213     }
1214    
1215     /* build xml representation for a way */
1216 harbaum 158 static char *osm_generate_xml(osm_t *osm, item_id_t changeset,
1217     type_t type, void *item) {
1218 harbaum 1 char str[32];
1219     xmlChar *result = NULL;
1220     int len = 0;
1221    
1222     LIBXML_TEST_VERSION;
1223    
1224     xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
1225     xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "osm");
1226     xmlDocSetRootElement(doc, root_node);
1227    
1228     switch(type) {
1229     case NODE:
1230     {
1231     node_t *node = (node_t*)item;
1232     xmlNodePtr node_node = xmlNewChild(root_node, NULL,
1233     BAD_CAST "node", NULL);
1234     /* new nodes don't have an id, but get one after the upload */
1235     if(!(node->flags & OSM_FLAG_NEW)) {
1236     snprintf(str, sizeof(str), "%u", (unsigned)node->id);
1237     xmlNewProp(node_node, BAD_CAST "id", BAD_CAST str);
1238     }
1239 harbaum 158 snprintf(str, sizeof(str), "%u", (unsigned)node->version);
1240     xmlNewProp(node_node, BAD_CAST "version", BAD_CAST str);
1241     snprintf(str, sizeof(str), "%u", (unsigned)changeset);
1242     xmlNewProp(node_node, BAD_CAST "changeset", BAD_CAST str);
1243 harbaum 156 g_ascii_formatd(str, sizeof(str), LL_FORMAT, node->pos.lat);
1244 harbaum 1 xmlNewProp(node_node, BAD_CAST "lat", BAD_CAST str);
1245 harbaum 156 g_ascii_formatd(str, sizeof(str), LL_FORMAT, node->pos.lon);
1246 harbaum 1 xmlNewProp(node_node, BAD_CAST "lon", BAD_CAST str);
1247     osm_generate_tags(node->tag, node_node);
1248     }
1249     break;
1250    
1251     case WAY:
1252     {
1253     way_t *way = (way_t*)item;
1254     xmlNodePtr way_node = xmlNewChild(root_node, NULL, BAD_CAST "way", NULL);
1255     snprintf(str, sizeof(str), "%u", (unsigned)way->id);
1256     xmlNewProp(way_node, BAD_CAST "id", BAD_CAST str);
1257 harbaum 158 snprintf(str, sizeof(str), "%u", (unsigned)way->version);
1258     xmlNewProp(way_node, BAD_CAST "version", BAD_CAST str);
1259     snprintf(str, sizeof(str), "%u", (unsigned)changeset);
1260     xmlNewProp(way_node, BAD_CAST "changeset", BAD_CAST str);
1261 harbaum 1
1262     node_chain_t *node_chain = way->node_chain;
1263     while(node_chain) {
1264     xmlNodePtr nd_node = xmlNewChild(way_node, NULL, BAD_CAST "nd", NULL);
1265 harbaum 161 char *str = g_strdup_printf(ITEM_ID_FORMAT, node_chain->node->id);
1266 harbaum 1 xmlNewProp(nd_node, BAD_CAST "ref", BAD_CAST str);
1267     g_free(str);
1268     node_chain = node_chain->next;
1269     }
1270    
1271     osm_generate_tags(way->tag, way_node);
1272     }
1273     break;
1274    
1275     case RELATION:
1276     {
1277     relation_t *relation = (relation_t*)item;
1278     xmlNodePtr rel_node = xmlNewChild(root_node, NULL,
1279     BAD_CAST "relation", NULL);
1280     snprintf(str, sizeof(str), "%u", (unsigned)relation->id);
1281     xmlNewProp(rel_node, BAD_CAST "id", BAD_CAST str);
1282 harbaum 158 snprintf(str, sizeof(str), "%u", (unsigned)relation->version);
1283     xmlNewProp(rel_node, BAD_CAST "version", BAD_CAST str);
1284     snprintf(str, sizeof(str), "%u", (unsigned)changeset);
1285     xmlNewProp(rel_node, BAD_CAST "changeset", BAD_CAST str);
1286 harbaum 1
1287     member_t *member = relation->member;
1288     while(member) {
1289     xmlNodePtr m_node = xmlNewChild(rel_node,NULL,BAD_CAST "member", NULL);
1290     char *str = NULL;
1291    
1292 harbaum 155 switch(member->object.type) {
1293 harbaum 1 case NODE:
1294     xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "node");
1295 harbaum 161 str = g_strdup_printf(ITEM_ID_FORMAT, member->object.node->id);
1296 harbaum 1 break;
1297    
1298     case WAY:
1299     xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "way");
1300 harbaum 161 str = g_strdup_printf(ITEM_ID_FORMAT, member->object.way->id);
1301 harbaum 1 break;
1302    
1303     case RELATION:
1304     xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "relation");
1305 harbaum 161 str = g_strdup_printf(ITEM_ID_FORMAT, member->object.relation->id);
1306 harbaum 1 break;
1307    
1308     default:
1309     break;
1310     }
1311    
1312     if(str) {
1313     xmlNewProp(m_node, BAD_CAST "ref", BAD_CAST str);
1314     g_free(str);
1315     }
1316    
1317     if(member->role)
1318     xmlNewProp(m_node, BAD_CAST "role", BAD_CAST member->role);
1319     else
1320     xmlNewProp(m_node, BAD_CAST "role", BAD_CAST "");
1321    
1322     member = member->next;
1323     }
1324     osm_generate_tags(relation->tag, rel_node);
1325     }
1326     break;
1327    
1328     default:
1329     printf("neither NODE nor WAY nor RELATION\n");
1330     g_assert(0);
1331     break;
1332     }
1333    
1334     xmlDocDumpFormatMemoryEnc(doc, &result, &len, "UTF-8", 1);
1335     xmlFreeDoc(doc);
1336     xmlCleanupParser();
1337    
1338     // puts("xml encoding result:");
1339     // puts((char*)result);
1340    
1341     return (char*)result;
1342     }
1343    
1344     /* build xml representation for a node */
1345 harbaum 158 char *osm_generate_xml_node(osm_t *osm, item_id_t changeset, node_t *node) {
1346     return osm_generate_xml(osm, changeset, NODE, node);
1347 harbaum 1 }
1348    
1349     /* build xml representation for a way */
1350 harbaum 158 char *osm_generate_xml_way(osm_t *osm, item_id_t changeset, way_t *way) {
1351     return osm_generate_xml(osm, changeset, WAY, way);
1352 harbaum 1 }
1353    
1354     /* build xml representation for a relation */
1355 harbaum 158 char *osm_generate_xml_relation(osm_t *osm, item_id_t changeset,
1356     relation_t *relation) {
1357     return osm_generate_xml(osm, changeset, RELATION, relation);
1358 harbaum 1 }
1359    
1360 harbaum 158 /* build xml representation for a changeset */
1361     char *osm_generate_xml_changeset(osm_t *osm, char *comment) {
1362     xmlChar *result = NULL;
1363     int len = 0;
1364    
1365     /* tags for this changeset */
1366     tag_t tag_comment = {
1367     .key = "comment", .value = comment, .next = NULL };
1368     tag_t tag_creator = {
1369     .key = "created_by", .value = PACKAGE " v" VERSION, .next = &tag_comment };
1370    
1371     LIBXML_TEST_VERSION;
1372    
1373     xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
1374     xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "osm");
1375     xmlDocSetRootElement(doc, root_node);
1376    
1377     xmlNodePtr cs_node = xmlNewChild(root_node, NULL, BAD_CAST "changeset", NULL);
1378     osm_generate_tags(&tag_creator, cs_node);
1379    
1380     xmlDocDumpFormatMemoryEnc(doc, &result, &len, "UTF-8", 1);
1381     xmlFreeDoc(doc);
1382     xmlCleanupParser();
1383    
1384     // puts("xml encoding result:");
1385     // puts((char*)result);
1386    
1387     return (char*)result;
1388     }
1389    
1390    
1391 harbaum 42 /* the following three functions are eating much CPU power */
1392     /* as they search the objects lists. Hashing is supposed to help */
1393 harbaum 1 node_t *osm_get_node_by_id(osm_t *osm, item_id_t id) {
1394 harbaum 42 if(id > 0 && osm->node_hash) {
1395     // use hash table if present
1396     hash_item_t *item = osm->node_hash->hash[ID2HASH(id)];
1397     while(item) {
1398     if(item->data.node->id == id)
1399     return item->data.node;
1400    
1401     item = item->next;
1402     }
1403     }
1404    
1405     /* use linear search if no hash tables are present or search in hash table failed */
1406 harbaum 1 node_t *node = osm->node;
1407     while(node) {
1408     if(node->id == id)
1409     return node;
1410 harbaum 42
1411 harbaum 1 node = node->next;
1412     }
1413 harbaum 42
1414 harbaum 1 return NULL;
1415     }
1416    
1417     way_t *osm_get_way_by_id(osm_t *osm, item_id_t id) {
1418 harbaum 42 if(id > 0 && osm->way_hash) {
1419     // use hash table if present
1420     hash_item_t *item = osm->way_hash->hash[ID2HASH(id)];
1421     while(item) {
1422     if(item->data.way->id == id)
1423     return item->data.way;
1424    
1425     item = item->next;
1426     }
1427     }
1428    
1429     /* use linear search if no hash tables are present or search on hash table failed */
1430 harbaum 1 way_t *way = osm->way;
1431     while(way) {
1432     if(way->id == id)
1433     return way;
1434 harbaum 42
1435 harbaum 1 way = way->next;
1436     }
1437 harbaum 42
1438 harbaum 1 return NULL;
1439     }
1440    
1441     relation_t *osm_get_relation_by_id(osm_t *osm, item_id_t id) {
1442 harbaum 42 // use linear search
1443 harbaum 1 relation_t *relation = osm->relation;
1444     while(relation) {
1445     if(relation->id == id)
1446     return relation;
1447    
1448     relation = relation->next;
1449     }
1450    
1451     return NULL;
1452     }
1453    
1454     /* ---------- edit functions ------------- */
1455    
1456     item_id_t osm_new_way_id(osm_t *osm) {
1457     item_id_t id = -1;
1458    
1459     while(TRUE) {
1460     gboolean found = FALSE;
1461     way_t *way = osm->way;
1462     while(way) {
1463     if(way->id == id)
1464     found = TRUE;
1465    
1466     way = way->next;
1467     }
1468    
1469     /* no such id so far -> use it */
1470     if(!found) return id;
1471    
1472     id--;
1473     }
1474     g_assert(0);
1475     return 0;
1476     }
1477    
1478     item_id_t osm_new_node_id(osm_t *osm) {
1479     item_id_t id = -1;
1480    
1481     while(TRUE) {
1482     gboolean found = FALSE;
1483     node_t *node = osm->node;
1484     while(node) {
1485     if(node->id == id)
1486     found = TRUE;
1487    
1488     node = node->next;
1489     }
1490    
1491     /* no such id so far -> use it */
1492     if(!found) return id;
1493    
1494     id--;
1495     }
1496     g_assert(0);
1497     return 0;
1498     }
1499    
1500 harbaum 73 item_id_t osm_new_relation_id(osm_t *osm) {
1501     item_id_t id = -1;
1502    
1503     while(TRUE) {
1504     gboolean found = FALSE;
1505     relation_t *relation = osm->relation;
1506     while(relation) {
1507     if(relation->id == id)
1508     found = TRUE;
1509    
1510     relation = relation->next;
1511     }
1512    
1513     /* no such id so far -> use it */
1514     if(!found) return id;
1515    
1516     id--;
1517     }
1518     g_assert(0);
1519     return 0;
1520     }
1521    
1522 harbaum 1 node_t *osm_node_new(osm_t *osm, gint x, gint y) {
1523     printf("Creating new node\n");
1524    
1525     node_t *node = g_new0(node_t, 1);
1526 harbaum 158 node->version = 1;
1527 harbaum 1 node->lpos.x = x;
1528     node->lpos.y = y;
1529     node->visible = TRUE;
1530     node->time = time(NULL);
1531    
1532     /* convert screen position back to ll */
1533     lpos2pos(osm->bounds, &node->lpos, &node->pos);
1534    
1535     printf(" new at %d %d (%f %f)\n",
1536     node->lpos.x, node->lpos.y, node->pos.lat, node->pos.lon);
1537    
1538     return node;
1539     }
1540    
1541    
1542     void osm_node_attach(osm_t *osm, node_t *node) {
1543     printf("Attaching node\n");
1544    
1545     node->id = osm_new_node_id(osm);
1546     node->flags = OSM_FLAG_NEW;
1547    
1548     /* attach to end of node list */
1549     node_t **lnode = &osm->node;
1550     while(*lnode) lnode = &(*lnode)->next;
1551     *lnode = node;
1552     }
1553    
1554 harbaum 64 void osm_node_restore(osm_t *osm, node_t *node) {
1555     printf("Restoring node\n");
1556    
1557     /* attach to end of node list */
1558     node_t **lnode = &osm->node;
1559     while(*lnode) lnode = &(*lnode)->next;
1560     *lnode = node;
1561     }
1562    
1563 harbaum 1 way_t *osm_way_new(void) {
1564     printf("Creating new way\n");
1565    
1566     way_t *way = g_new0(way_t, 1);
1567 harbaum 158 way->version = 1;
1568 harbaum 1 way->visible = TRUE;
1569     way->flags = OSM_FLAG_NEW;
1570     way->time = time(NULL);
1571    
1572     return way;
1573     }
1574    
1575     void osm_way_attach(osm_t *osm, way_t *way) {
1576     printf("Attaching way\n");
1577    
1578     way->id = osm_new_way_id(osm);
1579     way->flags = OSM_FLAG_NEW;
1580    
1581     /* attach to end of way list */
1582     way_t **lway = &osm->way;
1583     while(*lway) lway = &(*lway)->next;
1584     *lway = way;
1585     }
1586    
1587     /* returns pointer to chain of ways affected by this deletion */
1588     way_chain_t *osm_node_delete(osm_t *osm, icon_t **icon,
1589     node_t *node, gboolean permanently,
1590     gboolean affect_ways) {
1591     way_chain_t *way_chain = NULL, **cur_way_chain = &way_chain;
1592    
1593     /* new nodes aren't stored on the server and are just deleted permanently */
1594     if(node->flags & OSM_FLAG_NEW) {
1595 harbaum 161 printf("About to delete NEW node #" ITEM_ID_FORMAT
1596     " -> force permanent delete\n", node->id);
1597 harbaum 1 permanently = TRUE;
1598     }
1599    
1600     /* first remove node from all ways using it */
1601     way_t *way = osm->way;
1602     while(way) {
1603     node_chain_t **chain = &(way->node_chain);
1604     gboolean modified = FALSE;
1605     while(*chain) {
1606     /* remove node from chain */
1607     if(node == (*chain)->node) {
1608     modified = TRUE;
1609     if(affect_ways) {
1610     node_chain_t *next = (*chain)->next;
1611     g_free(*chain);
1612     *chain = next;
1613     } else
1614     chain = &((*chain)->next);
1615     } else
1616     chain = &((*chain)->next);
1617     }
1618    
1619     if(modified) {
1620     way->flags |= OSM_FLAG_DIRTY;
1621    
1622     /* and add the way to the list of affected ways */
1623     *cur_way_chain = g_new0(way_chain_t, 1);
1624     (*cur_way_chain)->way = way;
1625     cur_way_chain = &((*cur_way_chain)->next);
1626     }
1627    
1628     way = way->next;
1629     }
1630    
1631     if(!permanently) {
1632 harbaum 161 printf("mark node #" ITEM_ID_FORMAT " as deleted\n", node->id);
1633 harbaum 1 node->flags |= OSM_FLAG_DELETED;
1634     } else {
1635 harbaum 161 printf("permanently delete node #" ITEM_ID_FORMAT "\n", node->id);
1636 harbaum 1
1637     /* remove it from the chain */
1638     node_t **cnode = &osm->node;
1639     int found = 0;
1640    
1641     while(*cnode) {
1642     if(*cnode == node) {
1643     found++;
1644     *cnode = (*cnode)->next;
1645    
1646     osm_node_free(icon, node);
1647     } else
1648     cnode = &((*cnode)->next);
1649     }
1650     g_assert(found == 1);
1651     }
1652    
1653     return way_chain;
1654     }
1655    
1656     guint osm_way_number_of_nodes(way_t *way) {
1657     guint nodes = 0;
1658     node_chain_t *chain = way->node_chain;
1659     while(chain) {
1660     nodes++;
1661     chain = chain->next;
1662     }
1663     return nodes;
1664     }
1665    
1666     /* return all relations a node is in */
1667     relation_chain_t *osm_node_to_relation(osm_t *osm, node_t *node) {
1668     relation_chain_t *rel_chain = NULL, **cur_rel_chain = &rel_chain;
1669    
1670     relation_t *relation = osm->relation;
1671     while(relation) {
1672     gboolean is_member = FALSE;
1673    
1674     member_t *member = relation->member;
1675     while(member) {
1676 harbaum 155 switch(member->object.type) {
1677 harbaum 1 case NODE:
1678     /* nodes are checked directly */
1679 harbaum 155 if(member->object.node == node)
1680 harbaum 1 is_member = TRUE;
1681     break;
1682    
1683     case WAY: {
1684     /* ways have to be checked for the nodes they consist of */
1685 harbaum 155 node_chain_t *chain = member->object.way->node_chain;
1686 harbaum 1 while(chain && !is_member) {
1687     if(chain->node == node)
1688     is_member = TRUE;
1689    
1690     chain = chain->next;
1691     }
1692     } break;
1693    
1694     default:
1695     break;
1696     }
1697     member = member->next;
1698     }
1699    
1700     /* node is a member of this relation, so move it to the member chain */
1701     if(is_member) {
1702     *cur_rel_chain = g_new0(relation_chain_t, 1);
1703     (*cur_rel_chain)->relation = relation;
1704     cur_rel_chain = &((*cur_rel_chain)->next);
1705     }
1706    
1707     relation = relation->next;
1708     }
1709    
1710     return rel_chain;
1711     }
1712    
1713     /* return all relations a way is in */
1714     relation_chain_t *osm_way_to_relation(osm_t *osm, way_t *way) {
1715     relation_chain_t *rel_chain = NULL, **cur_rel_chain = &rel_chain;
1716    
1717     relation_t *relation = osm->relation;
1718     while(relation) {
1719     gboolean is_member = FALSE;
1720    
1721     member_t *member = relation->member;
1722     while(member) {
1723 harbaum 155 switch(member->object.type) {
1724 harbaum 1 case WAY: {
1725     /* ways can be check directly */
1726 harbaum 155 if(member->object.way == way)
1727 harbaum 1 is_member = TRUE;
1728     } break;
1729    
1730     default:
1731     break;
1732     }
1733     member = member->next;
1734     }
1735    
1736     /* way is a member of this relation, so move it to the member chain */
1737     if(is_member) {
1738     *cur_rel_chain = g_new0(relation_chain_t, 1);
1739     (*cur_rel_chain)->relation = relation;
1740     cur_rel_chain = &((*cur_rel_chain)->next);
1741     }
1742    
1743     relation = relation->next;
1744     }
1745    
1746     return rel_chain;
1747     }
1748    
1749 harbaum 191 /* return all relations a relation is in */
1750     relation_chain_t *osm_relation_to_relation(osm_t *osm, relation_t *rel) {
1751     relation_chain_t *rel_chain = NULL, **cur_rel_chain = &rel_chain;
1752    
1753     relation_t *relation = osm->relation;
1754     while(relation) {
1755     gboolean is_member = FALSE;
1756    
1757     member_t *member = relation->member;
1758     while(member) {
1759     switch(member->object.type) {
1760     case RELATION: {
1761     /* relations can be check directly */
1762     if(member->object.relation == rel)
1763     is_member = TRUE;
1764     } break;
1765    
1766     default:
1767     break;
1768     }
1769     member = member->next;
1770     }
1771    
1772     /* way is a member of this relation, so move it to the member chain */
1773     if(is_member) {
1774     *cur_rel_chain = g_new0(relation_chain_t, 1);
1775     (*cur_rel_chain)->relation = relation;
1776     cur_rel_chain = &((*cur_rel_chain)->next);
1777     }
1778    
1779     relation = relation->next;
1780     }
1781    
1782     return rel_chain;
1783     }
1784    
1785     /* return all relations an object is in */
1786     relation_chain_t *osm_object_to_relation(osm_t *osm, object_t *object) {
1787     relation_chain_t *rel_chain = NULL;
1788    
1789     switch(object->type) {
1790     case NODE:
1791     rel_chain = osm_node_to_relation(osm, object->node);
1792     break;
1793    
1794     case WAY:
1795     rel_chain = osm_way_to_relation(osm, object->way);
1796     break;
1797    
1798     case RELATION:
1799     rel_chain = osm_relation_to_relation(osm, object->relation);
1800     break;
1801    
1802     default:
1803     break;
1804     }
1805    
1806     return rel_chain;
1807     }
1808    
1809     void osm_relation_chain_free(relation_chain_t *rchain) {
1810     while(rchain) {
1811     relation_chain_t *next = rchain->next;
1812     g_free(rchain);
1813     rchain = next;
1814     }
1815     }
1816    
1817 harbaum 1 /* return all ways a node is in */
1818     way_chain_t *osm_node_to_way(osm_t *osm, node_t *node) {
1819     way_chain_t *chain = NULL, **cur_chain = &chain;
1820    
1821     way_t *way = osm->way;
1822     while(way) {
1823     gboolean is_member = FALSE;
1824    
1825     node_chain_t *node_chain = way->node_chain;
1826     while(node_chain) {
1827     if(node_chain->node == node)
1828     is_member = TRUE;
1829    
1830     node_chain = node_chain->next;
1831     }
1832    
1833     /* node is a member of this relation, so move it to the member chain */
1834     if(is_member) {
1835     *cur_chain = g_new0(way_chain_t, 1);
1836     (*cur_chain)->way = way;
1837     cur_chain = &((*cur_chain)->next);
1838     }
1839    
1840 harbaum 8 way = way->next;
1841 harbaum 1 }
1842    
1843     return chain;
1844     }
1845    
1846     gboolean osm_position_within_bounds(osm_t *osm, gint x, gint y) {
1847     if((x < osm->bounds->min.x) || (x > osm->bounds->max.x)) return FALSE;
1848     if((y < osm->bounds->min.y) || (y > osm->bounds->max.y)) return FALSE;
1849     return TRUE;
1850     }
1851    
1852     /* remove the given node from all relations. used if the node is to */
1853     /* be deleted */
1854     void osm_node_remove_from_relation(osm_t *osm, node_t *node) {
1855     relation_t *relation = osm->relation;
1856 harbaum 161 printf("removing node #" ITEM_ID_FORMAT " from all relations:\n", node->id);
1857 harbaum 1
1858     while(relation) {
1859     member_t **member = &relation->member;
1860     while(*member) {
1861 harbaum 155 if(((*member)->object.type == NODE) &&
1862     ((*member)->object.node == node)) {
1863 harbaum 1
1864 harbaum 161 printf(" from relation #" ITEM_ID_FORMAT "\n", relation->id);
1865 harbaum 1
1866     member_t *cur = *member;
1867     *member = (*member)->next;
1868     osm_member_free(cur);
1869    
1870     relation->flags |= OSM_FLAG_DIRTY;
1871     } else
1872     member = &(*member)->next;
1873     }
1874     relation = relation->next;
1875     }
1876     }
1877    
1878     /* remove the given way from all relations */
1879     void osm_way_remove_from_relation(osm_t *osm, way_t *way) {
1880     relation_t *relation = osm->relation;
1881 harbaum 161 printf("removing way #" ITEM_ID_FORMAT " from all relations:\n", way->id);
1882 harbaum 1
1883     while(relation) {
1884     member_t **member = &relation->member;
1885     while(*member) {
1886 harbaum 155 if(((*member)->object.type == WAY) &&
1887     ((*member)->object.way == way)) {
1888 harbaum 1
1889 harbaum 161 printf(" from relation #" ITEM_ID_FORMAT "\n", relation->id);
1890 harbaum 1
1891     member_t *cur = *member;
1892     *member = (*member)->next;
1893     osm_member_free(cur);
1894    
1895     relation->flags |= OSM_FLAG_DIRTY;
1896     } else
1897     member = &(*member)->next;
1898     }
1899     relation = relation->next;
1900     }
1901     }
1902    
1903 harbaum 73 relation_t *osm_relation_new(void) {
1904     printf("Creating new relation\n");
1905    
1906     relation_t *relation = g_new0(relation_t, 1);
1907 harbaum 158 relation->version = 1;
1908 harbaum 73 relation->visible = TRUE;
1909     relation->flags = OSM_FLAG_NEW;
1910     relation->time = time(NULL);
1911    
1912     return relation;
1913     }
1914    
1915     void osm_relation_attach(osm_t *osm, relation_t *relation) {
1916     printf("Attaching relation\n");
1917    
1918     relation->id = osm_new_relation_id(osm);
1919     relation->flags = OSM_FLAG_NEW;
1920    
1921     /* attach to end of relation list */
1922     relation_t **lrelation = &osm->relation;
1923     while(*lrelation) lrelation = &(*lrelation)->next;
1924     *lrelation = relation;
1925     }
1926    
1927    
1928 harbaum 1 void osm_way_delete(osm_t *osm, icon_t **icon,
1929     way_t *way, gboolean permanently) {
1930    
1931     /* new ways aren't stored on the server and are just deleted permanently */
1932     if(way->flags & OSM_FLAG_NEW) {
1933 harbaum 161 printf("About to delete NEW way #" ITEM_ID_FORMAT
1934     " -> force permanent delete\n", way->id);
1935 harbaum 1 permanently = TRUE;
1936     }
1937    
1938     /* delete all nodes that aren't in other use now */
1939     node_chain_t **chain = &way->node_chain;
1940     while(*chain) {
1941    
1942     (*chain)->node->ways--;
1943 harbaum 161 printf("checking node #" ITEM_ID_FORMAT " (still used by %d)\n",
1944 harbaum 1 (*chain)->node->id, (*chain)->node->ways);
1945    
1946     /* this node must only be part of this way */
1947     if(!(*chain)->node->ways) {
1948     /* delete this node, but don't let this actually affect the */
1949     /* associated ways as the only such way is the oen we are currently */
1950     /* deleting */
1951     way_chain_t *way_chain =
1952     osm_node_delete(osm, icon, (*chain)->node, FALSE, FALSE);
1953     g_assert(way_chain);
1954     while(way_chain) {
1955     way_chain_t *way_next = way_chain->next;
1956     g_assert(way_chain->way == way);
1957     g_free(way_chain);
1958     way_chain = way_next;
1959     }
1960     }
1961    
1962     node_chain_t *cur = (*chain);
1963     *chain = cur->next;
1964     g_free(cur);
1965     }
1966     way->node_chain = NULL;
1967    
1968     if(!permanently) {
1969 harbaum 161 printf("mark way #" ITEM_ID_FORMAT " as deleted\n", way->id);
1970 harbaum 1 way->flags |= OSM_FLAG_DELETED;
1971     } else {
1972 harbaum 161 printf("permanently delete way #" ITEM_ID_FORMAT "\n", way->id);
1973 harbaum 1
1974     /* remove it from the chain */
1975     way_t **cway = &osm->way;
1976     int found = 0;
1977    
1978     while(*cway) {
1979     if(*cway == way) {
1980     found++;
1981     *cway = (*cway)->next;
1982    
1983     osm_way_free(way);
1984     } else
1985     cway = &((*cway)->next);
1986     }
1987     g_assert(found == 1);
1988     }
1989     }
1990    
1991 harbaum 75 void osm_relation_delete(osm_t *osm, relation_t *relation,
1992     gboolean permanently) {
1993    
1994     /* new relations aren't stored on the server and are just */
1995     /* deleted permanently */
1996     if(relation->flags & OSM_FLAG_NEW) {
1997 harbaum 161 printf("About to delete NEW relation #" ITEM_ID_FORMAT
1998     " -> force permanent delete\n", relation->id);
1999 harbaum 75 permanently = TRUE;
2000     }
2001    
2002     /* the deletion of a relation doesn't affect the members as they */
2003     /* don't have any reference to the relation they are part of */
2004    
2005     if(!permanently) {
2006 harbaum 161 printf("mark relation #" ITEM_ID_FORMAT " as deleted\n", relation->id);
2007 harbaum 75 relation->flags |= OSM_FLAG_DELETED;
2008     } else {
2009 harbaum 161 printf("permanently delete relation #" ITEM_ID_FORMAT "\n", relation->id);
2010 harbaum 75
2011     /* remove it from the chain */
2012     relation_t **crelation = &osm->relation;
2013     int found = 0;
2014    
2015     while(*crelation) {
2016     if(*crelation == relation) {
2017     found++;
2018     *crelation = (*crelation)->next;
2019    
2020     osm_relation_free(relation);
2021     } else
2022     crelation = &((*crelation)->next);
2023     }
2024     g_assert(found == 1);
2025     }
2026     }
2027    
2028 achadwick 98 void osm_way_reverse(way_t *way) {
2029 harbaum 1 node_chain_t *new = NULL;
2030    
2031     /* walk old chain first to last */
2032     node_chain_t *old = way->node_chain;
2033     while(old) {
2034     node_chain_t *next = old->next;
2035    
2036     /* and prepend each node to the new chain */
2037     old->next = new;
2038     new = old;
2039    
2040     old = next;
2041     }
2042    
2043     way->node_chain = new;
2044     }
2045    
2046 achadwick 98 static const char *DS_ONEWAY_FWD = "yes";
2047     static const char *DS_ONEWAY_REV = "-1";
2048     static const char *DS_LEFT_SUFFIX = ":left";
2049     static const char *DS_RIGHT_SUFFIX = ":right";
2050    
2051     /* Reverse direction-sensitive tags like "oneway". Marks the way as dirty if
2052     * anything is changed, and returns the number of flipped tags. */
2053    
2054     guint
2055     osm_way_reverse_direction_sensitive_tags (way_t *way) {
2056     tag_t *tag = way->tag;
2057     guint n_tags_altered = 0;
2058     while (tag != NULL) {
2059     char *lc_key = g_ascii_strdown(tag->key, -1);
2060     char *lc_value = g_ascii_strdown(tag->value, -1);
2061    
2062     if (strcmp(lc_key, "oneway") == 0) {
2063     // oneway={yes/true/1/-1} is unusual.
2064     // Favour "yes" and "-1".
2065     if ((strcmp(lc_value, DS_ONEWAY_FWD) == 0) ||
2066     (strcmp(lc_value, "true") == 0) ||
2067     (strcmp(lc_value, "1") == 0)) {
2068     g_free(tag->value);
2069     tag->value = g_strdup(DS_ONEWAY_REV);
2070     n_tags_altered++;
2071     }
2072     else if (strcmp(lc_value, DS_ONEWAY_REV) == 0) {
2073     g_free(tag->value);
2074     tag->value = g_strdup(DS_ONEWAY_FWD);
2075     n_tags_altered++;
2076     }
2077     else {
2078     printf("warning: unknown tag: %s=%s\n", tag->key, tag->value);
2079     }
2080     }
2081    
2082     // :left and :right suffixes
2083     else if (g_str_has_suffix(lc_key, DS_LEFT_SUFFIX)) {
2084     char *key_old = tag->key;
2085     char *lastcolon = rindex(key_old, ':');
2086     g_assert(lastcolon != NULL);
2087     *lastcolon = '\000';
2088     tag->key = g_strconcat(key_old, DS_RIGHT_SUFFIX, NULL);
2089     *lastcolon = ':';
2090     g_free(key_old);
2091     n_tags_altered++;
2092     }
2093     else if (g_str_has_suffix(lc_key, DS_RIGHT_SUFFIX)) {
2094     char *key_old = tag->key;
2095     char *lastcolon = rindex(key_old, ':');
2096     g_assert(lastcolon != NULL);
2097     *lastcolon = '\000';
2098     tag->key = g_strconcat(key_old, DS_LEFT_SUFFIX, NULL);
2099     *lastcolon = ':';
2100     g_free(key_old);
2101     n_tags_altered++;
2102     }
2103    
2104     g_free(lc_key);
2105     g_free(lc_value);
2106     tag = tag->next;
2107     }
2108     if (n_tags_altered > 0) {
2109     way->flags |= OSM_FLAG_DIRTY;
2110     }
2111     return n_tags_altered;
2112     }
2113    
2114     /* Reverse a way's role within relations where the role is direction-sensitive.
2115     * Returns the number of roles flipped, and marks any relations changed as
2116     * dirty. */
2117    
2118     static const char *DS_ROUTE_FORWARD = "forward";
2119     static const char *DS_ROUTE_REVERSE = "reverse";
2120    
2121     guint
2122     osm_way_reverse_direction_sensitive_roles(osm_t *osm, way_t *way) {
2123     relation_chain_t *rel_chain0, *rel_chain;
2124     rel_chain0 = rel_chain = osm_way_to_relation(osm, way);
2125     guint n_roles_flipped = 0;
2126    
2127     for (; rel_chain != NULL; rel_chain = rel_chain->next) {
2128     char *type = osm_tag_get_by_key(rel_chain->relation->tag, "type");
2129    
2130     // Route relations; http://wiki.openstreetmap.org/wiki/Relation:route
2131     if (strcasecmp(type, "route") == 0) {
2132    
2133     // First find the member corresponding to our way:
2134     member_t *member = rel_chain->relation->member;
2135     for (; member != NULL; member = member->next) {
2136 harbaum 155 if (member->object.type == WAY) {
2137     if (member->object.way == way)
2138 achadwick 98 break;
2139     }
2140 harbaum 155 if (member->object.type == WAY_ID) {
2141     if (member->object.id == way->id)
2142 achadwick 98 break;
2143     }
2144     }
2145     g_assert(member); // osm_way_to_relation() broken?
2146    
2147     // Then flip its role if it's one of the direction-sensitive ones
2148 achadwick 141 if (member->role == NULL) {
2149 achadwick 142 printf("null role in route relation -> ignore\n");
2150 achadwick 141 }
2151     else if (strcasecmp(member->role, DS_ROUTE_FORWARD) == 0) {
2152 achadwick 98 g_free(member->role);
2153     member->role = g_strdup(DS_ROUTE_REVERSE);
2154     rel_chain->relation->flags |= OSM_FLAG_DIRTY;
2155     ++n_roles_flipped;
2156     }
2157     else if (strcasecmp(member->role, DS_ROUTE_REVERSE) == 0) {
2158     g_free(member->role);
2159     member->role = g_strdup(DS_ROUTE_FORWARD);
2160     rel_chain->relation->flags |= OSM_FLAG_DIRTY;
2161     ++n_roles_flipped;
2162     }
2163    
2164     // TODO: what about numbered stops? Guess we ignore them; there's no
2165     // consensus about whether they should be placed on the way or to one side
2166     // of it.
2167    
2168     }//if-route
2169    
2170    
2171     }
2172     if (rel_chain0) {
2173     g_free(rel_chain0);
2174     }
2175     return n_roles_flipped;
2176     }
2177    
2178 harbaum 1 node_t *osm_way_get_first_node(way_t *way) {
2179     node_chain_t *chain = way->node_chain;
2180     if(!chain) return NULL;
2181     return chain->node;
2182     }
2183    
2184     node_t *osm_way_get_last_node(way_t *way) {
2185     node_chain_t *chain = way->node_chain;
2186    
2187     while(chain && chain->next) chain=chain->next;
2188    
2189     if(!chain) return NULL;
2190    
2191     return chain->node;
2192     }
2193    
2194     void osm_way_rotate(way_t *way, gint offset) {
2195     if(!offset) return;
2196    
2197     /* needs at least two nodes to work properly */
2198     g_assert(way->node_chain);
2199     g_assert(way->node_chain->next);
2200    
2201     while(offset--) {
2202     node_chain_t *chain = way->node_chain;
2203     chain->node->ways--; // reduce way count of old start/end node
2204    
2205     /* move all nodes ahead one chain element ... */
2206     while(chain->next) {
2207     chain->node = chain->next->node;
2208     chain = chain->next;
2209     }
2210    
2211     /* ... and make last one same as first one */
2212     chain->node = way->node_chain->node;
2213     chain->node->ways++; // increase way count of new start/end node
2214     }
2215     }
2216    
2217     tag_t *osm_tags_copy(tag_t *src_tag, gboolean update_creator) {
2218     tag_t *new_tags = NULL;
2219     tag_t **dst_tag = &new_tags;
2220    
2221     while(src_tag) {
2222     *dst_tag = g_new0(tag_t, 1);
2223     (*dst_tag)->key = g_strdup(src_tag->key);
2224     if(update_creator && (strcasecmp(src_tag->key, "created_by") == 0))
2225     (*dst_tag)->value = g_strdup(PACKAGE " v" VERSION);
2226     else
2227     (*dst_tag)->value = g_strdup(src_tag->value);
2228    
2229     dst_tag = &(*dst_tag)->next;
2230     src_tag = src_tag->next;
2231     }
2232    
2233     return new_tags;
2234     }
2235 harbaum 54
2236     /* return plain text of type */
2237 harbaum 155 char *osm_object_type_string(object_t *object) {
2238 harbaum 54 const struct { type_t type; char *name; } types[] = {
2239     { ILLEGAL, "illegal" },
2240     { NODE, "node" },
2241     { WAY, "way" },
2242     { RELATION, "relation" },
2243     { NODE_ID, "node id" },
2244     { WAY_ID, "way id" },
2245     { RELATION_ID, "relation id" },
2246     { 0, NULL }
2247     };
2248    
2249     int i;
2250     for(i=0;types[i].name;i++)
2251 harbaum 155 if(object->type == types[i].type)
2252 harbaum 54 return types[i].name;
2253    
2254     return NULL;
2255     }
2256    
2257 harbaum 155 char *osm_object_string(object_t *object) {
2258     char *type_str = osm_object_type_string(object);
2259 harbaum 54
2260 harbaum 64 if(!object)
2261     return g_strdup_printf("%s #<invalid>", type_str);
2262    
2263 harbaum 155 switch(object->type) {
2264 harbaum 64 case ILLEGAL:
2265     return g_strdup_printf("%s #<unspec>", type_str);
2266     break;
2267     case NODE:
2268 harbaum 161 return g_strdup_printf("%s #" ITEM_ID_FORMAT, type_str, object->node->id);
2269 harbaum 64 break;
2270     case WAY:
2271 harbaum 161 return g_strdup_printf("%s #" ITEM_ID_FORMAT, type_str, object->way->id);
2272 harbaum 64 break;
2273     case RELATION:
2274 harbaum 161 return g_strdup_printf("%s #" ITEM_ID_FORMAT, type_str,
2275     object->relation->id);
2276 harbaum 64 break;
2277     case NODE_ID:
2278     case WAY_ID:
2279     case RELATION_ID:
2280 harbaum 161 return g_strdup_printf("%s #" ITEM_ID_FORMAT, type_str, object->id);
2281 harbaum 64 break;
2282     }
2283     return NULL;
2284     }
2285    
2286 harbaum 155 char *osm_object_id_string(object_t *object) {
2287 harbaum 76 if(!object) return NULL;
2288    
2289 harbaum 155 switch(object->type) {
2290 harbaum 76 case ILLEGAL:
2291     return NULL;
2292     break;
2293     case NODE:
2294 harbaum 161 return g_strdup_printf("#"ITEM_ID_FORMAT, object->node->id);
2295 harbaum 76 break;
2296     case WAY:
2297 harbaum 161 return g_strdup_printf("#"ITEM_ID_FORMAT, object->way->id);
2298 harbaum 76 break;
2299     case RELATION:
2300 harbaum 161 return g_strdup_printf("#"ITEM_ID_FORMAT, object->relation->id);
2301 harbaum 76 break;
2302     case NODE_ID:
2303     case WAY_ID:
2304     case RELATION_ID:
2305 harbaum 161 return g_strdup_printf("#"ITEM_ID_FORMAT, object->id);
2306 harbaum 76 break;
2307     }
2308     return NULL;
2309     }
2310    
2311 harbaum 155 tag_t *osm_object_get_tags(object_t *object) {
2312 harbaum 76 if(!object) return NULL;
2313    
2314 harbaum 155 switch(object->type) {
2315 harbaum 76 case ILLEGAL:
2316     return NULL;
2317     break;
2318     case NODE:
2319 harbaum 155 return object->node->tag;
2320 harbaum 76 break;
2321     case WAY:
2322 harbaum 155 return object->way->tag;
2323 harbaum 76 break;
2324     case RELATION:
2325 harbaum 155 return object->relation->tag;
2326 harbaum 76 break;
2327     case NODE_ID:
2328     case WAY_ID:
2329     case RELATION_ID:
2330     return NULL;
2331     break;
2332     }
2333     return NULL;
2334     }
2335    
2336    
2337     gint osm_relation_members_num(relation_t *relation) {
2338     gint num = 0;
2339     member_t *member = relation->member;
2340     while(member) {
2341     num++;
2342     member = member->next;
2343     }
2344     return num;
2345     }
2346    
2347 harbaum 153 void osm_object_set_flags(object_t *object, int set, int clr) {
2348    
2349     switch(object->type) {
2350     case NODE:
2351     object->node->flags |= set;
2352     object->node->flags &= ~clr;
2353     break;
2354    
2355     case WAY:
2356     object->way->flags |= set;
2357     object->way->flags &= ~clr;
2358     break;
2359    
2360     case RELATION:
2361     object->relation->flags |= set;
2362     object->relation->flags &= ~clr;
2363     break;
2364    
2365     default:
2366     g_assert(0);
2367     break;
2368     }
2369     }
2370    
2371 achadwick 28 // vim:et:ts=8:sw=2:sts=2:ai