Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations)
Tue Dec 9 20:06:06 2008 UTC (15 years, 5 months ago) by harbaum
Original Path: src/osm.c
File MIME type: text/plain
File size: 40675 byte(s)
Initial import
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 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <math.h>
24
25 #define __USE_XOPEN
26 #include <time.h>
27
28 #include <libxml/parser.h>
29 #include <libxml/tree.h>
30
31 #include "appdata.h"
32
33 #ifndef LIBXML_TREE_ENABLED
34 #error "Tree not enabled in libxml"
35 #endif
36
37 /* determine where a node/way/relation read from the osm file */
38 /* is inserted into the internal database */
39 // #define OSM_SORT_ID
40 #define OSM_SORT_LAST
41 // #define OSM_SORT_FIRST
42
43 /* ------------------------- user handling --------------------- */
44
45 static void osm_bounds_free(bounds_t *bounds) {
46 free(bounds);
47 }
48
49 static void osm_bounds_dump(bounds_t *bounds) {
50 printf("\nBounds: %f->%f %f->%f\n",
51 bounds->ll_min.lat, bounds->ll_max.lat,
52 bounds->ll_min.lon, bounds->ll_max.lon);
53 }
54
55 static bounds_t *osm_parse_osm_bounds(osm_t *osm,
56 xmlDocPtr doc, xmlNode *a_node) {
57 char *prop;
58
59 if(osm->bounds) {
60 errorf(NULL, "Doubly defined bounds");
61 return NULL;
62 }
63
64 bounds_t *bounds = g_new0(bounds_t, 1);
65
66 bounds->ll_min.lat = bounds->ll_min.lon = NAN;
67 bounds->ll_max.lat = bounds->ll_max.lon = NAN;
68
69 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"minlat"))) {
70 bounds->ll_min.lat = g_ascii_strtod(prop, NULL);
71 xmlFree(prop);
72 }
73
74 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"maxlat"))) {
75 bounds->ll_max.lat = g_ascii_strtod(prop, NULL);
76 xmlFree(prop);
77 }
78
79 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"minlon"))) {
80 bounds->ll_min.lon = g_ascii_strtod(prop, NULL);
81 xmlFree(prop);
82 }
83
84 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"maxlon"))) {
85 bounds->ll_max.lon = g_ascii_strtod(prop, NULL);
86 xmlFree(prop);
87 }
88
89 if(isnan(bounds->ll_min.lat) || isnan(bounds->ll_min.lon) ||
90 isnan(bounds->ll_max.lat) || isnan(bounds->ll_max.lon)) {
91 errorf(NULL, "Invalid coordinate in bounds (%f/%f/%f/%f)",
92 bounds->ll_min.lat, bounds->ll_min.lon,
93 bounds->ll_max.lat, bounds->ll_max.lon);
94
95 g_free(bounds);
96 return NULL;
97 }
98
99
100 /* calculate map zone which will be used as a reference for all */
101 /* drawing/projection later on */
102 pos_t center = { (bounds->ll_max.lat + bounds->ll_min.lat)/2,
103 (bounds->ll_max.lon + bounds->ll_min.lon)/2 };
104
105 pos2lpos_center(&center, &bounds->center);
106
107 /* the scale is needed to accomodate for "streching" */
108 /* by the mercartor projection */
109 bounds->scale = cos(DEG2RAD(center.lat));
110
111 pos2lpos_center(&bounds->ll_min, &bounds->min);
112 bounds->min.x -= bounds->center.x;
113 bounds->min.y -= bounds->center.y;
114 bounds->min.x *= bounds->scale;
115 bounds->min.y *= bounds->scale;
116
117 pos2lpos_center(&bounds->ll_max, &bounds->max);
118 bounds->max.x -= bounds->center.x;
119 bounds->max.y -= bounds->center.y;
120 bounds->max.x *= bounds->scale;
121 bounds->max.y *= bounds->scale;
122
123 return bounds;
124 }
125
126 /* ------------------------- user handling --------------------- */
127
128 void osm_users_free(user_t *user) {
129 while(user) {
130 user_t *next = user->next;
131
132 if(user->name) g_free(user->name);
133 g_free(user);
134
135 user = next;
136 }
137 }
138
139 void osm_users_dump(user_t *user) {
140 printf("\nUser list:\n");
141 while(user) {
142 printf("Name: %s\n", user->name);
143 user = user->next;
144 }
145 }
146
147 static user_t *osm_user(osm_t *osm, char *name) {
148
149 /* search through user list */
150 user_t **user = &osm->user;
151 while(*user && strcasecmp((*user)->name, name) < 0)
152 user = &(*user)->next;
153
154 /* end of list or inexact match? create new user entry! */
155 if(!*user || strcasecmp((*user)->name, name)) {
156 user_t *new = g_new0(user_t, 1);
157 new->name = g_strdup(name);
158 new->next = *user;
159 *user = new;
160
161 return new;
162 }
163
164 return *user;
165 }
166
167 static
168 time_t convert_iso8601(const char *str) {
169 tzset();
170
171 struct tm ctime;
172 memset(&ctime, 0, sizeof(struct tm));
173 strptime(str, "%FT%T%z", &ctime);
174
175 return mktime(&ctime) - timezone;
176 }
177
178 /* -------------------- tag handling ----------------------- */
179
180 void osm_tag_free(tag_t *tag) {
181 if(tag->key) g_free(tag->key);
182 if(tag->value) g_free(tag->value);
183 g_free(tag);
184 }
185
186 void osm_tags_free(tag_t *tag) {
187 while(tag) {
188 tag_t *next = tag->next;
189 osm_tag_free(tag);
190 tag = next;
191 }
192 }
193
194 static void osm_tags_dump(tag_t *tag) {
195 while(tag) {
196 printf("Key/Val: %s/%s\n", tag->key, tag->value);
197 tag = tag->next;
198 }
199 }
200
201 tag_t *osm_parse_osm_tag(osm_t *osm, xmlDocPtr doc, xmlNode *a_node) {
202 xmlNode *cur_node = NULL;
203
204 /* allocate a new tag structure */
205 tag_t *tag = g_new0(tag_t, 1);
206
207 char *prop;
208 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"k"))) {
209 if(strlen(prop) > 0) tag->key = g_strdup(prop);
210 xmlFree(prop);
211 }
212
213 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"v"))) {
214 if(strlen(prop) > 0) tag->value = g_strdup(prop);
215 xmlFree(prop);
216 }
217
218 if(!tag->key || !tag->value) {
219 printf("incomplete tag key/value %s/%s\n", tag->key, tag->value);
220 osm_tags_free(tag);
221 return NULL;
222 }
223
224 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next)
225 if (cur_node->type == XML_ELEMENT_NODE)
226 printf("found unhandled osm/node/tag/%s\n", cur_node->name);
227
228 return tag;
229 }
230
231 gboolean osm_is_creator_tag(tag_t *tag) {
232 if(strcasecmp(tag->key, "created_by") == 0) return TRUE;
233 if(strcasecmp(tag->key, "source") == 0) return TRUE;
234
235 return FALSE;
236 }
237
238 gboolean osm_tag_key_and_value_present(tag_t *haystack, tag_t *tag) {
239 while(haystack) {
240 if((strcasecmp(haystack->key, tag->key) == 0) &&
241 (strcasecmp(haystack->value, tag->value) == 0))
242 return TRUE;
243
244 haystack = haystack->next;
245 }
246 return FALSE;
247 }
248
249 gboolean osm_tag_key_other_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_way_ends_with_node(way_t *way, node_t *node) {
261 /* and deleted way may even not contain any nodes at all */
262 /* so ignore it */
263 if(way->flags & OSM_FLAG_DELETED)
264 return FALSE;
265
266 /* any valid way must have at least two nodes */
267 g_assert(way->node_chain && way->node_chain->next);
268
269 node_chain_t *chain = way->node_chain;
270 if(chain->node == node) return TRUE;
271
272 while(chain->next) chain = chain->next;
273 if(chain->node == node) return TRUE;
274
275 return FALSE;
276 }
277
278 /* ------------------- node handling ------------------- */
279
280 void osm_node_free(icon_t **icon, node_t *node) {
281 if(node->icon_buf)
282 icon_free(icon, node->icon_buf);
283
284 /* there must not be anything left in this chain */
285 g_assert(!node->map_item_chain);
286
287 osm_tags_free(node->tag);
288 g_free(node);
289 }
290
291 static void osm_nodes_free(icon_t **icon, node_t *node) {
292 while(node) {
293 node_t *next = node->next;
294 osm_node_free(icon, node);
295 node = next;
296 }
297 }
298
299 void osm_node_dump(node_t *node) {
300 char buf[64];
301 struct tm tm;
302
303 printf("Id: %lu\n", node->id);
304 printf("User: %s\n", node->user?node->user->name:"<unspecified>");
305 printf("Visible: %s\n", node->visible?"yes":"no");
306
307 localtime_r(&node->time, &tm);
308 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", &tm);
309 printf("Time: %s\n", buf);
310 osm_tags_dump(node->tag);
311 }
312
313 void osm_nodes_dump(node_t *node) {
314 printf("\nNode list:\n");
315 while(node) {
316 osm_node_dump(node);
317 printf("\n");
318 node = node->next;
319 }
320 }
321
322 static node_t *osm_parse_osm_node(osm_t *osm,
323 xmlDocPtr doc, xmlNode *a_node) {
324 xmlNode *cur_node = NULL;
325
326 /* allocate a new node structure */
327 node_t *node = g_new0(node_t, 1);
328 node->pos.lat = node->pos.lon = NAN;
329
330 char *prop;
331 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"id"))) {
332 node->id = strtoul(prop, NULL, 10);
333 xmlFree(prop);
334 }
335
336 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"lat"))) {
337 node->pos.lat = g_ascii_strtod(prop, NULL);
338 xmlFree(prop);
339 }
340
341 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"lon"))) {
342 node->pos.lon = g_ascii_strtod(prop, NULL);
343 xmlFree(prop);
344 }
345
346 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"user"))) {
347 node->user = osm_user(osm, prop);
348 xmlFree(prop);
349 }
350
351 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"visible"))) {
352 node->visible = (strcasecmp(prop, "true") == 0);
353 xmlFree(prop);
354 }
355
356 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"timestamp"))) {
357 node->time = convert_iso8601(prop);
358 xmlFree(prop);
359 }
360
361 /* scan for tags and attach a list of tags */
362 tag_t **tag = &node->tag;
363 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
364 if (cur_node->type == XML_ELEMENT_NODE) {
365 if(strcasecmp((char*)cur_node->name, "tag") == 0) {
366 /* attach tag to node */
367 *tag = osm_parse_osm_tag(osm, doc, cur_node);
368 if(*tag) tag = &((*tag)->next);
369 } else
370 printf("found unhandled osm/node/%s\n", cur_node->name);
371 }
372 }
373
374 pos2lpos(osm->bounds, &node->pos, &node->lpos);
375
376 return node;
377 }
378
379 /* ------------------- way handling ------------------- */
380
381 void osm_node_chain_free(node_chain_t *node_chain) {
382 while(node_chain) {
383 g_assert(node_chain->node->ways);
384
385 node_chain_t *next = node_chain->next;
386 node_chain->node->ways--;
387 g_free(node_chain);
388 node_chain = next;
389 }
390 }
391
392 void osm_way_free(way_t *way) {
393 // printf("freeing way #%ld\n", way->id);
394
395 osm_node_chain_free(way->node_chain);
396 osm_tags_free(way->tag);
397
398 /* there must not be anything left in this chain */
399 g_assert(!way->map_item_chain);
400
401 g_free(way);
402 }
403
404 static void osm_ways_free(way_t *way) {
405 while(way) {
406 way_t *next = way->next;
407 osm_way_free(way);
408 way = next;
409 }
410 }
411
412 void osm_way_append_node(way_t *way, node_t *node) {
413 node_chain_t **node_chain = &way->node_chain;
414
415 while(*node_chain)
416 node_chain = &((*node_chain)->next);
417
418 *node_chain = g_new0(node_chain_t, 1);
419 (*node_chain)->node = node;
420
421 node->ways++;
422 }
423
424 int osm_node_chain_length(node_chain_t *node_chain) {
425 int cnt = 0;
426 while(node_chain) {
427 cnt++;
428 node_chain = node_chain->next;
429 }
430
431 return cnt;
432 }
433
434 void osm_way_dump(way_t *way) {
435 char buf[64];
436 struct tm tm;
437
438 printf("Id: %lu\n", way->id);
439 printf("User: %s\n", way->user?way->user->name:"<unspecified>");
440 printf("Visible: %s\n", way->visible?"yes":"no");
441 node_chain_t *node_chain = way->node_chain;
442 while(node_chain) {
443 printf(" Node: %lu\n", node_chain->node->id);
444 node_chain = node_chain->next;
445 }
446
447 localtime_r(&way->time, &tm);
448 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", &tm);
449 printf("Time: %s\n", buf);
450 osm_tags_dump(way->tag);
451 }
452
453 void osm_ways_dump(way_t *way) {
454 printf("\nWay list:\n");
455 while(way) {
456 osm_way_dump(way);
457 printf("\n");
458 way = way->next;
459 }
460 }
461
462 node_chain_t *osm_parse_osm_way_nd(osm_t *osm,
463 xmlDocPtr doc, xmlNode *a_node) {
464 char *prop;
465
466 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"ref"))) {
467 item_id_t id = strtoul(prop, NULL, 10);
468 node_chain_t *node_chain = g_new0(node_chain_t, 1);
469
470 /* search matching node */
471 node_chain->node = osm->node;
472 while(node_chain->node && node_chain->node->id != id)
473 node_chain->node = node_chain->node->next;
474
475 if(!node_chain->node) printf("Node id %lu not found\n", id);
476
477 if(node_chain->node)
478 node_chain->node->ways++;
479
480 xmlFree(prop);
481
482 return node_chain;
483 }
484
485 return NULL;
486 }
487
488 static way_t *osm_parse_osm_way(osm_t *osm,
489 xmlDocPtr doc, xmlNode *a_node) {
490 xmlNode *cur_node = NULL;
491
492 /* allocate a new way structure */
493 way_t *way = g_new0(way_t, 1);
494
495 char *prop;
496 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"id"))) {
497 way->id = strtoul(prop, NULL, 10);
498 xmlFree(prop);
499 }
500
501 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"user"))) {
502 way->user = osm_user(osm, prop);
503 xmlFree(prop);
504 }
505
506 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"visible"))) {
507 way->visible = (strcasecmp(prop, "true") == 0);
508 xmlFree(prop);
509 }
510
511 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"timestamp"))) {
512 way->time = convert_iso8601(prop);
513 xmlFree(prop);
514 }
515
516 /* scan for tags/nodes and attach their lists */
517 tag_t **tag = &way->tag;
518 node_chain_t **node_chain = &way->node_chain;
519
520 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
521 if (cur_node->type == XML_ELEMENT_NODE) {
522 if(strcasecmp((char*)cur_node->name, "tag") == 0) {
523 /* attach tag to node */
524 *tag = osm_parse_osm_tag(osm, doc, cur_node);
525 if(*tag) tag = &((*tag)->next);
526 } else if(strcasecmp((char*)cur_node->name, "nd") == 0) {
527 *node_chain = osm_parse_osm_way_nd(osm, doc, cur_node);
528 if(*node_chain)
529 node_chain = &((*node_chain)->next);
530 } else
531 printf("found unhandled osm/node/%s\n", cur_node->name);
532 }
533 }
534
535 return way;
536 }
537
538 /* ------------------- relation handling ------------------- */
539
540 void osm_member_free(member_t *member) {
541 if(member->role) g_free(member->role);
542 g_free(member);
543 }
544
545 void osm_members_free(member_t *member) {
546 while(member) {
547 member_t *next = member->next;
548 osm_member_free(member);
549 member = next;
550 }
551 }
552
553 static void osm_relations_free(relation_t *relation) {
554 while(relation) {
555 relation_t *next = relation->next;
556
557 osm_tags_free(relation->tag);
558 osm_members_free(relation->member);
559
560 g_free(relation);
561 relation = next;
562 }
563 }
564
565 void osm_relations_dump(relation_t *relation) {
566 printf("\nRelation list:\n");
567 while(relation) {
568 char buf[64];
569 struct tm tm;
570
571 printf("Id: %lu\n", relation->id);
572 printf("User: %s\n",
573 relation->user?relation->user->name:"<unspecified>");
574 printf("Visible: %s\n", relation->visible?"yes":"no");
575
576 member_t *member = relation->member;
577 while(member) {
578 switch(member->type) {
579 case ILLEGAL:
580 case NODE_ID:
581 case WAY_ID:
582 case RELATION_ID:
583 break;
584
585 case NODE:
586 if(member->node)
587 printf(" Member: Node, id = %lu, role = %s\n",
588 member->node->id, member->role);
589 break;
590
591 case WAY:
592 if(member->way)
593 printf(" Member: Way, id = %lu, role = %s\n",
594 member->way->id, member->role);
595 break;
596
597 case RELATION:
598 if(member->relation)
599 printf(" Member: Relation, id = %lu, role = %s\n",
600 member->relation->id, member->role);
601 break;
602 }
603
604 member = member->next;
605 }
606
607 localtime_r(&relation->time, &tm);
608 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", &tm);
609 printf("Time: %s\n", buf);
610 osm_tags_dump(relation->tag);
611
612 printf("\n");
613 relation = relation->next;
614 }
615 }
616
617 member_t *osm_parse_osm_relation_member(osm_t *osm,
618 xmlDocPtr doc, xmlNode *a_node) {
619 char *prop;
620 member_t *member = g_new0(member_t, 1);
621 member->type = ILLEGAL;
622
623 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"type"))) {
624 if(strcasecmp(prop, "way") == 0) member->type = WAY;
625 else if(strcasecmp(prop, "node") == 0) member->type = NODE;
626 else if(strcasecmp(prop, "relation") == 0) member->type = RELATION;
627 xmlFree(prop);
628 }
629
630 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"ref"))) {
631 item_id_t id = strtoul(prop, NULL, 10);
632
633 switch(member->type) {
634 case ILLEGAL:
635 printf("Unable to store illegal type\n");
636 break;
637
638 case WAY:
639 /* search matching way */
640 member->way = osm->way;
641 while(member->way && member->way->id != id)
642 member->way = member->way->next;
643
644 if(!member->way) {
645 member->type = WAY_ID;
646 member->id = id;
647 }
648 break;
649
650 case NODE:
651 /* search matching node */
652 member->node = osm->node;
653 while(member->node && member->node->id != id)
654 member->node = member->node->next;
655
656 if(!member->node) {
657 member->type = NODE_ID;
658 member->id = id;
659 }
660 break;
661
662 case RELATION:
663 /* search matching relation */
664 member->relation = osm->relation;
665 while(member->relation && member->relation->id != id)
666 member->relation = member->relation->next;
667
668 if(!member->relation) {
669 member->type = NODE_ID;
670 member->id = id;
671 }
672 break;
673
674 case WAY_ID:
675 case NODE_ID:
676 case RELATION_ID:
677 break;
678 }
679
680 xmlFree(prop);
681 }
682
683 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"role"))) {
684 if(strlen(prop) > 0) member->role = g_strdup(prop);
685 xmlFree(prop);
686 }
687
688 return member;
689 }
690
691 static relation_t *osm_parse_osm_relation(osm_t *osm,
692 xmlDocPtr doc, xmlNode *a_node) {
693 xmlNode *cur_node = NULL;
694
695 /* allocate a new relation structure */
696 relation_t *relation = g_new0(relation_t, 1);
697
698 char *prop;
699 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"id"))) {
700 relation->id = strtoul(prop, NULL, 10);
701 xmlFree(prop);
702 }
703
704 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"user"))) {
705 relation->user = osm_user(osm, prop);
706 xmlFree(prop);
707 }
708
709 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"visible"))) {
710 relation->visible = (strcasecmp(prop, "true") == 0);
711 xmlFree(prop);
712 }
713
714 if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"timestamp"))) {
715 relation->time = convert_iso8601(prop);
716 xmlFree(prop);
717 }
718
719 /* scan for tags and attach a list of tags */
720 tag_t **tag = &relation->tag;
721 member_t **member = &relation->member;
722
723 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
724 if (cur_node->type == XML_ELEMENT_NODE) {
725 if(strcasecmp((char*)cur_node->name, "tag") == 0) {
726 /* attach tag to node */
727 *tag = osm_parse_osm_tag(osm, doc, cur_node);
728 if(*tag) tag = &((*tag)->next);
729 } else if(strcasecmp((char*)cur_node->name, "member") == 0) {
730 *member = osm_parse_osm_relation_member(osm, doc, cur_node);
731 if(*member) member = &((*member)->next);
732 } else
733 printf("found unhandled osm/node/%s\n", cur_node->name);
734 }
735 }
736
737 return relation;
738 }
739
740 /* ----------------------- generic xml handling -------------------------- */
741
742 /* parse loc entry */
743 static void osm_parse_osm(osm_t *osm, xmlDocPtr doc, xmlNode * a_node) {
744 xmlNode *cur_node = NULL;
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, "bounds") == 0)
749 osm->bounds = osm_parse_osm_bounds(osm, doc, cur_node);
750 else if(strcasecmp((char*)cur_node->name, "node") == 0) {
751 /* parse node and attach it to chain */
752 node_t *new = osm_parse_osm_node(osm, doc, cur_node);
753 if(new) {
754 node_t **node = &osm->node;
755
756 #ifdef OSM_SORT_ID
757 /* search chain of nodes */
758 while(*node && ((*node)->id < new->id))
759 node = &(*node)->next;
760 #endif
761
762 #ifdef OSM_SORT_LAST
763 while(*node) node = &(*node)->next;
764 #endif
765
766 /* insert into chain */
767 new->next = *node;
768 *node = new;
769 }
770 } else if(strcasecmp((char*)cur_node->name, "way") == 0) {
771 /* parse way and attach it to chain */
772 way_t *new = osm_parse_osm_way(osm, doc, cur_node);
773 if(new) {
774 way_t **way = &osm->way;
775
776 #ifdef OSM_SORT_ID
777 /* insert into chain */
778 while(*way && ((*way)->id < new->id))
779 way = &(*way)->next;
780 #endif
781
782 #ifdef OSM_SORT_LAST
783 while(*way) way = &(*way)->next;
784 #endif
785
786 /* insert into chain */
787 new->next = *way;
788 *way = new;
789 }
790 } else if(strcasecmp((char*)cur_node->name, "relation") == 0) {
791 /* parse relation and attach it to chain */
792 relation_t *new = osm_parse_osm_relation(osm, doc, cur_node);
793 if(new) {
794 relation_t **relation = &osm->relation;
795
796 #ifdef OSM_SORT_ID
797 /* search chain of ways */
798 while(*relation && ((*relation)->id < new->id))
799 relation = &(*relation)->next;
800 #endif
801
802 #ifdef OSM_SORT_LAST
803 while(*relation) relation = &(*relation)->next;
804 #endif
805
806 /* insert into chain */
807 new->next = *relation;
808 *relation = new;
809 }
810 } else
811 printf("found unhandled osm/%s\n", cur_node->name);
812
813 }
814 }
815 }
816
817 /* parse root element and search for "osm" */
818 static osm_t *osm_parse_root(xmlDocPtr doc, xmlNode * a_node) {
819 osm_t *osm;
820 xmlNode *cur_node = NULL;
821
822 /* allocate memory to hold osm file description */
823 osm = g_new0(osm_t, 1);
824
825 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
826 if (cur_node->type == XML_ELEMENT_NODE) {
827 /* parse osm osm file ... */
828 if(strcasecmp((char*)cur_node->name, "osm") == 0)
829 osm_parse_osm(osm, doc, cur_node);
830 else
831 printf("found unhandled %s\n", cur_node->name);
832 }
833 }
834
835 return osm;
836 }
837
838 static osm_t *osm_parse_doc(xmlDocPtr doc) {
839 osm_t *osm;
840
841 /* Get the root element node */
842 xmlNode *root_element = xmlDocGetRootElement(doc);
843
844 osm = osm_parse_root(doc, root_element);
845
846 /*free the document */
847 xmlFreeDoc(doc);
848
849 /*
850 * Free the global variables that may
851 * have been allocated by the parser.
852 */
853 xmlCleanupParser();
854
855 return osm;
856 }
857
858 /* ------------------ osm handling ----------------- */
859
860 void osm_free(icon_t **icon, osm_t *osm) {
861 if(!osm) return;
862
863 if(osm->bounds) osm_bounds_free(osm->bounds);
864 if(osm->user) osm_users_free(osm->user);
865 if(osm->way) osm_ways_free(osm->way);
866 if(osm->node) osm_nodes_free(icon, osm->node);
867 if(osm->relation) osm_relations_free(osm->relation);
868 g_free(osm);
869 }
870
871 void osm_dump(osm_t *osm) {
872 osm_bounds_dump(osm->bounds);
873 osm_users_dump(osm->user);
874 osm_nodes_dump(osm->node);
875 osm_ways_dump(osm->way);
876 osm_relations_dump(osm->relation);
877 }
878
879 osm_t *osm_parse(char *filename) {
880 xmlDoc *doc = NULL;
881
882 LIBXML_TEST_VERSION;
883
884 /* parse the file and get the DOM */
885 if ((doc = xmlReadFile(filename, NULL, 0)) == NULL) {
886 xmlErrorPtr errP = xmlGetLastError();
887 errorf(NULL, "While parsing \"%s\":\n\n%s", filename, errP->message);
888 return NULL;
889 }
890
891 return osm_parse_doc(doc);
892 }
893
894 gboolean osm_sanity_check(GtkWidget *parent, osm_t *osm) {
895 if(!osm->bounds) {
896 errorf(parent, _("Ivalid data in OSM file:\n"
897 "Boundary box missing!"));
898 return FALSE;
899 }
900 if(!osm->node) {
901 errorf(parent, _("Ivalid data in OSM file:\n"
902 "No drawable content found!"));
903 return FALSE;
904 }
905 return TRUE;
906 }
907
908 /* ------------------------- misc access functions -------------- */
909
910 char *osm_tag_get_by_key(tag_t *tag, char *key) {
911 if(!tag || !key) return NULL;
912
913 while(tag) {
914 if(strcasecmp(tag->key, key) == 0)
915 return tag->value;
916
917 tag = tag->next;
918 }
919
920 return NULL;
921 }
922
923 char *osm_way_get_value(way_t *way, char *key) {
924 tag_t *tag = way->tag;
925
926 while(tag) {
927 if(strcasecmp(tag->key, key) == 0)
928 return tag->value;
929
930 tag = tag->next;
931 }
932
933 return NULL;
934 }
935
936 char *osm_node_get_value(node_t *node, char *key) {
937 tag_t *tag = node->tag;
938
939 while(tag) {
940 if(strcasecmp(tag->key, key) == 0)
941 return tag->value;
942
943 tag = tag->next;
944 }
945
946 return NULL;
947 }
948
949 gboolean osm_way_has_value(way_t *way, char *str) {
950 tag_t *tag = way->tag;
951
952 while(tag) {
953 if(tag->value && strcasecmp(tag->value, str) == 0)
954 return TRUE;
955
956 tag = tag->next;
957 }
958 return FALSE;
959 }
960
961 gboolean osm_node_has_value(node_t *node, char *str) {
962 tag_t *tag = node->tag;
963
964 while(tag) {
965 if(tag->value && strcasecmp(tag->value, str) == 0)
966 return TRUE;
967
968 tag = tag->next;
969 }
970 return FALSE;
971 }
972
973 gboolean osm_node_has_tag(node_t *node) {
974 tag_t *tag = node->tag;
975
976 if(tag && strcasecmp(tag->key, "created_by") == 0)
977 tag = tag->next;
978
979 return tag != NULL;
980 }
981
982 /* return true if node is part of way */
983 gboolean osm_node_in_way(way_t *way, node_t *node) {
984 node_chain_t *node_chain = way->node_chain;
985 while(node_chain) {
986 if(node_chain->node == node)
987 return TRUE;
988
989 node_chain = node_chain->next;
990 }
991 return FALSE;
992 }
993
994 static void osm_generate_tags(tag_t *tag, xmlNodePtr node) {
995 while(tag) {
996 /* make sure "created_by" tag contains our id */
997 if(strcasecmp(tag->key, "created_by") == 0) {
998 g_free(tag->value);
999 tag->value = g_strdup(PACKAGE " v" VERSION);
1000 }
1001
1002 xmlNodePtr tag_node = xmlNewChild(node, NULL, BAD_CAST "tag", NULL);
1003 xmlNewProp(tag_node, BAD_CAST "k", BAD_CAST tag->key);
1004 xmlNewProp(tag_node, BAD_CAST "v", BAD_CAST tag->value);
1005 tag = tag->next;
1006 }
1007 }
1008
1009 /* build xml representation for a way */
1010 char *osm_generate_xml(osm_t *osm, type_t type, void *item) {
1011 char str[32];
1012 xmlChar *result = NULL;
1013 int len = 0;
1014
1015 LIBXML_TEST_VERSION;
1016
1017 xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
1018 xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "osm");
1019 xmlNewProp(root_node, BAD_CAST "version", BAD_CAST "0.5");
1020 xmlNewProp(root_node, BAD_CAST "generator", BAD_CAST PACKAGE " V" VERSION);
1021 xmlDocSetRootElement(doc, root_node);
1022
1023 switch(type) {
1024 case NODE:
1025 {
1026 node_t *node = (node_t*)item;
1027 xmlNodePtr node_node = xmlNewChild(root_node, NULL,
1028 BAD_CAST "node", NULL);
1029 /* new nodes don't have an id, but get one after the upload */
1030 if(!(node->flags & OSM_FLAG_NEW)) {
1031 snprintf(str, sizeof(str), "%u", (unsigned)node->id);
1032 xmlNewProp(node_node, BAD_CAST "id", BAD_CAST str);
1033 }
1034 g_ascii_dtostr(str, sizeof(str), node->pos.lat);
1035 xmlNewProp(node_node, BAD_CAST "lat", BAD_CAST str);
1036 g_ascii_dtostr(str, sizeof(str), node->pos.lon);
1037 xmlNewProp(node_node, BAD_CAST "lon", BAD_CAST str);
1038 osm_generate_tags(node->tag, node_node);
1039 }
1040 break;
1041
1042 case WAY:
1043 {
1044 way_t *way = (way_t*)item;
1045 xmlNodePtr way_node = xmlNewChild(root_node, NULL, BAD_CAST "way", NULL);
1046 snprintf(str, sizeof(str), "%u", (unsigned)way->id);
1047 xmlNewProp(way_node, BAD_CAST "id", BAD_CAST str);
1048
1049 node_chain_t *node_chain = way->node_chain;
1050 while(node_chain) {
1051 xmlNodePtr nd_node = xmlNewChild(way_node, NULL, BAD_CAST "nd", NULL);
1052 char *str = g_strdup_printf("%ld", node_chain->node->id);
1053 xmlNewProp(nd_node, BAD_CAST "ref", BAD_CAST str);
1054 g_free(str);
1055 node_chain = node_chain->next;
1056 }
1057
1058 osm_generate_tags(way->tag, way_node);
1059 }
1060 break;
1061
1062 case RELATION:
1063 {
1064 relation_t *relation = (relation_t*)item;
1065 xmlNodePtr rel_node = xmlNewChild(root_node, NULL,
1066 BAD_CAST "relation", NULL);
1067 snprintf(str, sizeof(str), "%u", (unsigned)relation->id);
1068 xmlNewProp(rel_node, BAD_CAST "id", BAD_CAST str);
1069
1070 member_t *member = relation->member;
1071 while(member) {
1072 xmlNodePtr m_node = xmlNewChild(rel_node,NULL,BAD_CAST "member", NULL);
1073 char *str = NULL;
1074
1075 switch(member->type) {
1076 case NODE:
1077 xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "node");
1078 str = g_strdup_printf("%ld", member->node->id);
1079 break;
1080
1081 case WAY:
1082 xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "way");
1083 str = g_strdup_printf("%ld", member->way->id);
1084 break;
1085
1086 case RELATION:
1087 xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "relation");
1088 str = g_strdup_printf("%ld", member->relation->id);
1089 break;
1090
1091 default:
1092 break;
1093 }
1094
1095 if(str) {
1096 xmlNewProp(m_node, BAD_CAST "ref", BAD_CAST str);
1097 g_free(str);
1098 }
1099
1100 if(member->role)
1101 xmlNewProp(m_node, BAD_CAST "role", BAD_CAST member->role);
1102 else
1103 xmlNewProp(m_node, BAD_CAST "role", BAD_CAST "");
1104
1105 member = member->next;
1106 }
1107 osm_generate_tags(relation->tag, rel_node);
1108 }
1109 break;
1110
1111 default:
1112 printf("neither NODE nor WAY nor RELATION\n");
1113 g_assert(0);
1114 break;
1115 }
1116
1117 xmlDocDumpFormatMemoryEnc(doc, &result, &len, "UTF-8", 1);
1118 xmlFreeDoc(doc);
1119 xmlCleanupParser();
1120
1121 // puts("xml encoding result:");
1122 // puts((char*)result);
1123
1124 return (char*)result;
1125 }
1126
1127 /* build xml representation for a node */
1128 char *osm_generate_xml_node(osm_t *osm, node_t *node) {
1129 return osm_generate_xml(osm, NODE, node);
1130 }
1131
1132 /* build xml representation for a way */
1133 char *osm_generate_xml_way(osm_t *osm, way_t *way) {
1134 return osm_generate_xml(osm, WAY, way);
1135 }
1136
1137 /* build xml representation for a relation */
1138 char *osm_generate_xml_relation(osm_t *osm, relation_t *relation) {
1139 return osm_generate_xml(osm, RELATION, relation);
1140 }
1141
1142 node_t *osm_get_node_by_id(osm_t *osm, item_id_t id) {
1143 node_t *node = osm->node;
1144 while(node) {
1145 if(node->id == id)
1146 return node;
1147
1148 node = node->next;
1149 }
1150 return NULL;
1151 }
1152
1153 way_t *osm_get_way_by_id(osm_t *osm, item_id_t id) {
1154 way_t *way = osm->way;
1155 while(way) {
1156 if(way->id == id)
1157 return way;
1158
1159 way = way->next;
1160 }
1161 return NULL;
1162 }
1163
1164 relation_t *osm_get_relation_by_id(osm_t *osm, item_id_t id) {
1165 relation_t *relation = osm->relation;
1166 while(relation) {
1167 if(relation->id == id)
1168 return relation;
1169
1170 relation = relation->next;
1171 }
1172
1173 return NULL;
1174 }
1175
1176 /* ---------- edit functions ------------- */
1177
1178 item_id_t osm_new_way_id(osm_t *osm) {
1179 item_id_t id = -1;
1180
1181 while(TRUE) {
1182 gboolean found = FALSE;
1183 way_t *way = osm->way;
1184 while(way) {
1185 if(way->id == id)
1186 found = TRUE;
1187
1188 way = way->next;
1189 }
1190
1191 /* no such id so far -> use it */
1192 if(!found) return id;
1193
1194 id--;
1195 }
1196 g_assert(0);
1197 return 0;
1198 }
1199
1200 item_id_t osm_new_node_id(osm_t *osm) {
1201 item_id_t id = -1;
1202
1203 while(TRUE) {
1204 gboolean found = FALSE;
1205 node_t *node = osm->node;
1206 while(node) {
1207 if(node->id == id)
1208 found = TRUE;
1209
1210 node = node->next;
1211 }
1212
1213 /* no such id so far -> use it */
1214 if(!found) return id;
1215
1216 id--;
1217 }
1218 g_assert(0);
1219 return 0;
1220 }
1221
1222 node_t *osm_node_new(osm_t *osm, gint x, gint y) {
1223 printf("Creating new node\n");
1224
1225 node_t *node = g_new0(node_t, 1);
1226 node->lpos.x = x;
1227 node->lpos.y = y;
1228 node->visible = TRUE;
1229 node->time = time(NULL);
1230
1231 /* add created_by tag */
1232 node->tag = g_new0(tag_t, 1);
1233 node->tag->key = g_strdup("created_by");
1234 node->tag->value = g_strdup(PACKAGE " v" VERSION);
1235
1236 /* convert screen position back to ll */
1237 lpos2pos(osm->bounds, &node->lpos, &node->pos);
1238
1239 printf(" new at %d %d (%f %f)\n",
1240 node->lpos.x, node->lpos.y, node->pos.lat, node->pos.lon);
1241
1242 return node;
1243 }
1244
1245
1246 void osm_node_attach(osm_t *osm, node_t *node) {
1247 printf("Attaching node\n");
1248
1249 node->id = osm_new_node_id(osm);
1250 node->flags = OSM_FLAG_NEW;
1251
1252 /* attach to end of node list */
1253 node_t **lnode = &osm->node;
1254 while(*lnode) lnode = &(*lnode)->next;
1255 *lnode = node;
1256 }
1257
1258 way_t *osm_way_new(void) {
1259 printf("Creating new way\n");
1260
1261 way_t *way = g_new0(way_t, 1);
1262 way->visible = TRUE;
1263 way->flags = OSM_FLAG_NEW;
1264 way->time = time(NULL);
1265
1266 /* add created_by tag */
1267 way->tag = g_new0(tag_t, 1);
1268 way->tag->key = g_strdup("created_by");
1269 way->tag->value = g_strdup(PACKAGE " v" VERSION);
1270
1271 return way;
1272 }
1273
1274 void osm_way_attach(osm_t *osm, way_t *way) {
1275 printf("Attaching way\n");
1276
1277 way->id = osm_new_way_id(osm);
1278 way->flags = OSM_FLAG_NEW;
1279
1280 /* attach to end of way list */
1281 way_t **lway = &osm->way;
1282 while(*lway) lway = &(*lway)->next;
1283 *lway = way;
1284 }
1285
1286 /* returns pointer to chain of ways affected by this deletion */
1287 way_chain_t *osm_node_delete(osm_t *osm, icon_t **icon,
1288 node_t *node, gboolean permanently,
1289 gboolean affect_ways) {
1290 way_chain_t *way_chain = NULL, **cur_way_chain = &way_chain;
1291
1292 /* new nodes aren't stored on the server and are just deleted permanently */
1293 if(node->flags & OSM_FLAG_NEW) {
1294 printf("About to delete NEW node #%ld -> force permanent delete\n",
1295 node->id);
1296 permanently = TRUE;
1297 }
1298
1299 /* first remove node from all ways using it */
1300 way_t *way = osm->way;
1301 while(way) {
1302 node_chain_t **chain = &(way->node_chain);
1303 gboolean modified = FALSE;
1304 while(*chain) {
1305 /* remove node from chain */
1306 if(node == (*chain)->node) {
1307 modified = TRUE;
1308 if(affect_ways) {
1309 node_chain_t *next = (*chain)->next;
1310 g_free(*chain);
1311 *chain = next;
1312 } else
1313 chain = &((*chain)->next);
1314 } else
1315 chain = &((*chain)->next);
1316 }
1317
1318 if(modified) {
1319 way->flags |= OSM_FLAG_DIRTY;
1320
1321 /* and add the way to the list of affected ways */
1322 *cur_way_chain = g_new0(way_chain_t, 1);
1323 (*cur_way_chain)->way = way;
1324 cur_way_chain = &((*cur_way_chain)->next);
1325 }
1326
1327 way = way->next;
1328 }
1329
1330 if(!permanently) {
1331 printf("mark node #%ld as deleted\n", node->id);
1332 node->flags |= OSM_FLAG_DELETED;
1333 } else {
1334 printf("permanently delete node #%ld\n", node->id);
1335
1336 /* remove it from the chain */
1337 node_t **cnode = &osm->node;
1338 int found = 0;
1339
1340 while(*cnode) {
1341 if(*cnode == node) {
1342 found++;
1343 *cnode = (*cnode)->next;
1344
1345 osm_node_free(icon, node);
1346 } else
1347 cnode = &((*cnode)->next);
1348 }
1349 g_assert(found == 1);
1350 }
1351
1352 return way_chain;
1353 }
1354
1355 guint osm_way_number_of_nodes(way_t *way) {
1356 guint nodes = 0;
1357 node_chain_t *chain = way->node_chain;
1358 while(chain) {
1359 nodes++;
1360 chain = chain->next;
1361 }
1362 return nodes;
1363 }
1364
1365 /* return all relations a node is in */
1366 relation_chain_t *osm_node_to_relation(osm_t *osm, node_t *node) {
1367 relation_chain_t *rel_chain = NULL, **cur_rel_chain = &rel_chain;
1368
1369 relation_t *relation = osm->relation;
1370 while(relation) {
1371 gboolean is_member = FALSE;
1372
1373 member_t *member = relation->member;
1374 while(member) {
1375 switch(member->type) {
1376 case NODE:
1377 /* nodes are checked directly */
1378 if(member->node == node)
1379 is_member = TRUE;
1380 break;
1381
1382 case WAY: {
1383 /* ways have to be checked for the nodes they consist of */
1384 node_chain_t *chain = member->way->node_chain;
1385 while(chain && !is_member) {
1386 if(chain->node == node)
1387 is_member = TRUE;
1388
1389 chain = chain->next;
1390 }
1391 } break;
1392
1393 default:
1394 break;
1395 }
1396 member = member->next;
1397 }
1398
1399 /* node is a member of this relation, so move it to the member chain */
1400 if(is_member) {
1401 *cur_rel_chain = g_new0(relation_chain_t, 1);
1402 (*cur_rel_chain)->relation = relation;
1403 cur_rel_chain = &((*cur_rel_chain)->next);
1404 }
1405
1406 relation = relation->next;
1407 }
1408
1409 return rel_chain;
1410 }
1411
1412 /* return all relations a way is in */
1413 relation_chain_t *osm_way_to_relation(osm_t *osm, way_t *way) {
1414 relation_chain_t *rel_chain = NULL, **cur_rel_chain = &rel_chain;
1415
1416 relation_t *relation = osm->relation;
1417 while(relation) {
1418 gboolean is_member = FALSE;
1419
1420 member_t *member = relation->member;
1421 while(member) {
1422 switch(member->type) {
1423 case WAY: {
1424 /* ways can be check directly */
1425 if(member->way == way)
1426 is_member = TRUE;
1427 } break;
1428
1429 default:
1430 break;
1431 }
1432 member = member->next;
1433 }
1434
1435 /* way is a member of this relation, so move it to the member chain */
1436 if(is_member) {
1437 *cur_rel_chain = g_new0(relation_chain_t, 1);
1438 (*cur_rel_chain)->relation = relation;
1439 cur_rel_chain = &((*cur_rel_chain)->next);
1440 }
1441
1442 relation = relation->next;
1443 }
1444
1445 return rel_chain;
1446 }
1447
1448 /* return all ways a node is in */
1449 way_chain_t *osm_node_to_way(osm_t *osm, node_t *node) {
1450 way_chain_t *chain = NULL, **cur_chain = &chain;
1451
1452 way_t *way = osm->way;
1453 while(way) {
1454 gboolean is_member = FALSE;
1455
1456 node_chain_t *node_chain = way->node_chain;
1457 while(node_chain) {
1458 if(node_chain->node == node)
1459 is_member = TRUE;
1460
1461 node_chain = node_chain->next;
1462 }
1463
1464 /* node is a member of this relation, so move it to the member chain */
1465 if(is_member) {
1466 *cur_chain = g_new0(way_chain_t, 1);
1467 (*cur_chain)->way = way;
1468 cur_chain = &((*cur_chain)->next);
1469 }
1470
1471 way = way->next;
1472 }
1473
1474 return chain;
1475 }
1476
1477 gboolean osm_position_within_bounds(osm_t *osm, gint x, gint y) {
1478 if((x < osm->bounds->min.x) || (x > osm->bounds->max.x)) return FALSE;
1479 if((y < osm->bounds->min.y) || (y > osm->bounds->max.y)) return FALSE;
1480 return TRUE;
1481 }
1482
1483 /* remove the given node from all relations. used if the node is to */
1484 /* be deleted */
1485 void osm_node_remove_from_relation(osm_t *osm, node_t *node) {
1486 relation_t *relation = osm->relation;
1487 printf("removing node #%ld from all relations:\n", node->id);
1488
1489 while(relation) {
1490 member_t **member = &relation->member;
1491 while(*member) {
1492 if(((*member)->type == NODE) &&
1493 ((*member)->node == node)) {
1494
1495 printf(" from relation #%ld\n", relation->id);
1496
1497 member_t *cur = *member;
1498 *member = (*member)->next;
1499 osm_member_free(cur);
1500
1501 relation->flags |= OSM_FLAG_DIRTY;
1502 } else
1503 member = &(*member)->next;
1504 }
1505 relation = relation->next;
1506 }
1507 }
1508
1509 /* remove the given way from all relations */
1510 void osm_way_remove_from_relation(osm_t *osm, way_t *way) {
1511 relation_t *relation = osm->relation;
1512 printf("removing way #%ld from all relations:\n", way->id);
1513
1514 while(relation) {
1515 member_t **member = &relation->member;
1516 while(*member) {
1517 if(((*member)->type == WAY) &&
1518 ((*member)->way == way)) {
1519
1520 printf(" from relation #%ld\n", relation->id);
1521
1522 member_t *cur = *member;
1523 *member = (*member)->next;
1524 osm_member_free(cur);
1525
1526 relation->flags |= OSM_FLAG_DIRTY;
1527 } else
1528 member = &(*member)->next;
1529 }
1530 relation = relation->next;
1531 }
1532 }
1533
1534 void osm_way_delete(osm_t *osm, icon_t **icon,
1535 way_t *way, gboolean permanently) {
1536
1537 /* new ways aren't stored on the server and are just deleted permanently */
1538 if(way->flags & OSM_FLAG_NEW) {
1539 printf("About to delete NEW way #%ld -> force permanent delete\n",
1540 way->id);
1541 permanently = TRUE;
1542 }
1543
1544 /* delete all nodes that aren't in other use now */
1545 node_chain_t **chain = &way->node_chain;
1546 while(*chain) {
1547
1548 (*chain)->node->ways--;
1549 printf("checking node #%ld (still used by %d)\n",
1550 (*chain)->node->id, (*chain)->node->ways);
1551
1552 /* this node must only be part of this way */
1553 if(!(*chain)->node->ways) {
1554 /* delete this node, but don't let this actually affect the */
1555 /* associated ways as the only such way is the oen we are currently */
1556 /* deleting */
1557 way_chain_t *way_chain =
1558 osm_node_delete(osm, icon, (*chain)->node, FALSE, FALSE);
1559 g_assert(way_chain);
1560 while(way_chain) {
1561 way_chain_t *way_next = way_chain->next;
1562 g_assert(way_chain->way == way);
1563 g_free(way_chain);
1564 way_chain = way_next;
1565 }
1566 }
1567
1568 node_chain_t *cur = (*chain);
1569 *chain = cur->next;
1570 g_free(cur);
1571 }
1572 way->node_chain = NULL;
1573
1574 if(!permanently) {
1575 printf("mark way #%ld as deleted\n", way->id);
1576 way->flags |= OSM_FLAG_DELETED;
1577 } else {
1578 printf("permanently delete way #%ld\n", way->id);
1579
1580 /* remove it from the chain */
1581 way_t **cway = &osm->way;
1582 int found = 0;
1583
1584 while(*cway) {
1585 if(*cway == way) {
1586 found++;
1587 *cway = (*cway)->next;
1588
1589 osm_way_free(way);
1590 } else
1591 cway = &((*cway)->next);
1592 }
1593 g_assert(found == 1);
1594 }
1595 }
1596
1597 void osm_way_revert(way_t *way) {
1598 node_chain_t *new = NULL;
1599
1600 /* walk old chain first to last */
1601 node_chain_t *old = way->node_chain;
1602 while(old) {
1603 node_chain_t *next = old->next;
1604
1605 /* and prepend each node to the new chain */
1606 old->next = new;
1607 new = old;
1608
1609 old = next;
1610 }
1611
1612 way->node_chain = new;
1613 }
1614
1615 node_t *osm_way_get_first_node(way_t *way) {
1616 node_chain_t *chain = way->node_chain;
1617 if(!chain) return NULL;
1618 return chain->node;
1619 }
1620
1621 node_t *osm_way_get_last_node(way_t *way) {
1622 node_chain_t *chain = way->node_chain;
1623
1624 while(chain && chain->next) chain=chain->next;
1625
1626 if(!chain) return NULL;
1627
1628 return chain->node;
1629 }
1630
1631 void osm_way_rotate(way_t *way, gint offset) {
1632 if(!offset) return;
1633
1634 /* needs at least two nodes to work properly */
1635 g_assert(way->node_chain);
1636 g_assert(way->node_chain->next);
1637
1638 while(offset--) {
1639 node_chain_t *chain = way->node_chain;
1640 chain->node->ways--; // reduce way count of old start/end node
1641
1642 /* move all nodes ahead one chain element ... */
1643 while(chain->next) {
1644 chain->node = chain->next->node;
1645 chain = chain->next;
1646 }
1647
1648 /* ... and make last one same as first one */
1649 chain->node = way->node_chain->node;
1650 chain->node->ways++; // increase way count of new start/end node
1651 }
1652 }
1653
1654 tag_t *osm_tags_copy(tag_t *src_tag, gboolean update_creator) {
1655 tag_t *new_tags = NULL;
1656 tag_t **dst_tag = &new_tags;
1657
1658 while(src_tag) {
1659 *dst_tag = g_new0(tag_t, 1);
1660 (*dst_tag)->key = g_strdup(src_tag->key);
1661 if(update_creator && (strcasecmp(src_tag->key, "created_by") == 0))
1662 (*dst_tag)->value = g_strdup(PACKAGE " v" VERSION);
1663 else
1664 (*dst_tag)->value = g_strdup(src_tag->value);
1665
1666 dst_tag = &(*dst_tag)->next;
1667 src_tag = src_tag->next;
1668 }
1669
1670 return new_tags;
1671 }