]> mj.ucw.cz Git - leo.git/blob - map.c
TODO
[leo.git] / map.c
1 /*
2  *      Hic Est Leo -- Global Map Operations
3  *
4  *      (c) 2014--2015 Martin Mares <mj@ucw.cz>
5  */
6
7 #include "leo.h"
8 #include "osm.h"
9 #include "shp.h"
10 #include "map.h"
11 #include "css.h"
12 #include "sym.h"
13 #include "fixed.h"
14
15 #include <ucw/conf.h>
16 #include <ucw/gary.h>
17 #include <ucw/mempool.h>
18 #include <ucw/simple-lists.h>
19
20 #include <stdio.h>
21 #include <math.h>
22
23 double map_min_x, map_min_y;
24 double map_max_x, map_max_y;
25 double page_width, page_height;
26 uns map_clip, map_rotate, map_draw_border;
27 char *map_xml_input;
28 char *map_projection;
29 char *map_style_sheet;
30 char *map_svg_output;
31 clist map_sources;
32
33 static struct cf_section map_style_cf = {
34 #define P(x) PTR_TO(struct data_source_style, x)
35   CF_TYPE(struct data_source_style),
36   CF_ITEMS {
37     CF_STRING("Name", P(name)),
38     CF_END
39   }
40 #undef P
41 };
42
43 static const char * const map_formats[] = {
44   "invalid",
45   "osmxml",
46   "fixed",
47   "shape",
48 };
49
50 static struct cf_section map_source_cf = {
51 #define P(x) PTR_TO(struct data_source, x)
52   CF_TYPE(struct data_source),
53   CF_ITEMS {
54     CF_STRING("File", P(file)),
55     CF_LOOKUP("Format", P(format), map_formats),
56     CF_LIST("StyleSheet", P(styles), &map_style_cf),
57     CF_INT("InlineStyles", P(inline_styles)),
58     CF_END
59   }
60 #undef P
61 };
62
63 static struct cf_section map_cf = {
64   CF_ITEMS {
65     CF_DOUBLE("MinX", &map_min_x),
66     CF_DOUBLE("MinY", &map_min_y),
67     CF_DOUBLE("MaxX", &map_max_x),
68     CF_DOUBLE("MaxY", &map_max_y),
69     CF_DOUBLE("PageWidth", &page_width),
70     CF_DOUBLE("PageHeight", &page_height),
71     CF_UNS("Clip", &map_clip),
72     CF_UNS("Rotate", &map_rotate),
73     CF_UNS("DrawBorder", &map_draw_border),
74     CF_LIST("Source", &map_sources, &map_source_cf),
75     CF_STRING("Projection", &map_projection),
76     CF_STRING("SVGOutput", &map_svg_output),
77     CF_END
78   }
79 };
80
81 static void CONSTRUCTOR map_preinit(void)
82 {
83   cf_declare_section("Map", &map_cf, 0);
84 }
85
86 // Calculated
87 double map_scale;
88 double page_offset_x, page_offset_y;
89 double page_map_width, page_map_height;
90
91 void map_set_scale(void)
92 {
93   double x_range = map_max_x - map_min_x;
94   double y_range = map_max_y - map_min_y;
95   double x_scale = page_width / x_range;
96   double y_scale = page_height / y_range;
97   map_scale = MIN(x_scale, y_scale);
98   page_map_width = x_range * map_scale;
99   page_map_height = y_range * map_scale;
100   page_offset_x = (page_width - page_map_width) / 2;
101   page_offset_y = (page_height - page_map_height) / 2;
102
103   msg(L_INFO, "Setting scale %.3g (orig window [%.6g,%.6g], page window [%.6g,%.6g]+[%.6g,%.6g] on [%.6g,%.6g])",
104     map_scale,
105     x_range, y_range,
106     page_map_width, page_map_height,
107     page_offset_x, page_offset_y,
108     page_width, page_height);
109
110   double pmin_x = INFINITY, pmax_x = -INFINITY;
111   double pmin_y = INFINITY, pmax_y = -INFINITY;
112   double rmin_x = INFINITY, rmax_x = -INFINITY;
113   double rmin_y = INFINITY, rmax_y = -INFINITY;
114   CLIST_FOR_EACH(struct data_source *, ds, map_sources)
115     {
116       if (ds->format == DATA_SOURCE_FIXED)
117         continue;
118       CLIST_FOR_EACH(struct osm_node *, n, ds->osm->obj_list[OSM_TYPE_NODE])
119         {
120           pmin_x = MIN(pmin_x, n->x);
121           pmax_x = MAX(pmax_x, n->x);
122           pmin_y = MIN(pmin_y, n->y);
123           pmax_y = MAX(pmax_y, n->y);
124           n->x = (n->x - map_min_x) * map_scale + page_offset_x;
125           n->y = page_height - page_offset_y - (n->y - map_min_y) * map_scale;
126           rmin_x = MIN(rmin_x, n->x);
127           rmax_x = MAX(rmax_x, n->x);
128           rmin_y = MIN(rmin_y, n->y);
129           rmax_y = MAX(rmax_y, n->y);
130         }
131     }
132   msg(L_INFO, "Bounds before scaling: [%.10g,%.10g] x [%.10g,%.10g]", pmin_x, pmax_x, pmin_y, pmax_y);
133   msg(L_INFO, "Bounds after scaling: [%.10g,%.10g] x [%.10g,%.10g]", rmin_x, rmax_x, rmin_y, rmax_y);
134
135   if (debug_dump_after_scaling)
136     {
137       puts("=== Map after scaling ===");
138       CLIST_FOR_EACH(struct data_source *, ds, map_sources)
139         {
140           osm_this = ds->osm;
141           osm_dump();
142         }
143     }
144 }
145
146 bool map_object_visible_p(struct osm_object *o)
147 {
148   double margin = 10;
149
150   switch (o->type)
151     {
152     case OSM_TYPE_NODE:
153       {
154         struct osm_node *n = (struct osm_node *) o;
155         return (n->x >= page_offset_x - margin && n->x <= page_offset_x + page_map_width + margin &&
156                 n->y >= page_offset_y - margin && n->y <= page_offset_y + page_map_height + margin);
157       }
158     case OSM_TYPE_WAY:
159       {
160         struct osm_way *w = (struct osm_way *) o;
161         bool ok = 0;
162         OSM_FOR_EACH_BEGIN(struct osm_object *, n, w->nodes)
163           {
164             ok |= map_object_visible_p(n);
165           }
166         OSM_FOR_EACH_END;
167         return ok;
168       }
169     case OSM_TYPE_RELATION:
170       {
171         struct osm_relation *r = (struct osm_relation *) o;
172         bool ok = 0;
173         OSM_FOR_EACH_BEGIN(struct osm_object *, n, r->members)
174           {
175             ok |= map_object_visible_p(n);
176           }
177         OSM_FOR_EACH_END;
178         return ok;
179       }
180     case OSM_TYPE_MULTIPOLYGON:
181       return map_object_visible_p(&((struct osm_multipolygon *) o)->rel->o);
182     default:
183       ASSERT(0);
184     }
185 }
186
187 void map_load_styles(void)
188 {
189   clist style_cache;
190   clist_init(&style_cache);
191
192   CLIST_FOR_EACH(struct data_source *, ds, map_sources)
193     {
194       CLIST_FOR_EACH(struct data_source_style *, ss, ds->styles)
195         {
196           CLIST_FOR_EACH(struct simp_node *, n, style_cache)
197             {
198               struct data_source_style *x = n->p;
199               if (!strcmp(x->name, ss->name))
200                 {
201                   ss->css = x->css;
202                   break;
203                 }
204             }
205           if (!ss->css)
206             {
207               msg(L_DEBUG, "Loading style sheet %s", ss->name);
208               ss->css = css_load(ss->name);
209
210               if (debug_dump_css)
211                 {
212                   printf("=== Stylesheet %s ===", ss->name);
213                   css_dump(ss->css);
214                 }
215
216               simp_append(cf_get_pool(), &style_cache)->p = ss;
217             }
218         }
219     }
220 }
221
222 static void map_load_source(struct data_source *ds)
223 {
224   ds->osm = osm_init();
225
226   bool need_mp = 0;
227   bool need_proj = 0;
228
229   switch (ds->format)
230     {
231     case DATA_SOURCE_OSMXML:
232       msg(L_INFO, "Parsing %s as OSM XML", ds->file);
233       if (!ds->file)
234         die("OSM XML data sources must have a file name");
235       osm_xml_parse(ds->file);
236       need_mp = 1;
237       need_proj = 1;
238       break;
239     case DATA_SOURCE_FIXED:
240       msg(L_INFO, "Adding fixed objects");
241       if (!ds->file)
242         ds->file = "fixed";
243       fixed_add();
244       break;
245     case DATA_SOURCE_SHAPE:
246       msg(L_INFO, "Parsing %s as shape file", ds->file);
247       if (!ds->file)
248         die("Shape data sources must have a file name");
249       shp_parse(ds->file);
250       need_proj = 1;
251       break;
252     default:
253       die("Invalid data source format");
254     }
255
256   osm_stats();
257   if (debug_dump_source)
258     {
259       puts("=== Source data ===");
260       osm_dump();
261     }
262   if (need_mp)
263     osm_make_multipolygons();
264
265   if (need_proj)
266     {
267       msg(L_INFO, "Projecting");
268       osm_project(map_projection);
269       if (debug_dump_after_proj)
270         {
271           puts("=== Map after projection ===");
272           osm_dump();
273         }
274     }
275 }
276
277 void map_load_sources(void)
278 {
279   CLIST_FOR_EACH(struct data_source *, ds, map_sources)
280     map_load_source(ds);
281 }
282
283 static void map_apply_inline_styles(struct osm_object *o, struct style_results *r)
284 {
285   char *name = NULL;
286
287   CLIST_FOR_EACH(struct osm_tag *, t, o->tags)
288     {
289       const char *key = osm_key_decode(t->key);
290       if (!strncmp(key, "style:", 6))
291         {
292           key += 6;
293           layer_t layer = STYLE_LAYER_DEFAULT;
294           char *sep = strstr(key, "::");
295           if (sep)
296             {
297               if (sep[2])
298                 {
299                   // XXX: Only layers defined in the main stylesheet can be modified
300                   layer = style_layer_encode_if_exists(sep+2);
301                   if (!layer)
302                     goto skip;
303                 }
304               int keylen = sep - key;
305               char *t = mp_alloc(r->pool, keylen+1);
306               memcpy(t, key, keylen);
307               t[keylen] = 0;
308               key = t;
309             }
310
311           if (!name)
312             name = mp_printf(r->pool, "inline style of object #%ju", (uintmax_t) o->id);
313           struct style_prop *p= css_parse_prop(r->pool, name, key, osm_val_decode(t->val));
314           style_set_by_layer(r, layer, p);
315 skip: ;
316         }
317     }
318 }
319
320 void map_apply_styles(struct svg *svg)
321 {
322   struct style_results r;
323   style_init(&r);
324
325   CLIST_FOR_EACH(struct data_source *, ds, map_sources)
326     {
327       msg(L_INFO, "Applying stylesheet on %s", ds->file);
328       for (uns i = OSM_TYPE_NODE; i <= OSM_TYPE_MULTIPOLYGON; i++)
329         CLIST_FOR_EACH(struct osm_object *, o, ds->osm->obj_list[i])
330           {
331             if (debug_dump_styling)
332               {
333                 puts("===============================");
334                 osm_obj_dump(o);
335               }
336             if (!map_object_visible_p(o))
337               {
338                 if (debug_dump_styling)
339                   printf("--> invisible\n");
340                 continue;
341               }
342             style_begin(&r, o);
343             CLIST_FOR_EACH(struct data_source_style *, ss, ds->styles)
344               css_apply(ss->css, &r);
345             if (ds->inline_styles)
346               map_apply_inline_styles(o, &r);
347             if (debug_dump_styling)
348               style_dump(&r);
349             sym_from_style(o, &r, svg);
350             style_end(&r);
351           }
352     }
353
354   style_cleanup(&r);
355 }