2 * Hic Est Leo -- Graph and Routing
4 * (c) 2022 Martin Mares <mj@ucw.cz>
10 #include <ucw/clists.h>
12 #include <ucw/mempool.h>
21 static uint bidir; // 0=unidir, 1=bidir balanced by #opened, 2=bidir balanced by distance
24 static struct cf_section graph_cf = {
26 CF_UNS("BiDir", &bidir),
27 CF_UNS("AStar", &astar),
32 static void CONSTRUCTOR map_preinit(void)
34 cf_declare_section("Graph", &graph_cf, 0);
37 static struct mempool *graph_pool;
38 static clist graph_vertices;
42 struct osm_node *node;
45 // Dijkstra forward + backward
47 int state[2]; // 0=unseen, 1=open, 2=closed
48 struct graph_edge *via_edge[2];
52 cnode n; // In src->edges
53 struct graph_vertex *dest;
54 struct graph_edge *twin;
59 static uint num_vertices;
60 static uint num_edges;
62 static struct graph_vertex *graph_lookup_vertex(struct osm_node *n)
66 struct graph_vertex *v = mp_alloc_zero(graph_pool, sizeof(*v));
69 clist_init(&v->edges);
70 clist_add_tail(&graph_vertices, &v->n);
76 static struct graph_vertex *graph_vertex_by_node_id(osm_id_t id)
78 struct osm_node *n = (struct osm_node *) osm_obj_find_by_id(OSM_TYPE_NODE, id);
80 die("Cannot find node #%jd", (uintmax_t) id);
82 ASSERT(n->vertex->node);
86 static uint graph_degree(struct graph_vertex *v)
90 CLIST_FOR_EACH(struct graph_edge *, e, v->edges)
95 static struct graph_edge *add_half_edge(struct graph_vertex *u, struct graph_vertex *v, struct osm_way *way, double length)
97 struct graph_edge *e = mp_alloc_zero(graph_pool, sizeof(*e));
99 clist_add_tail(&u->edges, &e->n);
102 clist_init(&e->ways);
106 struct osm_ref *ref = mp_alloc_zero(graph_pool, sizeof(*ref));
108 clist_add_tail(&e->ways, &ref->n);
114 static struct graph_edge *graph_add_edge(struct graph_vertex *u, struct graph_vertex *v, struct osm_way *way, double length)
116 struct graph_edge *e1 = add_half_edge(u, v, way, length);
117 struct graph_edge *e2 = add_half_edge(v, u, way, length);
124 static void graph_optimize(void)
126 struct graph_vertex *v, *tmp;
128 CLIST_WALK_DELSAFE(v, graph_vertices, tmp)
130 if (graph_degree(v) == 2)
132 struct graph_edge *e1 = clist_remove_head(&v->edges);
133 struct graph_edge *e2 = e1->twin;
134 struct graph_edge *f1 = clist_remove_head(&v->edges);
135 struct graph_edge *f2 = f1->twin;
136 struct graph_vertex *x = e1->dest;
137 struct graph_vertex *y = f1->dest;
138 clist_remove(&e2->n);
139 clist_remove(&f2->n);
145 struct graph_edge *g1 = graph_add_edge(x, y, NULL, e1->length + f1->length);
146 struct graph_edge *g2 = g1->twin;
147 clist_add_list_tail(&g1->ways, &e1->ways);
148 clist_add_list_tail(&g1->ways, &f1->ways);
149 clist_add_list_tail(&g2->ways, &f2->ways);
150 clist_add_list_tail(&g2->ways, &e2->ways);
155 static bool way_is_edge(struct osm_way *w)
157 osm_val_t hwy = osm_obj_find_tag(&w->o, KEY_HIGHWAY);
162 case VALUE_SECONDARY:
163 case VALUE_MOTORWAY_LINK:
164 case VALUE_TRUNK_LINK:
165 case VALUE_PRIMARY_LINK:
166 case VALUE_SECONDARY_LINK:
173 static double vertex_dist(struct graph_vertex *a, struct graph_vertex *b)
175 struct osm_node *aa = a->node;
176 struct osm_node *bb = b->node;
177 return hypot(bb->x - aa->x, bb->y - aa->y);
180 static void way_add_edge(struct osm_way *w)
182 struct graph_vertex *prev_v = NULL;
184 OSM_FOR_EACH_BEGIN(struct osm_node *, n, w->nodes)
186 struct graph_vertex *v = graph_lookup_vertex(n);
188 graph_add_edge(prev_v, v, w, vertex_dist(v, prev_v));
194 static void mark_edge_ways(struct graph_edge *e, const char *key, const char *val)
196 OSM_FOR_EACH_BEGIN(struct osm_way *, w, e->ways)
198 osm_obj_set_tag(&w->o, key, val);
203 static void graph_check_edges(void)
205 CLIST_FOR_EACH(struct graph_vertex *, v, graph_vertices)
206 CLIST_FOR_EACH(struct graph_edge *, e, v->edges)
208 double d = vertex_dist(e->dest, v);
210 msg(L_WARN, "Suspicious edge (#%jd, #%jd): length=%g dist=%g", (intmax_t) v->node->o.id, (intmax_t) e->dest->node->o.id, e->length, d);
214 static void visualize_states(struct graph_vertex *v_src, struct graph_vertex *v_dest)
216 CLIST_FOR_EACH(struct graph_vertex *, x, graph_vertices)
219 osm_obj_set_tag(&x->node->o, "pruvodce", "visited");
221 osm_obj_set_tag(&x->node->o, "pruvodce2", "visited");
222 if (x->state[0] == 2)
224 CLIST_FOR_EACH(struct graph_edge *, e, x->edges)
225 mark_edge_ways(e, "pruvodce", "visited");
227 if (x->state[1] == 2)
229 CLIST_FOR_EACH(struct graph_edge *, e, x->edges)
230 mark_edge_ways(e, "pruvodce2", "visited");
234 osm_obj_set_tag(&v_src->node->o, "pruvodce", "src");
235 osm_obj_set_tag(&v_dest->node->o, "pruvodce", "dest");
238 static void visualize_path(struct graph_vertex *v_dest, uint dir)
240 struct graph_vertex *x = v_dest;
241 while (x->via_edge[dir])
243 struct graph_edge *e = x->via_edge[dir]->twin;
244 mark_edge_ways(e, (dir ? "pruvodce2" : "pruvodce"), "path");
249 static void bidir_dijkstra(struct graph_vertex *v_src, struct graph_vertex *v_dest)
251 msg(L_INFO, "Finding bidirectional path from #%jd to #%jd", (uintmax_t) v_src->node->o.id, (uintmax_t) v_dest->node->o.id);
256 v_dest->state[1] = 1;
259 uint opened[2] = { 1, 1 }, closed[2] = { 0, 0 };
260 double last_dist[2] = { 0, 0 };
261 struct graph_vertex *w;
268 dir = (last_dist[0] <= last_dist[1]) ? 0 : 1;
270 dir = (opened[0] <= opened[1]) ? 0 : 1;
272 // msg(L_DEBUG, "Dijkstra: dir=%d", dir);
274 CLIST_FOR_EACH(struct graph_vertex *, v, graph_vertices)
276 if (v->state[dir] == 1)
278 double d = v->dist[dir];
280 d += vertex_dist(v, (dir ? v_src : v_dest));
281 if (!w || d < best_d)
287 die("Path not found");
289 // msg(L_DEBUG, "Dijkstra: closing vertex #%jd (d=%f, dir %d)", w->node->o.id, best_d, dir);
291 last_dist[dir] = best_d;
294 if (w->state[!dir] == 2)
297 CLIST_FOR_EACH(struct graph_edge *, e, w->edges)
299 struct graph_vertex *x = e->dest;
300 double d = w->dist[dir] + e->length;
301 // msg(L_DEBUG, "Neighbor: #%jd, state=%d, dist=%f vs. %f", x->node->o.id, x->state[dir], x->dist[dir], d);
302 if (x->state[dir] == 0 || x->dist[dir] > d)
304 if (x->state[dir] == 2)
305 msg(L_WARN, "Re-opening node #%jd in dir %d", (intmax_t) x->node->o.id, dir);
308 x->via_edge[dir] = e;
314 // One vertex was closed in both direction
315 double d2c = w->dist[0] + w->dist[1];
316 msg(L_INFO, "Dijkstra: Path via double-closed vertex: dist=%f+%f=%f", w->dist[0], w->dist[1], d2c);
318 // Look for edges closed0-closed1, which might yield a shorter path
320 struct graph_vertex *ev = NULL, *ew = NULL;
321 struct graph_edge *ee = NULL;
323 CLIST_FOR_EACH(struct graph_vertex *, v, graph_vertices)
324 if (v->state[0] == 2)
325 CLIST_FOR_EACH(struct graph_edge *, e, v->edges)
327 struct graph_vertex *w = e->dest;
328 if (w->state[1] == 2)
330 double d = v->dist[0] + e->length + w->dist[1];
332 d01 = d, ev = v, ew = w, ee = e;
338 msg(L_INFO, "Dijkstra: Better path via extra edge: dist=%f+%f+%f=%f", ev->dist[0], ee->length, ew->dist[1], d01);
339 ev->via_edge[1] = ee->twin;
343 msg(L_DEBUG, "Dijkstra: Stats: %u/%u opened, %u/%u closed", opened[0], opened[1], closed[0], closed[1]);
344 visualize_states(v_src, v_dest);
345 visualize_path(w, 0);
346 visualize_path(w, 1);
349 static void dijkstra(struct graph_vertex *v_src, struct graph_vertex *v_dest)
351 msg(L_INFO, "Finding path from #%jd to #%jd", (uintmax_t) v_src->node->o.id, (uintmax_t) v_dest->node->o.id);
356 uint opened = 1, closed = 0;
357 struct graph_vertex *w;
364 CLIST_FOR_EACH(struct graph_vertex *, v, graph_vertices)
365 if (v->state[0] == 1)
367 double d = v->dist[0];
369 d += vertex_dist(v, v_dest);
370 if (!w || d < best_d)
375 die("Path not found");
377 // msg(L_DEBUG, "Dijkstra: closing vertex #%jd", w->node->o.id);
384 CLIST_FOR_EACH(struct graph_edge *, e, w->edges)
386 struct graph_vertex *x = e->dest;
387 double d = w->dist[0] + e->length;
388 // msg(L_DEBUG, "Neighbor: #%jd, state=%d, dist=%f vs. %f", x->node->o.id, x->state[0], x->dist[0], d);
389 if (x->state[0] == 0 || x->dist[0] > d)
391 if (x->state[0] == 2)
392 msg(L_WARN, "Re-opening node #%jd", (intmax_t) x->node->o.id);
401 msg(L_INFO, "Dijkstra: Found path: dist=%f", w->dist[0]);
402 msg(L_INFO, "Dijkstra: Stats: %u opened, %u closed", opened, closed);
403 visualize_states(v_src, v_dest);
404 visualize_path(v_dest, 0);
407 void graph_build(void)
409 graph_pool = mp_new(65536);
410 clist_init(&graph_vertices);
412 // We are considering only the first data source for the graph.
413 // Otherwise, things start becoming ugly, because node IDs are generally not unique.
415 struct data_source *ds = clist_head(&map_sources);
418 CLIST_FOR_EACH(struct osm_way *, w, osm_this->obj_list[OSM_TYPE_WAY])
423 msg(L_INFO, "Built road graph: %u vertices, %u edges", num_vertices, num_edges);
426 msg(L_INFO, "Optimized road graph: %u vertices, %u edges", num_vertices, num_edges);
430 // struct graph_vertex *v1 = graph_vertex_by_node_id(30002893); // Praha-Zbraslav, K Přehradám x 101
431 struct graph_vertex *v1 = graph_vertex_by_node_id(96745573); // Beroun, Lidická x Pražská
432 // struct graph_vertex *v2 = graph_vertex_by_node_id(21289321); // Brno, Heršpická x Opuštěná
433 // struct graph_vertex *v2 = graph_vertex_by_node_id(271385400); // Znojmo, Vídeňská třída x Brněnská
434 // struct graph_vertex *v2 = graph_vertex_by_node_id(26753344); // Šumperk, Jesenická x Lidická
435 struct graph_vertex *v2 = graph_vertex_by_node_id(714908910); // Ostrava, Mariánskohorská x Plzeňská x 28. října
436 ASSERT(graph_degree(v1) && graph_degree(v2));
439 bidir_dijkstra(v1, v2);