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 "shumate-vector-symbol-container-private.h"
19 : : #include "shumate-vector-symbol-private.h"
20 : : #include "shumate-vector-collision-private.h"
21 : : #include "shumate-symbol-event-private.h"
22 : : #include "shumate-profiling-private.h"
23 : : #include "shumate-inspector-settings-private.h"
24 : :
25 : : typedef struct {
26 : : int layer_idx;
27 : : GPtrArray *symbols;
28 : : } LayerBucket;
29 : :
30 : : struct _ShumateVectorSymbolContainer
31 : : {
32 : : ShumateLayer parent_instance;
33 : :
34 : : ShumateMapSource *map_source;
35 : :
36 : : GPtrArray *layer_buckets;
37 : : ShumateVectorCollision *collision;
38 : :
39 : : int child_count, visible_count;
40 : :
41 : : double last_rotation;
42 : : double last_zoom;
43 : : double last_center_x, last_center_y;
44 : : double last_width, last_height;
45 : : gboolean labels_changed : 1;
46 : : };
47 : :
48 [ # # # # ]: 0 : G_DEFINE_TYPE (ShumateVectorSymbolContainer, shumate_vector_symbol_container, SHUMATE_TYPE_LAYER)
49 : :
50 : : enum {
51 : : PROP_0,
52 : : PROP_MAP_SOURCE,
53 : : N_PROPS,
54 : : };
55 : :
56 : : static GParamSpec *obj_properties[N_PROPS] = { NULL, };
57 : :
58 : : enum
59 : : {
60 : : SYMBOL_CLICKED,
61 : : LAST_SIGNAL
62 : : };
63 : :
64 : : static guint signals[LAST_SIGNAL] = { 0, };
65 : :
66 : :
67 : : typedef struct {
68 : : graphene_rect_t bounds;
69 : :
70 : : // do not need to be freed because they're owned by the widget
71 : : ShumateVectorSymbol *symbol;
72 : : ShumateVectorSymbolInfo *symbol_info;
73 : :
74 : : // These are coordinates [0, 1) within the tile
75 : : double x;
76 : : double y;
77 : :
78 : : int tile_x;
79 : : int tile_y;
80 : : int zoom;
81 : :
82 : : gboolean visible : 1;
83 : : } ChildInfo;
84 : :
85 : : static void
86 : 0 : layer_bucket_free (LayerBucket *bucket)
87 : : {
88 [ # # ]: 0 : g_clear_pointer (&bucket->symbols, g_ptr_array_unref);
89 : 0 : g_free (bucket);
90 : 0 : }
91 : :
92 : : static void
93 : 0 : add_symbol_to_layer_buckets (ShumateVectorSymbolContainer *self,
94 : : ChildInfo *info)
95 : : {
96 : 0 : LayerBucket *bucket;
97 : :
98 [ # # ]: 0 : for (int i = 0; i < self->layer_buckets->len; i ++)
99 : : {
100 : 0 : bucket = g_ptr_array_index (self->layer_buckets, i);
101 : :
102 [ # # ]: 0 : if (bucket->layer_idx == info->symbol_info->details->layer_idx)
103 : : {
104 : 0 : g_ptr_array_add (bucket->symbols, info);
105 : 0 : return;
106 : : }
107 : : }
108 : :
109 : 0 : bucket = g_new0 (LayerBucket, 1);
110 : 0 : bucket->layer_idx = info->symbol_info->details->layer_idx;
111 : 0 : bucket->symbols = g_ptr_array_new_with_free_func (g_free);
112 : 0 : g_ptr_array_add (self->layer_buckets, bucket);
113 : :
114 : 0 : g_ptr_array_add (bucket->symbols, info);
115 : : }
116 : :
117 : : static int
118 : 0 : child_info_compare (gconstpointer a, gconstpointer b)
119 : : {
120 : 0 : const ChildInfo *child_a = *(ChildInfo **)a;
121 : 0 : const ChildInfo *child_b = *(ChildInfo **)b;
122 : 0 : return child_a->symbol_info->details->symbol_sort_key - child_b->symbol_info->details->symbol_sort_key;
123 : : }
124 : :
125 : : static int
126 : 0 : layer_bucket_compare (gconstpointer a, gconstpointer b)
127 : : {
128 : 0 : const LayerBucket *bucket_a = *(LayerBucket **)a;
129 : 0 : const LayerBucket *bucket_b = *(LayerBucket **)b;
130 : 0 : return bucket_a->layer_idx - bucket_b->layer_idx;
131 : : }
132 : :
133 : : static void
134 : 0 : sort_layer_buckets (ShumateVectorSymbolContainer *self)
135 : : {
136 : 0 : g_ptr_array_sort (self->layer_buckets, (GCompareFunc)layer_bucket_compare);
137 : :
138 [ # # ]: 0 : for (int i = 0; i < self->layer_buckets->len; i ++)
139 : : {
140 : 0 : LayerBucket *bucket = self->layer_buckets->pdata[i];
141 : 0 : g_ptr_array_sort (bucket->symbols, (GCompareFunc)child_info_compare);
142 : : }
143 : 0 : }
144 : :
145 : : ShumateVectorSymbolContainer *
146 : 0 : shumate_vector_symbol_container_new (ShumateMapSource *map_source,
147 : : ShumateViewport *viewport)
148 : : {
149 : 0 : return g_object_new (SHUMATE_TYPE_VECTOR_SYMBOL_CONTAINER,
150 : : "map-source", map_source,
151 : : "viewport", viewport,
152 : : NULL);
153 : : }
154 : :
155 : :
156 : : static void
157 : 0 : on_viewport_changed (ShumateVectorSymbolContainer *self,
158 : : G_GNUC_UNUSED GParamSpec *pspec,
159 : : G_GNUC_UNUSED ShumateViewport *view)
160 : : {
161 : 0 : gtk_widget_queue_allocate (GTK_WIDGET (self));
162 : 0 : }
163 : :
164 : :
165 : : static void
166 : 0 : shumate_vector_symbol_container_constructed (GObject *object)
167 : : {
168 : 0 : ShumateVectorSymbolContainer *self = (ShumateVectorSymbolContainer *)object;
169 : 0 : ShumateInspectorSettings *settings = shumate_inspector_settings_get_default ();
170 : 0 : ShumateViewport *viewport;
171 : 0 : guint tile_size;
172 : :
173 : 0 : G_OBJECT_CLASS (shumate_vector_symbol_container_parent_class)->constructed (object);
174 : :
175 : 0 : tile_size = shumate_map_source_get_tile_size (self->map_source);
176 : 0 : self->collision = shumate_vector_collision_new (tile_size);
177 : :
178 : 0 : viewport = shumate_layer_get_viewport (SHUMATE_LAYER (self));
179 : :
180 : 0 : g_signal_connect_swapped (viewport, "notify::longitude", G_CALLBACK (on_viewport_changed), self);
181 : 0 : g_signal_connect_swapped (viewport, "notify::latitude", G_CALLBACK (on_viewport_changed), self);
182 : 0 : g_signal_connect_swapped (viewport, "notify::zoom-level", G_CALLBACK (on_viewport_changed), self);
183 : 0 : g_signal_connect_swapped (viewport, "notify::rotation", G_CALLBACK (on_viewport_changed), self);
184 : :
185 : 0 : g_signal_connect_object (settings, "notify::show-collision-boxes", G_CALLBACK (on_viewport_changed), self, G_CONNECT_SWAPPED);
186 : 0 : }
187 : :
188 : :
189 : : static void
190 : 0 : shumate_vector_symbol_container_finalize (GObject *object)
191 : : {
192 : 0 : ShumateVectorSymbolContainer *self = (ShumateVectorSymbolContainer *)object;
193 : :
194 [ # # ]: 0 : g_clear_pointer (&self->layer_buckets, g_ptr_array_unref);
195 [ # # ]: 0 : g_clear_pointer (&self->collision, shumate_vector_collision_free);
196 : :
197 : 0 : G_OBJECT_CLASS (shumate_vector_symbol_container_parent_class)->finalize (object);
198 : 0 : }
199 : :
200 : :
201 : : static void
202 : 0 : shumate_vector_symbol_container_dispose (GObject *object)
203 : : {
204 : 0 : ShumateVectorSymbolContainer *self = (ShumateVectorSymbolContainer *)object;
205 : 0 : ShumateViewport *viewport = shumate_layer_get_viewport (SHUMATE_LAYER (self));
206 : 0 : GtkWidget *child;
207 : :
208 : 0 : g_signal_handlers_disconnect_by_data (viewport, self);
209 : :
210 [ # # ]: 0 : while ((child = gtk_widget_get_first_child (GTK_WIDGET (object))))
211 : 0 : gtk_widget_unparent (child);
212 : :
213 : 0 : G_OBJECT_CLASS (shumate_vector_symbol_container_parent_class)->dispose (object);
214 : 0 : }
215 : :
216 : :
217 : : static void
218 : 0 : shumate_vector_symbol_container_get_property (GObject *object,
219 : : guint property_id,
220 : : GValue *value,
221 : : GParamSpec *pspec)
222 : : {
223 : 0 : ShumateVectorSymbolContainer *self = (ShumateVectorSymbolContainer *)object;
224 : :
225 [ # # ]: 0 : switch (property_id)
226 : : {
227 : 0 : case PROP_MAP_SOURCE:
228 : 0 : g_value_set_object (value, self->map_source);
229 : 0 : break;
230 : :
231 : 0 : default:
232 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
233 : : }
234 : 0 : }
235 : :
236 : :
237 : : static void
238 : 0 : shumate_vector_symbol_container_set_property (GObject *object,
239 : : guint property_id,
240 : : const GValue *value,
241 : : GParamSpec *pspec)
242 : : {
243 : 0 : ShumateVectorSymbolContainer *self = SHUMATE_VECTOR_SYMBOL_CONTAINER (object);
244 : :
245 [ # # ]: 0 : switch (property_id)
246 : : {
247 : 0 : case PROP_MAP_SOURCE:
248 : 0 : g_set_object (&self->map_source, g_value_get_object (value));
249 : 0 : gtk_widget_queue_allocate (GTK_WIDGET (self));
250 : 0 : break;
251 : :
252 : 0 : default:
253 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
254 : : }
255 : 0 : }
256 : :
257 : : static double
258 : 0 : get_effective_zoom_level (ShumateMapSource *map_source, ShumateViewport *viewport)
259 : : {
260 : 0 : double zoom_level = shumate_viewport_get_zoom_level (viewport);
261 : 0 : double our_tile_size = shumate_map_source_get_tile_size (map_source);
262 : 0 : double reference_tile_size = shumate_map_source_get_tile_size (shumate_viewport_get_reference_map_source (viewport));
263 : 0 : return log2 (reference_tile_size / our_tile_size) + zoom_level;
264 : : }
265 : :
266 : : static void
267 : 0 : rotate_around_origin (double *x,
268 : : double *y,
269 : : double angle)
270 : : {
271 : : /* Rotate (x, y) around (0, 0) */
272 : :
273 [ # # ]: 0 : if (angle == 0)
274 : : return;
275 : :
276 : 0 : double old_x = *x;
277 : 0 : double old_y = *y;
278 : :
279 : 0 : *x = cosf (angle) * old_x - sinf (angle) * old_y;
280 : 0 : *y = sinf (angle) * old_x + cosf (angle) * old_y;
281 : : }
282 : :
283 : :
284 : : static void
285 : 0 : rotate_around_center (double *x,
286 : : double *y,
287 : : double width,
288 : : double height,
289 : : double angle)
290 : : {
291 : : /* Rotate (x, y) around (width / 2, height / 2) */
292 : :
293 : 0 : double center_x = width / 2.0;
294 : 0 : double center_y = height / 2.0;
295 : :
296 : 0 : *x -= center_x;
297 : 0 : *y -= center_y;
298 : 0 : rotate_around_origin (x, y, angle);
299 : 0 : *x += center_x;
300 : 0 : *y += center_y;
301 : 0 : }
302 : :
303 : :
304 : : static void
305 : 0 : shumate_vector_symbol_container_size_allocate (GtkWidget *widget,
306 : : int width,
307 : : int height,
308 : : int baseline)
309 : : {
310 : 0 : SHUMATE_PROFILE_START ();
311 : :
312 : 0 : ShumateVectorSymbolContainer *self = SHUMATE_VECTOR_SYMBOL_CONTAINER (widget);
313 : 0 : GtkAllocation alloc;
314 : 0 : double tile_size = shumate_map_source_get_tile_size (self->map_source);
315 : 0 : ShumateViewport *viewport = shumate_layer_get_viewport (SHUMATE_LAYER (self));
316 : 0 : double zoom_level = get_effective_zoom_level (self->map_source, viewport);
317 : 0 : double rotation = shumate_viewport_get_rotation (viewport);
318 : 0 : double center_x = shumate_map_source_get_x (self->map_source, zoom_level, shumate_location_get_longitude (SHUMATE_LOCATION (viewport)));
319 : 0 : double center_y = shumate_map_source_get_y (self->map_source, zoom_level, shumate_location_get_latitude (SHUMATE_LOCATION (viewport)));
320 : :
321 : 0 : gboolean recalc = self->labels_changed
322 [ # # ]: 0 : || self->last_zoom != zoom_level
323 [ # # ]: 0 : || self->last_rotation != rotation
324 [ # # ]: 0 : || self->last_width != width
325 [ # # # # ]: 0 : || self->last_height != height;
326 : :
327 : 0 : if (recalc)
328 : : {
329 : 0 : shumate_vector_collision_clear (self->collision);
330 : 0 : self->last_center_x = center_x;
331 : 0 : self->last_center_y = center_y;
332 : 0 : self->collision->delta_x = 0;
333 : 0 : self->collision->delta_y = 0;
334 : 0 : self->visible_count = 0;
335 : : }
336 : : else
337 : : {
338 : 0 : self->collision->delta_x = center_x - self->last_center_x;
339 : 0 : self->collision->delta_y = center_y - self->last_center_y;
340 : 0 : rotate_around_origin (&self->collision->delta_x, &self->collision->delta_y, rotation);
341 : : }
342 : :
343 : : /* Higher layers have priority during placement, so iterate the
344 : : array from back to front */
345 [ # # ]: 0 : for (int i = self->layer_buckets->len - 1; i >= 0; i --)
346 : : {
347 : 0 : LayerBucket *bucket = g_ptr_array_index (self->layer_buckets, i);
348 : :
349 [ # # ]: 0 : for (int j = 0; j < bucket->symbols->len; j ++)
350 : : {
351 : 0 : ChildInfo *child = g_ptr_array_index (bucket->symbols, j);
352 : 0 : double tile_size_at_zoom = tile_size * pow (2, zoom_level - child->zoom);
353 : 0 : double x = (child->tile_x + child->x) * tile_size_at_zoom - center_x + width/2.0;
354 : 0 : double y = (child->tile_y + child->y) * tile_size_at_zoom - center_y + height/2.0;
355 : :
356 : 0 : rotate_around_center (&x, &y, width, height, rotation);
357 : :
358 [ # # ]: 0 : if (recalc)
359 : : {
360 : 0 : gboolean now_visible =
361 : 0 : shumate_vector_symbol_calculate_collision (child->symbol,
362 : : self->collision,
363 : : x,
364 : : y,
365 : : tile_size_at_zoom,
366 : : rotation,
367 : : &child->bounds);
368 : :
369 [ # # ]: 0 : if (now_visible != child->visible)
370 : : {
371 : 0 : gtk_widget_set_child_visible (GTK_WIDGET (child->symbol), now_visible);
372 : 0 : child->visible = now_visible;
373 : : }
374 : :
375 [ # # ]: 0 : if (now_visible)
376 : 0 : self->visible_count ++;
377 : : }
378 : :
379 [ # # ]: 0 : if (!child->visible)
380 : 0 : continue;
381 : :
382 : 0 : gtk_widget_measure (GTK_WIDGET (child->symbol), GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL, NULL, NULL);
383 : 0 : gtk_widget_measure (GTK_WIDGET (child->symbol), GTK_ORIENTATION_VERTICAL, -1, NULL, NULL, NULL, NULL);
384 : :
385 : 0 : alloc.x = child->bounds.origin.x - self->collision->delta_x;
386 : 0 : alloc.y = child->bounds.origin.y - self->collision->delta_y;
387 : 0 : alloc.width = child->bounds.size.width;
388 : 0 : alloc.height = child->bounds.size.height;
389 : :
390 : 0 : gtk_widget_size_allocate (GTK_WIDGET (child->symbol), &alloc, -1);
391 [ # # ]: 0 : if (child->symbol_info->line != NULL)
392 : 0 : gtk_widget_queue_draw (GTK_WIDGET (child->symbol));
393 : : }
394 : : }
395 : :
396 : 0 : self->labels_changed = FALSE;
397 : 0 : self->last_rotation = rotation;
398 : 0 : self->last_zoom = zoom_level;
399 : 0 : self->last_width = width;
400 : 0 : self->last_height = height;
401 : 0 : }
402 : :
403 : :
404 : : static void
405 : 0 : shumate_vector_symbol_container_snapshot (GtkWidget *widget,
406 : : GtkSnapshot *snapshot)
407 : : {
408 : 0 : SHUMATE_PROFILE_START ();
409 : :
410 : 0 : ShumateVectorSymbolContainer *self = SHUMATE_VECTOR_SYMBOL_CONTAINER (widget);
411 : 0 : ShumateInspectorSettings *settings = shumate_inspector_settings_get_default ();
412 : :
413 [ # # ]: 0 : for (int i = 0; i < self->layer_buckets->len; i ++)
414 : : {
415 : 0 : LayerBucket *bucket = g_ptr_array_index (self->layer_buckets, i);
416 : :
417 [ # # ]: 0 : for (int j = 0; j < bucket->symbols->len; j ++)
418 : : {
419 : 0 : ChildInfo *child = g_ptr_array_index (bucket->symbols, j);
420 : 0 : double correct_x = child->bounds.origin.x - self->collision->delta_x;
421 : 0 : double correct_y = child->bounds.origin.y - self->collision->delta_y;
422 : :
423 : 0 : gtk_snapshot_save (snapshot);
424 : 0 : gtk_snapshot_translate (snapshot,
425 : 0 : &GRAPHENE_POINT_INIT (
426 : : correct_x - (int) correct_x,
427 : : correct_y - (int) correct_y
428 : : ));
429 : 0 : gtk_widget_snapshot_child (widget, GTK_WIDGET (child->symbol), snapshot);
430 : 0 : gtk_snapshot_restore (snapshot);
431 : : }
432 : : }
433 : :
434 [ # # ]: 0 : if (shumate_inspector_settings_get_show_collision_boxes (settings))
435 : : {
436 : 0 : ShumateViewport *viewport = shumate_layer_get_viewport (SHUMATE_LAYER (self));
437 : 0 : double rotation = shumate_viewport_get_rotation (viewport);
438 : 0 : double delta_x = -self->collision->delta_x;
439 : 0 : double delta_y = -self->collision->delta_y;
440 : :
441 : 0 : rotate_around_origin (&delta_x, &delta_y, rotation);
442 : :
443 : 0 : gtk_snapshot_save (snapshot);
444 : 0 : gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (delta_x, delta_y));
445 : 0 : shumate_vector_collision_visualize (self->collision, snapshot);
446 : 0 : gtk_snapshot_restore (snapshot);
447 : : }
448 : 0 : }
449 : :
450 : :
451 : : static void
452 : 0 : shumate_vector_symbol_container_class_init (ShumateVectorSymbolContainerClass *klass)
453 : : {
454 : 0 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
455 : 0 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
456 : :
457 : 0 : object_class->constructed = shumate_vector_symbol_container_constructed;
458 : 0 : object_class->dispose = shumate_vector_symbol_container_dispose;
459 : 0 : object_class->finalize = shumate_vector_symbol_container_finalize;
460 : 0 : object_class->get_property = shumate_vector_symbol_container_get_property;
461 : 0 : object_class->set_property = shumate_vector_symbol_container_set_property;
462 : 0 : widget_class->size_allocate = shumate_vector_symbol_container_size_allocate;
463 : 0 : widget_class->snapshot = shumate_vector_symbol_container_snapshot;
464 : :
465 : 0 : obj_properties[PROP_MAP_SOURCE] =
466 : 0 : g_param_spec_object ("map-source",
467 : : "Map source",
468 : : "Map source",
469 : : SHUMATE_TYPE_MAP_SOURCE,
470 : : G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
471 : :
472 : 0 : g_object_class_install_properties (object_class, N_PROPS, obj_properties);
473 : :
474 : 0 : signals[SYMBOL_CLICKED] =
475 : 0 : g_signal_new ("symbol-clicked",
476 : : G_OBJECT_CLASS_TYPE (object_class),
477 : : G_SIGNAL_RUN_LAST,
478 : : 0, NULL, NULL,
479 : : g_cclosure_marshal_VOID__OBJECT,
480 : : G_TYPE_NONE,
481 : : 1,
482 : : SHUMATE_TYPE_SYMBOL_EVENT);
483 : 0 : }
484 : :
485 : : static void
486 : 0 : shumate_vector_symbol_container_init (ShumateVectorSymbolContainer *self)
487 : : {
488 : 0 : self->layer_buckets = g_ptr_array_new_with_free_func ((GDestroyNotify)layer_bucket_free);
489 : 0 : }
490 : :
491 : : static void
492 : 0 : on_symbol_clicked (ShumateVectorSymbolContainer *self,
493 : : ShumateSymbolEvent *event,
494 : : ShumateVectorSymbol *symbol)
495 : : {
496 : 0 : ShumateVectorSymbolInfo *symbol_info = shumate_vector_symbol_get_symbol_info (symbol);
497 : :
498 : 0 : double tile_size = shumate_map_source_get_tile_size (self->map_source);
499 : 0 : double lat = shumate_map_source_get_latitude (self->map_source,
500 : 0 : symbol_info->details->tile_zoom_level,
501 : 0 : (symbol_info->details->tile_y + symbol_info->y) * tile_size);
502 : 0 : double lon = shumate_map_source_get_longitude (self->map_source,
503 : 0 : symbol_info->details->tile_zoom_level,
504 : 0 : (symbol_info->details->tile_x + symbol_info->x) * tile_size);
505 : :
506 : 0 : shumate_symbol_event_set_lat_lon (event, lat, lon);
507 : :
508 : 0 : g_signal_emit (self, signals[SYMBOL_CLICKED], 0, event);
509 : 0 : }
510 : :
511 : :
512 : : void
513 : 0 : shumate_vector_symbol_container_add_symbols (ShumateVectorSymbolContainer *self,
514 : : GPtrArray *symbol_infos,
515 : : int tile_x,
516 : : int tile_y,
517 : : int zoom)
518 : : {
519 : 0 : SHUMATE_PROFILE_START ();
520 : :
521 [ # # ]: 0 : g_return_if_fail (SHUMATE_IS_VECTOR_SYMBOL_CONTAINER (self));
522 : :
523 [ # # ]: 0 : for (int i = 0; i < symbol_infos->len; i ++)
524 : : {
525 : 0 : ChildInfo *info = g_new0 (ChildInfo, 1);
526 : 0 : ShumateVectorSymbolInfo *symbol_info = symbol_infos->pdata[i];
527 : 0 : ShumateVectorSymbol *symbol = shumate_vector_symbol_new (symbol_info);
528 : :
529 : 0 : info->symbol = symbol;
530 : 0 : info->symbol_info = symbol_info;
531 : 0 : info->x = symbol_info->x;
532 : 0 : info->y = symbol_info->y;
533 : 0 : info->tile_x = tile_x;
534 : 0 : info->tile_y = tile_y;
535 : 0 : info->zoom = zoom;
536 : 0 : info->visible = TRUE;
537 : :
538 : 0 : add_symbol_to_layer_buckets (self, info);
539 : 0 : gtk_widget_set_parent (GTK_WIDGET (info->symbol), GTK_WIDGET (self));
540 : 0 : self->child_count ++;
541 : :
542 : 0 : g_signal_connect_object (info->symbol,
543 : : "clicked",
544 : : G_CALLBACK (on_symbol_clicked),
545 : : self,
546 : : G_CONNECT_SWAPPED);
547 : : }
548 : :
549 : 0 : sort_layer_buckets (self);
550 : 0 : self->labels_changed = TRUE;
551 : : }
552 : :
553 : :
554 : : void
555 : 0 : shumate_vector_symbol_container_remove_symbols (ShumateVectorSymbolContainer *self,
556 : : int tile_x,
557 : : int tile_y,
558 : : int zoom)
559 : : {
560 : 0 : SHUMATE_PROFILE_START ();
561 : :
562 [ # # ]: 0 : g_return_if_fail (SHUMATE_IS_VECTOR_SYMBOL_CONTAINER (self));
563 : :
564 [ # # ]: 0 : for (int i = 0; i < self->layer_buckets->len; i ++)
565 : : {
566 : 0 : LayerBucket *bucket = g_ptr_array_index (self->layer_buckets, i);
567 : 0 : int k = 0;
568 : :
569 [ # # ]: 0 : for (int j = 0; j < bucket->symbols->len; j ++)
570 : : {
571 : 0 : ChildInfo *info = g_ptr_array_index (bucket->symbols, j);
572 : :
573 [ # # # # : 0 : if (info->tile_x == tile_x && info->tile_y == tile_y && info->zoom == zoom)
# # ]
574 : : {
575 : 0 : gtk_widget_unparent (GTK_WIDGET (info->symbol));
576 : 0 : self->child_count --;
577 [ # # ]: 0 : g_clear_pointer (&g_ptr_array_index (bucket->symbols, j), g_free);
578 : : }
579 : : else
580 : : {
581 : 0 : g_ptr_array_index (bucket->symbols, j) = NULL;
582 : 0 : g_ptr_array_index (bucket->symbols, k) = info;
583 : 0 : k ++;
584 : : }
585 : : }
586 : :
587 : 0 : g_ptr_array_set_size (bucket->symbols, k);
588 : : }
589 : :
590 : 0 : self->labels_changed = TRUE;
591 : : }
592 : :
593 : :
594 : : ShumateMapSource *
595 : 0 : shumate_vector_symbol_container_get_map_source (ShumateVectorSymbolContainer *self)
596 : : {
597 [ # # ]: 0 : g_return_val_if_fail (SHUMATE_IS_VECTOR_SYMBOL_CONTAINER (self), NULL);
598 : 0 : return self->map_source;
599 : : }
600 : :
601 : :
602 : : ShumateVectorCollision *
603 : 0 : shumate_vector_symbol_container_get_collision (ShumateVectorSymbolContainer *self)
604 : : {
605 [ # # ]: 0 : g_return_val_if_fail (SHUMATE_IS_VECTOR_SYMBOL_CONTAINER (self), NULL);
606 : 0 : return self->collision;
607 : : }
608 : :
609 : :
610 : : char *
611 : 0 : shumate_vector_symbol_container_get_debug_text (ShumateVectorSymbolContainer *self)
612 : : {
613 : 0 : return g_strdup_printf ("symbols: %d, %d visible\n", self->child_count, self->visible_count);
614 : : }
|