Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (show annotations)
Wed Jan 21 20:01:18 2009 UTC (15 years, 3 months ago) by harbaum
File MIME type: text/plain
File size: 67799 byte(s)
ID hashing for fast ID resolving
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 /* these defines select one of three possible xml parsers */
21 /* this is in fact selected depending on the plattform in the Makefile */
22 // #define OSM_DOM_PARSER
23 // #define OSM_STREAM_PARSER
24 // #define OSM_QND_XML_PARSER
25
26 #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 #include "banner.h"
39
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 /* ------------------------- bounds handling --------------------- */
51
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 #ifdef OSM_DOM_PARSER
63 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 osm_bounds_free(bounds);
104 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 #endif
134
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 if(!name) return NULL;
158
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 if(!str) return 0;
180
181 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 #ifdef OSM_DOM_PARSER
335 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 /* 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 /* 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 #endif
401
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 node_chain->node = osm_get_node_by_id(osm, id);
495 if(!node_chain->node) printf("Node id %lu not found\n", id);
496 else node_chain->node->ways++;
497
498 xmlFree(prop);
499
500 return node_chain;
501 }
502
503 return NULL;
504 }
505
506 #ifdef OSM_DOM_PARSER
507 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 /* 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 /* 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 #endif
566
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 member->way = osm_get_way_by_id(osm, id);
670 if(!member->way) {
671 member->type = WAY_ID;
672 member->id = id;
673 }
674 break;
675
676 case NODE:
677 /* search matching node */
678 member->node = osm_get_node_by_id(osm, id);
679 if(!member->node) {
680 member->type = NODE_ID;
681 member->id = id;
682 }
683 break;
684
685 case RELATION:
686 /* search matching relation */
687 member->relation = osm_get_relation_by_id(osm, id);
688 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 #ifdef OSM_DOM_PARSER
712 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 /* parse osm entry */
764 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 osm->node_hash = g_new0(hash_table_t, 1);
846 osm->way_hash = g_new0(hash_table_t, 1);
847
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 #endif
881
882 /* ------------------ osm handling ----------------- */
883
884 /* 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 void osm_free(icon_t **icon, osm_t *osm) {
908 if(!osm) return;
909
910 osm_hash_tables_free(osm);
911
912 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 #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 /* 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 /* 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 node_chain->node = osm_get_node_by_id(osm, id);
1139 if(!node_chain->node) printf("Node id %lu not found\n", id);
1140 else node_chain->node->ways++;
1141
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 /* 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 /* 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 member->way = osm_get_way_by_id(osm, id);
1242 if(!member->way) {
1243 member->type = WAY_ID;
1244 member->id = id;
1245 }
1246 break;
1247
1248 case NODE:
1249 /* search matching node */
1250 member->node = osm_get_node_by_id(osm, id);
1251 if(!member->node) {
1252 member->type = NODE_ID;
1253 member->id = id;
1254 }
1255 break;
1256
1257 case RELATION:
1258 /* search matching relation */
1259 member->relation = osm_get_relation_by_id(osm, id);
1260 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 if(strcasecmp(subname, "member") == 0) {
1328 *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 osm->node_hash = g_new0(hash_table_t, 1);
1346 osm->way_hash = g_new0(hash_table_t, 1);
1347
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 int num_elems = 0;
1359 const int tick_every = 50; // Balance responsive appearance with performance.
1360 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
1397 if (num_elems++ > tick_every) {
1398 num_elems = 0;
1399 banner_busy_tick();
1400 }
1401 }
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 #ifdef OSM_QND_XML_PARSER
1433 /* -------------------------- qnd-xml parser tests ------------------- */
1434
1435 #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 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 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
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 GET_PROP_POS(attributes, "lat", &node->pos.lat);
1531 GET_PROP_POS(attributes, "lon", &node->pos.lon);
1532 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 /* 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 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 node_chain->node = osm_get_node_by_id(osm, id);
1566 if(!node_chain->node) printf("Node id %lu not found\n", id);
1567 else node_chain->node->ways++;
1568
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 /* 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 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 member->way = osm_get_way_by_id(osm, id);
1635 if(!member->way) {
1636 member->type = WAY_ID;
1637 member->id = id;
1638 }
1639 break;
1640
1641 case NODE:
1642 /* search matching node */
1643 member->node = osm_get_node_by_id(osm, id);
1644 if(!member->node) {
1645 member->type = NODE_ID;
1646 member->id = id;
1647 }
1648 break;
1649
1650 case RELATION:
1651 /* search matching relation */
1652 member->relation = osm_get_relation_by_id(osm, id);
1653 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 osm->node_hash = g_new0(hash_table_t, 1);
1703 osm->way_hash = g_new0(hash_table_t, 1);
1704
1705 /* 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 #include <sys/time.h>
1754
1755 osm_t *osm_parse(char *filename) {
1756
1757 struct timeval start;
1758 gettimeofday(&start, NULL);
1759
1760 #ifdef OSM_STREAM_PARSER
1761 LIBXML_TEST_VERSION;
1762
1763 // use stream parser
1764 osm_t *osm = process_file(filename);
1765 xmlCleanupParser();
1766 #endif
1767
1768 #ifdef OSM_DOM_PARSER
1769 LIBXML_TEST_VERSION;
1770
1771 // parse into a tree
1772 /* parse the file and get the DOM */
1773 xmlDoc *doc = NULL;
1774 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 osm_t *osm = osm_parse_doc(doc);
1781 #endif
1782
1783 #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 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 }
1800
1801 gboolean osm_sanity_check(GtkWidget *parent, osm_t *osm) {
1802 if(!osm->bounds) {
1803 errorf(parent, _("Invalid data in OSM file:\n"
1804 "Boundary box missing!"));
1805 return FALSE;
1806 }
1807 if(!osm->node) {
1808 errorf(parent, _("Invalid data in OSM file:\n"
1809 "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 /* the following three functions are eating much CPU power */
2050 /* as they search the objects lists. Hashing is supposed to help */
2051 node_t *osm_get_node_by_id(osm_t *osm, item_id_t id) {
2052 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 node_t *node = osm->node;
2065 while(node) {
2066 if(node->id == id)
2067 return node;
2068
2069 node = node->next;
2070 }
2071
2072 return NULL;
2073 }
2074
2075 way_t *osm_get_way_by_id(osm_t *osm, item_id_t id) {
2076 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 way_t *way = osm->way;
2089 while(way) {
2090 if(way->id == id)
2091 return way;
2092
2093 way = way->next;
2094 }
2095
2096 return NULL;
2097 }
2098
2099 relation_t *osm_get_relation_by_id(osm_t *osm, item_id_t id) {
2100 // use linear search
2101 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 way = way->next;
2408 }
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 // vim:et:ts=8:sw=2:sts=2:ai