]> mj.ucw.cz Git - leo.git/blob - map.c
Logo can be drawn as a fixed object now, no need for special hacks in leo.c
[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_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 void map_apply_styles(struct svg *svg)
284 {
285   struct style_results r;
286   style_init(&r);
287
288   CLIST_FOR_EACH(struct data_source *, ds, map_sources)
289     {
290       msg(L_INFO, "Applying stylesheet on %s", ds->file);
291       for (uns i = OSM_TYPE_NODE; i <= OSM_TYPE_MULTIPOLYGON; i++)
292         CLIST_FOR_EACH(struct osm_object *, o, ds->osm->obj_list[i])
293           {
294             if (debug_dump_styling)
295               {
296                 puts("===============================");
297                 osm_obj_dump(o);
298               }
299             if (!map_object_visible_p(o))
300               {
301                 if (debug_dump_styling)
302                   printf("--> invisible\n");
303                 continue;
304               }
305             style_begin(&r, o);
306             CLIST_FOR_EACH(struct data_source_style *, ss, ds->styles)
307               css_apply(ss->css, &r);
308             if (debug_dump_styling)
309               style_dump(&r);
310             sym_from_style(o, &r, svg);
311             style_end(&r);
312           }
313     }
314
315   // FIXME: Ought to destroy the style_results
316 }