diff options
Diffstat (limited to 'navit/maptool')
-rw-r--r-- | navit/maptool/osm.c | 479 |
1 files changed, 320 insertions, 159 deletions
diff --git a/navit/maptool/osm.c b/navit/maptool/osm.c index fbc0c2d2c..8d6e863bd 100644 --- a/navit/maptool/osm.c +++ b/navit/maptool/osm.c @@ -34,6 +34,7 @@ #include "file.h" #include "profile.h" #include "types.h" +#include "transform.h" #ifndef M_PI #define M_PI 3.14159265358979323846 @@ -885,26 +886,60 @@ build_countrytable(void) } } +static void +osm_logv(char *prefix, char *objtype, osmid id, int cont, struct coord_geo *geo, char *fmt, va_list ap) +{ + char str[4096]; + vsnprintf(str, sizeof(str), fmt, ap); + if(cont) + prefix=""; + if(objtype) + fprintf(stderr,"%shttp://www.openstreetmap.org/%s/"OSMID_FMT" %s", prefix, objtype, id, str); + else if(geo) + fprintf(stderr,"%shttp://www.openstreetmap.org/#map=19/%.5f/%.5f %s",prefix, geo->lat, geo->lng, str); + else + fprintf(stderr,"%s[no osm object info] %s",prefix, str); +} + void osm_warning(char *type, osmid id, int cont, char *fmt, ...) { - char str[4096]; va_list ap; va_start(ap, fmt); - vsnprintf(str, sizeof(str), fmt, ap); + osm_logv("OSM Warning:", type, id, cont, NULL, fmt, ap); va_end(ap); - fprintf(stderr,"%shttp://www.openstreetmap.org/browse/%s/"OSMID_FMT" %s",cont ? "":"OSM Warning:",type,id,str); } void osm_info(char *type, osmid id, int cont, char *fmt, ...) { - char str[4096]; va_list ap; va_start(ap, fmt); - vsnprintf(str, sizeof(str), fmt, ap); + osm_logv("OSM Info:", type, id, cont, NULL, fmt, ap); + va_end(ap); +} + +void +itembin_warning(struct item_bin *ib, int cont, char *fmt, ...) +{ + char *type=NULL; + osmid id; + struct coord_geo geo; + va_list ap; + if(0!=(id=item_bin_get_nodeid(ib))) { + type="node"; + } else if(0!=(id=item_bin_get_wayid(ib))) { + type="way"; + } else if(0!=(id=item_bin_get_relationid(ib))) { + type="relation"; + } else { + struct coord *c=(struct coord *)(ib+1); + transform_to_geo(projection_mg, c, &geo); + } + + va_start(ap, fmt); + osm_logv("OSM Warning:", type, id, cont, &geo, fmt, ap); va_end(ap); - fprintf(stderr,"%shttp://www.openstreetmap.org/browse/%s/"OSMID_FMT" %s",cont ? "":"OSM Info:",type,id,str); } static void @@ -1924,145 +1959,239 @@ osm_end_node(struct maptool_osm *osm) attr_longest_match_clear(); } -static struct country_table * +#define MAX_TOWN_ADMIN_LEVELS 11 + +struct town_country { + /* attrs[0] is reserved for postal code */ + /* attrs[1..] are for osm admin levels 3.. (admin_level=2 is always a country boundary)*/ + struct attr attrs[MAX_TOWN_ADMIN_LEVELS]; + struct country_table *country; +}; + +static struct town_country * +town_country_new(struct country_table *country) +{ + struct town_country *ret=g_malloc0(sizeof(struct town_country)); + ret->country=country; + return ret; +} + +static void +town_country_destroy(struct town_country *tc) +{ + g_free(tc); +} + +/** + * Insert a new record into the list of town info structures, if there's no such entry. + * + * @param in/out town_country_list pointer to GList* of town_country structures. + * @param in country country to add the town to + * @returns refernce to then new town_country structure added to the list, or NULL if GList already had an entry for the country given + */ +static struct town_country * +town_country_list_insert_if_new(GList **town_country_list, struct country_table *country) +{ + GList *l; + struct town_country *ret; + + if(!country) + return NULL; + + for(l=*town_country_list; l; l=g_list_next(l)) { + if(((struct town_country*)l->data)->country==country) + return NULL; + } + + ret=town_country_new(country); + *town_country_list=g_list_prepend(*town_country_list, ret); + return ret; +} + +static GList * osm_process_town_unknown_country(void) { static struct country_table *unknown; if (!unknown) unknown=country_from_countryid(999); - return unknown; + return g_list_prepend(NULL, town_country_new(unknown)); } -static struct country_table * -osm_process_town_by_is_in(struct item_bin *ib,char *is_in, struct attr *attrs, GHashTable *town_hash) -{ - struct country_table *result=NULL, *lookup; - char *tok,*dup=g_strdup(is_in),*buf=dup; - int conflict=0; - int find_town_name = 0; +/** + * Get town name from district is_in attribute if possible. + * + * @param in ib pointer to item_bin structure holding district information + * @param in town_hash hash of all town names + * @returns refernce to a list of struct town_country* the town belongs to + */ +static char * +osm_process_town_get_town_name_from_is_in(struct item_bin *ib, GHashTable *town_hash) +{ + char *is_in=item_bin_get_attr(ib, attr_osm_is_in, NULL); + char *tok,*dup,*buf; + char *town=NULL; if(!is_in) return NULL; - if (item_is_district(*ib)) - find_town_name = 1; - - while ((tok=strtok(buf, ",;"))) { + dup=g_strdup(is_in); + buf=dup; + while (!town && (tok=strtok(buf, ",;"))) { while (*tok==' ') tok++; - if (find_town_name && g_hash_table_lookup(town_hash, tok)) { - attrs[10].type = attr_town_name; - attrs[10].u.str = g_strdup(tok); - find_town_name = 0; - } - lookup=g_hash_table_lookup(country_table_hash,tok); - if (lookup) { - if (result && result->countryid != lookup->countryid) { - conflict=1; - } - result=lookup; - } + town=g_hash_table_lookup(town_hash, tok); buf=NULL; } g_free(dup); - if(conflict) { - char *label=item_bin_get_attr(ib, attr_town_name, NULL); - osm_warning("node",item_bin_get_nodeid(ib),0,"Country conflict for %s is_in=%s, choosen country %d (%s)\n", label, is_in, result->countryid, result->names); + return town; +} + + +/** + * Find list of countries which town or district belongs to. + * @param in ib pointer to item_bin structure holding town information + * @returns refernce to a list of struct town_country* the town belongs to + */ +static GList * +osm_process_town_by_is_in(struct item_bin *ib) +{ + struct country_table *country; + char *is_in=item_bin_get_attr(ib, attr_osm_is_in, NULL); + char *tok,*dup,*buf; + GList *town_country_list=NULL; + + if(!is_in) + return NULL; + + dup=g_strdup(is_in); + buf=dup; + while ((tok=strtok(buf, ",;"))) { + while (*tok==' ') + tok++; + country=g_hash_table_lookup(country_table_hash,tok); + town_country_list_insert_if_new(&town_country_list, country); + buf=NULL; } - return result; + g_free(dup); + + return town_country_list; } -static struct country_table * -osm_process_town_by_boundary(GList *bl, struct item_bin *ib, struct coord *c, struct attr *attrs) +/** + * Update town attributes based on its administrative parents. + * + * @param in town town initial item_bin data + * @param in/out tc town_country structure holding country information and town attributes to add to town item_bin + * @param in matches list of administrative boundaries the town belongs to (data is struct boundary *) + * @returns nothing + */ +static void +osm_process_town_by_boundary_update_attrs(struct item_bin *town, struct town_country *tc, GList *matches) { - GList *l,*matches=boundary_find_matches(bl, c); - struct boundary *match=NULL; + long long *nodeid; + long long node_id=0; + int max_possible_adm_level=-1; + int max_adm_level=0; + GList *l; + int a; + + if(tc->country->admin_levels) + max_possible_adm_level=strlen(tc->country->admin_levels)+3; - l=matches; - while (l) { + nodeid=item_bin_get_attr(town, attr_osm_nodeid, NULL); + if(nodeid) + node_id=*nodeid; + + for(l=matches;l;l=g_list_next(l)) { struct boundary *b=l->data; - if (b->country) { - if (match && match->country->countryid!=b->country->countryid) { - osm_warning("node",item_bin_get_nodeid(ib),0,"node (0x%x,0x%x) country conflict: ", c->x, c->y); - osm_warning("relation",boundary_relid(match),1,"replacing country %d (%s) with ",match->country->countryid, match->country->names); - osm_warning("relation",boundary_relid(b),1,"country %d (%s)\n",b->country->countryid, b->country->names); + char *boundary_admin_level_string; + char *name; + char *postal=osm_tag_value(b->ib, "postal_code"); + + if (postal) { + tc->attrs[0].type=attr_town_postal; + tc->attrs[0].u.str=postal; + } + + if(max_possible_adm_level==-1) + continue; + + boundary_admin_level_string=osm_tag_value(b->ib, "admin_level"); + + if (!boundary_admin_level_string) + continue; + + a=atoi(boundary_admin_level_string); + if (a > 2 && a < max_possible_adm_level) { + enum attr_type attr_type=attr_none; + switch(tc->country->admin_levels[a-3]) { + case 's': + attr_type=attr_state_name; + break; + case 'c': + attr_type=attr_county_name; + break; + case 'M': + /* Here we patch the boundary itself to convert it to town polygon later*/ + b->ib->type=type_poly_place6; + case 'm': + attr_type=attr_municipality_name; + break; + case 'T': + /* Here we patch the boundary itself to convert it to town polygon later*/ + b->ib->type=type_poly_place6; + break; + } + name=osm_tag_value(b->ib, "name"); + if (name && attr_type != attr_none) { + tc->attrs[a-2].type=attr_type; + tc->attrs[a-2].u.str=name; + } + } + if(b->admin_centre && b->admin_centre==node_id) { + if(!max_adm_level || max_adm_level<a) { + max_adm_level=a; } - match=b; } - l=g_list_next(l); } - if (match) { - if (match && match->country && match->country->admin_levels) { - long long *nodeid=item_bin_get_attr(ib, attr_osm_nodeid, NULL); - long long node_id=0; - int end=strlen(match->country->admin_levels)+3; - int a; - int max_adm_level=0; - - if(nodeid) - node_id=*nodeid; - - l=matches; - while (l) { - struct boundary *b=l->data; - char *boundary_admin_level_string=osm_tag_value(b->ib, "admin_level"); - char *postal=osm_tag_value(b->ib, "postal_code"); - if (boundary_admin_level_string) { - char *name; - a=atoi(boundary_admin_level_string); - if (a > 2 && a < end) { - enum attr_type attr_type=attr_none; - switch(match->country->admin_levels[a-3]) { - case 's': - attr_type=attr_state_name; - break; - case 'c': - attr_type=attr_county_name; - break; - case 'M': - b->ib->type=type_poly_place6; - case 'm': - attr_type=attr_municipality_name; - break; - case 'T': - b->ib->type=type_poly_place6; - break; - } - name=osm_tag_value(b->ib, "name"); - if (name && attr_type != attr_none) { - attrs[a-2].type=attr_type; - attrs[a-2].u.str=name; - } - } - if(b->admin_centre && b->admin_centre==node_id) { - if(!max_adm_level || max_adm_level<a){ - max_adm_level=a; - } - } - } - if (postal) { - attrs[0].type=attr_town_postal; - attrs[0].u.str=postal; - } - l=g_list_next(l); - } + /* Administrative centres are not to be contained in their own districts. */ + if(max_adm_level>0) + for(a=max_possible_adm_level-1;a>max_adm_level && a>2;a--) + tc->attrs[a-2].type=type_none; +} - /* Administrative centres are not to be contained in their own districts. */ - if(max_adm_level>0) - for(a=end-1;a>max_adm_level && a>2;a--) - attrs[a-2].type=type_none; +/** + * Find country which town belongs to. Find town administrative hierarchy attributes. + * + * @param in bl list of administrative boundaries (data is struct boundary *) + * @param in town item_bin structure holding town information + * @param in c town center coordinates + * @returns refernce to the list of town_country structures + */ +static GList * +osm_process_town_by_boundary(GList *bl, struct item_bin *town, struct coord *c) +{ + GList *matches=boundary_find_matches(bl, c); + GList *town_country_list=NULL; + GList *l; + + for (l=matches;l;l=g_list_next(l)) { + struct boundary *match=l->data; + if (match->country) { + struct town_country *tc=town_country_list_insert_if_new(&town_country_list, match->country); + if(tc) + osm_process_town_by_boundary_update_attrs(town, tc, matches); } - g_list_free(matches); - return match->country; - } else { - g_list_free(matches); - return NULL; } + + g_list_free(matches); + + return town_country_list; } static void @@ -2101,7 +2230,6 @@ osm_process_towns(FILE *in, FILE *boundaries, FILE *ways, char *suffix) struct item_bin *ib; GList *bl; GHashTable *town_hash; - struct attr attrs[11]; FILE *towns_poly; processed_nodes=processed_nodes_out=processed_ways=processed_relations=processed_tiles=0; @@ -2117,7 +2245,8 @@ osm_process_towns(FILE *in, FILE *boundaries, FILE *ways, char *suffix) if (!item_is_district(*ib)) { char *townname=item_bin_get_attr(ib, attr_town_name, NULL); - g_hash_table_insert(town_hash, strdup(townname), (gpointer)1); + char *dup=strdup(townname); + g_hash_table_replace(town_hash, dup, dup); } } fseek(in, 0, SEEK_SET); @@ -2126,63 +2255,95 @@ osm_process_towns(FILE *in, FILE *boundaries, FILE *ways, char *suffix) while ((ib=read_item(in))) { struct coord *c=(struct coord *)(ib+1); - struct country_table *result=NULL; - char *is_in=item_bin_get_attr(ib, attr_osm_is_in, NULL); - int i; - + GList *tc_list, *l; + struct item_bin *ib_copy=NULL; + processed_nodes++; - memset(attrs, 0, sizeof(attrs)); - result=osm_process_town_by_boundary(bl, ib, c, attrs); - if (!result) - result=osm_process_town_by_is_in(ib, is_in, attrs, town_hash); - else if (item_is_district(*ib)) // just for the town name - osm_process_town_by_is_in(ib, is_in, attrs, town_hash); + tc_list=osm_process_town_by_boundary(bl, ib, c); + if (!tc_list) + tc_list=osm_process_town_by_is_in(ib); - // treat a district like a town, if we could not find the town it belongs to - if (!item_bin_get_attr(ib, attr_town_name, NULL) && attrs[10].type != attr_town_name) { - char *district_name = item_bin_get_attr(ib, attr_district_name, NULL); + if (!tc_list && unknown_country) + tc_list=osm_process_town_unknown_country(); + + if (!tc_list) { + itembin_warning(ib, 0, "Lost town %s %s\n", item_bin_get_attr(ib, attr_town_name, NULL), item_bin_get_attr(ib, attr_district_name, NULL)); + } - if (district_name) { + if(tc_list && g_list_next(tc_list)) + ib_copy=item_bin_dup(ib); + + l=tc_list; + while(l) { + struct town_country *tc=l->data; + char *is_in; + long long *nodeid; + char *town_name=NULL; + int i; + + if (!tc->country->file) { + char *name=g_strdup_printf("country_%d.unsorted.tmp", tc->country->countryid); + tc->country->file=fopen(name,"wb"); + g_free(name); + } + + if (item_is_district(*ib) && NULL!=(town_name=osm_process_town_get_town_name_from_is_in(ib, town_hash))) { struct attr attr_new_town_name; attr_new_town_name.type = attr_town_name; - attr_new_town_name.u.str = district_name; - + attr_new_town_name.u.str = town_name; item_bin_add_attr(ib, &attr_new_town_name); - item_bin_remove_attr(ib, district_name); } - } - if (!result && unknown_country) - result=osm_process_town_unknown_country(); - if (result) { - if (!result->file) { - char *name=g_strdup_printf("country_%d.unsorted.tmp", result->countryid); - result->file=fopen(name,"wb"); - g_free(name); - } - if (result->file) { - long long *nodeid; - if (is_in) - item_bin_remove_attr(ib, is_in); - nodeid=item_bin_get_attr(ib, attr_osm_nodeid, NULL); - if (nodeid) - item_bin_remove_attr(ib, nodeid); - if (attrs[0].type != attr_none) { - char *postal=item_bin_get_attr(ib, attr_town_postal, NULL); - if (postal) - item_bin_remove_attr(ib, postal); - } - for (i = 0 ; i < 11 ; i++) { - if (attrs[i].type != attr_none) - item_bin_add_attr(ib, &attrs[i]); + if ((is_in=item_bin_get_attr(ib, attr_osm_is_in, NULL))!=NULL) + item_bin_remove_attr(ib, is_in); + + nodeid=item_bin_get_attr(ib, attr_osm_nodeid, NULL); + + if (nodeid) + item_bin_remove_attr(ib, nodeid); + + /* Treat district like a town, if we did not find the town it belongs to */ + if (!item_bin_get_attr(ib, attr_town_name, NULL)) { + char *district_name = item_bin_get_attr(ib, attr_district_name, NULL); + + if (district_name) { + struct attr attr_new_town_name; + attr_new_town_name.type = attr_town_name; + attr_new_town_name.u.str = district_name; + + item_bin_add_attr(ib, &attr_new_town_name); + item_bin_remove_attr(ib, district_name); } - if(item_bin_get_attr(ib, attr_district_name, NULL)) - item_bin_write_match(ib, attr_district_name, attr_district_name_match, 5, result->file); - else - item_bin_write_match(ib, attr_town_name, attr_town_name_match, 5, result->file); } + + /* FIXME: preserved from old code, but we'll have to reconsider if we really should drop attribute + * explicitely set on the town osm node and use an attribute derived from one of its surrounding boundaries. Thus we would + * use town central district' postal code instead of town one. */ + if (tc->attrs[0].type != attr_none) { + char *postal=item_bin_get_attr(ib, attr_town_postal, NULL); + if (postal) + item_bin_remove_attr(ib, postal); + } + + for (i = 0 ; i < MAX_TOWN_ADMIN_LEVELS ; i++) { + if (tc->attrs[i].type != attr_none) + item_bin_add_attr(ib, &tc->attrs[i]); + } + + if(item_bin_get_attr(ib, attr_district_name, NULL)) + item_bin_write_match(ib, attr_district_name, attr_district_name_match, 5, tc->country->file); + else + item_bin_write_match(ib, attr_town_name, attr_town_name_match, 5, tc->country->file); + + town_country_destroy(tc); + processed_nodes_out++; + l=g_list_next(l); + if(l!=NULL) + memcpy(ib, ib_copy, (ib_copy->len+1)*4); } + g_free(ib_copy); + g_list_free(tc_list); } towns_poly=tempfile(suffix,"towns_poly",1); |