Parent Directory | Revision Log
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 | #ifndef USE_GOOCANVAS |
21 | #error "Config error!" |
22 | #endif |
23 | |
24 | #include "appdata.h" |
25 | |
26 | canvas_item_t *canvas_circle_new(map_t *map, canvas_group_t group, |
27 | gint x, gint y, gint radius, gint border, |
28 | canvas_color_t fill_col, canvas_color_t border_col) { |
29 | return goo_canvas_ellipse_new(map->group[group], |
30 | (gdouble) x, (gdouble) y, |
31 | (gdouble) radius, (gdouble) radius, |
32 | "line-width", (double)border, |
33 | "stroke-color-rgba", border_col, |
34 | "fill-color-rgba", fill_col, |
35 | NULL); |
36 | } |
37 | |
38 | canvas_points_t *canvas_points_new(gint points) { |
39 | return goo_canvas_points_new(points); |
40 | } |
41 | |
42 | void canvas_point_set_pos(canvas_points_t *points, gint index, lpos_t *lpos) { |
43 | points->coords[2*index+0] = lpos->x; |
44 | points->coords[2*index+1] = lpos->y; |
45 | } |
46 | |
47 | void canvas_points_free(canvas_points_t *points) { |
48 | goo_canvas_points_unref(points); |
49 | } |
50 | |
51 | canvas_item_t *canvas_polyline_new(struct map_s *map, canvas_group_t group, |
52 | canvas_points_t *points, gint width, canvas_color_t color) { |
53 | return goo_canvas_polyline_new(map->group[group], FALSE, 0, |
54 | "points", points, |
55 | "line-width", (double)width, |
56 | "stroke-color-rgba", color, |
57 | "line-join", CAIRO_LINE_JOIN_ROUND, |
58 | "line-cap", CAIRO_LINE_CAP_ROUND, |
59 | NULL); |
60 | } |
61 | |
62 | canvas_item_t *canvas_polygon_new(struct map_s *map, canvas_group_t group, |
63 | canvas_points_t *points, gint width, canvas_color_t color, |
64 | canvas_color_t fill) { |
65 | return goo_canvas_polyline_new(map->group[group], TRUE, 0, |
66 | "points", points, |
67 | "line-width", (double)width, |
68 | "stroke-color-rgba", color, |
69 | "fill-color-rgba", fill, |
70 | "line-join", CAIRO_LINE_JOIN_ROUND, |
71 | "line-cap", CAIRO_LINE_CAP_ROUND, |
72 | NULL); |
73 | } |
74 | |
75 | void canvas_item_set_points(canvas_item_t *item, canvas_points_t *points) { |
76 | g_object_set(G_OBJECT(item), "points", points, NULL); |
77 | } |
78 | |
79 | void canvas_item_set_pos(canvas_item_t *item, lpos_t *lpos, gint radius) { |
80 | g_object_set(G_OBJECT(item), "center-x", (gdouble)lpos->x, |
81 | "center-y", (gdouble)lpos->y, NULL); |
82 | } |
83 | |
84 | void canvas_window2world(canvas_t *canvas, gint x, gint y, gint *wx, gint *wy) { |
85 | double sx = x, sy = y; |
86 | goo_canvas_convert_from_pixels(GOO_CANVAS(canvas), &sx, &sy); |
87 | *wx = sx; *wy = sy; |
88 | } |
89 | |
90 | canvas_item_t *canvas_get_item_at(canvas_t *canvas, gint x, gint y) { |
91 | return goo_canvas_get_item_at(GOO_CANVAS(canvas), x, y, TRUE); |
92 | } |
93 | |
94 | void canvas_item_to_bottom(canvas_item_t *item) { |
95 | goo_canvas_item_lower(item, NULL); |
96 | } |
97 | |
98 | void canvas_item_set_zoom_max(canvas_item_t *item, float zoom_max) { |
99 | gdouble vis_thres = zoom_max; |
100 | GooCanvasItemVisibility vis |
101 | = GOO_CANVAS_ITEM_VISIBLE_ABOVE_THRESHOLD; |
102 | if (vis_thres < 0) { |
103 | vis_thres = 0; |
104 | vis = GOO_CANVAS_ITEM_VISIBLE; |
105 | } |
106 | g_object_set(G_OBJECT(item), |
107 | "visibility", vis, |
108 | "visibility-threshold", vis_thres, |
109 | NULL); |
110 | } |
111 | |
112 | void canvas_item_set_dashed(canvas_item_t *item) { |
113 | static GooCanvasLineDash *dash; |
114 | if (!dash) { |
115 | dash = goo_canvas_line_dash_new(2, 3.0, 4.0, 0); |
116 | goo_canvas_line_dash_ref(dash); |
117 | } |
118 | // TODO: make the pattern width-dependent, or extend the elemstyles language |
119 | // to allow line patterns to be specified. |
120 | g_object_set(G_OBJECT(item), |
121 | "line-dash", dash, |
122 | NULL); |
123 | } |
124 | |
125 | void canvas_item_destroy(canvas_item_t *item) { |
126 | goo_canvas_item_remove(item); |
127 | } |
128 | |
129 | void canvas_item_set_user_data(canvas_item_t *item, void *data) { |
130 | g_object_set_data(G_OBJECT(item), "user data", data); |
131 | } |
132 | |
133 | void *canvas_item_get_user_data(canvas_item_t *item) { |
134 | return g_object_get_data(G_OBJECT(item), "user data"); |
135 | } |
136 | |
137 | typedef struct { |
138 | GCallback c_handler; |
139 | gpointer data; |
140 | } weak_t; |
141 | |
142 | static void canvas_item_weak_notify(gpointer data, GObject *invalid) { |
143 | weak_t *weak = data; |
144 | |
145 | ((void(*)(GtkWidget*, gpointer))weak->c_handler) (NULL, weak->data); |
146 | g_free(weak); |
147 | } |
148 | |
149 | void canvas_item_destroy_connect(canvas_item_t *item, |
150 | GCallback c_handler, gpointer data) { |
151 | weak_t *weak = g_new(weak_t,1); |
152 | weak->data = data; |
153 | weak->c_handler = c_handler; |
154 | |
155 | g_object_weak_ref(G_OBJECT(item), canvas_item_weak_notify, weak); |
156 | } |
157 | |
158 | void canvas_set_zoom(canvas_t *canvas, double zoom) { |
159 | goo_canvas_set_scale(GOO_CANVAS(canvas), zoom); |
160 | } |
161 | |
162 | void canvas_get_scroll_offsets(canvas_t *canvas, gint *sx, gint *sy) { |
163 | GtkAdjustment *hadj = ((struct _GooCanvas*)canvas)->hadjustment; |
164 | GtkAdjustment *vadj = ((struct _GooCanvas*)canvas)->vadjustment; |
165 | gdouble hs, vs; |
166 | gdouble zoom = goo_canvas_get_scale(GOO_CANVAS(canvas)); |
167 | |
168 | hs = gtk_adjustment_get_value(hadj); |
169 | vs = gtk_adjustment_get_value(vadj); |
170 | goo_canvas_convert_from_pixels(GOO_CANVAS(canvas), &hs, &vs); |
171 | |
172 | /* make values zoom independant */ |
173 | *sx = hs * zoom; |
174 | *sy = vs * zoom; |
175 | } |
176 | |
177 | void canvas_scroll_to(canvas_t *canvas, gint sx, gint sy) { |
178 | gdouble zoom = goo_canvas_get_scale(GOO_CANVAS(canvas)); |
179 | goo_canvas_scroll_to(GOO_CANVAS(canvas), sx/zoom, sy/zoom); |
180 | } |
181 | |
182 | void canvas_set_bounds(canvas_t *canvas, gint minx, gint miny, |
183 | gint maxx, gint maxy) { |
184 | goo_canvas_set_bounds(GOO_CANVAS(canvas), minx, miny, maxx, maxy); |
185 | } |
186 | |
187 | canvas_item_t *canvas_image_new(map_t *map, canvas_group_t group, |
188 | GdkPixbuf *pix, gint x, gint y, float hscale, float vscale) { |
189 | |
190 | canvas_item_t *item = goo_canvas_image_new(map->group[group], pix, |
191 | x/hscale, y/vscale, NULL); |
192 | goo_canvas_item_scale(item, hscale, vscale); |
193 | return item; |
194 | } |
195 | |
196 | void canvas_image_move(canvas_item_t *item, gint x, gint y, |
197 | float hscale, float vscale) { |
198 | |
199 | g_object_set(G_OBJECT(item), |
200 | "x", (gdouble)x / hscale, |
201 | "y", (gdouble)y / vscale, |
202 | NULL); |
203 | } |
204 | |
205 | /* get the polygon/polyway segment a certain coordinate is over */ |
206 | gint canvas_item_get_segment(canvas_item_t *item, gint x, gint y) { |
207 | |
208 | canvas_points_t *points = NULL; |
209 | double line_width = 0; |
210 | |
211 | g_object_get(G_OBJECT(item), |
212 | "points", &points, |
213 | "line-width", &line_width, |
214 | NULL); |
215 | |
216 | if(!points) return -1; |
217 | |
218 | gint retval = -1, i; |
219 | double mindist = 100; |
220 | for(i=0;i<points->num_points-1;i++) { |
221 | |
222 | #define AX (points->coords[2*i+0]) |
223 | #define AY (points->coords[2*i+1]) |
224 | #define BX (points->coords[2*i+2]) |
225 | #define BY (points->coords[2*i+3]) |
226 | #define CX ((double)x) |
227 | #define CY ((double)y) |
228 | |
229 | double len2 = pow(BY-AY,2)+pow(BX-AX,2); |
230 | double m = ((CX-AX)*(BX-AX)+(CY-AY)*(BY-AY)) / len2; |
231 | |
232 | /* this is a possible candidate */ |
233 | if((m >= 0.0) && (m <= 1.0)) { |
234 | |
235 | double n; |
236 | if(fabs(BX-AX) > fabs(BY-AY)) |
237 | n = fabs(sqrt(len2) * (AY+m*(BY-AY)-CY)/(BX-AX)); |
238 | else |
239 | n = fabs(sqrt(len2) * -(AX+m*(BX-AX)-CX)/(BY-AY)); |
240 | |
241 | /* check if this is actually on the line and closer than anything */ |
242 | /* we found so far */ |
243 | if((n <= line_width/2) && (n < mindist)) { |
244 | retval = i; |
245 | mindist = n; |
246 | } |
247 | } |
248 | } |
249 | |
250 | /* the last and first point are identical for polygons in osm2go. */ |
251 | /* goocanvas doesn't need that, but that's how OSM works and it saves */ |
252 | /* us from having to check the last->first connection for polygons */ |
253 | /* seperately */ |
254 | |
255 | return retval; |
256 | } |
257 | |
258 | void canvas_item_get_segment_pos(canvas_item_t *item, gint seg, |
259 | gint *x0, gint *y0, gint *x1, gint *y1) { |
260 | printf("get segment %d of item %p\n", seg, item); |
261 | |
262 | canvas_points_t *points = NULL; |
263 | g_object_get(G_OBJECT(item), "points", &points, NULL); |
264 | |
265 | g_assert(points); |
266 | g_assert(seg < points->num_points-1); |
267 | |
268 | *x0 = points->coords[2 * seg + 0]; |
269 | *y0 = points->coords[2 * seg + 1]; |
270 | *x1 = points->coords[2 * seg + 2]; |
271 | *y1 = points->coords[2 * seg + 3]; |
272 | } |