Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


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