]> git.street.me.uk Git - andy/viking.git/blob - src/vikslippymapsource.c
[DOC] Fix: Viking's sources are hosted by Git now
[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) Guilhem Bonnefille 2009 <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 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #ifdef HAVE_MATH_H
24 #include <math.h>
25 #endif
26
27 #include "globals.h"
28 #include "vikslippymapsource.h"
29
30 static gboolean _coord_to_mapcoord ( VikMapSource *self, const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest );
31 static void _mapcoord_to_center_coord ( VikMapSource *self, MapCoord *src, VikCoord *dest );
32 static int _download ( VikMapSource *self, MapCoord *src, const gchar *dest_fn, void *handle );
33 static void * _download_handle_init ( VikMapSource *self);
34 static void _download_handle_cleanup ( VikMapSource *self, void *handle);
35 static gboolean _supports_if_modified_since (VikMapSource *self );
36
37 static gchar *_get_uri( VikSlippyMapSource *self, MapCoord *src );
38 static gchar *_get_hostname( VikSlippyMapSource *self );
39 static DownloadOptions *_get_download_options( VikSlippyMapSource *self );
40
41 typedef struct _VikSlippyMapSourcePrivate VikSlippyMapSourcePrivate;
42 struct _VikSlippyMapSourcePrivate
43 {
44   gchar *hostname;
45   gchar *url;
46   DownloadOptions options;
47 };
48
49 #define VIK_SLIPPY_MAP_SOURCE_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), VIK_TYPE_SLIPPY_MAP_SOURCE, VikSlippyMapSourcePrivate))
50
51 /* properties */
52 enum
53 {
54   PROP_0,
55
56   PROP_HOSTNAME,
57   PROP_URL,
58   PROP_REFERER,
59   PROP_FOLLOW_LOCATION,
60   PROP_CHECK_FILE_SERVER_TIME,
61 };
62
63 G_DEFINE_TYPE_EXTENDED (VikSlippyMapSource, vik_slippy_map_source, VIK_TYPE_MAP_SOURCE_DEFAULT, (GTypeFlags)0,);
64
65 static void
66 vik_slippy_map_source_init (VikSlippyMapSource *self)
67 {
68   /* initialize the object here */
69   VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE (self);
70
71   priv->hostname = NULL;
72   priv->url = NULL;
73   priv->options.referer = NULL;
74   priv->options.follow_location = 0;
75   priv->options.check_file = a_check_map_file;
76   priv->options.check_file_server_time = FALSE;
77
78   g_object_set (G_OBJECT (self),
79                 "tilesize-x", 256,
80                 "tilesize-y", 256,
81                 "drawmode", VIK_VIEWPORT_DRAWMODE_MERCATOR,
82                 NULL);
83 }
84
85 static void
86 vik_slippy_map_source_finalize (GObject *object)
87 {
88   VikSlippyMapSource *self = VIK_SLIPPY_MAP_SOURCE (object);
89   VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE (self);
90
91   g_free (priv->hostname);
92   priv->hostname = NULL;
93   g_free (priv->url);
94   priv->url = NULL;
95   g_free (priv->options.referer);
96   priv->options.referer = NULL;
97
98   G_OBJECT_CLASS (vik_slippy_map_source_parent_class)->finalize (object);
99 }
100
101 static void
102 vik_slippy_map_source_set_property (GObject      *object,
103                                     guint         property_id,
104                                     const GValue *value,
105                                     GParamSpec   *pspec)
106 {
107   VikSlippyMapSource *self = VIK_SLIPPY_MAP_SOURCE (object);
108   VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE (self);
109
110   switch (property_id)
111     {
112     case PROP_HOSTNAME:
113       g_free (priv->hostname);
114       priv->hostname = g_value_dup_string (value);
115       break;
116
117     case PROP_URL:
118       g_free (priv->url);
119       priv->url = g_value_dup_string (value);
120       break;
121
122     case PROP_REFERER:
123       g_free (priv->options.referer);
124       priv->options.referer = g_value_dup_string (value);
125       break;
126
127     case PROP_FOLLOW_LOCATION:
128       priv->options.follow_location = g_value_get_long (value);
129       break;
130
131     case PROP_CHECK_FILE_SERVER_TIME:
132       priv->options.check_file_server_time = g_value_get_boolean (value);
133       break;
134
135     default:
136       /* We don't have any other property... */
137       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
138       break;
139     }
140 }
141
142 static void
143 vik_slippy_map_source_get_property (GObject    *object,
144                                     guint       property_id,
145                                     GValue     *value,
146                                     GParamSpec *pspec)
147 {
148   VikSlippyMapSource *self = VIK_SLIPPY_MAP_SOURCE (object);
149   VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE (self);
150
151   switch (property_id)
152     {
153     case PROP_HOSTNAME:
154       g_value_set_string (value, priv->hostname);
155       break;
156
157     case PROP_URL:
158       g_value_set_string (value, priv->url);
159       break;
160
161     case PROP_REFERER:
162       g_value_set_string (value, priv->options.referer);
163       break;
164
165     case PROP_FOLLOW_LOCATION:
166       g_value_set_long (value, priv->options.follow_location);
167       break;
168
169     case PROP_CHECK_FILE_SERVER_TIME:
170       g_value_set_boolean (value, priv->options.check_file_server_time);
171       break;
172           
173     default:
174       /* We don't have any other property... */
175       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
176       break;
177     }
178 }
179
180 static void
181 vik_slippy_map_source_class_init (VikSlippyMapSourceClass *klass)
182 {
183         GObjectClass* object_class = G_OBJECT_CLASS (klass);
184         VikMapSourceClass* parent_class = VIK_MAP_SOURCE_CLASS (klass);
185         GParamSpec *pspec = NULL;
186                 
187         object_class->set_property = vik_slippy_map_source_set_property;
188     object_class->get_property = vik_slippy_map_source_get_property;
189
190         /* Overiding methods */
191         parent_class->coord_to_mapcoord =        _coord_to_mapcoord;
192         parent_class->mapcoord_to_center_coord = _mapcoord_to_center_coord;
193         parent_class->download =                 _download;
194         parent_class->download_handle_init =     _download_handle_init;
195         parent_class->download_handle_cleanup =  _download_handle_cleanup;
196         parent_class->supports_if_modified_since = _supports_if_modified_since;
197         
198         /* Default implementation of methods */
199         klass->get_uri = _get_uri;
200         klass->get_hostname = _get_hostname;
201         klass->get_download_options = _get_download_options;
202
203         pspec = g_param_spec_string ("hostname",
204                                      "Hostname",
205                                      "The hostname of the map server",
206                                      "<no-set>" /* default value */,
207                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
208         g_object_class_install_property (object_class, PROP_HOSTNAME, pspec);
209
210         pspec = g_param_spec_string ("url",
211                                      "URL",
212                                      "The template of the tiles' URL",
213                                      "<no-set>" /* default value */,
214                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
215         g_object_class_install_property (object_class, PROP_URL, pspec);
216
217         pspec = g_param_spec_string ("referer",
218                                      "Referer",
219                                      "The REFERER string to use in HTTP request",
220                                      NULL /* default value */,
221                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
222         g_object_class_install_property (object_class, PROP_REFERER, pspec);
223         
224         pspec = g_param_spec_long ("follow-location",
225                                    "Follow location",
226                                "Specifies the number of retries to follow a redirect while downloading a page",
227                                0  /* minimum value */,
228                                G_MAXLONG /* maximum value */,
229                                0  /* default value */,
230                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
231         g_object_class_install_property (object_class, PROP_FOLLOW_LOCATION, pspec);
232         
233         pspec = g_param_spec_boolean ("check-file-server-time",
234                                       "Check file server time",
235                                   "Age of current cache before redownloading tile",
236                                   FALSE  /* default value */,
237                                   G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
238         g_object_class_install_property (object_class, PROP_CHECK_FILE_SERVER_TIME, pspec);
239
240         g_type_class_add_private (klass, sizeof (VikSlippyMapSourcePrivate));
241         
242         object_class->finalize = vik_slippy_map_source_finalize;
243 }
244
245 /* 1 << (x) is like a 2**(x) */
246 #define GZ(x) ((1<<x))
247
248 static const gdouble scale_mpps[] = { GZ(0), GZ(1), GZ(2), GZ(3), GZ(4), GZ(5), GZ(6), GZ(7), GZ(8), GZ(9),
249                                            GZ(10), GZ(11), GZ(12), GZ(13), GZ(14), GZ(15), GZ(16), GZ(17) };
250
251 static const gint num_scales = (sizeof(scale_mpps) / sizeof(scale_mpps[0]));
252
253 static const gdouble scale_neg_mpps[] = { 1.0/GZ(0), 1.0/GZ(1), 1.0/GZ(2), 1.0/GZ(3) };
254 static const gint num_scales_neg = (sizeof(scale_neg_mpps) / sizeof(scale_neg_mpps[0]));
255
256 #define ERROR_MARGIN 0.01
257 static gint slippy_zoom ( gdouble mpp ) {
258   gint i;
259   for ( i = 0; i < num_scales; i++ ) {
260     if ( ABS(scale_mpps[i] - mpp) < ERROR_MARGIN ) {
261       return i;
262     }
263   }
264   for ( i = 0; i < num_scales_neg; i++ ) {
265     if ( ABS(scale_neg_mpps[i] - mpp) < 0.000001 ) {
266       return -i;
267     }
268   }
269
270   return 255;
271 }
272
273 gchar *
274 vik_slippy_map_source_get_uri( VikSlippyMapSource *self, MapCoord *src )
275 {
276         VikSlippyMapSourceClass *klass;
277         g_return_val_if_fail (self != NULL, 0);
278         g_return_val_if_fail (VIK_IS_SLIPPY_MAP_SOURCE (self), 0);
279         klass = VIK_SLIPPY_MAP_SOURCE_GET_CLASS(self);
280
281         g_return_val_if_fail (klass->get_uri != NULL, 0);
282
283         return (*klass->get_uri)(self, src);
284 }
285
286 gchar *
287 vik_slippy_map_source_get_hostname( VikSlippyMapSource *self )
288 {
289         VikSlippyMapSourceClass *klass;
290         g_return_val_if_fail (self != NULL, 0);
291         g_return_val_if_fail (VIK_IS_SLIPPY_MAP_SOURCE (self), 0);
292         klass = VIK_SLIPPY_MAP_SOURCE_GET_CLASS(self);
293
294         g_return_val_if_fail (klass->get_hostname != NULL, 0);
295
296         return (*klass->get_hostname)(self);
297 }
298
299 DownloadOptions *
300 vik_slippy_map_source_get_download_options( VikSlippyMapSource *self )
301 {
302         VikSlippyMapSourceClass *klass;
303         g_return_val_if_fail (self != NULL, 0);
304         g_return_val_if_fail (VIK_IS_SLIPPY_MAP_SOURCE (self), 0);
305         klass = VIK_SLIPPY_MAP_SOURCE_GET_CLASS(self);
306
307         g_return_val_if_fail (klass->get_download_options != NULL, 0);
308
309         return (*klass->get_download_options)(self);
310 }
311
312 gboolean
313 _supports_if_modified_since (VikMapSource *self)
314 {
315         g_return_val_if_fail (VIK_IS_SLIPPY_MAP_SOURCE(self), FALSE);
316         
317     VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE(self);
318         
319         return priv->options.check_file_server_time;
320 }
321 static gboolean
322 _coord_to_mapcoord ( VikMapSource *self, const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest )
323 {
324   g_assert ( src->mode == VIK_COORD_LATLON );
325
326   if ( xzoom != yzoom )
327     return FALSE;
328
329   dest->scale = slippy_zoom ( xzoom );
330   if ( dest->scale == 255 )
331     return FALSE;
332
333   dest->x = (src->east_west + 180) / 360 * GZ(17) / xzoom;
334   dest->y = (180 - MERCLAT(src->north_south)) / 360 * GZ(17) / xzoom;
335   dest->z = 0;
336
337   return TRUE;
338 }
339
340 static void
341 _mapcoord_to_center_coord ( VikMapSource *self, MapCoord *src, VikCoord *dest )
342 {
343   gdouble socalled_mpp;
344   if (src->scale >= 0)
345     socalled_mpp = GZ(src->scale);
346   else
347     socalled_mpp = 1.0/GZ(-src->scale);
348   dest->mode = VIK_COORD_LATLON;
349   dest->east_west = ((src->x+0.5) / GZ(17) * socalled_mpp * 360) - 180;
350   dest->north_south = DEMERCLAT(180 - ((src->y+0.5) / GZ(17) * socalled_mpp * 360));
351 }
352
353 static int
354 _download ( VikMapSource *self, MapCoord *src, const gchar *dest_fn, void *handle )
355 {
356    int res;
357    gchar *uri = vik_slippy_map_source_get_uri(VIK_SLIPPY_MAP_SOURCE(self), src);
358    gchar *host = vik_slippy_map_source_get_hostname(VIK_SLIPPY_MAP_SOURCE(self));
359    DownloadOptions *options = vik_slippy_map_source_get_download_options(VIK_SLIPPY_MAP_SOURCE(self));
360    res = a_http_download_get_url ( host, uri, dest_fn, options, handle );
361    g_free ( uri );
362    g_free ( host );
363    return res;
364 }
365
366 static void *
367 _download_handle_init ( VikMapSource *self )
368 {
369    return a_download_handle_init ();
370 }
371
372
373 static void
374 _download_handle_cleanup ( VikMapSource *self, void *handle )
375 {
376    return a_download_handle_cleanup ( handle );
377 }
378
379 static gchar *
380 _get_uri( VikSlippyMapSource *self, MapCoord *src )
381 {
382         g_return_val_if_fail (VIK_IS_SLIPPY_MAP_SOURCE(self), NULL);
383         
384     VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE(self);
385         gchar *uri = g_strdup_printf (priv->url, 17 - src->scale, src->x, src->y);
386         return uri;
387
388
389 static gchar *
390 _get_hostname( VikSlippyMapSource *self )
391 {
392         g_return_val_if_fail (VIK_IS_SLIPPY_MAP_SOURCE(self), NULL);
393         
394     VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE(self);
395         return g_strdup( priv->hostname );
396 }
397
398 static DownloadOptions *
399 _get_download_options( VikSlippyMapSource *self )
400 {
401         g_return_val_if_fail (VIK_IS_SLIPPY_MAP_SOURCE(self), NULL);
402         
403         VikSlippyMapSourcePrivate *priv = VIK_SLIPPY_MAP_SOURCE_PRIVATE(self);
404         return &(priv->options);
405 }
406
407 VikSlippyMapSource *
408 vik_slippy_map_source_new_with_id (guint8 id, const gchar *label, const gchar *hostname, const gchar *url)
409 {
410         return g_object_new(VIK_TYPE_SLIPPY_MAP_SOURCE,
411                             "id", id, "label", label, "hostname", hostname, "url", url, NULL);
412 }