1 /*
2 * Copyright (C) 2010-2013 Jiri Techet <techet@gmail.com>
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, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 /**
20 * SECTION:champlain-map-source
21 * @short_description: A base class for map sources
22 *
23 * #ChamplainTile objects come from map sources which are represented by
24 * #ChamplainMapSource. This is should be considered an abstract
25 * type as it does nothing of interest.
26 *
27 * When loading new tiles, #ChamplainView calls champlain_map_source_fill_tile()
28 * on the current #ChamplainMapSource passing it a #ChamplainTile to be filled
29 * with the image.
30 *
31 * Apart from being a base class of all map sources, #ChamplainMapSource
32 * also supports cooperation of multiple map sources by arranging them into
33 * chains. Every map source has the #ChamplainMapSource:next-source property
34 * that determines the next map source in the chain. When a function of
35 * a #ChamplainMapSource object is invoked, the map source may decide to
36 * delegate the work to the next map source in the chain by invoking the
37 * same function on it.
38
39 * To understand the concept of chains, consider for instance a chain
40 * consisting of #ChamplainFileCache whose next source is
41 * #ChamplainNetworkTileSource whose next source is an error tile source
42 * created with champlain_map_source_factory_create_error_source ().
43 * When champlain_map_source_fill_tile() is called on the first object of the
44 * chain, #ChamplainFileCache, the cache checks whether it contains the
45 * requested tile in its database. If it does, it returns the tile; otherwise,
46 * it calls champlain_map_source_fill_tile() on the next source in the chain
47 * (#ChamplainNetworkTileSource). The network tile source loads the tile
48 * from the network. When successful, it returns the tile; otherwise it requests
49 * the tile from the next source in the chain (error tile source).
50 * The error tile source always generates an error tile, no matter what
51 * its next source is.
52 */
53
54 #include "champlain-map-source.h"
55
56 #include <math.h>
57
58 struct _ChamplainMapSourcePrivate
59 {
60 ChamplainMapSource *next_source;
61 ChamplainRenderer *renderer;
62 };
63
64 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ChamplainMapSource, champlain_map_source, G_TYPE_INITIALLY_UNOWNED)
65
66 enum
67 {
68 PROP_0,
69 PROP_NEXT_SOURCE,
70 PROP_RENDERER,
71 };
72
73 static void
champlain_map_source_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)74 champlain_map_source_get_property (GObject *object,
75 guint prop_id,
76 GValue *value,
77 GParamSpec *pspec)
78 {
79 ChamplainMapSourcePrivate *priv = CHAMPLAIN_MAP_SOURCE (object)->priv;
80
81 switch (prop_id)
82 {
83 case PROP_NEXT_SOURCE:
84 g_value_set_object (value, priv->next_source);
85 break;
86
87 case PROP_RENDERER:
88 g_value_set_object (value, priv->renderer);
89 break;
90
91 default:
92 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
93 }
94 }
95
96
97 static void
champlain_map_source_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)98 champlain_map_source_set_property (GObject *object,
99 guint prop_id,
100 const GValue *value,
101 GParamSpec *pspec)
102 {
103 ChamplainMapSource *map_source = CHAMPLAIN_MAP_SOURCE (object);
104
105 switch (prop_id)
106 {
107 case PROP_NEXT_SOURCE:
108 champlain_map_source_set_next_source (map_source,
109 g_value_get_object (value));
110 break;
111
112 case PROP_RENDERER:
113 champlain_map_source_set_renderer (map_source,
114 g_value_get_object (value));
115 break;
116
117 default:
118 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
119 }
120 }
121
122
123 static void
champlain_map_source_dispose(GObject * object)124 champlain_map_source_dispose (GObject *object)
125 {
126 ChamplainMapSourcePrivate *priv = CHAMPLAIN_MAP_SOURCE (object)->priv;
127
128 if (priv->next_source)
129 {
130 g_object_unref (priv->next_source);
131
132 priv->next_source = NULL;
133 }
134
135 if (priv->renderer)
136 {
137 g_object_unref (priv->renderer);
138
139 priv->renderer = NULL;
140 }
141
142 G_OBJECT_CLASS (champlain_map_source_parent_class)->dispose (object);
143 }
144
145
146 static void
champlain_map_source_finalize(GObject * object)147 champlain_map_source_finalize (GObject *object)
148 {
149 G_OBJECT_CLASS (champlain_map_source_parent_class)->finalize (object);
150 }
151
152
153 static void
champlain_map_source_constructed(GObject * object)154 champlain_map_source_constructed (GObject *object)
155 {
156 if (G_OBJECT_CLASS (champlain_map_source_parent_class)->constructed)
157 G_OBJECT_CLASS (champlain_map_source_parent_class)->constructed (object);
158 }
159
160
161 static void
champlain_map_source_class_init(ChamplainMapSourceClass * klass)162 champlain_map_source_class_init (ChamplainMapSourceClass *klass)
163 {
164 GObjectClass *object_class = G_OBJECT_CLASS (klass);
165 GParamSpec *pspec;
166
167 object_class->finalize = champlain_map_source_finalize;
168 object_class->dispose = champlain_map_source_dispose;
169 object_class->get_property = champlain_map_source_get_property;
170 object_class->set_property = champlain_map_source_set_property;
171 object_class->constructed = champlain_map_source_constructed;
172
173 klass->get_id = NULL;
174 klass->get_name = NULL;
175 klass->get_license = NULL;
176 klass->get_license_uri = NULL;
177 klass->get_min_zoom_level = NULL;
178 klass->get_max_zoom_level = NULL;
179 klass->get_tile_size = NULL;
180 klass->get_projection = NULL;
181
182 klass->fill_tile = NULL;
183
184 /**
185 * ChamplainMapSource:next-source:
186 *
187 * Next source in the loading chain.
188 *
189 * Since: 0.6
190 */
191 pspec = g_param_spec_object ("next-source",
192 "Next Source",
193 "Next source in the loading chain",
194 CHAMPLAIN_TYPE_MAP_SOURCE,
195 G_PARAM_READWRITE);
196 g_object_class_install_property (object_class, PROP_NEXT_SOURCE, pspec);
197
198 /**
199 * ChamplainMapSource:renderer:
200 *
201 * Renderer used for tiles rendering.
202 *
203 * Since: 0.8
204 */
205 pspec = g_param_spec_object ("renderer",
206 "Tile renderer",
207 "Tile renderer used to render tiles",
208 CHAMPLAIN_TYPE_RENDERER,
209 G_PARAM_READWRITE);
210 g_object_class_install_property (object_class, PROP_RENDERER, pspec);
211 }
212
213
214 static void
champlain_map_source_init(ChamplainMapSource * map_source)215 champlain_map_source_init (ChamplainMapSource *map_source)
216 {
217 ChamplainMapSourcePrivate *priv = champlain_map_source_get_instance_private (map_source);
218
219 map_source->priv = priv;
220
221 priv->next_source = NULL;
222 priv->renderer = NULL;
223 }
224
225
226 /**
227 * champlain_map_source_get_next_source:
228 * @map_source: a #ChamplainMapSource
229 *
230 * Get the next source in the chain.
231 *
232 * Returns: (transfer none): the next source in the chain.
233 *
234 * Since: 0.6
235 */
236 ChamplainMapSource *
champlain_map_source_get_next_source(ChamplainMapSource * map_source)237 champlain_map_source_get_next_source (ChamplainMapSource *map_source)
238 {
239 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), NULL);
240
241 return map_source->priv->next_source;
242 }
243
244
245 /**
246 * champlain_map_source_get_renderer:
247 * @map_source: a #ChamplainMapSource
248 *
249 * Get the renderer used for tiles rendering.
250 *
251 * Returns: (transfer none): the renderer.
252 *
253 * Since: 0.8
254 */
255 ChamplainRenderer *
champlain_map_source_get_renderer(ChamplainMapSource * map_source)256 champlain_map_source_get_renderer (ChamplainMapSource *map_source)
257 {
258 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), NULL);
259
260 return map_source->priv->renderer;
261 }
262
263
264 /**
265 * champlain_map_source_set_next_source:
266 * @map_source: a #ChamplainMapSource
267 * @next_source: the next #ChamplainMapSource in the chain
268 *
269 * Sets the next map source in the chain.
270 *
271 * Since: 0.6
272 */
273 void
champlain_map_source_set_next_source(ChamplainMapSource * map_source,ChamplainMapSource * next_source)274 champlain_map_source_set_next_source (ChamplainMapSource *map_source,
275 ChamplainMapSource *next_source)
276 {
277 g_return_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source));
278
279 ChamplainMapSourcePrivate *priv = map_source->priv;
280
281 if (priv->next_source != NULL)
282 g_object_unref (priv->next_source);
283
284 if (next_source)
285 {
286 g_return_if_fail (CHAMPLAIN_IS_MAP_SOURCE (next_source));
287
288 g_object_ref_sink (next_source);
289 }
290
291 priv->next_source = next_source;
292
293 g_object_notify (G_OBJECT (map_source), "next-source");
294 }
295
296
297 /**
298 * champlain_map_source_set_renderer:
299 * @map_source: a #ChamplainMapSource
300 * @renderer: the renderer
301 *
302 * Sets the renderer used for tiles rendering.
303 *
304 * Since: 0.8
305 */
306 void
champlain_map_source_set_renderer(ChamplainMapSource * map_source,ChamplainRenderer * renderer)307 champlain_map_source_set_renderer (ChamplainMapSource *map_source,
308 ChamplainRenderer *renderer)
309 {
310 g_return_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source));
311 g_return_if_fail (CHAMPLAIN_IS_RENDERER (renderer));
312
313 ChamplainMapSourcePrivate *priv = map_source->priv;
314
315 if (priv->renderer != NULL)
316 g_object_unref (priv->renderer);
317
318 g_object_ref_sink (renderer);
319 priv->renderer = renderer;
320
321 g_object_notify (G_OBJECT (map_source), "renderer");
322 }
323
324
325 /**
326 * champlain_map_source_get_id:
327 * @map_source: a #ChamplainMapSource
328 *
329 * Gets map source's id.
330 *
331 * Returns: the map source's id.
332 *
333 * Since: 0.4
334 */
335 const gchar *
champlain_map_source_get_id(ChamplainMapSource * map_source)336 champlain_map_source_get_id (ChamplainMapSource *map_source)
337 {
338 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), NULL);
339
340 return CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->get_id (map_source);
341 }
342
343
344 /**
345 * champlain_map_source_get_name:
346 * @map_source: a #ChamplainMapSource
347 *
348 * Gets map source's name.
349 *
350 * Returns: the map source's name.
351 *
352 * Since: 0.4
353 */
354 const gchar *
champlain_map_source_get_name(ChamplainMapSource * map_source)355 champlain_map_source_get_name (ChamplainMapSource *map_source)
356 {
357 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), NULL);
358
359 return CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->get_name (map_source);
360 }
361
362
363 /**
364 * champlain_map_source_get_license:
365 * @map_source: a #ChamplainMapSource
366 *
367 * Gets map source's license.
368 *
369 * Returns: the map source's license.
370 *
371 * Since: 0.4
372 */
373 const gchar *
champlain_map_source_get_license(ChamplainMapSource * map_source)374 champlain_map_source_get_license (ChamplainMapSource *map_source)
375 {
376 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), NULL);
377
378 return CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->get_license (map_source);
379 }
380
381
382 /**
383 * champlain_map_source_get_license_uri:
384 * @map_source: a #ChamplainMapSource
385 *
386 * Gets map source's license URI.
387 *
388 * Returns: the map source's license URI.
389 *
390 * Since: 0.4
391 */
392 const gchar *
champlain_map_source_get_license_uri(ChamplainMapSource * map_source)393 champlain_map_source_get_license_uri (ChamplainMapSource *map_source)
394 {
395 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), NULL);
396
397 return CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->get_license_uri (map_source);
398 }
399
400
401 /**
402 * champlain_map_source_get_min_zoom_level:
403 * @map_source: a #ChamplainMapSource
404 *
405 * Gets map source's minimum zoom level.
406 *
407 * Returns: the miminum zoom level this map source supports
408 *
409 * Since: 0.4
410 */
411 guint
champlain_map_source_get_min_zoom_level(ChamplainMapSource * map_source)412 champlain_map_source_get_min_zoom_level (ChamplainMapSource *map_source)
413 {
414 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
415
416 return CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->get_min_zoom_level (map_source);
417 }
418
419
420 /**
421 * champlain_map_source_get_max_zoom_level:
422 * @map_source: a #ChamplainMapSource
423 *
424 * Gets map source's maximum zoom level.
425 *
426 * Returns: the maximum zoom level this map source supports
427 *
428 * Since: 0.4
429 */
430 guint
champlain_map_source_get_max_zoom_level(ChamplainMapSource * map_source)431 champlain_map_source_get_max_zoom_level (ChamplainMapSource *map_source)
432 {
433 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
434
435 return CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->get_max_zoom_level (map_source);
436 }
437
438
439 /**
440 * champlain_map_source_get_tile_size:
441 * @map_source: a #ChamplainMapSource
442 *
443 * Gets map source's tile size.
444 *
445 * Returns: the tile's size (width and height) in pixels for this map source
446 *
447 * Since: 0.4
448 */
449 guint
champlain_map_source_get_tile_size(ChamplainMapSource * map_source)450 champlain_map_source_get_tile_size (ChamplainMapSource *map_source)
451 {
452 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
453
454 return CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->get_tile_size (map_source);
455 }
456
457
458 /**
459 * champlain_map_source_get_projection:
460 * @map_source: a #ChamplainMapSource
461 *
462 * Gets map source's projection.
463 *
464 * Returns: the map source's projection.
465 *
466 * Since: 0.4
467 */
468 ChamplainMapProjection
champlain_map_source_get_projection(ChamplainMapSource * map_source)469 champlain_map_source_get_projection (ChamplainMapSource *map_source)
470 {
471 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), CHAMPLAIN_MAP_PROJECTION_MERCATOR);
472
473 return CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->get_projection (map_source);
474 }
475
476
477 /**
478 * champlain_map_source_get_x:
479 * @map_source: a #ChamplainMapSource
480 * @zoom_level: the zoom level
481 * @longitude: a longitude
482 *
483 * Gets the x position on the map using this map source's projection.
484 * (0, 0) is located at the top left.
485 *
486 * Returns: the x position
487 *
488 * Since: 0.4
489 */
490 gdouble
champlain_map_source_get_x(ChamplainMapSource * map_source,guint zoom_level,gdouble longitude)491 champlain_map_source_get_x (ChamplainMapSource *map_source,
492 guint zoom_level,
493 gdouble longitude)
494 {
495 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
496
497 longitude = CLAMP (longitude, CHAMPLAIN_MIN_LONGITUDE, CHAMPLAIN_MAX_LONGITUDE);
498
499 /* FIXME: support other projections */
500 return ((longitude + 180.0) / 360.0 * pow (2.0, zoom_level)) * champlain_map_source_get_tile_size (map_source);
501 }
502
503
504 /**
505 * champlain_map_source_get_y:
506 * @map_source: a #ChamplainMapSource
507 * @zoom_level: the zoom level
508 * @latitude: a latitude
509 *
510 * Gets the y position on the map using this map source's projection.
511 * (0, 0) is located at the top left.
512 *
513 * Returns: the y position
514 *
515 * Since: 0.4
516 */
517 gdouble
champlain_map_source_get_y(ChamplainMapSource * map_source,guint zoom_level,gdouble latitude)518 champlain_map_source_get_y (ChamplainMapSource *map_source,
519 guint zoom_level,
520 gdouble latitude)
521 {
522 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
523
524 latitude = CLAMP (latitude, CHAMPLAIN_MIN_LATITUDE, CHAMPLAIN_MAX_LATITUDE);
525
526 /* FIXME: support other projections */
527 return ((1.0 - log (tan (latitude * M_PI / 180.0) + 1.0 / cos (latitude * M_PI / 180.0)) / M_PI) /
528 2.0 * pow (2.0, zoom_level)) * champlain_map_source_get_tile_size (map_source);
529 }
530
531
532 /**
533 * champlain_map_source_get_longitude:
534 * @map_source: a #ChamplainMapSource
535 * @zoom_level: the zoom level
536 * @x: a x position
537 *
538 * Gets the longitude corresponding to this x position in the map source's
539 * projection.
540 *
541 * Returns: the longitude
542 *
543 * Since: 0.4
544 */
545 gdouble
champlain_map_source_get_longitude(ChamplainMapSource * map_source,guint zoom_level,gdouble x)546 champlain_map_source_get_longitude (ChamplainMapSource *map_source,
547 guint zoom_level,
548 gdouble x)
549 {
550 gdouble longitude;
551
552 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0.0);
553 /* FIXME: support other projections */
554 gdouble dx = (gdouble) x / champlain_map_source_get_tile_size (map_source);
555 longitude = dx / pow (2.0, zoom_level) * 360.0 - 180.0;
556
557 return CLAMP (longitude, CHAMPLAIN_MIN_LONGITUDE, CHAMPLAIN_MAX_LONGITUDE);
558 }
559
560
561 /**
562 * champlain_map_source_get_latitude:
563 * @map_source: a #ChamplainMapSource
564 * @zoom_level: the zoom level
565 * @y: a y position
566 *
567 * Gets the latitude corresponding to this y position in the map source's
568 * projection.
569 *
570 * Returns: the latitude
571 *
572 * Since: 0.4
573 */
574 gdouble
champlain_map_source_get_latitude(ChamplainMapSource * map_source,guint zoom_level,gdouble y)575 champlain_map_source_get_latitude (ChamplainMapSource *map_source,
576 guint zoom_level,
577 gdouble y)
578 {
579 gdouble latitude;
580
581 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0.0);
582 /* FIXME: support other projections */
583 gdouble dy = (gdouble) y / champlain_map_source_get_tile_size (map_source);
584 gdouble n = M_PI - 2.0 * M_PI * dy / pow (2.0, zoom_level);
585 latitude = 180.0 / M_PI *atan (0.5 * (exp (n) - exp (-n)));
586
587 return CLAMP (latitude, CHAMPLAIN_MIN_LATITUDE, CHAMPLAIN_MAX_LATITUDE);
588 }
589
590
591 /**
592 * champlain_map_source_get_row_count:
593 * @map_source: a #ChamplainMapSource
594 * @zoom_level: the zoom level
595 *
596 * Gets the number of tiles in a row at this zoom level for this map source.
597 *
598 * Returns: the number of tiles in a row
599 *
600 * Since: 0.4
601 */
602 guint
champlain_map_source_get_row_count(ChamplainMapSource * map_source,guint zoom_level)603 champlain_map_source_get_row_count (ChamplainMapSource *map_source,
604 guint zoom_level)
605 {
606 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
607 /* FIXME: support other projections */
608 return (zoom_level != 0) ? 2 << (zoom_level - 1) : 1;
609 }
610
611
612 /**
613 * champlain_map_source_get_column_count:
614 * @map_source: a #ChamplainMapSource
615 * @zoom_level: the zoom level
616 *
617 * Gets the number of tiles in a column at this zoom level for this map
618 * source.
619 *
620 * Returns: the number of tiles in a column
621 *
622 * Since: 0.4
623 */
624 guint
champlain_map_source_get_column_count(ChamplainMapSource * map_source,guint zoom_level)625 champlain_map_source_get_column_count (ChamplainMapSource *map_source,
626 guint zoom_level)
627 {
628 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
629 /* FIXME: support other projections */
630 return (zoom_level != 0) ? 2 << (zoom_level - 1) : 1;
631 }
632
633
634 #define EARTH_RADIUS 6378137.0 /* meters, Equatorial radius */
635
636 /**
637 * champlain_map_source_get_meters_per_pixel:
638 * @map_source: a #ChamplainMapSource
639 * @zoom_level: the zoom level
640 * @latitude: a latitude
641 * @longitude: a longitude
642 *
643 * Gets meters per pixel at the position on the map using this map source's projection.
644 *
645 * Returns: the meters per pixel
646 *
647 * Since: 0.4.3
648 */
649 gdouble
champlain_map_source_get_meters_per_pixel(ChamplainMapSource * map_source,guint zoom_level,gdouble latitude,G_GNUC_UNUSED gdouble longitude)650 champlain_map_source_get_meters_per_pixel (ChamplainMapSource *map_source,
651 guint zoom_level,
652 gdouble latitude,
653 G_GNUC_UNUSED gdouble longitude)
654 {
655 g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0.0);
656
657 /*
658 * Width is in pixels. (1 px)
659 * m/px = radius_at_latitude / width_in_pixels
660 * k = radius of earth = 6 378.1 km
661 * radius_at_latitude = 2pi * k * sin (pi/2-theta)
662 */
663
664 gdouble tile_size = champlain_map_source_get_tile_size (map_source);
665 /* FIXME: support other projections */
666 return 2.0 *M_PI *EARTH_RADIUS *sin (M_PI / 2.0 - M_PI / 180.0 *latitude) /
667 (tile_size * champlain_map_source_get_row_count (map_source, zoom_level));
668 }
669
670
671 /**
672 * champlain_map_source_fill_tile:
673 * @map_source: a #ChamplainMapSource
674 * @tile: a #ChamplainTile
675 *
676 * Fills the tile with image data (either from cache, network or rendered
677 * locally).
678 *
679 * Since: 0.4
680 */
681 void
champlain_map_source_fill_tile(ChamplainMapSource * map_source,ChamplainTile * tile)682 champlain_map_source_fill_tile (ChamplainMapSource *map_source,
683 ChamplainTile *tile)
684 {
685 g_return_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source));
686
687 CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->fill_tile (map_source, tile);
688 }
689