]> mj.ucw.cz Git - leo.git/blobdiff - map.c
Průvodce: Dijkstra and its visualization
[leo.git] / map.c
diff --git a/map.c b/map.c
index 455d7aab19c3206c95b81ed79df881d4163790e3..5b8e92bdab73ccca964bee38707166546974056c 100644 (file)
--- a/map.c
+++ b/map.c
@@ -4,17 +4,22 @@
  *     (c) 2014 Martin Mares <mj@ucw.cz>
  */
 
-#include <ucw/lib.h>
+#include "leo.h"
+
 #include <ucw/conf.h>
 #include <ucw/gary.h>
 #include <ucw/mempool.h>
+#include <ucw/simple-lists.h>
 
 #include <stdio.h>
 #include <math.h>
 
-#include "leo.h"
 #include "osm.h"
+#include "shp.h"
 #include "map.h"
+#include "css.h"
+#include "sym.h"
+#include "fixed.h"
 
 double map_min_x, map_min_y;
 double map_max_x, map_max_y;
@@ -24,6 +29,36 @@ char *map_xml_input;
 char *map_projection;
 char *map_style_sheet;
 char *map_svg_output;
+clist map_sources;
+
+static struct cf_section map_style_cf = {
+#define P(x) PTR_TO(struct data_source_style, x)
+  CF_TYPE(struct data_source_style),
+  CF_ITEMS {
+    CF_STRING("Name", P(name)),
+    CF_END
+  }
+#undef P
+};
+
+static const char * const map_formats[] = {
+  "invalid",
+  "osmxml",
+  "fixed",
+  "shape",
+};
+
+static struct cf_section map_source_cf = {
+#define P(x) PTR_TO(struct data_source, x)
+  CF_TYPE(struct data_source),
+  CF_ITEMS {
+    CF_STRING("File", P(file)),
+    CF_LOOKUP("Format", P(format), map_formats),
+    CF_LIST("StyleSheet", P(styles), &map_style_cf),
+    CF_END
+  }
+#undef P
+};
 
 static struct cf_section map_cf = {
   CF_ITEMS {
@@ -36,9 +71,8 @@ static struct cf_section map_cf = {
     CF_UNS("Clip", &map_clip),
     CF_UNS("Rotate", &map_rotate),
     CF_UNS("DrawBorder", &map_draw_border),
-    CF_STRING("XMLInput", &map_xml_input),
+    CF_LIST("Source", &map_sources, &map_source_cf),
     CF_STRING("Projection", &map_projection),
-    CF_STRING("StyleSheet", &map_style_sheet),
     CF_STRING("SVGOutput", &map_svg_output),
     CF_END
   }
@@ -77,21 +111,34 @@ void map_set_scale(void)
   double pmin_y = INFINITY, pmax_y = -INFINITY;
   double rmin_x = INFINITY, rmax_x = -INFINITY;
   double rmin_y = INFINITY, rmax_y = -INFINITY;
-  CLIST_FOR_EACH(struct osm_node *, n, osm_obj_list[OSM_TYPE_NODE])
+  CLIST_FOR_EACH(struct data_source *, ds, map_sources)
     {
-      pmin_x = MIN(pmin_x, n->x);
-      pmax_x = MAX(pmax_x, n->x);
-      pmin_y = MIN(pmin_y, n->y);
-      pmax_y = MAX(pmax_y, n->y);
-      n->x = (n->x - map_min_x) * map_scale + page_offset_x;
-      n->y = page_height - page_offset_y - (n->y - map_min_y) * map_scale;
-      rmin_x = MIN(rmin_x, n->x);
-      rmax_x = MAX(rmax_x, n->x);
-      rmin_y = MIN(rmin_y, n->y);
-      rmax_y = MAX(rmax_y, n->y);
+      CLIST_FOR_EACH(struct osm_node *, n, ds->osm->obj_list[OSM_TYPE_NODE])
+       {
+         pmin_x = MIN(pmin_x, n->x);
+         pmax_x = MAX(pmax_x, n->x);
+         pmin_y = MIN(pmin_y, n->y);
+         pmax_y = MAX(pmax_y, n->y);
+         n->x = (n->x - map_min_x) * map_scale + page_offset_x;
+         n->y = page_height - page_offset_y - (n->y - map_min_y) * map_scale;
+         rmin_x = MIN(rmin_x, n->x);
+         rmax_x = MAX(rmax_x, n->x);
+         rmin_y = MIN(rmin_y, n->y);
+         rmax_y = MAX(rmax_y, n->y);
+       }
     }
   msg(L_INFO, "Bounds before scaling: [%.10g,%.10g] x [%.10g,%.10g]", pmin_x, pmax_x, pmin_y, pmax_y);
   msg(L_INFO, "Bounds after scaling: [%.10g,%.10g] x [%.10g,%.10g]", rmin_x, rmax_x, rmin_y, rmax_y);
+
+  if (debug_dump_after_scaling)
+    {
+      puts("=== Map after scaling ===");
+      CLIST_FOR_EACH(struct data_source *, ds, map_sources)
+       {
+         osm_this = ds->osm;
+         osm_dump();
+       }
+    }
 }
 
 bool map_object_visible_p(struct osm_object *o)
@@ -134,3 +181,225 @@ bool map_object_visible_p(struct osm_object *o)
       ASSERT(0);
     }
 }
+
+void map_load_styles(void)
+{
+  clist style_cache;
+  clist_init(&style_cache);
+
+  CLIST_FOR_EACH(struct data_source *, ds, map_sources)
+    {
+      CLIST_FOR_EACH(struct data_source_style *, ss, ds->styles)
+       {
+         CLIST_FOR_EACH(struct simp_node *, n, style_cache)
+           {
+             struct data_source_style *x = n->p;
+             if (!strcmp(x->name, ss->name))
+               {
+                 ss->css = x->css;
+                 break;
+               }
+           }
+         if (!ss->css)
+           {
+             msg(L_DEBUG, "Loading style sheet %s", ss->name);
+             ss->css = css_load(ss->name);
+
+             if (debug_dump_css)
+               {
+                 printf("=== Stylesheet %s ===", ss->name);
+                 css_dump(ss->css);
+               }
+
+             simp_append(cf_get_pool(), &style_cache)->p = ss;
+           }
+       }
+    }
+}
+
+static void map_load_source(struct data_source *ds)
+{
+  ds->osm = osm_init();
+
+  bool need_mp = 0;
+  bool need_proj = 0;
+
+  switch (ds->format)
+    {
+    case DATA_SOURCE_OSMXML:
+      msg(L_INFO, "Parsing %s as OSM XML", ds->file);
+      if (!ds->file)
+       die("OSM XML data sources must have a file name");
+      osm_xml_parse(ds->file);
+      need_mp = 1;
+      need_proj = 1;
+      break;
+    case DATA_SOURCE_FIXED:
+      msg(L_INFO, "Adding fixed objects");
+      if (!ds->file)
+       ds->file = "fixed";
+      fixed_add();
+      break;
+    case DATA_SOURCE_SHAPE:
+      msg(L_INFO, "Parsing %s as shape file", ds->file);
+      if (!ds->file)
+       die("Shape data sources must have a file name");
+      shp_parse(ds->file);
+      need_proj = 1;
+      break;
+    default:
+      die("Invalid data source format");
+    }
+
+  osm_stats();
+  if (debug_dump_source)
+    {
+      puts("=== Source data ===");
+      osm_dump();
+    }
+  if (need_mp)
+    osm_make_multipolygons();
+
+  if (need_proj)
+    {
+      msg(L_INFO, "Projecting");
+      osm_project(map_projection);
+      if (debug_dump_after_proj)
+       {
+         puts("=== Map after projection ===");
+         osm_dump();
+       }
+    }
+}
+
+void map_load_sources(void)
+{
+  CLIST_FOR_EACH(struct data_source *, ds, map_sources)
+    map_load_source(ds);
+}
+
+void map_apply_styles(struct svg *svg)
+{
+  struct style_results r;
+  style_init(&r);
+
+  CLIST_FOR_EACH(struct data_source *, ds, map_sources)
+    {
+      msg(L_INFO, "Applying stylesheet on %s", ds->file);
+      for (uns i = OSM_TYPE_NODE; i <= OSM_TYPE_MULTIPOLYGON; i++)
+       CLIST_FOR_EACH(struct osm_object *, o, ds->osm->obj_list[i])
+         {
+           if (debug_dump_styling)
+             {
+               puts("===============================");
+               osm_obj_dump(o);
+             }
+           if (!map_object_visible_p(o))
+             {
+               if (debug_dump_styling)
+                 printf("--> invisible\n");
+               continue;
+             }
+           style_begin(&r, o);
+           CLIST_FOR_EACH(struct data_source_style *, ss, ds->styles)
+             css_apply(ss->css, &r);
+           if (debug_dump_styling)
+             style_dump(&r);
+           sym_from_style(o, &r, svg);
+           style_end(&r);
+         }
+    }
+
+  // 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 = 1;
+  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);
+}