Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


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