Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


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