--- trunk/src/osm.c 2008/12/11 13:26:13 6 +++ trunk/src/osm.c 2008/12/11 20:42:10 8 @@ -17,6 +17,8 @@ * along with OSM2Go. If not, see . */ +#define OSM_STREAM_PARSER + #include #include #include @@ -52,6 +54,7 @@ bounds->ll_min.lon, bounds->ll_max.lon); } +#ifndef OSM_STREAM_PARSER static bounds_t *osm_parse_osm_bounds(osm_t *osm, xmlDocPtr doc, xmlNode *a_node) { char *prop; @@ -92,7 +95,7 @@ bounds->ll_min.lat, bounds->ll_min.lon, bounds->ll_max.lat, bounds->ll_max.lon); - g_free(bounds); + osm_bounds_free(bounds); return NULL; } @@ -122,6 +125,7 @@ return bounds; } +#endif /* ------------------------- user handling --------------------- */ @@ -319,6 +323,7 @@ } } +#ifndef OSM_STREAM_PARSER static node_t *osm_parse_osm_node(osm_t *osm, xmlDocPtr doc, xmlNode *a_node) { xmlNode *cur_node = NULL; @@ -375,6 +380,7 @@ return node; } +#endif /* ------------------- way handling ------------------- */ @@ -485,6 +491,7 @@ return NULL; } +#ifndef OSM_STREAM_PARSER static way_t *osm_parse_osm_way(osm_t *osm, xmlDocPtr doc, xmlNode *a_node) { xmlNode *cur_node = NULL; @@ -534,6 +541,7 @@ return way; } +#endif /* ------------------- relation handling ------------------- */ @@ -688,6 +696,7 @@ return member; } +#ifndef OSM_STREAM_PARSER static relation_t *osm_parse_osm_relation(osm_t *osm, xmlDocPtr doc, xmlNode *a_node) { xmlNode *cur_node = NULL; @@ -854,6 +863,7 @@ return osm; } +#endif /* ------------------ osm handling ----------------- */ @@ -876,19 +886,532 @@ osm_relations_dump(osm->relation); } +#ifdef OSM_STREAM_PARSER +/* -------------------------- stream parser tests ------------------- */ + +#include + +static gint my_strcmp(const xmlChar *a, const xmlChar *b) { + if(!a && !b) return 0; + if(!a) return -1; + if(!b) return +1; + return strcmp((char*)a,(char*)b); +} + +/* skip current element incl. everything below (mainly for testing) */ +/* returns FALSE if something failed */ +static gboolean skip_element(xmlTextReaderPtr reader) { + g_assert(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT); + const xmlChar *name = xmlTextReaderConstName(reader); + g_assert(name); + int depth = xmlTextReaderDepth(reader); + + if(xmlTextReaderIsEmptyElement(reader)) + return TRUE; + + int ret = xmlTextReaderRead(reader); + while((ret == 1) && + ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) || + (xmlTextReaderDepth(reader) > depth) || + (my_strcmp(xmlTextReaderConstName(reader), name) != 0))) { + ret = xmlTextReaderRead(reader); + } + return(ret == 1); +} + +/* parse bounds */ +static bounds_t *process_bounds(xmlTextReaderPtr reader) { + char *prop = 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*)xmlTextReaderGetAttribute(reader, BAD_CAST "minlat"))) { + bounds->ll_min.lat = g_ascii_strtod(prop, NULL); + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "maxlat"))) { + bounds->ll_max.lat = g_ascii_strtod(prop, NULL); + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "minlon"))) { + bounds->ll_min.lon = g_ascii_strtod(prop, NULL); + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "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; + } + + /* skip everything below */ + skip_element(reader); + + /* calculate map zone which will be used as a reference for all */ + /* drawing/projection later on */ + pos_t center = { (bounds->ll_max.lat + bounds->ll_min.lat)/2, + (bounds->ll_max.lon + bounds->ll_min.lon)/2 }; + + pos2lpos_center(¢er, &bounds->center); + + /* the scale is needed to accomodate for "streching" */ + /* by the mercartor projection */ + bounds->scale = cos(DEG2RAD(center.lat)); + + pos2lpos_center(&bounds->ll_min, &bounds->min); + bounds->min.x -= bounds->center.x; + bounds->min.y -= bounds->center.y; + bounds->min.x *= bounds->scale; + bounds->min.y *= bounds->scale; + + pos2lpos_center(&bounds->ll_max, &bounds->max); + bounds->max.x -= bounds->center.x; + bounds->max.y -= bounds->center.y; + bounds->max.x *= bounds->scale; + bounds->max.y *= bounds->scale; + + return bounds; +} + +static tag_t *process_tag(xmlTextReaderPtr reader) { + /* allocate a new tag structure */ + tag_t *tag = g_new0(tag_t, 1); + + char *prop; + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "k"))) { + if(strlen(prop) > 0) tag->key = g_strdup(prop); + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "v"))) { + if(strlen(prop) > 0) tag->value = g_strdup(prop); + xmlFree(prop); + } + + if(!tag->key || !tag->value) { + printf("incomplete tag key/value %s/%s\n", tag->key, tag->value); + osm_tags_free(tag); + tag = NULL; + } + + skip_element(reader); + return tag; +} + +static node_t *process_node(xmlTextReaderPtr reader, osm_t *osm) { + + /* 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*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) { + node->id = strtoul(prop, NULL, 10); + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "lat"))) { + node->pos.lat = g_ascii_strtod(prop, NULL); + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "lon"))) { + node->pos.lon = g_ascii_strtod(prop, NULL); + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) { + node->user = osm_user(osm, prop); + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) { + node->visible = (strcasecmp(prop, "true") == 0); + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) { + node->time = convert_iso8601(prop); + xmlFree(prop); + } + + pos2lpos(osm->bounds, &node->pos, &node->lpos); + + /* just an empty element? then return the node as it is */ + if(xmlTextReaderIsEmptyElement(reader)) + return node; + + /* parse tags if present */ + int depth = xmlTextReaderDepth(reader); + + /* scan all elements on same level or its children */ + tag_t **tag = &node->tag; + int ret = xmlTextReaderRead(reader); + while((ret == 1) && + ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) || + (xmlTextReaderDepth(reader) != depth))) { + + if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) { + char *subname = (char*)xmlTextReaderConstName(reader); + if(strcasecmp(subname, "tag") == 0) { + *tag = process_tag(reader); + if(*tag) tag = &(*tag)->next; + } else + skip_element(reader); + } + + ret = xmlTextReaderRead(reader); + } + + return node; +} + +static node_chain_t *process_nd(xmlTextReaderPtr reader, osm_t *osm) { + char *prop; + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref"))) { + item_id_t id = strtoul(prop, NULL, 10); + node_chain_t *node_chain = g_new0(node_chain_t, 1); + + /* search matching node */ + node_chain->node = osm->node; + while(node_chain->node && node_chain->node->id != id) + node_chain->node = node_chain->node->next; + + if(!node_chain->node) printf("Node id %lu not found\n", id); + + if(node_chain->node) + node_chain->node->ways++; + + xmlFree(prop); + + skip_element(reader); + return node_chain; + } + + skip_element(reader); + return NULL; +} + +static way_t *process_way(xmlTextReaderPtr reader, osm_t *osm) { + /* allocate a new way structure */ + way_t *way = g_new0(way_t, 1); + + char *prop; + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) { + way->id = strtoul(prop, NULL, 10); + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) { + way->user = osm_user(osm, prop); + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) { + way->visible = (strcasecmp(prop, "true") == 0); + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) { + way->time = convert_iso8601(prop); + xmlFree(prop); + } + + /* just an empty element? then return the way as it is */ + /* (this should in fact never happen as this would be a way without nodes) */ + if(xmlTextReaderIsEmptyElement(reader)) + return way; + + /* parse tags/nodes if present */ + int depth = xmlTextReaderDepth(reader); + + /* scan all elements on same level or its children */ + tag_t **tag = &way->tag; + node_chain_t **node_chain = &way->node_chain; + int ret = xmlTextReaderRead(reader); + while((ret == 1) && + ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) || + (xmlTextReaderDepth(reader) != depth))) { + + if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) { + char *subname = (char*)xmlTextReaderConstName(reader); + if(strcasecmp(subname, "nd") == 0) { + *node_chain = process_nd(reader, osm); + if(*node_chain) node_chain = &(*node_chain)->next; + } else if(strcasecmp(subname, "tag") == 0) { + *tag = process_tag(reader); + if(*tag) tag = &(*tag)->next; + } else + skip_element(reader); + } + ret = xmlTextReaderRead(reader); + } + + return way; +} + +static member_t *process_member(xmlTextReaderPtr reader, osm_t *osm) { + char *prop; + member_t *member = g_new0(member_t, 1); + member->type = ILLEGAL; + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "type"))) { + if(strcasecmp(prop, "way") == 0) member->type = WAY; + else if(strcasecmp(prop, "node") == 0) member->type = NODE; + else if(strcasecmp(prop, "relation") == 0) member->type = RELATION; + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref"))) { + item_id_t id = strtoul(prop, NULL, 10); + + switch(member->type) { + case ILLEGAL: + printf("Unable to store illegal type\n"); + break; + + case WAY: + /* search matching way */ + member->way = osm->way; + while(member->way && member->way->id != id) + member->way = member->way->next; + + if(!member->way) { + member->type = WAY_ID; + member->id = id; + } + break; + + case NODE: + /* search matching node */ + member->node = osm->node; + while(member->node && member->node->id != id) + member->node = member->node->next; + + if(!member->node) { + member->type = NODE_ID; + member->id = id; + } + break; + + case RELATION: + /* search matching relation */ + member->relation = osm->relation; + while(member->relation && member->relation->id != id) + member->relation = member->relation->next; + + if(!member->relation) { + member->type = NODE_ID; + member->id = id; + } + break; + + case WAY_ID: + case NODE_ID: + case RELATION_ID: + break; + } + + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "role"))) { + if(strlen(prop) > 0) member->role = g_strdup(prop); + xmlFree(prop); + } + + return member; +} + +static relation_t *process_relation(xmlTextReaderPtr reader, osm_t *osm) { + /* allocate a new relation structure */ + relation_t *relation = g_new0(relation_t, 1); + + char *prop; + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) { + relation->id = strtoul(prop, NULL, 10); + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) { + relation->user = osm_user(osm, prop); + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) { + relation->visible = (strcasecmp(prop, "true") == 0); + xmlFree(prop); + } + + if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) { + relation->time = convert_iso8601(prop); + xmlFree(prop); + } + + /* just an empty element? then return the relation as it is */ + /* (this should in fact never happen as this would be a relation */ + /* without members) */ + if(xmlTextReaderIsEmptyElement(reader)) + return relation; + + /* parse tags/member if present */ + int depth = xmlTextReaderDepth(reader); + + /* scan all elements on same level or its children */ + tag_t **tag = &relation->tag; + member_t **member = &relation->member; + int ret = xmlTextReaderRead(reader); + while((ret == 1) && + ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) || + (xmlTextReaderDepth(reader) != depth))) { + + if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) { + char *subname = (char*)xmlTextReaderConstName(reader); + if(strcasecmp(subname, "nd") == 0) { + *member = process_member(reader, osm); + if(*member) member = &(*member)->next; + } else if(strcasecmp(subname, "tag") == 0) { + *tag = process_tag(reader); + if(*tag) tag = &(*tag)->next; + } else + skip_element(reader); + } + ret = xmlTextReaderRead(reader); + } + + return relation; +} + +static osm_t *process_osm(xmlTextReaderPtr reader) { + /* alloc osm structure */ + osm_t *osm = g_new0(osm_t, 1); + + node_t **node = &osm->node; + way_t **way = &osm->way; + relation_t **relation = &osm->relation; + + /* no attributes of interest */ + + const xmlChar *name = xmlTextReaderConstName(reader); + g_assert(name); + + /* read next node */ + int ret = xmlTextReaderRead(reader); + while(ret == 1) { + + switch(xmlTextReaderNodeType(reader)) { + case XML_READER_TYPE_ELEMENT: + + g_assert(xmlTextReaderDepth(reader) == 1); + char *name = (char*)xmlTextReaderConstName(reader); + if(strcasecmp(name, "bounds") == 0) { + osm->bounds = process_bounds(reader); + } else if(strcasecmp(name, "node") == 0) { + *node = process_node(reader, osm); + if(*node) node = &(*node)->next; + } else if(strcasecmp(name, "way") == 0) { + *way = process_way(reader, osm); + if(*way) way = &(*way)->next; + } else if(strcasecmp(name, "relation") == 0) { + *relation = process_relation(reader, osm); + if(*relation) relation = &(*relation)->next; + } else { + printf("something unknown found\n"); + g_assert(0); + skip_element(reader); + } + break; + + case XML_READER_TYPE_END_ELEMENT: + /* end element must be for the current element */ + g_assert(xmlTextReaderDepth(reader) == 0); + return osm; + break; + + default: + break; + } + ret = xmlTextReaderRead(reader); + } + + g_assert(0); + return NULL; +} + +static osm_t *process_file(const char *filename) { + osm_t *osm = NULL; + xmlTextReaderPtr reader; + int ret; + + reader = xmlReaderForFile(filename, NULL, 0); + if (reader != NULL) { + ret = xmlTextReaderRead(reader); + if(ret == 1) { + char *name = (char*)xmlTextReaderConstName(reader); + if(name && strcasecmp(name, "osm") == 0) + osm = process_osm(reader); + } else + printf("file empty\n"); + + xmlFreeTextReader(reader); + } else { + fprintf(stderr, "Unable to open %s\n", filename); + } + return osm; +} + +/* ----------------------- end of stream parser tests ------------------- */ +#endif + +#include + osm_t *osm_parse(char *filename) { - xmlDoc *doc = NULL; + + struct timeval start; + gettimeofday(&start, NULL); LIBXML_TEST_VERSION; +#ifdef OSM_STREAM_PARSER + // use stream parser + osm_t *osm = process_file(filename); + xmlCleanupParser(); + +#else + // 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; } - return osm_parse_doc(doc); + osm_t *osm = osm_parse_doc(doc); +#endif + + struct timeval end; + gettimeofday(&end, NULL); + + printf("total parse time: %ldms\n", + (end.tv_usec - start.tv_usec)/1000 + + (end.tv_sec - start.tv_sec)*1000); + + return osm; } gboolean osm_sanity_check(GtkWidget *parent, osm_t *osm) { @@ -1468,7 +1991,7 @@ cur_chain = &((*cur_chain)->next); } - way = way->next; + way = way->next; } return chain;