Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


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