Diff of /trunk/src/osm.c

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

src/osm.c revision 1 by harbaum, Tue Dec 9 20:06:06 2008 UTC trunk/src/osm.c revision 161 by harbaum, Sat Apr 11 11:28:56 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"
36  #endif  #endif
37    
38  /* determine where a node/way/relation read from the osm file */  /* ------------------------- bounds handling --------------------- */
 /* is inserted into the internal database */  
 // #define OSM_SORT_ID  
 #define OSM_SORT_LAST  
 // #define OSM_SORT_FIRST  
   
 /* ------------------------- user handling --------------------- */  
39    
40  static void osm_bounds_free(bounds_t *bounds) {  static void osm_bounds_free(bounds_t *bounds) {
41    free(bounds);    free(bounds);
# Line 52  static void osm_bounds_dump(bounds_t *bo Line 47  static void osm_bounds_dump(bounds_t *bo
47           bounds->ll_min.lon, bounds->ll_max.lon);           bounds->ll_min.lon, bounds->ll_max.lon);
48  }  }
49    
 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;  
 }  
   
50  /* ------------------------- user handling --------------------- */  /* ------------------------- user handling --------------------- */
51    
52  void osm_users_free(user_t *user) {  void osm_users_free(user_t *user) {
# Line 145  void osm_users_dump(user_t *user) { Line 69  void osm_users_dump(user_t *user) {
69  }  }
70    
71  static user_t *osm_user(osm_t *osm, char *name) {  static user_t *osm_user(osm_t *osm, char *name) {
72      if(!name) return NULL;
73    
74    /* search through user list */    /* search through user list */
75    user_t **user = &osm->user;    user_t **user = &osm->user;
# Line 166  static user_t *osm_user(osm_t *osm, char Line 91  static user_t *osm_user(osm_t *osm, char
91    
92  static  static
93  time_t convert_iso8601(const char *str) {  time_t convert_iso8601(const char *str) {
94      if(!str) return 0;
95    
96    tzset();    tzset();
97    
98    struct tm ctime;    struct tm ctime;
# Line 300  void osm_node_dump(node_t *node) { Line 227  void osm_node_dump(node_t *node) {
227    char buf[64];    char buf[64];
228    struct tm tm;    struct tm tm;
229    
230    printf("Id:      %lu\n", node->id);    printf("Id:      "ITEM_ID_FORMAT"\n", node->id);
231    printf("User:    %s\n", node->user?node->user->name:"<unspecified>");    printf("User:    %s\n", node->user?node->user->name:"<unspecified>");
232    printf("Visible: %s\n", node->visible?"yes":"no");    printf("Visible: %s\n", node->visible?"yes":"no");
233    
# Line 319  void osm_nodes_dump(node_t *node) { Line 246  void osm_nodes_dump(node_t *node) {
246    }    }
247  }  }
248    
 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;  
 }  
   
249  /* ------------------- way handling ------------------- */  /* ------------------- way handling ------------------- */
250    
251  void osm_node_chain_free(node_chain_t *node_chain) {  void osm_node_chain_free(node_chain_t *node_chain) {
# Line 390  void osm_node_chain_free(node_chain_t *n Line 260  void osm_node_chain_free(node_chain_t *n
260  }  }
261    
262  void osm_way_free(way_t *way) {  void osm_way_free(way_t *way) {
263    //  printf("freeing way #%ld\n", way->id);    //  printf("freeing way #" ITEM_ID_FORMAT "\n", way->id);
264    
265    osm_node_chain_free(way->node_chain);    osm_node_chain_free(way->node_chain);
266    osm_tags_free(way->tag);    osm_tags_free(way->tag);
# Line 435  void osm_way_dump(way_t *way) { Line 305  void osm_way_dump(way_t *way) {
305    char buf[64];    char buf[64];
306    struct tm tm;    struct tm tm;
307    
308    printf("Id:      %lu\n", way->id);    printf("Id:      "ITEM_ID_FORMAT"\n", way->id);
309    printf("User:    %s\n", way->user?way->user->name:"<unspecified>");    printf("User:    %s\n", way->user?way->user->name:"<unspecified>");
310    printf("Visible: %s\n", way->visible?"yes":"no");    printf("Visible: %s\n", way->visible?"yes":"no");
311    node_chain_t *node_chain = way->node_chain;    node_chain_t *node_chain = way->node_chain;
312    while(node_chain) {    while(node_chain) {
313      printf("  Node:  %lu\n", node_chain->node->id);      printf("  Node:  "ITEM_ID_FORMAT"\n", node_chain->node->id);
314      node_chain = node_chain->next;      node_chain = node_chain->next;
315    }    }
316    
# Line 468  node_chain_t *osm_parse_osm_way_nd(osm_t Line 338  node_chain_t *osm_parse_osm_way_nd(osm_t
338      node_chain_t *node_chain = g_new0(node_chain_t, 1);      node_chain_t *node_chain = g_new0(node_chain_t, 1);
339    
340      /* search matching node */      /* search matching node */
341      node_chain->node = osm->node;      node_chain->node = osm_get_node_by_id(osm, id);
342      while(node_chain->node && node_chain->node->id != id)      if(!node_chain->node) printf("Node id " ITEM_ID_FORMAT " not found\n", id);
343        node_chain->node = node_chain->node->next;      else                  node_chain->node->ways++;
   
     if(!node_chain->node) printf("Node id %lu not found\n", id);  
   
     if(node_chain->node)  
       node_chain->node->ways++;  
344    
345      xmlFree(prop);      xmlFree(prop);
346    
# Line 485  node_chain_t *osm_parse_osm_way_nd(osm_t Line 350  node_chain_t *osm_parse_osm_way_nd(osm_t
350    return NULL;    return NULL;
351  }  }
352    
 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;  
 }  
   
353  /* ------------------- relation handling ------------------- */  /* ------------------- relation handling ------------------- */
354    
355  void osm_member_free(member_t *member) {  void osm_member_free(member_t *member) {
# Line 550  void osm_members_free(member_t *member) Line 365  void osm_members_free(member_t *member)
365    }    }
366  }  }
367    
368    void osm_relation_free(relation_t *relation) {
369      osm_tags_free(relation->tag);
370      osm_members_free(relation->member);
371    
372      g_free(relation);
373    }
374    
375  static void osm_relations_free(relation_t *relation) {  static void osm_relations_free(relation_t *relation) {
376    while(relation) {    while(relation) {
377      relation_t *next = relation->next;      relation_t *next = relation->next;
378        osm_relation_free(relation);
     osm_tags_free(relation->tag);  
     osm_members_free(relation->member);  
   
     g_free(relation);  
379      relation = next;      relation = next;
380    }    }
381  }  }
# Line 568  void osm_relations_dump(relation_t *rela Line 386  void osm_relations_dump(relation_t *rela
386      char buf[64];      char buf[64];
387      struct tm tm;      struct tm tm;
388    
389      printf("Id:      %lu\n", relation->id);      printf("Id:      "ITEM_ID_FORMAT"\n", relation->id);
390      printf("User:    %s\n",      printf("User:    %s\n",
391             relation->user?relation->user->name:"<unspecified>");             relation->user?relation->user->name:"<unspecified>");
392      printf("Visible: %s\n", relation->visible?"yes":"no");      printf("Visible: %s\n", relation->visible?"yes":"no");
393    
394      member_t *member = relation->member;      member_t *member = relation->member;
395      while(member) {      while(member) {
396        switch(member->type) {        switch(member->object.type) {
397        case ILLEGAL:        case ILLEGAL:
398        case NODE_ID:        case NODE_ID:
399        case WAY_ID:        case WAY_ID:
# Line 583  void osm_relations_dump(relation_t *rela Line 401  void osm_relations_dump(relation_t *rela
401          break;          break;
402    
403        case NODE:        case NODE:
404          if(member->node)          if(member->object.node)
405            printf(" Member: Node, id = %lu, role = %s\n",            printf(" Member: Node, id = " ITEM_ID_FORMAT ", role = %s\n",
406                   member->node->id, member->role);                   member->object.node->id, member->role);
407          break;          break;
408    
409        case WAY:        case WAY:
410          if(member->way)          if(member->object.way)
411          printf(" Member: Way, id = %lu, role = %s\n",            printf(" Member: Way, id = " ITEM_ID_FORMAT ", role = %s\n",
412                 member->way->id, member->role);                   member->object.way->id, member->role);
413          break;          break;
414    
415        case RELATION:        case RELATION:
416          if(member->relation)          if(member->object.relation)
417          printf(" Member: Relation, id = %lu, role = %s\n",            printf(" Member: Relation, id = " ITEM_ID_FORMAT ", role = %s\n",
418                 member->relation->id, member->role);                   member->object.relation->id, member->role);
419          break;          break;
420        }        }
421    
# Line 618  member_t *osm_parse_osm_relation_member( Line 436  member_t *osm_parse_osm_relation_member(
436                            xmlDocPtr doc, xmlNode *a_node) {                            xmlDocPtr doc, xmlNode *a_node) {
437    char *prop;    char *prop;
438    member_t *member = g_new0(member_t, 1);    member_t *member = g_new0(member_t, 1);
439    member->type = ILLEGAL;    member->object.type = ILLEGAL;
440    
441    if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"type"))) {    if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"type"))) {
442      if(strcasecmp(prop, "way") == 0)           member->type = WAY;      if(strcasecmp(prop, "way") == 0)           member->object.type = WAY;
443      else if(strcasecmp(prop, "node") == 0)     member->type = NODE;      else if(strcasecmp(prop, "node") == 0)     member->object.type = NODE;
444      else if(strcasecmp(prop, "relation") == 0) member->type = RELATION;      else if(strcasecmp(prop, "relation") == 0) member->object.type = RELATION;
445      xmlFree(prop);      xmlFree(prop);
446    }    }
447    
448    if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"ref"))) {    if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"ref"))) {
449      item_id_t id = strtoul(prop, NULL, 10);      item_id_t id = strtoul(prop, NULL, 10);
450    
451      switch(member->type) {      switch(member->object.type) {
452      case ILLEGAL:      case ILLEGAL:
453        printf("Unable to store illegal type\n");        printf("Unable to store illegal type\n");
454        break;        break;
455    
456      case WAY:      case WAY:
457        /* search matching way */        /* search matching way */
458        member->way = osm->way;        member->object.way = osm_get_way_by_id(osm, id);
459        while(member->way && member->way->id != id)        if(!member->object.way) {
460          member->way = member->way->next;          member->object.type = WAY_ID;
461            member->object.id = id;
       if(!member->way) {  
         member->type = WAY_ID;  
         member->id = id;  
462        }        }
463        break;        break;
464    
465      case NODE:      case NODE:
466        /* search matching node */        /* search matching node */
467        member->node = osm->node;        member->object.node = osm_get_node_by_id(osm, id);
468        while(member->node && member->node->id != id)        if(!member->object.node) {
469          member->node = member->node->next;          member->object.type = NODE_ID;
470            member->object.id = id;
       if(!member->node) {  
         member->type = NODE_ID;  
         member->id = id;  
471        }        }
472        break;        break;
473    
474      case RELATION:      case RELATION:
475        /* search matching relation */        /* search matching relation */
476        member->relation = osm->relation;        member->object.relation = osm_get_relation_by_id(osm, id);
477        while(member->relation && member->relation->id != id)        if(!member->object.relation) {
478          member->relation = member->relation->next;          member->object.type = NODE_ID;
479            member->object.id = id;
       if(!member->relation) {  
         member->type = NODE_ID;  
         member->id = id;  
480        }        }
481        break;        break;
482    
# Line 688  member_t *osm_parse_osm_relation_member( Line 497  member_t *osm_parse_osm_relation_member(
497    return member;    return member;
498  }  }
499    
500  static relation_t *osm_parse_osm_relation(osm_t *osm,  /* ------------------ osm handling ----------------- */
                           xmlDocPtr doc, xmlNode *a_node) {  
   xmlNode *cur_node = NULL;  
501    
502    /* allocate a new relation structure */  /* the two hash tables eat over 512kBytes memory and may thus be */
503    relation_t *relation = g_new0(relation_t, 1);  /* freed at any time. osm2go can work without them (albeit slower) */
504    static void hash_table_free(hash_table_t *table) {
505      if(!table) return;
506    
507      int i;
508      for(i=0;i<65536;i++) {
509        hash_item_t *item = table->hash[i];
510        while(item) {
511          hash_item_t *next = item->next;
512          g_free(item);
513          item = next;
514        }
515      }
516    }
517    
518    void osm_hash_tables_free(osm_t *osm) {
519      hash_table_free(osm->node_hash);
520      osm->node_hash = NULL;
521      hash_table_free(osm->way_hash);
522      osm->way_hash = NULL;
523    }
524    
525    void osm_free(icon_t **icon, osm_t *osm) {
526      if(!osm) return;
527    
528      osm_hash_tables_free(osm);
529    
530      if(osm->bounds)   osm_bounds_free(osm->bounds);
531      if(osm->user)     osm_users_free(osm->user);
532      if(osm->way)      osm_ways_free(osm->way);
533      if(osm->node)     osm_nodes_free(icon, osm->node);
534      if(osm->relation) osm_relations_free(osm->relation);
535      g_free(osm);
536    }
537    
538    void osm_dump(osm_t *osm) {
539      osm_bounds_dump(osm->bounds);
540      osm_users_dump(osm->user);
541      osm_nodes_dump(osm->node);
542      osm_ways_dump(osm->way);
543      osm_relations_dump(osm->relation);
544    }
545    
546    /* -------------------------- stream parser ------------------- */
547    
548    #include <libxml/xmlreader.h>
549    
550    static gint my_strcmp(const xmlChar *a, const xmlChar *b) {
551      if(!a && !b) return 0;
552      if(!a) return -1;
553      if(!b) return +1;
554      return strcmp((char*)a,(char*)b);
555    }
556    
557    /* skip current element incl. everything below (mainly for testing) */
558    /* returns FALSE if something failed */
559    static gboolean skip_element(xmlTextReaderPtr reader) {
560      g_assert(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT);
561      const xmlChar *name = xmlTextReaderConstName(reader);
562      g_assert(name);
563      int depth = xmlTextReaderDepth(reader);
564    
565      if(xmlTextReaderIsEmptyElement(reader))
566        return TRUE;
567    
568      int ret = xmlTextReaderRead(reader);
569      while((ret == 1) &&
570            ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
571             (xmlTextReaderDepth(reader) > depth) ||
572             (my_strcmp(xmlTextReaderConstName(reader), name) != 0))) {
573        ret = xmlTextReaderRead(reader);
574      }
575      return(ret == 1);
576    }
577    
578    /* parse bounds */
579    static bounds_t *process_bounds(xmlTextReaderPtr reader) {
580      char *prop = NULL;
581      bounds_t *bounds = g_new0(bounds_t, 1);
582    
583      bounds->ll_min.lat = bounds->ll_min.lon = NAN;
584      bounds->ll_max.lat = bounds->ll_max.lon = NAN;
585    
586      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "minlat"))) {
587        bounds->ll_min.lat = g_ascii_strtod(prop, NULL);
588        xmlFree(prop);
589      }
590    
591      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "maxlat"))) {
592        bounds->ll_max.lat = g_ascii_strtod(prop, NULL);
593        xmlFree(prop);
594      }
595    
596      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "minlon"))) {
597        bounds->ll_min.lon = g_ascii_strtod(prop, NULL);
598        xmlFree(prop);
599      }
600    
601      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "maxlon"))) {
602        bounds->ll_max.lon = g_ascii_strtod(prop, NULL);
603        xmlFree(prop);
604      }
605    
606      if(isnan(bounds->ll_min.lat) || isnan(bounds->ll_min.lon) ||
607         isnan(bounds->ll_max.lat) || isnan(bounds->ll_max.lon)) {
608        errorf(NULL, "Invalid coordinate in bounds (%f/%f/%f/%f)",
609               bounds->ll_min.lat, bounds->ll_min.lon,
610               bounds->ll_max.lat, bounds->ll_max.lon);
611    
612        osm_bounds_free(bounds);
613        return NULL;
614      }
615    
616      /* skip everything below */
617      skip_element(reader);
618    
619      /* calculate map zone which will be used as a reference for all */
620      /* drawing/projection later on */
621      pos_t center = { (bounds->ll_max.lat + bounds->ll_min.lat)/2,
622                       (bounds->ll_max.lon + bounds->ll_min.lon)/2 };
623    
624      pos2lpos_center(&center, &bounds->center);
625    
626      /* the scale is needed to accomodate for "streching" */
627      /* by the mercartor projection */
628      bounds->scale = cos(DEG2RAD(center.lat));
629    
630      pos2lpos_center(&bounds->ll_min, &bounds->min);
631      bounds->min.x -= bounds->center.x;
632      bounds->min.y -= bounds->center.y;
633      bounds->min.x *= bounds->scale;
634      bounds->min.y *= bounds->scale;
635    
636      pos2lpos_center(&bounds->ll_max, &bounds->max);
637      bounds->max.x -= bounds->center.x;
638      bounds->max.y -= bounds->center.y;
639      bounds->max.x *= bounds->scale;
640      bounds->max.y *= bounds->scale;
641    
642      return bounds;
643    }
644    
645    static tag_t *process_tag(xmlTextReaderPtr reader) {
646      /* allocate a new tag structure */
647      tag_t *tag = g_new0(tag_t, 1);
648    
649    char *prop;    char *prop;
650    if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"id"))) {    if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "k"))) {
651      relation->id = strtoul(prop, NULL, 10);      if(strlen(prop) > 0) tag->key = g_strdup(prop);
652      xmlFree(prop);      xmlFree(prop);
653    }    }
654    
655    if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"user"))) {    if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "v"))) {
656      relation->user = osm_user(osm, prop);      if(strlen(prop) > 0) tag->value = g_strdup(prop);
657      xmlFree(prop);      xmlFree(prop);
658    }    }
659    
660    if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"visible"))) {    if(!tag->key || !tag->value) {
661      relation->visible = (strcasecmp(prop, "true") == 0);      printf("incomplete tag key/value %s/%s\n", tag->key, tag->value);
662        osm_tags_free(tag);
663        tag = NULL;
664      }
665    
666      skip_element(reader);
667      return tag;
668    }
669    
670    static node_t *process_node(xmlTextReaderPtr reader, osm_t *osm) {
671    
672      /* allocate a new node structure */
673      node_t *node = g_new0(node_t, 1);
674      node->pos.lat = node->pos.lon = NAN;
675    
676      char *prop;
677      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
678        node->id = strtoul(prop, NULL, 10);
679      xmlFree(prop);      xmlFree(prop);
680    }    }
681    
682    if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"timestamp"))) {    /* new in api 0.6: */
683      relation->time = convert_iso8601(prop);    if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "version"))) {
684        node->version = strtoul(prop, NULL, 10);
685      xmlFree(prop);      xmlFree(prop);
686    }    }
687    
688    /* scan for tags and attach a list of tags */    if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "lat"))) {
689    tag_t **tag = &relation->tag;      node->pos.lat = g_ascii_strtod(prop, NULL);
690    member_t **member = &relation->member;      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    for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {    /* parse tags if present */
729      if (cur_node->type == XML_ELEMENT_NODE) {    int depth = xmlTextReaderDepth(reader);
730        if(strcasecmp((char*)cur_node->name, "tag") == 0) {  
731          /* attach tag to node */    /* scan all elements on same level or its children */
732          *tag = osm_parse_osm_tag(osm, doc, cur_node);    tag_t **tag = &node->tag;
733          if(*tag) tag = &((*tag)->next);    int ret = xmlTextReaderRead(reader);
734        } else if(strcasecmp((char*)cur_node->name, "member") == 0) {    while((ret == 1) &&
735          *member = osm_parse_osm_relation_member(osm, doc, cur_node);          ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
736          if(*member) member = &((*member)->next);           (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        } else
744          printf("found unhandled osm/node/%s\n", cur_node->name);          skip_element(reader);
745      }      }
746    
747        ret = xmlTextReaderRead(reader);
748    }    }
749    
750    return relation;    return node;
751  }  }
752    
753  /* ----------------------- generic xml handling -------------------------- */  static node_chain_t *process_nd(xmlTextReaderPtr reader, osm_t *osm) {
754      char *prop;
755    
756  /* parse loc entry */    if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref"))) {
757  static void osm_parse_osm(osm_t *osm, xmlDocPtr doc, xmlNode * a_node) {      item_id_t id = strtoul(prop, NULL, 10);
758    xmlNode *cur_node = NULL;      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 " ITEM_ID_FORMAT " not found\n", id);
763        else                  node_chain->node->ways++;
764    
765    for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {      xmlFree(prop);
     if (cur_node->type == XML_ELEMENT_NODE) {  
       if(strcasecmp((char*)cur_node->name, "bounds") == 0)  
         osm->bounds = osm_parse_osm_bounds(osm, doc, cur_node);  
       else if(strcasecmp((char*)cur_node->name, "node") == 0) {  
         /* 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  
766    
767            /* insert into chain */      skip_element(reader);
768            new->next = *node;      return node_chain;
769            *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  
770    
771            /* insert into chain */    skip_element(reader);
772            new->next = *way;    return NULL;
773            *way = new;  }
774          }  
775        } else if(strcasecmp((char*)cur_node->name, "relation") == 0) {  static way_t *process_way(xmlTextReaderPtr reader, osm_t *osm) {
776          /* parse relation and attach it to chain */    /* allocate a new way structure */
777          relation_t *new = osm_parse_osm_relation(osm, doc, cur_node);    way_t *way = g_new0(way_t, 1);
778          if(new) {  
779            relation_t **relation = &osm->relation;    char *prop;
780      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
781  #ifdef OSM_SORT_ID      way->id = strtoul(prop, NULL, 10);
782            /* search chain of ways */      xmlFree(prop);
783            while(*relation && ((*relation)->id < new->id))    }
784              relation = &(*relation)->next;  
785  #endif    /* new in api 0.6: */
786      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "version"))) {
787  #ifdef OSM_SORT_LAST      way->version = strtoul(prop, NULL, 10);
788            while(*relation) relation = &(*relation)->next;      xmlFree(prop);
789  #endif    }
790    
791      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) {
792        way->user = osm_user(osm, prop);
793        xmlFree(prop);
794      }
795    
796      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) {
797        way->visible = (strcasecmp(prop, "true") == 0);
798        xmlFree(prop);
799      }
800    
801      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) {
802        way->time = convert_iso8601(prop);
803        xmlFree(prop);
804      }
805    
806      /* append way to end of hash table if present */
807      if(osm->way_hash) {
808        hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)];
809        while(*item) item = &(*item)->next;
810    
811        *item = g_new0(hash_item_t, 1);
812        (*item)->data.way = way;
813      }
814    
815      /* just an empty element? then return the way as it is */
816      /* (this should in fact never happen as this would be a way without nodes) */
817      if(xmlTextReaderIsEmptyElement(reader))
818        return way;
819    
820      /* parse tags/nodes if present */
821      int depth = xmlTextReaderDepth(reader);
822    
823      /* scan all elements on same level or its children */
824      tag_t **tag = &way->tag;
825      node_chain_t **node_chain = &way->node_chain;
826      int ret = xmlTextReaderRead(reader);
827      while((ret == 1) &&
828            ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
829             (xmlTextReaderDepth(reader) != depth))) {
830    
831        if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
832          char *subname = (char*)xmlTextReaderConstName(reader);
833          if(strcasecmp(subname, "nd") == 0) {
834            *node_chain = process_nd(reader, osm);
835            if(*node_chain) node_chain = &(*node_chain)->next;
836          } else if(strcasecmp(subname, "tag") == 0) {
837            *tag = process_tag(reader);
838            if(*tag) tag = &(*tag)->next;
839          } else
840            skip_element(reader);
841        }
842        ret = xmlTextReaderRead(reader);
843      }
844    
845      return way;
846    }
847    
848    static member_t *process_member(xmlTextReaderPtr reader, osm_t *osm) {
849      char *prop;
850      member_t *member = g_new0(member_t, 1);
851      member->object.type = ILLEGAL;
852    
853      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "type"))) {
854        if(strcasecmp(prop, "way") == 0)           member->object.type = WAY;
855        else if(strcasecmp(prop, "node") == 0)     member->object.type = NODE;
856        else if(strcasecmp(prop, "relation") == 0) member->object.type = RELATION;
857        xmlFree(prop);
858      }
859    
860      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref"))) {
861        item_id_t id = strtoul(prop, NULL, 10);
862    
863        switch(member->object.type) {
864        case ILLEGAL:
865          printf("Unable to store illegal type\n");
866          break;
867    
868        case WAY:
869          /* search matching way */
870          member->object.way = osm_get_way_by_id(osm, id);
871          if(!member->object.way) {
872            member->object.type = WAY_ID;
873            member->object.id = id;
874          }
875          break;
876    
877        case NODE:
878          /* search matching node */
879          member->object.node = osm_get_node_by_id(osm, id);
880          if(!member->object.node) {
881            member->object.type = NODE_ID;
882            member->object.id = id;
883          }
884          break;
885    
886        case RELATION:
887          /* search matching relation */
888          member->object.relation = osm_get_relation_by_id(osm, id);
889          if(!member->object.relation) {
890            member->object.type = NODE_ID;
891            member->object.id = id;
892          }
893          break;
894    
895        case WAY_ID:
896        case NODE_ID:
897        case RELATION_ID:
898          break;
899        }
900    
901        xmlFree(prop);
902      }
903    
904      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "role"))) {
905        if(strlen(prop) > 0) member->role = g_strdup(prop);
906        xmlFree(prop);
907      }
908    
909      return member;
910    }
911    
912    static relation_t *process_relation(xmlTextReaderPtr reader, osm_t *osm) {
913      /* allocate a new relation structure */
914      relation_t *relation = g_new0(relation_t, 1);
915    
916      char *prop;
917      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
918        relation->id = strtoul(prop, NULL, 10);
919        xmlFree(prop);
920      }
921    
922      /* new in api 0.6: */
923      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "version"))) {
924        relation->version = strtoul(prop, NULL, 10);
925        xmlFree(prop);
926      }
927    
928      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) {
929        relation->user = osm_user(osm, prop);
930        xmlFree(prop);
931      }
932    
933      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) {
934        relation->visible = (strcasecmp(prop, "true") == 0);
935        xmlFree(prop);
936      }
937    
938            /* insert into chain */    if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) {
939            new->next = *relation;      relation->time = convert_iso8601(prop);
940            *relation = new;      xmlFree(prop);
         }  
       } else  
         printf("found unhandled osm/%s\n", cur_node->name);  
   
     }  
941    }    }
 }  
942    
943  /* parse root element and search for "osm" */    /* just an empty element? then return the relation as it is */
944  static osm_t *osm_parse_root(xmlDocPtr doc, xmlNode * a_node) {    /* (this should in fact never happen as this would be a relation */
945    osm_t *osm;    /* without members) */
946    xmlNode *cur_node = NULL;    if(xmlTextReaderIsEmptyElement(reader))
947        return relation;
948    
949    /* allocate memory to hold osm file description */    /* parse tags/member if present */
950    osm = g_new0(osm_t, 1);    int depth = xmlTextReaderDepth(reader);
951    
952    for (cur_node = a_node; cur_node; cur_node = cur_node->next) {    /* scan all elements on same level or its children */
953      if (cur_node->type == XML_ELEMENT_NODE) {    tag_t **tag = &relation->tag;
954        /* parse osm osm file ... */    member_t **member = &relation->member;
955        if(strcasecmp((char*)cur_node->name, "osm") == 0)    int ret = xmlTextReaderRead(reader);
956          osm_parse_osm(osm, doc, cur_node);    while((ret == 1) &&
957        else          ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
958          printf("found unhandled %s\n", cur_node->name);           (xmlTextReaderDepth(reader) != depth))) {
959    
960        if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
961          char *subname = (char*)xmlTextReaderConstName(reader);
962          if(strcasecmp(subname, "member") == 0) {
963            *member = process_member(reader, osm);
964            if(*member) member = &(*member)->next;
965          } else if(strcasecmp(subname, "tag") == 0) {
966            *tag = process_tag(reader);
967            if(*tag) tag = &(*tag)->next;
968          } else
969            skip_element(reader);
970      }      }
971        ret = xmlTextReaderRead(reader);
972    }    }
973    
974    return osm;    return relation;
975  }  }
976    
977  static osm_t *osm_parse_doc(xmlDocPtr doc) {  static osm_t *process_osm(xmlTextReaderPtr reader) {
978    osm_t *osm;    /* alloc osm structure */
979      osm_t *osm = g_new0(osm_t, 1);
980    /* Get the root element node */    osm->node_hash = g_new0(hash_table_t, 1);
981    xmlNode *root_element = xmlDocGetRootElement(doc);    osm->way_hash = g_new0(hash_table_t, 1);
982    
983      node_t **node = &osm->node;
984      way_t **way = &osm->way;
985      relation_t **relation = &osm->relation;
986    
987      /* no attributes of interest */
988    
989    osm = osm_parse_root(doc, root_element);    const xmlChar *name = xmlTextReaderConstName(reader);
990      g_assert(name);
991    
992    /*free the document */    /* read next node */
993    xmlFreeDoc(doc);    int num_elems = 0;
994      const int tick_every = 50; // Balance responsive appearance with performance.
995      int ret = xmlTextReaderRead(reader);
996      while(ret == 1) {
997    
998        switch(xmlTextReaderNodeType(reader)) {
999        case XML_READER_TYPE_ELEMENT:
1000    
1001          g_assert(xmlTextReaderDepth(reader) == 1);
1002          char *name = (char*)xmlTextReaderConstName(reader);
1003          if(strcasecmp(name, "bounds") == 0) {
1004            osm->bounds = process_bounds(reader);
1005          } else if(strcasecmp(name, "node") == 0) {
1006            *node = process_node(reader, osm);
1007            if(*node) node = &(*node)->next;
1008          } else if(strcasecmp(name, "way") == 0) {
1009            *way = process_way(reader, osm);
1010            if(*way) way = &(*way)->next;
1011          } else if(strcasecmp(name, "relation") == 0) {
1012            *relation = process_relation(reader, osm);
1013            if(*relation) relation = &(*relation)->next;
1014          } else {
1015            printf("something unknown found\n");
1016            g_assert(0);
1017            skip_element(reader);
1018          }
1019          break;
1020    
1021        case XML_READER_TYPE_END_ELEMENT:
1022          /* end element must be for the current element */
1023          g_assert(xmlTextReaderDepth(reader) == 0);
1024          return osm;
1025          break;
1026    
1027        default:
1028          break;
1029        }
1030        ret = xmlTextReaderRead(reader);
1031    
1032    /*      if (num_elems++ > tick_every) {
1033     * Free the global variables that may        num_elems = 0;
1034     * have been allocated by the parser.        banner_busy_tick();
1035     */      }
1036    xmlCleanupParser();    }
1037    
1038    return osm;    g_assert(0);
1039      return NULL;
1040  }  }
1041    
1042  /* ------------------ osm handling ----------------- */  static osm_t *process_file(const char *filename) {
1043      osm_t *osm = NULL;
1044  void osm_free(icon_t **icon, osm_t *osm) {    xmlTextReaderPtr reader;
1045    if(!osm) return;    int ret;
1046    
1047      reader = xmlReaderForFile(filename, NULL, 0);
1048      if (reader != NULL) {
1049        ret = xmlTextReaderRead(reader);
1050        if(ret == 1) {
1051          char *name = (char*)xmlTextReaderConstName(reader);
1052          if(name && strcasecmp(name, "osm") == 0)
1053            osm = process_osm(reader);
1054        } else
1055          printf("file empty\n");
1056    
1057    if(osm->bounds)   osm_bounds_free(osm->bounds);      xmlFreeTextReader(reader);
1058    if(osm->user)     osm_users_free(osm->user);    } else {
1059    if(osm->way)      osm_ways_free(osm->way);      fprintf(stderr, "Unable to open %s\n", filename);
1060    if(osm->node)     osm_nodes_free(icon, osm->node);    }
1061    if(osm->relation) osm_relations_free(osm->relation);    return osm;
   g_free(osm);  
1062  }  }
1063    
1064  void osm_dump(osm_t *osm) {  /* ----------------------- end of stream parser ------------------- */
1065    osm_bounds_dump(osm->bounds);  
1066    osm_users_dump(osm->user);  #include <sys/time.h>
   osm_nodes_dump(osm->node);  
   osm_ways_dump(osm->way);  
   osm_relations_dump(osm->relation);  
 }  
1067    
1068  osm_t *osm_parse(char *filename) {  osm_t *osm_parse(char *filename) {
1069    xmlDoc *doc = NULL;  
1070      struct timeval start;
1071      gettimeofday(&start, NULL);
1072    
1073    LIBXML_TEST_VERSION;    LIBXML_TEST_VERSION;
1074    
1075    /* parse the file and get the DOM */    // use stream parser
1076    if ((doc = xmlReadFile(filename, NULL, 0)) == NULL) {    osm_t *osm = process_file(filename);
1077      xmlErrorPtr errP = xmlGetLastError();    xmlCleanupParser();
1078      errorf(NULL, "While parsing \"%s\":\n\n%s", filename, errP->message);  
1079      return NULL;    struct timeval end;
1080    }    gettimeofday(&end, NULL);
1081    
1082    return osm_parse_doc(doc);    printf("total parse time: %ldms\n",
1083             (end.tv_usec - start.tv_usec)/1000 +
1084             (end.tv_sec - start.tv_sec)*1000);
1085    
1086      return osm;
1087  }  }
1088    
1089  gboolean osm_sanity_check(GtkWidget *parent, osm_t *osm) {  gboolean osm_sanity_check(GtkWidget *parent, osm_t *osm) {
1090    if(!osm->bounds) {    if(!osm->bounds) {
1091      errorf(parent, _("Ivalid data in OSM file:\n"      errorf(parent, _("Invalid data in OSM file:\n"
1092                       "Boundary box missing!"));                       "Boundary box missing!"));
1093      return FALSE;      return FALSE;
1094    }    }
1095    if(!osm->node) {    if(!osm->node) {
1096      errorf(parent, _("Ivalid data in OSM file:\n"      errorf(parent, _("Invalid data in OSM file:\n"
1097                       "No drawable content found!"));                       "No drawable content found!"));
1098      return FALSE;      return FALSE;
1099    }    }
# Line 973  gboolean osm_node_has_value(node_t *node Line 1168  gboolean osm_node_has_value(node_t *node
1168  gboolean osm_node_has_tag(node_t *node) {  gboolean osm_node_has_tag(node_t *node) {
1169    tag_t *tag = node->tag;    tag_t *tag = node->tag;
1170    
1171      /* created_by tags don't count as real tags */
1172    if(tag && strcasecmp(tag->key, "created_by") == 0)    if(tag && strcasecmp(tag->key, "created_by") == 0)
1173      tag = tag->next;      tag = tag->next;
1174    
# Line 995  static void osm_generate_tags(tag_t *tag Line 1191  static void osm_generate_tags(tag_t *tag
1191    while(tag) {    while(tag) {
1192      /* make sure "created_by" tag contains our id */      /* make sure "created_by" tag contains our id */
1193      if(strcasecmp(tag->key, "created_by") == 0) {      if(strcasecmp(tag->key, "created_by") == 0) {
1194        g_free(tag->value);        if(strcasecmp(tag->value, PACKAGE " v" VERSION) != 0) {
1195        tag->value = g_strdup(PACKAGE " v" VERSION);          g_free(tag->value);
1196            tag->value = g_strdup(PACKAGE " v" VERSION);
1197          }
1198      }      }
1199    
1200      xmlNodePtr tag_node = xmlNewChild(node, NULL, BAD_CAST "tag", NULL);      xmlNodePtr tag_node = xmlNewChild(node, NULL, BAD_CAST "tag", NULL);
# Line 1007  static void osm_generate_tags(tag_t *tag Line 1205  static void osm_generate_tags(tag_t *tag
1205  }  }
1206    
1207  /* build xml representation for a way */  /* build xml representation for a way */
1208  char *osm_generate_xml(osm_t *osm, type_t type, void *item) {  static char *osm_generate_xml(osm_t *osm, item_id_t changeset,
1209                           type_t type, void *item) {
1210    char str[32];    char str[32];
1211    xmlChar *result = NULL;    xmlChar *result = NULL;
1212    int len = 0;    int len = 0;
# Line 1016  char *osm_generate_xml(osm_t *osm, type_ Line 1215  char *osm_generate_xml(osm_t *osm, type_
1215    
1216    xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");    xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
1217    xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "osm");    xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "osm");
1218    #ifndef API06
1219    xmlNewProp(root_node, BAD_CAST "version", BAD_CAST "0.5");    xmlNewProp(root_node, BAD_CAST "version", BAD_CAST "0.5");
1220    xmlNewProp(root_node, BAD_CAST "generator", BAD_CAST PACKAGE " V" VERSION);    xmlNewProp(root_node, BAD_CAST "generator", BAD_CAST PACKAGE " v" VERSION);
1221    #endif
1222    xmlDocSetRootElement(doc, root_node);    xmlDocSetRootElement(doc, root_node);
1223    
1224    switch(type) {    switch(type) {
# Line 1031  char *osm_generate_xml(osm_t *osm, type_ Line 1232  char *osm_generate_xml(osm_t *osm, type_
1232          snprintf(str, sizeof(str), "%u", (unsigned)node->id);          snprintf(str, sizeof(str), "%u", (unsigned)node->id);
1233          xmlNewProp(node_node, BAD_CAST "id", BAD_CAST str);          xmlNewProp(node_node, BAD_CAST "id", BAD_CAST str);
1234        }        }
1235        g_ascii_dtostr(str, sizeof(str), node->pos.lat);  #ifdef API06
1236          snprintf(str, sizeof(str), "%u", (unsigned)node->version);
1237          xmlNewProp(node_node, BAD_CAST "version", BAD_CAST str);
1238          snprintf(str, sizeof(str), "%u", (unsigned)changeset);
1239          xmlNewProp(node_node, BAD_CAST "changeset", BAD_CAST str);
1240    #endif
1241          g_ascii_formatd(str, sizeof(str), LL_FORMAT, node->pos.lat);
1242        xmlNewProp(node_node, BAD_CAST "lat", BAD_CAST str);        xmlNewProp(node_node, BAD_CAST "lat", BAD_CAST str);
1243        g_ascii_dtostr(str, sizeof(str), node->pos.lon);        g_ascii_formatd(str, sizeof(str), LL_FORMAT, node->pos.lon);
1244        xmlNewProp(node_node, BAD_CAST "lon", BAD_CAST str);        xmlNewProp(node_node, BAD_CAST "lon", BAD_CAST str);
1245        osm_generate_tags(node->tag, node_node);        osm_generate_tags(node->tag, node_node);
1246      }      }
# Line 1045  char *osm_generate_xml(osm_t *osm, type_ Line 1252  char *osm_generate_xml(osm_t *osm, type_
1252        xmlNodePtr way_node = xmlNewChild(root_node, NULL, BAD_CAST "way", NULL);        xmlNodePtr way_node = xmlNewChild(root_node, NULL, BAD_CAST "way", NULL);
1253        snprintf(str, sizeof(str), "%u", (unsigned)way->id);        snprintf(str, sizeof(str), "%u", (unsigned)way->id);
1254        xmlNewProp(way_node, BAD_CAST "id", BAD_CAST str);        xmlNewProp(way_node, BAD_CAST "id", BAD_CAST str);
1255    #ifdef API06
1256          snprintf(str, sizeof(str), "%u", (unsigned)way->version);
1257          xmlNewProp(way_node, BAD_CAST "version", BAD_CAST str);
1258          snprintf(str, sizeof(str), "%u", (unsigned)changeset);
1259          xmlNewProp(way_node, BAD_CAST "changeset", BAD_CAST str);
1260    #endif
1261    
1262        node_chain_t *node_chain = way->node_chain;        node_chain_t *node_chain = way->node_chain;
1263        while(node_chain) {        while(node_chain) {
1264          xmlNodePtr nd_node = xmlNewChild(way_node, NULL, BAD_CAST "nd", NULL);          xmlNodePtr nd_node = xmlNewChild(way_node, NULL, BAD_CAST "nd", NULL);
1265          char *str = g_strdup_printf("%ld", node_chain->node->id);          char *str = g_strdup_printf(ITEM_ID_FORMAT, node_chain->node->id);
1266          xmlNewProp(nd_node, BAD_CAST "ref", BAD_CAST str);          xmlNewProp(nd_node, BAD_CAST "ref", BAD_CAST str);
1267          g_free(str);          g_free(str);
1268          node_chain = node_chain->next;          node_chain = node_chain->next;
# Line 1066  char *osm_generate_xml(osm_t *osm, type_ Line 1279  char *osm_generate_xml(osm_t *osm, type_
1279                                          BAD_CAST "relation", NULL);                                          BAD_CAST "relation", NULL);
1280        snprintf(str, sizeof(str), "%u", (unsigned)relation->id);        snprintf(str, sizeof(str), "%u", (unsigned)relation->id);
1281        xmlNewProp(rel_node, BAD_CAST "id", BAD_CAST str);        xmlNewProp(rel_node, BAD_CAST "id", BAD_CAST str);
1282    #ifdef API06
1283          snprintf(str, sizeof(str), "%u", (unsigned)relation->version);
1284          xmlNewProp(rel_node, BAD_CAST "version", BAD_CAST str);
1285          snprintf(str, sizeof(str), "%u", (unsigned)changeset);
1286          xmlNewProp(rel_node, BAD_CAST "changeset", BAD_CAST str);
1287    #endif
1288    
1289        member_t *member = relation->member;        member_t *member = relation->member;
1290        while(member) {        while(member) {
1291          xmlNodePtr m_node = xmlNewChild(rel_node,NULL,BAD_CAST "member", NULL);          xmlNodePtr m_node = xmlNewChild(rel_node,NULL,BAD_CAST "member", NULL);
1292          char *str = NULL;          char *str = NULL;
1293    
1294          switch(member->type) {          switch(member->object.type) {
1295          case NODE:          case NODE:
1296            xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "node");            xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "node");
1297            str = g_strdup_printf("%ld", member->node->id);            str = g_strdup_printf(ITEM_ID_FORMAT, member->object.node->id);
1298            break;            break;
1299    
1300          case WAY:          case WAY:
1301            xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "way");            xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "way");
1302            str = g_strdup_printf("%ld", member->way->id);            str = g_strdup_printf(ITEM_ID_FORMAT, member->object.way->id);
1303            break;            break;
1304    
1305          case RELATION:          case RELATION:
1306            xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "relation");            xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "relation");
1307            str = g_strdup_printf("%ld", member->relation->id);            str = g_strdup_printf(ITEM_ID_FORMAT, member->object.relation->id);
1308            break;            break;
1309    
1310          default:          default:
# Line 1125  char *osm_generate_xml(osm_t *osm, type_ Line 1344  char *osm_generate_xml(osm_t *osm, type_
1344  }  }
1345    
1346  /* build xml representation for a node */  /* build xml representation for a node */
1347  char *osm_generate_xml_node(osm_t *osm, node_t *node) {  char *osm_generate_xml_node(osm_t *osm, item_id_t changeset, node_t *node) {
1348    return osm_generate_xml(osm, NODE, node);    return osm_generate_xml(osm, changeset, NODE, node);
1349  }  }
1350    
1351  /* build xml representation for a way */  /* build xml representation for a way */
1352  char *osm_generate_xml_way(osm_t *osm, way_t *way) {  char *osm_generate_xml_way(osm_t *osm, item_id_t changeset, way_t *way) {
1353    return osm_generate_xml(osm, WAY, way);    return osm_generate_xml(osm, changeset, WAY, way);
1354  }  }
1355    
1356  /* build xml representation for a relation */  /* build xml representation for a relation */
1357  char *osm_generate_xml_relation(osm_t *osm, relation_t *relation) {  char *osm_generate_xml_relation(osm_t *osm, item_id_t changeset,
1358    return osm_generate_xml(osm, RELATION, relation);                                  relation_t *relation) {
1359      return osm_generate_xml(osm, changeset, RELATION, relation);
1360    }
1361    
1362    /* build xml representation for a changeset */
1363    char *osm_generate_xml_changeset(osm_t *osm, char *comment) {
1364      xmlChar *result = NULL;
1365      int len = 0;
1366    
1367      /* tags for this changeset */
1368      tag_t tag_comment = {
1369        .key = "comment", .value = comment, .next = NULL };
1370      tag_t tag_creator = {
1371        .key = "created_by", .value = PACKAGE " v" VERSION, .next = &tag_comment };
1372    
1373      LIBXML_TEST_VERSION;
1374    
1375      xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
1376      xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "osm");
1377      xmlDocSetRootElement(doc, root_node);
1378    
1379      xmlNodePtr cs_node = xmlNewChild(root_node, NULL, BAD_CAST "changeset", NULL);
1380      osm_generate_tags(&tag_creator, cs_node);
1381    
1382      xmlDocDumpFormatMemoryEnc(doc, &result, &len, "UTF-8", 1);
1383      xmlFreeDoc(doc);
1384      xmlCleanupParser();
1385    
1386      //  puts("xml encoding result:");
1387      //  puts((char*)result);
1388    
1389      return (char*)result;
1390  }  }
1391    
1392    
1393    /* the following three functions are eating much CPU power */
1394    /* as they search the objects lists. Hashing is supposed to help */
1395  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) {
1396      if(id > 0 && osm->node_hash) {
1397        // use hash table if present
1398        hash_item_t *item = osm->node_hash->hash[ID2HASH(id)];
1399        while(item) {
1400          if(item->data.node->id == id)
1401            return item->data.node;
1402    
1403          item = item->next;
1404        }
1405      }
1406    
1407      /* use linear search if no hash tables are present or search in hash table failed */
1408    node_t *node = osm->node;    node_t *node = osm->node;
1409    while(node) {    while(node) {
1410      if(node->id == id)      if(node->id == id)
1411        return node;        return node;
1412    
1413      node = node->next;      node = node->next;
1414    }    }
1415    
1416    return NULL;    return NULL;
1417  }  }
1418    
1419  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) {
1420      if(id > 0 && osm->way_hash) {
1421        // use hash table if present
1422        hash_item_t *item = osm->way_hash->hash[ID2HASH(id)];
1423        while(item) {
1424          if(item->data.way->id == id)
1425            return item->data.way;
1426    
1427          item = item->next;
1428        }
1429      }
1430    
1431      /* use linear search if no hash tables are present or search on hash table failed */
1432    way_t *way = osm->way;    way_t *way = osm->way;
1433    while(way) {    while(way) {
1434      if(way->id == id)      if(way->id == id)
1435        return way;        return way;
1436    
1437      way = way->next;      way = way->next;
1438    }    }
1439    
1440    return NULL;    return NULL;
1441  }  }
1442    
1443  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) {
1444      // use linear search
1445    relation_t *relation = osm->relation;    relation_t *relation = osm->relation;
1446    while(relation) {    while(relation) {
1447      if(relation->id == id)      if(relation->id == id)
# Line 1219  item_id_t osm_new_node_id(osm_t *osm) { Line 1499  item_id_t osm_new_node_id(osm_t *osm) {
1499    return 0;    return 0;
1500  }  }
1501    
1502    item_id_t osm_new_relation_id(osm_t *osm) {
1503      item_id_t id = -1;
1504    
1505      while(TRUE) {
1506        gboolean found = FALSE;
1507        relation_t *relation = osm->relation;
1508        while(relation) {
1509          if(relation->id == id)
1510            found = TRUE;
1511    
1512          relation = relation->next;
1513        }
1514    
1515        /* no such id so far -> use it */
1516        if(!found) return id;
1517    
1518        id--;
1519      }
1520      g_assert(0);
1521      return 0;
1522    }
1523    
1524  node_t *osm_node_new(osm_t *osm, gint x, gint y) {  node_t *osm_node_new(osm_t *osm, gint x, gint y) {
1525    printf("Creating new node\n");    printf("Creating new node\n");
1526    
1527    node_t *node = g_new0(node_t, 1);    node_t *node = g_new0(node_t, 1);
1528      node->version = 1;
1529    node->lpos.x = x;    node->lpos.x = x;
1530    node->lpos.y = y;    node->lpos.y = y;
1531    node->visible = TRUE;    node->visible = TRUE;
1532    node->time = time(NULL);    node->time = time(NULL);
1533    
1534    #ifndef API06
1535    /* add created_by tag */    /* add created_by tag */
1536    node->tag = g_new0(tag_t, 1);    node->tag = g_new0(tag_t, 1);
1537    node->tag->key = g_strdup("created_by");    node->tag->key = g_strdup("created_by");
1538    node->tag->value = g_strdup(PACKAGE " v" VERSION);    node->tag->value = g_strdup(PACKAGE " v" VERSION);
1539    #endif
1540    
1541    /* convert screen position back to ll */    /* convert screen position back to ll */
1542    lpos2pos(osm->bounds, &node->lpos, &node->pos);    lpos2pos(osm->bounds, &node->lpos, &node->pos);
# Line 1255  void osm_node_attach(osm_t *osm, node_t Line 1560  void osm_node_attach(osm_t *osm, node_t
1560    *lnode = node;    *lnode = node;
1561  }  }
1562    
1563    void osm_node_restore(osm_t *osm, node_t *node) {
1564      printf("Restoring node\n");
1565    
1566      /* attach to end of node list */
1567      node_t **lnode = &osm->node;
1568      while(*lnode) lnode = &(*lnode)->next;
1569      *lnode = node;
1570    }
1571    
1572  way_t *osm_way_new(void) {  way_t *osm_way_new(void) {
1573    printf("Creating new way\n");    printf("Creating new way\n");
1574    
1575    way_t *way = g_new0(way_t, 1);    way_t *way = g_new0(way_t, 1);
1576      way->version = 1;
1577    way->visible = TRUE;    way->visible = TRUE;
1578    way->flags = OSM_FLAG_NEW;    way->flags = OSM_FLAG_NEW;
1579    way->time = time(NULL);    way->time = time(NULL);
1580    
1581    #ifndef API06
1582    /* add created_by tag */    /* add created_by tag */
1583    way->tag = g_new0(tag_t, 1);    way->tag = g_new0(tag_t, 1);
1584    way->tag->key = g_strdup("created_by");    way->tag->key = g_strdup("created_by");
1585    way->tag->value = g_strdup(PACKAGE " v" VERSION);    way->tag->value = g_strdup(PACKAGE " v" VERSION);
1586    #endif
1587    
1588    return way;    return way;
1589  }  }
# Line 1291  way_chain_t *osm_node_delete(osm_t *osm, Line 1608  way_chain_t *osm_node_delete(osm_t *osm,
1608    
1609    /* new nodes aren't stored on the server and are just deleted permanently */    /* new nodes aren't stored on the server and are just deleted permanently */
1610    if(node->flags & OSM_FLAG_NEW) {    if(node->flags & OSM_FLAG_NEW) {
1611      printf("About to delete NEW node #%ld -> force permanent delete\n",      printf("About to delete NEW node #" ITEM_ID_FORMAT
1612             node->id);             " -> force permanent delete\n", node->id);
1613      permanently = TRUE;      permanently = TRUE;
1614    }    }
1615    
# Line 1328  way_chain_t *osm_node_delete(osm_t *osm, Line 1645  way_chain_t *osm_node_delete(osm_t *osm,
1645    }    }
1646    
1647    if(!permanently) {    if(!permanently) {
1648      printf("mark node #%ld as deleted\n", node->id);      printf("mark node #" ITEM_ID_FORMAT " as deleted\n", node->id);
1649      node->flags |= OSM_FLAG_DELETED;      node->flags |= OSM_FLAG_DELETED;
1650    } else {    } else {
1651      printf("permanently delete node #%ld\n", node->id);      printf("permanently delete node #" ITEM_ID_FORMAT "\n", node->id);
1652    
1653      /* remove it from the chain */      /* remove it from the chain */
1654      node_t **cnode = &osm->node;      node_t **cnode = &osm->node;
# Line 1372  relation_chain_t *osm_node_to_relation(o Line 1689  relation_chain_t *osm_node_to_relation(o
1689    
1690      member_t *member = relation->member;      member_t *member = relation->member;
1691      while(member) {      while(member) {
1692        switch(member->type) {        switch(member->object.type) {
1693        case NODE:        case NODE:
1694          /* nodes are checked directly */          /* nodes are checked directly */
1695          if(member->node == node)          if(member->object.node == node)
1696            is_member = TRUE;            is_member = TRUE;
1697          break;          break;
1698    
1699        case WAY: {        case WAY: {
1700          /* ways have to be checked for the nodes they consist of */          /* ways have to be checked for the nodes they consist of */
1701          node_chain_t *chain = member->way->node_chain;          node_chain_t *chain = member->object.way->node_chain;
1702          while(chain && !is_member) {          while(chain && !is_member) {
1703            if(chain->node == node)            if(chain->node == node)
1704              is_member = TRUE;              is_member = TRUE;
# Line 1419  relation_chain_t *osm_way_to_relation(os Line 1736  relation_chain_t *osm_way_to_relation(os
1736    
1737      member_t *member = relation->member;      member_t *member = relation->member;
1738      while(member) {      while(member) {
1739        switch(member->type) {        switch(member->object.type) {
1740        case WAY: {        case WAY: {
1741          /* ways can be check directly */          /* ways can be check directly */
1742          if(member->way == way)          if(member->object.way == way)
1743            is_member = TRUE;            is_member = TRUE;
1744        } break;        } break;
1745    
# Line 1468  way_chain_t *osm_node_to_way(osm_t *osm, Line 1785  way_chain_t *osm_node_to_way(osm_t *osm,
1785        cur_chain = &((*cur_chain)->next);        cur_chain = &((*cur_chain)->next);
1786      }      }
1787    
1788      way = way->next;       way = way->next;
1789    }    }
1790    
1791    return chain;    return chain;
# Line 1484  gboolean osm_position_within_bounds(osm_ Line 1801  gboolean osm_position_within_bounds(osm_
1801  /* be deleted */  /* be deleted */
1802  void osm_node_remove_from_relation(osm_t *osm, node_t *node) {  void osm_node_remove_from_relation(osm_t *osm, node_t *node) {
1803    relation_t *relation = osm->relation;    relation_t *relation = osm->relation;
1804    printf("removing node #%ld from all relations:\n", node->id);    printf("removing node #" ITEM_ID_FORMAT " from all relations:\n", node->id);
1805    
1806    while(relation) {    while(relation) {
1807      member_t **member = &relation->member;      member_t **member = &relation->member;
1808      while(*member) {      while(*member) {
1809        if(((*member)->type == NODE) &&        if(((*member)->object.type == NODE) &&
1810           ((*member)->node == node)) {           ((*member)->object.node == node)) {
1811    
1812          printf("  from relation #%ld\n", relation->id);          printf("  from relation #" ITEM_ID_FORMAT "\n", relation->id);
1813    
1814          member_t *cur = *member;          member_t *cur = *member;
1815          *member = (*member)->next;          *member = (*member)->next;
# Line 1509  void osm_node_remove_from_relation(osm_t Line 1826  void osm_node_remove_from_relation(osm_t
1826  /* remove the given way from all relations */  /* remove the given way from all relations */
1827  void osm_way_remove_from_relation(osm_t *osm, way_t *way) {  void osm_way_remove_from_relation(osm_t *osm, way_t *way) {
1828    relation_t *relation = osm->relation;    relation_t *relation = osm->relation;
1829    printf("removing way #%ld from all relations:\n", way->id);    printf("removing way #" ITEM_ID_FORMAT " from all relations:\n", way->id);
1830    
1831    while(relation) {    while(relation) {
1832      member_t **member = &relation->member;      member_t **member = &relation->member;
1833      while(*member) {      while(*member) {
1834        if(((*member)->type == WAY) &&        if(((*member)->object.type == WAY) &&
1835           ((*member)->way == way)) {           ((*member)->object.way == way)) {
1836    
1837          printf("  from relation #%ld\n", relation->id);          printf("  from relation #" ITEM_ID_FORMAT "\n", relation->id);
1838    
1839          member_t *cur = *member;          member_t *cur = *member;
1840          *member = (*member)->next;          *member = (*member)->next;
# Line 1531  void osm_way_remove_from_relation(osm_t Line 1848  void osm_way_remove_from_relation(osm_t
1848    }    }
1849  }  }
1850    
1851    relation_t *osm_relation_new(void) {
1852      printf("Creating new relation\n");
1853    
1854      relation_t *relation = g_new0(relation_t, 1);
1855      relation->version = 1;
1856      relation->visible = TRUE;
1857      relation->flags = OSM_FLAG_NEW;
1858      relation->time = time(NULL);
1859    
1860    #ifndef API06
1861      /* add created_by tag */
1862      relation->tag = g_new0(tag_t, 1);
1863      relation->tag->key = g_strdup("created_by");
1864      relation->tag->value = g_strdup(PACKAGE " v" VERSION);
1865    #endif
1866    
1867      return relation;
1868    }
1869    
1870    void osm_relation_attach(osm_t *osm, relation_t *relation) {
1871      printf("Attaching relation\n");
1872    
1873      relation->id = osm_new_relation_id(osm);
1874      relation->flags = OSM_FLAG_NEW;
1875    
1876      /* attach to end of relation list */
1877      relation_t **lrelation = &osm->relation;
1878      while(*lrelation) lrelation = &(*lrelation)->next;
1879      *lrelation = relation;
1880    }
1881    
1882    
1883  void osm_way_delete(osm_t *osm, icon_t **icon,  void osm_way_delete(osm_t *osm, icon_t **icon,
1884                      way_t *way, gboolean permanently) {                      way_t *way, gboolean permanently) {
1885    
1886    /* new ways aren't stored on the server and are just deleted permanently */    /* new ways aren't stored on the server and are just deleted permanently */
1887    if(way->flags & OSM_FLAG_NEW) {    if(way->flags & OSM_FLAG_NEW) {
1888      printf("About to delete NEW way #%ld -> force permanent delete\n",      printf("About to delete NEW way #" ITEM_ID_FORMAT
1889             way->id);             " -> force permanent delete\n", way->id);
1890      permanently = TRUE;      permanently = TRUE;
1891    }    }
1892    
# Line 1546  void osm_way_delete(osm_t *osm, icon_t * Line 1895  void osm_way_delete(osm_t *osm, icon_t *
1895    while(*chain) {    while(*chain) {
1896    
1897      (*chain)->node->ways--;      (*chain)->node->ways--;
1898      printf("checking node #%ld (still used by %d)\n",      printf("checking node #" ITEM_ID_FORMAT " (still used by %d)\n",
1899             (*chain)->node->id, (*chain)->node->ways);             (*chain)->node->id, (*chain)->node->ways);
1900    
1901      /* this node must only be part of this way */      /* this node must only be part of this way */
# Line 1572  void osm_way_delete(osm_t *osm, icon_t * Line 1921  void osm_way_delete(osm_t *osm, icon_t *
1921    way->node_chain = NULL;    way->node_chain = NULL;
1922    
1923    if(!permanently) {    if(!permanently) {
1924      printf("mark way #%ld as deleted\n", way->id);      printf("mark way #" ITEM_ID_FORMAT " as deleted\n", way->id);
1925      way->flags |= OSM_FLAG_DELETED;      way->flags |= OSM_FLAG_DELETED;
1926    } else {    } else {
1927      printf("permanently delete way #%ld\n", way->id);      printf("permanently delete way #" ITEM_ID_FORMAT "\n", way->id);
1928    
1929      /* remove it from the chain */      /* remove it from the chain */
1930      way_t **cway = &osm->way;      way_t **cway = &osm->way;
# Line 1594  void osm_way_delete(osm_t *osm, icon_t * Line 1943  void osm_way_delete(osm_t *osm, icon_t *
1943    }    }
1944  }  }
1945    
1946  void osm_way_revert(way_t *way) {  void osm_relation_delete(osm_t *osm, relation_t *relation,
1947                             gboolean permanently) {
1948    
1949      /* new relations aren't stored on the server and are just */
1950      /* deleted permanently */
1951      if(relation->flags & OSM_FLAG_NEW) {
1952        printf("About to delete NEW relation #" ITEM_ID_FORMAT
1953               " -> force permanent delete\n", relation->id);
1954        permanently = TRUE;
1955      }
1956    
1957      /* the deletion of a relation doesn't affect the members as they */
1958      /* don't have any reference to the relation they are part of */
1959    
1960      if(!permanently) {
1961        printf("mark relation #" ITEM_ID_FORMAT " as deleted\n", relation->id);
1962        relation->flags |= OSM_FLAG_DELETED;
1963      } else {
1964        printf("permanently delete relation #" ITEM_ID_FORMAT "\n", relation->id);
1965    
1966        /* remove it from the chain */
1967        relation_t **crelation = &osm->relation;
1968        int found = 0;
1969    
1970        while(*crelation) {
1971          if(*crelation == relation) {
1972            found++;
1973            *crelation = (*crelation)->next;
1974    
1975            osm_relation_free(relation);
1976          } else
1977            crelation = &((*crelation)->next);
1978        }
1979        g_assert(found == 1);
1980      }
1981    }
1982    
1983    void osm_way_reverse(way_t *way) {
1984    node_chain_t *new = NULL;    node_chain_t *new = NULL;
1985    
1986    /* walk old chain first to last */    /* walk old chain first to last */
# Line 1612  void osm_way_revert(way_t *way) { Line 1998  void osm_way_revert(way_t *way) {
1998    way->node_chain = new;    way->node_chain = new;
1999  }  }
2000    
2001    static const char *DS_ONEWAY_FWD = "yes";
2002    static const char *DS_ONEWAY_REV = "-1";
2003    static const char *DS_LEFT_SUFFIX = ":left";
2004    static const char *DS_RIGHT_SUFFIX = ":right";
2005    
2006    /* Reverse direction-sensitive tags like "oneway". Marks the way as dirty if
2007     * anything is changed, and returns the number of flipped tags. */
2008    
2009    guint
2010    osm_way_reverse_direction_sensitive_tags (way_t *way) {
2011      tag_t *tag = way->tag;
2012      guint n_tags_altered = 0;
2013      while (tag != NULL) {
2014        char *lc_key = g_ascii_strdown(tag->key, -1);
2015        char *lc_value = g_ascii_strdown(tag->value, -1);
2016    
2017        if (strcmp(lc_key, "oneway") == 0) {
2018          // oneway={yes/true/1/-1} is unusual.
2019          // Favour "yes" and "-1".
2020          if ((strcmp(lc_value, DS_ONEWAY_FWD) == 0) ||
2021              (strcmp(lc_value, "true") == 0) ||
2022              (strcmp(lc_value, "1") == 0)) {
2023            g_free(tag->value);
2024            tag->value = g_strdup(DS_ONEWAY_REV);
2025            n_tags_altered++;
2026          }
2027          else if (strcmp(lc_value, DS_ONEWAY_REV) == 0) {
2028            g_free(tag->value);
2029            tag->value = g_strdup(DS_ONEWAY_FWD);
2030            n_tags_altered++;
2031          }
2032          else {
2033            printf("warning: unknown tag: %s=%s\n", tag->key, tag->value);
2034          }
2035        }
2036    
2037        // :left and :right suffixes
2038        else if (g_str_has_suffix(lc_key, DS_LEFT_SUFFIX)) {
2039          char *key_old = tag->key;
2040          char *lastcolon = rindex(key_old, ':');
2041          g_assert(lastcolon != NULL);
2042          *lastcolon = '\000';
2043          tag->key = g_strconcat(key_old, DS_RIGHT_SUFFIX, NULL);
2044          *lastcolon = ':';
2045          g_free(key_old);
2046          n_tags_altered++;
2047        }
2048        else if (g_str_has_suffix(lc_key, DS_RIGHT_SUFFIX)) {
2049          char *key_old = tag->key;
2050          char *lastcolon = rindex(key_old, ':');
2051          g_assert(lastcolon != NULL);
2052          *lastcolon = '\000';
2053          tag->key = g_strconcat(key_old, DS_LEFT_SUFFIX, NULL);
2054          *lastcolon = ':';
2055          g_free(key_old);
2056          n_tags_altered++;
2057        }
2058    
2059        g_free(lc_key);
2060        g_free(lc_value);
2061        tag = tag->next;
2062      }
2063      if (n_tags_altered > 0) {
2064        way->flags |= OSM_FLAG_DIRTY;
2065      }
2066      return n_tags_altered;
2067    }
2068    
2069    /* Reverse a way's role within relations where the role is direction-sensitive.
2070     * Returns the number of roles flipped, and marks any relations changed as
2071     * dirty. */
2072    
2073    static const char *DS_ROUTE_FORWARD = "forward";
2074    static const char *DS_ROUTE_REVERSE = "reverse";
2075    
2076    guint
2077    osm_way_reverse_direction_sensitive_roles(osm_t *osm, way_t *way) {
2078      relation_chain_t *rel_chain0, *rel_chain;
2079      rel_chain0 = rel_chain = osm_way_to_relation(osm, way);
2080      guint n_roles_flipped = 0;
2081    
2082      for (; rel_chain != NULL; rel_chain = rel_chain->next) {
2083        char *type = osm_tag_get_by_key(rel_chain->relation->tag, "type");
2084    
2085        // Route relations; http://wiki.openstreetmap.org/wiki/Relation:route
2086        if (strcasecmp(type, "route") == 0) {
2087    
2088          // First find the member corresponding to our way:
2089          member_t *member = rel_chain->relation->member;
2090          for (; member != NULL; member = member->next) {
2091            if (member->object.type == WAY) {
2092              if (member->object.way == way)
2093                break;
2094            }
2095            if (member->object.type == WAY_ID) {
2096              if (member->object.id == way->id)
2097                break;
2098            }
2099          }
2100          g_assert(member);  // osm_way_to_relation() broken?
2101    
2102          // Then flip its role if it's one of the direction-sensitive ones
2103          if (member->role == NULL) {
2104            printf("null role in route relation -> ignore\n");
2105          }
2106          else if (strcasecmp(member->role, DS_ROUTE_FORWARD) == 0) {
2107            g_free(member->role);
2108            member->role = g_strdup(DS_ROUTE_REVERSE);
2109            rel_chain->relation->flags |= OSM_FLAG_DIRTY;
2110            ++n_roles_flipped;
2111          }
2112          else if (strcasecmp(member->role, DS_ROUTE_REVERSE) == 0) {
2113            g_free(member->role);
2114            member->role = g_strdup(DS_ROUTE_FORWARD);
2115            rel_chain->relation->flags |= OSM_FLAG_DIRTY;
2116            ++n_roles_flipped;
2117          }
2118    
2119          // TODO: what about numbered stops? Guess we ignore them; there's no
2120          // consensus about whether they should be placed on the way or to one side
2121          // of it.
2122    
2123        }//if-route
2124    
2125    
2126      }
2127      if (rel_chain0) {
2128        g_free(rel_chain0);
2129      }
2130      return n_roles_flipped;
2131    }
2132    
2133  node_t *osm_way_get_first_node(way_t *way) {  node_t *osm_way_get_first_node(way_t *way) {
2134    node_chain_t *chain = way->node_chain;    node_chain_t *chain = way->node_chain;
2135    if(!chain) return NULL;    if(!chain) return NULL;
# Line 1669  tag_t *osm_tags_copy(tag_t *src_tag, gbo Line 2187  tag_t *osm_tags_copy(tag_t *src_tag, gbo
2187    
2188    return new_tags;    return new_tags;
2189  }  }
2190    
2191    /* return plain text of type */
2192    char *osm_object_type_string(object_t *object) {
2193      const struct { type_t type; char *name; } types[] = {
2194        { ILLEGAL,     "illegal" },
2195        { NODE,        "node" },
2196        { WAY,         "way" },
2197        { RELATION,    "relation" },
2198        { NODE_ID,     "node id" },
2199        { WAY_ID,      "way id" },
2200        { RELATION_ID, "relation id" },
2201        { 0, NULL }
2202      };
2203    
2204      int i;
2205      for(i=0;types[i].name;i++)
2206        if(object->type == types[i].type)
2207          return types[i].name;
2208    
2209      return NULL;
2210    }
2211    
2212    char *osm_object_string(object_t *object) {
2213      char *type_str = osm_object_type_string(object);
2214    
2215      if(!object)
2216        return g_strdup_printf("%s #<invalid>", type_str);
2217    
2218      switch(object->type) {
2219      case ILLEGAL:
2220        return g_strdup_printf("%s #<unspec>", type_str);
2221        break;
2222      case NODE:
2223        return g_strdup_printf("%s #" ITEM_ID_FORMAT, type_str, object->node->id);
2224        break;
2225      case WAY:
2226        return g_strdup_printf("%s #" ITEM_ID_FORMAT, type_str, object->way->id);
2227        break;
2228      case RELATION:
2229        return g_strdup_printf("%s #" ITEM_ID_FORMAT, type_str,
2230                               object->relation->id);
2231        break;
2232      case NODE_ID:
2233      case WAY_ID:
2234      case RELATION_ID:
2235        return g_strdup_printf("%s #" ITEM_ID_FORMAT, type_str, object->id);
2236        break;
2237      }
2238      return NULL;
2239    }
2240    
2241    char *osm_object_id_string(object_t *object) {
2242      if(!object) return NULL;
2243    
2244      switch(object->type) {
2245      case ILLEGAL:
2246        return NULL;
2247        break;
2248      case NODE:
2249        return g_strdup_printf("#"ITEM_ID_FORMAT, object->node->id);
2250        break;
2251      case WAY:
2252        return g_strdup_printf("#"ITEM_ID_FORMAT, object->way->id);
2253        break;
2254      case RELATION:
2255        return g_strdup_printf("#"ITEM_ID_FORMAT, object->relation->id);
2256        break;
2257      case NODE_ID:
2258      case WAY_ID:
2259      case RELATION_ID:
2260        return g_strdup_printf("#"ITEM_ID_FORMAT, object->id);
2261        break;
2262      }
2263      return NULL;
2264    }
2265    
2266    tag_t *osm_object_get_tags(object_t *object) {
2267      if(!object) return NULL;
2268    
2269      switch(object->type) {
2270      case ILLEGAL:
2271        return NULL;
2272        break;
2273      case NODE:
2274        return object->node->tag;
2275        break;
2276      case WAY:
2277        return object->way->tag;
2278        break;
2279      case RELATION:
2280        return object->relation->tag;
2281        break;
2282      case NODE_ID:
2283      case WAY_ID:
2284      case RELATION_ID:
2285        return NULL;
2286        break;
2287      }
2288      return NULL;
2289    }
2290    
2291    
2292    gint osm_relation_members_num(relation_t *relation) {
2293      gint num = 0;
2294      member_t *member = relation->member;
2295      while(member) {
2296        num++;
2297        member = member->next;
2298      }
2299      return num;
2300    }
2301    
2302    void osm_object_set_flags(object_t *object, int set, int clr) {
2303    
2304      switch(object->type) {
2305      case NODE:
2306        object->node->flags |=  set;
2307        object->node->flags &= ~clr;
2308        break;
2309    
2310      case WAY:
2311        object->way->flags |=  set;
2312        object->way->flags &= ~clr;
2313        break;
2314    
2315      case RELATION:
2316        object->relation->flags |=  set;
2317        object->relation->flags &= ~clr;
2318        break;
2319    
2320      default:
2321        g_assert(0);
2322        break;
2323      }
2324    }
2325    
2326    // vim:et:ts=8:sw=2:sts=2:ai

Legend:
Removed from v.1  
changed lines
  Added in v.161