]> mj.ucw.cz Git - leo.git/blob - map.c
Support for fixed objects
[leo.git] / map.c
1 /*
2  *      Hic Est Leo -- Global Map Operations
3  *
4  *      (c) 2014 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 "map.h"
19 #include "css.h"
20 #include "sym.h"
21 #include "fixed.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 };
48
49 static struct cf_section map_source_cf = {
50 #define P(x) PTR_TO(struct data_source, x)
51   CF_TYPE(struct data_source),
52   CF_ITEMS {
53     CF_STRING("File", P(file)),
54     CF_LOOKUP("Format", P(format), map_formats),
55     CF_LIST("StyleSheet", P(styles), &map_style_cf),
56     CF_END
57   }
58 #undef P
59 };
60
61 static struct cf_section map_cf = {
62   CF_ITEMS {
63     CF_DOUBLE("MinX", &map_min_x),
64     CF_DOUBLE("MinY", &map_min_y),
65     CF_DOUBLE("MaxX", &map_max_x),
66     CF_DOUBLE("MaxY", &map_max_y),
67     CF_DOUBLE("PageWidth", &page_width),
68     CF_DOUBLE("PageHeight", &page_height),
69     CF_UNS("Clip", &map_clip),
70     CF_UNS("Rotate", &map_rotate),
71     CF_UNS("DrawBorder", &map_draw_border),
72     CF_LIST("Source", &map_sources, &map_source_cf),
73     CF_STRING("Projection", &map_projection),
74     CF_STRING("SVGOutput", &map_svg_output),
75     CF_END
76   }
77 };
78
79 static void CONSTRUCTOR map_preinit(void)
80 {
81   cf_declare_section("Map", &map_cf, 0);
82 }
83
84 // Calculated
85 double map_scale;
86 double page_offset_x, page_offset_y;
87 double page_map_width, page_map_height;
88
89 void map_set_scale(void)
90 {
91   double x_range = map_max_x - map_min_x;
92   double y_range = map_max_y - map_min_y;
93   double x_scale = page_width / x_range;
94   double y_scale = page_height / y_range;
95   map_scale = MIN(x_scale, y_scale);
96   page_map_width = x_range * map_scale;
97   page_map_height = y_range * map_scale;
98   page_offset_x = (page_width - page_map_width) / 2;
99   page_offset_y = (page_height - page_map_height) / 2;
100
101   msg(L_INFO, "Setting scale %.3g (orig window [%.6g,%.6g], page window [%.6g,%.6g]+[%.6g,%.6g] on [%.6g,%.6g])",
102     map_scale,
103     x_range, y_range,
104     page_map_width, page_map_height,
105     page_offset_x, page_offset_y,
106     page_width, page_height);
107
108   double pmin_x = INFINITY, pmax_x = -INFINITY;
109   double pmin_y = INFINITY, pmax_y = -INFINITY;
110   double rmin_x = INFINITY, rmax_x = -INFINITY;
111   double rmin_y = INFINITY, rmax_y = -INFINITY;
112   CLIST_FOR_EACH(struct data_source *, ds, map_sources)
113     {
114       CLIST_FOR_EACH(struct osm_node *, n, ds->osm->obj_list[OSM_TYPE_NODE])
115         {
116           pmin_x = MIN(pmin_x, n->x);
117           pmax_x = MAX(pmax_x, n->x);
118           pmin_y = MIN(pmin_y, n->y);
119           pmax_y = MAX(pmax_y, n->y);
120           n->x = (n->x - map_min_x) * map_scale + page_offset_x;
121           n->y = page_height - page_offset_y - (n->y - map_min_y) * map_scale;
122           rmin_x = MIN(rmin_x, n->x);
123           rmax_x = MAX(rmax_x, n->x);
124           rmin_y = MIN(rmin_y, n->y);
125           rmax_y = MAX(rmax_y, n->y);
126         }
127     }
128   msg(L_INFO, "Bounds before scaling: [%.10g,%.10g] x [%.10g,%.10g]", pmin_x, pmax_x, pmin_y, pmax_y);
129   msg(L_INFO, "Bounds after scaling: [%.10g,%.10g] x [%.10g,%.10g]", rmin_x, rmax_x, rmin_y, rmax_y);
130
131   if (debug_dump_after_scaling)
132     {
133       puts("=== Map after scaling ===");
134       CLIST_FOR_EACH(struct data_source *, ds, map_sources)
135         {
136           osm_this = ds->osm;
137           osm_dump();
138         }
139     }
140 }
141
142 bool map_object_visible_p(struct osm_object *o)
143 {
144   double margin = 10;
145
146   switch (o->type)
147     {
148     case OSM_TYPE_NODE:
149       {
150         struct osm_node *n = (struct osm_node *) o;
151         return (n->x >= page_offset_x - margin && n->x <= page_offset_x + page_map_width + margin &&
152                 n->y >= page_offset_y - margin && n->y <= page_offset_y + page_map_height + margin);
153       }
154     case OSM_TYPE_WAY:
155       {
156         struct osm_way *w = (struct osm_way *) o;
157         bool ok = 0;
158         OSM_FOR_EACH_BEGIN(struct osm_object *, n, w->nodes)
159           {
160             ok |= map_object_visible_p(n);
161           }
162         OSM_FOR_EACH_END;
163         return ok;
164       }
165     case OSM_TYPE_RELATION:
166       {
167         struct osm_relation *r = (struct osm_relation *) o;
168         bool ok = 0;
169         OSM_FOR_EACH_BEGIN(struct osm_object *, n, r->members)
170           {
171             ok |= map_object_visible_p(n);
172           }
173         OSM_FOR_EACH_END;
174         return ok;
175       }
176     case OSM_TYPE_MULTIPOLYGON:
177       return map_object_visible_p(&((struct osm_multipolygon *) o)->rel->o);
178     default:
179       ASSERT(0);
180     }
181 }
182
183 void map_load_styles(void)
184 {
185   clist style_cache;
186   clist_init(&style_cache);
187
188   CLIST_FOR_EACH(struct data_source *, ds, map_sources)
189     {
190       CLIST_FOR_EACH(struct data_source_style *, ss, ds->styles)
191         {
192           CLIST_FOR_EACH(struct simp_node *, n, style_cache)
193             {
194               struct data_source_style *x = n->p;
195               if (!strcmp(x->name, ss->name))
196                 {
197                   ss->css = x->css;
198                   break;
199                 }
200             }
201           if (!ss->css)
202             {
203               msg(L_DEBUG, "Loading style sheet %s", ss->name);
204               ss->css = css_load(ss->name);
205
206               if (debug_dump_css)
207                 {
208                   printf("=== Stylesheet %s ===", ss->name);
209                   css_dump(ss->css);
210                 }
211
212               simp_append(cf_get_pool(), &style_cache)->p = ss;
213             }
214         }
215     }
216 }
217
218 static void map_load_source(struct data_source *ds)
219 {
220   ds->osm = osm_init();
221
222   bool need_mp = 0;
223   bool need_proj = 0;
224
225   switch (ds->format)
226     {
227     case DATA_SOURCE_OSMXML:
228       msg(L_INFO, "Parsing %s as OSM XML", ds->file);
229       if (!ds->file)
230         die("OSM XML data sources must have a file name");
231       osm_xml_parse(ds->file);
232       need_mp = 1;
233       need_proj = 1;
234       break;
235     case DATA_SOURCE_FIXED:
236       msg(L_INFO, "Adding fixed objects");
237       fixed_add();
238       break;
239     default:
240       die("Invalid data source format");
241     }
242
243   if (debug_dump_source)
244     {
245       puts("=== Source data ===");
246       osm_dump();
247     }
248   if (need_mp)
249     osm_make_multipolygons();
250
251   if (need_proj)
252     {
253       msg(L_INFO, "Projecting");
254       osm_project(map_projection);
255       if (debug_dump_after_proj)
256         {
257           puts("=== Map after projection ===");
258           osm_dump();
259         }
260     }
261 }
262
263 void map_load_sources(void)
264 {
265   CLIST_FOR_EACH(struct data_source *, ds, map_sources)
266     map_load_source(ds);
267 }
268
269 void map_apply_styles(struct svg *svg)
270 {
271   struct style_results r;
272   style_init(&r);
273
274   CLIST_FOR_EACH(struct data_source *, ds, map_sources)
275     {
276       msg(L_INFO, "Applying stylesheet on %s", ds->file);
277       for (uns i = OSM_TYPE_NODE; i <= OSM_TYPE_MULTIPOLYGON; i++)
278         CLIST_FOR_EACH(struct osm_object *, o, ds->osm->obj_list[i])
279           {
280             if (debug_dump_styling)
281               {
282                 puts("===============================");
283                 osm_obj_dump(o);
284               }
285             if (!map_object_visible_p(o))
286               {
287                 if (debug_dump_styling)
288                   printf("--> invisible\n");
289                 continue;
290               }
291             style_begin(&r, o);
292             CLIST_FOR_EACH(struct data_source_style *, ss, ds->styles)
293               css_apply(ss->css, &r);
294             if (debug_dump_styling)
295               style_dump(&r);
296             sym_from_style(o, &r, svg);
297             style_end(&r);
298           }
299     }
300
301   // FIXME: Ought to destroy the style_results
302 }