2 * Hic Est Leo -- Graph and Routing
4 * (c) 2022 Martin Mares <mj@ucw.cz>
10 #include <ucw/clists.h>
11 #include <ucw/mempool.h>
20 static struct mempool *graph_pool;
21 static clist graph_vertices;
25 struct osm_node *node;
30 int state; // 0=unseen, 1=open, 2=closed
31 struct graph_edge *via_edge;
35 cnode n; // In src->edges
36 struct graph_vertex *dest;
37 struct graph_edge *twin;
42 static uint num_vertices;
43 static uint num_edges;
45 static struct graph_vertex *graph_lookup_vertex(struct osm_node *n)
49 struct graph_vertex *v = mp_alloc_zero(graph_pool, sizeof(*v));
52 clist_init(&v->edges);
53 clist_add_tail(&graph_vertices, &v->n);
59 static struct graph_vertex *graph_vertex_by_node_id(osm_id_t id)
61 struct osm_node *n = (struct osm_node *) osm_obj_find_by_id(OSM_TYPE_NODE, id);
63 die("Cannot find node #%jd", (uintmax_t) id);
65 ASSERT(n->vertex->node);
69 static uint graph_degree(struct graph_vertex *v)
73 CLIST_FOR_EACH(struct graph_edge *, e, v->edges)
78 static struct graph_edge *add_half_edge(struct graph_vertex *u, struct graph_vertex *v, struct osm_way *way, double length)
80 struct graph_edge *e = mp_alloc_zero(graph_pool, sizeof(*e));
82 clist_add_tail(&u->edges, &e->n);
89 struct osm_ref *ref = mp_alloc_zero(graph_pool, sizeof(*ref));
91 clist_add_tail(&e->ways, &ref->n);
97 static struct graph_edge *graph_add_edge(struct graph_vertex *u, struct graph_vertex *v, struct osm_way *way, double length)
99 struct graph_edge *e1 = add_half_edge(u, v, way, length);
100 struct graph_edge *e2 = add_half_edge(v, u, way, length);
107 static void graph_optimize(void)
109 struct graph_vertex *v, *tmp;
111 CLIST_WALK_DELSAFE(v, graph_vertices, tmp)
113 if (graph_degree(v) == 2)
115 struct graph_edge *e1 = clist_remove_head(&v->edges);
116 struct graph_edge *e2 = e1->twin;
117 struct graph_edge *f1 = clist_remove_head(&v->edges);
118 struct graph_edge *f2 = f1->twin;
119 struct graph_vertex *x = e1->dest;
120 struct graph_vertex *y = f1->dest;
121 clist_remove(&e2->n);
122 clist_remove(&f2->n);
128 struct graph_edge *g1 = graph_add_edge(x, y, NULL, e1->length + f1->length);
129 struct graph_edge *g2 = g1->twin;
130 clist_add_list_tail(&g1->ways, &e1->ways);
131 clist_add_list_tail(&g1->ways, &f1->ways);
132 clist_add_list_tail(&g2->ways, &f2->ways);
133 clist_add_list_tail(&g2->ways, &e2->ways);
138 static bool way_is_edge(struct osm_way *w)
140 osm_val_t hwy = osm_obj_find_tag(&w->o, KEY_HIGHWAY);
145 case VALUE_SECONDARY:
146 case VALUE_MOTORWAY_LINK:
147 case VALUE_TRUNK_LINK:
148 case VALUE_PRIMARY_LINK:
149 case VALUE_SECONDARY_LINK:
156 static double vertex_dist(struct graph_vertex *a, struct graph_vertex *b)
158 struct osm_node *aa = a->node;
159 struct osm_node *bb = b->node;
160 return hypot(bb->x - aa->x, bb->y - aa->y);
163 static void way_add_edge(struct osm_way *w)
165 struct graph_vertex *prev_v = NULL;
167 OSM_FOR_EACH_BEGIN(struct osm_node *, n, w->nodes)
169 struct graph_vertex *v = graph_lookup_vertex(n);
171 graph_add_edge(prev_v, v, w, vertex_dist(v, prev_v));
177 static void mark_edge_ways(struct graph_edge *e, const char *key, const char *val)
179 OSM_FOR_EACH_BEGIN(struct osm_way *, w, e->ways)
181 osm_obj_set_tag(&w->o, key, val);
186 static void graph_check_edges(void)
188 CLIST_FOR_EACH(struct graph_vertex *, v, graph_vertices)
189 CLIST_FOR_EACH(struct graph_edge *, e, v->edges)
191 double d = vertex_dist(e->dest, v);
193 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);
197 static void visualize(struct graph_vertex *v_src, struct graph_vertex *v_dest)
199 CLIST_FOR_EACH(struct graph_vertex *, x, graph_vertices)
202 osm_obj_set_tag(&x->node->o, "pruvodce", "visited");
204 CLIST_FOR_EACH(struct graph_edge *, e, x->edges)
205 mark_edge_ways(e, "pruvodce", "visited");
208 struct graph_vertex *x = v_dest;
211 struct graph_edge *e = x->via_edge->twin;
212 mark_edge_ways(e, "pruvodce", "path");
216 osm_obj_set_tag(&v_src->node->o, "pruvodce", "src");
217 osm_obj_set_tag(&v_dest->node->o, "pruvodce", "dest");
220 static void dijkstra(struct graph_vertex *v_src, struct graph_vertex *v_dest)
227 struct graph_vertex *w = NULL;
230 CLIST_FOR_EACH(struct graph_vertex *, v, graph_vertices)
234 d += vertex_dist(v, v_dest);
235 if (!w || d < best_d)
240 die("Path not found");
242 // msg(L_DEBUG, "Dijkstra: closing vertex #%jd", w->node->o.id);
246 msg(L_INFO, "Found path: dist=%f", w->dist);
247 visualize(v_src, v_dest);
252 CLIST_FOR_EACH(struct graph_edge *, e, w->edges)
254 struct graph_vertex *x = e->dest;
255 double d = w->dist + e->length;
256 // msg(L_DEBUG, "Neighbor: #%jd, state=%d, dist=%f vs. %f", x->node->o.id, x->state, x->dist, d);
257 if (x->state == 0 || x->dist > d)
260 msg(L_WARN, "Re-opening node #%jd", (intmax_t) x->node->o.id);
269 void graph_build(void)
271 graph_pool = mp_new(65536);
272 clist_init(&graph_vertices);
274 // We are considering only the first data source for the graph.
275 // Otherwise, things start becoming ugly, because node IDs are generally not unique.
277 struct data_source *ds = clist_head(&map_sources);
280 CLIST_FOR_EACH(struct osm_way *, w, osm_this->obj_list[OSM_TYPE_WAY])
285 msg(L_INFO, "Built road graph: %u vertices, %u edges", num_vertices, num_edges);
288 msg(L_INFO, "Optimized road graph: %u vertices, %u edges", num_vertices, num_edges);
292 // struct graph_vertex *v1 = graph_vertex_by_node_id(30002893); // Praha-Zbraslav, K Přehradám x 101
293 struct graph_vertex *v1 = graph_vertex_by_node_id(96745573); // Beroun, Lidická x Pražská
294 // struct graph_vertex *v2 = graph_vertex_by_node_id(21289321); // Brno, Heršpická x Opuštěná
295 // struct graph_vertex *v2 = graph_vertex_by_node_id(271385400); // Znojmo, Vídeňská třída x Brněnská
296 // struct graph_vertex *v2 = graph_vertex_by_node_id(26753344); // Šumperk, Jesenická x Lidická
297 struct graph_vertex *v2 = graph_vertex_by_node_id(714908910); // Ostrava, Mariánskohorská x Plzeňská x 28. října
298 ASSERT(graph_degree(v1) && graph_degree(v2));
299 msg(L_INFO, "Finding path from #%jd to #%jd", (uintmax_t) v1->node->o.id, (uintmax_t) v2->node->o.id);