2 * Hic Est Leo -- Map Simplification
4 * (c) 2022 Martin Mares <mj@ucw.cz>
8 // #pragma GCC optimize("-O0")
13 #include <ucw/clists.h>
14 #include <ucw/mempool.h>
24 static struct mempool *simp_pool;
25 static struct osm *simp_osm;
26 static osm_id_t simp_last_id;
34 struct simp_node *snode;
38 struct osm_node *node;
39 struct simp_way_ref *prev_on_way;
40 struct simp_way_ref *next_on_way;
47 byte raw_coords[sizeof(struct coords)];
52 static void simp_hash_init_data(struct simp_node *s)
54 clist_init(&s->way_refs);
57 #define HASH_NODE struct simp_node
58 #define HASH_PREFIX(x) simp_hash_##x
59 #define HASH_KEY_MEMORY raw_coords
60 #define HASH_KEY_SIZE sizeof(struct coords)
61 #define HASH_WANT_LOOKUP
62 #define HASH_GIVE_INIT_DATA
63 #define HASH_USE_POOL simp_pool
64 #define HASH_TABLE_ALLOC
65 #include <ucw/hashtable.h>
67 static struct simp_node *simp_lookup_node(struct osm_node *n)
69 struct coords c = { .x = n->x, .y = n->y };
70 return simp_hash_lookup((byte *) &c);
73 static void simp_register_symbol(struct symbol *sym, z_index_t zindex)
75 struct osm_object *o = sym->o;
80 simp_lookup_node((struct osm_node *) o);
84 struct osm_way *w = (struct osm_way *) o;
85 struct simp_way_ref *wr_last = NULL;
86 struct osm_node *prev_n = NULL;
89 for (uint i=0; i<2; i++)
91 struct osm_node *n = (struct osm_node *) (i ? osm_ref_head(&w->nodes) : osm_ref_tail(&w->nodes));
92 struct sym_point *sp = sym_point_new(&n->o);
93 sp->shape = VALUE_CIRCLE;
95 sp->fill_color = 0x0000ff;
102 if (clist_size(&w->nodes) < 2)
105 OSM_FOR_EACH_BEGIN(struct osm_node *, n, w->nodes)
110 struct simp_node *s = simp_lookup_node(n);
111 struct simp_way_ref *wref = mp_alloc(simp_pool, sizeof(*wref));
114 wref->zindex = zindex;
117 wref->prev_on_way = wr_last;
118 wref->next_on_way = NULL;
120 wr_last->next_on_way = wref;
122 wref->merge_done = 0;
123 clist_add_tail(&s->way_refs, &wref->n);
131 die("Simplify: Unsupported symbol type %u", o->type);
135 static struct simp_way_ref *wref_other_end(struct simp_way_ref *wr)
139 while (wr->prev_on_way)
140 wr = wr->prev_on_way;
144 while (wr->next_on_way)
145 wr = wr->next_on_way;
150 static struct simp_way_ref *wref_only_other(struct simp_node *sn, struct simp_way_ref *in_wr)
152 struct simp_way_ref *out_wr = NULL;
154 CLIST_FOR_EACH(struct simp_way_ref *, wr, sn->way_refs)
167 /*** Merging of ways ***/
169 static struct simp_way_ref **merge_wrefs;
170 static uint num_merged_ways, num_merged_segments;
172 static void simp_merge_way(struct simp_node *s)
174 // XXX: This is grossly over-simplified. We assume that every way generates
175 // at most one symbol, which is definitely not the case for ways rendered
176 // with casing. But it should be sufficient for now.
178 uint nrefs = clist_size(&s->way_refs);
182 struct simp_way_ref *wr1, *wr2, *wr;
183 wr1 = clist_head(&s->way_refs);
184 wr2 = clist_tail(&s->way_refs);
185 if (!(!wr1->prev_on_way != !wr1->next_on_way && !wr2->prev_on_way != !wr2->next_on_way))
187 if (wr1->merge_done || wr2->merge_done)
191 DBG("Merge: Starting with wr %p", wr);
196 struct simp_way_ref *or = wref_other_end(wr);
197 struct simp_node *on = or->snode;
199 struct simp_way_ref *cr = wref_only_other(on, or);
200 DBG("XXX: wr=%p or=%p cr=%p", wr, or, cr);
201 if (!cr || !sym_same_attrs_p(or->sym, cr->sym))
203 DBG("Merge: Back stop at %p", cr);
207 DBG("Merge: Back to %p", cr);
212 DBG("Merge: Loop detected");
217 // Now, we are at the beginning/end of a single way
219 DBG("Merge: Back to %p", wr);
220 wr = wref_other_end(wr);
221 DBG("Merge: Other end is %p", wr);
222 GARY_RESIZE(merge_wrefs, 0);
223 *GARY_PUSH(merge_wrefs) = wr;
228 struct simp_way_ref *or = wref_other_end(wr);
229 struct simp_node *on = or->snode;
231 struct simp_way_ref *cr = wref_only_other(on, or);
232 if (!cr || !sym_same_attrs_p(or->sym, cr->sym))
234 DBG("Merge: Fwd stop at %p", cr);
238 DBG("Merge: Fwd to %p", cr);
239 *GARY_PUSH(merge_wrefs) = cr;
243 if (GARY_SIZE(merge_wrefs) < 2)
249 struct osm_way *nw = osm_way_new(++simp_last_id);
250 struct symbol *main_sym = NULL;
252 for (uint i=0; i < GARY_SIZE(merge_wrefs); i++)
255 clist_remove(&wr->n);
258 osm_way_add_node(nw, wr->node);
260 main_sym->o = &nw->o;
263 sym_disable(wr->sym);
267 while (wr = wr->prev_on_way)
269 clist_remove(&wr->n);
270 osm_way_add_node(nw, wr->node);
275 // ASSERT(wr->next_on_way);
276 while (wr = wr->next_on_way)
278 clist_remove(&wr->n);
279 osm_way_add_node(nw, wr->node);
283 num_merged_segments++;
286 // osm_obj_dump(&nw->o);
287 simp_register_symbol(main_sym, wr1->zindex);
288 // ((struct sym_line *) main_sym)->color = 0x0000ff;
291 static void simp_merge_ways(void)
293 GARY_INIT_ALLOC(merge_wrefs, 0, &simp_pool->allocator);
295 HASH_FOR_ALL(simp_hash, s)
301 msg(L_INFO, "Simplify: Merged %u segments to %u new ways", num_merged_segments, num_merged_ways);
304 /*** Splitting of ways ***/
306 static struct simp_way_ref **split_breakpoints;
307 static uint num_split_ways, num_split_segments;
309 static void simp_split_way(struct simp_way_ref *wr)
311 GARY_RESIZE(split_breakpoints, 0);
312 for (struct simp_way_ref *tr=wr; tr; tr=tr->next_on_way)
313 if (tr->prev_on_way && tr->next_on_way)
315 struct simp_node *sn = tr->snode;
316 struct simp_way_ref *x = clist_head(&sn->way_refs);
317 if (x && clist_next(&sn->way_refs, &x->n))
318 *GARY_PUSH(split_breakpoints) = tr;
321 uint num_bp = GARY_SIZE(split_breakpoints);
325 DBG("Split: Want to split wr %p (%u times)", wr, num_bp);
326 // ((struct sym_line *) wr->sym)->color = 0x0000ff;
330 struct symbol *orig_sym = wr->sym;
331 struct symbol *sym = NULL;
332 struct osm_way *w = NULL;
334 for (struct simp_way_ref *tr=wr; tr; tr=tr->next_on_way)
336 clist_remove(&tr->n);
339 w = osm_way_new(++simp_last_id);
340 sym = sym_clone(orig_sym);
343 osm_way_add_node(w, tr->node);
344 if (bp < num_bp && split_breakpoints[bp] == tr)
346 sym_plan(sym, wr->zindex);
347 num_split_segments++;
356 num_split_segments++;
357 sym_plan(sym, wr->zindex);
358 simp_register_symbol(sym, wr->zindex);
361 sym_disable(orig_sym);
364 static void simp_split_ways(void)
366 GARY_INIT_ALLOC(split_breakpoints, 0, &simp_pool->allocator);
368 HASH_FOR_ALL(simp_hash, s)
370 struct simp_way_ref *wr, *tmp;
371 CLIST_WALK_DELSAFE(wr, s->way_refs, tmp)
372 if (!wr->prev_on_way && wr->next_on_way)
377 msg(L_INFO, "Simplify: Split %u ways to %u segments", num_split_ways, num_split_segments);
380 /*** Generalization ***/
384 struct osm_ref **refs;
389 static void generalize_recursively(struct gen_context *gc, int first, int last)
391 if (last - first <= 1)
395 struct osm_node *f = (struct osm_node *) gc->refs[first]->o, *l = (struct osm_node *) gc->refs[last]->o;
398 double dx = l->x - fx;
399 double dy = l->y - fy;
400 double dd = dx*dx + dy*dy;
402 for (int i = first + 1; i < last; i++)
404 struct osm_node *n = (struct osm_node *) gc->refs[i]->o;
405 double nx = n->x - f->x;
406 double ny = n->y - f->y;
407 double p = dx*nx + dy*ny; // (px,py) = projection of (nx,ny) onto (dx,dy)
408 double px = dx*p / dd;
409 double py = dy*p / dd;
410 double ex = px - nx; // (ex,ey) is the error vector: (nx,ny) minus its projection
412 double e = ex*ex + ey*ey;
413 max_err = MAX(max_err, e);
417 if (max_err > close*close)
419 int mid = (first + last) / 2;
420 ASSERT(first < mid && mid < last);
421 generalize_recursively(gc, first, mid);
422 clist_add_tail(&gc->w->nodes, &gc->refs[mid]->n);
423 generalize_recursively(gc, mid, last);
427 static void simp_generalize_way(struct gen_context *gc, struct simp_way_ref *wr)
429 // XXX: This does not connect the way back to the simplification graph,
430 // but it does not hurt since generalization is the last pass.
432 struct osm_way *w = wr->way;
434 GARY_RESIZE(gc->refs, 0);
436 CLIST_FOR_EACH(struct osm_ref *, r, w->nodes)
437 *GARY_PUSH(gc->refs) = r;
439 int N = GARY_SIZE(gc->refs);
446 for (int i=0; i<N; i++)
448 struct osm_node *n = (struct osm_node *) gc->refs[i]->o;
449 msg(L_DEBUG, "Generalize: @%d #%jd [%f,%f]", i, (intmax_t) n->o.id, n->x, n->y);
453 clist_init(&w->nodes);
454 clist_add_tail(&w->nodes, &gc->refs[0]->n);
455 generalize_recursively(gc, 0, N-1);
456 clist_add_tail(&w->nodes, &gc->refs[N-1]->n);
458 CLIST_FOR_EACH(struct osm_ref *, r, w->nodes)
462 static void simp_generalize_ways(void)
464 struct gen_context gc = { };
465 GARY_INIT_ALLOC(gc.refs, 0, &simp_pool->allocator);
467 HASH_FOR_ALL(simp_hash, s)
469 struct simp_way_ref *wr, *tmp;
470 CLIST_WALK_DELSAFE(wr, s->way_refs, tmp)
471 if (!wr->prev_on_way && wr->next_on_way)
472 simp_generalize_way(&gc, wr);
476 msg(L_INFO, "Generalization: %u nodes in, %u nodes out", gc.in_nodes, gc.out_nodes);
479 /*** Entry point ***/
483 msg(L_INFO, "Simplifying symbol topology");
484 simp_pool = mp_new(65536);
485 simp_osm = osm_init();
488 sym_for_all_planned(simp_register_symbol);
491 simp_generalize_ways();
493 mp_delete(simp_pool);