summaryrefslogtreecommitdiff
path: root/search.c
diff options
context:
space:
mode:
Diffstat (limited to 'search.c')
-rw-r--r--search.c687
1 files changed, 687 insertions, 0 deletions
diff --git a/search.c b/search.c
new file mode 100644
index 00000000..7c8b7fd9
--- /dev/null
+++ b/search.c
@@ -0,0 +1,687 @@
+/**
+ * Navit, a modular navigation system.
+ * Copyright (C) 2005-2008 Navit Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <glib.h>
+#include <string.h>
+#include "debug.h"
+#include "projection.h"
+#include "item.h"
+#include "map.h"
+#include "mapset.h"
+#include "coord.h"
+#include "search.h"
+
+struct search_list_level {
+ struct mapset *ms;
+ struct search_list_common *parent;
+ struct attr attr;
+ int partial;
+ int selected;
+ struct mapset_search *search;
+ GHashTable *hash;
+ GList *list,*curr,*last;
+};
+
+struct search_list {
+ struct mapset *ms;
+ int level;
+ struct search_list_level levels[4];
+ struct search_list_result result;
+ struct search_list_result last_result;
+ int last_result_valid;
+};
+
+static guint
+search_item_hash_hash(gconstpointer key)
+{
+ const struct item *itm=key;
+ gconstpointer hashkey=(gconstpointer)(itm->id_hi^itm->id_lo);
+ return g_direct_hash(hashkey);
+}
+
+static gboolean
+search_item_hash_equal(gconstpointer a, gconstpointer b)
+{
+ const struct item *itm_a=a;
+ const struct item *itm_b=b;
+ if (item_is_equal_id(*itm_a, *itm_b))
+ return TRUE;
+ return FALSE;
+}
+
+struct search_list *
+search_list_new(struct mapset *ms)
+{
+ struct search_list *ret;
+
+ ret=g_new0(struct search_list, 1);
+ ret->ms=ms;
+
+ return ret;
+}
+
+static void search_list_search_free(struct search_list *sl, int level);
+
+static int
+search_list_level(enum attr_type attr_type)
+{
+ switch(attr_type) {
+ case attr_country_all:
+ case attr_country_id:
+ case attr_country_iso2:
+ case attr_country_iso3:
+ case attr_country_car:
+ case attr_country_name:
+ return 0;
+ case attr_town_postal:
+ return 1;
+ case attr_town_name:
+ case attr_district_name:
+ case attr_town_or_district_name:
+ return 1;
+ case attr_street_name:
+ return 2;
+ case attr_house_number:
+ return 3;
+ default:
+ dbg(0,"unknown search '%s'\n",attr_to_name(attr_type));
+ return -1;
+ }
+}
+
+void
+search_list_search(struct search_list *this_, struct attr *search_attr, int partial)
+{
+ struct search_list_level *le;
+ int level=search_list_level(search_attr->type);
+ dbg(1,"level=%d\n", level);
+ if (level != -1) {
+ this_->result.id=0;
+ this_->level=level;
+ le=&this_->levels[level];
+ le->attr=*search_attr;
+ if (search_attr->type != attr_country_id)
+ le->attr.u.str=g_strdup(search_attr->u.str);
+ search_list_search_free(this_, level);
+ le->partial=partial;
+ if (level > 0) {
+ le=&this_->levels[level-1];
+ le->curr=le->list;
+ }
+ dbg(1,"le=%p partial=%d\n", le, partial);
+ }
+}
+
+struct search_list_common *
+search_list_select(struct search_list *this_, enum attr_type attr_type, int id, int mode)
+{
+ int level=search_list_level(attr_type);
+ int num=0;
+ struct search_list_level *le;
+ struct search_list_common *slc;
+ GList *curr;
+ le=&this_->levels[level];
+ curr=le->list;
+ if (mode > 0 || !id)
+ le->selected=mode;
+ dbg(1,"enter level=%d %d %d %p\n", level, id, mode, curr);
+ while (curr) {
+ num++;
+ if (! id || num == id) {
+ slc=curr->data;
+ slc->selected=mode;
+ if (id) {
+ dbg(1,"found\n");
+ return slc;
+ }
+ }
+ curr=g_list_next(curr);
+ }
+ dbg(1,"not found\n");
+ return NULL;
+}
+
+static struct search_list_country *
+search_list_country_new(struct item *item)
+{
+ struct search_list_country *ret=g_new0(struct search_list_country, 1);
+ struct attr attr;
+
+ ret->common.item=ret->common.unique=*item;
+ if (item_attr_get(item, attr_country_car, &attr))
+ ret->car=g_strdup(attr.u.str);
+ if (item_attr_get(item, attr_country_iso2, &attr)) {
+ ret->iso2=g_strdup(attr.u.str);
+ ret->flag=g_strdup_printf("country_%s", ret->iso2);
+ }
+ if (item_attr_get(item, attr_country_iso3, &attr))
+ ret->iso3=g_strdup(attr.u.str);
+ if (item_attr_get(item, attr_country_name, &attr))
+ ret->name=g_strdup(attr.u.str);
+ return ret;
+}
+
+static void
+search_list_country_destroy(struct search_list_country *this_)
+{
+ g_free(this_->car);
+ g_free(this_->iso2);
+ g_free(this_->iso3);
+ g_free(this_->flag);
+ g_free(this_->name);
+ g_free(this_);
+}
+
+static struct search_list_town *
+search_list_town_new(struct item *item)
+{
+ struct search_list_town *ret=g_new0(struct search_list_town, 1);
+ struct attr attr;
+ struct coord c;
+
+ ret->itemt=*item;
+ ret->common.item=ret->common.unique=*item;
+ if (item_attr_get(item, attr_town_streets_item, &attr)) {
+ dbg(1,"town_assoc 0x%x 0x%x\n", attr.u.item->id_hi, attr.u.item->id_lo);
+ ret->common.unique=*attr.u.item;
+ }
+ if (item_attr_get(item, attr_town_name, &attr))
+ ret->name=map_convert_string(item->map,attr.u.str);
+ else
+ ret->name=NULL;
+ if (item_attr_get(item, attr_town_postal, &attr))
+ ret->common.postal=map_convert_string(item->map,attr.u.str);
+ else
+ ret->common.postal=NULL;
+ if (item_attr_get(item, attr_district_name, &attr))
+ ret->district=map_convert_string(item->map,attr.u.str);
+ else
+ ret->district=NULL;
+ if (item_coord_get(item, &c, 1)) {
+ ret->common.c=g_new(struct pcoord, 1);
+ ret->common.c->x=c.x;
+ ret->common.c->y=c.y;
+ ret->common.c->pro = map_projection(item->map);
+ }
+ return ret;
+}
+
+static void
+search_list_town_destroy(struct search_list_town *this_)
+{
+ map_convert_free(this_->name);
+ map_convert_free(this_->common.postal);
+ if (this_->common.c)
+ g_free(this_->common.c);
+ g_free(this_);
+}
+
+static void
+search_list_common_new(struct item *item, struct search_list_common *common)
+{
+ struct attr attr;
+ if (item_attr_get(item, attr_town_name, &attr))
+ common->town_name=map_convert_string(item->map, attr.u.str);
+ else
+ common->town_name=NULL;
+ if (item_attr_get(item, attr_district_name, &attr))
+ common->district_name=map_convert_string(item->map, attr.u.str);
+ else
+ common->district_name=NULL;
+ if (item_attr_get(item, attr_postal, &attr))
+ common->postal=map_convert_string(item->map, attr.u.str);
+ else
+ common->postal=NULL;
+}
+
+static struct search_list_street *
+search_list_street_new(struct item *item)
+{
+ struct search_list_street *ret=g_new0(struct search_list_street, 1);
+ struct attr attr;
+ struct coord c;
+
+ ret->common.item=ret->common.unique=*item;
+ if (item_attr_get(item, attr_street_name, &attr))
+ ret->name=map_convert_string(item->map, attr.u.str);
+ else
+ ret->name=NULL;
+ search_list_common_new(item, &ret->common);
+ if (item_coord_get(item, &c, 1)) {
+ ret->common.c=g_new(struct pcoord, 1);
+ ret->common.c->x=c.x;
+ ret->common.c->y=c.y;
+ ret->common.c->pro = map_projection(item->map);
+ }
+ return ret;
+}
+
+static void
+search_list_common_destroy(struct search_list_common *common)
+{
+ map_convert_free(common->town_name);
+ map_convert_free(common->district_name);
+ map_convert_free(common->postal);
+}
+
+static void
+search_list_street_destroy(struct search_list_street *this_)
+{
+ map_convert_free(this_->name);
+ search_list_common_destroy(&this_->common);
+ if (this_->common.c)
+ g_free(this_->common.c);
+ g_free(this_);
+}
+
+static struct search_list_house_number *
+search_list_house_number_new(struct item *item)
+{
+ struct search_list_house_number *ret=g_new0(struct search_list_house_number, 1);
+ struct attr attr;
+ struct coord c;
+
+ ret->common.item=ret->common.unique=*item;
+ if (item_attr_get(item, attr_house_number, &attr))
+ ret->house_number=map_convert_string(item->map, attr.u.str);
+ search_list_common_new(item, &ret->common);
+ if (item_coord_get(item, &c, 1)) {
+ ret->common.c=g_new(struct pcoord, 1);
+ ret->common.c->x=c.x;
+ ret->common.c->y=c.y;
+ ret->common.c->pro = map_projection(item->map);
+ }
+ return ret;
+}
+
+static void
+search_list_house_number_destroy(struct search_list_house_number *this_)
+{
+ map_convert_free(this_->house_number);
+ search_list_common_destroy(&this_->common);
+ if (this_->common.c)
+ g_free(this_->common.c);
+ g_free(this_);
+}
+
+static void
+search_list_result_destroy(int level, void *p)
+{
+ switch (level) {
+ case 0:
+ search_list_country_destroy(p);
+ break;
+ case 1:
+ search_list_town_destroy(p);
+ break;
+ case 2:
+ search_list_street_destroy(p);
+ break;
+ case 3:
+ search_list_house_number_destroy(p);
+ break;
+ }
+}
+
+static void
+search_list_search_free(struct search_list *sl, int level)
+{
+ struct search_list_level *le=&sl->levels[level];
+ GList *next,*curr;
+ if (le->search) {
+ mapset_search_destroy(le->search);
+ le->search=NULL;
+ }
+#if 0 /* FIXME */
+ if (le->hash) {
+ g_hash_table_destroy(le->hash);
+ le->hash=NULL;
+ }
+#endif
+ curr=le->list;
+ while (curr) {
+ search_list_result_destroy(level, curr->data);
+ next=g_list_next(curr);
+ curr=next;
+ }
+ g_list_free(le->list);
+ le->list=NULL;
+ le->curr=NULL;
+ le->last=NULL;
+
+}
+
+static char *
+postal_merge(char *mask, char *new)
+{
+ dbg(1,"enter %s %s\n", mask, new);
+ int i;
+ char *ret=NULL;
+ if (!new)
+ return NULL;
+ if (!mask)
+ return g_strdup(new);
+ i=0;
+ while (mask[i] && new[i]) {
+ if (mask[i] != '.' && mask[i] != new[i])
+ break;
+ i++;
+
+ }
+ if (mask[i]) {
+ ret=g_strdup(mask);
+ while (mask[i])
+ ret[i++]='.';
+ }
+ dbg(1,"merged %s with %s as %s\n", mask, new, ret);
+ return ret;
+}
+
+static int
+search_add_result(struct search_list_level *le, struct search_list_common *slc)
+{
+ struct search_list_common *slo;
+ char *merged;
+ slo=g_hash_table_lookup(le->hash, &slc->unique);
+ if (!slo) {
+ g_hash_table_insert(le->hash, &slc->unique, slc);
+ if (slc->postal && !slc->postal_mask) {
+ slc->postal_mask=g_strdup(slc->postal);
+ }
+ le->list=g_list_append(le->list, slc);
+ return 1;
+ }
+ merged=postal_merge(slo->postal_mask, slc->postal);
+ if (merged) {
+ g_free(slo->postal_mask);
+ slo->postal_mask=merged;
+ }
+ return 0;
+}
+
+struct search_list_result *
+search_list_get_result(struct search_list *this_)
+{
+ struct search_list_level *le,*leu;
+ struct item *item;
+ int level=this_->level;
+
+ dbg(1,"enter\n");
+ le=&this_->levels[level];
+ dbg(1,"le=%p\n", le);
+ for (;;) {
+ dbg(1,"le->search=%p\n", le->search);
+ if (! le->search) {
+ dbg(1,"partial=%d level=%d\n", le->partial, level);
+ if (! level)
+ le->parent=NULL;
+ else {
+ leu=&this_->levels[level-1];
+ dbg(1,"leu->curr=%p\n", leu->curr);
+ for (;;) {
+ struct search_list_common *slc;
+ if (! leu->curr)
+ return NULL;
+ le->parent=leu->curr->data;
+ leu->last=leu->curr;
+ leu->curr=g_list_next(leu->curr);
+ slc=(struct search_list_common *)(le->parent);
+ if (!slc)
+ break;
+ if (slc->selected == leu->selected)
+ break;
+ }
+ }
+ if (le->parent)
+ dbg(1,"mapset_search_new with item(%d,%d)\n", le->parent->item.id_hi, le->parent->item.id_lo);
+ dbg(1,"attr=%s\n", attr_to_name(le->attr.type));
+ le->search=mapset_search_new(this_->ms, &le->parent->item, &le->attr, le->partial);
+ le->hash=g_hash_table_new(search_item_hash_hash, search_item_hash_equal);
+ }
+ dbg(1,"le->search=%p\n", le->search);
+ item=mapset_search_get_item(le->search);
+ dbg(1,"item=%p\n", item);
+ if (item) {
+ void *p=NULL;
+ dbg(1,"id_hi=%d id_lo=%d\n", item->id_hi, item->id_lo);
+ this_->result.country=NULL;
+ this_->result.town=NULL;
+ this_->result.street=NULL;
+ this_->result.c=NULL;
+ switch (level) {
+ case 0:
+ p=search_list_country_new(item);
+ this_->result.country=p;
+ break;
+ case 1:
+ p=search_list_town_new(item);
+ this_->result.country=this_->levels[0].last->data;
+ this_->result.town=p;
+ this_->result.c=this_->result.town->common.c;
+ break;
+ case 2:
+ p=search_list_street_new(item);
+ this_->result.country=this_->levels[0].last->data;
+ this_->result.town=this_->levels[1].last->data;
+ this_->result.street=p;
+ this_->result.c=this_->result.street->common.c;
+ break;
+ case 3:
+ p=search_list_house_number_new(item);
+ this_->result.country=this_->levels[0].last->data;
+ this_->result.town=this_->levels[1].last->data;
+ this_->result.street=this_->levels[2].last->data;
+ this_->result.house_number=p;
+ this_->result.c=this_->result.street->common.c;
+
+ }
+ if (p) {
+ if (search_add_result(le, p)) {
+ this_->result.id++;
+ return &this_->result;
+ } else
+ search_list_result_destroy(level, p);
+ }
+ } else {
+ mapset_search_destroy(le->search);
+ le->search=NULL;
+ g_hash_table_destroy(le->hash);
+ if (! level)
+ break;
+ }
+ }
+ return NULL;
+}
+
+void
+search_list_destroy(struct search_list *this_)
+{
+ g_free(this_);
+}
+
+void
+search_init(void)
+{
+}
+
+#if 0
+static char *
+search_fix_spaces(char *str)
+{
+ int i;
+ int len=strlen(str);
+ char c,*s,*d,*ret=g_strdup(str);
+
+ for (i = 0 ; i < len ; i++) {
+ if (ret[i] == ',' || ret[i] == ',' || ret[i] == '/')
+ ret[i]=' ';
+ }
+ s=ret;
+ d=ret;
+ len=0;
+ do {
+ c=*s++;
+ if (c != ' ' || len != 0) {
+ *d++=c;
+ len++;
+ }
+ while (c == ' ' && *s == ' ')
+ s++;
+ if (c == ' ' && *s == '\0') {
+ d--;
+ len--;
+ }
+ } while (c);
+ return ret;
+}
+
+static GList *
+search_split_phrases(char *str)
+{
+ char *tmp,*s,*d;
+ s=str;
+ GList *ret=NULL;
+ do {
+ tmp=g_strdup(s);
+ d=tmp+strlen(s)-1;
+ ret=g_list_append(ret, g_strdup(s));
+ while (d >= tmp) {
+ if (*d == ' ') {
+ *d = '\0';
+ ret=g_list_append(ret, g_strdup(tmp));
+ }
+ d--;
+ }
+ g_free(tmp);
+ do {
+ s++;
+ if (*s == ' ') {
+ s++;
+ break;
+ }
+ } while (*s != '\0');
+ } while (*s != '\0');
+ return ret;
+}
+
+static void
+search_address_housenumber(struct search_list *sl, GList *phrases, GList *exclude1, GList *exclude2, GList *exclude3)
+{
+ struct search_list_result *slr;
+ GList *tmp=phrases;
+ int count=0;
+ struct attr attr;
+ attr.type=attr_street_name;
+ while (slr=search_list_get_result(sl)) {
+ dbg(0,"%p\n",slr);
+ dbg(0,"%p %p\n",slr->country,slr->town);
+ dbg(0,"%s %s %s %s %s\n",slr->country->car,slr->town->common.postal,slr->town->name,slr->town->district,slr->street->name);
+ count++;
+ }
+ if (!count)
+ return;
+ dbg(0,"count %d\n",count);
+ while (tmp) {
+ if (tmp != exclude1 && tmp != exclude2 && tmp != exclude3) {
+ attr.type=attr_house_number;
+ attr.u.str=tmp->data;
+ search_list_search(sl, &attr, 0);
+ while (slr=search_list_get_result(sl)) {
+ dbg(0,"result %s %s(%s) %s %s\n",slr->house_number->common.postal,slr->house_number->common.town_name, slr->house_number->common.district_name,slr->street->name,slr->house_number->house_number);
+ }
+
+ }
+ tmp=g_list_next(tmp);
+ }
+}
+static void
+search_address_street(struct search_list *sl, GList *phrases, GList *exclude1, GList *exclude2)
+{
+ struct search_list_result *slr;
+ GList *tmp=phrases;
+ int count=0;
+ struct attr attr;
+ attr.type=attr_street_name;
+ while (slr=search_list_get_result(sl)) {
+#if 0
+ dbg(0,"%s %s %s %s",slr->country->car,slr->town->name,slr->town->district,slr->street->name);
+#endif
+ dbg(0,"%s %s %s %s\n",slr->country->car,slr->town->common.postal,slr->town->name,slr->town->district);
+ count++;
+ }
+ if (!count)
+ return;
+ dbg(0,"count %d\n",count);
+ while (tmp) {
+ if (tmp != exclude1 && tmp != exclude2) {
+ attr.u.str=tmp->data;
+ search_list_search(sl, &attr, 0);
+ search_address_housenumber(sl, phrases, exclude1, exclude2, tmp);
+ }
+ tmp=g_list_next(tmp);
+ }
+}
+
+static void
+search_address_town(struct search_list *sl, GList *phrases, GList *exclude)
+{
+ GList *tmp=phrases;
+ int count=0;
+ struct attr attr;
+ attr.type=attr_town_or_district_name;
+ while (search_list_get_result(sl))
+ count++;
+ if (!count)
+ return;
+ dbg(0,"count %d\n",count);
+ while (tmp) {
+ if (tmp != exclude) {
+ attr.u.str=tmp->data;
+ search_list_search(sl, &attr, 0);
+ search_address_street(sl, phrases, exclude, tmp);
+ }
+ tmp=g_list_next(tmp);
+ }
+}
+
+void
+search_by_address(struct mapset *ms, char *addr)
+{
+ char *str=search_fix_spaces(addr);
+ GList *tmp,*phrases=search_split_phrases(str);
+ dbg(0,"enter %s\n",addr);
+ struct search_list *sl;
+ struct search_list_result *slr;
+ struct attr attr;
+ attr.type=attr_country_all;
+ tmp=phrases;
+ sl=search_list_new(ms);
+ while (tmp) {
+ attr.u.str=tmp->data;
+ search_list_search(sl, &attr, 0);
+ search_address_town(sl, phrases, tmp);
+ tmp=g_list_next(tmp);
+ }
+ search_list_search(sl, country_default(), 0);
+ search_address_town(sl, phrases, NULL);
+
+ g_free(str);
+}
+#endif
+