29 |
#include <libxml/tree.h> |
#include <libxml/tree.h> |
30 |
|
|
31 |
#include "appdata.h" |
#include "appdata.h" |
32 |
|
#include "banner.h" |
33 |
|
|
34 |
#ifndef LIBXML_TREE_ENABLED |
#ifndef LIBXML_TREE_ENABLED |
35 |
#error "Tree not enabled in libxml" |
#error "Tree not enabled in libxml" |
41 |
#define OSM_SORT_LAST |
#define OSM_SORT_LAST |
42 |
// #define OSM_SORT_FIRST |
// #define OSM_SORT_FIRST |
43 |
|
|
44 |
/* ------------------------- user handling --------------------- */ |
/* ------------------------- bounds handling --------------------- */ |
45 |
|
|
46 |
static void osm_bounds_free(bounds_t *bounds) { |
static void osm_bounds_free(bounds_t *bounds) { |
47 |
free(bounds); |
free(bounds); |
53 |
bounds->ll_min.lon, bounds->ll_max.lon); |
bounds->ll_min.lon, bounds->ll_max.lon); |
54 |
} |
} |
55 |
|
|
|
static bounds_t *osm_parse_osm_bounds(osm_t *osm, |
|
|
xmlDocPtr doc, xmlNode *a_node) { |
|
|
char *prop; |
|
|
|
|
|
if(osm->bounds) { |
|
|
errorf(NULL, "Doubly defined bounds"); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
bounds_t *bounds = g_new0(bounds_t, 1); |
|
|
|
|
|
bounds->ll_min.lat = bounds->ll_min.lon = NAN; |
|
|
bounds->ll_max.lat = bounds->ll_max.lon = NAN; |
|
|
|
|
|
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"minlat"))) { |
|
|
bounds->ll_min.lat = g_ascii_strtod(prop, NULL); |
|
|
xmlFree(prop); |
|
|
} |
|
|
|
|
|
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"maxlat"))) { |
|
|
bounds->ll_max.lat = g_ascii_strtod(prop, NULL); |
|
|
xmlFree(prop); |
|
|
} |
|
|
|
|
|
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"minlon"))) { |
|
|
bounds->ll_min.lon = g_ascii_strtod(prop, NULL); |
|
|
xmlFree(prop); |
|
|
} |
|
|
|
|
|
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"maxlon"))) { |
|
|
bounds->ll_max.lon = g_ascii_strtod(prop, NULL); |
|
|
xmlFree(prop); |
|
|
} |
|
|
|
|
|
if(isnan(bounds->ll_min.lat) || isnan(bounds->ll_min.lon) || |
|
|
isnan(bounds->ll_max.lat) || isnan(bounds->ll_max.lon)) { |
|
|
errorf(NULL, "Invalid coordinate in bounds (%f/%f/%f/%f)", |
|
|
bounds->ll_min.lat, bounds->ll_min.lon, |
|
|
bounds->ll_max.lat, bounds->ll_max.lon); |
|
|
|
|
|
g_free(bounds); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
|
|
|
/* calculate map zone which will be used as a reference for all */ |
|
|
/* drawing/projection later on */ |
|
|
pos_t center = { (bounds->ll_max.lat + bounds->ll_min.lat)/2, |
|
|
(bounds->ll_max.lon + bounds->ll_min.lon)/2 }; |
|
|
|
|
|
pos2lpos_center(¢er, &bounds->center); |
|
|
|
|
|
/* the scale is needed to accomodate for "streching" */ |
|
|
/* by the mercartor projection */ |
|
|
bounds->scale = cos(DEG2RAD(center.lat)); |
|
|
|
|
|
pos2lpos_center(&bounds->ll_min, &bounds->min); |
|
|
bounds->min.x -= bounds->center.x; |
|
|
bounds->min.y -= bounds->center.y; |
|
|
bounds->min.x *= bounds->scale; |
|
|
bounds->min.y *= bounds->scale; |
|
|
|
|
|
pos2lpos_center(&bounds->ll_max, &bounds->max); |
|
|
bounds->max.x -= bounds->center.x; |
|
|
bounds->max.y -= bounds->center.y; |
|
|
bounds->max.x *= bounds->scale; |
|
|
bounds->max.y *= bounds->scale; |
|
|
|
|
|
return bounds; |
|
|
} |
|
|
|
|
56 |
/* ------------------------- user handling --------------------- */ |
/* ------------------------- user handling --------------------- */ |
57 |
|
|
58 |
void osm_users_free(user_t *user) { |
void osm_users_free(user_t *user) { |
75 |
} |
} |
76 |
|
|
77 |
static user_t *osm_user(osm_t *osm, char *name) { |
static user_t *osm_user(osm_t *osm, char *name) { |
78 |
|
if(!name) return NULL; |
79 |
|
|
80 |
/* search through user list */ |
/* search through user list */ |
81 |
user_t **user = &osm->user; |
user_t **user = &osm->user; |
97 |
|
|
98 |
static |
static |
99 |
time_t convert_iso8601(const char *str) { |
time_t convert_iso8601(const char *str) { |
100 |
|
if(!str) return 0; |
101 |
|
|
102 |
tzset(); |
tzset(); |
103 |
|
|
104 |
struct tm ctime; |
struct tm ctime; |
252 |
} |
} |
253 |
} |
} |
254 |
|
|
|
static node_t *osm_parse_osm_node(osm_t *osm, |
|
|
xmlDocPtr doc, xmlNode *a_node) { |
|
|
xmlNode *cur_node = NULL; |
|
|
|
|
|
/* allocate a new node structure */ |
|
|
node_t *node = g_new0(node_t, 1); |
|
|
node->pos.lat = node->pos.lon = NAN; |
|
|
|
|
|
char *prop; |
|
|
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"id"))) { |
|
|
node->id = strtoul(prop, NULL, 10); |
|
|
xmlFree(prop); |
|
|
} |
|
|
|
|
|
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"lat"))) { |
|
|
node->pos.lat = g_ascii_strtod(prop, NULL); |
|
|
xmlFree(prop); |
|
|
} |
|
|
|
|
|
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"lon"))) { |
|
|
node->pos.lon = g_ascii_strtod(prop, NULL); |
|
|
xmlFree(prop); |
|
|
} |
|
|
|
|
|
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"user"))) { |
|
|
node->user = osm_user(osm, prop); |
|
|
xmlFree(prop); |
|
|
} |
|
|
|
|
|
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"visible"))) { |
|
|
node->visible = (strcasecmp(prop, "true") == 0); |
|
|
xmlFree(prop); |
|
|
} |
|
|
|
|
|
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"timestamp"))) { |
|
|
node->time = convert_iso8601(prop); |
|
|
xmlFree(prop); |
|
|
} |
|
|
|
|
|
/* scan for tags and attach a list of tags */ |
|
|
tag_t **tag = &node->tag; |
|
|
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) { |
|
|
if (cur_node->type == XML_ELEMENT_NODE) { |
|
|
if(strcasecmp((char*)cur_node->name, "tag") == 0) { |
|
|
/* attach tag to node */ |
|
|
*tag = osm_parse_osm_tag(osm, doc, cur_node); |
|
|
if(*tag) tag = &((*tag)->next); |
|
|
} else |
|
|
printf("found unhandled osm/node/%s\n", cur_node->name); |
|
|
} |
|
|
} |
|
|
|
|
|
pos2lpos(osm->bounds, &node->pos, &node->lpos); |
|
|
|
|
|
return node; |
|
|
} |
|
|
|
|
255 |
/* ------------------- way handling ------------------- */ |
/* ------------------- way handling ------------------- */ |
256 |
|
|
257 |
void osm_node_chain_free(node_chain_t *node_chain) { |
void osm_node_chain_free(node_chain_t *node_chain) { |
344 |
node_chain_t *node_chain = g_new0(node_chain_t, 1); |
node_chain_t *node_chain = g_new0(node_chain_t, 1); |
345 |
|
|
346 |
/* search matching node */ |
/* search matching node */ |
347 |
node_chain->node = osm->node; |
node_chain->node = osm_get_node_by_id(osm, id); |
|
while(node_chain->node && node_chain->node->id != id) |
|
|
node_chain->node = node_chain->node->next; |
|
|
|
|
348 |
if(!node_chain->node) printf("Node id %lu not found\n", id); |
if(!node_chain->node) printf("Node id %lu not found\n", id); |
349 |
|
else node_chain->node->ways++; |
|
if(node_chain->node) |
|
|
node_chain->node->ways++; |
|
350 |
|
|
351 |
xmlFree(prop); |
xmlFree(prop); |
352 |
|
|
356 |
return NULL; |
return NULL; |
357 |
} |
} |
358 |
|
|
|
static way_t *osm_parse_osm_way(osm_t *osm, |
|
|
xmlDocPtr doc, xmlNode *a_node) { |
|
|
xmlNode *cur_node = NULL; |
|
|
|
|
|
/* allocate a new way structure */ |
|
|
way_t *way = g_new0(way_t, 1); |
|
|
|
|
|
char *prop; |
|
|
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"id"))) { |
|
|
way->id = strtoul(prop, NULL, 10); |
|
|
xmlFree(prop); |
|
|
} |
|
|
|
|
|
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"user"))) { |
|
|
way->user = osm_user(osm, prop); |
|
|
xmlFree(prop); |
|
|
} |
|
|
|
|
|
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"visible"))) { |
|
|
way->visible = (strcasecmp(prop, "true") == 0); |
|
|
xmlFree(prop); |
|
|
} |
|
|
|
|
|
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"timestamp"))) { |
|
|
way->time = convert_iso8601(prop); |
|
|
xmlFree(prop); |
|
|
} |
|
|
|
|
|
/* scan for tags/nodes and attach their lists */ |
|
|
tag_t **tag = &way->tag; |
|
|
node_chain_t **node_chain = &way->node_chain; |
|
|
|
|
|
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) { |
|
|
if (cur_node->type == XML_ELEMENT_NODE) { |
|
|
if(strcasecmp((char*)cur_node->name, "tag") == 0) { |
|
|
/* attach tag to node */ |
|
|
*tag = osm_parse_osm_tag(osm, doc, cur_node); |
|
|
if(*tag) tag = &((*tag)->next); |
|
|
} else if(strcasecmp((char*)cur_node->name, "nd") == 0) { |
|
|
*node_chain = osm_parse_osm_way_nd(osm, doc, cur_node); |
|
|
if(*node_chain) |
|
|
node_chain = &((*node_chain)->next); |
|
|
} else |
|
|
printf("found unhandled osm/node/%s\n", cur_node->name); |
|
|
} |
|
|
} |
|
|
|
|
|
return way; |
|
|
} |
|
|
|
|
359 |
/* ------------------- relation handling ------------------- */ |
/* ------------------- relation handling ------------------- */ |
360 |
|
|
361 |
void osm_member_free(member_t *member) { |
void osm_member_free(member_t *member) { |
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) { |
static void osm_relations_free(relation_t *relation) { |
382 |
while(relation) { |
while(relation) { |
383 |
relation_t *next = relation->next; |
relation_t *next = relation->next; |
384 |
|
osm_relation_free(relation); |
|
osm_tags_free(relation->tag); |
|
|
osm_members_free(relation->member); |
|
|
|
|
|
g_free(relation); |
|
385 |
relation = next; |
relation = next; |
386 |
} |
} |
387 |
} |
} |
418 |
member->way->id, member->role); |
member->way->id, member->role); |
419 |
break; |
break; |
420 |
|
|
421 |
case RELATION: |
case RELATION: |
422 |
if(member->relation) |
if(member->relation) |
423 |
printf(" Member: Relation, id = %lu, role = %s\n", |
printf(" Member: Relation, id = %lu, role = %s\n", |
424 |
member->relation->id, member->role); |
member->relation->id, member->role); |
425 |
break; |
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(¢er, &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 |
member = member->next; |
/* 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); |
|
localtime_r(&relation->time, &tm); |
|
|
strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", &tm); |
|
|
printf("Time: %s\n", buf); |
|
|
osm_tags_dump(relation->tag); |
|
|
|
|
|
printf("\n"); |
|
|
relation = relation->next; |
|
837 |
} |
} |
838 |
|
|
839 |
|
return way; |
840 |
} |
} |
841 |
|
|
842 |
member_t *osm_parse_osm_relation_member(osm_t *osm, |
static member_t *process_member(xmlTextReaderPtr reader, osm_t *osm) { |
|
xmlDocPtr doc, xmlNode *a_node) { |
|
843 |
char *prop; |
char *prop; |
844 |
member_t *member = g_new0(member_t, 1); |
member_t *member = g_new0(member_t, 1); |
845 |
member->type = ILLEGAL; |
member->type = ILLEGAL; |
846 |
|
|
847 |
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"type"))) { |
if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "type"))) { |
848 |
if(strcasecmp(prop, "way") == 0) member->type = WAY; |
if(strcasecmp(prop, "way") == 0) member->type = WAY; |
849 |
else if(strcasecmp(prop, "node") == 0) member->type = NODE; |
else if(strcasecmp(prop, "node") == 0) member->type = NODE; |
850 |
else if(strcasecmp(prop, "relation") == 0) member->type = RELATION; |
else if(strcasecmp(prop, "relation") == 0) member->type = RELATION; |
851 |
xmlFree(prop); |
xmlFree(prop); |
852 |
} |
} |
853 |
|
|
854 |
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"ref"))) { |
if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref"))) { |
855 |
item_id_t id = strtoul(prop, NULL, 10); |
item_id_t id = strtoul(prop, NULL, 10); |
856 |
|
|
857 |
switch(member->type) { |
switch(member->type) { |
861 |
|
|
862 |
case WAY: |
case WAY: |
863 |
/* search matching way */ |
/* search matching way */ |
864 |
member->way = osm->way; |
member->way = osm_get_way_by_id(osm, id); |
|
while(member->way && member->way->id != id) |
|
|
member->way = member->way->next; |
|
|
|
|
865 |
if(!member->way) { |
if(!member->way) { |
866 |
member->type = WAY_ID; |
member->type = WAY_ID; |
867 |
member->id = id; |
member->id = id; |
870 |
|
|
871 |
case NODE: |
case NODE: |
872 |
/* search matching node */ |
/* search matching node */ |
873 |
member->node = osm->node; |
member->node = osm_get_node_by_id(osm, id); |
|
while(member->node && member->node->id != id) |
|
|
member->node = member->node->next; |
|
|
|
|
874 |
if(!member->node) { |
if(!member->node) { |
875 |
member->type = NODE_ID; |
member->type = NODE_ID; |
876 |
member->id = id; |
member->id = id; |
879 |
|
|
880 |
case RELATION: |
case RELATION: |
881 |
/* search matching relation */ |
/* search matching relation */ |
882 |
member->relation = osm->relation; |
member->relation = osm_get_relation_by_id(osm, id); |
|
while(member->relation && member->relation->id != id) |
|
|
member->relation = member->relation->next; |
|
|
|
|
883 |
if(!member->relation) { |
if(!member->relation) { |
884 |
member->type = NODE_ID; |
member->type = NODE_ID; |
885 |
member->id = id; |
member->id = id; |
895 |
xmlFree(prop); |
xmlFree(prop); |
896 |
} |
} |
897 |
|
|
898 |
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"role"))) { |
if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "role"))) { |
899 |
if(strlen(prop) > 0) member->role = g_strdup(prop); |
if(strlen(prop) > 0) member->role = g_strdup(prop); |
900 |
xmlFree(prop); |
xmlFree(prop); |
901 |
} |
} |
903 |
return member; |
return member; |
904 |
} |
} |
905 |
|
|
906 |
static relation_t *osm_parse_osm_relation(osm_t *osm, |
static relation_t *process_relation(xmlTextReaderPtr reader, osm_t *osm) { |
|
xmlDocPtr doc, xmlNode *a_node) { |
|
|
xmlNode *cur_node = NULL; |
|
|
|
|
907 |
/* allocate a new relation structure */ |
/* allocate a new relation structure */ |
908 |
relation_t *relation = g_new0(relation_t, 1); |
relation_t *relation = g_new0(relation_t, 1); |
909 |
|
|
910 |
char *prop; |
char *prop; |
911 |
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"id"))) { |
if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) { |
912 |
relation->id = strtoul(prop, NULL, 10); |
relation->id = strtoul(prop, NULL, 10); |
913 |
xmlFree(prop); |
xmlFree(prop); |
914 |
} |
} |
915 |
|
|
916 |
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"user"))) { |
if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) { |
917 |
relation->user = osm_user(osm, prop); |
relation->user = osm_user(osm, prop); |
918 |
xmlFree(prop); |
xmlFree(prop); |
919 |
} |
} |
920 |
|
|
921 |
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"visible"))) { |
if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) { |
922 |
relation->visible = (strcasecmp(prop, "true") == 0); |
relation->visible = (strcasecmp(prop, "true") == 0); |
923 |
xmlFree(prop); |
xmlFree(prop); |
924 |
} |
} |
925 |
|
|
926 |
if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"timestamp"))) { |
if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) { |
927 |
relation->time = convert_iso8601(prop); |
relation->time = convert_iso8601(prop); |
928 |
xmlFree(prop); |
xmlFree(prop); |
929 |
} |
} |
930 |
|
|
931 |
/* scan for tags and attach a list of tags */ |
/* 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; |
tag_t **tag = &relation->tag; |
942 |
member_t **member = &relation->member; |
member_t **member = &relation->member; |
943 |
|
int ret = xmlTextReaderRead(reader); |
944 |
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) { |
while((ret == 1) && |
945 |
if (cur_node->type == XML_ELEMENT_NODE) { |
((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) || |
946 |
if(strcasecmp((char*)cur_node->name, "tag") == 0) { |
(xmlTextReaderDepth(reader) != depth))) { |
947 |
/* attach tag to node */ |
|
948 |
*tag = osm_parse_osm_tag(osm, doc, cur_node); |
if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) { |
949 |
if(*tag) tag = &((*tag)->next); |
char *subname = (char*)xmlTextReaderConstName(reader); |
950 |
} else if(strcasecmp((char*)cur_node->name, "member") == 0) { |
if(strcasecmp(subname, "member") == 0) { |
951 |
*member = osm_parse_osm_relation_member(osm, doc, cur_node); |
*member = process_member(reader, osm); |
952 |
if(*member) member = &((*member)->next); |
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 |
} else |
957 |
printf("found unhandled osm/node/%s\n", cur_node->name); |
skip_element(reader); |
958 |
} |
} |
959 |
|
ret = xmlTextReaderRead(reader); |
960 |
} |
} |
961 |
|
|
962 |
return relation; |
return relation; |
963 |
} |
} |
964 |
|
|
965 |
/* ----------------------- generic xml handling -------------------------- */ |
static osm_t *process_osm(xmlTextReaderPtr reader) { |
966 |
|
/* alloc osm structure */ |
967 |
/* parse loc entry */ |
osm_t *osm = g_new0(osm_t, 1); |
968 |
static void osm_parse_osm(osm_t *osm, xmlDocPtr doc, xmlNode * a_node) { |
osm->node_hash = g_new0(hash_table_t, 1); |
969 |
xmlNode *cur_node = NULL; |
osm->way_hash = g_new0(hash_table_t, 1); |
970 |
|
|
971 |
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) { |
node_t **node = &osm->node; |
972 |
if (cur_node->type == XML_ELEMENT_NODE) { |
way_t **way = &osm->way; |
973 |
if(strcasecmp((char*)cur_node->name, "bounds") == 0) |
relation_t **relation = &osm->relation; |
974 |
osm->bounds = osm_parse_osm_bounds(osm, doc, cur_node); |
|
975 |
else if(strcasecmp((char*)cur_node->name, "node") == 0) { |
/* no attributes of interest */ |
|
/* parse node and attach it to chain */ |
|
|
node_t *new = osm_parse_osm_node(osm, doc, cur_node); |
|
|
if(new) { |
|
|
node_t **node = &osm->node; |
|
|
|
|
|
#ifdef OSM_SORT_ID |
|
|
/* search chain of nodes */ |
|
|
while(*node && ((*node)->id < new->id)) |
|
|
node = &(*node)->next; |
|
|
#endif |
|
|
|
|
|
#ifdef OSM_SORT_LAST |
|
|
while(*node) node = &(*node)->next; |
|
|
#endif |
|
976 |
|
|
977 |
/* insert into chain */ |
const xmlChar *name = xmlTextReaderConstName(reader); |
978 |
new->next = *node; |
g_assert(name); |
|
*node = new; |
|
|
} |
|
|
} else if(strcasecmp((char*)cur_node->name, "way") == 0) { |
|
|
/* parse way and attach it to chain */ |
|
|
way_t *new = osm_parse_osm_way(osm, doc, cur_node); |
|
|
if(new) { |
|
|
way_t **way = &osm->way; |
|
|
|
|
|
#ifdef OSM_SORT_ID |
|
|
/* insert into chain */ |
|
|
while(*way && ((*way)->id < new->id)) |
|
|
way = &(*way)->next; |
|
|
#endif |
|
|
|
|
|
#ifdef OSM_SORT_LAST |
|
|
while(*way) way = &(*way)->next; |
|
|
#endif |
|
979 |
|
|
980 |
/* insert into chain */ |
/* read next node */ |
981 |
new->next = *way; |
int num_elems = 0; |
982 |
*way = new; |
const int tick_every = 50; // Balance responsive appearance with performance. |
983 |
} |
int ret = xmlTextReaderRead(reader); |
984 |
} else if(strcasecmp((char*)cur_node->name, "relation") == 0) { |
while(ret == 1) { |
985 |
/* parse relation and attach it to chain */ |
|
986 |
relation_t *new = osm_parse_osm_relation(osm, doc, cur_node); |
switch(xmlTextReaderNodeType(reader)) { |
987 |
if(new) { |
case XML_READER_TYPE_ELEMENT: |
988 |
relation_t **relation = &osm->relation; |
|
989 |
|
g_assert(xmlTextReaderDepth(reader) == 1); |
990 |
#ifdef OSM_SORT_ID |
char *name = (char*)xmlTextReaderConstName(reader); |
991 |
/* search chain of ways */ |
if(strcasecmp(name, "bounds") == 0) { |
992 |
while(*relation && ((*relation)->id < new->id)) |
osm->bounds = process_bounds(reader); |
993 |
relation = &(*relation)->next; |
} else if(strcasecmp(name, "node") == 0) { |
994 |
#endif |
*node = process_node(reader, osm); |
995 |
|
if(*node) node = &(*node)->next; |
996 |
#ifdef OSM_SORT_LAST |
} else if(strcasecmp(name, "way") == 0) { |
997 |
while(*relation) relation = &(*relation)->next; |
*way = process_way(reader, osm); |
998 |
#endif |
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 |
/* insert into chain */ |
if (num_elems++ > tick_every) { |
1021 |
new->next = *relation; |
num_elems = 0; |
1022 |
*relation = new; |
banner_busy_tick(); |
|
} |
|
|
} else |
|
|
printf("found unhandled osm/%s\n", cur_node->name); |
|
|
|
|
1023 |
} |
} |
1024 |
} |
} |
|
} |
|
1025 |
|
|
1026 |
/* parse root element and search for "osm" */ |
g_assert(0); |
1027 |
static osm_t *osm_parse_root(xmlDocPtr doc, xmlNode * a_node) { |
return NULL; |
1028 |
osm_t *osm; |
} |
|
xmlNode *cur_node = NULL; |
|
1029 |
|
|
1030 |
/* allocate memory to hold osm file description */ |
static osm_t *process_file(const char *filename) { |
1031 |
osm = g_new0(osm_t, 1); |
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 |
for (cur_node = a_node; cur_node; cur_node = cur_node->next) { |
xmlFreeTextReader(reader); |
1046 |
if (cur_node->type == XML_ELEMENT_NODE) { |
} else { |
1047 |
/* parse osm osm file ... */ |
fprintf(stderr, "Unable to open %s\n", filename); |
|
if(strcasecmp((char*)cur_node->name, "osm") == 0) |
|
|
osm_parse_osm(osm, doc, cur_node); |
|
|
else |
|
|
printf("found unhandled %s\n", cur_node->name); |
|
|
} |
|
1048 |
} |
} |
|
|
|
1049 |
return osm; |
return osm; |
1050 |
} |
} |
1051 |
|
|
1052 |
static osm_t *osm_parse_doc(xmlDocPtr doc) { |
/* ----------------------- end of stream parser ------------------- */ |
|
osm_t *osm; |
|
|
|
|
|
/* Get the root element node */ |
|
|
xmlNode *root_element = xmlDocGetRootElement(doc); |
|
|
|
|
|
osm = osm_parse_root(doc, root_element); |
|
|
|
|
|
/*free the document */ |
|
|
xmlFreeDoc(doc); |
|
|
|
|
|
/* |
|
|
* Free the global variables that may |
|
|
* have been allocated by the parser. |
|
|
*/ |
|
|
xmlCleanupParser(); |
|
1053 |
|
|
1054 |
return osm; |
#include <sys/time.h> |
|
} |
|
1055 |
|
|
1056 |
/* ------------------ osm handling ----------------- */ |
osm_t *osm_parse(char *filename) { |
1057 |
|
|
1058 |
void osm_free(icon_t **icon, osm_t *osm) { |
struct timeval start; |
1059 |
if(!osm) return; |
gettimeofday(&start, NULL); |
1060 |
|
|
1061 |
if(osm->bounds) osm_bounds_free(osm->bounds); |
LIBXML_TEST_VERSION; |
|
if(osm->user) osm_users_free(osm->user); |
|
|
if(osm->way) osm_ways_free(osm->way); |
|
|
if(osm->node) osm_nodes_free(icon, osm->node); |
|
|
if(osm->relation) osm_relations_free(osm->relation); |
|
|
g_free(osm); |
|
|
} |
|
1062 |
|
|
1063 |
void osm_dump(osm_t *osm) { |
// use stream parser |
1064 |
osm_bounds_dump(osm->bounds); |
osm_t *osm = process_file(filename); |
1065 |
osm_users_dump(osm->user); |
xmlCleanupParser(); |
|
osm_nodes_dump(osm->node); |
|
|
osm_ways_dump(osm->way); |
|
|
osm_relations_dump(osm->relation); |
|
|
} |
|
1066 |
|
|
1067 |
osm_t *osm_parse(char *filename) { |
struct timeval end; |
1068 |
xmlDoc *doc = NULL; |
gettimeofday(&end, NULL); |
1069 |
|
|
1070 |
LIBXML_TEST_VERSION; |
printf("total parse time: %ldms\n", |
1071 |
|
(end.tv_usec - start.tv_usec)/1000 + |
1072 |
|
(end.tv_sec - start.tv_sec)*1000); |
1073 |
|
|
1074 |
/* parse the file and get the DOM */ |
return osm; |
|
if ((doc = xmlReadFile(filename, NULL, 0)) == NULL) { |
|
|
xmlErrorPtr errP = xmlGetLastError(); |
|
|
errorf(NULL, "While parsing \"%s\":\n\n%s", filename, errP->message); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
return osm_parse_doc(doc); |
|
1075 |
} |
} |
1076 |
|
|
1077 |
gboolean osm_sanity_check(GtkWidget *parent, osm_t *osm) { |
gboolean osm_sanity_check(GtkWidget *parent, osm_t *osm) { |
1322 |
return osm_generate_xml(osm, RELATION, relation); |
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) { |
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; |
node_t *node = osm->node; |
1341 |
while(node) { |
while(node) { |
1342 |
if(node->id == id) |
if(node->id == id) |
1343 |
return node; |
return node; |
1344 |
|
|
1345 |
node = node->next; |
node = node->next; |
1346 |
} |
} |
1347 |
|
|
1348 |
return NULL; |
return NULL; |
1349 |
} |
} |
1350 |
|
|
1351 |
way_t *osm_get_way_by_id(osm_t *osm, item_id_t id) { |
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; |
way_t *way = osm->way; |
1365 |
while(way) { |
while(way) { |
1366 |
if(way->id == id) |
if(way->id == id) |
1367 |
return way; |
return way; |
1368 |
|
|
1369 |
way = way->next; |
way = way->next; |
1370 |
} |
} |
1371 |
|
|
1372 |
return NULL; |
return NULL; |
1373 |
} |
} |
1374 |
|
|
1375 |
relation_t *osm_get_relation_by_id(osm_t *osm, item_id_t id) { |
relation_t *osm_get_relation_by_id(osm_t *osm, item_id_t id) { |
1376 |
|
// use linear search |
1377 |
relation_t *relation = osm->relation; |
relation_t *relation = osm->relation; |
1378 |
while(relation) { |
while(relation) { |
1379 |
if(relation->id == id) |
if(relation->id == id) |
1431 |
return 0; |
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) { |
node_t *osm_node_new(osm_t *osm, gint x, gint y) { |
1457 |
printf("Creating new node\n"); |
printf("Creating new node\n"); |
1458 |
|
|
1489 |
*lnode = node; |
*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) { |
way_t *osm_way_new(void) { |
1502 |
printf("Creating new way\n"); |
printf("Creating new way\n"); |
1503 |
|
|
1711 |
cur_chain = &((*cur_chain)->next); |
cur_chain = &((*cur_chain)->next); |
1712 |
} |
} |
1713 |
|
|
1714 |
way = way->next; |
way = way->next; |
1715 |
} |
} |
1716 |
|
|
1717 |
return chain; |
return chain; |
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, |
void osm_way_delete(osm_t *osm, icon_t **icon, |
1807 |
way_t *way, gboolean permanently) { |
way_t *way, gboolean permanently) { |
1808 |
|
|
1866 |
} |
} |
1867 |
} |
} |
1868 |
|
|
1869 |
void osm_way_revert(way_t *way) { |
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; |
node_chain_t *new = NULL; |
1908 |
|
|
1909 |
/* walk old chain first to last */ |
/* walk old chain first to last */ |
1921 |
way->node_chain = new; |
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 (strcasecmp(member->role, DS_ROUTE_FORWARD) == 0) { |
2027 |
|
g_free(member->role); |
2028 |
|
member->role = g_strdup(DS_ROUTE_REVERSE); |
2029 |
|
rel_chain->relation->flags |= OSM_FLAG_DIRTY; |
2030 |
|
++n_roles_flipped; |
2031 |
|
} |
2032 |
|
else if (strcasecmp(member->role, DS_ROUTE_REVERSE) == 0) { |
2033 |
|
g_free(member->role); |
2034 |
|
member->role = g_strdup(DS_ROUTE_FORWARD); |
2035 |
|
rel_chain->relation->flags |= OSM_FLAG_DIRTY; |
2036 |
|
++n_roles_flipped; |
2037 |
|
} |
2038 |
|
|
2039 |
|
// TODO: what about numbered stops? Guess we ignore them; there's no |
2040 |
|
// consensus about whether they should be placed on the way or to one side |
2041 |
|
// of it. |
2042 |
|
|
2043 |
|
}//if-route |
2044 |
|
|
2045 |
|
|
2046 |
|
} |
2047 |
|
if (rel_chain0) { |
2048 |
|
g_free(rel_chain0); |
2049 |
|
} |
2050 |
|
return n_roles_flipped; |
2051 |
|
} |
2052 |
|
|
2053 |
node_t *osm_way_get_first_node(way_t *way) { |
node_t *osm_way_get_first_node(way_t *way) { |
2054 |
node_chain_t *chain = way->node_chain; |
node_chain_t *chain = way->node_chain; |
2055 |
if(!chain) return NULL; |
if(!chain) return NULL; |
2107 |
|
|
2108 |
return new_tags; |
return new_tags; |
2109 |
} |
} |
2110 |
|
|
2111 |
|
/* return plain text of type */ |
2112 |
|
char *osm_type_string(type_t type) { |
2113 |
|
const struct { type_t type; char *name; } types[] = { |
2114 |
|
{ ILLEGAL, "illegal" }, |
2115 |
|
{ NODE, "node" }, |
2116 |
|
{ WAY, "way" }, |
2117 |
|
{ RELATION, "relation" }, |
2118 |
|
{ NODE_ID, "node id" }, |
2119 |
|
{ WAY_ID, "way id" }, |
2120 |
|
{ RELATION_ID, "relation id" }, |
2121 |
|
{ 0, NULL } |
2122 |
|
}; |
2123 |
|
|
2124 |
|
int i; |
2125 |
|
for(i=0;types[i].name;i++) |
2126 |
|
if(type == types[i].type) |
2127 |
|
return types[i].name; |
2128 |
|
|
2129 |
|
return NULL; |
2130 |
|
} |
2131 |
|
|
2132 |
|
char *osm_object_string(type_t type, void *object) { |
2133 |
|
char *type_str = osm_type_string(type); |
2134 |
|
|
2135 |
|
if(!object) |
2136 |
|
return g_strdup_printf("%s #<invalid>", type_str); |
2137 |
|
|
2138 |
|
switch(type) { |
2139 |
|
case ILLEGAL: |
2140 |
|
return g_strdup_printf("%s #<unspec>", type_str); |
2141 |
|
break; |
2142 |
|
case NODE: |
2143 |
|
return g_strdup_printf("%s #%ld", type_str, ((node_t*)object)->id); |
2144 |
|
break; |
2145 |
|
case WAY: |
2146 |
|
return g_strdup_printf("%s #%ld", type_str, ((way_t*)object)->id); |
2147 |
|
break; |
2148 |
|
case RELATION: |
2149 |
|
return g_strdup_printf("%s #%ld", type_str, ((relation_t*)object)->id); |
2150 |
|
break; |
2151 |
|
case NODE_ID: |
2152 |
|
case WAY_ID: |
2153 |
|
case RELATION_ID: |
2154 |
|
return g_strdup_printf("%s #%ld", type_str, ((item_id_t)object)); |
2155 |
|
break; |
2156 |
|
} |
2157 |
|
return NULL; |
2158 |
|
} |
2159 |
|
|
2160 |
|
char *osm_id_string(type_t type, void *object) { |
2161 |
|
if(!object) return NULL; |
2162 |
|
|
2163 |
|
switch(type) { |
2164 |
|
case ILLEGAL: |
2165 |
|
return NULL; |
2166 |
|
break; |
2167 |
|
case NODE: |
2168 |
|
return g_strdup_printf("#%ld", ((node_t*)object)->id); |
2169 |
|
break; |
2170 |
|
case WAY: |
2171 |
|
return g_strdup_printf("#%ld", ((way_t*)object)->id); |
2172 |
|
break; |
2173 |
|
case RELATION: |
2174 |
|
return g_strdup_printf("#%ld", ((relation_t*)object)->id); |
2175 |
|
break; |
2176 |
|
case NODE_ID: |
2177 |
|
case WAY_ID: |
2178 |
|
case RELATION_ID: |
2179 |
|
return g_strdup_printf("#%ld", ((item_id_t)object)); |
2180 |
|
break; |
2181 |
|
} |
2182 |
|
return NULL; |
2183 |
|
} |
2184 |
|
|
2185 |
|
tag_t *osm_object_get_tags(type_t type, void *object) { |
2186 |
|
if(!object) return NULL; |
2187 |
|
|
2188 |
|
switch(type) { |
2189 |
|
case ILLEGAL: |
2190 |
|
return NULL; |
2191 |
|
break; |
2192 |
|
case NODE: |
2193 |
|
return ((node_t*)object)->tag; |
2194 |
|
break; |
2195 |
|
case WAY: |
2196 |
|
return ((way_t*)object)->tag; |
2197 |
|
break; |
2198 |
|
case RELATION: |
2199 |
|
return ((relation_t*)object)->tag; |
2200 |
|
break; |
2201 |
|
case NODE_ID: |
2202 |
|
case WAY_ID: |
2203 |
|
case RELATION_ID: |
2204 |
|
return NULL; |
2205 |
|
break; |
2206 |
|
} |
2207 |
|
return NULL; |
2208 |
|
} |
2209 |
|
|
2210 |
|
|
2211 |
|
gint osm_relation_members_num(relation_t *relation) { |
2212 |
|
gint num = 0; |
2213 |
|
member_t *member = relation->member; |
2214 |
|
while(member) { |
2215 |
|
num++; |
2216 |
|
member = member->next; |
2217 |
|
} |
2218 |
|
return num; |
2219 |
|
} |
2220 |
|
|
2221 |
|
// vim:et:ts=8:sw=2:sts=2:ai |