]> err.no Git - mapper/blob - src/osm-db.c
184023b173877b182f618b84d6b1ab49b1789596
[mapper] / src / osm-db.c
1 #define _GNU_SOURCE
2
3 #include <stdio.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <strings.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <math.h>
11 #include <glib.h>
12 #include <sqlite3.h>
13 #include <expat.h>
14
15 #include "osm.h"
16
17 struct sql_select_stmt {
18         sqlite3_stmt *select_way;
19         sqlite3_stmt *select_way_nodes;
20         sqlite3_stmt *select_way_name;
21         sqlite3_stmt *select_way_ref;
22         sqlite3_stmt *select_place;
23         sqlite3_stmt *select_near_place;
24 };
25 static struct sql_select_stmt sql;
26
27 gboolean osm_way_get_nodes(osm_way *w);
28
29 gboolean
30 osm_db_prepare(sqlite3 *db)
31 {
32 /* Place */
33 /* Select nearest place inside lat,lon+-range */
34 if (sqlite3_prepare_v2(db, "select name,(($LAT-lat)*($LAT-lat))+(($LON-lon)*($LON-lon)) as dist,"
35                                         " lat,lon,places.nid,isin "
36                                         " from places,nodes where type=$TYPE "
37                                         " and nodes.nid=places.nid "
38                                         " and lat between $LAT-$RANGE and $LAT+$RANGE"
39                                         " and lon between $LON-$RANGE and $LON+$RANGE order by dist limit 1",
40                     -1, &sql.select_near_place, NULL)!=SQLITE_OK)
41         return FALSE;
42 /* Select place name, distance, location, parent-place and type with given ID */
43
44 if (sqlite3_prepare_v2(db, "select name,(($LAT-lat)*($LAT-lat))+(($LON-lon)*($LON-lon)) as dist,"
45                                         " lat,lon,type,isin "
46                                         " from places,nodes where "
47                                         " nodes.nid=places.nid "
48                                         " and places.nid=$NID order by dist limit 1",
49                     -1, &sql.select_place, NULL)!=SQLITE_OK)
50         return FALSE;
51
52 /* Ways */
53 /* Select neareset ways inside lat,lon+-range */
54 if (sqlite3_prepare_v2(db, "select wid,type,nodes,flags,"
55                                         "(($LAT-lat)*($LAT-lat))+(($LON-lon)*($LON-lon)) as dist, num"
56                                         " from way,way_seg,nodes"
57                                         " where wid=wsid and way_seg.node=nodes.nid "
58                                         " and lat between $LAT-$RANGE and $LAT+$RANGE "
59                                         " and lon between $LON-$RANGE and $LON+$RANGE "
60                                         " order by dist",
61                     -1, &sql.select_way, NULL)!=SQLITE_OK)
62         return FALSE;
63
64 /* Select way nodes */
65 if (sqlite3_prepare_v2(db, "select num,lat,lon from way_seg,nodes where wsid=? and way_seg.node=nodes.nid order by num",
66                     -1, &sql.select_way_nodes, NULL)!=SQLITE_OK)
67         return FALSE;
68
69 /* Way name and ref */
70 if (sqlite3_prepare_v2(db, "select name from way_names where wid=?",
71                     -1, &sql.select_way_name, NULL)!=SQLITE_OK)
72         return FALSE;
73
74 if (sqlite3_prepare_v2(db, "select ref,int_ref from way_ref where rid=?",
75                     -1, &sql.select_way_ref, NULL)!=SQLITE_OK)
76         return FALSE;
77
78 return TRUE;
79 }
80
81 gboolean
82 osm_init()
83 {
84 return TRUE;
85 }
86
87 /**
88  * Free way nodes list 
89  */
90 void
91 osm_way_nodes_free(osm_way *w)
92 {
93 GList *iter;
94
95 if (!w->nodes) 
96         return;
97
98 for (iter=w->nodes; iter!=NULL; iter=iter->next)
99         g_slice_free(osm_way_node, (osm_way_node*)iter->data);
100
101 g_list_free(w->nodes);
102 }
103
104 /**
105  * Free a osm_way structure
106  */
107 void
108 osm_way_free(osm_way *w)
109 {
110 if (!w)
111         return;
112 osm_way_nodes_free(w);
113 if (w->name)
114         g_free(w->name);
115 if (w->ref)
116         g_free(w->ref);
117 if (w->int_ref)
118         g_free(w->int_ref);
119 g_slice_free(osm_way, w);
120 }
121
122 /**
123  * Get place with given id and distance to current location
124  */
125 gboolean
126 osm_place_get(guint32 id, gint lat, gint lon, osm_place *n)
127 {
128 sqlite3_clear_bindings(sql.select_place);
129 sqlite3_reset(sql.select_place);
130
131 if (SQLITE_OK != sqlite3_bind_int(sql.select_place, 1, lat) ||
132     SQLITE_OK != sqlite3_bind_int(sql.select_place, 2, lon) ||
133     SQLITE_OK != sqlite3_bind_int(sql.select_place, 3, id)) {
134         g_printerr("Failed to bind values for place\n");
135         return FALSE;
136 }
137
138 if (SQLITE_ROW == sqlite3_step(sql.select_place)) {
139         const gchar *place;
140         guint32 dist;
141
142         place=sqlite3_column_text(sql.select_place, 0);
143         n->name=g_strdup(place);
144         dist=sqlite3_column_int(sql.select_place, 1);
145         n->dist=sqrt((double)dist);
146         n->lat=sqlite3_column_int(sql.select_place, 2);
147         n->lon=sqlite3_column_int(sql.select_place, 3);
148         n->type=sqlite3_column_int(sql.select_place, 4);
149         n->isin=sqlite3_column_int(sql.select_place, 5);
150         return TRUE;
151 }
152 return FALSE;
153 }
154
155 /**
156  * Search for the nearest place, type
157  */
158 gboolean
159 osm_find_nearest_place(node_type_t type, gint lat, gint lon, osm_place *n)
160 {
161 gint range;
162
163 switch (type) {
164         case NODE_PLACE_SUBURB:
165                 range=95000;
166         break;
167         case NODE_PLACE_CITY:
168         case NODE_PLACE_TOWN:
169                 range=250000;
170         break;
171         case NODE_PLACE_HAMLET:
172         case NODE_PLACE_VILLAGE:
173                 range=100000;
174         break;
175         default:
176                 range=90000;
177         break;
178 }
179
180 sqlite3_clear_bindings(sql.select_near_place);
181 sqlite3_reset(sql.select_near_place);
182
183 if (SQLITE_OK != sqlite3_bind_int(sql.select_near_place, 1, lat) ||
184     SQLITE_OK != sqlite3_bind_int(sql.select_near_place, 2, lon) ||
185     SQLITE_OK != sqlite3_bind_int(sql.select_near_place, 3, type) ||
186     SQLITE_OK != sqlite3_bind_int(sql.select_near_place, 4, range)) {
187         g_printerr("Failed to bind values for near place\n");
188         return FALSE;
189 }
190
191 if (n->name) {
192         g_free(n->name);
193         n->name=NULL;
194 }
195 n->isin=n->lat=n->lon=n->dist=0;
196 if (SQLITE_ROW == sqlite3_step(sql.select_near_place)) {
197         const gchar *place;
198         guint32 dist;
199
200         place=sqlite3_column_text(sql.select_near_place, 0);
201         n->name=g_strdup(place);
202         dist=sqlite3_column_int(sql.select_near_place, 1);
203         n->dist=sqrt((double)dist);
204         n->lat=sqlite3_column_int(sql.select_near_place, 2);
205         n->lon=sqlite3_column_int(sql.select_near_place, 3);
206         n->id=sqlite3_column_int(sql.select_near_place, 4);
207         n->isin=sqlite3_column_int(sql.select_near_place, 5);
208         n->type=type;
209
210         return TRUE;
211 }
212 return FALSE;
213 }
214
215 /* Way helper */
216 static GList *
217 osm_find_nearest_way_nodes(gint lat, gint lon, guint range)
218 {
219 GList *ways=NULL;
220 osm_way *w;
221
222 sqlite3_reset(sql.select_way);
223 sqlite3_clear_bindings(sql.select_way);
224
225 if (SQLITE_OK != sqlite3_bind_int(sql.select_way, 1, lat) ||
226     SQLITE_OK != sqlite3_bind_int(sql.select_way, 2, lon) ||
227     SQLITE_OK != sqlite3_bind_int(sql.select_way, 3, range)) {
228         g_printerr("Failed to bind values for way\n");
229         return NULL;
230 }
231
232 while (SQLITE_ROW == sqlite3_step(sql.select_way)) {
233         guint32 dist;
234
235         w=g_slice_new0(osm_way);
236         w->id=sqlite3_column_int(sql.select_way, 0);
237         w->type=sqlite3_column_int(sql.select_way, 1);
238         w->nodecnt=sqlite3_column_int(sql.select_way, 2);
239         w->flags=sqlite3_column_int(sql.select_way, 3);
240         dist=sqlite3_column_int(sql.select_way, 4);
241         w->dist=sqrt((gdouble)dist);
242         w->node_num=sqlite3_column_int(sql.select_way, 5);
243         ways=g_list_prepend(ways, w);
244 }
245
246 return ways;
247 }
248
249 static gdouble magnitude(gdouble x1, gdouble y1, gdouble x2, gdouble y2)
250 {
251 gdouble x,y;
252 x=x2-x1;
253 y=y2-y1;
254
255 return sqrt((x*x)+(y*y));
256 }
257
258 gboolean distance_point_to_line(gint x, gint y, gint x1, gint y1, gint x2, gint y2, gdouble *d)
259 {
260 gdouble lm,u,tmp;
261 gdouble ix,iy;
262
263 lm=magnitude((gdouble)x1,(gdouble)y1,(gdouble)x2,(gdouble)y2);
264  
265 tmp=(gdouble)((x-x1)*(x2-x1))+((y-y1)*(y2-y1));
266 u=tmp/(lm*lm);
267
268 if (u<0.0f || u>1.0f)
269         return FALSE;
270  
271 ix=(gdouble)x1+u*(gdouble)(x2-x1);
272 iy=(gdouble)y1+u*(gdouble)(y2-y1);
273  
274 *d=magnitude((gdouble)x,(gdouble)y, ix, iy);
275  
276 return TRUE;
277 }
278
279 /**
280  * Search for the nearest way (road)
281  * - First search for ways with nearest node
282  * - If only one is returned then we are done.
283  * - If more than one, go trough the results:
284  *   - Load nodes for the way, check prev/next nodes
285  *   - Store result if closer than before
286  * - Return closest way
287  */
288 osm_way *
289 osm_find_nearest_way(gint lat, gint lon)
290 {
291 GList *iter;
292 GList *w=NULL;
293 guint range=8192;
294 osm_way *cw=NULL;
295
296 while ((w=osm_find_nearest_way_nodes(lat, lon, range))==NULL && range<=65536) {
297         range=range<<1;
298         g_printf("Trying with range: %d\n", range);
299 }
300
301 g_printf("Found ways: %d\n", g_list_length(w));
302
303 switch (g_list_length(w)) {
304         case 0:
305                 return NULL;
306         break;
307         case 1:
308                 cw=w->data;
309         break;
310         default:
311         {
312                 gint dist=900000, ndist;
313                 gdouble pdist=900000.0, pndist=9000000.0;
314
315                 for (iter=w; iter!=NULL; iter=iter->next) {
316                         osm_way_node *wnf;
317                         osm_way_node *wnt;
318
319                         osm_way *way=(osm_way*)iter->data;
320
321                         g_printf("WAY %d (%d) HAS %d NODES, nearest is %d\n", 
322                                 way->id, way->type, way->nodecnt, way->node_num);
323
324                         if (osm_way_get_nodes(way)==FALSE)
325                                 continue;
326
327                         if (way->nodes==0) {
328                                 g_printerr("Way with 0 nodes ? Skipping\n");
329                                 continue;
330                         }
331
332                         wnf=g_list_nth_data(way->nodes, way->node_num);
333                         if (!wnf) {
334                                 osm_way_free(way);
335                                 continue;
336                         }
337
338                         if ( (way->node_num==way->nodecnt) || (way->node_num==0)) {
339                                 wnt=g_list_nth_data(way->nodes, way->node_num==way->nodecnt ? way->nodecnt-1 : 1);
340                                 if (!wnt) {
341                                         osm_way_free(way);
342                                         continue;
343                                 }
344                                 if (distance_point_to_line(lon, lat, wnf->lon, wnf->lat, wnt->lon, wnt->lat, &pndist)==FALSE) {
345                                         osm_way_free(way);
346                                         continue;
347                                 }
348                         } else {
349                                 wnt=g_list_nth_data(way->nodes, way->node_num-1);
350                                 if (!wnt) {
351                                         osm_way_free(way);
352                                         continue;
353                                 }
354                                 if (distance_point_to_line(lon, lat, wnf->lon, wnf->lat, wnt->lon, wnt->lat, &pndist)==FALSE) {
355                                         wnt=g_list_nth_data(way->nodes, way->node_num+1);
356                                         if (!wnt) {
357                                                 osm_way_free(way);
358                                                 continue;
359                                         }
360                                         if (distance_point_to_line(lon, lat, wnf->lon, wnf->lat, wnt->lon, wnt->lat, &pndist)==FALSE) {
361                                                 osm_way_free(way);
362                                                 continue;
363                                         }
364                                 }
365                         }
366
367                         if (pndist<pdist) {
368                                 g_printf("Found close way, distance: %f (Previous distance: %f)\n", pndist, pdist);
369                                 pdist=pndist;
370                                 cw=way;
371                         } else {
372                                 g_printf("Way is not closer, freeing\n");
373                                 osm_way_free(way);
374                         }
375                 }
376         }
377         break;
378 }
379
380 g_list_free(w);
381 if (cw==NULL)
382         return NULL;
383
384 osm_way_get_name(cw);
385 if (cw->type==WAY_MOTORWAY || cw->type==WAY_TRUNK || 
386         cw->type==WAY_PRIMARY || cw->type==WAY_SECONDARY ||
387         cw->type==WAY_TERTIARY) {
388                 osm_way_get_ref(cw);
389 }
390
391 g_printf("BEST WAY: %d %d %d %d: %s %s %s\n", 
392         cw->id, cw->type, cw->flags,
393         cw->nodes, cw->dist, cw->name, 
394         cw->ref, cw->int_ref);
395
396 return cw;
397 }
398
399 /**
400  * Get list of nodes for given way
401  */
402 gboolean
403 osm_way_get_nodes(osm_way *w)
404 {
405 if (w->nodes!=NULL)
406         return TRUE;
407
408 sqlite3_reset(sql.select_way_nodes);
409 sqlite3_clear_bindings(sql.select_way_nodes);
410
411 if (SQLITE_OK != sqlite3_bind_int(sql.select_way_nodes, 1, w->id)) {
412         g_printerr("Failed to bind values way nodes\n");
413         return FALSE;
414 }
415
416 while (SQLITE_ROW == sqlite3_step(sql.select_way_nodes)) {
417         osm_way_node *n;
418
419         n=g_slice_new(osm_way_node);
420         n->num=sqlite3_column_int(sql.select_way_nodes, 0);
421         n->lat=sqlite3_column_int(sql.select_way_nodes, 1);
422         n->lon=sqlite3_column_int(sql.select_way_nodes, 2);
423         w->nodes=g_list_append(w->nodes, n);
424 }
425
426 return (w->nodes==NULL) ? FALSE : TRUE;
427 }
428
429 gboolean
430 osm_way_get_name(osm_way *w)
431 {
432 sqlite3_reset(sql.select_way_name);
433 sqlite3_clear_bindings(sql.select_way_name);
434
435 if (SQLITE_OK != sqlite3_bind_int(sql.select_way_name, 1, w->id)) {
436         g_printerr("Failed to bind values for way name\n");
437         return FALSE;
438 }
439
440 if (SQLITE_ROW == sqlite3_step(sql.select_way_name)) {
441         const gchar *place;
442         place=sqlite3_column_text(sql.select_way_name, 0);
443         w->name=g_strdup(place);
444 }
445 return FALSE;
446 }
447
448 gboolean
449 osm_way_get_ref(osm_way *w)
450 {
451 sqlite3_reset(sql.select_way_ref);
452 sqlite3_clear_bindings(sql.select_way_ref);
453
454 if (SQLITE_OK != sqlite3_bind_int(sql.select_way_ref, 1, w->id)) {
455         g_printerr("Failed to bind values for way ref\n");
456         return FALSE;
457 }
458
459 if (SQLITE_ROW == sqlite3_step(sql.select_way_ref)) {
460         const gchar *ref, *int_ref;
461         ref=sqlite3_column_text(sql.select_way_ref, 0);
462         int_ref=sqlite3_column_text(sql.select_way_ref, 1);
463         w->ref=g_strdup(ref);
464         w->int_ref=g_strdup(int_ref);
465 }
466 return FALSE;
467 }
468