]> git.street.me.uk Git - andy/viking.git/blob - src/viktmsmapsource.c
Ensure calculation of the waypoint bounds even when the layer is not currently visible.
[andy/viking.git] / src / viktmsmapsource.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * viking
4  * Copyright (C) 2010, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
5  * 
6  * viking is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  * 
11  * viking is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along
17  * with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20  /**
21   * SECTION:viktmsmapsource
22   * @short_description: the class for TMS oriented map sources
23   * 
24   * The #VikTmsMapSource class handles TMS oriented map sources.
25   * 
26   * The tiles are in 'equirectangular'.
27   * http://en.wikipedia.org/wiki/Equirectangular_projection
28   * 
29   * Such service is also a type of TMS (Tile Map Service) as defined in
30   * OSGeo's wiki.
31   * http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification
32   * Following this specification, the protocol handled by this class
33   * follows the global-geodetic profile.
34   */
35   
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #ifdef HAVE_MATH_H
41 #include <math.h>
42 #endif
43
44 #include "globals.h"
45 #include "viktmsmapsource.h"
46 #include "maputils.h"
47
48 static gboolean _coord_to_mapcoord ( VikMapSource *self, const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest );
49 static void _mapcoord_to_center_coord ( VikMapSource *self, MapCoord *src, VikCoord *dest );
50 static gboolean _supports_download_only_new ( VikMapSource *self );
51 static gboolean _is_direct_file_access ( VikMapSource *self );
52 static gboolean _is_mbtiles ( VikMapSource *self );
53 static gboolean _is_osm_meta_tiles (VikMapSource *self );
54 static guint8 _get_zoom_min(VikMapSource *self );
55 static guint8 _get_zoom_max(VikMapSource *self );
56 static gdouble _get_lat_min(VikMapSource *self );
57 static gdouble _get_lat_max(VikMapSource *self );
58 static gdouble _get_lon_min(VikMapSource *self );
59 static gdouble _get_lon_max(VikMapSource *self );
60
61 static gchar *_get_uri( VikMapSourceDefault *self, MapCoord *src );
62 static gchar *_get_hostname( VikMapSourceDefault *self );
63 static DownloadMapOptions *_get_download_options( VikMapSourceDefault *self );
64
65 typedef struct _VikTmsMapSourcePrivate VikTmsMapSourcePrivate;
66 struct _VikTmsMapSourcePrivate
67 {
68   gchar *hostname;
69   gchar *url;
70   DownloadMapOptions options;
71   guint zoom_min; // TMS Zoom level: 0 = Whole World // http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
72   guint zoom_max; // TMS Zoom level: Often 18 for zoomed in.
73   gdouble lat_min; // Degrees
74   gdouble lat_max; // Degrees
75   gdouble lon_min; // Degrees
76   gdouble lon_max; // Degrees
77 };
78
79 #define VIK_TMS_MAP_SOURCE_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), VIK_TYPE_TMS_MAP_SOURCE, VikTmsMapSourcePrivate))
80
81 /* properties */
82 enum
83 {
84   PROP_0,
85
86   PROP_HOSTNAME,
87   PROP_URL,
88   PROP_REFERER,
89   PROP_FOLLOW_LOCATION,
90   PROP_CHECK_FILE_SERVER_TIME,
91   PROP_ZOOM_MIN,
92   PROP_ZOOM_MAX,
93   PROP_LAT_MIN,
94   PROP_LAT_MAX,
95   PROP_LON_MIN,
96   PROP_LON_MAX,
97 };
98
99 G_DEFINE_TYPE (VikTmsMapSource, vik_tms_map_source, VIK_TYPE_MAP_SOURCE_DEFAULT);
100
101 static void
102 vik_tms_map_source_init (VikTmsMapSource *self)
103 {
104   /* initialize the object here */
105   VikTmsMapSourcePrivate *priv = VIK_TMS_MAP_SOURCE_PRIVATE (self);
106
107   priv->hostname = NULL;
108   priv->url = NULL;
109   priv->options.referer = NULL;
110   priv->options.follow_location = 0;
111   priv->options.check_file = a_check_map_file;
112   priv->options.check_file_server_time = FALSE;
113   priv->zoom_min = 0;
114   priv->zoom_max = 18;
115   priv->lat_min = -90.0;
116   priv->lat_max = 90.0;
117   priv->lon_min = -180.0;
118   priv->lon_max = 180.0;
119
120   g_object_set (G_OBJECT (self),
121                 "tilesize-x", 256,
122                 "tilesize-y", 256,
123                 "drawmode", VIK_VIEWPORT_DRAWMODE_LATLON,
124                 NULL);
125 }
126
127 static void
128 vik_tms_map_source_finalize (GObject *object)
129 {
130   VikTmsMapSource *self = VIK_TMS_MAP_SOURCE (object);
131   VikTmsMapSourcePrivate *priv = VIK_TMS_MAP_SOURCE_PRIVATE (self);
132
133   g_free (priv->hostname);
134   priv->hostname = NULL;
135   g_free (priv->url);
136   priv->url = NULL;
137   g_free (priv->options.referer);
138   priv->options.referer = NULL;
139
140   G_OBJECT_CLASS (vik_tms_map_source_parent_class)->finalize (object);
141 }
142
143 static void
144 vik_tms_map_source_set_property (GObject      *object,
145                                     guint         property_id,
146                                     const GValue *value,
147                                     GParamSpec   *pspec)
148 {
149   VikTmsMapSource *self = VIK_TMS_MAP_SOURCE (object);
150   VikTmsMapSourcePrivate *priv = VIK_TMS_MAP_SOURCE_PRIVATE (self);
151
152   switch (property_id)
153     {
154     case PROP_HOSTNAME:
155       g_free (priv->hostname);
156       priv->hostname = g_value_dup_string (value);
157       break;
158
159     case PROP_URL:
160       g_free (priv->url);
161       priv->url = g_value_dup_string (value);
162       break;
163
164     case PROP_REFERER:
165       g_free (priv->options.referer);
166       priv->options.referer = g_value_dup_string (value);
167       break;
168
169     case PROP_FOLLOW_LOCATION:
170       priv->options.follow_location = g_value_get_long (value);
171       break;
172
173     case PROP_CHECK_FILE_SERVER_TIME:
174       priv->options.check_file_server_time = g_value_get_boolean (value);
175       break;
176
177     case PROP_ZOOM_MIN:
178       priv->zoom_min = g_value_get_uint (value);
179       break;
180
181     case PROP_ZOOM_MAX:
182       priv->zoom_max = g_value_get_uint (value);
183       break;
184
185     case PROP_LAT_MIN:
186       priv->lat_min = g_value_get_double (value);
187       break;
188
189     case PROP_LAT_MAX:
190       priv->lat_max = g_value_get_double (value);
191       break;
192
193     case PROP_LON_MIN:
194       priv->lon_min = g_value_get_double (value);
195       break;
196
197     case PROP_LON_MAX:
198       priv->lon_max = g_value_get_double (value);
199       break;
200
201     default:
202       /* We don't have any other property... */
203       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
204       break;
205     }
206 }
207
208 static void
209 vik_tms_map_source_get_property (GObject    *object,
210                                     guint       property_id,
211                                     GValue     *value,
212                                     GParamSpec *pspec)
213 {
214   VikTmsMapSource *self = VIK_TMS_MAP_SOURCE (object);
215   VikTmsMapSourcePrivate *priv = VIK_TMS_MAP_SOURCE_PRIVATE (self);
216
217   switch (property_id)
218     {
219     case PROP_HOSTNAME:
220       g_value_set_string (value, priv->hostname);
221       break;
222
223     case PROP_URL:
224       g_value_set_string (value, priv->url);
225       break;
226
227     case PROP_REFERER:
228       g_value_set_string (value, priv->options.referer);
229       break;
230
231     case PROP_FOLLOW_LOCATION:
232       g_value_set_long (value, priv->options.follow_location);
233       break;
234
235     case PROP_CHECK_FILE_SERVER_TIME:
236       g_value_set_boolean (value, priv->options.check_file_server_time);
237       break;
238
239     case PROP_ZOOM_MIN:
240       g_value_set_uint (value, priv->zoom_min);
241       break;
242
243     case PROP_ZOOM_MAX:
244       g_value_set_uint (value, priv->zoom_max);
245       break;
246
247     case PROP_LON_MIN:
248       g_value_set_double (value, priv->lon_min);
249       break;
250
251     case PROP_LON_MAX:
252       g_value_set_double (value, priv->lon_max);
253       break;
254
255     case PROP_LAT_MIN:
256       g_value_set_double (value, priv->lat_min);
257       break;
258
259     case PROP_LAT_MAX:
260       g_value_set_double (value, priv->lat_max);
261       break;
262
263     default:
264       /* We don't have any other property... */
265       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
266       break;
267     }
268 }
269
270 static void
271 vik_tms_map_source_class_init (VikTmsMapSourceClass *klass)
272 {
273         GObjectClass* object_class = G_OBJECT_CLASS (klass);
274         VikMapSourceClass* grandparent_class = VIK_MAP_SOURCE_CLASS (klass);
275         VikMapSourceDefaultClass* parent_class = VIK_MAP_SOURCE_DEFAULT_CLASS (klass);
276         GParamSpec *pspec = NULL;
277                 
278         object_class->set_property = vik_tms_map_source_set_property;
279     object_class->get_property = vik_tms_map_source_get_property;
280
281         /* Overiding methods */
282         grandparent_class->coord_to_mapcoord =        _coord_to_mapcoord;
283         grandparent_class->mapcoord_to_center_coord = _mapcoord_to_center_coord;
284         grandparent_class->supports_download_only_new = _supports_download_only_new;
285         grandparent_class->is_direct_file_access = _is_direct_file_access;
286         grandparent_class->is_mbtiles = _is_mbtiles;
287         grandparent_class->is_osm_meta_tiles = _is_osm_meta_tiles;
288         grandparent_class->get_zoom_min = _get_zoom_min;
289         grandparent_class->get_zoom_max = _get_zoom_max;
290         grandparent_class->get_lat_min = _get_lat_min;
291         grandparent_class->get_lat_max = _get_lat_max;
292         grandparent_class->get_lon_min = _get_lon_min;
293         grandparent_class->get_lon_max = _get_lon_max;
294
295         parent_class->get_uri = _get_uri;
296         parent_class->get_hostname = _get_hostname;
297         parent_class->get_download_options = _get_download_options;
298
299         pspec = g_param_spec_string ("hostname",
300                                      "Hostname",
301                                      "The hostname of the map server",
302                                      "<no-set>" /* default value */,
303                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
304         g_object_class_install_property (object_class, PROP_HOSTNAME, pspec);
305
306         pspec = g_param_spec_string ("url",
307                                      "URL",
308                                      "The template of the tiles' URL",
309                                      "<no-set>" /* default value */,
310                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
311         g_object_class_install_property (object_class, PROP_URL, pspec);
312
313         pspec = g_param_spec_string ("referer",
314                                      "Referer",
315                                      "The REFERER string to use in HTTP request",
316                                      NULL /* default value */,
317                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
318         g_object_class_install_property (object_class, PROP_REFERER, pspec);
319         
320         pspec = g_param_spec_long ("follow-location",
321                                    "Follow location",
322                                "Specifies the number of retries to follow a redirect while downloading a page",
323                                0  /* minimum value */,
324                                G_MAXLONG /* maximum value */,
325                                0  /* default value */,
326                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
327         g_object_class_install_property (object_class, PROP_FOLLOW_LOCATION, pspec);
328         
329         pspec = g_param_spec_boolean ("check-file-server-time",
330                                       "Check file server time",
331                                   "Age of current cache before redownloading tile",
332                                   FALSE  /* default value */,
333                                   G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
334         g_object_class_install_property (object_class, PROP_CHECK_FILE_SERVER_TIME, pspec);
335
336         pspec = g_param_spec_uint ("zoom-min",
337                                    "Minimum zoom",
338                                    "Minimum Zoom level supported by the map provider",
339                                    0,  // minimum value,
340                                    22, // maximum value
341                                    0, // default value
342                                    G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
343         g_object_class_install_property (object_class, PROP_ZOOM_MIN, pspec);
344
345         pspec = g_param_spec_uint ("zoom-max",
346                                    "Maximum zoom",
347                                    "Maximum Zoom level supported by the map provider",
348                                    0,  // minimum value,
349                                    22, // maximum value
350                                    18, // default value
351                                    G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
352         g_object_class_install_property (object_class, PROP_ZOOM_MAX, pspec);
353
354         pspec = g_param_spec_double ("lat-min",
355                                      "Minimum latitude",
356                                      "Minimum latitude in degrees supported by the map provider",
357                                      -90.0,  // minimum value
358                                      90.0, // maximum value
359                                      -90.0, // default value
360                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
361         g_object_class_install_property (object_class, PROP_LAT_MIN, pspec);
362
363         pspec = g_param_spec_double ("lat-max",
364                                      "Maximum latitude",
365                                      "Maximum latitude in degrees supported by the map provider",
366                                      -90.0,  // minimum value
367                                      90.0, // maximum value
368                                      90.0, // default value
369                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
370         g_object_class_install_property (object_class, PROP_LAT_MAX, pspec);
371
372         pspec = g_param_spec_double ("lon-min",
373                                      "Minimum longitude",
374                                      "Minimum longitude in degrees supported by the map provider",
375                                      -180.0,  // minimum value
376                                      180.0, // maximum value
377                                      -180.0, // default value
378                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
379         g_object_class_install_property (object_class, PROP_LON_MIN, pspec);
380
381         pspec = g_param_spec_double ("lon-max",
382                                      "Maximum longitude",
383                                      "Maximum longitude in degrees supported by the map provider",
384                                      -180.0,  // minimum value
385                                      180.0, // maximum value
386                                      180.0, // default value
387                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
388         g_object_class_install_property (object_class, PROP_LON_MAX, pspec);
389
390         g_type_class_add_private (klass, sizeof (VikTmsMapSourcePrivate));
391         
392         object_class->finalize = vik_tms_map_source_finalize;
393 }
394
395 static gboolean
396 _is_direct_file_access ( VikMapSource *self )
397 {
398         return FALSE;
399 }
400
401 static gboolean
402 _is_mbtiles ( VikMapSource *self )
403 {
404         return FALSE;
405 }
406
407 static gboolean
408 _is_osm_meta_tiles ( VikMapSource *self )
409 {
410         return FALSE;
411 }
412
413 static gboolean
414 _supports_download_only_new ( VikMapSource *self )
415 {
416         g_return_val_if_fail (VIK_IS_TMS_MAP_SOURCE(self), FALSE);
417         
418     VikTmsMapSourcePrivate *priv = VIK_TMS_MAP_SOURCE_PRIVATE(self);
419         
420         return priv->options.check_file_server_time;
421 }
422
423 static gboolean
424 _coord_to_mapcoord ( VikMapSource *self, const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest )
425 {
426   g_assert ( src->mode == VIK_COORD_LATLON );
427
428   if ( xzoom != yzoom )
429     return FALSE;
430
431   dest->scale = map_utils_mpp_to_scale ( xzoom );
432   if ( dest->scale == 255 )
433     return FALSE;
434
435   /* Note : VIK_GZ(17) / xzoom / 2 = number of tile on Y axis */
436         g_debug("%s: xzoom=%f yzoom=%f -> %f", __FUNCTION__,
437           xzoom, yzoom, VIK_GZ(17) / xzoom / 2);
438   dest->x = floor((src->east_west + 180) / 180 * VIK_GZ(17) / xzoom / 2);
439   /* We should restore logic of viking:
440    * tile index on Y axis follow a screen logic (top -> down)
441    */
442   dest->y = floor((180 - (src->north_south + 90)) / 180 * VIK_GZ(17) / xzoom / 2);
443   dest->z = 0;
444   g_debug("%s: %f,%f -> %d,%d", __FUNCTION__,
445           src->east_west, src->north_south, dest->x, dest->y);
446   return TRUE;
447 }
448
449 static void
450 _mapcoord_to_center_coord ( VikMapSource *self, MapCoord *src, VikCoord *dest )
451 {
452   gdouble socalled_mpp;
453   if (src->scale >= 0)
454     socalled_mpp = VIK_GZ(src->scale);
455   else
456     socalled_mpp = 1.0/VIK_GZ(-src->scale);
457   dest->mode = VIK_COORD_LATLON;
458   dest->east_west = (src->x+0.5) * 180 / VIK_GZ(17) * socalled_mpp * 2 - 180;
459   /* We should restore logic of viking:
460    * tile index on Y axis follow a screen logic (top -> down)
461    */
462   dest->north_south = -((src->y+0.5) * 180 / VIK_GZ(17) * socalled_mpp * 2 - 90);
463   g_debug("%s: %d,%d -> %f,%f", __FUNCTION__,
464           src->x, src->y, dest->east_west, dest->north_south);
465 }
466
467 static gchar *
468 _get_uri( VikMapSourceDefault *self, MapCoord *src )
469 {
470         g_return_val_if_fail (VIK_IS_TMS_MAP_SOURCE(self), NULL);
471         
472     VikTmsMapSourcePrivate *priv = VIK_TMS_MAP_SOURCE_PRIVATE(self);
473         /* We should restore logic of viking:
474      * tile index on Y axis follow a screen logic (top -> down)
475      */
476
477         /* Note : nb tiles on Y axis */
478         gint nb_tiles = VIK_GZ(17 - src->scale - 1);
479
480         gchar *uri = g_strdup_printf (priv->url, 17 - src->scale - 1, src->x, nb_tiles - src->y - 1);
481         
482         return uri;
483
484
485 static gchar *
486 _get_hostname( VikMapSourceDefault *self )
487 {
488         g_return_val_if_fail (VIK_IS_TMS_MAP_SOURCE(self), NULL);
489         
490     VikTmsMapSourcePrivate *priv = VIK_TMS_MAP_SOURCE_PRIVATE(self);
491         return g_strdup( priv->hostname );
492 }
493
494 static DownloadMapOptions *
495 _get_download_options( VikMapSourceDefault *self )
496 {
497         g_return_val_if_fail (VIK_IS_TMS_MAP_SOURCE(self), NULL);
498         
499         VikTmsMapSourcePrivate *priv = VIK_TMS_MAP_SOURCE_PRIVATE(self);
500         return &(priv->options);
501 }
502
503 /**
504  *
505  */
506 static guint8
507 _get_zoom_min (VikMapSource *self)
508 {
509         g_return_val_if_fail (VIK_IS_TMS_MAP_SOURCE(self), FALSE);
510         VikTmsMapSourcePrivate *priv = VIK_TMS_MAP_SOURCE_PRIVATE(self);
511         return priv->zoom_min;
512 }
513
514 /**
515  *
516  */
517 static guint8
518 _get_zoom_max (VikMapSource *self)
519 {
520         g_return_val_if_fail (VIK_IS_TMS_MAP_SOURCE(self), FALSE);
521         VikTmsMapSourcePrivate *priv = VIK_TMS_MAP_SOURCE_PRIVATE(self);
522         return priv->zoom_max;
523 }
524
525 /**
526  *
527  */
528 static gdouble
529 _get_lat_min (VikMapSource *self)
530 {
531         g_return_val_if_fail (VIK_IS_TMS_MAP_SOURCE(self), FALSE);
532         VikTmsMapSourcePrivate *priv = VIK_TMS_MAP_SOURCE_PRIVATE(self);
533         return priv->lat_min;
534 }
535
536 /**
537  *
538  */
539 static gdouble
540 _get_lat_max (VikMapSource *self)
541 {
542         g_return_val_if_fail (VIK_IS_TMS_MAP_SOURCE(self), FALSE);
543         VikTmsMapSourcePrivate *priv = VIK_TMS_MAP_SOURCE_PRIVATE(self);
544         return priv->lat_max;
545 }
546
547 /**
548  *
549  */
550 static gdouble
551 _get_lon_min (VikMapSource *self)
552 {
553         g_return_val_if_fail (VIK_IS_TMS_MAP_SOURCE(self), FALSE);
554         VikTmsMapSourcePrivate *priv = VIK_TMS_MAP_SOURCE_PRIVATE(self);
555         return priv->lon_min;
556 }
557
558 /**
559  *
560  */
561 static gdouble
562 _get_lon_max (VikMapSource *self)
563 {
564         g_return_val_if_fail (VIK_IS_TMS_MAP_SOURCE(self), FALSE);
565         VikTmsMapSourcePrivate *priv = VIK_TMS_MAP_SOURCE_PRIVATE(self);
566         return priv->lon_max;
567 }
568
569 VikTmsMapSource *
570 vik_tms_map_source_new_with_id (guint16 id, const gchar *label, const gchar *hostname, const gchar *url)
571 {
572         return g_object_new(VIK_TYPE_TMS_MAP_SOURCE,
573                             "id", id, "label", label, "hostname", hostname, "url", url, NULL);
574 }