summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormdankov <mdankov@users.noreply.github.com>2017-08-23 18:52:14 +0300
committerPierre GRANDIN <pgrandin@users.noreply.github.com>2017-08-23 08:52:14 -0700
commit25f865029859ad4a337ba6c91a2fd39be7d4ef06 (patch)
tree674d6cf6a2b2ec86247fdc7b47c92654704ce11c
parent28478e7f26c1a0eedc06fb4765e2f736079c6f0c (diff)
downloadnavit-25f865029859ad4a337ba6c91a2fd39be7d4ef06.tar.gz
Add multicountry towns support in maptool (#305)
* maptool:Allow multinational towns OSM community tends to allow to map disputed territories all around the world as belonging to every country, which claims the territory. maptool was randomly choosing only one of such countries for each town an a disputed territory. This commit makes maptool to conform OSM practice. It will assign a town to every country, which drew its boundary around the town node. * Fix maptool crash related to freed memory access * Reduce debug noise in maptool
-rw-r--r--navit/maptool/osm.c479
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);