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/6shumate_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 |