#define _GNU_SOURCE
-#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
#include <math.h>
#include <glib.h>
+#include <glib/gstdio.h>
#include <sqlite3.h>
-#include <expat.h>
#include "osm.h"
+#include "latlon.h"
+
+/* #define DEBUG_OSM */
+#define OSM_PLACE_CACHE_MAX_ITEMS 40
struct sql_select_stmt {
sqlite3_stmt *select_way;
static struct sql_select_stmt sql;
gboolean osm_way_get_nodes(osm_way *w);
+gboolean osm_way_get_name(osm_way *w);
+gboolean osm_way_get_ref(osm_way *w);
+
+static GTimer *dbt;
+
+/* Cache hash tables */
+static GHashTable *_place_cache;
+
+/*****************************************************************************/
+
+int
+osm_progress(void *ud)
+{
+g_print(".");
+return 0;
+}
gboolean
osm_db_prepare(sqlite3 *db)
{
+#ifdef DEBUG
+sqlite3_progress_handler(db, 1000, osm_progress, NULL);
+#endif
+
/* Place */
/* Select nearest place inside lat,lon+-range */
if (sqlite3_prepare_v2(db, "select name,(($LAT-lat)*($LAT-lat))+(($LON-lon)*($LON-lon)) as dist,"
" where wid=wsid and way_seg.node=nodes.nid "
" and lat between $LAT-$RANGE and $LAT+$RANGE "
" and lon between $LON-$RANGE and $LON+$RANGE "
+ " and type between $WTS and $WTY "
" order by dist",
-1, &sql.select_way, NULL)!=SQLITE_OK)
return FALSE;
}
gboolean
-osm_init()
+osm_init(void)
{
+_place_cache=g_hash_table_new(g_direct_hash, g_direct_equal);
+dbt=g_timer_new();
return TRUE;
}
+void
+osm_deinit(void)
+{
+g_hash_table_destroy(_place_cache);
+g_timer_destroy(dbt);
+}
+
+/*****************************************************************************/
+
/**
* Free way nodes list
*/
g_slice_free(osm_way, w);
}
+/*****************************************************************************/
+
+static void
+osm_place_free(osm_place *p)
+{
+if (p->name)
+ g_free(p->name);
+g_slice_free(osm_place, p);
+}
+
+static gboolean
+osm_place_remove(gpointer k, gpointer v, gpointer ud)
+{
+osm_place_free((osm_place *)v);
+return TRUE;
+}
+
+static osm_place *
+osm_place_new(void)
+{
+return g_slice_new0(osm_place);
+}
+
+static osm_place *
+osm_place_cache_lookup(guint32 id)
+{
+return g_hash_table_lookup(_place_cache, GINT_TO_POINTER(id));
+}
+
+static void
+osm_place_cache_add(osm_place *p)
+{
+if (osm_place_cache_lookup(p->id)==NULL)
+ g_hash_table_insert(_place_cache, GINT_TO_POINTER(p->id), p);
+}
+
+static void
+osm_place_cache_gc(void)
+{
+gint r;
+g_printf("*** Clearing place cache\n");
+r=g_hash_table_foreach_remove(_place_cache, osm_place_remove, NULL);
+g_printf("*** Removed %d items\n", r);
+}
+
+static void
+osm_place_update_distance(osm_place *p, gint lat, gint lon)
+{
+gdouble lam, lom;
+
+lam=(gdouble)((lat-p->lat)*(lat-p->lat));
+lom=(gdouble)((lon-p->lon)*(lon-p->lon));
+
+p->dist=sqrt(lam+lom);
+}
+
/**
* Get place with given id and distance to current location
*/
gboolean
osm_place_get(guint32 id, gint lat, gint lon, osm_place *n)
{
+n=osm_place_cache_lookup(id);
+if (n) {
+ g_printf("*** Place cache hit!\n");
+ osm_place_update_distance(n, lat, lon);
+ return TRUE;
+}
+n=NULL;
+
+/* XXX: better place for this */
+if (g_hash_table_size(_place_cache)>OSM_PLACE_CACHE_MAX_ITEMS)
+ osm_place_cache_gc();
+
sqlite3_clear_bindings(sql.select_place);
sqlite3_reset(sql.select_place);
const gchar *place;
guint32 dist;
+ n=osm_place_new();
place=sqlite3_column_text(sql.select_place, 0);
n->name=g_strdup(place);
dist=sqlite3_column_int(sql.select_place, 1);
* Search for the nearest place, type
*/
gboolean
-osm_find_nearest_place(node_type_t type, gint lat, gint lon, osm_place *n)
+osm_find_nearest_place(node_type_t type, gint lat, gint lon, osm_place **nr)
{
gint range;
+osm_place *n=NULL;
switch (type) {
case NODE_PLACE_SUBURB:
return FALSE;
}
-if (n->name) {
- g_free(n->name);
- n->name=NULL;
-}
+n=osm_place_new();
n->isin=n->lat=n->lon=n->dist=0;
if (SQLITE_ROW == sqlite3_step(sql.select_near_place)) {
const gchar *place;
n->isin=sqlite3_column_int(sql.select_near_place, 5);
n->type=type;
+ osm_place_cache_add(n);
+
+ *nr=n;
return TRUE;
}
+*nr=n;
return FALSE;
}
{
GList *ways=NULL;
osm_way *w;
+gulong tms;
+gint wc=0;
sqlite3_reset(sql.select_way);
sqlite3_clear_bindings(sql.select_way);
if (SQLITE_OK != sqlite3_bind_int(sql.select_way, 1, lat) ||
SQLITE_OK != sqlite3_bind_int(sql.select_way, 2, lon) ||
- SQLITE_OK != sqlite3_bind_int(sql.select_way, 3, range)) {
+ SQLITE_OK != sqlite3_bind_int(sql.select_way, 3, range) ||
+ SQLITE_OK != sqlite3_bind_int(sql.select_way, 4, WAY_ROAD_START) ||
+ SQLITE_OK != sqlite3_bind_int(sql.select_way, 5, WAY_ROAD_END)) {
g_printerr("Failed to bind values for way\n");
return NULL;
}
+g_timer_start(dbt);
+
while (SQLITE_ROW == sqlite3_step(sql.select_way)) {
guint32 dist;
+ wc++;
w=g_slice_new0(osm_way);
w->id=sqlite3_column_int(sql.select_way, 0);
w->type=sqlite3_column_int(sql.select_way, 1);
ways=g_list_prepend(ways, w);
}
+g_timer_stop(dbt);
+g_printf("Query took: %f sec, found: %d ways\n", g_timer_elapsed(dbt, &tms), wc);
+
return ways;
}
-static gdouble magnitude(gdouble x1, gdouble y1, gdouble x2, gdouble y2)
+inline gdouble magnitude(gdouble x1, gdouble y1, gdouble x2, gdouble y2)
{
gdouble x,y;
x=x2-x1;
return sqrt((x*x)+(y*y));
}
-gboolean distance_point_to_line(gint x, gint y, gint x1, gint y1, gint x2, gint y2, gdouble *d)
+gboolean distance_point_to_line(gdouble x, gdouble y, gdouble x1, gdouble y1, gdouble x2, gdouble y2, gdouble *d)
{
gdouble lm,u,tmp;
gdouble ix,iy;
-lm=magnitude((gdouble)x1,(gdouble)y1,(gdouble)x2,(gdouble)y2);
-
-tmp=(gdouble)((x-x1)*(x2-x1))+((y-y1)*(y2-y1));
+lm=magnitude(x1,y1,x2,y2);
+if (lm==0.0f)
+ return FALSE;
+
+tmp=((x-x1)*(x2-x1))+((y-y1)*(y2-y1));
u=tmp/(lm*lm);
if (u<0.0f || u>1.0f)
return FALSE;
-ix=(gdouble)x1+u*(gdouble)(x2-x1);
-iy=(gdouble)y1+u*(gdouble)(y2-y1);
+ix=x1+u*(x2-x1);
+iy=y1+u*(y2-y1);
-*d=magnitude((gdouble)x,(gdouble)y, ix, iy);
+*d=magnitude(x,y, ix, iy);
return TRUE;
}
+gboolean osm_way_distance(gint lat, gint lon, osm_way_node *f, osm_way_node *t, gdouble *d)
+{
+if (!f || !t)
+ return FALSE;
+
+return distance_point_to_line((gdouble)lon, (gdouble)lat, (gdouble)f->lon, (gdouble)f->lat, (gdouble)t->lon, (gdouble)t->lat, d);
+}
+
/**
* Search for the nearest way (road)
* - First search for ways with nearest node
GList *w=NULL;
guint range=8192;
osm_way *cw=NULL;
+gdouble pdist=900000.0, pndist=9000000.0;
while ((w=osm_find_nearest_way_nodes(lat, lon, range))==NULL && range<=65536) {
range=range<<1;
g_printf("Found ways: %d\n", g_list_length(w));
-switch (g_list_length(w)) {
- case 0:
- return NULL;
- break;
- case 1:
- cw=w->data;
- break;
- default:
- {
- gint dist=900000, ndist;
- gdouble pdist=900000.0, pndist=9000000.0;
+if (g_list_length(w)==0)
+ return NULL;
- for (iter=w; iter!=NULL; iter=iter->next) {
- osm_way_node *wnf;
- osm_way_node *wnt;
+for (iter=w; iter!=NULL; iter=iter->next) {
+ osm_way_node *wnf;
+ osm_way_node *wnt;
+ osm_way *way=(osm_way*)iter->data;
- osm_way *way=(osm_way*)iter->data;
+#ifdef DEBUG_OSM
+ g_printf("WAY %d (%d) HAS %d NODES, nearest is %d\n",
+ way->id, way->type, way->nodecnt, way->node_num);
+#endif
- g_printf("WAY %d (%d) HAS %d NODES, nearest is %d\n",
- way->id, way->type, way->nodecnt, way->node_num);
+ if (osm_way_get_nodes(way)==FALSE)
+ continue;
- if (osm_way_get_nodes(way)==FALSE)
- continue;
+ if (way->nodes==0) {
+ g_printerr("Way with 0 nodes ? Skipping\n");
+ continue;
+ }
- if (way->nodes==0) {
- g_printerr("Way with 0 nodes ? Skipping\n");
- continue;
- }
+ wnf=g_list_nth_data(way->nodes, way->node_num);
+ if (!wnf) {
+ osm_way_free(way);
+ continue;
+ }
- wnf=g_list_nth_data(way->nodes, way->node_num);
- if (!wnf) {
+ if ((way->node_num==way->nodecnt) || (way->node_num==0)) {
+ wnt=g_list_nth_data(way->nodes, way->node_num==way->nodecnt ? way->nodecnt-1 : 1);
+ if (osm_way_distance(lat, lon, wnf, wnt, &pndist)==FALSE) {
+ osm_way_free(way);
+ continue;
+ }
+ } else {
+ wnt=g_list_nth_data(way->nodes, way->node_num-1);
+ if (osm_way_distance(lat, lon, wnf, wnt, &pndist)==FALSE) {
+ wnt=g_list_nth_data(way->nodes, way->node_num+1);
+ if (osm_way_distance(lat, lon, wnf, wnt, &pndist)==FALSE) {
osm_way_free(way);
continue;
}
-
- if ( (way->node_num==way->nodecnt) || (way->node_num==0)) {
- wnt=g_list_nth_data(way->nodes, way->node_num==way->nodecnt ? way->nodecnt-1 : 1);
- if (!wnt) {
- osm_way_free(way);
- continue;
- }
- if (distance_point_to_line(lon, lat, wnf->lon, wnf->lat, wnt->lon, wnt->lat, &pndist)==FALSE) {
- osm_way_free(way);
- continue;
- }
- } else {
- wnt=g_list_nth_data(way->nodes, way->node_num-1);
- if (!wnt) {
- osm_way_free(way);
- continue;
- }
- if (distance_point_to_line(lon, lat, wnf->lon, wnf->lat, wnt->lon, wnt->lat, &pndist)==FALSE) {
- wnt=g_list_nth_data(way->nodes, way->node_num+1);
- if (!wnt) {
- osm_way_free(way);
- continue;
- }
- if (distance_point_to_line(lon, lat, wnf->lon, wnf->lat, wnt->lon, wnt->lat, &pndist)==FALSE) {
- osm_way_free(way);
- continue;
- }
- }
- }
-
- if (pndist<pdist) {
- g_printf("Found close way, distance: %f (Previous distance: %f)\n", pndist, pdist);
- pdist=pndist;
- cw=way;
- } else {
- g_printf("Way is not closer, freeing\n");
- osm_way_free(way);
- }
}
}
- break;
+
+ if (pndist<pdist) {
+ g_printf("Found close way, distance: %f (Previous distance: %f)\n", pndist, pdist);
+ pdist=pndist;
+ way->node_f=wnf;
+ way->node_t=wnt;
+ way->distance=pndist;
+ cw=way;
+ } else {
+ osm_way_free(way);
+ way=NULL;
+ }
}
g_list_free(w);
osm_way_get_ref(cw);
}
-g_printf("BEST WAY: %d %d %d %d: %s %s %s\n",
- cw->id, cw->type, cw->flags,
- cw->nodes, cw->dist, cw->name,
- cw->ref, cw->int_ref);
+#ifdef DEBUG_OSM
+g_printf("BEST WAY(%d): %s (%s,%s)\n",
+ cw->id, cw->name, cw->ref, cw->int_ref);
+g_printf("\tT: %d F: %d N: %d D: %f\n",
+ cw->type, cw->flags, cw->nodecnt, cw->dist);
+g_printf("\tNF: %d NT: %d DT %f\n",
+ cw->node_f->num,
+ cw->node_t->num, cw->distance);
+#endif
return cw;
}