Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


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