GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 85.6% 179 / 0 / 209
Functions: 89.3% 25 / 0 / 28
Branches: 62.7% 104 / 0 / 166

shumate/shumate-vector-reader-iter.c
Line Branch Exec Source
1 /*
2 * Copyright (C) 2023 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-reader-private.h"
19 #include "shumate-vector-reader-iter-private.h"
20 #include "vector/shumate-vector-utils-private.h"
21
22 /**
23 * ShumateVectorReaderIter:
24 *
25 * Reads the layers and features of a vector tile.
26 *
27 * To create a new [class@VectorReaderIter], use [method@VectorReader.iterate].
28 *
29 * A vector tile consists of named layers, which contain features. Each feature
30 * has an ID, a geometry, and a set of key/value tags. The meanings of
31 * the IDs and tags depends on the data source that the tile came from. The
32 * [OpenMapTiles schema](https://openmaptiles.org/schema/) is a common schema
33 * for vector tiles.
34 *
35 * To read all layers in a tile, use [method@VectorReaderIter.get_layer_count] and
36 * [method@VectorReaderIter.read_layer]. If you know the name of the layer you
37 * want, you can also use [method@VectorReaderIter.read_layer_by_name].
38 * Once the iterator is reading a layer, you can call
39 * [method@VectorReaderIter.next_feature] in a loop to read all the features in
40 * the layer.
41 *
42 * A [class@VectorReaderIter] is not thread-safe, but iterators created
43 * from the same [class@VectorReader] can be used in different threads.
44 *
45 * See [the Mapbox Vector Tile specification](https://github.com/mapbox/vector-tile-spec/tree/master/2.1)
46 * for more information about the vector tile format.
47 *
48 * Since: 1.2
49 */
50
51 struct _ShumateVectorReaderIter
52 {
53 GObject parent_instance;
54
55 ShumateVectorReader *reader;
56
57 VectorTile__Tile__Layer *layer;
58 VectorTile__Tile__Feature *feature;
59 int feature_index;
60 };
61
62 enum {
63 PROP_0,
64 PROP_READER,
65 N_PROPERTIES
66 };
67
68 static GParamSpec *properties[N_PROPERTIES];
69
70
4/6
shumate_vector_reader_iter_class_intern_init:
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 9 times.
shumate_vector_reader_iter_get_type:
✓ Branch 2 → 3 taken 9 times.
✓ Branch 2 → 7 taken 450 times.
✓ Branch 4 → 5 taken 9 times.
✗ Branch 4 → 7 not taken.
477 G_DEFINE_TYPE (ShumateVectorReaderIter, shumate_vector_reader_iter, G_TYPE_OBJECT)
71
72 static void
73 ✗ shumate_vector_reader_iter_get_property (GObject *object,
74 guint prop_id,
75 GValue *value,
76 GParamSpec *pspec)
77 {
78 ✗ ShumateVectorReaderIter *self = SHUMATE_VECTOR_READER_ITER (object);
79
80 ✗ switch (prop_id)
81 {
82 ✗ case PROP_READER:
83 ✗ g_value_set_object (value, self->reader);
84 ✗ break;
85 ✗ default:
86 ✗ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
87 }
88 ✗ }
89
90 static void
91 26 shumate_vector_reader_iter_set_property (GObject *object,
92 guint prop_id,
93 const GValue *value,
94 GParamSpec *pspec)
95 {
96 26 ShumateVectorReaderIter *self = SHUMATE_VECTOR_READER_ITER (object);
97
98
1/2
✓ Branch 2 → 3 taken 26 times.
✗ Branch 2 → 5 not taken.
26 switch (prop_id)
99 {
100 26 case PROP_READER:
101 26 self->reader = g_value_dup_object (value);
102 26 break;
103 ✗ default:
104 ✗ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
105 }
106 26 }
107
108 static void
109 26 shumate_vector_reader_iter_finalize (GObject *object)
110 {
111 26 ShumateVectorReaderIter *self = SHUMATE_VECTOR_READER_ITER (object);
112
113
1/2
✓ Branch 2 → 3 taken 26 times.
✗ Branch 2 → 4 not taken.
26 g_clear_object (&self->reader);
114
115 26 G_OBJECT_CLASS (shumate_vector_reader_iter_parent_class)->finalize (object);
116 26 }
117
118 static void
119 9 shumate_vector_reader_iter_class_init (ShumateVectorReaderIterClass *klass)
120 {
121 9 GObjectClass *object_class = G_OBJECT_CLASS (klass);
122
123 9 object_class->finalize = shumate_vector_reader_iter_finalize;
124 9 object_class->get_property = shumate_vector_reader_iter_get_property;
125 9 object_class->set_property = shumate_vector_reader_iter_set_property;
126
127 /**
128 * VectorReaderIter:reader:
129 *
130 * The [class@VectorReader] that the iterator is iterating over.
131 *
132 * Since: 1.2
133 */
134 18 properties[PROP_READER] =
135 9 g_param_spec_object ("reader", NULL, NULL,
136 SHUMATE_TYPE_VECTOR_READER,
137 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
138
139 9 g_object_class_install_properties (object_class, N_PROPERTIES, properties);
140 9 }
141
142 static void
143 26 shumate_vector_reader_iter_init (ShumateVectorReaderIter *self)
144 {
145 26 }
146
147 /**
148 * shumate_vector_reader_iter_get_reader:
149 * @self: A [class@VectorReaderIter]
150 *
151 * Gets the reader that the iterator is iterating over.
152 *
153 * Returns: (transfer none): The reader that the iterator is iterating over.
154 *
155 * Since: 1.2
156 */
157 ShumateVectorReader *
158 ✗ shumate_vector_reader_iter_get_reader (ShumateVectorReaderIter *self)
159 {
160 ✗ g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), NULL);
161 ✗ return self->reader;
162 }
163
164 /**
165 * shumate_vector_reader_iter_get_layer_count:
166 * @self: A #ShumateVectorReader.
167 *
168 * Gets the number of layers in the vector tile.
169 *
170 * Returns: The number of layers.
171 *
172 * Since: 1.2
173 */
174 guint
175 2 shumate_vector_reader_iter_get_layer_count (ShumateVectorReaderIter *self)
176 {
177
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
2 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), 0);
178
179 2 return self->reader->tile->n_layers;
180 }
181
182 /**
183 * shumate_vector_reader_iter_read_layer:
184 * @self: A #ShumateVectorReader.
185 * @index: The index of the layer to read.
186 *
187 * Sets the current layer of the reader to the layer at the given index.
188 *
189 * Since: 1.2
190 */
191 void
192 4 shumate_vector_reader_iter_read_layer (ShumateVectorReaderIter *self, int index)
193 {
194
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 4 times.
4 g_return_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self));
195
196
1/2
✓ Branch 5 → 6 taken 4 times.
✗ Branch 5 → 7 not taken.
4 g_return_if_fail (index >= 0);
197
1/2
✓ Branch 6 → 8 taken 4 times.
✗ Branch 6 → 9 not taken.
4 g_return_if_fail (index < self->reader->tile->n_layers);
198
199 4 self->layer = self->reader->tile->layers[index];
200 4 self->feature = NULL;
201 }
202
203 /**
204 * shumate_vector_reader_iter_read_layer_by_name:
205 * @self: A #ShumateVectorReader.
206 *
207 * Moves the iterator to the layer with the given name, if present.
208 *
209 * If the layer is not found, the current layer will be set to %NULL and the
210 * function will return %FALSE. Layers are typically omitted if they are empty,
211 * so don't assume that a layer in the schema will always be present.
212 *
213 * The iterator's current feature will be %NULL after calling this function;
214 * use [method@VectorReaderIter.next_feature] to advance to the first feature
215 * in the layer.
216 *
217 * Returns: %TRUE if the layer was found, %FALSE otherwise.
218 *
219 * Since: 1.2
220 */
221 gboolean
222 28 shumate_vector_reader_iter_read_layer_by_name (ShumateVectorReaderIter *self,
223 const char *name)
224 {
225
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 28 times.
28 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), FALSE);
226
227 28 self->layer = NULL;
228 28 self->feature = NULL;
229
230
1/2
✓ Branch 9 → 6 taken 58 times.
✗ Branch 9 → 10 not taken.
58 for (int i = 0; i < self->reader->tile->n_layers; i++)
231 {
232
2/2
✓ Branch 6 → 7 taken 28 times.
✓ Branch 6 → 8 taken 30 times.
58 if (strcmp (self->reader->tile->layers[i]->name, name) == 0)
233 {
234 28 self->layer = self->reader->tile->layers[i];
235 28 self->feature = NULL;
236 28 return TRUE;
237 }
238 }
239
240 return FALSE;
241 }
242
243 /*< private >
244 * shumate_vector_reader_iter_get_layer_index:
245 * @self: A #ShumateVectorReaderIter.
246 *
247 * Gets the index of the current layer.
248 *
249 * Returns: The index of the current layer.
250 *
251 * Since: 1.3
252 */
253 int
254 20 shumate_vector_reader_iter_get_layer_index (ShumateVectorReaderIter *self)
255 {
256
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 20 times.
20 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), -1);
257
258
1/2
✓ Branch 7 → 5 taken 50 times.
✗ Branch 7 → 8 not taken.
50 for (int i = 0; i < self->reader->tile->n_layers; i++)
259 {
260
2/2
✓ Branch 5 → 6 taken 30 times.
✓ Branch 5 → 8 taken 20 times.
50 if (self->layer == self->reader->tile->layers[i])
261 {
262 return i;
263 }
264 }
265
266 return -1;
267 }
268
269 /**
270 * shumate_vector_reader_iter_get_layer_name:
271 * @self: A #ShumateVectorReader.
272 *
273 * Gets the name of the current layer.
274 *
275 * Returns: (transfer none): The name of the current layer.
276 *
277 * Since: 1.2
278 */
279 const char *
280 18 shumate_vector_reader_iter_get_layer_name (ShumateVectorReaderIter *self)
281 {
282
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 18 times.
18 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), NULL);
283
284
1/2
✓ Branch 5 → 6 taken 18 times.
✗ Branch 5 → 7 not taken.
18 g_return_val_if_fail (self->layer != NULL, NULL);
285 18 return self->layer->name;
286 }
287
288 /**
289 * shumate_vector_reader_iter_get_layer_feature_count:
290 * @self: A #ShumateVectorReader.
291 *
292 * Gets the number of features in the current layer.
293 *
294 * You can loop over all features in the current layer by calling
295 * [method@VectorReaderIter.read_feature] with each index from 0 to
296 * the feature count, but it might be easier to use
297 * [method@VectorReaderIter.next_feature] instead.
298 *
299 * Returns: The number of features in the current layer.
300 *
301 * Since: 1.2
302 */
303 guint
304 4 shumate_vector_reader_iter_get_layer_feature_count (ShumateVectorReaderIter *self)
305 {
306
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 4 times.
4 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), 0);
307
308
1/2
✓ Branch 5 → 6 taken 4 times.
✗ Branch 5 → 7 not taken.
4 g_return_val_if_fail (self->layer != NULL, 0);
309 4 return self->layer->n_features;
310 }
311
312 /**
313 * shumate_vector_reader_iter_get_layer_extent:
314 * @self: A [class@VectorReaderIter].
315 *
316 * Gets the extent for coordinates in the current layer.
317 *
318 * 0 represents the top and left edges of the tile, and this value
319 * represents the bottom and right edges. Feature geometries may extend
320 * outside of this range, since tiles often include some margin.
321 *
322 * Tiles do not contain metadata about the location of the tile within
323 * the world, so it is up to the caller to know the tile's coordinates
324 * and convert latitude/longitude to tile-space coordinates.
325 *
326 * Returns: The layer's extent
327 *
328 * Since: 1.2
329 */
330 guint
331 4 shumate_vector_reader_iter_get_layer_extent (ShumateVectorReaderIter *self)
332 {
333
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 4 times.
4 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), 0);
334
335
1/2
✓ Branch 5 → 6 taken 4 times.
✗ Branch 5 → 7 not taken.
4 g_return_val_if_fail (self->layer != NULL, 0);
336 4 return self->layer->extent;
337 }
338
339 /**
340 * shumate_vector_reader_iter_read_feature:
341 * @self: A #ShumateVectorReader.
342 * @index: The index of the feature to read.
343 *
344 * Moves the iterator to the feature at the given index in the current layer.
345 *
346 * You can get the number of features in the current layer with
347 * [method@VectorReaderIter.get_layer_feature_count].
348 *
349 * Since: 1.2
350 */
351 void
352 10 shumate_vector_reader_iter_read_feature (ShumateVectorReaderIter *self,
353 int index)
354 {
355
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 10 times.
10 g_return_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self));
356
357
1/2
✓ Branch 5 → 6 taken 10 times.
✗ Branch 5 → 7 not taken.
10 g_return_if_fail (index >= 0);
358
1/2
✓ Branch 6 → 8 taken 10 times.
✗ Branch 6 → 9 not taken.
10 g_return_if_fail (self->layer != NULL);
359
1/2
✓ Branch 8 → 10 taken 10 times.
✗ Branch 8 → 11 not taken.
10 g_return_if_fail (index < self->layer->n_features);
360
361 10 self->feature = self->layer->features[index];
362 10 self->feature_index = index;
363 }
364
365 /**
366 * shumate_vector_reader_iter_next_feature:
367 * @self: A #ShumateVectorReader.
368 *
369 * Advances the iterator to the next feature in the current layer.
370 *
371 * Returns: %TRUE if there is a next feature, %FALSE otherwise.
372 *
373 * Since: 1.2
374 */
375 gboolean
376 62 shumate_vector_reader_iter_next_feature (ShumateVectorReaderIter *self)
377 {
378 62 int next_index = 0;
379
380
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 62 times.
62 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), FALSE);
381
382
1/2
✓ Branch 5 → 6 taken 62 times.
✗ Branch 5 → 7 not taken.
62 g_return_val_if_fail (self->layer != NULL, FALSE);
383
384
2/2
✓ Branch 6 → 8 taken 48 times.
✓ Branch 6 → 9 taken 14 times.
62 if (self->feature == NULL)
385 next_index = 0;
386 else
387 48 next_index = self->feature_index + 1;
388
389
2/2
✓ Branch 9 → 10 taken 46 times.
✓ Branch 9 → 11 taken 16 times.
62 if (next_index < self->layer->n_features)
390 {
391 46 self->feature = self->layer->features[next_index];
392 46 self->feature_index = next_index;
393 46 return TRUE;
394 }
395 else
396 return FALSE;
397 }
398
399 /*< private >
400 * shumate_vector_reader_iter_get_feature_index:
401 * @self: A [class@VectorReaderIter].
402 *
403 * Gets the index of the current feature.
404 *
405 * Returns: The index of the current feature.
406 *
407 * Since: 1.3
408 */
409 int
410 ✗ shumate_vector_reader_iter_get_feature_index (ShumateVectorReaderIter *self)
411 {
412 ✗ g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), 0);
413
414 ✗ return self->feature_index;
415 }
416
417 /**
418 * shumate_vector_reader_iter_get_feature_id:
419 * @self: A [class@VectorReaderIter].
420 *
421 * Gets the ID of the current feature.
422 *
423 * Returns: The ID of the current feature.
424 *
425 * Since: 1.2
426 */
427 guint64
428 2 shumate_vector_reader_iter_get_feature_id (ShumateVectorReaderIter *self)
429 {
430
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
2 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), 0);
431
432
1/2
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 7 not taken.
2 g_return_val_if_fail (self->feature != NULL, 0);
433 2 return self->feature->id;
434 }
435
436 /**
437 * shumate_vector_reader_iter_get_feature_tag:
438 * @self: A [class@VectorReaderIter].
439 * @key: The key of the tag to get.
440 * @value: (out): The value of the tag.
441 *
442 * Gets the value of the tag with the given key.
443 *
444 * Returns: %TRUE if the tag was found, %FALSE otherwise.
445 *
446 * Since: 1.2
447 */
448 gboolean
449 2 shumate_vector_reader_iter_get_feature_tag (ShumateVectorReaderIter *self,
450 const char *key,
451 GValue *value)
452 {
453
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
2 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), FALSE);
454
455
1/2
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 7 not taken.
2 g_return_val_if_fail (self->feature != NULL, FALSE);
456
457
1/2
✓ Branch 6 → 32 taken 2 times.
✗ Branch 6 → 33 not taken.
2 if (key == NULL)
458 return FALSE;
459
460
1/2
✓ Branch 32 → 8 taken 2 times.
✗ Branch 32 → 33 not taken.
2 for (int i = 0; i + 1 < self->feature->n_tags; i += 2)
461 {
462
1/2
✓ Branch 8 → 9 taken 2 times.
✗ Branch 8 → 31 not taken.
2 if (strcmp (self->layer->keys[self->feature->tags[i]], key) == 0)
463 {
464 2 VectorTile__Tile__Value *v = self->layer->values[self->feature->tags[i + 1]];
465
466
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 12 taken 2 times.
2 if (v->has_int_value)
467 {
468 ✗ g_value_init (value, G_TYPE_INT64);
469 ✗ g_value_set_int64 (value, v->int_value);
470 }
471
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 15 taken 2 times.
2 else if (v->has_uint_value)
472 {
473 ✗ g_value_init (value, G_TYPE_UINT64);
474 ✗ g_value_set_uint64 (value, v->uint_value);
475 }
476
1/2
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 18 taken 2 times.
2 else if (v->has_sint_value)
477 {
478 ✗ g_value_init (value, G_TYPE_INT64);
479 ✗ g_value_set_int64 (value, v->sint_value);
480 }
481
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 21 taken 2 times.
2 else if (v->has_float_value)
482 {
483 ✗ g_value_init (value, G_TYPE_FLOAT);
484 ✗ g_value_set_float (value, v->float_value);
485 }
486
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 24 taken 2 times.
2 else if (v->has_double_value)
487 {
488 ✗ g_value_init (value, G_TYPE_DOUBLE);
489 ✗ g_value_set_double (value, v->double_value);
490 }
491
1/2
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 27 taken 2 times.
2 else if (v->has_bool_value)
492 {
493 ✗ g_value_init (value, G_TYPE_BOOLEAN);
494 ✗ g_value_set_boolean (value, v->bool_value);
495 }
496
1/2
✓ Branch 27 → 28 taken 2 times.
✗ Branch 27 → 30 not taken.
2 else if (v->string_value != NULL)
497 {
498 2 g_value_init (value, G_TYPE_STRING);
499 2 g_value_set_string (value, v->string_value);
500 }
501 else
502 ✗ g_value_unset (value);
503
504 return TRUE;
505 }
506 }
507
508 return FALSE;
509 }
510
511 /**
512 * shumate_vector_reader_iter_get_feature_keys:
513 * @self: A [class@VectorReaderIter].
514 *
515 * Gets the keys of the tags of the current feature.
516 *
517 * Returns: (transfer container) (array zero-terminated=1): The keys of the tags of the current feature.
518 *
519 * Since: 1.2
520 */
521 const char **
522 2 shumate_vector_reader_iter_get_feature_keys (ShumateVectorReaderIter *self)
523 {
524
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
2 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), NULL);
525
526 2 const char **keys;
527 2 uint32_t n_keys;
528
529
1/2
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 7 not taken.
2 g_return_val_if_fail (self->feature != NULL, NULL);
530
531 2 n_keys = self->feature->n_tags / 2;
532
1/2
✗ Branch 6 → 8 not taken.
✓ Branch 6 → 9 taken 2 times.
2 keys = g_new (const char *, n_keys + 1);
533
2/2
✓ Branch 12 → 11 taken 2 times.
✓ Branch 12 → 13 taken 2 times.
4 for (int i = 0; i < n_keys; i ++)
534 2 keys[i] = self->layer->keys[self->feature->tags[i * 2]];
535 2 keys[n_keys] = NULL;
536
537 2 return keys;
538 }
539
540 /**
541 * shumate_vector_reader_iter_get_feature_geometry_type:
542 * @self: A [class@VectorReaderIter].
543 *
544 * Gets the geometry type of the current feature.
545 *
546 * Returns: The geometry type of the current feature.
547 *
548 * Since: 1.2
549 */
550 ShumateGeometryType
551 16 shumate_vector_reader_iter_get_feature_geometry_type (ShumateVectorReaderIter *self)
552 {
553 /* MVT doesn't distinguish between multi geometries and single geometries, but
554 we do, so this function contains a bunch of extra logic to determine whether
555 the geometry data contains multiple geometries. */
556
557
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 16 times.
16 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), SHUMATE_GEOMETRY_TYPE_UNKNOWN);
558
559
1/2
✓ Branch 5 → 6 taken 16 times.
✗ Branch 5 → 7 not taken.
16 g_return_val_if_fail (self->feature != NULL, SHUMATE_GEOMETRY_TYPE_UNKNOWN);
560
561
3/4
✓ Branch 6 → 8 taken 8 times.
✓ Branch 6 → 10 taken 4 times.
✓ Branch 6 → 17 taken 4 times.
✗ Branch 6 → 29 not taken.
16 switch (self->feature->type)
562 {
563 case VECTOR_TILE__TILE__GEOM_TYPE__UNKNOWN:
564 return SHUMATE_GEOMETRY_TYPE_UNKNOWN;
565
566 8 case VECTOR_TILE__TILE__GEOM_TYPE__POINT:
567
2/2
✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 29 taken 6 times.
8 if (self->feature->n_geometry == 3)
568 return SHUMATE_GEOMETRY_TYPE_POINT;
569 else
570 2 return SHUMATE_GEOMETRY_TYPE_MULTIPOINT;
571
572 4 case VECTOR_TILE__TILE__GEOM_TYPE__LINESTRING:
573 {
574 4 ShumateVectorGeometryIter iter = { .feature = self->feature };
575 4 int move_tos = 0;
576
2/2
✓ Branch 15 → 11 taken 14 times.
✓ Branch 15 → 16 taken 2 times.
16 while (shumate_vector_geometry_iter (&iter))
577 {
578
2/2
✓ Branch 11 → 12 taken 6 times.
✓ Branch 11 → 13 taken 8 times.
14 if (iter.op == 1)
579 {
580 6 move_tos ++;
581
2/2
✓ Branch 12 → 13 taken 4 times.
✓ Branch 12 → 16 taken 2 times.
6 if (move_tos > 1)
582 return SHUMATE_GEOMETRY_TYPE_MULTILINESTRING;
583 }
584 }
585 return SHUMATE_GEOMETRY_TYPE_LINESTRING;
586 }
587
588 4 case VECTOR_TILE__TILE__GEOM_TYPE__POLYGON:
589 {
590 /* Use the shoelace formula to determine whether each ring is
591 exterior or interior. A single polygon can have interior rings
592 in addition to its exterior ring; a multipolygon has multiple
593 exterior rings. */
594
595 4 ShumateVectorGeometryIter iter = { .feature = self->feature };
596 4 int prev_x = 0, prev_y = 0;
597 4 double area = 0;
598 4 int exterior_rings = 0;
599
600
2/2
✓ Branch 27 → 18 taken 50 times.
✓ Branch 27 → 28 taken 2 times.
52 while (shumate_vector_geometry_iter (&iter))
601 {
602 /* See https://en.wikipedia.org/wiki/Shoelace_formula#Triangle_formula */
603 50 if (iter.op == SHUMATE_VECTOR_GEOMETRY_OP_LINE_TO
604
2/2
✓ Branch 18 → 19 taken 40 times.
✓ Branch 18 → 20 taken 10 times.
50 || iter.op == SHUMATE_VECTOR_GEOMETRY_OP_CLOSE_PATH)
605 40 area += (double) prev_x * iter.y - (double) iter.x * prev_y;
606
607
2/2
✓ Branch 20 → 21 taken 10 times.
✓ Branch 20 → 25 taken 40 times.
50 if (iter.op == SHUMATE_VECTOR_GEOMETRY_OP_CLOSE_PATH)
608 {
609
2/2
✓ Branch 21 → 22 taken 6 times.
✓ Branch 21 → 23 taken 4 times.
10 if (area > 0)
610 6 exterior_rings ++;
611
612
2/2
✓ Branch 23 → 24 taken 2 times.
✓ Branch 23 → 25 taken 8 times.
10 if (exterior_rings > 1)
613 2 return SHUMATE_GEOMETRY_TYPE_MULTIPOLYGON;
614
615 area = 0;
616 }
617
618 48 prev_x = iter.x;
619 48 prev_y = iter.y;
620 }
621 }
622 2 return SHUMATE_GEOMETRY_TYPE_POLYGON;
623
624 default:
625 return SHUMATE_GEOMETRY_TYPE_UNKNOWN;
626 }
627 }
628
629 static int
630 4 zigzag (guint value)
631 {
632 4 return (value >> 1) ^ (-(value & 1));
633 }
634
635 /**
636 * shumate_vector_reader_iter_get_feature_point:
637 * @self: A [class@VectorReaderIter].
638 * @x: (out) (optional): The x coordinate of the point.
639 * @y: (out) (optional): The y coordinate of the point.
640 *
641 * Gets the coordinates of the current feature in tile space, if the
642 * feature is a single point.
643 *
644 * See [method@VectorReaderIter.get_layer_extent] to get the range
645 * of the coordinates.
646 *
647 * It is an error to call this function if the feature is not a single point.
648 * Use [method@VectorReaderIter.get_feature_geometry_type] to check
649 * the feature's geometry type.
650 *
651 * Returns: %TRUE if the feature is a point, %FALSE otherwise.
652 *
653 * Since: 1.2
654 */
655 gboolean
656 2 shumate_vector_reader_iter_get_feature_point (ShumateVectorReaderIter *self,
657 double *x,
658 double *y)
659 {
660
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
2 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), FALSE);
661
662
1/2
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 7 not taken.
2 g_return_val_if_fail (self->feature != NULL, FALSE);
663
1/2
✓ Branch 6 → 8 taken 2 times.
✗ Branch 6 → 9 not taken.
2 g_return_val_if_fail (self->feature->type == VECTOR_TILE__TILE__GEOM_TYPE__POINT, FALSE);
664
1/2
✓ Branch 8 → 10 taken 2 times.
✗ Branch 8 → 11 not taken.
2 g_return_val_if_fail (self->feature->n_geometry == 3, FALSE);
665
666
1/2
✓ Branch 10 → 12 taken 2 times.
✗ Branch 10 → 13 not taken.
2 if (x != NULL)
667 2 *x = zigzag (self->feature->geometry[1]);
668
1/2
✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 15 not taken.
2 if (y != NULL)
669 2 *y = zigzag (self->feature->geometry[2]);
670
671 return TRUE;
672 }
673
674 /**
675 * shumate_vector_reader_iter_feature_contains_point:
676 * @self: A [class@VectorReaderIter].
677 *
678 * Determines whether the current feature contains the given point.
679 *
680 * The point must be specified in tile space. See
681 * [method@VectorReaderIter.get_layer_extent] to get the range of the
682 * coordinates.
683 *
684 * Only polygon or multipolygon features can contain a point. For all
685 * other feature types, this function returns %FALSE.
686 *
687 * If the point is on the border of the polygon, this function may return
688 * either %TRUE or %FALSE.
689 *
690 * Returns: %TRUE if the feature contains the point, %FALSE otherwise.
691 *
692 * Since: 1.2
693 */
694 gboolean
695 32 shumate_vector_reader_iter_feature_contains_point (ShumateVectorReaderIter *self,
696 double x,
697 double y)
698 {
699
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 32 times.
32 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), FALSE);
700
701 32 ShumateVectorGeometryIter iter = {0};
702 32 int prev_x = 0, prev_y = 0;
703 32 int winding_number = 0;
704
705
1/2
✓ Branch 5 → 6 taken 32 times.
✗ Branch 5 → 7 not taken.
32 g_return_val_if_fail (self->feature != NULL, FALSE);
706
707
1/2
✓ Branch 6 → 8 taken 32 times.
✗ Branch 6 → 22 not taken.
32 if (self->feature->type != VECTOR_TILE__TILE__GEOM_TYPE__POLYGON)
708 return FALSE;
709
710 32 iter.feature = self->feature;
711
712 /* See <https://web.archive.org/web/20130126163405/http://geomalgorithms.com/a03-_inclusion.html>.
713 I chose the winding algorithm because it has fewer edge cases. */
714
715
2/2
✓ Branch 20 → 9 taken 450 times.
✓ Branch 20 → 21 taken 32 times.
482 while (shumate_vector_geometry_iter (&iter))
716 {
717
2/2
✓ Branch 9 → 10 taken 360 times.
✓ Branch 9 → 18 taken 90 times.
450 switch (iter.op)
718 {
719 case SHUMATE_VECTOR_GEOMETRY_OP_MOVE_TO:
720 break;
721 360 case SHUMATE_VECTOR_GEOMETRY_OP_LINE_TO:
722 case SHUMATE_VECTOR_GEOMETRY_OP_CLOSE_PATH:
723
4/4
✓ Branch 10 → 11 taken 132 times.
✓ Branch 10 → 14 taken 228 times.
✓ Branch 11 → 12 taken 54 times.
✓ Branch 11 → 14 taken 78 times.
360 if (prev_y <= y && iter.y > y)
724 {
725
2/2
✓ Branch 12 → 13 taken 22 times.
✓ Branch 12 → 18 taken 32 times.
54 if ((iter.x - prev_x) * (y - prev_y) > (iter.y - prev_y) * (x - prev_x))
726 22 winding_number ++;
727 }
728
4/4
✓ Branch 14 → 15 taken 228 times.
✓ Branch 14 → 18 taken 78 times.
✓ Branch 15 → 16 taken 54 times.
✓ Branch 15 → 18 taken 174 times.
306 else if (prev_y > y && iter.y <= y)
729 {
730
2/2
✓ Branch 16 → 17 taken 10 times.
✓ Branch 16 → 18 taken 44 times.
54 if ((iter.x - prev_x) * (y - prev_y) < (iter.y - prev_y) * (x - prev_x))
731 10 winding_number --;
732 }
733 break;
734 }
735
736 450 prev_x = iter.x;
737 450 prev_y = iter.y;
738 }
739
740 32 return winding_number != 0;
741 }
742
743 /*< private >
744 * shumate_vector_reader_iter_new:
745 * @reader: A [class@VectorReader]
746 *
747 * Creates a new [class@VectorReaderIter] for @reader.
748 *
749 * The public API is [method@VectorReader.iterate].
750 *
751 * Returns: (transfer full): A new [class@VectorReaderIter]
752 */
753 ShumateVectorReaderIter *
754 26 shumate_vector_reader_iter_new (ShumateVectorReader *reader)
755 {
756
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 26 times.
26 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER (reader), NULL);
757
758 26 return g_object_new (SHUMATE_TYPE_VECTOR_READER_ITER,
759 "reader", reader,
760 NULL);
761 }
762
763 /*< private >
764 * shumate_vector_reader_iter_get_layer_struct:
765 * @self: A [class@VectorReaderIter]
766 *
767 * Gets the raw protobuf struct for the current layer.
768 *
769 * Returns: (transfer none): a [struct@VectorTile__Tile__Layer]
770 */
771 VectorTile__Tile__Layer *
772 104 shumate_vector_reader_iter_get_layer_struct (ShumateVectorReaderIter *self)
773 {
774
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 104 times.
104 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), NULL);
775 104 return self->layer;
776 }
777
778 /*< private >
779 * shumate_vector_reader_iter_get_feature_struct:
780 *
781 * Gets the raw protobuf struct for the current feature.
782 *
783 * Returns: (transfer none): a [struct@VectorTile__Tile__Feature]
784 */
785 VectorTile__Tile__Feature *
786 120 shumate_vector_reader_iter_get_feature_struct (ShumateVectorReaderIter *self)
787 {
788
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 120 times.
120 g_return_val_if_fail (SHUMATE_IS_VECTOR_READER_ITER (self), NULL);
789 120 return self->feature;
790 }
791