Contents of /src/josm_elemstyles.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations)
Tue Dec 9 20:06:06 2008 UTC (15 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 12077 byte(s)
Initial import
1 /*
2 * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3 *
4 * This file is part of OSM2Go.
5 *
6 * OSM2Go is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * OSM2Go is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "appdata.h"
21
22 #include <libxml/parser.h>
23 #include <libxml/tree.h>
24
25 #ifndef LIBXML_TREE_ENABLED
26 #error "Tree not enabled in libxml"
27 #endif
28
29 // ratio conversions
30
31 // Scaling constants. Our "zoom" is a screenpx:canvasunit ratio, and the figure
32 // given by an elemstyles.xml is the denominator of a screen:real ratio.
33
34 #define N810_PX_PER_METRE (800 / 0.09)
35 // XXX should probably ask the windowing system for DPI and
36 // work from that instead
37
38 inline float scaledn_to_zoom(const float scaledn) {
39 return N810_PX_PER_METRE / scaledn;
40 }
41
42 inline float zoom_to_scaledn(const float zoom) {
43 return N810_PX_PER_METRE / zoom;
44 }
45
46
47 /* --------------------- elemstyles.xml parsing ----------------------- */
48
49 gboolean parse_color(xmlNode *a_node, char *name,
50 elemstyle_color_t *color) {
51 char *color_str = (char*)xmlGetProp(a_node, BAD_CAST name);
52 if(color_str) {
53 GdkColor gdk_color;
54 if(gdk_color_parse(color_str, &gdk_color)) {
55 *color =
56 ((gdk_color.red << 8) & 0xff0000) |
57 ((gdk_color.green ) & 0xff00) |
58 ((gdk_color.blue >> 8) & 0xff);
59
60 xmlFree(color_str);
61 return TRUE;
62 }
63 xmlFree(color_str);
64 }
65 return FALSE;
66 }
67
68 static gboolean parse_gint(xmlNode *a_node, char *name, gint *val) {
69 char *num_str = (char*)xmlGetProp(a_node, BAD_CAST name);
70 if(num_str) {
71 *val = strtoul(num_str, NULL, 10);
72 xmlFree(num_str);
73 return TRUE;
74 }
75 return FALSE;
76 }
77
78 static gboolean parse_scale_max(xmlNode *a_node, float *val) {
79 xmlChar *val_str = xmlNodeGetContent(a_node);
80 if (val_str) {
81 *val = scaledn_to_zoom(strtod((char *)val_str, NULL));
82 xmlFree(val_str);
83 return TRUE;
84 }
85 return FALSE;
86 }
87
88 static elemstyle_line_t *parse_line(xmlDocPtr doc, xmlNode *a_node) {
89 elemstyle_line_t *line = g_new0(elemstyle_line_t, 1);
90
91 /* these have to be present */
92 g_assert(parse_color(a_node, "colour", &line->color));
93 g_assert(parse_gint(a_node, "width", &line->width));
94
95 line->real.valid =
96 parse_gint(a_node, "realwidth", &line->real.width);
97
98 line->bg.valid =
99 parse_gint(a_node, "width_bg", &line->bg.width) &&
100 parse_color(a_node, "colour_bg", &line->bg.color);
101
102 return line;
103 }
104
105 static elemstyle_area_t *parse_area(xmlDocPtr doc, xmlNode *a_node) {
106 elemstyle_area_t *area = g_new0(elemstyle_area_t, 1);
107
108 /* these have to be present */
109 g_assert(parse_color(a_node, "colour", &area->color));
110 return area;
111 }
112
113 static elemstyle_icon_t *parse_icon(xmlDocPtr doc, xmlNode *a_node) {
114 elemstyle_icon_t *icon = g_new0(elemstyle_icon_t, 1);
115
116 char *annotate = (char*)xmlGetProp(a_node, BAD_CAST "annotate");
117 if(annotate) {
118 icon->annotate = (strcasecmp(annotate, "true")==0);
119 xmlFree(annotate);
120 }
121
122 icon->filename = (char*)xmlGetProp(a_node, BAD_CAST "src");
123 g_assert(icon->filename);
124
125 icon->filename = josm_icon_name_adjust(icon->filename);
126
127 icon->zoom_max = 0;
128
129 return icon;
130 }
131
132 static elemstyle_t *parse_rule(xmlDocPtr doc, xmlNode *a_node) {
133 xmlNode *cur_node = NULL;
134 elemstyle_t *elemstyle = g_new0(elemstyle_t, 1);
135
136 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
137 if (cur_node->type == XML_ELEMENT_NODE) {
138 if(strcasecmp((char*)cur_node->name, "condition") == 0) {
139 /* ------ parse condition ------ */
140 elemstyle->condition.key = (char*)xmlGetProp(cur_node, BAD_CAST "k");
141 elemstyle->condition.value = (char*)xmlGetProp(cur_node, BAD_CAST "v");
142 } else if(strcasecmp((char*)cur_node->name, "line") == 0) {
143 /* ------ parse line ------ */
144 g_assert(elemstyle->type == ES_TYPE_NONE);
145 elemstyle->type = ES_TYPE_LINE;
146 elemstyle->line = parse_line(doc, cur_node);
147 } else if(strcasecmp((char*)cur_node->name, "area") == 0) {
148 /* ------ parse area ------ */
149 g_assert(elemstyle->type == ES_TYPE_NONE);
150 elemstyle->type = ES_TYPE_AREA;
151 elemstyle->area = parse_area(doc, cur_node);
152 } else if(strcasecmp((char*)cur_node->name, "icon") == 0) {
153 elemstyle->icon = parse_icon(doc, cur_node);
154 } else if(strcasecmp((char*)cur_node->name, "scale_min") == 0) {
155 /* scale_min is currently ignored */
156 } else if(strcasecmp((char*)cur_node->name, "scale_max") == 0) {
157 switch (elemstyle->type) {
158 case ES_TYPE_LINE:
159 parse_scale_max(cur_node, &elemstyle->line->zoom_max);
160 break;
161 case ES_TYPE_AREA:
162 parse_scale_max(cur_node, &elemstyle->area->zoom_max);
163 break;
164 default:
165 if (elemstyle->icon) {
166 parse_scale_max(cur_node, &elemstyle->icon->zoom_max);
167 }
168 else {
169 printf("scale_max for unhandled elemstyletype=0x02%x\n", elemstyle->type);
170 }
171 break;
172 }
173 } else {
174 printf("found unhandled rules/rule/%s\n", cur_node->name);
175 }
176 }
177 }
178
179 return elemstyle;
180 }
181
182 static elemstyle_t *parse_rules(xmlDocPtr doc, xmlNode *a_node) {
183 xmlNode *cur_node = NULL;
184 elemstyle_t *elemstyles = NULL, **elemstyle = &elemstyles;
185
186 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
187 if (cur_node->type == XML_ELEMENT_NODE) {
188 if(strcasecmp((char*)cur_node->name, "rule") == 0) {
189 *elemstyle = parse_rule(doc, cur_node);
190 if(*elemstyle) elemstyle = &((*elemstyle)->next);
191 } else
192 printf("found unhandled rules/%s\n", cur_node->name);
193 }
194 }
195 return elemstyles;
196 }
197
198 static elemstyle_t *parse_doc(xmlDocPtr doc) {
199 /* Get the root element node */
200 xmlNode *cur_node = NULL;
201 elemstyle_t *elemstyles = NULL;
202
203 for(cur_node = xmlDocGetRootElement(doc);
204 cur_node; cur_node = cur_node->next) {
205 if (cur_node->type == XML_ELEMENT_NODE) {
206 if(strcasecmp((char*)cur_node->name, "rules") == 0) {
207 elemstyles = parse_rules(doc, cur_node);
208 } else
209 printf("found unhandled %s\n", cur_node->name);
210 }
211 }
212
213 xmlFreeDoc(doc);
214 xmlCleanupParser();
215 return elemstyles;
216 }
217
218 elemstyle_t *josm_elemstyles_load(char *name) {
219 elemstyle_t *elemstyles = NULL;
220
221 printf("Loading JOSM elemstyles ...\n");
222
223 char *filename = find_file(name);
224 if(!filename) {
225 printf("elemstyle file not found\n");
226 return NULL;
227 }
228
229 LIBXML_TEST_VERSION;
230
231 /* parse the file and get the DOM */
232 xmlDoc *doc = NULL;
233 if((doc = xmlReadFile(filename, NULL, 0)) == NULL) {
234 xmlErrorPtr errP = xmlGetLastError();
235 printf("elemstyles download failed: "
236 "XML error while parsing:\n"
237 "%s\n", errP->message);
238 } else {
239 printf("ok, parse doc tree\n");
240 elemstyles = parse_doc(doc);
241 }
242
243 g_free(filename);
244 return elemstyles;
245 }
246
247 /* ----------------------- cleaning up --------------------- */
248
249 static void free_line(elemstyle_line_t *line) {
250 g_free(line);
251 }
252
253 static void free_area(elemstyle_area_t *area) {
254 g_free(area);
255 }
256
257 static void free_icon(elemstyle_icon_t *icon) {
258 if(icon->filename) xmlFree(icon->filename);
259 g_free(icon);
260 }
261
262 static void elemstyle_free(elemstyle_t *elemstyle) {
263 if(elemstyle->condition.key) xmlFree(elemstyle->condition.key);
264 if(elemstyle->condition.value) xmlFree(elemstyle->condition.value);
265
266 switch(elemstyle->type) {
267 case ES_TYPE_NONE:
268 break;
269 case ES_TYPE_LINE:
270 free_line(elemstyle->line);
271 break;
272 case ES_TYPE_AREA:
273 free_area(elemstyle->area);
274 break;
275 }
276 if(elemstyle->icon) free_icon(elemstyle->icon);
277 g_free(elemstyle);
278 }
279
280 void josm_elemstyles_free(elemstyle_t *elemstyles) {
281 while(elemstyles) {
282 elemstyle_t *next = elemstyles->next;
283 elemstyle_free(elemstyles);
284 elemstyles = next;
285 }
286 }
287
288 #define WIDTH_SCALE (1.0)
289
290 void josm_elemstyles_colorize_node(style_t *style, node_t *node) {
291 node->zoom_max = style->node.zoom_max;
292 elemstyle_t *elemstyle = style->elemstyles;
293
294 while(elemstyle) {
295 gboolean match = FALSE;
296
297 if(elemstyle->condition.key) {
298 char *value = osm_node_get_value(node, elemstyle->condition.key);
299 if(value) {
300 if(elemstyle->condition.value) {
301 if(strcasecmp(value, elemstyle->condition.value) == 0)
302 match = TRUE;
303 } else
304 match = TRUE;
305 }
306 } else
307 if(osm_node_has_value(node, elemstyle->condition.value))
308 match = TRUE;
309
310
311 if(match && elemstyle->icon) {
312 char *name = g_strdup_printf("styles/%s/%s",
313 style->icon.path_prefix,
314 elemstyle->icon->filename);
315
316 /* free old icon if there's one present */
317 if(node->icon_buf)
318 icon_free(style->iconP, node->icon_buf);
319
320 node->icon_buf = icon_load(style->iconP, name);
321 g_free(name);
322
323 if (elemstyle->icon->zoom_max > 0) {
324 node->zoom_max = elemstyle->icon->zoom_max;
325 }
326 }
327
328 elemstyle = elemstyle->next;
329 }
330 }
331
332 void josm_elemstyles_colorize_way(style_t *style, way_t *way) {
333 elemstyle_t *elemstyle = style->elemstyles;
334
335 /* use dark grey/no stroke/not filled for everything unknown */
336 way->draw.color = RGB2CANVAS(style->way.color);
337 way->draw.width = style->way.width;
338 way->draw.flags = 0;
339 way->draw.zoom_max = 0; // draw at all zoom levels
340
341 gboolean way_is_closed =
342 (osm_way_get_last_node(way) == osm_way_get_first_node(way));
343
344 while(elemstyle) {
345 // printf("a %s %s\n", elemstyle->condition.key,
346 // elemstyle->condition.value);
347
348 gboolean match = FALSE;
349
350 if(elemstyle->condition.key) {
351 char *value = osm_way_get_value(way, elemstyle->condition.key);
352 if(value) {
353 if(elemstyle->condition.value) {
354 if(strcasecmp(value, elemstyle->condition.value) == 0)
355 match = TRUE;
356 } else
357 match = TRUE;
358 }
359 } else
360 if(osm_way_has_value(way, elemstyle->condition.value))
361 match = TRUE;
362
363 if(match) {
364 switch(elemstyle->type) {
365 case ES_TYPE_NONE:
366 /* this entry does not contain line or area descriptions and is */
367 /* likely just an icon. ignore this as it doesn't make much sense */
368 /* for a way */
369 break;
370
371 case ES_TYPE_LINE:
372 way->draw.color = (elemstyle->line->color << 8) | 0xff;
373 way->draw.width = WIDTH_SCALE * elemstyle->line->width;
374 if(elemstyle->line->bg.valid) {
375 way->draw.flags |= OSM_DRAW_FLAG_BG;
376 way->draw.bg.color = (elemstyle->line->bg.color << 8) | 0xff;
377 way->draw.bg.width = WIDTH_SCALE * elemstyle->line->bg.width;
378 }
379 if (elemstyle->line->zoom_max > 0) {
380 way->draw.zoom_max = elemstyle->line->zoom_max;
381 }
382 else {
383 way->draw.zoom_max = style->way.zoom_max;
384 }
385 return;
386 break;
387
388 case ES_TYPE_AREA:
389 if(way_is_closed) {
390 way->draw.flags |= OSM_DRAW_FLAG_AREA;
391 /* comment the following line for grey border around all areas */
392 /* (potlatch style) */
393
394 if(style->area.has_border_color)
395 way->draw.color = (style->area.border_color << 8) | 0xff;
396 else
397 way->draw.color = (elemstyle->area->color << 8) | 0xff;
398
399 way->draw.width = WIDTH_SCALE * style->area.border_width;
400 way->draw.area.color = (elemstyle->area->color << 8) |
401 style->area.opaque;
402 if (elemstyle->area->zoom_max > 0) {
403 way->draw.zoom_max = elemstyle->area->zoom_max;
404 }
405 else {
406 way->draw.zoom_max = style->area.zoom_max;
407 }
408 return;
409 }
410 break;
411 }
412 }
413 elemstyle = elemstyle->next;
414 }
415 }
416
417 void josm_elemstyles_colorize_world(style_t *styles, osm_t *osm) {
418
419 printf("preparing colors\n");
420
421 /* colorize ways */
422 way_t *way = osm->way;
423 while(way) {
424 josm_elemstyles_colorize_way(styles, way);
425 way = way->next;
426 }
427
428 /* icons */
429 node_t *node = osm->node;
430 while(node) {
431 josm_elemstyles_colorize_node(styles, node);
432 node = node->next;
433 }
434 }