Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


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