Contents of /src/josm_elemstyles.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (hide annotations)
Tue Dec 9 20:06:06 2008 UTC (15 years, 6 months ago) by harbaum
File MIME type: text/plain
File size: 12077 byte(s)
Initial import
1 harbaum 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     }