Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


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