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