Diff of /trunk/src/osm.c

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

revision 70 by harbaum, Wed Feb 11 19:43:03 2009 UTC revision 156 by harbaum, Wed Apr 1 12:47:35 2009 UTC
# Line 17  Line 17 
17   * along with OSM2Go.  If not, see <http://www.gnu.org/licenses/>.   * along with OSM2Go.  If not, see <http://www.gnu.org/licenses/>.
18   */   */
19    
 /* these defines select one of three possible xml parsers */  
 /* this is in fact selected depending on the plattform in the Makefile */  
 // #define OSM_DOM_PARSER  
 // #define OSM_STREAM_PARSER  
   
20  #include <stdio.h>  #include <stdio.h>
21  #include <stdlib.h>  #include <stdlib.h>
22  #include <string.h>  #include <string.h>
# Line 40  Line 35 
35  #error "Tree not enabled in libxml"  #error "Tree not enabled in libxml"
36  #endif  #endif
37    
 /* determine where a node/way/relation read from the osm file */  
 /* is inserted into the internal database */  
 // #define OSM_SORT_ID  
 #define OSM_SORT_LAST  
 // #define OSM_SORT_FIRST  
   
38  /* ------------------------- bounds handling --------------------- */  /* ------------------------- bounds handling --------------------- */
39    
40  static void osm_bounds_free(bounds_t *bounds) {  static void osm_bounds_free(bounds_t *bounds) {
# Line 58  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    
 #ifdef OSM_DOM_PARSER  
 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);  
   
     osm_bounds_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;  
 }  
 #endif  
   
50  /* ------------------------- user handling --------------------- */  /* ------------------------- user handling --------------------- */
51    
52  void osm_users_free(user_t *user) {  void osm_users_free(user_t *user) {
# Line 330  void osm_nodes_dump(node_t *node) { Line 246  void osm_nodes_dump(node_t *node) {
246    }    }
247  }  }
248    
 #ifdef OSM_DOM_PARSER  
 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);  
   }  
   
   /* append node to end of hash table if present */  
   if(osm->node_hash) {  
     hash_item_t **item = &osm->node_hash->hash[ID2HASH(node->id)];  
     while(*item) item = &(*item)->next;  
   
     *item = g_new0(hash_item_t, 1);  
     (*item)->data.node = node;  
   }  
   
   /* 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;  
 }  
 #endif  
   
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 502  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    
 #ifdef OSM_DOM_PARSER  
 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);  
   }  
   
   /* append way to end of hash table if present */  
   if(osm->way_hash) {  
     hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)];  
     while(*item) item = &(*item)->next;  
   
     *item = g_new0(hash_item_t, 1);  
     (*item)->data.way = way;  
   }  
   
   /* 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;  
 }  
 #endif  
   
353  /* ------------------- relation handling ------------------- */  /* ------------------- relation handling ------------------- */
354    
355  void osm_member_free(member_t *member) {  void osm_member_free(member_t *member) {
# Line 578  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 603  void osm_relations_dump(relation_t *rela Line 393  void osm_relations_dump(relation_t *rela
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 611  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 = %lu, 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 = %lu, 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 = %lu, role = %s\n",
418                 member->relation->id, member->role);                 member->object.relation->id, member->role);
419          break;          break;
420        }        }
421    
# Line 646  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_get_way_by_id(osm, id);        member->object.way = osm_get_way_by_id(osm, id);
459        if(!member->way) {        if(!member->object.way) {
460          member->type = WAY_ID;          member->object.type = WAY_ID;
461          member->id = id;          member->object.id = id;
462        }        }
463        break;        break;
464    
465      case NODE:      case NODE:
466        /* search matching node */        /* search matching node */
467        member->node = osm_get_node_by_id(osm, id);        member->object.node = osm_get_node_by_id(osm, id);
468        if(!member->node) {        if(!member->object.node) {
469          member->type = NODE_ID;          member->object.type = NODE_ID;
470          member->id = id;          member->object.id = id;
471        }        }
472        break;        break;
473    
474      case RELATION:      case RELATION:
475        /* search matching relation */        /* search matching relation */
476        member->relation = osm_get_relation_by_id(osm, id);        member->object.relation = osm_get_relation_by_id(osm, id);
477        if(!member->relation) {        if(!member->object.relation) {
478          member->type = NODE_ID;          member->object.type = NODE_ID;
479          member->id = id;          member->object.id = id;
480        }        }
481        break;        break;
482    
# Line 707  member_t *osm_parse_osm_relation_member( Line 497  member_t *osm_parse_osm_relation_member(
497    return member;    return member;
498  }  }
499    
 #ifdef OSM_DOM_PARSER  
 static relation_t *osm_parse_osm_relation(osm_t *osm,  
                           xmlDocPtr doc, xmlNode *a_node) {  
   xmlNode *cur_node = NULL;  
   
   /* allocate a new relation structure */  
   relation_t *relation = g_new0(relation_t, 1);  
   
   char *prop;  
   if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"id"))) {  
     relation->id = strtoul(prop, NULL, 10);  
     xmlFree(prop);  
   }  
   
   if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"user"))) {  
     relation->user = osm_user(osm, prop);  
     xmlFree(prop);  
   }  
   
   if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"visible"))) {  
     relation->visible = (strcasecmp(prop, "true") == 0);  
     xmlFree(prop);  
   }  
   
   if((prop = (char*)xmlGetProp(a_node, (unsigned char*)"timestamp"))) {  
     relation->time = convert_iso8601(prop);  
     xmlFree(prop);  
   }  
   
   /* scan for tags and attach a list of tags */  
   tag_t **tag = &relation->tag;  
   member_t **member = &relation->member;  
   
   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, "member") == 0) {  
         *member = osm_parse_osm_relation_member(osm, doc, cur_node);  
         if(*member) member = &((*member)->next);  
       } else  
         printf("found unhandled osm/node/%s\n", cur_node->name);  
     }  
   }  
   
   return relation;  
 }  
   
 /* ----------------------- generic xml handling -------------------------- */  
   
 /* parse osm entry */  
 static void osm_parse_osm(osm_t *osm, xmlDocPtr doc, xmlNode * a_node) {  
   xmlNode *cur_node = NULL;  
   
   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, "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  
   
           /* insert into chain */  
           new->next = *node;  
           *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  
   
           /* insert into chain */  
           new->next = *way;  
           *way = new;  
         }  
       } else if(strcasecmp((char*)cur_node->name, "relation") == 0) {  
         /* parse relation and attach it to chain */  
         relation_t *new = osm_parse_osm_relation(osm, doc, cur_node);  
         if(new) {  
           relation_t **relation = &osm->relation;  
   
 #ifdef OSM_SORT_ID  
           /* search chain of ways */  
           while(*relation && ((*relation)->id < new->id))  
             relation = &(*relation)->next;  
 #endif  
   
 #ifdef OSM_SORT_LAST  
           while(*relation) relation = &(*relation)->next;  
 #endif  
   
           /* insert into chain */  
           new->next = *relation;  
           *relation = new;  
         }  
       } else  
         printf("found unhandled osm/%s\n", cur_node->name);  
   
     }  
   }  
 }  
   
 /* parse root element and search for "osm" */  
 static osm_t *osm_parse_root(xmlDocPtr doc, xmlNode * a_node) {  
   osm_t *osm;  
   xmlNode *cur_node = NULL;  
   
   /* allocate memory to hold osm file description */  
   osm = g_new0(osm_t, 1);  
   osm->node_hash = g_new0(hash_table_t, 1);  
   osm->way_hash = g_new0(hash_table_t, 1);  
   
   for (cur_node = a_node; cur_node; cur_node = cur_node->next) {  
     if (cur_node->type == XML_ELEMENT_NODE) {  
       /* parse osm osm file ... */  
       if(strcasecmp((char*)cur_node->name, "osm") == 0)  
         osm_parse_osm(osm, doc, cur_node);  
       else  
         printf("found unhandled %s\n", cur_node->name);  
     }  
   }  
   
   return osm;  
 }  
   
 static osm_t *osm_parse_doc(xmlDocPtr doc) {  
   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();  
   
   return osm;  
 }  
 #endif  
   
500  /* ------------------ osm handling ----------------- */  /* ------------------ osm handling ----------------- */
501    
502  /* the two hash tables eat over 512kBytes memory and may thus be */  /* the two hash tables eat over 512kBytes memory and may thus be */
# Line 924  void osm_dump(osm_t *osm) { Line 543  void osm_dump(osm_t *osm) {
543    osm_relations_dump(osm->relation);    osm_relations_dump(osm->relation);
544  }  }
545    
546  #ifdef OSM_STREAM_PARSER  /* -------------------------- stream parser ------------------- */
 /* -------------------------- stream parser tests ------------------- */  
547    
548  #include <libxml/xmlreader.h>  #include <libxml/xmlreader.h>
549    
# Line 1218  static way_t *process_way(xmlTextReaderP Line 836  static way_t *process_way(xmlTextReaderP
836  static member_t *process_member(xmlTextReaderPtr reader, osm_t *osm) {  static member_t *process_member(xmlTextReaderPtr reader, osm_t *osm) {
837    char *prop;    char *prop;
838    member_t *member = g_new0(member_t, 1);    member_t *member = g_new0(member_t, 1);
839    member->type = ILLEGAL;    member->object.type = ILLEGAL;
840    
841    if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "type"))) {    if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "type"))) {
842      if(strcasecmp(prop, "way") == 0)           member->type = WAY;      if(strcasecmp(prop, "way") == 0)           member->object.type = WAY;
843      else if(strcasecmp(prop, "node") == 0)     member->type = NODE;      else if(strcasecmp(prop, "node") == 0)     member->object.type = NODE;
844      else if(strcasecmp(prop, "relation") == 0) member->type = RELATION;      else if(strcasecmp(prop, "relation") == 0) member->object.type = RELATION;
845      xmlFree(prop);      xmlFree(prop);
846    }    }
847    
848    if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref"))) {    if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref"))) {
849      item_id_t id = strtoul(prop, NULL, 10);      item_id_t id = strtoul(prop, NULL, 10);
850    
851      switch(member->type) {      switch(member->object.type) {
852      case ILLEGAL:      case ILLEGAL:
853        printf("Unable to store illegal type\n");        printf("Unable to store illegal type\n");
854        break;        break;
855    
856      case WAY:      case WAY:
857        /* search matching way */        /* search matching way */
858        member->way = osm_get_way_by_id(osm, id);        member->object.way = osm_get_way_by_id(osm, id);
859        if(!member->way) {        if(!member->object.way) {
860          member->type = WAY_ID;          member->object.type = WAY_ID;
861          member->id = id;          member->object.id = id;
862        }        }
863        break;        break;
864    
865      case NODE:      case NODE:
866        /* search matching node */        /* search matching node */
867        member->node = osm_get_node_by_id(osm, id);        member->object.node = osm_get_node_by_id(osm, id);
868        if(!member->node) {        if(!member->object.node) {
869          member->type = NODE_ID;          member->object.type = NODE_ID;
870          member->id = id;          member->object.id = id;
871        }        }
872        break;        break;
873    
874      case RELATION:      case RELATION:
875        /* search matching relation */        /* search matching relation */
876        member->relation = osm_get_relation_by_id(osm, id);        member->object.relation = osm_get_relation_by_id(osm, id);
877        if(!member->relation) {        if(!member->object.relation) {
878          member->type = NODE_ID;          member->object.type = NODE_ID;
879          member->id = id;          member->object.id = id;
880        }        }
881        break;        break;
882    
# Line 1425  static osm_t *process_file(const char *f Line 1043  static osm_t *process_file(const char *f
1043    return osm;    return osm;
1044  }  }
1045    
1046  /* ----------------------- end of stream parser tests ------------------- */  /* ----------------------- end of stream parser ------------------- */
 #endif  
1047    
1048  #ifdef OSM_QND_XML_PARSER  #include <sys/time.h>
 /* -------------------------- qnd-xml parser tests ------------------- */  
1049    
1050  #ifdef USE_FLOAT  osm_t *osm_parse(char *filename) {
 #define GET_PROP_POS(a,b,c) qnd_xml_get_prop_float(a, b, c)  
 #else  
 #define GET_PROP_POS(a,b,c) qnd_xml_get_prop_double(a, b, c)  
 #endif  
1051    
1052  gboolean osm_bounds_cb(qnd_xml_stack_t *stack,    struct timeval start;
1053                         qnd_xml_attribute_t *attributes, gpointer data) {    gettimeofday(&start, NULL);
1054    
1055    /* get parent pointer */    LIBXML_TEST_VERSION;
   osm_t *osm = (osm_t*)stack->prev->userdata[0];  
1056    
1057    if(osm->bounds) {    // use stream parser
1058      errorf(NULL, "Doubly defined bounds");    osm_t *osm = process_file(filename);
1059      return FALSE;    xmlCleanupParser();
   }  
1060    
1061    bounds_t *bounds = osm->bounds = g_new0(bounds_t, 1);    struct timeval end;
1062      gettimeofday(&end, NULL);
   bounds->ll_min.lat = bounds->ll_min.lon = NAN;  
   bounds->ll_max.lat = bounds->ll_max.lon = NAN;  
   
   GET_PROP_POS(attributes, "minlat", &bounds->ll_min.lat);  
   GET_PROP_POS(attributes, "minlon", &bounds->ll_min.lon);  
   GET_PROP_POS(attributes, "maxlat", &bounds->ll_max.lat);  
   GET_PROP_POS(attributes, "maxlon", &bounds->ll_max.lon);  
   
   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);  
   
     osm_bounds_free(bounds);  
     osm->bounds = NULL;  
     return FALSE;  
   }  
   
   
   /* 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 TRUE;  
 }  
   
 static gboolean osm_tag_cb(qnd_xml_stack_t *stack,  
                          qnd_xml_attribute_t *attributes, gpointer data) {  
   
   tag_t *tag = *(tag_t**)stack->prev->userdata[1] = g_new0(tag_t, 1);  
   
   tag->key = qnd_xml_get_prop_str(attributes, "k");  
   tag->value = qnd_xml_get_prop_str(attributes, "v");  
   
   if(!tag->key || !tag->value) {  
     printf("incomplete tag key/value %s/%s\n", tag->key, tag->value);  
     osm_tags_free(tag);  
     tag = NULL;  
   } else  
     stack->prev->userdata[1] = &tag->next;  
   
   return TRUE;  
 }  
   
 static gboolean osm_node_cb(qnd_xml_stack_t *stack,  
                      qnd_xml_attribute_t *attributes, gpointer data) {  
   
   osm_t *osm = (osm_t*)stack->prev->userdata[0];  
   
   /* allocate a new node structure. userdata[1] points to the current */  
   /* position a new node is to be stored */  
   node_t *node = *(node_t**)stack->prev->userdata[1] =  
     stack->userdata[0] = g_new0(node_t, 1);  
   stack->prev->userdata[1] = &node->next;  
   
   qnd_xml_get_prop_gulong(attributes, "id", &node->id);  
   GET_PROP_POS(attributes, "lat", &node->pos.lat);  
   GET_PROP_POS(attributes, "lon", &node->pos.lon);  
   node->user = osm_user(osm, qnd_xml_get_prop(attributes, "user"));  
   node->visible = qnd_xml_get_prop_is(attributes, "visible", "true");  
   node->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp"));  
   
   pos2lpos(osm->bounds, &node->pos, &node->lpos);  
   
   /* store current tag pointer in userdata for fast access to current tag */  
   stack->userdata[1] = &node->tag;  
   
   /* append node to end of hash table if present */  
   if(osm->node_hash) {  
     hash_item_t **item = &osm->node_hash->hash[ID2HASH(node->id)];  
     while(*item) item = &(*item)->next;  
   
     *item = g_new0(hash_item_t, 1);  
     (*item)->data.node = node;  
   }  
   
   return TRUE;  
 }  
   
 static gboolean osm_way_nd_cb(qnd_xml_stack_t *stack,  
                          qnd_xml_attribute_t *attributes, gpointer data) {  
   
   osm_t *osm = (osm_t*)stack->prev->prev->userdata[0];  
   
   item_id_t id;  
   if(qnd_xml_get_prop_gulong(attributes, "ref", &id)) {  
     /* allocate a new node_chain structure */  
     node_chain_t *node_chain = *(node_chain_t**)stack->prev->userdata[2] =  
       g_new0(node_chain_t, 1);  
   
     /* search matching node */  
     node_chain->node = osm_get_node_by_id(osm, id);  
     if(!node_chain->node) printf("Node id %lu not found\n", id);  
     else                  node_chain->node->ways++;  
   
     stack->prev->userdata[2] = &node_chain->next;  
   }  
   
   return TRUE;  
 }  
   
 gboolean osm_way_cb(qnd_xml_stack_t *stack,  
                     qnd_xml_attribute_t *attributes, gpointer data) {  
   
   osm_t *osm = (osm_t*)stack->prev->userdata[0];  
   
   /* allocate a new way structure. userdata[2] points to the current */  
   /* position a new way is to be stored in the way list */  
   way_t *way = *(way_t**)stack->prev->userdata[2] =  
     stack->userdata[0] = g_new0(way_t, 1);  
   stack->prev->userdata[2] = &way->next;  
   
   qnd_xml_get_prop_gulong(attributes, "id", &way->id);  
   way->user = osm_user(osm, qnd_xml_get_prop(attributes, "user"));  
   way->visible = qnd_xml_get_prop_is(attributes, "visible", "true");  
   way->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp"));  
   
   /* store current tag and node_chain pointers in userdata for fast */  
   /* access to current tag/node_chain entry */  
   stack->userdata[1] = &way->tag;  
   stack->userdata[2] = &way->node_chain;  
   
   /* append way to end of hash table if present */  
   if(osm->way_hash) {  
     hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)];  
     while(*item) item = &(*item)->next;  
   
     *item = g_new0(hash_item_t, 1);  
     (*item)->data.way = way;  
   }  
   
   return TRUE;  
 }  
   
 static gboolean osm_rel_member_cb(qnd_xml_stack_t *stack,  
                          qnd_xml_attribute_t *attributes, gpointer data) {  
   
   osm_t *osm = (osm_t*)stack->prev->prev->userdata[0];  
   
   member_t *member = *(member_t**)stack->prev->userdata[2] =  
     g_new0(member_t, 1);  
   stack->prev->userdata[2] = &member->next;  
   member->type = ILLEGAL;  
   
   char *type = qnd_xml_get_prop(attributes, "type");  
   if(type) {  
     if(strcasecmp(type, "way") == 0)           member->type = WAY;  
     else if(strcasecmp(type, "node") == 0)     member->type = NODE;  
     else if(strcasecmp(type, "relation") == 0) member->type = RELATION;  
   }  
   
   item_id_t id;  
   if(qnd_xml_get_prop_gulong(attributes, "ref", &id)) {  
     switch(member->type) {  
     case ILLEGAL:  
       printf("Unable to store illegal type\n");  
       break;  
   
     case WAY:  
       /* search matching way */  
       member->way = osm_get_way_by_id(osm, id);  
       if(!member->way) {  
         member->type = WAY_ID;  
         member->id = id;  
       }  
       break;  
   
     case NODE:  
       /* search matching node */  
       member->node = osm_get_node_by_id(osm, id);  
       if(!member->node) {  
         member->type = NODE_ID;  
         member->id = id;  
       }  
       break;  
   
     case RELATION:  
       /* search matching relation */  
       member->relation = osm_get_relation_by_id(osm, id);  
       if(!member->relation) {  
         member->type = NODE_ID;  
         member->id = id;  
       }  
       break;  
   
     case WAY_ID:  
     case NODE_ID:  
     case RELATION_ID:  
       break;  
     }  
   }  
   
   return TRUE;  
 }  
   
 gboolean osm_rel_cb(qnd_xml_stack_t *stack,  
                     qnd_xml_attribute_t *attributes, gpointer data) {  
   
   osm_t *osm = (osm_t*)stack->prev->userdata[0];  
   
   /* allocate a new relation structure. userdata[3] points to the current */  
   /* position a new relation is to be stored at in the relation list */  
   relation_t *relation = *(relation_t**)stack->prev->userdata[3] =  
     stack->userdata[0] = g_new0(relation_t, 1);  
   stack->prev->userdata[3] = &relation->next;  
   
   qnd_xml_get_prop_gulong(attributes, "id", &relation->id);  
   relation->user = osm_user(osm, qnd_xml_get_prop(attributes, "user"));  
   relation->visible = qnd_xml_get_prop_is(attributes, "visible", "true");  
   relation->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp"));  
   
   /* store current tag and member pointers in userdata for fast access */  
   /* to current tag and members in their chains */  
   stack->userdata[1] = &relation->tag;  
   stack->userdata[2] = &relation->member;  
   
   return TRUE;  
 }  
   
 gboolean osm_cb(qnd_xml_stack_t *stack,  
                 qnd_xml_attribute_t *attributes, gpointer data) {  
   
   g_assert(!stack->userdata[0]);  
   
   /* also set parents (roots) userdata as it's the parsers return value */  
   osm_t *osm = stack->prev->userdata[0] =  
     stack->userdata[0] = g_new0(osm_t, 1);  
   
   osm->node_hash = g_new0(hash_table_t, 1);  
   osm->way_hash = g_new0(hash_table_t, 1);  
   
   /* store direct pointers for faster list access */  
   /* (otherwise we'd have to search the end of the lists for every item */  
   /* to be attached) */  
   stack->userdata[1] = &osm->node;  
   stack->userdata[2] = &osm->way;  
   stack->userdata[3] = &osm->relation;  
   
   return TRUE;  
 }  
   
   
 /* these structures describe the content qnd_xml expects while parsing */  
 qnd_xml_entry_t osm_node_tag = { "tag", osm_tag_cb, QND_XML_LEAF };  
   
 qnd_xml_entry_t osm_way_tag = { "tag", osm_tag_cb, QND_XML_LEAF };  
 qnd_xml_entry_t osm_way_nd = { "nd", osm_way_nd_cb, QND_XML_LEAF };  
   
 qnd_xml_entry_t osm_rel_tag = { "tag", osm_tag_cb, QND_XML_LEAF };  
 qnd_xml_entry_t osm_rel_member = { "member", osm_rel_member_cb, QND_XML_LEAF };  
   
 qnd_xml_entry_t osm_bounds = { "bounds", osm_bounds_cb, QND_XML_LEAF };  
   
 qnd_xml_entry_t *node_children[] = { &osm_node_tag },  
   osm_node = { "node", osm_node_cb, QND_XML_CHILDREN(node_children) };  
   
 qnd_xml_entry_t *way_children[] = { &osm_way_tag, &osm_way_nd },  
   osm_way = { "way", osm_way_cb, QND_XML_CHILDREN(way_children) };  
   
 qnd_xml_entry_t *rel_children[] = { &osm_rel_tag, &osm_rel_member },  
   osm_rel = { "rel", osm_rel_cb, QND_XML_CHILDREN(rel_children) };  
   
 /* the osm element */  
 qnd_xml_entry_t *osm_children[] = {  
   &osm_bounds, &osm_node, &osm_way, &osm_rel };  
 qnd_xml_entry_t osm = { "osm", osm_cb, QND_XML_CHILDREN(osm_children) };  
   
 /* the root element */  
 qnd_xml_entry_t *root_children[] = { &osm };  
 qnd_xml_entry_t root = { "<root>", NULL, QND_XML_CHILDREN(root_children) };  
   
 // gcc `pkg-config --cflags --libs glib-2.0` -o qnd_xml qnd_xml.c  
   
   
   
 /* ----------------------- end of qnd-xml parser tests ------------------- */  
 #endif  
   
   
 #include <sys/time.h>  
   
 osm_t *osm_parse(char *filename) {  
   
   struct timeval start;  
   gettimeofday(&start, NULL);  
   
 #ifdef OSM_STREAM_PARSER  
   LIBXML_TEST_VERSION;  
   
   // use stream parser  
   osm_t *osm = process_file(filename);  
   xmlCleanupParser();  
 #endif  
   
 #ifdef OSM_DOM_PARSER  
   LIBXML_TEST_VERSION;  
   
   // parse into a tree  
   /* parse the file and get the DOM */  
   xmlDoc *doc = NULL;  
   if ((doc = xmlReadFile(filename, NULL, 0)) == NULL) {  
     xmlErrorPtr errP = xmlGetLastError();  
     errorf(NULL, "While parsing \"%s\":\n\n%s", filename, errP->message);  
     return NULL;  
   }  
   
   osm_t *osm = osm_parse_doc(doc);  
 #endif  
   
 #ifdef OSM_QND_XML_PARSER  
   osm_t *osm = NULL;  
   if(!(osm = qnd_xml_parse(filename, &root, NULL))) {  
     errorf(NULL, "While parsing \"%s\"", filename);  
     return NULL;  
   }  
 #endif  
   
   struct timeval end;  
   gettimeofday(&end, NULL);  
1063    
1064    printf("total parse time: %ldms\n",    printf("total parse time: %ldms\n",
1065           (end.tv_usec - start.tv_usec)/1000 +           (end.tv_usec - start.tv_usec)/1000 +
# Line 1937  char *osm_generate_xml(osm_t *osm, type_ Line 1208  char *osm_generate_xml(osm_t *osm, type_
1208          snprintf(str, sizeof(str), "%u", (unsigned)node->id);          snprintf(str, sizeof(str), "%u", (unsigned)node->id);
1209          xmlNewProp(node_node, BAD_CAST "id", BAD_CAST str);          xmlNewProp(node_node, BAD_CAST "id", BAD_CAST str);
1210        }        }
1211        g_ascii_dtostr(str, sizeof(str), node->pos.lat);        g_ascii_formatd(str, sizeof(str), LL_FORMAT, node->pos.lat);
1212        xmlNewProp(node_node, BAD_CAST "lat", BAD_CAST str);        xmlNewProp(node_node, BAD_CAST "lat", BAD_CAST str);
1213        g_ascii_dtostr(str, sizeof(str), node->pos.lon);        g_ascii_formatd(str, sizeof(str), LL_FORMAT, node->pos.lon);
1214        xmlNewProp(node_node, BAD_CAST "lon", BAD_CAST str);        xmlNewProp(node_node, BAD_CAST "lon", BAD_CAST str);
1215        osm_generate_tags(node->tag, node_node);        osm_generate_tags(node->tag, node_node);
1216      }      }
# Line 1978  char *osm_generate_xml(osm_t *osm, type_ Line 1249  char *osm_generate_xml(osm_t *osm, type_
1249          xmlNodePtr m_node = xmlNewChild(rel_node,NULL,BAD_CAST "member", NULL);          xmlNodePtr m_node = xmlNewChild(rel_node,NULL,BAD_CAST "member", NULL);
1250          char *str = NULL;          char *str = NULL;
1251    
1252          switch(member->type) {          switch(member->object.type) {
1253          case NODE:          case NODE:
1254            xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "node");            xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "node");
1255            str = g_strdup_printf("%ld", member->node->id);            str = g_strdup_printf("%ld", member->object.node->id);
1256            break;            break;
1257    
1258          case WAY:          case WAY:
1259            xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "way");            xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "way");
1260            str = g_strdup_printf("%ld", member->way->id);            str = g_strdup_printf("%ld", member->object.way->id);
1261            break;            break;
1262    
1263          case RELATION:          case RELATION:
1264            xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "relation");            xmlNewProp(m_node, BAD_CAST "type", BAD_CAST "relation");
1265            str = g_strdup_printf("%ld", member->relation->id);            str = g_strdup_printf("%ld", member->object.relation->id);
1266            break;            break;
1267    
1268          default:          default:
# Line 2154  item_id_t osm_new_node_id(osm_t *osm) { Line 1425  item_id_t osm_new_node_id(osm_t *osm) {
1425    return 0;    return 0;
1426  }  }
1427    
1428    item_id_t osm_new_relation_id(osm_t *osm) {
1429      item_id_t id = -1;
1430    
1431      while(TRUE) {
1432        gboolean found = FALSE;
1433        relation_t *relation = osm->relation;
1434        while(relation) {
1435          if(relation->id == id)
1436            found = TRUE;
1437    
1438          relation = relation->next;
1439        }
1440    
1441        /* no such id so far -> use it */
1442        if(!found) return id;
1443    
1444        id--;
1445      }
1446      g_assert(0);
1447      return 0;
1448    }
1449    
1450  node_t *osm_node_new(osm_t *osm, gint x, gint y) {  node_t *osm_node_new(osm_t *osm, gint x, gint y) {
1451    printf("Creating new node\n");    printf("Creating new node\n");
1452    
# Line 2316  relation_chain_t *osm_node_to_relation(o Line 1609  relation_chain_t *osm_node_to_relation(o
1609    
1610      member_t *member = relation->member;      member_t *member = relation->member;
1611      while(member) {      while(member) {
1612        switch(member->type) {        switch(member->object.type) {
1613        case NODE:        case NODE:
1614          /* nodes are checked directly */          /* nodes are checked directly */
1615          if(member->node == node)          if(member->object.node == node)
1616            is_member = TRUE;            is_member = TRUE;
1617          break;          break;
1618    
1619        case WAY: {        case WAY: {
1620          /* ways have to be checked for the nodes they consist of */          /* ways have to be checked for the nodes they consist of */
1621          node_chain_t *chain = member->way->node_chain;          node_chain_t *chain = member->object.way->node_chain;
1622          while(chain && !is_member) {          while(chain && !is_member) {
1623            if(chain->node == node)            if(chain->node == node)
1624              is_member = TRUE;              is_member = TRUE;
# Line 2363  relation_chain_t *osm_way_to_relation(os Line 1656  relation_chain_t *osm_way_to_relation(os
1656    
1657      member_t *member = relation->member;      member_t *member = relation->member;
1658      while(member) {      while(member) {
1659        switch(member->type) {        switch(member->object.type) {
1660        case WAY: {        case WAY: {
1661          /* ways can be check directly */          /* ways can be check directly */
1662          if(member->way == way)          if(member->object.way == way)
1663            is_member = TRUE;            is_member = TRUE;
1664        } break;        } break;
1665    
# Line 2433  void osm_node_remove_from_relation(osm_t Line 1726  void osm_node_remove_from_relation(osm_t
1726    while(relation) {    while(relation) {
1727      member_t **member = &relation->member;      member_t **member = &relation->member;
1728      while(*member) {      while(*member) {
1729        if(((*member)->type == NODE) &&        if(((*member)->object.type == NODE) &&
1730           ((*member)->node == node)) {           ((*member)->object.node == node)) {
1731    
1732          printf("  from relation #%ld\n", relation->id);          printf("  from relation #%ld\n", relation->id);
1733    
# Line 2458  void osm_way_remove_from_relation(osm_t Line 1751  void osm_way_remove_from_relation(osm_t
1751    while(relation) {    while(relation) {
1752      member_t **member = &relation->member;      member_t **member = &relation->member;
1753      while(*member) {      while(*member) {
1754        if(((*member)->type == WAY) &&        if(((*member)->object.type == WAY) &&
1755           ((*member)->way == way)) {           ((*member)->object.way == way)) {
1756    
1757          printf("  from relation #%ld\n", relation->id);          printf("  from relation #%ld\n", relation->id);
1758    
# Line 2475  void osm_way_remove_from_relation(osm_t Line 1768  void osm_way_remove_from_relation(osm_t
1768    }    }
1769  }  }
1770    
1771    relation_t *osm_relation_new(void) {
1772      printf("Creating new relation\n");
1773    
1774      relation_t *relation = g_new0(relation_t, 1);
1775      relation->visible = TRUE;
1776      relation->flags = OSM_FLAG_NEW;
1777      relation->time = time(NULL);
1778    
1779      /* add created_by tag */
1780      relation->tag = g_new0(tag_t, 1);
1781      relation->tag->key = g_strdup("created_by");
1782      relation->tag->value = g_strdup(PACKAGE " v" VERSION);
1783    
1784      return relation;
1785    }
1786    
1787    void osm_relation_attach(osm_t *osm, relation_t *relation) {
1788      printf("Attaching relation\n");
1789    
1790      relation->id = osm_new_relation_id(osm);
1791      relation->flags = OSM_FLAG_NEW;
1792    
1793      /* attach to end of relation list */
1794      relation_t **lrelation = &osm->relation;
1795      while(*lrelation) lrelation = &(*lrelation)->next;
1796      *lrelation = relation;
1797    }
1798    
1799    
1800  void osm_way_delete(osm_t *osm, icon_t **icon,  void osm_way_delete(osm_t *osm, icon_t **icon,
1801                      way_t *way, gboolean permanently) {                      way_t *way, gboolean permanently) {
1802    
# Line 2538  void osm_way_delete(osm_t *osm, icon_t * Line 1860  void osm_way_delete(osm_t *osm, icon_t *
1860    }    }
1861  }  }
1862    
1863  void osm_way_revert(way_t *way) {  void osm_relation_delete(osm_t *osm, relation_t *relation,
1864                             gboolean permanently) {
1865    
1866      /* new relations aren't stored on the server and are just */
1867      /* deleted permanently */
1868      if(relation->flags & OSM_FLAG_NEW) {
1869        printf("About to delete NEW relation #%ld -> force permanent delete\n",
1870               relation->id);
1871        permanently = TRUE;
1872      }
1873    
1874      /* the deletion of a relation doesn't affect the members as they */
1875      /* don't have any reference to the relation they are part of */
1876    
1877      if(!permanently) {
1878        printf("mark relation #%ld as deleted\n", relation->id);
1879        relation->flags |= OSM_FLAG_DELETED;
1880      } else {
1881        printf("permanently delete relation #%ld\n", relation->id);
1882    
1883        /* remove it from the chain */
1884        relation_t **crelation = &osm->relation;
1885        int found = 0;
1886    
1887        while(*crelation) {
1888          if(*crelation == relation) {
1889            found++;
1890            *crelation = (*crelation)->next;
1891    
1892            osm_relation_free(relation);
1893          } else
1894            crelation = &((*crelation)->next);
1895        }
1896        g_assert(found == 1);
1897      }
1898    }
1899    
1900    void osm_way_reverse(way_t *way) {
1901    node_chain_t *new = NULL;    node_chain_t *new = NULL;
1902    
1903    /* walk old chain first to last */    /* walk old chain first to last */
# Line 2556  void osm_way_revert(way_t *way) { Line 1915  void osm_way_revert(way_t *way) {
1915    way->node_chain = new;    way->node_chain = new;
1916  }  }
1917    
1918    static const char *DS_ONEWAY_FWD = "yes";
1919    static const char *DS_ONEWAY_REV = "-1";
1920    static const char *DS_LEFT_SUFFIX = ":left";
1921    static const char *DS_RIGHT_SUFFIX = ":right";
1922    
1923    /* Reverse direction-sensitive tags like "oneway". Marks the way as dirty if
1924     * anything is changed, and returns the number of flipped tags. */
1925    
1926    guint
1927    osm_way_reverse_direction_sensitive_tags (way_t *way) {
1928      tag_t *tag = way->tag;
1929      guint n_tags_altered = 0;
1930      while (tag != NULL) {
1931        char *lc_key = g_ascii_strdown(tag->key, -1);
1932        char *lc_value = g_ascii_strdown(tag->value, -1);
1933    
1934        if (strcmp(lc_key, "oneway") == 0) {
1935          // oneway={yes/true/1/-1} is unusual.
1936          // Favour "yes" and "-1".
1937          if ((strcmp(lc_value, DS_ONEWAY_FWD) == 0) ||
1938              (strcmp(lc_value, "true") == 0) ||
1939              (strcmp(lc_value, "1") == 0)) {
1940            g_free(tag->value);
1941            tag->value = g_strdup(DS_ONEWAY_REV);
1942            n_tags_altered++;
1943          }
1944          else if (strcmp(lc_value, DS_ONEWAY_REV) == 0) {
1945            g_free(tag->value);
1946            tag->value = g_strdup(DS_ONEWAY_FWD);
1947            n_tags_altered++;
1948          }
1949          else {
1950            printf("warning: unknown tag: %s=%s\n", tag->key, tag->value);
1951          }
1952        }
1953    
1954        // :left and :right suffixes
1955        else if (g_str_has_suffix(lc_key, DS_LEFT_SUFFIX)) {
1956          char *key_old = tag->key;
1957          char *lastcolon = rindex(key_old, ':');
1958          g_assert(lastcolon != NULL);
1959          *lastcolon = '\000';
1960          tag->key = g_strconcat(key_old, DS_RIGHT_SUFFIX, NULL);
1961          *lastcolon = ':';
1962          g_free(key_old);
1963          n_tags_altered++;
1964        }
1965        else if (g_str_has_suffix(lc_key, DS_RIGHT_SUFFIX)) {
1966          char *key_old = tag->key;
1967          char *lastcolon = rindex(key_old, ':');
1968          g_assert(lastcolon != NULL);
1969          *lastcolon = '\000';
1970          tag->key = g_strconcat(key_old, DS_LEFT_SUFFIX, NULL);
1971          *lastcolon = ':';
1972          g_free(key_old);
1973          n_tags_altered++;
1974        }
1975    
1976        g_free(lc_key);
1977        g_free(lc_value);
1978        tag = tag->next;
1979      }
1980      if (n_tags_altered > 0) {
1981        way->flags |= OSM_FLAG_DIRTY;
1982      }
1983      return n_tags_altered;
1984    }
1985    
1986    /* Reverse a way's role within relations where the role is direction-sensitive.
1987     * Returns the number of roles flipped, and marks any relations changed as
1988     * dirty. */
1989    
1990    static const char *DS_ROUTE_FORWARD = "forward";
1991    static const char *DS_ROUTE_REVERSE = "reverse";
1992    
1993    guint
1994    osm_way_reverse_direction_sensitive_roles(osm_t *osm, way_t *way) {
1995      relation_chain_t *rel_chain0, *rel_chain;
1996      rel_chain0 = rel_chain = osm_way_to_relation(osm, way);
1997      guint n_roles_flipped = 0;
1998    
1999      for (; rel_chain != NULL; rel_chain = rel_chain->next) {
2000        char *type = osm_tag_get_by_key(rel_chain->relation->tag, "type");
2001    
2002        // Route relations; http://wiki.openstreetmap.org/wiki/Relation:route
2003        if (strcasecmp(type, "route") == 0) {
2004    
2005          // First find the member corresponding to our way:
2006          member_t *member = rel_chain->relation->member;
2007          for (; member != NULL; member = member->next) {
2008            if (member->object.type == WAY) {
2009              if (member->object.way == way)
2010                break;
2011            }
2012            if (member->object.type == WAY_ID) {
2013              if (member->object.id == way->id)
2014                break;
2015            }
2016          }
2017          g_assert(member);  // osm_way_to_relation() broken?
2018    
2019          // Then flip its role if it's one of the direction-sensitive ones
2020          if (member->role == NULL) {
2021            printf("null role in route relation -> ignore\n");
2022          }
2023          else if (strcasecmp(member->role, DS_ROUTE_FORWARD) == 0) {
2024            g_free(member->role);
2025            member->role = g_strdup(DS_ROUTE_REVERSE);
2026            rel_chain->relation->flags |= OSM_FLAG_DIRTY;
2027            ++n_roles_flipped;
2028          }
2029          else if (strcasecmp(member->role, DS_ROUTE_REVERSE) == 0) {
2030            g_free(member->role);
2031            member->role = g_strdup(DS_ROUTE_FORWARD);
2032            rel_chain->relation->flags |= OSM_FLAG_DIRTY;
2033            ++n_roles_flipped;
2034          }
2035    
2036          // TODO: what about numbered stops? Guess we ignore them; there's no
2037          // consensus about whether they should be placed on the way or to one side
2038          // of it.
2039    
2040        }//if-route
2041    
2042    
2043      }
2044      if (rel_chain0) {
2045        g_free(rel_chain0);
2046      }
2047      return n_roles_flipped;
2048    }
2049    
2050  node_t *osm_way_get_first_node(way_t *way) {  node_t *osm_way_get_first_node(way_t *way) {
2051    node_chain_t *chain = way->node_chain;    node_chain_t *chain = way->node_chain;
2052    if(!chain) return NULL;    if(!chain) return NULL;
# Line 2615  tag_t *osm_tags_copy(tag_t *src_tag, gbo Line 2106  tag_t *osm_tags_copy(tag_t *src_tag, gbo
2106  }  }
2107    
2108  /* return plain text of type */  /* return plain text of type */
2109  char *osm_type_string(type_t type) {  char *osm_object_type_string(object_t *object) {
2110    const struct { type_t type; char *name; } types[] = {    const struct { type_t type; char *name; } types[] = {
2111      { ILLEGAL,     "illegal" },      { ILLEGAL,     "illegal" },
2112      { NODE,        "node" },      { NODE,        "node" },
# Line 2629  char *osm_type_string(type_t type) { Line 2120  char *osm_type_string(type_t type) {
2120    
2121    int i;    int i;
2122    for(i=0;types[i].name;i++)    for(i=0;types[i].name;i++)
2123      if(type == types[i].type)      if(object->type == types[i].type)
2124        return types[i].name;        return types[i].name;
2125    
2126    return NULL;    return NULL;
2127  }  }
2128    
2129  char *osm_object_string(type_t type, void *object) {  char *osm_object_string(object_t *object) {
2130    char *type_str = osm_type_string(type);    char *type_str = osm_object_type_string(object);
2131    
2132    if(!object)    if(!object)
2133      return g_strdup_printf("%s #<invalid>", type_str);      return g_strdup_printf("%s #<invalid>", type_str);
2134    
2135    switch(type) {    switch(object->type) {
2136    case ILLEGAL:    case ILLEGAL:
2137      return g_strdup_printf("%s #<unspec>", type_str);      return g_strdup_printf("%s #<unspec>", type_str);
2138      break;      break;
2139    case NODE:    case NODE:
2140      return g_strdup_printf("%s #%ld", type_str, ((node_t*)object)->id);      return g_strdup_printf("%s #%ld", type_str, object->node->id);
2141      break;      break;
2142    case WAY:    case WAY:
2143      return g_strdup_printf("%s #%ld", type_str, ((way_t*)object)->id);      return g_strdup_printf("%s #%ld", type_str, object->way->id);
2144      break;      break;
2145    case RELATION:    case RELATION:
2146      return g_strdup_printf("%s #%ld", type_str, ((relation_t*)object)->id);      return g_strdup_printf("%s #%ld", type_str, object->relation->id);
2147      break;      break;
2148    case NODE_ID:    case NODE_ID:
2149    case WAY_ID:    case WAY_ID:
2150    case RELATION_ID:    case RELATION_ID:
2151      return g_strdup_printf("%s #%ld", type_str, *((item_id_t*)object));      return g_strdup_printf("%s #%ld", type_str, object->id);
2152      break;      break;
2153    }    }
2154    return NULL;    return NULL;
2155  }  }
2156    
2157    char *osm_object_id_string(object_t *object) {
2158      if(!object) return NULL;
2159    
2160      switch(object->type) {
2161      case ILLEGAL:
2162        return NULL;
2163        break;
2164      case NODE:
2165        return g_strdup_printf("#%ld", object->node->id);
2166        break;
2167      case WAY:
2168        return g_strdup_printf("#%ld", object->way->id);
2169        break;
2170      case RELATION:
2171        return g_strdup_printf("#%ld", object->relation->id);
2172        break;
2173      case NODE_ID:
2174      case WAY_ID:
2175      case RELATION_ID:
2176        return g_strdup_printf("#%ld", object->id);
2177        break;
2178      }
2179      return NULL;
2180    }
2181    
2182    tag_t *osm_object_get_tags(object_t *object) {
2183      if(!object) return NULL;
2184    
2185      switch(object->type) {
2186      case ILLEGAL:
2187        return NULL;
2188        break;
2189      case NODE:
2190        return object->node->tag;
2191        break;
2192      case WAY:
2193        return object->way->tag;
2194        break;
2195      case RELATION:
2196        return object->relation->tag;
2197        break;
2198      case NODE_ID:
2199      case WAY_ID:
2200      case RELATION_ID:
2201        return NULL;
2202        break;
2203      }
2204      return NULL;
2205    }
2206    
2207    
2208    gint osm_relation_members_num(relation_t *relation) {
2209      gint num = 0;
2210      member_t *member = relation->member;
2211      while(member) {
2212        num++;
2213        member = member->next;
2214      }
2215      return num;
2216    }
2217    
2218    void osm_object_set_flags(object_t *object, int set, int clr) {
2219    
2220      switch(object->type) {
2221      case NODE:
2222        object->node->flags |=  set;
2223        object->node->flags &= ~clr;
2224        break;
2225    
2226      case WAY:
2227        object->way->flags |=  set;
2228        object->way->flags &= ~clr;
2229        break;
2230    
2231      case RELATION:
2232        object->relation->flags |=  set;
2233        object->relation->flags &= ~clr;
2234        break;
2235    
2236      default:
2237        g_assert(0);
2238        break;
2239      }
2240    }
2241    
2242  // vim:et:ts=8:sw=2:sts=2:ai  // vim:et:ts=8:sw=2:sts=2:ai

Legend:
Removed from v.70  
changed lines
  Added in v.156