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