Contents of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log


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