]> git.street.me.uk Git - andy/viking.git/blob - src/vikroutingwebengine.c
Fix display of map tile source information when it has extended characters.
[andy/viking.git] / src / vikroutingwebengine.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (c) 2013, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 /**
23  * SECTION:vikroutingwebengine
24  * @short_description: A generic class for WEB based routing engine
25  * 
26  * The #VikRoutingWebEngine class handles WEB based
27  * routing engine.
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include <string.h>
35
36 #include <glib.h>
37 #include <glib/gstdio.h>
38
39 #include "babel.h"
40
41 #include "vikroutingwebengine.h"
42
43 static void vik_routing_web_engine_finalize ( GObject *gob );
44
45 static gboolean vik_routing_web_engine_find ( VikRoutingEngine *self, VikTrwLayer *vtl, struct LatLon start, struct LatLon end );
46 static gchar *vik_routing_web_engine_get_cmd_from_directions(VikRoutingEngine *self, const gchar *start, const gchar *end);
47 static gboolean vik_routing_web_engine_supports_direction(VikRoutingEngine *self);
48 static gboolean vik_routing_web_engine_refine ( VikRoutingEngine *self, VikTrwLayer *vtl, VikTrack *vt );
49 static gboolean vik_routing_web_engine_supports_refine ( VikRoutingEngine *self );
50
51 typedef struct _VikRoutingWebEnginePrivate VikRoutingWebEnginePrivate;
52 struct _VikRoutingWebEnginePrivate
53 {
54         gchar *url_base;
55         
56         /* LatLon */
57         gchar *url_start_ll_fmt;
58         gchar *url_stop_ll_fmt;
59         gchar *url_via_ll_fmt;
60
61         /* Directions */
62         gchar *url_start_dir_fmt;
63         gchar *url_stop_dir_fmt;
64
65         DownloadMapOptions options;
66 };
67
68 #define VIK_ROUTING_WEB_ENGINE_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), VIK_ROUTING_WEB_ENGINE_TYPE, VikRoutingWebEnginePrivate))
69
70 /* properties */
71 enum
72 {
73   PROP_0,
74
75   PROP_URL_BASE,
76   
77   /* LatLon */
78   PROP_URL_START_LL,
79   PROP_URL_STOP_LL,
80   PROP_URL_VIA_LL,
81
82   /* Direction */
83   PROP_URL_START_DIR,
84   PROP_URL_STOP_DIR,
85
86   PROP_REFERER,
87   PROP_FOLLOW_LOCATION,
88 };
89
90 G_DEFINE_TYPE (VikRoutingWebEngine, vik_routing_web_engine, VIK_ROUTING_ENGINE_TYPE)
91
92 static void
93 vik_routing_web_engine_set_property (GObject      *object,
94                           guint         property_id,
95                           const GValue *value,
96                           GParamSpec   *pspec)
97 {
98   VikRoutingWebEnginePrivate *priv = VIK_ROUTING_WEB_ENGINE_PRIVATE ( object );
99
100   switch (property_id)
101     {
102     case PROP_URL_BASE:
103       g_free (priv->url_base);
104       priv->url_base = g_strdup(g_value_get_string (value));
105       break;
106
107     case PROP_URL_START_LL:
108       g_free (priv->url_start_ll_fmt);
109       priv->url_start_ll_fmt = g_strdup(g_value_get_string (value));
110       break;
111
112     case PROP_URL_STOP_LL:
113       g_free (priv->url_stop_ll_fmt);
114       priv->url_stop_ll_fmt = g_strdup(g_value_get_string (value));
115       break;
116
117     case PROP_URL_VIA_LL:
118       g_free (priv->url_via_ll_fmt);
119       priv->url_via_ll_fmt = g_strdup(g_value_get_string (value));
120       break;
121
122     case PROP_URL_START_DIR:
123       g_free (priv->url_start_dir_fmt);
124       priv->url_start_dir_fmt = g_strdup(g_value_get_string (value));
125       break;
126
127     case PROP_URL_STOP_DIR:
128       g_free (priv->url_stop_dir_fmt);
129       priv->url_stop_dir_fmt = g_strdup(g_value_get_string (value));
130       break;
131
132     case PROP_REFERER:
133       g_free (priv->options.referer);
134       priv->options.referer = g_value_dup_string (value);
135       break;
136
137     case PROP_FOLLOW_LOCATION:
138       priv->options.follow_location = g_value_get_long (value);
139       break;
140
141     default:
142       /* We don't have any other property... */
143       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
144       break;
145     }
146 }
147
148 static void
149 vik_routing_web_engine_get_property (GObject    *object,
150                           guint       property_id,
151                           GValue     *value,
152                           GParamSpec *pspec)
153 {
154   VikRoutingWebEnginePrivate *priv = VIK_ROUTING_WEB_ENGINE_PRIVATE ( object );
155
156   switch (property_id)
157     {
158     case PROP_URL_BASE:
159       g_value_set_string (value, priv->url_base);
160       break;
161
162     case PROP_URL_START_LL:
163       g_value_set_string (value, priv->url_start_ll_fmt);
164       break;
165
166     case PROP_URL_STOP_LL:
167       g_value_set_string (value, priv->url_stop_ll_fmt);
168       break;
169
170     case PROP_URL_VIA_LL:
171       g_value_set_string (value, priv->url_via_ll_fmt);
172       break;
173
174     case PROP_URL_START_DIR:
175       g_value_set_string (value, priv->url_start_dir_fmt);
176       break;
177
178     case PROP_URL_STOP_DIR:
179       g_value_set_string (value, priv->url_stop_dir_fmt);
180       break;
181
182     case PROP_REFERER:
183       g_value_set_string (value, priv->options.referer);
184       break;
185
186     case PROP_FOLLOW_LOCATION:
187       g_value_set_long (value, priv->options.follow_location);
188       break;
189
190     default:
191       /* We don't have any other property... */
192       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
193       break;
194     }
195 }
196
197 static void vik_routing_web_engine_class_init ( VikRoutingWebEngineClass *klass )
198 {
199   GObjectClass *object_class;
200   VikRoutingEngineClass *parent_class;
201   GParamSpec *pspec = NULL;
202
203   object_class = G_OBJECT_CLASS (klass);
204
205   object_class->set_property = vik_routing_web_engine_set_property;
206   object_class->get_property = vik_routing_web_engine_get_property;
207   object_class->finalize = vik_routing_web_engine_finalize;
208
209   parent_class = VIK_ROUTING_ENGINE_CLASS (klass);
210
211   parent_class->find = vik_routing_web_engine_find;
212   parent_class->supports_direction = vik_routing_web_engine_supports_direction;
213   parent_class->get_cmd_from_directions = vik_routing_web_engine_get_cmd_from_directions;
214   parent_class->refine = vik_routing_web_engine_refine;
215   parent_class->supports_refine = vik_routing_web_engine_supports_refine;
216
217   /**
218    * VikRoutingWebEngine:url-base:
219    *
220    * The base URL of the routing engine.
221    */
222   pspec = g_param_spec_string ("url-base",
223                                "URL's base",
224                                "The base URL of the routing engine",
225                                "<no-set>" /* default value */,
226                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
227   g_object_class_install_property (object_class, PROP_URL_BASE, pspec);
228   
229
230   /**
231    * VikRoutingWebEngine:url-start-ll:
232    *
233    * The part of the request hosting the end point.
234    */
235   pspec = g_param_spec_string ("url-start-ll",
236                                "Start part of the URL",
237                                "The part of the request hosting the start point",
238                                "<no-set>" /* default value */,
239                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
240   g_object_class_install_property (object_class, PROP_URL_START_LL, pspec);
241     
242
243   /**
244    * VikRoutingWebEngine:url-stop-ll:
245    *
246    * The part of the request hosting the end point.
247    */
248   pspec = g_param_spec_string ("url-stop-ll",
249                                "Stop part of the URL",
250                                "The part of the request hosting the end point",
251                                "<no-set>" /* default value */,
252                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
253   g_object_class_install_property (object_class, PROP_URL_STOP_LL, pspec);
254
255
256   /**
257    * VikRoutingWebEngine:url-via-ll:
258    *
259    * The param of the request for setting a via point.
260    */
261   pspec = g_param_spec_string ("url-via-ll",
262                                "Via part of the URL",
263                                "The param of the request for setting a via point",
264                                NULL /* default value */,
265                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
266   g_object_class_install_property (object_class, PROP_URL_VIA_LL, pspec);
267
268
269   /**
270    * VikRoutingWebEngine:url-start-dir:
271    *
272    * The part of the request hosting the end point.
273    */
274   pspec = g_param_spec_string ("url-start-dir",
275                                "Start part of the URL",
276                                "The part of the request hosting the start point",
277                                NULL /* default value */,
278                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
279   g_object_class_install_property (object_class, PROP_URL_START_DIR, pspec);
280     
281
282   /**
283    * VikRoutingWebEngine:url-stop-dir:
284    *
285    * The part of the request hosting the end point.
286    */
287   pspec = g_param_spec_string ("url-stop-dir",
288                                "Stop part of the URL",
289                                "The part of the request hosting the end point",
290                                NULL /* default value */,
291                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
292   g_object_class_install_property (object_class, PROP_URL_STOP_DIR, pspec);
293
294
295   /**
296    * VikRoutingWebEngine:referer:
297    *
298    * The REFERER string to use in HTTP request.
299    */
300   pspec = g_param_spec_string ("referer",
301                                "Referer",
302                                "The REFERER string to use in HTTP request",
303                                NULL /* default value */,
304                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
305   g_object_class_install_property (object_class, PROP_REFERER, pspec);
306
307
308   /**
309    * VikRoutingWebEngine:follow-location:
310    *
311    * Specifies the number of retries to follow a redirect while downloading a page.
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                              2  /* default value */,
319                              G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
320   g_object_class_install_property (object_class, PROP_FOLLOW_LOCATION, pspec);
321
322   g_type_class_add_private (klass, sizeof (VikRoutingWebEnginePrivate));
323 }
324
325 static void vik_routing_web_engine_init ( VikRoutingWebEngine *self )
326 {
327   VikRoutingWebEnginePrivate *priv = VIK_ROUTING_WEB_ENGINE_PRIVATE ( self );
328
329   priv->url_base = NULL;
330   
331   /* LatLon */
332   priv->url_start_ll_fmt = NULL;
333   priv->url_stop_ll_fmt = NULL;
334   priv->url_via_ll_fmt = NULL;
335
336   /* Directions */
337   priv->url_start_dir_fmt = NULL;
338   priv->url_stop_dir_fmt = NULL;
339
340   priv->options.referer = NULL;
341   priv->options.follow_location = 0;
342   priv->options.check_file = NULL;
343   priv->options.check_file_server_time = FALSE;
344   priv->options.use_etag = FALSE;
345 }
346
347 static void vik_routing_web_engine_finalize ( GObject *gob )
348 {
349   VikRoutingWebEnginePrivate *priv = VIK_ROUTING_WEB_ENGINE_PRIVATE ( gob );
350
351   g_free (priv->url_base);
352   priv->url_base = NULL;
353   
354   /* LatLon */
355   g_free (priv->url_start_ll_fmt);
356   priv->url_start_ll_fmt = NULL;
357   g_free (priv->url_stop_ll_fmt);
358   priv->url_stop_ll_fmt = NULL;
359   g_free (priv->url_via_ll_fmt);
360   priv->url_via_ll_fmt = NULL;
361
362   /* Directions */
363   g_free (priv->url_start_dir_fmt);
364   priv->url_start_dir_fmt = NULL;
365   g_free (priv->url_stop_dir_fmt);
366   priv->url_stop_dir_fmt = NULL;
367
368   g_free (priv->options.referer);
369   priv->options.referer = NULL;
370
371   G_OBJECT_CLASS (vik_routing_web_engine_parent_class)->finalize(gob);
372 }
373
374 static DownloadMapOptions *
375 vik_routing_web_engine_get_download_options ( VikRoutingEngine *self )
376 {
377         g_return_val_if_fail (VIK_IS_ROUTING_WEB_ENGINE(self), NULL);
378         
379         VikRoutingWebEnginePrivate *priv = VIK_ROUTING_WEB_ENGINE_PRIVATE(self);
380         
381         return &(priv->options);
382 }
383
384 static gchar *
385 substitute_latlon ( const gchar *fmt, struct LatLon ll )
386 {
387         gchar lat[G_ASCII_DTOSTR_BUF_SIZE], lon[G_ASCII_DTOSTR_BUF_SIZE];
388         gchar *substituted = g_strdup_printf(fmt,
389                           g_ascii_dtostr (lat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) ll.lat),
390                           g_ascii_dtostr (lon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) ll.lon));
391         return substituted;
392 }
393
394 static gchar *
395 vik_routing_web_engine_get_url_for_coords ( VikRoutingEngine *self, struct LatLon start, struct LatLon end )
396 {
397         gchar *startURL;
398         gchar *endURL;
399         gchar *url;
400         
401         g_return_val_if_fail ( VIK_IS_ROUTING_WEB_ENGINE (self), NULL);
402
403         VikRoutingWebEnginePrivate *priv = VIK_ROUTING_WEB_ENGINE_PRIVATE ( self );
404
405         g_return_val_if_fail ( priv->url_base != NULL, NULL);
406         g_return_val_if_fail ( priv->url_start_ll_fmt != NULL, NULL);
407         g_return_val_if_fail ( priv->url_stop_ll_fmt != NULL, NULL);
408
409         startURL = substitute_latlon ( priv->url_start_ll_fmt, start );
410         endURL = substitute_latlon ( priv->url_stop_ll_fmt, end );
411         url = g_strconcat ( priv->url_base, startURL, endURL, NULL );
412
413         /* Free memory */
414         g_free ( startURL );
415         g_free ( endURL );
416
417         return url;
418 }
419
420 static gboolean
421 vik_routing_web_engine_find ( VikRoutingEngine *self, VikTrwLayer *vtl, struct LatLon start, struct LatLon end )
422 {
423   gchar *uri = vik_routing_web_engine_get_url_for_coords(self, start, end);
424
425   DownloadMapOptions *options = vik_routing_web_engine_get_download_options(self);
426   
427   gchar *format = vik_routing_engine_get_format ( self );
428   gboolean ret = a_babel_convert_from_url ( vtl, uri, format, NULL, NULL, options );
429
430   g_free(uri);
431
432   return ret;
433 }
434
435 static gchar *
436 vik_routing_web_engine_get_cmd_from_directions ( VikRoutingEngine *self, const gchar *start, const gchar *end )
437 {
438   g_return_val_if_fail ( VIK_IS_ROUTING_WEB_ENGINE (self), NULL);
439
440   VikRoutingWebEnginePrivate *priv = VIK_ROUTING_WEB_ENGINE_PRIVATE ( self );
441
442   g_return_val_if_fail ( priv->url_base != NULL, NULL);
443   g_return_val_if_fail ( priv->url_start_dir_fmt != NULL, NULL);
444   g_return_val_if_fail ( priv->url_stop_dir_fmt != NULL, NULL);
445
446   gchar *from_quoted, *to_quoted;
447   gchar **from_split, **to_split;
448   from_quoted = g_shell_quote ( start );
449   to_quoted = g_shell_quote ( end );
450
451   from_split = g_strsplit( from_quoted, " ", 0);
452   to_split = g_strsplit( to_quoted, " ", 0);
453
454   from_quoted = g_strjoinv( "%20", from_split);
455   to_quoted = g_strjoinv( "%20", to_split);
456
457   gchar *url_fmt = g_strconcat ( priv->url_base, priv->url_start_dir_fmt, priv->url_stop_dir_fmt, NULL );
458   gchar *url = g_strdup_printf ( url_fmt, from_quoted, to_quoted );
459
460   g_free ( url_fmt );
461
462   g_free(from_quoted);
463   g_free(to_quoted);
464   g_strfreev(from_split);
465   g_strfreev(to_split);
466
467   return url;
468 }
469
470 static gboolean
471 vik_routing_web_engine_supports_direction ( VikRoutingEngine *self )
472 {
473   g_return_val_if_fail ( VIK_IS_ROUTING_WEB_ENGINE (self), FALSE);
474
475   VikRoutingWebEnginePrivate *priv = VIK_ROUTING_WEB_ENGINE_PRIVATE ( self );
476
477   return (priv->url_start_dir_fmt) != NULL;
478 }
479
480 struct _append_ctx {
481   VikRoutingWebEnginePrivate *priv;
482   gchar **urlParts;
483   int nb;
484 };
485
486 static void
487 _append_stringified_coords ( gpointer data, gpointer user_data )
488 {
489   VikTrackpoint *vtp = (VikTrackpoint*)data;
490   struct _append_ctx *ctx = (struct _append_ctx*)user_data;
491   
492   /* Stringify coordinate */
493   struct LatLon position;
494   vik_coord_to_latlon ( &(vtp->coord), &position );
495   gchar *string = substitute_latlon ( ctx->priv->url_via_ll_fmt, position );
496   
497   /* Append */
498   ctx->urlParts[ctx->nb] = string;
499   ctx->nb++;
500 }
501
502 static gchar *
503 vik_routing_web_engine_get_url_for_track ( VikRoutingEngine *self, VikTrack *vt )
504 {
505   gchar **urlParts;
506   gchar *url;
507
508   VikRoutingWebEnginePrivate *priv = VIK_ROUTING_WEB_ENGINE_PRIVATE ( self );
509
510   g_return_val_if_fail ( priv->url_base != NULL, NULL );
511   g_return_val_if_fail ( priv->url_start_ll_fmt != NULL, NULL );
512   g_return_val_if_fail ( priv->url_stop_ll_fmt != NULL, NULL );
513   g_return_val_if_fail ( priv->url_via_ll_fmt != NULL, NULL );
514
515   /* Init temporary storage */
516   gsize len = 1 + g_list_length ( vt->trackpoints ) + 1; /* base + trackpoints + NULL */
517   urlParts = g_malloc ( sizeof(gchar*)*len );
518   urlParts[0] = g_strdup ( priv->url_base );
519   urlParts[len-1] = NULL;
520
521   struct _append_ctx ctx;
522   ctx.priv = priv;
523   ctx.urlParts = urlParts;
524   ctx.nb = 1; /* First cell available, previous used for base URL */
525
526   /* Append all trackpoints to URL */
527   g_list_foreach ( vt->trackpoints, _append_stringified_coords, &ctx );
528
529   /* Override first and last positions with associated formats */
530   struct LatLon position;
531   VikTrackpoint *vtp;
532   g_free ( urlParts[1] );
533   vtp = g_list_first ( vt->trackpoints )->data;
534   vik_coord_to_latlon ( &(vtp->coord ), &position );
535   urlParts[1] = substitute_latlon ( priv->url_start_ll_fmt, position );
536   g_free ( urlParts[len-2] );
537   vtp = g_list_last ( vt->trackpoints )->data;
538   vik_coord_to_latlon ( &(vtp->coord), &position );
539   urlParts[len-2] = substitute_latlon ( priv->url_stop_ll_fmt, position );
540
541   /* Concat */
542   url = g_strjoinv ( NULL, urlParts );
543   g_debug ( "%s: %s", __FUNCTION__, url );
544
545   /* Free */
546   g_strfreev ( urlParts );
547
548   return url;
549 }
550
551 static gboolean
552 vik_routing_web_engine_refine ( VikRoutingEngine *self, VikTrwLayer *vtl, VikTrack *vt )
553 {
554   /* Compute URL */
555   gchar *uri = vik_routing_web_engine_get_url_for_track ( self, vt );
556
557   /* Download data */
558   DownloadMapOptions *options = vik_routing_web_engine_get_download_options ( self );
559
560   /* Convert and insert data in model */
561   gchar *format = vik_routing_engine_get_format ( self );
562   gboolean ret = a_babel_convert_from_url ( vtl, uri, format, NULL, NULL, options );
563
564   g_free(uri);
565
566   return ret;
567 }
568
569 static gboolean
570 vik_routing_web_engine_supports_refine ( VikRoutingEngine *self )
571 {
572   g_return_val_if_fail ( VIK_IS_ROUTING_WEB_ENGINE (self), FALSE);
573
574   VikRoutingWebEnginePrivate *priv = VIK_ROUTING_WEB_ENGINE_PRIVATE ( self );
575
576   return priv->url_via_ll_fmt != NULL;
577 }