Contents of /trunk/src/josm_elemstyles.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 13 - (show annotations)
Mon Dec 15 14:17:29 2008 UTC (15 years, 5 months ago) by achadwick
File MIME type: text/plain
File size: 12641 byte(s)
Support for a dashed line style. Tweak Mapnik style to use it, and then
compensate a bit for that fact that it makes things slower.
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 gboolean parse_gboolean(xmlNode *a_node, char *name, gboolean *val) {
89 char *bool_str = (char*)xmlGetProp(a_node, BAD_CAST name);
90 if (!bool_str) {
91 *val = FALSE;
92 return FALSE;
93 }
94 static const char *true_str[] = { "1", "yes", "true", 0 };
95 int i;
96 for (i=0; true_str[i]; ++i) {
97 if (strcasecmp(bool_str, true_str[i])==0) {
98 *val = TRUE;
99 return TRUE;
100 }
101 }
102 *val = FALSE;
103 return TRUE;
104 }
105
106 static elemstyle_line_t *parse_line(xmlDocPtr doc, xmlNode *a_node) {
107 elemstyle_line_t *line = g_new0(elemstyle_line_t, 1);
108
109 /* these have to be present */
110 g_assert(parse_color(a_node, "colour", &line->color));
111 g_assert(parse_gint(a_node, "width", &line->width));
112
113 line->real.valid =
114 parse_gint(a_node, "realwidth", &line->real.width);
115
116 line->bg.valid =
117 parse_gint(a_node, "width_bg", &line->bg.width) &&
118 parse_color(a_node, "colour_bg", &line->bg.color);
119
120 parse_gboolean(a_node, "dashed", &line->dashed);
121
122 return line;
123 }
124
125 static elemstyle_area_t *parse_area(xmlDocPtr doc, xmlNode *a_node) {
126 elemstyle_area_t *area = g_new0(elemstyle_area_t, 1);
127
128 /* these have to be present */
129 g_assert(parse_color(a_node, "colour", &area->color));
130 return area;
131 }
132
133 static elemstyle_icon_t *parse_icon(xmlDocPtr doc, xmlNode *a_node) {
134 elemstyle_icon_t *icon = g_new0(elemstyle_icon_t, 1);
135
136 char *annotate = (char*)xmlGetProp(a_node, BAD_CAST "annotate");
137 if(annotate) {
138 icon->annotate = (strcasecmp(annotate, "true")==0);
139 xmlFree(annotate);
140 }
141
142 icon->filename = (char*)xmlGetProp(a_node, BAD_CAST "src");
143 g_assert(icon->filename);
144
145 icon->filename = josm_icon_name_adjust(icon->filename);
146
147 icon->zoom_max = 0;
148
149 return icon;
150 }
151
152 static elemstyle_t *parse_rule(xmlDocPtr doc, xmlNode *a_node) {
153 xmlNode *cur_node = NULL;
154 elemstyle_t *elemstyle = g_new0(elemstyle_t, 1);
155
156 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
157 if (cur_node->type == XML_ELEMENT_NODE) {
158 if(strcasecmp((char*)cur_node->name, "condition") == 0) {
159 /* ------ parse condition ------ */
160 elemstyle->condition.key = (char*)xmlGetProp(cur_node, BAD_CAST "k");
161 elemstyle->condition.value = (char*)xmlGetProp(cur_node, BAD_CAST "v");
162 } else if(strcasecmp((char*)cur_node->name, "line") == 0) {
163 /* ------ parse line ------ */
164 g_assert(elemstyle->type == ES_TYPE_NONE);
165 elemstyle->type = ES_TYPE_LINE;
166 elemstyle->line = parse_line(doc, cur_node);
167 } else if(strcasecmp((char*)cur_node->name, "area") == 0) {
168 /* ------ parse area ------ */
169 g_assert(elemstyle->type == ES_TYPE_NONE);
170 elemstyle->type = ES_TYPE_AREA;
171 elemstyle->area = parse_area(doc, cur_node);
172 } else if(strcasecmp((char*)cur_node->name, "icon") == 0) {
173 elemstyle->icon = parse_icon(doc, cur_node);
174 } else if(strcasecmp((char*)cur_node->name, "scale_min") == 0) {
175 /* scale_min is currently ignored */
176 } else if(strcasecmp((char*)cur_node->name, "scale_max") == 0) {
177 switch (elemstyle->type) {
178 case ES_TYPE_LINE:
179 parse_scale_max(cur_node, &elemstyle->line->zoom_max);
180 break;
181 case ES_TYPE_AREA:
182 parse_scale_max(cur_node, &elemstyle->area->zoom_max);
183 break;
184 default:
185 if (elemstyle->icon) {
186 parse_scale_max(cur_node, &elemstyle->icon->zoom_max);
187 }
188 else {
189 printf("scale_max for unhandled elemstyletype=0x02%x\n", elemstyle->type);
190 }
191 break;
192 }
193 } else {
194 printf("found unhandled rules/rule/%s\n", cur_node->name);
195 }
196 }
197 }
198
199 return elemstyle;
200 }
201
202 static elemstyle_t *parse_rules(xmlDocPtr doc, xmlNode *a_node) {
203 xmlNode *cur_node = NULL;
204 elemstyle_t *elemstyles = NULL, **elemstyle = &elemstyles;
205
206 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
207 if (cur_node->type == XML_ELEMENT_NODE) {
208 if(strcasecmp((char*)cur_node->name, "rule") == 0) {
209 *elemstyle = parse_rule(doc, cur_node);
210 if(*elemstyle) elemstyle = &((*elemstyle)->next);
211 } else
212 printf("found unhandled rules/%s\n", cur_node->name);
213 }
214 }
215 return elemstyles;
216 }
217
218 static elemstyle_t *parse_doc(xmlDocPtr doc) {
219 /* Get the root element node */
220 xmlNode *cur_node = NULL;
221 elemstyle_t *elemstyles = NULL;
222
223 for(cur_node = xmlDocGetRootElement(doc);
224 cur_node; cur_node = cur_node->next) {
225 if (cur_node->type == XML_ELEMENT_NODE) {
226 if(strcasecmp((char*)cur_node->name, "rules") == 0) {
227 elemstyles = parse_rules(doc, cur_node);
228 } else
229 printf("found unhandled %s\n", cur_node->name);
230 }
231 }
232
233 xmlFreeDoc(doc);
234 xmlCleanupParser();
235 return elemstyles;
236 }
237
238 elemstyle_t *josm_elemstyles_load(char *name) {
239 elemstyle_t *elemstyles = NULL;
240
241 printf("Loading JOSM elemstyles ...\n");
242
243 char *filename = find_file(name);
244 if(!filename) {
245 printf("elemstyle file not found\n");
246 return NULL;
247 }
248
249 LIBXML_TEST_VERSION;
250
251 /* parse the file and get the DOM */
252 xmlDoc *doc = NULL;
253 if((doc = xmlReadFile(filename, NULL, 0)) == NULL) {
254 xmlErrorPtr errP = xmlGetLastError();
255 printf("elemstyles download failed: "
256 "XML error while parsing:\n"
257 "%s\n", errP->message);
258 } else {
259 printf("ok, parse doc tree\n");
260 elemstyles = parse_doc(doc);
261 }
262
263 g_free(filename);
264 return elemstyles;
265 }
266
267 /* ----------------------- cleaning up --------------------- */
268
269 static void free_line(elemstyle_line_t *line) {
270 g_free(line);
271 }
272
273 static void free_area(elemstyle_area_t *area) {
274 g_free(area);
275 }
276
277 static void free_icon(elemstyle_icon_t *icon) {
278 if(icon->filename) xmlFree(icon->filename);
279 g_free(icon);
280 }
281
282 static void elemstyle_free(elemstyle_t *elemstyle) {
283 if(elemstyle->condition.key) xmlFree(elemstyle->condition.key);
284 if(elemstyle->condition.value) xmlFree(elemstyle->condition.value);
285
286 switch(elemstyle->type) {
287 case ES_TYPE_NONE:
288 break;
289 case ES_TYPE_LINE:
290 free_line(elemstyle->line);
291 break;
292 case ES_TYPE_AREA:
293 free_area(elemstyle->area);
294 break;
295 }
296 if(elemstyle->icon) free_icon(elemstyle->icon);
297 g_free(elemstyle);
298 }
299
300 void josm_elemstyles_free(elemstyle_t *elemstyles) {
301 while(elemstyles) {
302 elemstyle_t *next = elemstyles->next;
303 elemstyle_free(elemstyles);
304 elemstyles = next;
305 }
306 }
307
308 #define WIDTH_SCALE (1.0)
309
310 void josm_elemstyles_colorize_node(style_t *style, node_t *node) {
311 node->zoom_max = style->node.zoom_max;
312 elemstyle_t *elemstyle = style->elemstyles;
313
314 while(elemstyle) {
315 gboolean match = FALSE;
316
317 if(elemstyle->condition.key) {
318 char *value = osm_node_get_value(node, elemstyle->condition.key);
319 if(value) {
320 if(elemstyle->condition.value) {
321 if(strcasecmp(value, elemstyle->condition.value) == 0)
322 match = TRUE;
323 } else
324 match = TRUE;
325 }
326 } else
327 if(osm_node_has_value(node, elemstyle->condition.value))
328 match = TRUE;
329
330
331 if(match && elemstyle->icon) {
332 char *name = g_strdup_printf("styles/%s/%s",
333 style->icon.path_prefix,
334 elemstyle->icon->filename);
335
336 /* free old icon if there's one present */
337 if(node->icon_buf)
338 icon_free(style->iconP, node->icon_buf);
339
340 node->icon_buf = icon_load(style->iconP, name);
341 g_free(name);
342
343 if (elemstyle->icon->zoom_max > 0) {
344 node->zoom_max = elemstyle->icon->zoom_max;
345 }
346 }
347
348 elemstyle = elemstyle->next;
349 }
350 }
351
352 void josm_elemstyles_colorize_way(style_t *style, way_t *way) {
353 elemstyle_t *elemstyle = style->elemstyles;
354
355 /* use dark grey/no stroke/not filled for everything unknown */
356 way->draw.color = RGB2CANVAS(style->way.color);
357 way->draw.width = style->way.width;
358 way->draw.flags = 0;
359 way->draw.zoom_max = 0; // draw at all zoom levels
360
361 gboolean way_is_closed =
362 (osm_way_get_last_node(way) == osm_way_get_first_node(way));
363
364 while(elemstyle) {
365 // printf("a %s %s\n", elemstyle->condition.key,
366 // elemstyle->condition.value);
367
368 gboolean match = FALSE;
369
370 if(elemstyle->condition.key) {
371 char *value = osm_way_get_value(way, elemstyle->condition.key);
372 if(value) {
373 if(elemstyle->condition.value) {
374 if(strcasecmp(value, elemstyle->condition.value) == 0)
375 match = TRUE;
376 } else
377 match = TRUE;
378 }
379 } else
380 if(osm_way_has_value(way, elemstyle->condition.value))
381 match = TRUE;
382
383 if(match) {
384 switch(elemstyle->type) {
385 case ES_TYPE_NONE:
386 /* this entry does not contain line or area descriptions and is */
387 /* likely just an icon. ignore this as it doesn't make much sense */
388 /* for a way */
389 break;
390
391 case ES_TYPE_LINE:
392 way->draw.color = (elemstyle->line->color << 8) | 0xff;
393 way->draw.width = WIDTH_SCALE * elemstyle->line->width;
394 if(elemstyle->line->bg.valid) {
395 way->draw.flags |= OSM_DRAW_FLAG_BG;
396 way->draw.bg.color = (elemstyle->line->bg.color << 8) | 0xff;
397 way->draw.bg.width = WIDTH_SCALE * elemstyle->line->bg.width;
398 }
399 if (elemstyle->line->zoom_max > 0) {
400 way->draw.zoom_max = elemstyle->line->zoom_max;
401 }
402 else {
403 way->draw.zoom_max = style->way.zoom_max;
404 }
405 way->draw.dashed = elemstyle->line->dashed;
406 return;
407 break;
408
409 case ES_TYPE_AREA:
410 if(way_is_closed) {
411 way->draw.flags |= OSM_DRAW_FLAG_AREA;
412 /* comment the following line for grey border around all areas */
413 /* (potlatch style) */
414
415 if(style->area.has_border_color)
416 way->draw.color = (style->area.border_color << 8) | 0xff;
417 else
418 way->draw.color = (elemstyle->area->color << 8) | 0xff;
419
420 way->draw.width = WIDTH_SCALE * style->area.border_width;
421 way->draw.area.color = (elemstyle->area->color << 8) |
422 style->area.opaque;
423 if (elemstyle->area->zoom_max > 0) {
424 way->draw.zoom_max = elemstyle->area->zoom_max;
425 }
426 else {
427 way->draw.zoom_max = style->area.zoom_max;
428 }
429 return;
430 }
431 break;
432 }
433 }
434 elemstyle = elemstyle->next;
435 }
436 }
437
438 void josm_elemstyles_colorize_world(style_t *styles, osm_t *osm) {
439
440 printf("preparing colors\n");
441
442 /* colorize ways */
443 way_t *way = osm->way;
444 while(way) {
445 josm_elemstyles_colorize_way(styles, way);
446 way = way->next;
447 }
448
449 /* icons */
450 node_t *node = osm->node;
451 while(node) {
452 josm_elemstyles_colorize_node(styles, node);
453 node = node->next;
454 }
455 }
456
457 // vim:et:ts=8:sw=2:sts=2:ai