Diff of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 6 by achadwick, Thu Dec 11 13:26:13 2008 UTC revision 98 by achadwick, Sun Feb 22 03:41:09 2009 UTC
# Line 29  Line 29 
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"
# Line 40  Line 41 
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);
# Line 52  static void osm_bounds_dump(bounds_t *bo Line 53  static void osm_bounds_dump(bounds_t *bo
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(&center, &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) {
# Line 145  void osm_users_dump(user_t *user) { Line 75  void osm_users_dump(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;
# Line 166  static user_t *osm_user(osm_t *osm, char Line 97  static user_t *osm_user(osm_t *osm, char
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;
# Line 319  void osm_nodes_dump(node_t *node) { Line 252  void osm_nodes_dump(node_t *node) {
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) {
# Line 468  node_chain_t *osm_parse_osm_way_nd(osm_t Line 344  node_chain_t *osm_parse_osm_way_nd(osm_t
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    
# Line 485  node_chain_t *osm_parse_osm_way_nd(osm_t Line 356  node_chain_t *osm_parse_osm_way_nd(osm_t
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) {
# Line 550  void osm_members_free(member_t *member) Line 371  void osm_members_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  }  }
# Line 594  void osm_relations_dump(relation_t *rela Line 418  void osm_relations_dump(relation_t *rela
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(&center, &bounds->center);
631    
632      /* the scale is needed to accomodate for "streching" */
633      /* by the mercartor projection */
634      bounds->scale = cos(DEG2RAD(center.lat));
635    
636      pos2lpos_center(&bounds->ll_min, &bounds->min);
637      bounds->min.x -= bounds->center.x;
638      bounds->min.y -= bounds->center.y;
639      bounds->min.x *= bounds->scale;
640      bounds->min.y *= bounds->scale;
641    
642      pos2lpos_center(&bounds->ll_max, &bounds->max);
643      bounds->max.x -= bounds->center.x;
644      bounds->max.y -= bounds->center.y;
645      bounds->max.x *= bounds->scale;
646      bounds->max.y *= bounds->scale;
647    
648      return bounds;
649    }
650    
651    static tag_t *process_tag(xmlTextReaderPtr reader) {
652      /* allocate a new tag structure */
653      tag_t *tag = g_new0(tag_t, 1);
654    
655      char *prop;
656      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "k"))) {
657        if(strlen(prop) > 0) tag->key = g_strdup(prop);
658        xmlFree(prop);
659      }
660    
661      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "v"))) {
662        if(strlen(prop) > 0) tag->value = g_strdup(prop);
663        xmlFree(prop);
664      }
665    
666      if(!tag->key || !tag->value) {
667        printf("incomplete tag key/value %s/%s\n", tag->key, tag->value);
668        osm_tags_free(tag);
669        tag = NULL;
670      }
671    
672      skip_element(reader);
673      return tag;
674    }
675    
676    static node_t *process_node(xmlTextReaderPtr reader, osm_t *osm) {
677    
678      /* allocate a new node structure */
679      node_t *node = g_new0(node_t, 1);
680      node->pos.lat = node->pos.lon = NAN;
681    
682      char *prop;
683      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
684        node->id = strtoul(prop, NULL, 10);
685        xmlFree(prop);
686      }
687    
688      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "lat"))) {
689        node->pos.lat = g_ascii_strtod(prop, NULL);
690        xmlFree(prop);
691      }
692    
693      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "lon"))) {
694        node->pos.lon = g_ascii_strtod(prop, NULL);
695        xmlFree(prop);
696      }
697    
698      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) {
699        node->user = osm_user(osm, prop);
700        xmlFree(prop);
701      }
702    
703      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) {
704        node->visible = (strcasecmp(prop, "true") == 0);
705        xmlFree(prop);
706      }
707    
708      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) {
709        node->time = convert_iso8601(prop);
710        xmlFree(prop);
711      }
712    
713      pos2lpos(osm->bounds, &node->pos, &node->lpos);
714    
715      /* append node to end of hash table if present */
716      if(osm->node_hash) {
717        hash_item_t **item = &osm->node_hash->hash[ID2HASH(node->id)];
718        while(*item) item = &(*item)->next;
719    
720        *item = g_new0(hash_item_t, 1);
721        (*item)->data.node = node;
722      }
723    
724      /* just an empty element? then return the node as it is */
725      if(xmlTextReaderIsEmptyElement(reader))
726        return node;
727    
728      /* parse tags if present */
729      int depth = xmlTextReaderDepth(reader);
730    
731      /* scan all elements on same level or its children */
732      tag_t **tag = &node->tag;
733      int ret = xmlTextReaderRead(reader);
734      while((ret == 1) &&
735            ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
736             (xmlTextReaderDepth(reader) != depth))) {
737    
738        if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
739          char *subname = (char*)xmlTextReaderConstName(reader);
740          if(strcasecmp(subname, "tag") == 0) {
741            *tag = process_tag(reader);
742            if(*tag) tag = &(*tag)->next;
743          } else
744            skip_element(reader);
745        }
746    
747        ret = xmlTextReaderRead(reader);
748      }
749    
750      return node;
751    }
752    
753    static node_chain_t *process_nd(xmlTextReaderPtr reader, osm_t *osm) {
754      char *prop;
755    
756      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref"))) {
757        item_id_t id = strtoul(prop, NULL, 10);
758        node_chain_t *node_chain = g_new0(node_chain_t, 1);
759    
760        /* search matching node */
761        node_chain->node = osm_get_node_by_id(osm, id);
762        if(!node_chain->node) printf("Node id %lu not found\n", id);
763        else                  node_chain->node->ways++;
764    
765        xmlFree(prop);
766    
767        skip_element(reader);
768        return node_chain;
769      }
770    
771      skip_element(reader);
772      return NULL;
773    }
774    
775    static way_t *process_way(xmlTextReaderPtr reader, osm_t *osm) {
776      /* allocate a new way structure */
777      way_t *way = g_new0(way_t, 1);
778    
779      char *prop;
780      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
781        way->id = strtoul(prop, NULL, 10);
782        xmlFree(prop);
783      }
784    
785      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) {
786        way->user = osm_user(osm, prop);
787        xmlFree(prop);
788      }
789    
790      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) {
791        way->visible = (strcasecmp(prop, "true") == 0);
792        xmlFree(prop);
793      }
794    
795      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) {
796        way->time = convert_iso8601(prop);
797        xmlFree(prop);
798      }
799    
800      /* append way to end of hash table if present */
801      if(osm->way_hash) {
802        hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)];
803        while(*item) item = &(*item)->next;
804    
805        *item = g_new0(hash_item_t, 1);
806        (*item)->data.way = way;
807      }
808    
809      /* just an empty element? then return the way as it is */
810      /* (this should in fact never happen as this would be a way without nodes) */
811      if(xmlTextReaderIsEmptyElement(reader))
812        return way;
813    
814        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) {
# Line 637  member_t *osm_parse_osm_relation_member( Line 861  member_t *osm_parse_osm_relation_member(
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;
# Line 649  member_t *osm_parse_osm_relation_member( Line 870  member_t *osm_parse_osm_relation_member(
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;
# Line 661  member_t *osm_parse_osm_relation_member( Line 879  member_t *osm_parse_osm_relation_member(
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;
# Line 680  member_t *osm_parse_osm_relation_member( Line 895  member_t *osm_parse_osm_relation_member(
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    }    }
# Line 688  member_t *osm_parse_osm_relation_member( Line 903  member_t *osm_parse_osm_relation_member(
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) {
# Line 1139  char *osm_generate_xml_relation(osm_t *o Line 1322  char *osm_generate_xml_relation(osm_t *o
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)
# Line 1219  item_id_t osm_new_node_id(osm_t *osm) { Line 1431  item_id_t osm_new_node_id(osm_t *osm) {
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    
# Line 1255  void osm_node_attach(osm_t *osm, node_t Line 1489  void osm_node_attach(osm_t *osm, node_t
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    
# Line 1468  way_chain_t *osm_node_to_way(osm_t *osm, Line 1711  way_chain_t *osm_node_to_way(osm_t *osm,
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;
# Line 1531  void osm_way_remove_from_relation(osm_t Line 1774  void osm_way_remove_from_relation(osm_t
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    
# Line 1594  void osm_way_delete(osm_t *osm, icon_t * Line 1866  void osm_way_delete(osm_t *osm, icon_t *
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 */
# Line 1612  void osm_way_revert(way_t *way) { Line 1921  void osm_way_revert(way_t *way) {
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;
# Line 1669  tag_t *osm_tags_copy(tag_t *src_tag, gbo Line 2107  tag_t *osm_tags_copy(tag_t *src_tag, gbo
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

Legend:
Removed from v.6  
changed lines
  Added in v.98