]> mj.ucw.cz Git - leo.git/commitdiff
An attempt at simple cartographic generalization
authorMartin Mares <mj@ucw.cz>
Sun, 13 Mar 2022 20:41:38 +0000 (21:41 +0100)
committerMartin Mares <mj@ucw.cz>
Sun, 13 Mar 2022 20:41:38 +0000 (21:41 +0100)
leo.c
map.c
map.h

diff --git a/leo.c b/leo.c
index b30996581f424d104a7775d91494fd0430810209..69ee1e39167e501d61ff6c590d297b56ef7409e0 100644 (file)
--- a/leo.c
+++ b/leo.c
@@ -121,6 +121,7 @@ int main(int argc UNUSED, char **argv)
   map_load_styles();
   map_load_sources();
   map_set_scale();
+  map_generalize();
 
   struct svg *svg = svg_open(map_svg_output);
   if (!map_rotate)
diff --git a/map.c b/map.c
index bbcd7463b1f33de9864a3becb632b2b8c2c3f50e..fc377bcf45631790b56277463ec79000f9a8206a 100644 (file)
--- a/map.c
+++ b/map.c
@@ -312,3 +312,94 @@ void map_apply_styles(struct svg *svg)
 
   // FIXME: Ought to destroy the style_results
 }
+
+struct gen_context {
+  struct osm_way *w;
+  struct osm_ref **refs;
+  uint in_nodes;
+  uint out_nodes;
+};
+
+static void generalize_recursively(struct gen_context *gc, int first, int last)
+{
+  if (last - first <= 1)
+    return;
+
+  double max_err = 0;
+  struct osm_node *f = (struct osm_node *) gc->refs[first]->o, *l = (struct osm_node *) gc->refs[last]->o;
+  double fx = f->x;
+  double fy = f->y;
+  double dx = l->x - fx;
+  double dy = l->y - fy;
+  double dd = dx*dx + dy*dy;
+
+  for (int i = first + 1; i < last; i++)
+    {
+      struct osm_node *n = (struct osm_node *) gc->refs[i]->o;
+      double nx = n->x - f->x;
+      double ny = n->y - f->y;
+      double p = dx*nx + dy*ny;                // (px,py) = projection of (nx,ny) onto (dx,dy)
+      double px = dx*p / dd;
+      double py = dy*p / dd;
+      double ex = px - nx;             // (ex,ey) is the error vector: (nx,ny) minus its projection
+      double ey = py - ny;
+      double e = ex*ex + ey*ey;
+      max_err = MAX(max_err, e);
+    }
+
+  double close = 5;
+  if (max_err > close*close)
+    {
+      int mid = (first + last) / 2;
+      ASSERT(first < mid && mid < last);
+      generalize_recursively(gc, first, mid);
+      clist_add_tail(&gc->w->nodes, &gc->refs[mid]->n);
+      generalize_recursively(gc, mid, last);
+    }
+}
+
+static void generalize_way(struct gen_context *gc, struct osm_way *w)
+{
+  gc->w = w;
+  GARY_RESIZE(gc->refs, 0);
+
+  CLIST_FOR_EACH(struct osm_ref *, r, w->nodes)
+    *GARY_PUSH(gc->refs) = r;
+
+  int N = GARY_SIZE(gc->refs);
+  if (N <= 2)
+    return;
+
+  gc->in_nodes += N;
+
+#if 0
+  for (int i=0; i<N; i++)
+    {
+      struct osm_node *n = (struct osm_node *) gc->refs[i]->o;
+      msg(L_DEBUG, "Generalize: @%d #%jd [%f,%f]", i, (intmax_t) n->o.id, n->x, n->y);
+    }
+#endif
+
+  clist_init(&w->nodes);
+  clist_add_tail(&w->nodes, &gc->refs[0]->n);
+  generalize_recursively(gc, 0, N-1);
+  clist_add_tail(&w->nodes, &gc->refs[N-1]->n);
+
+  CLIST_FOR_EACH(struct osm_ref *, r, w->nodes)
+    gc->out_nodes++;
+}
+
+void map_generalize(void)
+{
+  msg(L_INFO, "Generalizing ways");
+  struct gen_context gc = { };
+  GARY_INIT(gc.refs, 0);
+
+  CLIST_FOR_EACH(struct data_source *, ds, map_sources)
+    CLIST_FOR_EACH(struct osm_way *, w, ds->osm->obj_list[OSM_TYPE_WAY])
+      generalize_way(&gc, w);
+
+  GARY_FREE(gc.refs);
+
+  msg(L_INFO, "Generalization: %u nodes in, %u nodes out", gc.in_nodes, gc.out_nodes);
+}
diff --git a/map.h b/map.h
index 9dd80a795bea79538b51b186ef7f7ad3278959bf..19e610e0fb252df80702cc6cb51b6a07452461db 100644 (file)
--- a/map.h
+++ b/map.h
@@ -55,6 +55,8 @@ extern double page_map_width, page_map_height;
 void map_set_scale(void);
 bool map_object_visible_p(struct osm_object *o);
 
+void map_generalize(void);
+
 void map_load_styles(void);
 void map_load_sources(void);
 void map_apply_styles(struct svg *svg);