source: branches/client-library-split/renderer.c @ 89

Last change on this file since 89 was 89, checked in by simon, 5 years ago

fix a typo

  • Property svn:eol-style set to native
  • Property svn:keywords set to "Date Author Id Revision HeadURL"
  • Property svn:mime-type set to text/x-csrc
File size: 11.7 KB
Line 
1/*
2 * Memphis - Cairo Rederer for OSM in C
3 * Copyright (C) 2008  Marius Rieder <marius.rieder@durchmesser.ch>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20#include <glib.h>
21#include <math.h>
22#include <time.h>
23#include <cairo.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include "main.h"
29#include "osm05.h"
30#include "libmercator.h"
31#include "list.h"
32#include "renderer.h"
33#include "ruleset.h"
34#include "textpath.h"
35
36// External Vars
37extern memphisOpt   *opts;
38
39/*
40 * Internal used return values for stringInStrings.
41 */
42typedef enum compare_result_e {
43    TAG_CMP_NOT_EQUAL   = 0,
44    TAG_CMP_EQUAL       = 1,
45    TAG_CMP_ANY         = 2,
46    TAG_CMP_MISSING     = 3,
47} compare_result_e;
48
49/*
50 * function: drawPath
51 * @cr  Array of cairo resources
52 * @nd  Liked list if osmNd's
53 *
54 * This function is used to prepare a Path.
55 */
56static void drawPath(renderInfo *info, GSList *nodes) {
57    GSList *iter;
58    osmNode *nd;
59    coordinates xy;
60   
61    if (G_UNLIKELY(opts->debug > 1))
62        fprintf(stdout,"drawPath\n");
63
64    iter = nodes;
65    nd = iter->data;
66    xy = coord2xy(nd->lat, nd->lon, info->zoom);
67    cairo_move_to(info->cr, xy.x-info->offset.x,
68                            xy.y-info->offset.y);
69   
70    iter = g_slist_next(iter);
71    while(iter) {
72        nd = iter->data;
73        xy = coord2xy(nd->lat, nd->lon, info->zoom);
74        cairo_line_to(info->cr, xy.x-info->offset.x, xy.y-info->offset.y);
75        iter = g_slist_next(iter);
76    }
77}
78
79/*
80 * function: strokePath
81 * @cr  Array of cairo resources
82 *
83 * This function is stroke all current path without drawing anithing.
84 */
85static void strokePath(renderInfo *info) {
86    if (G_UNLIKELY(opts->debug > 1))
87        fprintf(stdout,"strokePath\n");
88       
89    cairo_set_line_width (info->cr, 0);
90    cairo_stroke(info->cr);
91}
92
93/*
94 * function: drawPolygone
95 *
96 * This function fill the prepared paths with the configured color.
97 */
98static void drawPolygone(renderInfo *info, cfgDraw *draw) {
99    if (G_UNLIKELY(opts->debug > 1))
100        fprintf(stdout,"drawPolygone\n");
101
102    cairo_surface_t *image = NULL;
103    cairo_pattern_t *pattern = NULL;
104
105    if(draw->pattern) {
106        char *filename;
107
108        /* TODO ast: the pattern may be cached, e.g. using a GCache structure */
109
110        filename = g_strdup_printf("pattern/%s.png", draw->pattern);
111        image = cairo_image_surface_create_from_png(filename);
112        if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
113            g_warning("pattern '%s' not found\n", filename);
114            g_free(filename);
115            return;
116        }
117        g_free(filename);
118
119        pattern = cairo_pattern_create_for_surface (image);
120        cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
121        cairo_surface_destroy (image);
122    }
123
124    cairo_set_fill_rule (info->cr, CAIRO_FILL_RULE_EVEN_ODD);
125
126    if(pattern)
127        cairo_set_source (info->cr, pattern);
128    else
129        cairo_set_source_rgb (info->cr, (double)draw->color[0]/(double)255,
130                                        (double)draw->color[1]/(double)255,
131                                        (double)draw->color[2]/(double)255);
132
133    cairo_fill_preserve(info->cr);
134
135    if(pattern)
136        cairo_pattern_destroy (pattern);
137}
138
139/*
140 * function: drawLine
141 *
142 * This function draw the prepared paths with the configured color.
143 */
144static void drawLine(renderInfo *info, cfgDraw *draw) {
145    if (G_UNLIKELY(opts->debug > 1))
146        fprintf(stdout,"drawLine\n");
147
148    cairo_set_line_cap  (info->cr, CAIRO_LINE_CAP_ROUND);
149    cairo_set_line_join (info->cr, CAIRO_LINE_JOIN_ROUND);
150    cairo_set_line_width (info->cr, draw->width*LINESIZE(info->zoom));
151
152    cairo_set_source_rgb (info->cr, (double)draw->color[0]/(double)255,
153                                    (double)draw->color[1]/(double)255,
154                                    (double)draw->color[2]/(double)255);
155    cairo_stroke_preserve(info->cr);
156}
157
158/*
159 * function: drawText
160 *
161 * This function draw the given text along the current path.
162 */
163static void drawText(renderInfo *info, char *text, cfgDraw *draw) {
164    if (G_UNLIKELY(opts->debug > 1))
165        fprintf(stdout,"drawText\n");
166
167    cairo_select_font_face (info->cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
168                                              CAIRO_FONT_WEIGHT_NORMAL);
169    cairo_set_source_rgb (info->cr, (double)draw->color[0]/(double)255,
170                                    (double)draw->color[1]/(double)255,
171                                    (double)draw->color[2]/(double)255);
172    cairo_set_font_size (info->cr, draw->width*LINESIZE(info->zoom));
173    textPath(info->cr, text);
174}
175
176/*
177 * function: stringInStrings
178 *
179 * Check if string is an strings.
180 */
181static compare_result_e stringInStrings(char *string, char **strings) {
182    if (G_UNLIKELY(opts->debug > 1))
183        fprintf(stdout,"stringInStrings\n");
184    compare_result_e r = TAG_CMP_NOT_EQUAL;
185    while (*strings != NULL) {
186        if (string == *strings) {
187            return TAG_CMP_EQUAL;
188        }
189        if(strcmp(*strings,"*") == 0)
190            r = TAG_CMP_ANY;
191        if(strcmp(*strings,"~") == 0)
192            r = TAG_CMP_MISSING;
193
194        strings++;
195    }
196    return (r);
197}
198
199/*
200 * function: matchRule
201 *
202 * Check if a element matchs a rule.
203 */
204static int matchRule(cfgRule *rule, osmTag *tag) {
205    int k, v;
206   
207    if (G_UNLIKELY(opts->debug > 1))
208        fprintf(stdout,"matchRule\n");
209       
210    while(tag) {
211        k = stringInStrings(tag->key, rule->key);
212        v = stringInStrings(tag->value, rule->value);
213
214        if (k == TAG_CMP_EQUAL && v == TAG_CMP_EQUAL)
215            return TRUE;
216        if (k == TAG_CMP_EQUAL && v == TAG_CMP_ANY)
217            return TRUE;
218        if (k == TAG_CMP_NOT_EQUAL && v == TAG_CMP_MISSING)
219            return TRUE;
220
221        tag = tag->next;
222    }
223    return FALSE;
224}
225
226/*
227 * function: checkRule
228 *
229 * Check if a element match to a rule and all it's parent.
230 */
231static int checkRule(cfgRule *rule, osmTag *tag, short int type) {
232    if (G_UNLIKELY(opts->debug > 1))
233        fprintf(stdout,"checkRule\n");
234
235    int not;
236    cfgRule *iter;
237
238    if(rule->nparent) {
239        iter = rule->nparent;
240        not = TRUE;
241    } else {
242        iter = rule->parent;
243        not = FALSE;
244    }
245
246    while(iter) {
247
248        if(matchRule(iter, tag) == not) {
249            return(0);
250        }
251
252        if(iter->nparent) {
253            iter = iter->nparent;
254            not = TRUE;
255        } else {
256            iter = iter->parent;
257            not = FALSE;
258        }
259    }
260
261    if(matchRule(rule, tag)) {
262        return(1);
263    } else {
264        return(-1);
265    }
266
267}
268
269static void renderPaths(renderInfo *info, int layer, cfgRule *rule, cfgDraw *draw) {
270    int paths = 0;
271    osmWay *way;
272
273    // Loop through ways for
274    LIST_FOREACH(way, info->osm->ways) {
275        //Only objects on current layer
276        if(way->layer != layer)
277            continue;
278
279        if( checkRule(rule, way->tag, WAY) == 1) {
280            drawPath(info, way->nd);
281            paths++;
282        }
283    }
284    if(paths) {
285        while(draw) {
286            if(draw->minzoom > info->zoom || draw->maxzoom < info->zoom) {
287                draw = draw->next;
288                continue;
289            }
290            switch(draw->type) {
291                case POLYGONE:
292                    drawPolygone(info, draw);
293                    break;
294                case LINE:
295                    drawLine(info, draw);
296                    break;
297                case TEXT: break;   /* ignore */
298            }
299            draw = draw->next;
300        }
301    }
302    strokePath(info);
303}
304
305static void renderText(renderInfo *info, int layer, cfgRule *rule, cfgDraw *draw) {
306    osmWay      *way;
307    while(draw) {
308        if (draw->type == TEXT) {
309            if(draw->minzoom <= info->zoom && info->zoom <= draw->maxzoom) {
310                LIST_FOREACH(way, info->osm->ways) {
311                    //Only objects on current layer
312                    if(way->layer != layer || way->name == NULL)
313                        continue;
314
315                    if( checkRule(rule, way->tag, WAY) == 1) {
316                        drawPath(info, way->nd);
317                        drawText(info, way->name, draw);
318                    }
319                }
320
321            }
322
323            break;
324        }
325        draw = draw->next;
326    }
327    strokePath(info);
328}
329
330/*
331 * function: renderCairoRun
332 */
333static int renderCairoRun(renderInfo *info) {
334    if (opts->debug > 1)
335        fprintf(stdout,"renderCairoRun\n");
336    int layer;
337
338    // Vars used while looping through data
339    cfgRule     *rule;
340
341    // Start checking osm from bottom layer.
342    for(layer = -5; layer <= 5; layer++) {
343
344        if (opts->debug > 0) {
345            fprintf(stdout,"\r Cairo drawing z%i Layer % 2i", info->zoom, layer);
346            fflush(stdout);
347        }
348
349        // Process Rule by Rule
350        LIST_FOREACH(rule, info->ruleset->rule) {
351
352            if(rule->draw != NULL) { // Draw Match first
353                renderPaths(info, layer, rule, rule->draw);
354
355                // Text Rendering
356                renderText(info, layer, rule, rule->draw);
357            }
358            if (rule->ndraw != NULL) { // Draw Else after
359                renderPaths(info, layer, rule, rule->ndraw);
360            }
361        }
362    }
363
364    if (opts->debug > 0)
365        fprintf(stdout,"\r Cairo drawing done\n");
366
367    return(0);
368}
369
370/*
371 * function: renderCairo
372 */
373int renderCairo(cfgRules *ruleset, osmFile *osm) {
374    if (opts->debug > 1)
375        fprintf(stdout,"renderCairo\n");
376    int z;
377    renderInfo *info;
378   
379    // Initialize all layers
380    for (z = 0; z <= (opts->maxlayer - opts->minlayer); z++) {
381        coordinates min, max;
382       
383        info = g_new(renderInfo, 1);
384        info->zoom = z + opts->minlayer;
385        info->ruleset = ruleset;
386        info->osm = osm;
387               
388        min = coord2xy(osm->minlat, osm->minlon, info->zoom);
389        max = coord2xy(osm->maxlat, osm->maxlon, info->zoom);
390        int w = (int)ceil(max.x-min.x);
391        int h = (int)ceil(min.y-max.y);
392
393        info->offset = coord2xy(osm->maxlat, osm->minlon, info->zoom);
394
395        info->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,w,h);
396        info->cr = cairo_create(info->surface);
397
398        cairo_rectangle(info->cr, 0, 0, w, h);
399        cairo_set_source_rgb(info->cr,
400                                    (double)ruleset->background[0]/(double)255,
401                                    (double)ruleset->background[1]/(double)255,
402                                    (double)ruleset->background[2]/(double)255);
403        cairo_fill(info->cr);
404       
405        renderCairoRun(info);
406       
407        // Saving Images
408        char *filename;
409
410        filename = g_strdup_printf("%s/%02i.png", opts->outdir, info->zoom);
411        if (opts->debug > 0) {
412            fprintf(stdout," Cairo rendering Z%i to '%s'", info->zoom, filename);
413            fflush(stdout);
414        }
415        cairo_surface_write_to_png(info->surface, filename);
416        g_free(filename);
417        cairo_destroy(info->cr);
418        cairo_surface_destroy(info->surface);
419        if (opts->debug > 0)
420            fprintf(stdout," done.\n");
421           
422        g_free(info);
423    }
424   
425    return (0);
426}
427
428/*
429 * vim: expandtab shiftwidth=4 tabstop=4:
430 */
431
Note: See TracBrowser for help on using the repository browser.