Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2021 James Westman <james@jwestman.net>
3 : : *
4 : : * This library is free software; you can redistribute it and/or
5 : : * modify it under the terms of the GNU Lesser General Public
6 : : * License as published by the Free Software Foundation; either
7 : : * version 2.1 of the License, or (at your option) any later version.
8 : : *
9 : : * This library is distributed in the hope that it will be useful,
10 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : : * Lesser General Public License for more details.
13 : : *
14 : : * You should have received a copy of the GNU Lesser General Public
15 : : * License along with this library; if not, see <https://www.gnu.org/licenses/>.
16 : : */
17 : :
18 : : #include <gtk/gtk.h>
19 : : #include "shumate-vector-symbol-info-private.h"
20 : : #include "shumate-vector-symbol-layer-private.h"
21 : : #include "shumate-vector-expression-private.h"
22 : : #include "shumate-vector-renderer.h"
23 : : #include "shumate-vector-utils-private.h"
24 : :
25 : : struct _ShumateVectorSymbolLayer
26 : : {
27 : : ShumateVectorLayer parent_instance;
28 : :
29 : : ShumateVectorExpression *icon_allow_overlap;
30 : : ShumateVectorExpression *icon_anchor;
31 : : ShumateVectorExpression *icon_color;
32 : : ShumateVectorExpression *icon_ignore_placement;
33 : : ShumateVectorExpression *icon_image;
34 : : ShumateVectorExpression *icon_opacity;
35 : : ShumateVectorExpression *icon_optional;
36 : : ShumateVectorExpression *icon_overlap;
37 : : ShumateVectorExpression *icon_padding;
38 : : ShumateVectorExpression *icon_rotate;
39 : : ShumateVectorExpression *icon_rotation_alignment;
40 : : ShumateVectorExpression *icon_size;
41 : : ShumateVectorExpression *text_anchor;
42 : : ShumateVectorExpression *text_field;
43 : : ShumateVectorExpression *text_letter_spacing;
44 : : ShumateVectorExpression *text_allow_overlap;
45 : : ShumateVectorExpression *text_ignore_placement;
46 : : ShumateVectorExpression *text_color;
47 : : ShumateVectorExpression *text_opacity;
48 : : ShumateVectorExpression *text_optional;
49 : : ShumateVectorExpression *text_overlap;
50 : : ShumateVectorExpression *text_size;
51 : : ShumateVectorExpression *text_transform;
52 : : ShumateVectorExpression *cursor;
53 : : ShumateVectorExpression *text_padding;
54 : : ShumateVectorExpression *text_keep_upright;
55 : : ShumateVectorExpression *text_rotation_alignment;
56 : : ShumateVectorExpression *symbol_sort_key;
57 : : ShumateVectorExpression *symbol_placement;
58 : : ShumateVectorExpression *symbol_spacing;
59 : : char *text_fonts;
60 : : double icon_offset_x, icon_offset_y;
61 : : double text_offset_x, text_offset_y;
62 : : };
63 : :
64 [ + + + - ]: 18 : G_DEFINE_TYPE (ShumateVectorSymbolLayer, shumate_vector_symbol_layer, SHUMATE_TYPE_VECTOR_LAYER)
65 : :
66 : :
67 : : ShumateVectorLayer *
68 : 6 : shumate_vector_symbol_layer_create_from_json (JsonObject *object, GError **error)
69 : : {
70 : 6 : ShumateVectorSymbolLayer *layer = g_object_new (SHUMATE_TYPE_VECTOR_SYMBOL_LAYER, NULL);
71 : :
72 [ - + ]: 6 : if (json_object_has_member (object, "paint"))
73 : : {
74 : 0 : JsonObject *paint = json_object_get_object_member (object, "paint");
75 : :
76 : 0 : layer->icon_color = shumate_vector_expression_from_json (json_object_get_member (paint, "icon-color"), error);
77 [ # # ]: 0 : if (layer->icon_color == NULL)
78 : : return NULL;
79 : :
80 : 0 : layer->icon_opacity = shumate_vector_expression_from_json (json_object_get_member (paint, "icon-opacity"), error);
81 [ # # ]: 0 : if (layer->icon_opacity == NULL)
82 : : return NULL;
83 : :
84 : 0 : layer->text_color = shumate_vector_expression_from_json (json_object_get_member (paint, "text-color"), error);
85 [ # # ]: 0 : if (layer->text_color == NULL)
86 : : return NULL;
87 : :
88 : 0 : layer->text_opacity = shumate_vector_expression_from_json (json_object_get_member (paint, "text-opacity"), error);
89 [ # # ]: 0 : if (layer->text_opacity == NULL)
90 : : return NULL;
91 : : }
92 : :
93 [ + - ]: 6 : if (json_object_has_member (object, "layout"))
94 : : {
95 : 6 : JsonObject *layout = json_object_get_object_member (object, "layout");
96 : 6 : JsonNode *icon_offset_node;
97 : 6 : JsonNode *text_font_node;
98 : 6 : JsonNode *text_offset_node;
99 : 6 : JsonArray *text_font;
100 : :
101 : 6 : layer->icon_allow_overlap = shumate_vector_expression_from_json (json_object_get_member (layout, "icon-allow-overlap"), error);
102 [ + - ]: 6 : if (layer->icon_allow_overlap == NULL)
103 : 0 : return NULL;
104 : :
105 : 6 : layer->icon_anchor = shumate_vector_expression_from_json (json_object_get_member (layout, "icon-anchor"), error);
106 [ + - ]: 6 : if (layer->icon_anchor == NULL)
107 : : return NULL;
108 : :
109 : 6 : layer->icon_ignore_placement = shumate_vector_expression_from_json (json_object_get_member (layout, "icon-ignore-placement"), error);
110 [ + - ]: 6 : if (layer->icon_ignore_placement == NULL)
111 : : return NULL;
112 : :
113 : 6 : layer->icon_image = shumate_vector_expression_from_json (json_object_get_member (layout, "icon-image"), error);
114 [ + - ]: 6 : if (layer->icon_image == NULL)
115 : : return NULL;
116 : :
117 : 6 : icon_offset_node = json_object_get_member (layout, "icon-offset");
118 [ - + ]: 6 : if (icon_offset_node != NULL)
119 : : {
120 : 0 : JsonArray *icon_offset;
121 : :
122 [ # # ]: 0 : if (!shumate_vector_json_get_array (icon_offset_node, &icon_offset, error))
123 : 0 : return NULL;
124 : :
125 [ # # ]: 0 : if (json_array_get_length (icon_offset) != 2)
126 : : {
127 : 0 : g_set_error (error,
128 : : SHUMATE_STYLE_ERROR,
129 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
130 : : "Expected `icon-offset` to be an array of two numbers");
131 : 0 : return NULL;
132 : : }
133 : :
134 : 0 : layer->icon_offset_x = json_array_get_double_element (icon_offset, 0);
135 : 0 : layer->icon_offset_y = json_array_get_double_element (icon_offset, 1);
136 : : }
137 : :
138 : 6 : layer->icon_optional = shumate_vector_expression_from_json (json_object_get_member (layout, "icon-optional"), error);
139 [ + - ]: 6 : if (layer->icon_optional == NULL)
140 : : return NULL;
141 : :
142 : 6 : layer->icon_overlap = shumate_vector_expression_from_json (json_object_get_member (layout, "icon-overlap"), error);
143 [ + - ]: 6 : if (layer->icon_overlap == NULL)
144 : : return NULL;
145 : :
146 : 6 : layer->icon_padding = shumate_vector_expression_from_json (json_object_get_member (layout, "icon-padding"), error);
147 [ + - ]: 6 : if (layer->icon_padding == NULL)
148 : : return NULL;
149 : :
150 : 6 : layer->icon_rotate = shumate_vector_expression_from_json (json_object_get_member (layout, "icon-rotate"), error);
151 [ + - ]: 6 : if (layer->icon_rotate == NULL)
152 : : return NULL;
153 : :
154 : 6 : layer->icon_size = shumate_vector_expression_from_json (json_object_get_member (layout, "icon-size"), error);
155 [ + - ]: 6 : if (layer->icon_size == NULL)
156 : : return NULL;
157 : :
158 : 6 : layer->icon_rotation_alignment = shumate_vector_expression_from_json (json_object_get_member (layout, "icon-rotation-alignment"), error);
159 [ + - ]: 6 : if (layer->icon_rotation_alignment == NULL)
160 : : return NULL;
161 : :
162 : 6 : layer->text_allow_overlap = shumate_vector_expression_from_json (json_object_get_member (layout, "text-allow-overlap"), error);
163 [ + - ]: 6 : if (layer->text_allow_overlap == NULL)
164 : : return NULL;
165 : :
166 : 6 : layer->text_field = shumate_vector_expression_from_json (json_object_get_member (layout, "text-field"), error);
167 [ + - ]: 6 : if (layer->text_field == NULL)
168 : : return NULL;
169 : :
170 : 6 : layer->text_ignore_placement = shumate_vector_expression_from_json (json_object_get_member (layout, "text-ignore-placement"), error);
171 [ + - ]: 6 : if (layer->text_ignore_placement == NULL)
172 : : return NULL;
173 : :
174 : 6 : layer->text_letter_spacing = shumate_vector_expression_from_json (json_object_get_member (layout, "text-letter-spacing"), error);
175 [ + - ]: 6 : if (layer->text_letter_spacing == NULL)
176 : : return NULL;
177 : :
178 : 6 : layer->text_transform = shumate_vector_expression_from_json (json_object_get_member (layout, "text-transform"), error);
179 [ + - ]: 6 : if (layer->text_transform == NULL)
180 : : return NULL;
181 : :
182 : 6 : layer->text_anchor = shumate_vector_expression_from_json (json_object_get_member (layout, "text-anchor"), error);
183 [ + - ]: 6 : if (layer->text_anchor == NULL)
184 : : return NULL;
185 : :
186 : 6 : layer->text_keep_upright = shumate_vector_expression_from_json (json_object_get_member (layout, "text-keep-upright"), error);
187 [ + - ]: 6 : if (layer->text_keep_upright == NULL)
188 : : return NULL;
189 : :
190 : 6 : text_offset_node = json_object_get_member (layout, "text-offset");
191 [ - + ]: 6 : if (text_offset_node != NULL)
192 : : {
193 : 0 : JsonArray *text_offset;
194 : :
195 [ # # ]: 0 : if (!shumate_vector_json_get_array (text_offset_node, &text_offset, error))
196 : 0 : return NULL;
197 : :
198 [ # # ]: 0 : if (json_array_get_length (text_offset) != 2)
199 : : {
200 : 0 : g_set_error (error,
201 : : SHUMATE_STYLE_ERROR,
202 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
203 : : "Expected `text-offset` to be an array of two numbers");
204 : 0 : return NULL;
205 : : }
206 : :
207 : 0 : layer->text_offset_x = json_array_get_double_element (text_offset, 0);
208 : 0 : layer->text_offset_y = json_array_get_double_element (text_offset, 1);
209 : : }
210 : :
211 : 6 : layer->text_optional = shumate_vector_expression_from_json (json_object_get_member (layout, "text-optional"), error);
212 [ + - ]: 6 : if (layer->text_optional == NULL)
213 : : return NULL;
214 : :
215 : 6 : layer->text_overlap = shumate_vector_expression_from_json (json_object_get_member (layout, "text-overlap"), error);
216 [ + - ]: 6 : if (layer->text_overlap == NULL)
217 : : return NULL;
218 : :
219 : 6 : layer->text_rotation_alignment = shumate_vector_expression_from_json (json_object_get_member (layout, "text-rotation-alignment"), error);
220 [ + - ]: 6 : if (layer->text_rotation_alignment == NULL)
221 : : return NULL;
222 : :
223 : 6 : text_font_node = json_object_get_member (layout, "text-font");
224 [ - + ]: 6 : if (text_font_node != NULL)
225 : : {
226 : 0 : g_autoptr(GStrvBuilder) builder = g_strv_builder_new ();
227 [ # # ]: 0 : g_auto(GStrv) fonts = NULL;
228 : :
229 [ # # ]: 0 : if (!shumate_vector_json_get_array (text_font_node, &text_font, error))
230 [ # # ]: 0 : return NULL;
231 : :
232 [ # # ]: 0 : for (int i = 0, n = json_array_get_length (text_font); i < n; i ++)
233 : 0 : g_strv_builder_add (builder, json_array_get_string_element (text_font, i));
234 : :
235 : 0 : fonts = g_strv_builder_end (builder);
236 [ # # ]: 0 : layer->text_fonts = g_strjoinv (", ", fonts);
237 : : }
238 : :
239 : 6 : layer->symbol_placement = shumate_vector_expression_from_json (json_object_get_member (layout, "symbol-placement"), error);
240 [ + - ]: 6 : if (layer->symbol_placement == NULL)
241 : : return NULL;
242 : :
243 : 6 : layer->symbol_spacing = shumate_vector_expression_from_json (json_object_get_member (layout, "symbol-spacing"), error);
244 [ + - ]: 6 : if (layer->symbol_spacing == NULL)
245 : : return NULL;
246 : :
247 : 6 : layer->text_size = shumate_vector_expression_from_json (json_object_get_member (layout, "text-size"), error);
248 [ + - ]: 6 : if (layer->text_size == NULL)
249 : : return NULL;
250 : :
251 : 6 : layer->text_padding = shumate_vector_expression_from_json (json_object_get_member (layout, "text-padding"), error);
252 [ + - ]: 6 : if (layer->text_padding == NULL)
253 : : return NULL;
254 : :
255 : 6 : layer->symbol_sort_key = shumate_vector_expression_from_json (json_object_get_member (layout, "symbol-sort-key"), error);
256 [ + - ]: 6 : if (layer->symbol_sort_key == NULL)
257 : : return NULL;
258 : : }
259 : :
260 : : /* libshumate-specific extensions to the MapLibre style format */
261 [ - + ]: 6 : if (json_object_has_member (object, "metadata"))
262 : : {
263 : 0 : JsonObject *metadata = json_object_get_object_member (object, "metadata");
264 : :
265 : : /* Specifies the cursor to use when hovering over the symbol. In
266 : : * MapLibre GL JS, you would to this by listening to mouseenter and
267 : : * mouseleave and setting the cursor on the whole map via JS.
268 : : *
269 : : * See gdk_cursor_new_from_name for possible values. */
270 : 0 : layer->cursor = shumate_vector_expression_from_json (json_object_get_member (metadata, "libshumate:cursor"), error);
271 [ # # ]: 0 : if (layer->cursor == NULL)
272 : : return NULL;
273 : : }
274 : :
275 : : return (ShumateVectorLayer *)layer;
276 : : }
277 : :
278 : :
279 : : static void
280 : 6 : shumate_vector_symbol_layer_finalize (GObject *object)
281 : : {
282 : 6 : ShumateVectorSymbolLayer *self = SHUMATE_VECTOR_SYMBOL_LAYER (object);
283 : :
284 [ + - ]: 6 : g_clear_object (&self->icon_allow_overlap);
285 [ + - ]: 6 : g_clear_object (&self->icon_anchor);
286 [ - + ]: 6 : g_clear_object (&self->icon_color);
287 [ + - ]: 6 : g_clear_object (&self->icon_ignore_placement);
288 [ + - ]: 6 : g_clear_object (&self->icon_image);
289 [ - + ]: 6 : g_clear_object (&self->icon_opacity);
290 [ + - ]: 6 : g_clear_object (&self->icon_optional);
291 [ + - ]: 6 : g_clear_object (&self->icon_overlap);
292 [ + - ]: 6 : g_clear_object (&self->icon_padding);
293 [ + - ]: 6 : g_clear_object (&self->icon_rotate);
294 [ + - ]: 6 : g_clear_object (&self->icon_rotation_alignment);
295 [ + - ]: 6 : g_clear_object (&self->icon_size);
296 [ + - ]: 6 : g_clear_object (&self->text_allow_overlap);
297 [ + - ]: 6 : g_clear_object (&self->text_field);
298 [ + - ]: 6 : g_clear_object (&self->text_ignore_placement);
299 [ + - ]: 6 : g_clear_object (&self->text_anchor);
300 [ - + ]: 6 : g_clear_object (&self->text_color);
301 [ + - ]: 6 : g_clear_object (&self->text_size);
302 [ - + ]: 6 : g_clear_object (&self->cursor);
303 [ + - ]: 6 : g_clear_object (&self->text_keep_upright);
304 [ + - ]: 6 : g_clear_object (&self->text_letter_spacing);
305 [ - + ]: 6 : g_clear_object (&self->text_opacity);
306 [ + - ]: 6 : g_clear_object (&self->text_optional);
307 [ + - ]: 6 : g_clear_object (&self->text_overlap);
308 [ + - ]: 6 : g_clear_object (&self->text_padding);
309 [ + - ]: 6 : g_clear_object (&self->text_rotation_alignment);
310 [ + - ]: 6 : g_clear_object (&self->text_transform);
311 [ + - ]: 6 : g_clear_object (&self->symbol_sort_key);
312 [ + - ]: 6 : g_clear_object (&self->symbol_placement);
313 [ + - ]: 6 : g_clear_object (&self->symbol_spacing);
314 [ - + ]: 6 : g_clear_pointer (&self->text_fonts, g_free);
315 : :
316 : 6 : G_OBJECT_CLASS (shumate_vector_symbol_layer_parent_class)->finalize (object);
317 : 6 : }
318 : :
319 : : static ShumateVectorSymbolInfo *
320 : 15 : create_symbol_info (ShumateVectorSymbolDetails *details,
321 : : double x,
322 : : double y)
323 : : {
324 : 15 : ShumateVectorSymbolInfo *symbol_info = g_new0 (ShumateVectorSymbolInfo, 1);
325 : 30 : *symbol_info = (ShumateVectorSymbolInfo) {
326 : 15 : .details = shumate_vector_symbol_details_ref (details),
327 : : .x = x,
328 : : .y = y,
329 : : .ref_count = 1,
330 : : };
331 : 15 : return symbol_info;
332 : : }
333 : :
334 : :
335 : : static void
336 : 15 : place_point_label (ShumateVectorSymbolDetails *details,
337 : : double x,
338 : : double y,
339 : : ShumateVectorRenderScope *scope)
340 : : {
341 [ + - + - : 15 : if (x >= 0 && x < 1 && y >= 0 && y < 1)
+ - + - ]
342 : 15 : g_ptr_array_add (scope->symbols, create_symbol_info (details, x, y));
343 : 15 : }
344 : :
345 : :
346 : : static void
347 : 0 : place_line_label (ShumateVectorSymbolDetails *details,
348 : : ShumateVectorRenderScope *scope)
349 : : {
350 : 0 : ShumateVectorSymbolInfo *symbol_info;
351 : 0 : GPtrArray *lines;
352 : 0 : g_autoptr(GPtrArray) simplified_lines = g_ptr_array_new_with_free_func ((GDestroyNotify)shumate_vector_line_string_free);
353 : 0 : guint i;
354 : 0 : float spacing = details->symbol_spacing / scope->target_size;
355 : 0 : float total_length = 0;
356 : 0 : float remaining_distance;
357 : :
358 [ # # ]: 0 : if (spacing <= 0)
359 [ # # ]: 0 : return;
360 : :
361 : 0 : lines = shumate_vector_render_scope_get_geometry (scope);
362 : 0 : g_ptr_array_set_free_func (lines, NULL);
363 [ # # ]: 0 : for (i = 0; i < lines->len; i ++)
364 : : {
365 : 0 : if (details->text_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_MAP
366 [ # # ]: 0 : || details->text_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_VIEWPORT_GLYPH)
367 : : {
368 : 0 : GPtrArray *split_lines = shumate_vector_line_string_simplify ((ShumateVectorLineString *)lines->pdata[i]);
369 : 0 : g_ptr_array_extend_and_steal (simplified_lines, split_lines);
370 : : }
371 : : else
372 : 0 : g_ptr_array_add (simplified_lines, (ShumateVectorLineString *)lines->pdata[i]);
373 : : }
374 : 0 : g_clear_pointer (&lines, g_ptr_array_unref);
375 : :
376 [ # # ]: 0 : for (i = 0; i < simplified_lines->len; i ++)
377 : 0 : total_length += shumate_vector_line_string_length ((ShumateVectorLineString *)simplified_lines->pdata[i]);
378 : :
379 : : /* Make the spacing even on both sides */
380 : 0 : remaining_distance = fmod (total_length / 2, spacing);
381 : :
382 [ # # ]: 0 : for (i = 0; i < simplified_lines->len; i ++)
383 : : {
384 : 0 : ShumateVectorLineString *linestring = (ShumateVectorLineString *)simplified_lines->pdata[i];
385 : 0 : float distance = 0;
386 : 0 : ShumateVectorPointIter iter;
387 : :
388 : 0 : shumate_vector_point_iter_init (&iter, linestring);
389 : :
390 : 0 : shumate_vector_point_iter_advance (&iter, remaining_distance);
391 : 0 : distance += remaining_distance;
392 : :
393 [ # # ]: 0 : while (!shumate_vector_point_iter_is_at_end (&iter))
394 : : {
395 : 0 : ShumateVectorPoint point;
396 : 0 : shumate_vector_point_iter_get_current_point (&iter, &point);
397 : :
398 [ # # # # : 0 : if (point.x >= 0 && point.x < 1 && point.y >= 0 && point.y < 1)
# # # # ]
399 : : {
400 : 0 : symbol_info = create_symbol_info (details, point.x, point.y);
401 : :
402 : 0 : shumate_vector_symbol_info_set_line_points (symbol_info,
403 : : shumate_vector_line_string_copy (linestring),
404 : : distance);
405 : :
406 : 0 : g_ptr_array_add (scope->symbols, symbol_info);
407 : : }
408 : :
409 : 0 : shumate_vector_point_iter_advance (&iter, spacing);
410 : 0 : distance += spacing;
411 : : }
412 : :
413 : 0 : remaining_distance = distance - shumate_vector_line_string_length (linestring);
414 : : }
415 : : }
416 : :
417 : :
418 : : static void
419 : 15 : shumate_vector_symbol_layer_render (ShumateVectorLayer *layer, ShumateVectorRenderScope *scope)
420 : : {
421 : 15 : ShumateVectorSymbolLayer *self = SHUMATE_VECTOR_SYMBOL_LAYER (layer);
422 : 15 : g_auto(ShumateVectorValue) value = SHUMATE_VECTOR_VALUE_INIT;
423 : 15 : g_auto(ShumateVectorValue) icon_padding_value = SHUMATE_VECTOR_VALUE_INIT;
424 : 15 : GPtrArray *icon_padding_array;
425 : 15 : g_autoptr(GPtrArray) text_field = NULL;
426 [ - + - - ]: 15 : g_autofree char *cursor = NULL;
427 : 15 : g_autofree char *feature_id = NULL;
428 : 15 : ShumateVectorPlacement symbol_placement;
429 : 15 : ShumateVectorAlignment icon_rotation_alignment, text_rotation_alignment;
430 : 15 : g_autofree char *text_transform = NULL;
431 : 15 : g_autoptr(GHashTable) tags = NULL;
432 [ + - ]: 15 : g_autoptr(ShumateVectorSymbolDetails) details = NULL;
433 : 30 : g_autoptr(ShumateVectorSprite) icon_image = shumate_vector_expression_eval_image (self->icon_image, scope);
434 : 15 : ShumateVectorGeometryType geometry_type = shumate_vector_render_scope_get_geometry_type (scope);
435 : 15 : VectorTile__Tile__Layer *layer_struct = shumate_vector_reader_iter_get_layer_struct (scope->reader);
436 : 15 : VectorTile__Tile__Feature *feature = shumate_vector_reader_iter_get_feature_struct (scope->reader);
437 : 15 : double x, y;
438 : :
439 : 15 : shumate_vector_expression_eval (self->text_field, scope, &value);
440 [ - + ]: 15 : if (value.type == SHUMATE_VECTOR_VALUE_TYPE_FORMATTED_STRING)
441 : : {
442 : 0 : shumate_vector_value_get_formatted (&value, &text_field);
443 [ # # ]: 0 : if (text_field->len == 0)
444 : 0 : text_field = NULL;
445 : : else
446 : 0 : g_ptr_array_ref (text_field);
447 : : }
448 [ + - ]: 15 : else if (value.type == SHUMATE_VECTOR_VALUE_TYPE_STRING)
449 : : {
450 : 30 : g_autofree char *text = shumate_vector_value_as_string (&value);
451 : :
452 [ + - ]: 15 : if (strlen (text) > 0)
453 : : {
454 : 15 : ShumateVectorFormatPart *part;
455 : 15 : text_field = g_ptr_array_new_with_free_func ((GDestroyNotify)shumate_vector_format_part_free);
456 : 15 : part = g_new0 (ShumateVectorFormatPart, 1);
457 : 15 : part->string = g_steal_pointer (&text);
458 : 15 : g_ptr_array_add (text_field, part);
459 : : }
460 : : }
461 : :
462 [ - + - - ]: 15 : if (text_field == NULL && icon_image == NULL)
463 : : return;
464 : :
465 : 15 : text_transform = shumate_vector_expression_eval_string (self->text_transform, scope, NULL);
466 [ + - - + ]: 15 : if (text_field != NULL && text_transform != NULL)
467 : : {
468 [ # # ]: 0 : for (int i = 0; i < text_field->len; i ++)
469 : : {
470 : 0 : ShumateVectorFormatPart *part = g_ptr_array_index (text_field, i);
471 [ # # ]: 0 : if (part->string != NULL)
472 : : {
473 [ # # ]: 0 : if (g_strcmp0 (text_transform, "uppercase") == 0)
474 : 0 : part->string = g_utf8_strup (part->string, -1);
475 [ # # ]: 0 : else if (g_strcmp0 (text_transform, "lowercase") == 0)
476 : 0 : part->string = g_utf8_strdown (part->string, -1);
477 : : }
478 : : }
479 : : }
480 : :
481 : 15 : symbol_placement = shumate_vector_expression_eval_placement (self->symbol_placement, scope);
482 [ - + ]: 15 : if (geometry_type == SHUMATE_VECTOR_GEOMETRY_POINT && symbol_placement != SHUMATE_VECTOR_PLACEMENT_POINT)
483 [ # # ]: 0 : return;
484 : :
485 : 15 : icon_rotation_alignment = shumate_vector_expression_eval_alignment (self->icon_rotation_alignment, scope);
486 [ + - ]: 15 : if (icon_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_AUTO)
487 : : {
488 : 15 : icon_rotation_alignment = symbol_placement == SHUMATE_VECTOR_PLACEMENT_POINT
489 : : ? SHUMATE_VECTOR_ALIGNMENT_VIEWPORT
490 [ - + ]: 15 : : SHUMATE_VECTOR_ALIGNMENT_MAP;
491 : : }
492 : :
493 : 15 : text_rotation_alignment = shumate_vector_expression_eval_alignment (self->text_rotation_alignment, scope);
494 [ + - ]: 15 : if (text_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_AUTO)
495 : : {
496 : 15 : text_rotation_alignment = symbol_placement == SHUMATE_VECTOR_PLACEMENT_POINT
497 : : ? SHUMATE_VECTOR_ALIGNMENT_VIEWPORT
498 [ - + ]: 15 : : SHUMATE_VECTOR_ALIGNMENT_MAP;
499 : : }
500 : :
501 : 15 : feature_id = g_strdup_printf ("%ld", feature->id);
502 : 15 : cursor = shumate_vector_expression_eval_string (self->cursor, scope, NULL);
503 : :
504 : 15 : tags = shumate_vector_render_scope_create_tag_table (scope);
505 : :
506 : 15 : details = g_new0 (ShumateVectorSymbolDetails, 1);
507 : 45 : *details = (ShumateVectorSymbolDetails) {
508 : : .ref_count = 1,
509 [ - + ]: 15 : .layer = g_strdup (shumate_vector_layer_get_id (layer)),
510 [ - + ]: 15 : .source_layer = g_strdup (layer_struct->name),
511 [ - + ]: 15 : .feature_id = g_strdup (feature_id),
512 : 15 : .tags = g_hash_table_ref (tags),
513 : :
514 : 15 : .icon_anchor = shumate_vector_expression_eval_anchor (self->icon_anchor, scope),
515 : 30 : .icon_ignore_placement = shumate_vector_expression_eval_boolean (self->icon_ignore_placement, scope, FALSE),
516 : 15 : .icon_image = g_steal_pointer (&icon_image),
517 [ + - + - ]: 15 : .icon_opacity = CLAMP (shumate_vector_expression_eval_number (self->icon_opacity, scope, 1.0), 0.0, 1.0),
518 : 15 : .icon_optional = shumate_vector_expression_eval_boolean (self->icon_optional, scope, FALSE),
519 : 15 : .icon_overlap = shumate_vector_expression_eval_overlap (self->icon_overlap, self->icon_allow_overlap, scope),
520 : 15 : .icon_rotate = shumate_vector_expression_eval_number (self->icon_rotate, scope, 0.0) * G_PI / 180,
521 : : .icon_rotation_alignment = icon_rotation_alignment,
522 : 15 : .icon_size = shumate_vector_expression_eval_number (self->icon_size, scope, 1.0),
523 : 15 : .icon_offset_x = self->icon_offset_x,
524 : 15 : .icon_offset_y = self->icon_offset_y,
525 : :
526 : 15 : .formatted_text = g_steal_pointer (&text_field),
527 : :
528 : 15 : .text_anchor = shumate_vector_expression_eval_anchor (self->text_anchor, scope),
529 : 30 : .text_ignore_placement = shumate_vector_expression_eval_boolean (self->text_ignore_placement, scope, FALSE),
530 : 15 : .text_letter_spacing = shumate_vector_expression_eval_number (self->text_letter_spacing, scope, 0.0),
531 : 15 : .text_opacity = shumate_vector_expression_eval_number (self->text_opacity, scope, 1.0),
532 : 15 : .text_optional = shumate_vector_expression_eval_boolean (self->text_optional, scope, FALSE),
533 : 15 : .text_overlap = shumate_vector_expression_eval_overlap (self->text_overlap, self->text_allow_overlap, scope),
534 : 15 : .text_size = shumate_vector_expression_eval_number (self->text_size, scope, 16.0),
535 : 15 : .text_padding = shumate_vector_expression_eval_number (self->text_padding, scope, 2.0),
536 [ - + ]: 15 : .text_font = g_strdup (self->text_fonts),
537 : 15 : .text_keep_upright = shumate_vector_expression_eval_boolean (self->text_keep_upright, scope, TRUE),
538 : : .text_rotation_alignment = text_rotation_alignment,
539 : 15 : .text_offset_x = self->text_offset_x,
540 : 15 : .text_offset_y = self->text_offset_y,
541 : :
542 : : .symbol_placement = symbol_placement,
543 : 15 : .symbol_spacing = shumate_vector_expression_eval_number (self->symbol_spacing, scope, 250),
544 : :
545 [ - + ]: 30 : .cursor = g_strdup (cursor),
546 : 15 : .layer_idx = scope->layer_idx,
547 : 15 : .symbol_sort_key = shumate_vector_expression_eval_number (self->symbol_sort_key, scope, 0),
548 : 15 : .tile_x = scope->tile_x,
549 : 15 : .tile_y = scope->tile_y,
550 : 15 : .tile_zoom_level = scope->zoom_level,
551 : : };
552 : :
553 : 15 : details->icon_color = SHUMATE_VECTOR_COLOR_BLACK;
554 : 15 : shumate_vector_expression_eval_color (self->icon_color, scope, &details->icon_color);
555 : :
556 : 15 : details->text_color = SHUMATE_VECTOR_COLOR_BLACK;
557 : 15 : shumate_vector_expression_eval_color (self->text_color, scope, &details->text_color);
558 : :
559 : 15 : shumate_vector_expression_eval (self->icon_padding, scope, &icon_padding_value);
560 [ - + ]: 15 : if ((icon_padding_array = shumate_vector_value_get_array (&icon_padding_value)) != NULL)
561 : : {
562 [ # # # # ]: 0 : if (icon_padding_array->len == 0 || !shumate_vector_value_get_number (g_ptr_array_index (icon_padding_array, 0), &details->icon_padding_top))
563 : 0 : details->icon_padding_top = 2;
564 : :
565 [ # # # # ]: 0 : if (icon_padding_array->len <= 1 || !shumate_vector_value_get_number (g_ptr_array_index (icon_padding_array, 1), &details->icon_padding_right))
566 : 0 : details->icon_padding_right = details->icon_padding_top;
567 : :
568 [ # # # # ]: 0 : if (icon_padding_array->len <= 2 || !shumate_vector_value_get_number (g_ptr_array_index (icon_padding_array, 2), &details->icon_padding_bottom))
569 : 0 : details->icon_padding_bottom = details->icon_padding_top;
570 : :
571 [ # # # # ]: 0 : if (icon_padding_array->len <= 3 || !shumate_vector_value_get_number (g_ptr_array_index (icon_padding_array, 3), &details->icon_padding_left))
572 : 0 : details->icon_padding_left = details->icon_padding_right;
573 : : }
574 : : else
575 : : {
576 [ + - ]: 15 : if (!shumate_vector_value_get_number (&icon_padding_value, &details->icon_padding_top))
577 : 15 : details->icon_padding_top = 2;
578 : :
579 : 15 : details->icon_padding_right = details->icon_padding_top;
580 : 15 : details->icon_padding_bottom = details->icon_padding_top;
581 : 15 : details->icon_padding_left = details->icon_padding_top;
582 : : }
583 : :
584 [ + - - ]: 15 : switch (shumate_vector_render_scope_get_geometry_type (scope))
585 : : {
586 : 15 : case SHUMATE_VECTOR_GEOMETRY_POINT:
587 : : {
588 : 30 : g_autoptr(GPtrArray) geometry = shumate_vector_render_scope_get_geometry (scope);
589 : :
590 [ + + ]: 30 : for (int i = 0; i < geometry->len; i ++)
591 : : {
592 : 15 : ShumateVectorLineString *string = g_ptr_array_index (geometry, i);
593 : :
594 [ + + ]: 30 : for (int j = 0; j < string->n_points; j ++)
595 : : {
596 : 15 : x = string->points[j].x;
597 : 15 : y = string->points[j].y;
598 : 15 : place_point_label (details, x, y, scope);
599 : : }
600 : : }
601 : : }
602 : :
603 : 15 : break;
604 : :
605 : 0 : case SHUMATE_VECTOR_GEOMETRY_LINESTRING:
606 : : case SHUMATE_VECTOR_GEOMETRY_POLYGON:
607 : 0 : shumate_vector_render_scope_get_geometry_center (scope, &x, &y);
608 : :
609 : 0 : if (symbol_placement == SHUMATE_VECTOR_PLACEMENT_LINE
610 [ # # ]: 0 : || symbol_placement == SHUMATE_VECTOR_PLACEMENT_LINE_CENTER)
611 : 0 : place_line_label (details, scope);
612 : : else
613 : : {
614 [ # # ]: 0 : if (shumate_vector_render_scope_get_geometry_type (scope) == SHUMATE_VECTOR_GEOMETRY_LINESTRING)
615 : : {
616 : 0 : double distance = 0;
617 : 0 : g_autoptr(GPtrArray) geometry = shumate_vector_render_scope_get_geometry (scope);
618 : :
619 [ # # ]: 0 : for (int i = 0; i < geometry->len; i ++)
620 : 0 : distance += shumate_vector_line_string_length (g_ptr_array_index (geometry, i)) / 2;
621 : :
622 [ # # ]: 0 : for (int i = 0; i < geometry->len; i ++)
623 : : {
624 : 0 : ShumateVectorLineString *linestring = g_ptr_array_index (geometry, i);
625 : 0 : ShumateVectorPointIter iter;
626 : :
627 : 0 : shumate_vector_point_iter_init (&iter, linestring);
628 : 0 : shumate_vector_point_iter_advance (&iter, distance);
629 : :
630 [ # # ]: 0 : if (shumate_vector_point_iter_is_at_end (&iter))
631 : 0 : distance -= shumate_vector_line_string_length (linestring);
632 : : else
633 : : {
634 : 0 : ShumateVectorPoint point;
635 : 0 : shumate_vector_point_iter_get_current_point (&iter, &point);
636 : 0 : place_point_label (details, point.x, point.y, scope);
637 : 0 : break;
638 : : }
639 : : }
640 : : }
641 : : else
642 : : {
643 : : // Place at the center of the polygon's bounding box
644 : 0 : place_point_label (details, x, y, scope);
645 : : }
646 : : }
647 : :
648 : : break;
649 : : }
650 : : }
651 : :
652 : :
653 : : static void
654 : 6 : shumate_vector_symbol_layer_class_init (ShumateVectorSymbolLayerClass *klass)
655 : : {
656 : 6 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
657 : 6 : ShumateVectorLayerClass *layer_class = SHUMATE_VECTOR_LAYER_CLASS (klass);
658 : :
659 : 6 : object_class->finalize = shumate_vector_symbol_layer_finalize;
660 : 6 : layer_class->render = shumate_vector_symbol_layer_render;
661 : : }
662 : :
663 : :
664 : : static void
665 : 6 : shumate_vector_symbol_layer_init (ShumateVectorSymbolLayer *self)
666 : : {
667 : 6 : }
|