]> git.street.me.uk Git - andy/viking.git/blob - src/vikslippymapsource.c
Extract a UI module for babel
[andy/viking.git] / src / vikslippymapsource.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * viking
4  * Copyright (C) 2009, 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 3 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:vikslippymapsource
22   * @short_description: the class for SlippyMap oriented map sources
23   * 
24   * The #VikSlippyMapSource class handles slippy map oriented map sources.
25   * The related service is tile oriented, à la Google.
26   * 
27   * The tiles are in 'google spherical mercator', which is
28   * basically a mercator projection that assumes a spherical earth.
29   * http://docs.openlayers.org/library/spherical_mercator.html
30   * 
31   * Such service is also a type of TMS (Tile Map Service) as defined in
32   * OSGeo's wiki.
33   * http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification
34   * But take care that the Y axis is inverted, ie the origin is at top-left
35   * corner.
36   * Following this specification, the protocol handled by this class
37   * follows the global-mercator profile.
38   * 
39   * You can also find many interesting information on the OSM's wiki.
40   * http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
41   * http://wiki.openstreetmap.org/wiki/Setting_up_TMS
42   */
43   
44 #ifdef HAVE_CONFIG_H
45 #include "config.h"
46 #endif
47
48 #ifdef HAVE_MATH_H
49 #include <math.h>
50 #endif
51
52 #include "globals.h"
53 #include "vikslippymapsource.h"
54 #include "maputils.h"
55
56 static gboolean _coord_to_mapcoord ( VikMapSource *self, const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest );
57 static void _mapcoord_to_center_coord ( VikMapSource *self, MapCoord *src, VikCoord *dest );
58
59 static gboolean _is_direct_file_access (VikMapSource *self );
60 static gboolean _is_mbtiles (VikMapSource *self );
61 static gboolean _supports_download_only_new (VikMapSource *self );
62
63 static gchar *_get_uri( VikMapSourceDefault *self, MapCoord *src );
64 static gchar *_get_hostname( VikMapSourceDefault *self );
65 static DownloadMapOptions *_get_download_options( VikMapSourceDefault *self );
66
67 typedef struct _VikSlippyMapSourcePrivate VikSlippyMapSourcePrivate;
68 struct _VikSlippyMapSourcePrivate
69 {
70   gchar *hostname;
71   gchar *url;
72   DownloadMapOptions options;
73   gboolean is_direct_file_access;
74   gboolean is_mbtiles;
75 };
76
77 #define VIK_SLIPPY_MAP_SOURCE_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), VIK_TYPE_SLIPPY_MAP_SOURCE, VikSlippyMapSourcePrivate))
78
79 /* properties */
80 enum
81 {
82   PROP_0,
83
84   PROP_HOSTNAME,
85   PROP_URL,
86   PROP_REFERER,
87   PROP_FOLLOW_LOCATION,
88   PROP_CHECK_FILE_SERVER_TIME,
89   PROP_USE_ETAG,
90   PROP_IS_DIRECT_FILE_ACCESS,
91   PROP_IS_MBTILES,
92 };
93
94 G_DEFINE_TYPE (VikSlippyMapSource, vik_slippy_map_source, VIK_TYPE_MAP_SOURCE_DEFAULT);
95
96 static void
97 vik_slippy_map_source_init (VikSlippyMapSource *self)
98 {
99   /* initialize the object here */
100   VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE (self);
101
102   priv->hostname = NULL;
103   priv->url = NULL;
104   priv->options.referer = NULL;
105   priv->options.follow_location = 0;
106   priv->options.check_file = a_check_map_file;
107   priv->options.check_file_server_time = FALSE;
108   priv->options.use_etag = FALSE;
109   priv->is_direct_file_access = FALSE;
110   priv->is_mbtiles = FALSE;
111
112   g_object_set (G_OBJECT (self),
113                 "tilesize-x", 256,
114                 "tilesize-y", 256,
115                 "drawmode", VIK_VIEWPORT_DRAWMODE_MERCATOR,
116                 NULL);
117 }
118
119 static void
120 vik_slippy_map_source_finalize (GObject *object)
121 {
122   VikSlippyMapSource *self = VIK_SLIPPY_MAP_SOURCE (object);
123   VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE (self);
124
125   g_free (priv->hostname);
126   priv->hostname = NULL;
127   g_free (priv->url);
128   priv->url = NULL;
129   g_free (priv->options.referer);
130   priv->options.referer = NULL;
131
132   G_OBJECT_CLASS (vik_slippy_map_source_parent_class)->finalize (object);
133 }
134
135 static void
136 vik_slippy_map_source_set_property (GObject      *object,
137                                     guint         property_id,
138                                     const GValue *value,
139                                     GParamSpec   *pspec)
140 {
141   VikSlippyMapSource *self = VIK_SLIPPY_MAP_SOURCE (object);
142   VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE (self);
143
144   switch (property_id)
145     {
146     case PROP_HOSTNAME:
147       g_free (priv->hostname);
148       priv->hostname = g_value_dup_string (value);
149       break;
150
151     case PROP_URL:
152       g_free (priv->url);
153       priv->url = g_value_dup_string (value);
154       break;
155
156     case PROP_REFERER:
157       g_free (priv->options.referer);
158       priv->options.referer = g_value_dup_string (value);
159       break;
160
161     case PROP_FOLLOW_LOCATION:
162       priv->options.follow_location = g_value_get_long (value);
163       break;
164
165     case PROP_CHECK_FILE_SERVER_TIME:
166       priv->options.check_file_server_time = g_value_get_boolean (value);
167       break;
168
169     case PROP_USE_ETAG:
170       priv->options.use_etag = g_value_get_boolean (value);
171       break;
172
173     case PROP_IS_DIRECT_FILE_ACCESS:
174       priv->is_direct_file_access = g_value_get_boolean (value);
175       break;
176
177     case PROP_IS_MBTILES:
178       priv->is_mbtiles = g_value_get_boolean (value);
179       break;
180
181     default:
182       /* We don't have any other property... */
183       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
184       break;
185     }
186 }
187
188 static void
189 vik_slippy_map_source_get_property (GObject    *object,
190                                     guint       property_id,
191                                     GValue     *value,
192                                     GParamSpec *pspec)
193 {
194   VikSlippyMapSource *self = VIK_SLIPPY_MAP_SOURCE (object);
195   VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE (self);
196
197   switch (property_id)
198     {
199     case PROP_HOSTNAME:
200       g_value_set_string (value, priv->hostname);
201       break;
202
203     case PROP_URL:
204       g_value_set_string (value, priv->url);
205       break;
206
207     case PROP_REFERER:
208       g_value_set_string (value, priv->options.referer);
209       break;
210
211     case PROP_FOLLOW_LOCATION:
212       g_value_set_long (value, priv->options.follow_location);
213       break;
214
215     case PROP_CHECK_FILE_SERVER_TIME:
216       g_value_set_boolean (value, priv->options.check_file_server_time);
217       break;
218           
219     case PROP_USE_ETAG:
220       g_value_set_boolean (value, priv->options.use_etag);
221       break;
222
223     case PROP_IS_DIRECT_FILE_ACCESS:
224       g_value_set_boolean (value, priv->is_direct_file_access);
225       break;
226
227     case PROP_IS_MBTILES:
228       g_value_set_boolean (value, priv->is_mbtiles);
229       break;
230
231     default:
232       /* We don't have any other property... */
233       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
234       break;
235     }
236 }
237
238 static void
239 vik_slippy_map_source_class_init (VikSlippyMapSourceClass *klass)
240 {
241         GObjectClass* object_class = G_OBJECT_CLASS (klass);
242         VikMapSourceClass* grandparent_class = VIK_MAP_SOURCE_CLASS (klass);
243         VikMapSourceDefaultClass* parent_class = VIK_MAP_SOURCE_DEFAULT_CLASS (klass);
244         GParamSpec *pspec = NULL;
245                 
246         object_class->set_property = vik_slippy_map_source_set_property;
247     object_class->get_property = vik_slippy_map_source_get_property;
248
249         /* Overiding methods */
250         grandparent_class->coord_to_mapcoord =        _coord_to_mapcoord;
251         grandparent_class->mapcoord_to_center_coord = _mapcoord_to_center_coord;
252         grandparent_class->is_direct_file_access = _is_direct_file_access;
253         grandparent_class->is_mbtiles = _is_mbtiles;
254         grandparent_class->supports_download_only_new = _supports_download_only_new;
255
256         parent_class->get_uri = _get_uri;
257         parent_class->get_hostname = _get_hostname;
258         parent_class->get_download_options = _get_download_options;
259
260         pspec = g_param_spec_string ("hostname",
261                                      "Hostname",
262                                      "The hostname of the map server",
263                                      "<no-set>" /* default value */,
264                                      G_PARAM_READWRITE);
265         g_object_class_install_property (object_class, PROP_HOSTNAME, pspec);
266
267         pspec = g_param_spec_string ("url",
268                                      "URL",
269                                      "The template of the tiles' URL",
270                                      "<no-set>" /* default value */,
271                                      G_PARAM_READWRITE);
272         g_object_class_install_property (object_class, PROP_URL, pspec);
273
274         pspec = g_param_spec_string ("referer",
275                                      "Referer",
276                                      "The REFERER string to use in HTTP request",
277                                      NULL /* default value */,
278                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
279         g_object_class_install_property (object_class, PROP_REFERER, pspec);
280         
281         pspec = g_param_spec_long ("follow-location",
282                                    "Follow location",
283                                "Specifies the number of retries to follow a redirect while downloading a page",
284                                0  /* minimum value */,
285                                G_MAXLONG /* maximum value */,
286                                0  /* default value */,
287                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
288         g_object_class_install_property (object_class, PROP_FOLLOW_LOCATION, pspec);
289         
290         pspec = g_param_spec_boolean ("check-file-server-time",
291                                       "Check file server time",
292                                   "Age of current cache before redownloading tile",
293                                   FALSE  /* default value */,
294                                   G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
295         g_object_class_install_property (object_class, PROP_CHECK_FILE_SERVER_TIME, pspec);
296
297         pspec = g_param_spec_boolean ("use-etag",
298                                       "Use etag values with server",
299                                   "Store etag in a file, and send it to server to check if we have the latest file",
300                                   FALSE  /* default value */,
301                                   G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
302         g_object_class_install_property (object_class, PROP_USE_ETAG, pspec);
303
304         pspec = g_param_spec_boolean ("use-direct-file-access",
305                                       "Use direct file access",
306                                       "Use direct file access to OSM like tile images - no need for a webservice",
307                                   FALSE  /* default value */,
308                                   G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
309         g_object_class_install_property (object_class, PROP_IS_DIRECT_FILE_ACCESS, pspec);
310
311         pspec = g_param_spec_boolean ("is-mbtiles",
312                                       "Is an SQL MBTiles File",
313                                       "Use an SQL MBTiles File for the tileset - no need for a webservice",
314                                       FALSE  /* default value */,
315                                       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
316         g_object_class_install_property (object_class, PROP_IS_MBTILES, pspec);
317         g_type_class_add_private (klass, sizeof (VikSlippyMapSourcePrivate));
318         
319         object_class->finalize = vik_slippy_map_source_finalize;
320 }
321
322 static gboolean
323 _is_direct_file_access (VikMapSource *self)
324 {
325   g_return_val_if_fail (VIK_IS_SLIPPY_MAP_SOURCE(self), FALSE);
326
327   VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE(self);
328
329   return priv->is_direct_file_access;
330 }
331
332 static gboolean
333 _is_mbtiles (VikMapSource *self)
334 {
335   g_return_val_if_fail (VIK_IS_SLIPPY_MAP_SOURCE(self), FALSE);
336
337   VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE(self);
338
339   return priv->is_mbtiles;
340 }
341
342 static gboolean
343 _supports_download_only_new (VikMapSource *self)
344 {
345   g_return_val_if_fail (VIK_IS_SLIPPY_MAP_SOURCE(self), FALSE);
346         
347   VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE(self);
348         
349   return priv->options.check_file_server_time || priv->options.use_etag;
350 }
351
352 static gboolean
353 _coord_to_mapcoord ( VikMapSource *self, const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest )
354 {
355   g_assert ( src->mode == VIK_COORD_LATLON );
356
357   if ( xzoom != yzoom )
358     return FALSE;
359
360   dest->scale = map_utils_mpp_to_scale ( xzoom );
361   if ( dest->scale == 255 )
362     return FALSE;
363
364   dest->x = (src->east_west + 180) / 360 * VIK_GZ(17) / xzoom;
365   dest->y = (180 - MERCLAT(src->north_south)) / 360 * VIK_GZ(17) / xzoom;
366   dest->z = 0;
367
368   return TRUE;
369 }
370
371 static void
372 _mapcoord_to_center_coord ( VikMapSource *self, MapCoord *src, VikCoord *dest )
373 {
374   gdouble socalled_mpp;
375   if (src->scale >= 0)
376     socalled_mpp = VIK_GZ(src->scale);
377   else
378     socalled_mpp = 1.0/VIK_GZ(-src->scale);
379   dest->mode = VIK_COORD_LATLON;
380   dest->east_west = ((src->x+0.5) / VIK_GZ(17) * socalled_mpp * 360) - 180;
381   dest->north_south = DEMERCLAT(180 - ((src->y+0.5) / VIK_GZ(17) * socalled_mpp * 360));
382 }
383
384 static gchar *
385 _get_uri( VikMapSourceDefault *self, MapCoord *src )
386 {
387         g_return_val_if_fail (VIK_IS_SLIPPY_MAP_SOURCE(self), NULL);
388         
389     VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE(self);
390         gchar *uri = g_strdup_printf (priv->url, 17 - src->scale, src->x, src->y);
391         return uri;
392
393
394 static gchar *
395 _get_hostname( VikMapSourceDefault *self )
396 {
397         g_return_val_if_fail (VIK_IS_SLIPPY_MAP_SOURCE(self), NULL);
398         
399     VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE(self);
400         return g_strdup( priv->hostname );
401 }
402
403 static DownloadMapOptions *
404 _get_download_options( VikMapSourceDefault *self )
405 {
406         g_return_val_if_fail (VIK_IS_SLIPPY_MAP_SOURCE(self), NULL);
407         
408         VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE(self);
409         return &(priv->options);
410 }
411
412 VikSlippyMapSource *
413 vik_slippy_map_source_new_with_id (guint16 id, const gchar *label, const gchar *hostname, const gchar *url)
414 {
415         return g_object_new(VIK_TYPE_SLIPPY_MAP_SOURCE,
416                             "id", id, "label", label, "hostname", hostname, "url", url, NULL);
417 }