Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


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