shumate/shumate-raster-renderer.c
| Line | Branch | Exec | Source |
|---|---|---|---|
| 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 | |||
| 19 | #include "shumate-data-source.h" | ||
| 20 | #include "shumate-raster-renderer.h" | ||
| 21 | #include "shumate-tile-downloader.h" | ||
| 22 | |||
| 23 | |||
| 24 | struct _ShumateRasterRenderer | ||
| 25 | { | ||
| 26 | ShumateMapSource parent_instance; | ||
| 27 | |||
| 28 | ShumateDataSource *data_source; | ||
| 29 | }; | ||
| 30 | |||
| 31 |
4/6shumate_raster_renderer_class_intern_init:
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 3 times.
shumate_raster_renderer_get_type:
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 7 taken 16 times.
✓ Branch 4 → 5 taken 3 times.
✗ Branch 4 → 7 not taken.
|
25 | G_DEFINE_TYPE (ShumateRasterRenderer, shumate_raster_renderer, SHUMATE_TYPE_MAP_SOURCE) |
| 32 | |||
| 33 | enum { | ||
| 34 | PROP_0, | ||
| 35 | PROP_DATA_SOURCE, | ||
| 36 | N_PROPS | ||
| 37 | }; | ||
| 38 | |||
| 39 | static GParamSpec *properties [N_PROPS]; | ||
| 40 | |||
| 41 | |||
| 42 | /** | ||
| 43 | * shumate_raster_renderer_new: | ||
| 44 | * @data_source: a [class@DataSource] to provide tile image data | ||
| 45 | * | ||
| 46 | * Creates a new [class@RasterRenderer] that uses the given data source. | ||
| 47 | * | ||
| 48 | * Returns: (transfer full): a newly constructed [class@RasterRenderer] | ||
| 49 | */ | ||
| 50 | ShumateRasterRenderer * | ||
| 51 | ✗ | shumate_raster_renderer_new (ShumateDataSource *data_source) | |
| 52 | { | ||
| 53 | ✗ | g_return_val_if_fail (SHUMATE_IS_DATA_SOURCE (data_source), NULL); | |
| 54 | ✗ | return g_object_new (SHUMATE_TYPE_RASTER_RENDERER, | |
| 55 | "data-source", data_source, | ||
| 56 | NULL); | ||
| 57 | } | ||
| 58 | |||
| 59 | /** | ||
| 60 | * shumate_raster_renderer_new_from_url: | ||
| 61 | * @url_template: a URL template to fetch tiles from | ||
| 62 | * | ||
| 63 | * Creates a new [class@RasterRenderer] that fetches tiles from the given URL | ||
| 64 | * using a [class@TileDownloader] data source. | ||
| 65 | * | ||
| 66 | * Equivalent to: | ||
| 67 | * | ||
| 68 | * ```c | ||
| 69 | * g_autoptr(ShumateTileDownloader) source = shumate_tile_downloader_new (url_template); | ||
| 70 | * ShumateRasterRenderer *renderer = shumate_raster_renderer_new (source); | ||
| 71 | * ``` | ||
| 72 | * | ||
| 73 | * Returns: (transfer full): a newly constructed [class@RasterRenderer] | ||
| 74 | */ | ||
| 75 | ShumateRasterRenderer * | ||
| 76 | ✗ | shumate_raster_renderer_new_from_url (const char *url_template) | |
| 77 | { | ||
| 78 | ✗ | g_autoptr(ShumateDataSource) data_source = NULL; | |
| 79 | |||
| 80 | ✗ | g_return_val_if_fail (url_template != NULL, NULL); | |
| 81 | |||
| 82 | ✗ | data_source = SHUMATE_DATA_SOURCE (shumate_tile_downloader_new (url_template)); | |
| 83 | ✗ | return shumate_raster_renderer_new (data_source); | |
| 84 | } | ||
| 85 | |||
| 86 | |||
| 87 | /** | ||
| 88 | * shumate_raster_renderer_new_full: | ||
| 89 | * @id: the map source's id | ||
| 90 | * @name: the map source's name | ||
| 91 | * @license: the map source's license | ||
| 92 | * @license_uri: the map source's license URI | ||
| 93 | * @min_zoom: the map source's minimum zoom level | ||
| 94 | * @max_zoom: the map source's maximum zoom level | ||
| 95 | * @tile_size: the map source's tile size (in pixels) | ||
| 96 | * @projection: the map source's projection | ||
| 97 | * @data_source: a [class@DataSource] to provide tile image data | ||
| 98 | * | ||
| 99 | * Creates a new [class@RasterRenderer] with the given details and a data | ||
| 100 | * source. | ||
| 101 | * | ||
| 102 | * Returns: a newly constructed [class@RasterRenderer] | ||
| 103 | */ | ||
| 104 | ShumateRasterRenderer * | ||
| 105 | ✗ | shumate_raster_renderer_new_full (const char *id, | |
| 106 | const char *name, | ||
| 107 | const char *license, | ||
| 108 | const char *license_uri, | ||
| 109 | guint min_zoom, | ||
| 110 | guint max_zoom, | ||
| 111 | guint tile_size, | ||
| 112 | ShumateMapProjection projection, | ||
| 113 | ShumateDataSource *data_source) | ||
| 114 | { | ||
| 115 | ✗ | g_return_val_if_fail (SHUMATE_IS_DATA_SOURCE (data_source), NULL); | |
| 116 | |||
| 117 | ✗ | return g_object_new (SHUMATE_TYPE_RASTER_RENDERER, | |
| 118 | "id", id, | ||
| 119 | "name", name, | ||
| 120 | "license", license, | ||
| 121 | "license-uri", license_uri, | ||
| 122 | "min-zoom-level", min_zoom, | ||
| 123 | "max-zoom-level", max_zoom, | ||
| 124 | "tile-size", tile_size, | ||
| 125 | "projection", projection, | ||
| 126 | "data-source", data_source, | ||
| 127 | NULL); | ||
| 128 | } | ||
| 129 | |||
| 130 | |||
| 131 | /** | ||
| 132 | * shumate_raster_renderer_new_full_from_url: | ||
| 133 | * @id: the map source's id | ||
| 134 | * @name: the map source's name | ||
| 135 | * @license: the map source's license | ||
| 136 | * @license_uri: the map source's license URI | ||
| 137 | * @min_zoom: the map source's minimum zoom level | ||
| 138 | * @max_zoom: the map source's maximum zoom level | ||
| 139 | * @tile_size: the map source's tile size (in pixels) | ||
| 140 | * @projection: the map source's projection | ||
| 141 | * @url_template: a URL template to fetch tiles from | ||
| 142 | * | ||
| 143 | * Creates a new [class@RasterRenderer] with the given details and a data | ||
| 144 | * source. | ||
| 145 | * | ||
| 146 | * Returns: a newly constructed [class@RasterRenderer] | ||
| 147 | */ | ||
| 148 | ShumateRasterRenderer * | ||
| 149 | 18 | shumate_raster_renderer_new_full_from_url (const char *id, | |
| 150 | const char *name, | ||
| 151 | const char *license, | ||
| 152 | const char *license_uri, | ||
| 153 | guint min_zoom, | ||
| 154 | guint max_zoom, | ||
| 155 | guint tile_size, | ||
| 156 | ShumateMapProjection projection, | ||
| 157 | const char *url_template) | ||
| 158 | { | ||
| 159 | 36 | g_autoptr(ShumateTileDownloader) data_source = NULL; | |
| 160 | |||
| 161 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 18 times.
|
18 | g_return_val_if_fail (url_template != NULL, NULL); |
| 162 | |||
| 163 | 18 | data_source = shumate_tile_downloader_new (url_template); | |
| 164 | |||
| 165 | 18 | return g_object_new (SHUMATE_TYPE_RASTER_RENDERER, | |
| 166 | "id", id, | ||
| 167 | "name", name, | ||
| 168 | "license", license, | ||
| 169 | "license-uri", license_uri, | ||
| 170 | "min-zoom-level", min_zoom, | ||
| 171 | "max-zoom-level", max_zoom, | ||
| 172 | "tile-size", tile_size, | ||
| 173 | "projection", projection, | ||
| 174 | "data-source", data_source, | ||
| 175 | NULL); | ||
| 176 | } | ||
| 177 | |||
| 178 | static void | ||
| 179 | 18 | shumate_raster_renderer_finalize (GObject *object) | |
| 180 | { | ||
| 181 | 18 | ShumateRasterRenderer *self = (ShumateRasterRenderer *)object; | |
| 182 | |||
| 183 |
1/2✓ Branch 2 → 3 taken 18 times.
✗ Branch 2 → 4 not taken.
|
18 | g_clear_object (&self->data_source); |
| 184 | |||
| 185 | 18 | G_OBJECT_CLASS (shumate_raster_renderer_parent_class)->finalize (object); | |
| 186 | 18 | } | |
| 187 | |||
| 188 | static void | ||
| 189 | ✗ | shumate_raster_renderer_get_property (GObject *object, | |
| 190 | guint prop_id, | ||
| 191 | GValue *value, | ||
| 192 | GParamSpec *pspec) | ||
| 193 | { | ||
| 194 | ✗ | ShumateRasterRenderer *self = SHUMATE_RASTER_RENDERER (object); | |
| 195 | |||
| 196 | ✗ | switch (prop_id) | |
| 197 | { | ||
| 198 | ✗ | case PROP_DATA_SOURCE: | |
| 199 | ✗ | g_value_set_object (value, self->data_source); | |
| 200 | ✗ | break; | |
| 201 | ✗ | default: | |
| 202 | ✗ | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
| 203 | } | ||
| 204 | ✗ | } | |
| 205 | |||
| 206 | static void | ||
| 207 | 18 | shumate_raster_renderer_set_property (GObject *object, | |
| 208 | guint prop_id, | ||
| 209 | const GValue *value, | ||
| 210 | GParamSpec *pspec) | ||
| 211 | { | ||
| 212 | 18 | ShumateRasterRenderer *self = SHUMATE_RASTER_RENDERER (object); | |
| 213 | |||
| 214 |
1/2✓ Branch 2 → 3 taken 18 times.
✗ Branch 2 → 6 not taken.
|
18 | switch (prop_id) |
| 215 | { | ||
| 216 | 18 | case PROP_DATA_SOURCE: | |
| 217 | 18 | g_set_object (&self->data_source, g_value_get_object (value)); | |
| 218 | 18 | break; | |
| 219 | ✗ | default: | |
| 220 | ✗ | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
| 221 | } | ||
| 222 | 18 | } | |
| 223 | |||
| 224 | static void shumate_raster_renderer_fill_tile_async (ShumateMapSource *map_source, | ||
| 225 | ShumateTile *tile, | ||
| 226 | GCancellable *cancellable, | ||
| 227 | GAsyncReadyCallback callback, | ||
| 228 | gpointer user_data); | ||
| 229 | |||
| 230 | static gboolean shumate_raster_renderer_fill_tile_finish (ShumateMapSource *map_source, | ||
| 231 | GAsyncResult *result, | ||
| 232 | GError **error); | ||
| 233 | |||
| 234 | static void | ||
| 235 | 3 | shumate_raster_renderer_class_init (ShumateRasterRendererClass *klass) | |
| 236 | { | ||
| 237 | 3 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
| 238 | 3 | ShumateMapSourceClass *map_source_class = SHUMATE_MAP_SOURCE_CLASS (klass); | |
| 239 | |||
| 240 | 3 | object_class->finalize = shumate_raster_renderer_finalize; | |
| 241 | 3 | object_class->get_property = shumate_raster_renderer_get_property; | |
| 242 | 3 | object_class->set_property = shumate_raster_renderer_set_property; | |
| 243 | |||
| 244 | 3 | map_source_class->fill_tile_async = shumate_raster_renderer_fill_tile_async; | |
| 245 | 3 | map_source_class->fill_tile_finish = shumate_raster_renderer_fill_tile_finish; | |
| 246 | |||
| 247 | /** | ||
| 248 | * ShumateRasterRenderer:data-source: | ||
| 249 | * | ||
| 250 | * The data source that provides image tiles to display. In most cases, | ||
| 251 | * a [class@TileDownloader] is sufficient. | ||
| 252 | */ | ||
| 253 | 6 | properties[PROP_DATA_SOURCE] = | |
| 254 | 3 | g_param_spec_object ("data-source", NULL, NULL, | |
| 255 | SHUMATE_TYPE_DATA_SOURCE, | ||
| 256 | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); | ||
| 257 | |||
| 258 | 3 | g_object_class_install_properties (object_class, N_PROPS, properties); | |
| 259 | 3 | } | |
| 260 | |||
| 261 | static void | ||
| 262 | 18 | shumate_raster_renderer_init (ShumateRasterRenderer *self) | |
| 263 | { | ||
| 264 | 18 | } | |
| 265 | |||
| 266 | |||
| 267 | static void | ||
| 268 | ✗ | on_request_notify (ShumateDataSourceRequest *req, | |
| 269 | GParamSpec *pspec, | ||
| 270 | gpointer user_data) | ||
| 271 | { | ||
| 272 | ✗ | GTask *task = user_data; | |
| 273 | ✗ | GBytes *data; | |
| 274 | ✗ | ShumateTile *tile = g_task_get_task_data (task); | |
| 275 | |||
| 276 | ✗ | if ((data = shumate_data_source_request_get_data (req))) | |
| 277 | { | ||
| 278 | ✗ | g_autoptr(GError) error = NULL; | |
| 279 | ✗ | g_autoptr(GdkPixbuf) pixbuf = NULL; | |
| 280 | ✗ | g_autoptr(GInputStream) stream = NULL; | |
| 281 | ✗ | g_autoptr(GdkTexture) texture = NULL; | |
| 282 | |||
| 283 | ✗ | stream = g_memory_input_stream_new_from_bytes (data); | |
| 284 | ✗ | pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, &error); | |
| 285 | ✗ | if (error) | |
| 286 | { | ||
| 287 | ✗ | g_warning ("Failed to create texture from tile data (%d, %d @ %d): %s", | |
| 288 | shumate_tile_get_x (tile), | ||
| 289 | shumate_tile_get_y (tile), | ||
| 290 | shumate_tile_get_zoom_level (tile), | ||
| 291 | error->message); | ||
| 292 | ✗ | return; | |
| 293 | } | ||
| 294 | |||
| 295 | ✗ | texture = gdk_texture_new_for_pixbuf (pixbuf); | |
| 296 | ✗ | shumate_tile_set_paintable (tile, GDK_PAINTABLE (texture)); | |
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | static void | ||
| 301 | ✗ | on_request_notify_completed (ShumateDataSourceRequest *req, | |
| 302 | GParamSpec *pspec, | ||
| 303 | gpointer user_data) | ||
| 304 | { | ||
| 305 | ✗ | g_autoptr(GTask) task = user_data; | |
| 306 | ✗ | GError *error; | |
| 307 | ✗ | ShumateTile *tile = g_task_get_task_data (task); | |
| 308 | |||
| 309 | ✗ | shumate_tile_set_state (tile, SHUMATE_STATE_DONE); | |
| 310 | |||
| 311 | ✗ | if ((error = shumate_data_source_request_get_error (req))) | |
| 312 | ✗ | g_task_return_error (task, g_error_copy (error)); | |
| 313 | else | ||
| 314 | ✗ | g_task_return_boolean (task, TRUE); | |
| 315 | ✗ | } | |
| 316 | |||
| 317 | static void | ||
| 318 | ✗ | shumate_raster_renderer_fill_tile_async (ShumateMapSource *map_source, | |
| 319 | ShumateTile *tile, | ||
| 320 | GCancellable *cancellable, | ||
| 321 | GAsyncReadyCallback callback, | ||
| 322 | gpointer user_data) | ||
| 323 | { | ||
| 324 | ✗ | ShumateRasterRenderer *self = (ShumateRasterRenderer *)map_source; | |
| 325 | ✗ | g_autoptr(GTask) task = NULL; | |
| 326 | ✗ | g_autoptr(ShumateDataSourceRequest) req = NULL; | |
| 327 | |||
| 328 | ✗ | g_return_if_fail (SHUMATE_IS_RASTER_RENDERER (self)); | |
| 329 | ✗ | g_return_if_fail (SHUMATE_IS_TILE (tile)); | |
| 330 | ✗ | g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); | |
| 331 | |||
| 332 | ✗ | task = g_task_new (self, cancellable, callback, user_data); | |
| 333 | ✗ | g_task_set_source_tag (task, shumate_raster_renderer_fill_tile_async); | |
| 334 | |||
| 335 | ✗ | g_task_set_task_data (task, g_object_ref (tile), (GDestroyNotify)g_object_unref); | |
| 336 | |||
| 337 | ✗ | req = shumate_data_source_start_request (self->data_source, | |
| 338 | ✗ | shumate_tile_get_x (tile), | |
| 339 | ✗ | shumate_tile_get_y (tile), | |
| 340 | ✗ | shumate_tile_get_zoom_level (tile), | |
| 341 | cancellable); | ||
| 342 | |||
| 343 | ✗ | if (shumate_data_source_request_is_completed (req)) | |
| 344 | { | ||
| 345 | ✗ | on_request_notify (req, NULL, task); | |
| 346 | ✗ | on_request_notify_completed (req, NULL, g_steal_pointer (&task)); | |
| 347 | } | ||
| 348 | else | ||
| 349 | { | ||
| 350 | ✗ | g_signal_connect_object (req, "notify::data", (GCallback)on_request_notify, task, 0); | |
| 351 | ✗ | g_signal_connect_object (req, "notify::completed", (GCallback)on_request_notify_completed, g_object_ref (task), 0); | |
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | static gboolean | ||
| 356 | ✗ | shumate_raster_renderer_fill_tile_finish (ShumateMapSource *map_source, | |
| 357 | GAsyncResult *result, | ||
| 358 | GError **error) | ||
| 359 | { | ||
| 360 | ✗ | ShumateRasterRenderer *self = (ShumateRasterRenderer *)map_source; | |
| 361 | |||
| 362 | ✗ | g_return_val_if_fail (SHUMATE_IS_RASTER_RENDERER (self), FALSE); | |
| 363 | ✗ | g_return_val_if_fail (g_task_is_valid (result, self), FALSE); | |
| 364 | |||
| 365 | ✗ | return g_task_propagate_boolean (G_TASK (result), error); | |
| 366 | } | ||
| 367 |