Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 73 - (show annotations)
Thu Feb 12 14:27:52 2009 UTC (15 years, 3 months ago) by harbaum
File MIME type: text/plain
File size: 70370 byte(s)
Create new relations, more intuitive role editing
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 void osm_relation_free(relation_t *relation) {
582 osm_tags_free(relation->tag);
583 osm_members_free(relation->member);
584
585 g_free(relation);
586 }
587
588 static void osm_relations_free(relation_t *relation) {
589 while(relation) {
590 relation_t *next = relation->next;
591 osm_relation_free(relation);
592 relation = next;
593 }
594 }
595
596 void osm_relations_dump(relation_t *relation) {
597 printf("\nRelation list:\n");
598 while(relation) {
599 char buf[64];
600 struct tm tm;
601
602 printf("Id: %lu\n", relation->id);
603 printf("User: %s\n",
604 relation->user?relation->user->name:"<unspecified>");
605 printf("Visible: %s\n", relation->visible?"yes":"no");
606
607 member_t *member = relation->member;
608 while(member) {
609 switch(member->type) {
610 case ILLEGAL:
611 case NODE_ID:
612 case WAY_ID:
613 case RELATION_ID:
614 break;
615
616 case NODE:
617 if(member->node)
618 printf(" Member: Node, id = %lu, role = %s\n",
619 member->node->id, member->role);
620 break;
621
622 case WAY:
623 if(member->way)
624 printf(" Member: Way, id = %lu, role = %s\n",
625 member->way->id, member->role);
626 break;
627
628 case RELATION:
629 if(member->relation)
630 printf(" Member: Relation, id = %lu, role = %s\n",
631 member->relation->id, member->role);
632 break;
633 }
634
635 member = member->next;
636 }
637
638 localtime_r(&relation->time, &tm);
639 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", &tm);
640 printf("Time: %s\n", buf);
641 osm_tags_dump(relation->tag);
642
643 printf("\n");
644 relation = relation->next;
645 }
646 }
647
648 member_t *osm_parse_osm_relation_member(osm_t *osm,
649 xmlDocPtr doc, xmlNode *a_node) {
650 char *prop;
651 member_t *member = g_new0(member_t, 1);
652 member->type = ILLEGAL;
653
654 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"type"))) {
655 if(strcasecmp(prop, "way") == 0) member->type = WAY;
656 else if(strcasecmp(prop, "node") == 0) member->type = NODE;
657 else if(strcasecmp(prop, "relation") == 0) member->type = RELATION;
658 xmlFree(prop);
659 }
660
661 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"ref"))) {
662 item_id_t id = strtoul(prop, NULL, 10);
663
664 switch(member->type) {
665 case ILLEGAL:
666 printf("Unable to store illegal type\n");
667 break;
668
669 case WAY:
670 /* search matching way */
671 member->way = osm_get_way_by_id(osm, id);
672 if(!member->way) {
673 member->type = WAY_ID;
674 member->id = id;
675 }
676 break;
677
678 case NODE:
679 /* search matching node */
680 member->node = osm_get_node_by_id(osm, id);
681 if(!member->node) {
682 member->type = NODE_ID;
683 member->id = id;
684 }
685 break;
686
687 case RELATION:
688 /* search matching relation */
689 member->relation = osm_get_relation_by_id(osm, id);
690 if(!member->relation) {
691 member->type = NODE_ID;
692 member->id = id;
693 }
694 break;
695
696 case WAY_ID:
697 case NODE_ID:
698 case RELATION_ID:
699 break;
700 }
701
702 xmlFree(prop);
703 }
704
705 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"role"))) {
706 if(strlen(prop) > 0) member->role = g_strdup(prop);
707 xmlFree(prop);
708 }
709
710 return member;
711 }
712
713 #ifdef OSM_DOM_PARSER
714 static relation_t *osm_parse_osm_relation(osm_t *osm,
715 xmlDocPtr doc, xmlNode *a_node) {
716 xmlNode *cur_node = NULL;
717
718 /* allocate a new relation structure */
719 relation_t *relation = g_new0(relation_t, 1);
720
721 char *prop;
722 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"id"))) {
723 relation->id = strtoul(prop, NULL, 10);
724 xmlFree(prop);
725 }
726
727 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"user"))) {
728 relation->user = osm_user(osm, prop);
729 xmlFree(prop);
730 }
731
732 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"visible"))) {
733 relation->visible = (strcasecmp(prop, "true") == 0);
734 xmlFree(prop);
735 }
736
737 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"timestamp"))) {
738 relation->time = convert_iso8601(prop);
739 xmlFree(prop);
740 }
741
742 /* scan for tags and attach a list of tags */
743 tag_t **tag = &relation->tag;
744 member_t **member = &relation->member;
745
746 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
747 if (cur_node->type == XML_ELEMENT_NODE) {
748 if(strcasecmp((char*)cur_node->name, "tag") == 0) {
749 /* attach tag to node */
750 *tag = osm_parse_osm_tag(osm, doc, cur_node);
751 if(*tag) tag = &((*tag)->next);
752 } else if(strcasecmp((char*)cur_node->name, "member") == 0) {
753 *member = osm_parse_osm_relation_member(osm, doc, cur_node);
754 if(*member) member = &((*member)->next);
755 } else
756 printf("found unhandled osm/node/%s\n", cur_node->name);
757 }
758 }
759
760 return relation;
761 }
762
763 /* ----------------------- generic xml handling -------------------------- */
764
765 /* parse osm entry */
766 static void osm_parse_osm(osm_t *osm, xmlDocPtr doc, xmlNode * a_node) {
767 xmlNode *cur_node = NULL;
768
769 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
770 if (cur_node->type == XML_ELEMENT_NODE) {
771 if(strcasecmp((char*)cur_node->name, "bounds") == 0)
772 osm->bounds = osm_parse_osm_bounds(osm, doc, cur_node);
773 else if(strcasecmp((char*)cur_node->name, "node") == 0) {
774 /* parse node and attach it to chain */
775 node_t *new = osm_parse_osm_node(osm, doc, cur_node);
776 if(new) {
777 node_t **node = &osm->node;
778
779 #ifdef OSM_SORT_ID
780 /* search chain of nodes */
781 while(*node && ((*node)->id < new->id))
782 node = &(*node)->next;
783 #endif
784
785 #ifdef OSM_SORT_LAST
786 while(*node) node = &(*node)->next;
787 #endif
788
789 /* insert into chain */
790 new->next = *node;
791 *node = new;
792 }
793 } else if(strcasecmp((char*)cur_node->name, "way") == 0) {
794 /* parse way and attach it to chain */
795 way_t *new = osm_parse_osm_way(osm, doc, cur_node);
796 if(new) {
797 way_t **way = &osm->way;
798
799 #ifdef OSM_SORT_ID
800 /* insert into chain */
801 while(*way && ((*way)->id < new->id))
802 way = &(*way)->next;
803 #endif
804
805 #ifdef OSM_SORT_LAST
806 while(*way) way = &(*way)->next;
807 #endif
808
809 /* insert into chain */
810 new->next = *way;
811 *way = new;
812 }
813 } else if(strcasecmp((char*)cur_node->name, "relation") == 0) {
814 /* parse relation and attach it to chain */
815 relation_t *new = osm_parse_osm_relation(osm, doc, cur_node);
816 if(new) {
817 relation_t **relation = &osm->relation;
818
819 #ifdef OSM_SORT_ID
820 /* search chain of ways */
821 while(*relation && ((*relation)->id < new->id))
822 relation = &(*relation)->next;
823 #endif
824
825 #ifdef OSM_SORT_LAST
826 while(*relation) relation = &(*relation)->next;
827 #endif
828
829 /* insert into chain */
830 new->next = *relation;
831 *relation = new;
832 }
833 } else
834 printf("found unhandled osm/%s\n", cur_node->name);
835
836 }
837 }
838 }
839
840 /* parse root element and search for "osm" */
841 static osm_t *osm_parse_root(xmlDocPtr doc, xmlNode * a_node) {
842 osm_t *osm;
843 xmlNode *cur_node = NULL;
844
845 /* allocate memory to hold osm file description */
846 osm = g_new0(osm_t, 1);
847 osm->node_hash = g_new0(hash_table_t, 1);
848 osm->way_hash = g_new0(hash_table_t, 1);
849
850 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
851 if (cur_node->type == XML_ELEMENT_NODE) {
852 /* parse osm osm file ... */
853 if(strcasecmp((char*)cur_node->name, "osm") == 0)
854 osm_parse_osm(osm, doc, cur_node);
855 else
856 printf("found unhandled %s\n", cur_node->name);
857 }
858 }
859
860 return osm;
861 }
862
863 static osm_t *osm_parse_doc(xmlDocPtr doc) {
864 osm_t *osm;
865
866 /* Get the root element node */
867 xmlNode *root_element = xmlDocGetRootElement(doc);
868
869 osm = osm_parse_root(doc, root_element);
870
871 /*free the document */
872 xmlFreeDoc(doc);
873
874 /*
875 * Free the global variables that may
876 * have been allocated by the parser.
877 */
878 xmlCleanupParser();
879
880 return osm;
881 }
882 #endif
883
884 /* ------------------ osm handling ----------------- */
885
886 /* the two hash tables eat over 512kBytes memory and may thus be */
887 /* freed at any time. osm2go can work without them (albeit slower) */
888 static void hash_table_free(hash_table_t *table) {
889 if(!table) return;
890
891 int i;
892 for(i=0;i<65536;i++) {
893 hash_item_t *item = table->hash[i];
894 while(item) {
895 hash_item_t *next = item->next;
896 g_free(item);
897 item = next;
898 }
899 }
900 }
901
902 void osm_hash_tables_free(osm_t *osm) {
903 hash_table_free(osm->node_hash);
904 osm->node_hash = NULL;
905 hash_table_free(osm->way_hash);
906 osm->way_hash = NULL;
907 }
908
909 void osm_free(icon_t **icon, osm_t *osm) {
910 if(!osm) return;
911
912 osm_hash_tables_free(osm);
913
914 if(osm->bounds) osm_bounds_free(osm->bounds);
915 if(osm->user) osm_users_free(osm->user);
916 if(osm->way) osm_ways_free(osm->way);
917 if(osm->node) osm_nodes_free(icon, osm->node);
918 if(osm->relation) osm_relations_free(osm->relation);
919 g_free(osm);
920 }
921
922 void osm_dump(osm_t *osm) {
923 osm_bounds_dump(osm->bounds);
924 osm_users_dump(osm->user);
925 osm_nodes_dump(osm->node);
926 osm_ways_dump(osm->way);
927 osm_relations_dump(osm->relation);
928 }
929
930 #ifdef OSM_STREAM_PARSER
931 /* -------------------------- stream parser tests ------------------- */
932
933 #include <libxml/xmlreader.h>
934
935 static gint my_strcmp(const xmlChar *a, const xmlChar *b) {
936 if(!a && !b) return 0;
937 if(!a) return -1;
938 if(!b) return +1;
939 return strcmp((char*)a,(char*)b);
940 }
941
942 /* skip current element incl. everything below (mainly for testing) */
943 /* returns FALSE if something failed */
944 static gboolean skip_element(xmlTextReaderPtr reader) {
945 g_assert(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT);
946 const xmlChar *name = xmlTextReaderConstName(reader);
947 g_assert(name);
948 int depth = xmlTextReaderDepth(reader);
949
950 if(xmlTextReaderIsEmptyElement(reader))
951 return TRUE;
952
953 int ret = xmlTextReaderRead(reader);
954 while((ret == 1) &&
955 ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
956 (xmlTextReaderDepth(reader) > depth) ||
957 (my_strcmp(xmlTextReaderConstName(reader), name) != 0))) {
958 ret = xmlTextReaderRead(reader);
959 }
960 return(ret == 1);
961 }
962
963 /* parse bounds */
964 static bounds_t *process_bounds(xmlTextReaderPtr reader) {
965 char *prop = NULL;
966 bounds_t *bounds = g_new0(bounds_t, 1);
967
968 bounds->ll_min.lat = bounds->ll_min.lon = NAN;
969 bounds->ll_max.lat = bounds->ll_max.lon = NAN;
970
971 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "minlat"))) {
972 bounds->ll_min.lat = g_ascii_strtod(prop, NULL);
973 xmlFree(prop);
974 }
975
976 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "maxlat"))) {
977 bounds->ll_max.lat = g_ascii_strtod(prop, NULL);
978 xmlFree(prop);
979 }
980
981 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "minlon"))) {
982 bounds->ll_min.lon = g_ascii_strtod(prop, NULL);
983 xmlFree(prop);
984 }
985
986 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "maxlon"))) {
987 bounds->ll_max.lon = g_ascii_strtod(prop, NULL);
988 xmlFree(prop);
989 }
990
991 if(isnan(bounds->ll_min.lat) || isnan(bounds->ll_min.lon) ||
992 isnan(bounds->ll_max.lat) || isnan(bounds->ll_max.lon)) {
993 errorf(NULL, "Invalid coordinate in bounds (%f/%f/%f/%f)",
994 bounds->ll_min.lat, bounds->ll_min.lon,
995 bounds->ll_max.lat, bounds->ll_max.lon);
996
997 osm_bounds_free(bounds);
998 return NULL;
999 }
1000
1001 /* skip everything below */
1002 skip_element(reader);
1003
1004 /* calculate map zone which will be used as a reference for all */
1005 /* drawing/projection later on */
1006 pos_t center = { (bounds->ll_max.lat + bounds->ll_min.lat)/2,
1007 (bounds->ll_max.lon + bounds->ll_min.lon)/2 };
1008
1009 pos2lpos_center(&center, &bounds->center);
1010
1011 /* the scale is needed to accomodate for "streching" */
1012 /* by the mercartor projection */
1013 bounds->scale = cos(DEG2RAD(center.lat));
1014
1015 pos2lpos_center(&bounds->ll_min, &bounds->min);
1016 bounds->min.x -= bounds->center.x;
1017 bounds->min.y -= bounds->center.y;
1018 bounds->min.x *= bounds->scale;
1019 bounds->min.y *= bounds->scale;
1020
1021 pos2lpos_center(&bounds->ll_max, &bounds->max);
1022 bounds->max.x -= bounds->center.x;
1023 bounds->max.y -= bounds->center.y;
1024 bounds->max.x *= bounds->scale;
1025 bounds->max.y *= bounds->scale;
1026
1027 return bounds;
1028 }
1029
1030 static tag_t *process_tag(xmlTextReaderPtr reader) {
1031 /* allocate a new tag structure */
1032 tag_t *tag = g_new0(tag_t, 1);
1033
1034 char *prop;
1035 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "k"))) {
1036 if(strlen(prop) > 0) tag->key = g_strdup(prop);
1037 xmlFree(prop);
1038 }
1039
1040 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "v"))) {
1041 if(strlen(prop) > 0) tag->value = g_strdup(prop);
1042 xmlFree(prop);
1043 }
1044
1045 if(!tag->key || !tag->value) {
1046 printf("incomplete tag key/value %s/%s\n", tag->key, tag->value);
1047 osm_tags_free(tag);
1048 tag = NULL;
1049 }
1050
1051 skip_element(reader);
1052 return tag;
1053 }
1054
1055 static node_t *process_node(xmlTextReaderPtr reader, osm_t *osm) {
1056
1057 /* allocate a new node structure */
1058 node_t *node = g_new0(node_t, 1);
1059 node->pos.lat = node->pos.lon = NAN;
1060
1061 char *prop;
1062 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
1063 node->id = strtoul(prop, NULL, 10);
1064 xmlFree(prop);
1065 }
1066
1067 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "lat"))) {
1068 node->pos.lat = g_ascii_strtod(prop, NULL);
1069 xmlFree(prop);
1070 }
1071
1072 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "lon"))) {
1073 node->pos.lon = g_ascii_strtod(prop, NULL);
1074 xmlFree(prop);
1075 }
1076
1077 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) {
1078 node->user = osm_user(osm, prop);
1079 xmlFree(prop);
1080 }
1081
1082 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) {
1083 node->visible = (strcasecmp(prop, "true") == 0);
1084 xmlFree(prop);
1085 }
1086
1087 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) {
1088 node->time = convert_iso8601(prop);
1089 xmlFree(prop);
1090 }
1091
1092 pos2lpos(osm->bounds, &node->pos, &node->lpos);
1093
1094 /* append node to end of hash table if present */
1095 if(osm->node_hash) {
1096 hash_item_t **item = &osm->node_hash->hash[ID2HASH(node->id)];
1097 while(*item) item = &(*item)->next;
1098
1099 *item = g_new0(hash_item_t, 1);
1100 (*item)->data.node = node;
1101 }
1102
1103 /* just an empty element? then return the node as it is */
1104 if(xmlTextReaderIsEmptyElement(reader))
1105 return node;
1106
1107 /* parse tags if present */
1108 int depth = xmlTextReaderDepth(reader);
1109
1110 /* scan all elements on same level or its children */
1111 tag_t **tag = &node->tag;
1112 int ret = xmlTextReaderRead(reader);
1113 while((ret == 1) &&
1114 ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
1115 (xmlTextReaderDepth(reader) != depth))) {
1116
1117 if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
1118 char *subname = (char*)xmlTextReaderConstName(reader);
1119 if(strcasecmp(subname, "tag") == 0) {
1120 *tag = process_tag(reader);
1121 if(*tag) tag = &(*tag)->next;
1122 } else
1123 skip_element(reader);
1124 }
1125
1126 ret = xmlTextReaderRead(reader);
1127 }
1128
1129 return node;
1130 }
1131
1132 static node_chain_t *process_nd(xmlTextReaderPtr reader, osm_t *osm) {
1133 char *prop;
1134
1135 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref"))) {
1136 item_id_t id = strtoul(prop, NULL, 10);
1137 node_chain_t *node_chain = g_new0(node_chain_t, 1);
1138
1139 /* search matching node */
1140 node_chain->node = osm_get_node_by_id(osm, id);
1141 if(!node_chain->node) printf("Node id %lu not found\n", id);
1142 else node_chain->node->ways++;
1143
1144 xmlFree(prop);
1145
1146 skip_element(reader);
1147 return node_chain;
1148 }
1149
1150 skip_element(reader);
1151 return NULL;
1152 }
1153
1154 static way_t *process_way(xmlTextReaderPtr reader, osm_t *osm) {
1155 /* allocate a new way structure */
1156 way_t *way = g_new0(way_t, 1);
1157
1158 char *prop;
1159 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
1160 way->id = strtoul(prop, NULL, 10);
1161 xmlFree(prop);
1162 }
1163
1164 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) {
1165 way->user = osm_user(osm, prop);
1166 xmlFree(prop);
1167 }
1168
1169 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) {
1170 way->visible = (strcasecmp(prop, "true") == 0);
1171 xmlFree(prop);
1172 }
1173
1174 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) {
1175 way->time = convert_iso8601(prop);
1176 xmlFree(prop);
1177 }
1178
1179 /* append way to end of hash table if present */
1180 if(osm->way_hash) {
1181 hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)];
1182 while(*item) item = &(*item)->next;
1183
1184 *item = g_new0(hash_item_t, 1);
1185 (*item)->data.way = way;
1186 }
1187
1188 /* just an empty element? then return the way as it is */
1189 /* (this should in fact never happen as this would be a way without nodes) */
1190 if(xmlTextReaderIsEmptyElement(reader))
1191 return way;
1192
1193 /* parse tags/nodes if present */
1194 int depth = xmlTextReaderDepth(reader);
1195
1196 /* scan all elements on same level or its children */
1197 tag_t **tag = &way->tag;
1198 node_chain_t **node_chain = &way->node_chain;
1199 int ret = xmlTextReaderRead(reader);
1200 while((ret == 1) &&
1201 ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
1202 (xmlTextReaderDepth(reader) != depth))) {
1203
1204 if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
1205 char *subname = (char*)xmlTextReaderConstName(reader);
1206 if(strcasecmp(subname, "nd") == 0) {
1207 *node_chain = process_nd(reader, osm);
1208 if(*node_chain) node_chain = &(*node_chain)->next;
1209 } else if(strcasecmp(subname, "tag") == 0) {
1210 *tag = process_tag(reader);
1211 if(*tag) tag = &(*tag)->next;
1212 } else
1213 skip_element(reader);
1214 }
1215 ret = xmlTextReaderRead(reader);
1216 }
1217
1218 return way;
1219 }
1220
1221 static member_t *process_member(xmlTextReaderPtr reader, osm_t *osm) {
1222 char *prop;
1223 member_t *member = g_new0(member_t, 1);
1224 member->type = ILLEGAL;
1225
1226 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "type"))) {
1227 if(strcasecmp(prop, "way") == 0) member->type = WAY;
1228 else if(strcasecmp(prop, "node") == 0) member->type = NODE;
1229 else if(strcasecmp(prop, "relation") == 0) member->type = RELATION;
1230 xmlFree(prop);
1231 }
1232
1233 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref"))) {
1234 item_id_t id = strtoul(prop, NULL, 10);
1235
1236 switch(member->type) {
1237 case ILLEGAL:
1238 printf("Unable to store illegal type\n");
1239 break;
1240
1241 case WAY:
1242 /* search matching way */
1243 member->way = osm_get_way_by_id(osm, id);
1244 if(!member->way) {
1245 member->type = WAY_ID;
1246 member->id = id;
1247 }
1248 break;
1249
1250 case NODE:
1251 /* search matching node */
1252 member->node = osm_get_node_by_id(osm, id);
1253 if(!member->node) {
1254 member->type = NODE_ID;
1255 member->id = id;
1256 }
1257 break;
1258
1259 case RELATION:
1260 /* search matching relation */
1261 member->relation = osm_get_relation_by_id(osm, id);
1262 if(!member->relation) {
1263 member->type = NODE_ID;
1264 member->id = id;
1265 }
1266 break;
1267
1268 case WAY_ID:
1269 case NODE_ID:
1270 case RELATION_ID:
1271 break;
1272 }
1273
1274 xmlFree(prop);
1275 }
1276
1277 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "role"))) {
1278 if(strlen(prop) > 0) member->role = g_strdup(prop);
1279 xmlFree(prop);
1280 }
1281
1282 return member;
1283 }
1284
1285 static relation_t *process_relation(xmlTextReaderPtr reader, osm_t *osm) {
1286 /* allocate a new relation structure */
1287 relation_t *relation = g_new0(relation_t, 1);
1288
1289 char *prop;
1290 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
1291 relation->id = strtoul(prop, NULL, 10);
1292 xmlFree(prop);
1293 }
1294
1295 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) {
1296 relation->user = osm_user(osm, prop);
1297 xmlFree(prop);
1298 }
1299
1300 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) {
1301 relation->visible = (strcasecmp(prop, "true") == 0);
1302 xmlFree(prop);
1303 }
1304
1305 if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) {
1306 relation->time = convert_iso8601(prop);
1307 xmlFree(prop);
1308 }
1309
1310 /* just an empty element? then return the relation as it is */
1311 /* (this should in fact never happen as this would be a relation */
1312 /* without members) */
1313 if(xmlTextReaderIsEmptyElement(reader))
1314 return relation;
1315
1316 /* parse tags/member if present */
1317 int depth = xmlTextReaderDepth(reader);
1318
1319 /* scan all elements on same level or its children */
1320 tag_t **tag = &relation->tag;
1321 member_t **member = &relation->member;
1322 int ret = xmlTextReaderRead(reader);
1323 while((ret == 1) &&
1324 ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
1325 (xmlTextReaderDepth(reader) != depth))) {
1326
1327 if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
1328 char *subname = (char*)xmlTextReaderConstName(reader);
1329 if(strcasecmp(subname, "member") == 0) {
1330 *member = process_member(reader, osm);
1331 if(*member) member = &(*member)->next;
1332 } else if(strcasecmp(subname, "tag") == 0) {
1333 *tag = process_tag(reader);
1334 if(*tag) tag = &(*tag)->next;
1335 } else
1336 skip_element(reader);
1337 }
1338 ret = xmlTextReaderRead(reader);
1339 }
1340
1341 return relation;
1342 }
1343
1344 static osm_t *process_osm(xmlTextReaderPtr reader) {
1345 /* alloc osm structure */
1346 osm_t *osm = g_new0(osm_t, 1);
1347 osm->node_hash = g_new0(hash_table_t, 1);
1348 osm->way_hash = g_new0(hash_table_t, 1);
1349
1350 node_t **node = &osm->node;
1351 way_t **way = &osm->way;
1352 relation_t **relation = &osm->relation;
1353
1354 /* no attributes of interest */
1355
1356 const xmlChar *name = xmlTextReaderConstName(reader);
1357 g_assert(name);
1358
1359 /* read next node */
1360 int num_elems = 0;
1361 const int tick_every = 50; // Balance responsive appearance with performance.
1362 int ret = xmlTextReaderRead(reader);
1363 while(ret == 1) {
1364
1365 switch(xmlTextReaderNodeType(reader)) {
1366 case XML_READER_TYPE_ELEMENT:
1367
1368 g_assert(xmlTextReaderDepth(reader) == 1);
1369 char *name = (char*)xmlTextReaderConstName(reader);
1370 if(strcasecmp(name, "bounds") == 0) {
1371 osm->bounds = process_bounds(reader);
1372 } else if(strcasecmp(name, "node") == 0) {
1373 *node = process_node(reader, osm);
1374 if(*node) node = &(*node)->next;
1375 } else if(strcasecmp(name, "way") == 0) {
1376 *way = process_way(reader, osm);
1377 if(*way) way = &(*way)->next;
1378 } else if(strcasecmp(name, "relation") == 0) {
1379 *relation = process_relation(reader, osm);
1380 if(*relation) relation = &(*relation)->next;
1381 } else {
1382 printf("something unknown found\n");
1383 g_assert(0);
1384 skip_element(reader);
1385 }
1386 break;
1387
1388 case XML_READER_TYPE_END_ELEMENT:
1389 /* end element must be for the current element */
1390 g_assert(xmlTextReaderDepth(reader) == 0);
1391 return osm;
1392 break;
1393
1394 default:
1395 break;
1396 }
1397 ret = xmlTextReaderRead(reader);
1398
1399 if (num_elems++ > tick_every) {
1400 num_elems = 0;
1401 banner_busy_tick();
1402 }
1403 }
1404
1405 g_assert(0);
1406 return NULL;
1407 }
1408
1409 static osm_t *process_file(const char *filename) {
1410 osm_t *osm = NULL;
1411 xmlTextReaderPtr reader;
1412 int ret;
1413
1414 reader = xmlReaderForFile(filename, NULL, 0);
1415 if (reader != NULL) {
1416 ret = xmlTextReaderRead(reader);
1417 if(ret == 1) {
1418 char *name = (char*)xmlTextReaderConstName(reader);
1419 if(name && strcasecmp(name, "osm") == 0)
1420 osm = process_osm(reader);
1421 } else
1422 printf("file empty\n");
1423
1424 xmlFreeTextReader(reader);
1425 } else {
1426 fprintf(stderr, "Unable to open %s\n", filename);
1427 }
1428 return osm;
1429 }
1430
1431 /* ----------------------- end of stream parser tests ------------------- */
1432 #endif
1433
1434 #ifdef OSM_QND_XML_PARSER
1435 /* -------------------------- qnd-xml parser tests ------------------- */
1436
1437 #ifdef USE_FLOAT
1438 #define GET_PROP_POS(a,b,c) qnd_xml_get_prop_float(a, b, c)
1439 #else
1440 #define GET_PROP_POS(a,b,c) qnd_xml_get_prop_double(a, b, c)
1441 #endif
1442
1443 gboolean osm_bounds_cb(qnd_xml_stack_t *stack,
1444 qnd_xml_attribute_t *attributes, gpointer data) {
1445
1446 /* get parent pointer */
1447 osm_t *osm = (osm_t*)stack->prev->userdata[0];
1448
1449 if(osm->bounds) {
1450 errorf(NULL, "Doubly defined bounds");
1451 return FALSE;
1452 }
1453
1454 bounds_t *bounds = osm->bounds = g_new0(bounds_t, 1);
1455
1456 bounds->ll_min.lat = bounds->ll_min.lon = NAN;
1457 bounds->ll_max.lat = bounds->ll_max.lon = NAN;
1458
1459 GET_PROP_POS(attributes, "minlat", &bounds->ll_min.lat);
1460 GET_PROP_POS(attributes, "minlon", &bounds->ll_min.lon);
1461 GET_PROP_POS(attributes, "maxlat", &bounds->ll_max.lat);
1462 GET_PROP_POS(attributes, "maxlon", &bounds->ll_max.lon);
1463
1464 if(isnan(bounds->ll_min.lat) || isnan(bounds->ll_min.lon) ||
1465 isnan(bounds->ll_max.lat) || isnan(bounds->ll_max.lon)) {
1466 errorf(NULL, "Invalid coordinate in bounds (%f/%f/%f/%f)",
1467 bounds->ll_min.lat, bounds->ll_min.lon,
1468 bounds->ll_max.lat, bounds->ll_max.lon);
1469
1470 osm_bounds_free(bounds);
1471 osm->bounds = NULL;
1472 return FALSE;
1473 }
1474
1475
1476 /* calculate map zone which will be used as a reference for all */
1477 /* drawing/projection later on */
1478 pos_t center = { (bounds->ll_max.lat + bounds->ll_min.lat)/2,
1479 (bounds->ll_max.lon + bounds->ll_min.lon)/2 };
1480
1481 pos2lpos_center(&center, &bounds->center);
1482
1483 /* the scale is needed to accomodate for "streching" */
1484 /* by the mercartor projection */
1485 bounds->scale = cos(DEG2RAD(center.lat));
1486
1487 pos2lpos_center(&bounds->ll_min, &bounds->min);
1488 bounds->min.x -= bounds->center.x;
1489 bounds->min.y -= bounds->center.y;
1490 bounds->min.x *= bounds->scale;
1491 bounds->min.y *= bounds->scale;
1492
1493 pos2lpos_center(&bounds->ll_max, &bounds->max);
1494 bounds->max.x -= bounds->center.x;
1495 bounds->max.y -= bounds->center.y;
1496 bounds->max.x *= bounds->scale;
1497 bounds->max.y *= bounds->scale;
1498
1499 return TRUE;
1500 }
1501
1502 static gboolean osm_tag_cb(qnd_xml_stack_t *stack,
1503 qnd_xml_attribute_t *attributes, gpointer data) {
1504
1505 tag_t *tag = *(tag_t**)stack->prev->userdata[1] = g_new0(tag_t, 1);
1506
1507 tag->key = qnd_xml_get_prop_str(attributes, "k");
1508 tag->value = qnd_xml_get_prop_str(attributes, "v");
1509
1510 if(!tag->key || !tag->value) {
1511 printf("incomplete tag key/value %s/%s\n", tag->key, tag->value);
1512 osm_tags_free(tag);
1513 tag = NULL;
1514 } else
1515 stack->prev->userdata[1] = &tag->next;
1516
1517 return TRUE;
1518 }
1519
1520 static gboolean osm_node_cb(qnd_xml_stack_t *stack,
1521 qnd_xml_attribute_t *attributes, gpointer data) {
1522
1523 osm_t *osm = (osm_t*)stack->prev->userdata[0];
1524
1525 /* allocate a new node structure. userdata[1] points to the current */
1526 /* position a new node is to be stored */
1527 node_t *node = *(node_t**)stack->prev->userdata[1] =
1528 stack->userdata[0] = g_new0(node_t, 1);
1529 stack->prev->userdata[1] = &node->next;
1530
1531 qnd_xml_get_prop_gulong(attributes, "id", &node->id);
1532 GET_PROP_POS(attributes, "lat", &node->pos.lat);
1533 GET_PROP_POS(attributes, "lon", &node->pos.lon);
1534 node->user = osm_user(osm, qnd_xml_get_prop(attributes, "user"));
1535 node->visible = qnd_xml_get_prop_is(attributes, "visible", "true");
1536 node->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp"));
1537
1538 pos2lpos(osm->bounds, &node->pos, &node->lpos);
1539
1540 /* store current tag pointer in userdata for fast access to current tag */
1541 stack->userdata[1] = &node->tag;
1542
1543 /* append node to end of hash table if present */
1544 if(osm->node_hash) {
1545 hash_item_t **item = &osm->node_hash->hash[ID2HASH(node->id)];
1546 while(*item) item = &(*item)->next;
1547
1548 *item = g_new0(hash_item_t, 1);
1549 (*item)->data.node = node;
1550 }
1551
1552 return TRUE;
1553 }
1554
1555 static gboolean osm_way_nd_cb(qnd_xml_stack_t *stack,
1556 qnd_xml_attribute_t *attributes, gpointer data) {
1557
1558 osm_t *osm = (osm_t*)stack->prev->prev->userdata[0];
1559
1560 item_id_t id;
1561 if(qnd_xml_get_prop_gulong(attributes, "ref", &id)) {
1562 /* allocate a new node_chain structure */
1563 node_chain_t *node_chain = *(node_chain_t**)stack->prev->userdata[2] =
1564 g_new0(node_chain_t, 1);
1565
1566 /* search matching node */
1567 node_chain->node = osm_get_node_by_id(osm, id);
1568 if(!node_chain->node) printf("Node id %lu not found\n", id);
1569 else node_chain->node->ways++;
1570
1571 stack->prev->userdata[2] = &node_chain->next;
1572 }
1573
1574 return TRUE;
1575 }
1576
1577 gboolean osm_way_cb(qnd_xml_stack_t *stack,
1578 qnd_xml_attribute_t *attributes, gpointer data) {
1579
1580 osm_t *osm = (osm_t*)stack->prev->userdata[0];
1581
1582 /* allocate a new way structure. userdata[2] points to the current */
1583 /* position a new way is to be stored in the way list */
1584 way_t *way = *(way_t**)stack->prev->userdata[2] =
1585 stack->userdata[0] = g_new0(way_t, 1);
1586 stack->prev->userdata[2] = &way->next;
1587
1588 qnd_xml_get_prop_gulong(attributes, "id", &way->id);
1589 way->user = osm_user(osm, qnd_xml_get_prop(attributes, "user"));
1590 way->visible = qnd_xml_get_prop_is(attributes, "visible", "true");
1591 way->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp"));
1592
1593 /* store current tag and node_chain pointers in userdata for fast */
1594 /* access to current tag/node_chain entry */
1595 stack->userdata[1] = &way->tag;
1596 stack->userdata[2] = &way->node_chain;
1597
1598 /* append way to end of hash table if present */
1599 if(osm->way_hash) {
1600 hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)];
1601 while(*item) item = &(*item)->next;
1602
1603 *item = g_new0(hash_item_t, 1);
1604 (*item)->data.way = way;
1605 }
1606
1607 return TRUE;
1608 }
1609
1610 static gboolean osm_rel_member_cb(qnd_xml_stack_t *stack,
1611 qnd_xml_attribute_t *attributes, gpointer data) {
1612
1613 osm_t *osm = (osm_t*)stack->prev->prev->userdata[0];
1614
1615 member_t *member = *(member_t**)stack->prev->userdata[2] =
1616 g_new0(member_t, 1);
1617 stack->prev->userdata[2] = &member->next;
1618 member->type = ILLEGAL;
1619
1620 char *type = qnd_xml_get_prop(attributes, "type");
1621 if(type) {
1622 if(strcasecmp(type, "way") == 0) member->type = WAY;
1623 else if(strcasecmp(type, "node") == 0) member->type = NODE;
1624 else if(strcasecmp(type, "relation") == 0) member->type = RELATION;
1625 }
1626
1627 item_id_t id;
1628 if(qnd_xml_get_prop_gulong(attributes, "ref", &id)) {
1629 switch(member->type) {
1630 case ILLEGAL:
1631 printf("Unable to store illegal type\n");
1632 break;
1633
1634 case WAY:
1635 /* search matching way */
1636 member->way = osm_get_way_by_id(osm, id);
1637 if(!member->way) {
1638 member->type = WAY_ID;
1639 member->id = id;
1640 }
1641 break;
1642
1643 case NODE:
1644 /* search matching node */
1645 member->node = osm_get_node_by_id(osm, id);
1646 if(!member->node) {
1647 member->type = NODE_ID;
1648 member->id = id;
1649 }
1650 break;
1651
1652 case RELATION:
1653 /* search matching relation */
1654 member->relation = osm_get_relation_by_id(osm, id);
1655 if(!member->relation) {
1656 member->type = NODE_ID;
1657 member->id = id;
1658 }
1659 break;
1660
1661 case WAY_ID:
1662 case NODE_ID:
1663 case RELATION_ID:
1664 break;
1665 }
1666 }
1667
1668 return TRUE;
1669 }
1670
1671 gboolean osm_rel_cb(qnd_xml_stack_t *stack,
1672 qnd_xml_attribute_t *attributes, gpointer data) {
1673
1674 osm_t *osm = (osm_t*)stack->prev->userdata[0];
1675
1676 /* allocate a new relation structure. userdata[3] points to the current */
1677 /* position a new relation is to be stored at in the relation list */
1678 relation_t *relation = *(relation_t**)stack->prev->userdata[3] =
1679 stack->userdata[0] = g_new0(relation_t, 1);
1680 stack->prev->userdata[3] = &relation->next;
1681
1682 qnd_xml_get_prop_gulong(attributes, "id", &relation->id);
1683 relation->user = osm_user(osm, qnd_xml_get_prop(attributes, "user"));
1684 relation->visible = qnd_xml_get_prop_is(attributes, "visible", "true");
1685 relation->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp"));
1686
1687 /* store current tag and member pointers in userdata for fast access */
1688 /* to current tag and members in their chains */
1689 stack->userdata[1] = &relation->tag;
1690 stack->userdata[2] = &relation->member;
1691
1692 return TRUE;
1693 }
1694
1695 gboolean osm_cb(qnd_xml_stack_t *stack,
1696 qnd_xml_attribute_t *attributes, gpointer data) {
1697
1698 g_assert(!stack->userdata[0]);
1699
1700 /* also set parents (roots) userdata as it's the parsers return value */
1701 osm_t *osm = stack->prev->userdata[0] =
1702 stack->userdata[0] = g_new0(osm_t, 1);
1703
1704 osm->node_hash = g_new0(hash_table_t, 1);
1705 osm->way_hash = g_new0(hash_table_t, 1);
1706
1707 /* store direct pointers for faster list access */
1708 /* (otherwise we'd have to search the end of the lists for every item */
1709 /* to be attached) */
1710 stack->userdata[1] = &osm->node;
1711 stack->userdata[2] = &osm->way;
1712 stack->userdata[3] = &osm->relation;
1713
1714 return TRUE;
1715 }
1716
1717
1718 /* these structures describe the content qnd_xml expects while parsing */
1719 qnd_xml_entry_t osm_node_tag = { "tag", osm_tag_cb, QND_XML_LEAF };
1720
1721 qnd_xml_entry_t osm_way_tag = { "tag", osm_tag_cb, QND_XML_LEAF };
1722 qnd_xml_entry_t osm_way_nd = { "nd", osm_way_nd_cb, QND_XML_LEAF };
1723
1724 qnd_xml_entry_t osm_rel_tag = { "tag", osm_tag_cb, QND_XML_LEAF };
1725 qnd_xml_entry_t osm_rel_member = { "member", osm_rel_member_cb, QND_XML_LEAF };
1726
1727 qnd_xml_entry_t osm_bounds = { "bounds", osm_bounds_cb, QND_XML_LEAF };
1728
1729 qnd_xml_entry_t *node_children[] = { &osm_node_tag },
1730 osm_node = { "node", osm_node_cb, QND_XML_CHILDREN(node_children) };
1731
1732 qnd_xml_entry_t *way_children[] = { &osm_way_tag, &osm_way_nd },
1733 osm_way = { "way", osm_way_cb, QND_XML_CHILDREN(way_children) };
1734
1735 qnd_xml_entry_t *rel_children[] = { &osm_rel_tag, &osm_rel_member },
1736 osm_rel = { "rel", osm_rel_cb, QND_XML_CHILDREN(rel_children) };
1737
1738 /* the osm element */
1739 qnd_xml_entry_t *osm_children[] = {
1740 &osm_bounds, &osm_node, &osm_way, &osm_rel };
1741 qnd_xml_entry_t osm = { "osm", osm_cb, QND_XML_CHILDREN(osm_children) };
1742
1743 /* the root element */
1744 qnd_xml_entry_t *root_children[] = { &osm };
1745 qnd_xml_entry_t root = { "<root>", NULL, QND_XML_CHILDREN(root_children) };
1746
1747 // gcc `pkg-config --cflags --libs glib-2.0` -o qnd_xml qnd_xml.c
1748
1749
1750
1751 /* ----------------------- end of qnd-xml parser tests ------------------- */
1752 #endif
1753
1754
1755 #include <sys/time.h>
1756
1757 osm_t *osm_parse(char *filename) {
1758
1759 struct timeval start;
1760 gettimeofday(&start, NULL);
1761
1762 #ifdef OSM_STREAM_PARSER
1763 LIBXML_TEST_VERSION;
1764
1765 // use stream parser
1766 osm_t *osm = process_file(filename);
1767 xmlCleanupParser();
1768 #endif
1769
1770 #ifdef OSM_DOM_PARSER
1771 LIBXML_TEST_VERSION;
1772
1773 // parse into a tree
1774 /* parse the file and get the DOM */
1775 xmlDoc *doc = NULL;
1776 if ((doc = xmlReadFile(filename, NULL, 0)) == NULL) {
1777 xmlErrorPtr errP = xmlGetLastError();
1778 errorf(NULL, "While parsing \"%s\":\n\n%s", filename, errP->message);
1779 return NULL;
1780 }
1781
1782 osm_t *osm = osm_parse_doc(doc);
1783 #endif
1784
1785 #ifdef OSM_QND_XML_PARSER
1786 osm_t *osm = NULL;
1787 if(!(osm = qnd_xml_parse(filename, &root, NULL))) {
1788 errorf(NULL, "While parsing \"%s\"", filename);
1789 return NULL;
1790 }
1791 #endif
1792
1793 struct timeval end;
1794 gettimeofday(&end, NULL);
1795
1796 printf("total parse time: %ldms\n",
1797 (end.tv_usec - start.tv_usec)/1000 +
1798 (end.tv_sec - start.tv_sec)*1000);
1799
1800 return osm;
1801 }
1802
1803 gboolean osm_sanity_check(GtkWidget *parent, osm_t *osm) {
1804 if(!osm->bounds) {
1805 errorf(parent, _("Invalid data in OSM file:\n"
1806 "Boundary box missing!"));
1807 return FALSE;
1808 }
1809 if(!osm->node) {
1810 errorf(parent, _("Invalid data in OSM file:\n"
1811 "No drawable content found!"));
1812 return FALSE;
1813 }
1814 return TRUE;
1815 }
1816
1817 /* ------------------------- misc access functions -------------- */
1818
1819 char *osm_tag_get_by_key(tag_t *tag, char *key) {
1820 if(!tag || !key) return NULL;
1821
1822 while(tag) {
1823 if(strcasecmp(tag->key, key) == 0)
1824 return tag->value;
1825
1826 tag = tag->next;
1827 }
1828
1829 return NULL;
1830 }
1831
1832 char *osm_way_get_value(way_t *way, char *key) {
1833 tag_t *tag = way->tag;
1834
1835 while(tag) {
1836 if(strcasecmp(tag->key, key) == 0)
1837 return tag->value;
1838
1839 tag = tag->next;
1840 }
1841
1842 return NULL;
1843 }
1844
1845 char *osm_node_get_value(node_t *node, char *key) {
1846 tag_t *tag = node->tag;
1847
1848 while(tag) {
1849 if(strcasecmp(tag->key, key) == 0)
1850 return tag->value;
1851
1852 tag = tag->next;
1853 }
1854
1855 return NULL;
1856 }
1857
1858 gboolean osm_way_has_value(way_t *way, char *str) {
1859 tag_t *tag = way->tag;
1860
1861 while(tag) {
1862 if(tag->value && strcasecmp(tag->value, str) == 0)
1863 return TRUE;
1864
1865 tag = tag->next;
1866 }
1867 return FALSE;
1868 }
1869
1870 gboolean osm_node_has_value(node_t *node, char *str) {
1871 tag_t *tag = node->tag;
1872
1873 while(tag) {
1874 if(tag->value && strcasecmp(tag->value, str) == 0)
1875 return TRUE;
1876
1877 tag = tag->next;
1878 }
1879 return FALSE;
1880 }
1881
1882 gboolean osm_node_has_tag(node_t *node) {
1883 tag_t *tag = node->tag;
1884
1885 if(tag && strcasecmp(tag->key, "created_by") == 0)
1886 tag = tag->next;
1887
1888 return tag != NULL;
1889 }
1890
1891 /* return true if node is part of way */
1892 gboolean osm_node_in_way(way_t *way, node_t *node) {
1893 node_chain_t *node_chain = way->node_chain;
1894 while(node_chain) {
1895 if(node_chain->node == node)
1896 return TRUE;
1897
1898 node_chain = node_chain->next;
1899 }
1900 return FALSE;
1901 }
1902
1903 static void osm_generate_tags(tag_t *tag, xmlNodePtr node) {
1904 while(tag) {
1905 /* make sure "created_by" tag contains our id */
1906 if(strcasecmp(tag->key, "created_by") == 0) {
1907 g_free(tag->value);
1908 tag->value = g_strdup(PACKAGE " v" VERSION);
1909 }
1910
1911 xmlNodePtr tag_node = xmlNewChild(node, NULL, BAD_CAST "tag", NULL);
1912 xmlNewProp(tag_node, BAD_CAST "k", BAD_CAST tag->key);
1913 xmlNewProp(tag_node, BAD_CAST "v", BAD_CAST tag->value);
1914 tag = tag->next;
1915 }
1916 }
1917
1918 /* build xml representation for a way */
1919 char *osm_generate_xml(osm_t *osm, type_t type, void *item) {
1920 char str[32];
1921 xmlChar *result = NULL;
1922 int len = 0;
1923
1924 LIBXML_TEST_VERSION;
1925
1926 xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
1927 xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "osm");
1928 xmlNewProp(root_node, BAD_CAST "version", BAD_CAST "0.5");
1929 xmlNewProp(root_node, BAD_CAST "generator", BAD_CAST PACKAGE " V" VERSION);
1930 xmlDocSetRootElement(doc, root_node);
1931
1932 switch(type) {
1933 case NODE:
1934 {
1935 node_t *node = (node_t*)item;
1936 xmlNodePtr node_node = xmlNewChild(root_node, NULL,
1937 BAD_CAST "node", NULL);
1938 /* new nodes don't have an id, but get one after the upload */
1939 if(!(node->flags & OSM_FLAG_NEW)) {
1940 snprintf(str, sizeof(str), "%u", (unsigned)node->id);
1941 xmlNewProp(node_node, BAD_CAST "id", BAD_CAST str);
1942 }
1943 g_ascii_dtostr(str, sizeof(str), node->pos.lat);
1944 xmlNewProp(node_node, BAD_CAST "lat", BAD_CAST str);
1945 g_ascii_dtostr(str, sizeof(str), node->pos.lon);
1946 xmlNewProp(node_node, BAD_CAST "lon", BAD_CAST str);
1947 osm_generate_tags(node->tag, node_node);
1948 }
1949 break;
1950
1951 case WAY:
1952 {
1953 way_t *way = (way_t*)item;
1954 xmlNodePtr way_node = xmlNewChild(root_node, NULL, BAD_CAST "way", NULL);
1955 snprintf(str, sizeof(str), "%u", (unsigned)way->id);
1956 xmlNewProp(way_node, BAD_CAST "id", BAD_CAST str);
1957
1958 node_chain_t *node_chain = way->node_chain;
1959 while(node_chain) {
1960 xmlNodePtr nd_node = xmlNewChild(way_node, NULL, BAD_CAST "nd", NULL);
1961 char *str = g_strdup_printf("%ld", node_chain->node->id);
1962 xmlNewProp(nd_node, BAD_CAST "ref", BAD_CAST str);
1963 g_free(str);
1964 node_chain = node_chain->next;
1965 }
1966
1967 osm_generate_tags(way->tag, way_node);
1968 }
1969 break;
1970
1971 case RELATION:
1972 {
1973 relation_t *relation = (relation_t*)item;
1974 xmlNodePtr rel_node = xmlNewChild(root_node, NULL,
1975 BAD_CAST "relation", NULL);
1976 snprintf(str, sizeof(str), "%u", (unsigned)relation->id);
1977 xmlNewProp(rel_node, BAD_CAST "id", BAD_CAST str);
1978
1979 member_t *member = relation->member;
1980 while(member) {
1981 xmlNodePtr m_node = xmlNewChild(rel_node,NULL,BAD_CAST "member", NULL);
1982 char *str = NULL;
1983
1984 switch(member->type) {
1985 case NODE:
1986 xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "node");
1987 str = g_strdup_printf("%ld", member->node->id);
1988 break;
1989
1990 case WAY:
1991 xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "way");
1992 str = g_strdup_printf("%ld", member->way->id);
1993 break;
1994
1995 case RELATION:
1996 xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "relation");
1997 str = g_strdup_printf("%ld", member->relation->id);
1998 break;
1999
2000 default:
2001 break;
2002 }
2003
2004 if(str) {
2005 xmlNewProp(m_node, BAD_CAST "ref", BAD_CAST str);
2006 g_free(str);
2007 }
2008
2009 if(member->role)
2010 xmlNewProp(m_node, BAD_CAST "role", BAD_CAST member->role);
2011 else
2012 xmlNewProp(m_node, BAD_CAST "role", BAD_CAST "");
2013
2014 member = member->next;
2015 }
2016 osm_generate_tags(relation->tag, rel_node);
2017 }
2018 break;
2019
2020 default:
2021 printf("neither NODE nor WAY nor RELATION\n");
2022 g_assert(0);
2023 break;
2024 }
2025
2026 xmlDocDumpFormatMemoryEnc(doc, &result, &len, "UTF-8", 1);
2027 xmlFreeDoc(doc);
2028 xmlCleanupParser();
2029
2030 // puts("xml encoding result:");
2031 // puts((char*)result);
2032
2033 return (char*)result;
2034 }
2035
2036 /* build xml representation for a node */
2037 char *osm_generate_xml_node(osm_t *osm, node_t *node) {
2038 return osm_generate_xml(osm, NODE, node);
2039 }
2040
2041 /* build xml representation for a way */
2042 char *osm_generate_xml_way(osm_t *osm, way_t *way) {
2043 return osm_generate_xml(osm, WAY, way);
2044 }
2045
2046 /* build xml representation for a relation */
2047 char *osm_generate_xml_relation(osm_t *osm, relation_t *relation) {
2048 return osm_generate_xml(osm, RELATION, relation);
2049 }
2050
2051 /* the following three functions are eating much CPU power */
2052 /* as they search the objects lists. Hashing is supposed to help */
2053 node_t *osm_get_node_by_id(osm_t *osm, item_id_t id) {
2054 if(id > 0 && osm->node_hash) {
2055 // use hash table if present
2056 hash_item_t *item = osm->node_hash->hash[ID2HASH(id)];
2057 while(item) {
2058 if(item->data.node->id == id)
2059 return item->data.node;
2060
2061 item = item->next;
2062 }
2063 }
2064
2065 /* use linear search if no hash tables are present or search in hash table failed */
2066 node_t *node = osm->node;
2067 while(node) {
2068 if(node->id == id)
2069 return node;
2070
2071 node = node->next;
2072 }
2073
2074 return NULL;
2075 }
2076
2077 way_t *osm_get_way_by_id(osm_t *osm, item_id_t id) {
2078 if(id > 0 && osm->way_hash) {
2079 // use hash table if present
2080 hash_item_t *item = osm->way_hash->hash[ID2HASH(id)];
2081 while(item) {
2082 if(item->data.way->id == id)
2083 return item->data.way;
2084
2085 item = item->next;
2086 }
2087 }
2088
2089 /* use linear search if no hash tables are present or search on hash table failed */
2090 way_t *way = osm->way;
2091 while(way) {
2092 if(way->id == id)
2093 return way;
2094
2095 way = way->next;
2096 }
2097
2098 return NULL;
2099 }
2100
2101 relation_t *osm_get_relation_by_id(osm_t *osm, item_id_t id) {
2102 // use linear search
2103 relation_t *relation = osm->relation;
2104 while(relation) {
2105 if(relation->id == id)
2106 return relation;
2107
2108 relation = relation->next;
2109 }
2110
2111 return NULL;
2112 }
2113
2114 /* ---------- edit functions ------------- */
2115
2116 item_id_t osm_new_way_id(osm_t *osm) {
2117 item_id_t id = -1;
2118
2119 while(TRUE) {
2120 gboolean found = FALSE;
2121 way_t *way = osm->way;
2122 while(way) {
2123 if(way->id == id)
2124 found = TRUE;
2125
2126 way = way->next;
2127 }
2128
2129 /* no such id so far -> use it */
2130 if(!found) return id;
2131
2132 id--;
2133 }
2134 g_assert(0);
2135 return 0;
2136 }
2137
2138 item_id_t osm_new_node_id(osm_t *osm) {
2139 item_id_t id = -1;
2140
2141 while(TRUE) {
2142 gboolean found = FALSE;
2143 node_t *node = osm->node;
2144 while(node) {
2145 if(node->id == id)
2146 found = TRUE;
2147
2148 node = node->next;
2149 }
2150
2151 /* no such id so far -> use it */
2152 if(!found) return id;
2153
2154 id--;
2155 }
2156 g_assert(0);
2157 return 0;
2158 }
2159
2160 item_id_t osm_new_relation_id(osm_t *osm) {
2161 item_id_t id = -1;
2162
2163 while(TRUE) {
2164 gboolean found = FALSE;
2165 relation_t *relation = osm->relation;
2166 while(relation) {
2167 if(relation->id == id)
2168 found = TRUE;
2169
2170 relation = relation->next;
2171 }
2172
2173 /* no such id so far -> use it */
2174 if(!found) return id;
2175
2176 id--;
2177 }
2178 g_assert(0);
2179 return 0;
2180 }
2181
2182 node_t *osm_node_new(osm_t *osm, gint x, gint y) {
2183 printf("Creating new node\n");
2184
2185 node_t *node = g_new0(node_t, 1);
2186 node->lpos.x = x;
2187 node->lpos.y = y;
2188 node->visible = TRUE;
2189 node->time = time(NULL);
2190
2191 /* add created_by tag */
2192 node->tag = g_new0(tag_t, 1);
2193 node->tag->key = g_strdup("created_by");
2194 node->tag->value = g_strdup(PACKAGE " v" VERSION);
2195
2196 /* convert screen position back to ll */
2197 lpos2pos(osm->bounds, &node->lpos, &node->pos);
2198
2199 printf(" new at %d %d (%f %f)\n",
2200 node->lpos.x, node->lpos.y, node->pos.lat, node->pos.lon);
2201
2202 return node;
2203 }
2204
2205
2206 void osm_node_attach(osm_t *osm, node_t *node) {
2207 printf("Attaching node\n");
2208
2209 node->id = osm_new_node_id(osm);
2210 node->flags = OSM_FLAG_NEW;
2211
2212 /* attach to end of node list */
2213 node_t **lnode = &osm->node;
2214 while(*lnode) lnode = &(*lnode)->next;
2215 *lnode = node;
2216 }
2217
2218 void osm_node_restore(osm_t *osm, node_t *node) {
2219 printf("Restoring node\n");
2220
2221 /* attach to end of node list */
2222 node_t **lnode = &osm->node;
2223 while(*lnode) lnode = &(*lnode)->next;
2224 *lnode = node;
2225 }
2226
2227 way_t *osm_way_new(void) {
2228 printf("Creating new way\n");
2229
2230 way_t *way = g_new0(way_t, 1);
2231 way->visible = TRUE;
2232 way->flags = OSM_FLAG_NEW;
2233 way->time = time(NULL);
2234
2235 /* add created_by tag */
2236 way->tag = g_new0(tag_t, 1);
2237 way->tag->key = g_strdup("created_by");
2238 way->tag->value = g_strdup(PACKAGE " v" VERSION);
2239
2240 return way;
2241 }
2242
2243 void osm_way_attach(osm_t *osm, way_t *way) {
2244 printf("Attaching way\n");
2245
2246 way->id = osm_new_way_id(osm);
2247 way->flags = OSM_FLAG_NEW;
2248
2249 /* attach to end of way list */
2250 way_t **lway = &osm->way;
2251 while(*lway) lway = &(*lway)->next;
2252 *lway = way;
2253 }
2254
2255 /* returns pointer to chain of ways affected by this deletion */
2256 way_chain_t *osm_node_delete(osm_t *osm, icon_t **icon,
2257 node_t *node, gboolean permanently,
2258 gboolean affect_ways) {
2259 way_chain_t *way_chain = NULL, **cur_way_chain = &way_chain;
2260
2261 /* new nodes aren't stored on the server and are just deleted permanently */
2262 if(node->flags & OSM_FLAG_NEW) {
2263 printf("About to delete NEW node #%ld -> force permanent delete\n",
2264 node->id);
2265 permanently = TRUE;
2266 }
2267
2268 /* first remove node from all ways using it */
2269 way_t *way = osm->way;
2270 while(way) {
2271 node_chain_t **chain = &(way->node_chain);
2272 gboolean modified = FALSE;
2273 while(*chain) {
2274 /* remove node from chain */
2275 if(node == (*chain)->node) {
2276 modified = TRUE;
2277 if(affect_ways) {
2278 node_chain_t *next = (*chain)->next;
2279 g_free(*chain);
2280 *chain = next;
2281 } else
2282 chain = &((*chain)->next);
2283 } else
2284 chain = &((*chain)->next);
2285 }
2286
2287 if(modified) {
2288 way->flags |= OSM_FLAG_DIRTY;
2289
2290 /* and add the way to the list of affected ways */
2291 *cur_way_chain = g_new0(way_chain_t, 1);
2292 (*cur_way_chain)->way = way;
2293 cur_way_chain = &((*cur_way_chain)->next);
2294 }
2295
2296 way = way->next;
2297 }
2298
2299 if(!permanently) {
2300 printf("mark node #%ld as deleted\n", node->id);
2301 node->flags |= OSM_FLAG_DELETED;
2302 } else {
2303 printf("permanently delete node #%ld\n", node->id);
2304
2305 /* remove it from the chain */
2306 node_t **cnode = &osm->node;
2307 int found = 0;
2308
2309 while(*cnode) {
2310 if(*cnode == node) {
2311 found++;
2312 *cnode = (*cnode)->next;
2313
2314 osm_node_free(icon, node);
2315 } else
2316 cnode = &((*cnode)->next);
2317 }
2318 g_assert(found == 1);
2319 }
2320
2321 return way_chain;
2322 }
2323
2324 guint osm_way_number_of_nodes(way_t *way) {
2325 guint nodes = 0;
2326 node_chain_t *chain = way->node_chain;
2327 while(chain) {
2328 nodes++;
2329 chain = chain->next;
2330 }
2331 return nodes;
2332 }
2333
2334 /* return all relations a node is in */
2335 relation_chain_t *osm_node_to_relation(osm_t *osm, node_t *node) {
2336 relation_chain_t *rel_chain = NULL, **cur_rel_chain = &rel_chain;
2337
2338 relation_t *relation = osm->relation;
2339 while(relation) {
2340 gboolean is_member = FALSE;
2341
2342 member_t *member = relation->member;
2343 while(member) {
2344 switch(member->type) {
2345 case NODE:
2346 /* nodes are checked directly */
2347 if(member->node == node)
2348 is_member = TRUE;
2349 break;
2350
2351 case WAY: {
2352 /* ways have to be checked for the nodes they consist of */
2353 node_chain_t *chain = member->way->node_chain;
2354 while(chain && !is_member) {
2355 if(chain->node == node)
2356 is_member = TRUE;
2357
2358 chain = chain->next;
2359 }
2360 } break;
2361
2362 default:
2363 break;
2364 }
2365 member = member->next;
2366 }
2367
2368 /* node is a member of this relation, so move it to the member chain */
2369 if(is_member) {
2370 *cur_rel_chain = g_new0(relation_chain_t, 1);
2371 (*cur_rel_chain)->relation = relation;
2372 cur_rel_chain = &((*cur_rel_chain)->next);
2373 }
2374
2375 relation = relation->next;
2376 }
2377
2378 return rel_chain;
2379 }
2380
2381 /* return all relations a way is in */
2382 relation_chain_t *osm_way_to_relation(osm_t *osm, way_t *way) {
2383 relation_chain_t *rel_chain = NULL, **cur_rel_chain = &rel_chain;
2384
2385 relation_t *relation = osm->relation;
2386 while(relation) {
2387 gboolean is_member = FALSE;
2388
2389 member_t *member = relation->member;
2390 while(member) {
2391 switch(member->type) {
2392 case WAY: {
2393 /* ways can be check directly */
2394 if(member->way == way)
2395 is_member = TRUE;
2396 } break;
2397
2398 default:
2399 break;
2400 }
2401 member = member->next;
2402 }
2403
2404 /* way is a member of this relation, so move it to the member chain */
2405 if(is_member) {
2406 *cur_rel_chain = g_new0(relation_chain_t, 1);
2407 (*cur_rel_chain)->relation = relation;
2408 cur_rel_chain = &((*cur_rel_chain)->next);
2409 }
2410
2411 relation = relation->next;
2412 }
2413
2414 return rel_chain;
2415 }
2416
2417 /* return all ways a node is in */
2418 way_chain_t *osm_node_to_way(osm_t *osm, node_t *node) {
2419 way_chain_t *chain = NULL, **cur_chain = &chain;
2420
2421 way_t *way = osm->way;
2422 while(way) {
2423 gboolean is_member = FALSE;
2424
2425 node_chain_t *node_chain = way->node_chain;
2426 while(node_chain) {
2427 if(node_chain->node == node)
2428 is_member = TRUE;
2429
2430 node_chain = node_chain->next;
2431 }
2432
2433 /* node is a member of this relation, so move it to the member chain */
2434 if(is_member) {
2435 *cur_chain = g_new0(way_chain_t, 1);
2436 (*cur_chain)->way = way;
2437 cur_chain = &((*cur_chain)->next);
2438 }
2439
2440 way = way->next;
2441 }
2442
2443 return chain;
2444 }
2445
2446 gboolean osm_position_within_bounds(osm_t *osm, gint x, gint y) {
2447 if((x < osm->bounds->min.x) || (x > osm->bounds->max.x)) return FALSE;
2448 if((y < osm->bounds->min.y) || (y > osm->bounds->max.y)) return FALSE;
2449 return TRUE;
2450 }
2451
2452 /* remove the given node from all relations. used if the node is to */
2453 /* be deleted */
2454 void osm_node_remove_from_relation(osm_t *osm, node_t *node) {
2455 relation_t *relation = osm->relation;
2456 printf("removing node #%ld from all relations:\n", node->id);
2457
2458 while(relation) {
2459 member_t **member = &relation->member;
2460 while(*member) {
2461 if(((*member)->type == NODE) &&
2462 ((*member)->node == node)) {
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 /* remove the given way from all relations */
2479 void osm_way_remove_from_relation(osm_t *osm, way_t *way) {
2480 relation_t *relation = osm->relation;
2481 printf("removing way #%ld from all relations:\n", way->id);
2482
2483 while(relation) {
2484 member_t **member = &relation->member;
2485 while(*member) {
2486 if(((*member)->type == WAY) &&
2487 ((*member)->way == way)) {
2488
2489 printf(" from relation #%ld\n", relation->id);
2490
2491 member_t *cur = *member;
2492 *member = (*member)->next;
2493 osm_member_free(cur);
2494
2495 relation->flags |= OSM_FLAG_DIRTY;
2496 } else
2497 member = &(*member)->next;
2498 }
2499 relation = relation->next;
2500 }
2501 }
2502
2503 relation_t *osm_relation_new(void) {
2504 printf("Creating new relation\n");
2505
2506 relation_t *relation = g_new0(relation_t, 1);
2507 relation->visible = TRUE;
2508 relation->flags = OSM_FLAG_NEW;
2509 relation->time = time(NULL);
2510
2511 /* add created_by tag */
2512 relation->tag = g_new0(tag_t, 1);
2513 relation->tag->key = g_strdup("created_by");
2514 relation->tag->value = g_strdup(PACKAGE " v" VERSION);
2515
2516 return relation;
2517 }
2518
2519 void osm_relation_attach(osm_t *osm, relation_t *relation) {
2520 printf("Attaching relation\n");
2521
2522 relation->id = osm_new_relation_id(osm);
2523 relation->flags = OSM_FLAG_NEW;
2524
2525 /* attach to end of relation list */
2526 relation_t **lrelation = &osm->relation;
2527 while(*lrelation) lrelation = &(*lrelation)->next;
2528 *lrelation = relation;
2529 }
2530
2531
2532 void osm_way_delete(osm_t *osm, icon_t **icon,
2533 way_t *way, gboolean permanently) {
2534
2535 /* new ways aren't stored on the server and are just deleted permanently */
2536 if(way->flags & OSM_FLAG_NEW) {
2537 printf("About to delete NEW way #%ld -> force permanent delete\n",
2538 way->id);
2539 permanently = TRUE;
2540 }
2541
2542 /* delete all nodes that aren't in other use now */
2543 node_chain_t **chain = &way->node_chain;
2544 while(*chain) {
2545
2546 (*chain)->node->ways--;
2547 printf("checking node #%ld (still used by %d)\n",
2548 (*chain)->node->id, (*chain)->node->ways);
2549
2550 /* this node must only be part of this way */
2551 if(!(*chain)->node->ways) {
2552 /* delete this node, but don't let this actually affect the */
2553 /* associated ways as the only such way is the oen we are currently */
2554 /* deleting */
2555 way_chain_t *way_chain =
2556 osm_node_delete(osm, icon, (*chain)->node, FALSE, FALSE);
2557 g_assert(way_chain);
2558 while(way_chain) {
2559 way_chain_t *way_next = way_chain->next;
2560 g_assert(way_chain->way == way);
2561 g_free(way_chain);
2562 way_chain = way_next;
2563 }
2564 }
2565
2566 node_chain_t *cur = (*chain);
2567 *chain = cur->next;
2568 g_free(cur);
2569 }
2570 way->node_chain = NULL;
2571
2572 if(!permanently) {
2573 printf("mark way #%ld as deleted\n", way->id);
2574 way->flags |= OSM_FLAG_DELETED;
2575 } else {
2576 printf("permanently delete way #%ld\n", way->id);
2577
2578 /* remove it from the chain */
2579 way_t **cway = &osm->way;
2580 int found = 0;
2581
2582 while(*cway) {
2583 if(*cway == way) {
2584 found++;
2585 *cway = (*cway)->next;
2586
2587 osm_way_free(way);
2588 } else
2589 cway = &((*cway)->next);
2590 }
2591 g_assert(found == 1);
2592 }
2593 }
2594
2595 void osm_way_revert(way_t *way) {
2596 node_chain_t *new = NULL;
2597
2598 /* walk old chain first to last */
2599 node_chain_t *old = way->node_chain;
2600 while(old) {
2601 node_chain_t *next = old->next;
2602
2603 /* and prepend each node to the new chain */
2604 old->next = new;
2605 new = old;
2606
2607 old = next;
2608 }
2609
2610 way->node_chain = new;
2611 }
2612
2613 node_t *osm_way_get_first_node(way_t *way) {
2614 node_chain_t *chain = way->node_chain;
2615 if(!chain) return NULL;
2616 return chain->node;
2617 }
2618
2619 node_t *osm_way_get_last_node(way_t *way) {
2620 node_chain_t *chain = way->node_chain;
2621
2622 while(chain && chain->next) chain=chain->next;
2623
2624 if(!chain) return NULL;
2625
2626 return chain->node;
2627 }
2628
2629 void osm_way_rotate(way_t *way, gint offset) {
2630 if(!offset) return;
2631
2632 /* needs at least two nodes to work properly */
2633 g_assert(way->node_chain);
2634 g_assert(way->node_chain->next);
2635
2636 while(offset--) {
2637 node_chain_t *chain = way->node_chain;
2638 chain->node->ways--; // reduce way count of old start/end node
2639
2640 /* move all nodes ahead one chain element ... */
2641 while(chain->next) {
2642 chain->node = chain->next->node;
2643 chain = chain->next;
2644 }
2645
2646 /* ... and make last one same as first one */
2647 chain->node = way->node_chain->node;
2648 chain->node->ways++; // increase way count of new start/end node
2649 }
2650 }
2651
2652 tag_t *osm_tags_copy(tag_t *src_tag, gboolean update_creator) {
2653 tag_t *new_tags = NULL;
2654 tag_t **dst_tag = &new_tags;
2655
2656 while(src_tag) {
2657 *dst_tag = g_new0(tag_t, 1);
2658 (*dst_tag)->key = g_strdup(src_tag->key);
2659 if(update_creator && (strcasecmp(src_tag->key, "created_by") == 0))
2660 (*dst_tag)->value = g_strdup(PACKAGE " v" VERSION);
2661 else
2662 (*dst_tag)->value = g_strdup(src_tag->value);
2663
2664 dst_tag = &(*dst_tag)->next;
2665 src_tag = src_tag->next;
2666 }
2667
2668 return new_tags;
2669 }
2670
2671 /* return plain text of type */
2672 char *osm_type_string(type_t type) {
2673 const struct { type_t type; char *name; } types[] = {
2674 { ILLEGAL, "illegal" },
2675 { NODE, "node" },
2676 { WAY, "way" },
2677 { RELATION, "relation" },
2678 { NODE_ID, "node id" },
2679 { WAY_ID, "way id" },
2680 { RELATION_ID, "relation id" },
2681 { 0, NULL }
2682 };
2683
2684 int i;
2685 for(i=0;types[i].name;i++)
2686 if(type == types[i].type)
2687 return types[i].name;
2688
2689 return NULL;
2690 }
2691
2692 char *osm_object_string(type_t type, void *object) {
2693 char *type_str = osm_type_string(type);
2694
2695 if(!object)
2696 return g_strdup_printf("%s #<invalid>", type_str);
2697
2698 switch(type) {
2699 case ILLEGAL:
2700 return g_strdup_printf("%s #<unspec>", type_str);
2701 break;
2702 case NODE:
2703 return g_strdup_printf("%s #%ld", type_str, ((node_t*)object)->id);
2704 break;
2705 case WAY:
2706 return g_strdup_printf("%s #%ld", type_str, ((way_t*)object)->id);
2707 break;
2708 case RELATION:
2709 return g_strdup_printf("%s #%ld", type_str, ((relation_t*)object)->id);
2710 break;
2711 case NODE_ID:
2712 case WAY_ID:
2713 case RELATION_ID:
2714 return g_strdup_printf("%s #%ld", type_str, *((item_id_t*)object));
2715 break;
2716 }
2717 return NULL;
2718 }
2719
2720 // vim:et:ts=8:sw=2:sts=2:ai