]> git.street.me.uk Git - andy/viking.git/blob - src/vikwebtool_datasource.c
Ensure calculation of the waypoint bounds even when the layer is not currently visible.
[andy/viking.git] / src / vikwebtool_datasource.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4  *
5  * Copyright (C) 2013-2015, Rob Norris <rw_norris@hotmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "vikwebtool_datasource.h"
28 #include <ctype.h>
29 #include <string.h>
30
31 #include <glib.h>
32 #include <glib/gi18n.h>
33
34 #include "globals.h"
35 #include "acquire.h"
36 #include "maputils.h"
37 #include "dialog.h"
38
39 static GObjectClass *parent_class;
40 static GHashTable *last_user_strings = NULL;
41
42 static void webtool_datasource_finalize ( GObject *gob );
43
44 static gchar *webtool_datasource_get_url ( VikWebtool *self, VikWindow *vw );
45
46 static gboolean webtool_needs_user_string ( VikWebtool *self );
47
48 typedef struct _VikWebtoolDatasourcePrivate VikWebtoolDatasourcePrivate;
49
50 struct _VikWebtoolDatasourcePrivate
51 {
52         gchar *url;
53         gchar *url_format_code;
54         gchar *file_type;
55         gchar *babel_filter_args;
56     gchar *input_label;
57         gchar *user_string;
58 };
59
60 #define WEBTOOL_DATASOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
61                                            VIK_WEBTOOL_DATASOURCE_TYPE,      \
62                                            VikWebtoolDatasourcePrivate))
63
64 G_DEFINE_TYPE (VikWebtoolDatasource, vik_webtool_datasource, VIK_WEBTOOL_TYPE)
65
66 enum
67 {
68         PROP_0,
69         PROP_URL,
70         PROP_URL_FORMAT_CODE,
71         PROP_FILE_TYPE,
72         PROP_BABEL_FILTER_ARGS,
73     PROP_INPUT_LABEL
74 };
75
76 static void webtool_datasource_set_property (GObject      *object,
77                                              guint         property_id,
78                                              const GValue *value,
79                                              GParamSpec   *pspec)
80 {
81         VikWebtoolDatasource *self = VIK_WEBTOOL_DATASOURCE ( object );
82         VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
83
84         switch ( property_id ) {
85         
86     case PROP_URL:
87                 g_free ( priv->url );
88                 priv->url = g_value_dup_string ( value );
89                 g_debug ( "VikWebtoolDatasource.url: %s", priv->url );
90                 break;
91
92         case PROP_URL_FORMAT_CODE:
93                 g_free ( priv->url_format_code );
94                 priv->url_format_code = g_value_dup_string ( value );
95                 g_debug ( "VikWebtoolDatasource.url_format_code: %s", priv->url_format_code );
96                 break;
97
98         case PROP_FILE_TYPE:
99                 g_free ( priv->file_type );
100                 priv->file_type = g_value_dup_string ( value );
101                 g_debug ( "VikWebtoolDatasource.file_type: %s", priv->url_format_code );
102                 break;
103
104         case PROP_BABEL_FILTER_ARGS:
105                 g_free ( priv->babel_filter_args );
106                 priv->babel_filter_args = g_value_dup_string ( value );
107                 g_debug ( "VikWebtoolDatasource.babel_filter_args: %s", priv->babel_filter_args );
108                 break;
109
110     case PROP_INPUT_LABEL:
111                 g_free ( priv->input_label );
112                 priv->input_label = g_value_dup_string ( value );
113                 g_debug ( "VikWebtoolDatasource.input_label: %s", priv->input_label );
114                 break;
115
116         default:
117                 /* We don't have any other property... */
118                 G_OBJECT_WARN_INVALID_PROPERTY_ID ( object, property_id, pspec );
119                 break;
120         }
121 }
122
123 static void webtool_datasource_get_property (GObject    *object,
124                                              guint       property_id,
125                                              GValue     *value,
126                                              GParamSpec *pspec)
127 {
128         VikWebtoolDatasource *self = VIK_WEBTOOL_DATASOURCE ( object );
129         VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
130
131         switch ( property_id ) {
132
133         case PROP_URL:               g_value_set_string ( value, priv->url ); break;
134         case PROP_URL_FORMAT_CODE:       g_value_set_string ( value, priv->url_format_code ); break;
135         case PROP_FILE_TYPE:         g_value_set_string ( value, priv->url ); break;
136         case PROP_BABEL_FILTER_ARGS: g_value_set_string ( value, priv->babel_filter_args ); break;
137         case PROP_INPUT_LABEL:       g_value_set_string ( value, priv->input_label ); break;
138
139         default:
140                 /* We don't have any other property... */
141                 G_OBJECT_WARN_INVALID_PROPERTY_ID ( object, property_id, pspec );
142                 break;
143         }
144 }
145
146 typedef struct {
147         VikExtTool *self;
148         VikWindow *vw;
149         VikViewport *vvp;
150         GtkWidget *user_string;
151 } datasource_t;
152
153
154 static void ensure_last_user_strings_hash() {
155     if ( last_user_strings == NULL ) {
156         last_user_strings = g_hash_table_new_full ( g_str_hash, 
157                                                     g_str_equal,
158                                                     g_free,
159                                                     g_free ); 
160     }
161 }
162
163
164 static gchar *get_last_user_string ( const datasource_t *source ) {
165     ensure_last_user_strings_hash();
166     gchar *label = vik_ext_tool_get_label ( source->self );
167     gchar *last_str = g_hash_table_lookup ( last_user_strings, label );
168     g_free( label );
169     return last_str;
170 }
171
172
173 static void set_last_user_string ( const datasource_t *source, const gchar *s ) {
174     ensure_last_user_strings_hash();
175     g_hash_table_insert ( last_user_strings, 
176                           vik_ext_tool_get_label ( source->self ), 
177                           g_strdup ( s ) );
178 }
179
180 static gpointer datasource_init ( acq_vik_t *avt )
181 {
182         datasource_t *data = g_malloc(sizeof(*data));
183         data->self = avt->userdata;
184         data->vw = avt->vw;
185         data->vvp = avt->vvp;
186         data->user_string = NULL;
187         return data;
188 }
189
190 static void datasource_add_setup_widgets ( GtkWidget *dialog, VikViewport *vvp, gpointer user_data )
191 {
192         datasource_t *widgets = (datasource_t *)user_data;
193         VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( widgets->self );
194         GtkWidget *user_string_label;
195     gchar *label = g_strdup_printf( "%s:", priv->input_label );
196         user_string_label = gtk_label_new ( label );
197         widgets->user_string = gtk_entry_new ( );
198
199     gchar *last_str = get_last_user_string ( widgets );
200     if ( last_str )
201         gtk_entry_set_text( GTK_ENTRY( widgets->user_string ), last_str );
202
203         // 'ok' when press return in the entry
204         g_signal_connect_swapped (widgets->user_string, "activate", G_CALLBACK(a_dialog_response_accept), dialog);
205
206         /* Packing all widgets */
207         GtkBox *box = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
208         gtk_box_pack_start ( box, user_string_label, FALSE, FALSE, 5 );
209         gtk_box_pack_start ( box, widgets->user_string, FALSE, FALSE, 5 );
210         gtk_widget_show_all ( dialog );
211         gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
212         // NB presently the focus is overridden later on by the acquire.c code.
213         gtk_widget_grab_focus ( widgets->user_string );
214
215     g_free ( label );
216 }
217
218
219
220 static void datasource_get_process_options ( gpointer user_data, ProcessOptions *po, DownloadMapOptions *options, const gchar *notused1, const gchar *notused2 )
221 {
222         datasource_t *data = (datasource_t*) user_data;
223
224         VikWebtool *vwd = VIK_WEBTOOL ( data->self );
225         VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( data->self );
226
227         if ( webtool_needs_user_string ( vwd ) ) {
228                 priv->user_string = g_strdup ( gtk_entry_get_text ( GTK_ENTRY ( data->user_string ) ) );
229
230         if ( priv->user_string[0] != '\0' ) {
231             set_last_user_string ( data, priv->user_string );
232         }
233     }
234
235         gchar *url = vik_webtool_get_url ( vwd, data->vw );
236         g_debug ("%s: %s", __FUNCTION__, url );
237
238         po->url = g_strdup ( url );
239
240         // Only use first section of the file_type string
241         // One can't use values like 'kml -x transform,rte=wpt' in order to do fancy things
242         //  since it won't be in the right order for the overall GPSBabel command
243         // So prevent any potentially dangerous behaviour
244         gchar **parts = NULL;
245         if ( priv->file_type )
246                 parts = g_strsplit ( priv->file_type, " ", 0);
247         if ( parts )
248                 po->input_file_type = g_strdup ( parts[0] );
249         else
250                 po->input_file_type = NULL;
251         g_strfreev ( parts );
252
253         options = NULL;
254         po->babel_filters = priv->babel_filter_args;
255 }
256
257 static void cleanup ( gpointer data )
258 {
259         g_free ( data );
260 }
261
262 static void webtool_datasource_open ( VikExtTool *self, VikWindow *vw )
263 {
264         gboolean search = webtool_needs_user_string ( VIK_WEBTOOL ( self ) );
265
266         // Use VikDataSourceInterface to give thready goodness controls of downloading stuff (i.e. can cancel the request)
267
268         // Can now create a 'VikDataSourceInterface' on the fly...
269         VikDataSourceInterface *vik_datasource_interface = g_malloc(sizeof(VikDataSourceInterface));
270
271         // An 'easy' way of assigning values
272         VikDataSourceInterface data = {
273                 vik_ext_tool_get_label (self),
274                 vik_ext_tool_get_label (self),
275                 VIK_DATASOURCE_ADDTOLAYER,
276                 VIK_DATASOURCE_INPUTTYPE_NONE,
277                 FALSE, // Maintain current view - rather than setting it to the acquired points
278                 TRUE,
279                 TRUE,
280                 (VikDataSourceInitFunc)               datasource_init,
281                 (VikDataSourceCheckExistenceFunc)     NULL,
282                 (VikDataSourceAddSetupWidgetsFunc)    (search ? datasource_add_setup_widgets : NULL),
283                 (VikDataSourceGetProcessOptionsFunc)  datasource_get_process_options,
284                 (VikDataSourceProcessFunc)            a_babel_convert_from,
285                 (VikDataSourceProgressFunc)           NULL,
286                 (VikDataSourceAddProgressWidgetsFunc) NULL,
287                 (VikDataSourceCleanupFunc)            cleanup,
288                 (VikDataSourceOffFunc)                NULL,
289                 NULL,
290                 0,
291                 NULL,
292                 NULL,
293                 0
294         };
295         memcpy ( vik_datasource_interface, &data, sizeof(VikDataSourceInterface) );
296
297         a_acquire ( vw, vik_window_layers_panel(vw), vik_window_viewport (vw), data.mode, vik_datasource_interface, self, cleanup );
298 }
299
300 static void vik_webtool_datasource_class_init ( VikWebtoolDatasourceClass *klass )
301 {
302         GObjectClass *gobject_class;
303         VikWebtoolClass *base_class;
304         GParamSpec *pspec;
305
306         gobject_class = G_OBJECT_CLASS (klass);
307
308         gobject_class->finalize = webtool_datasource_finalize;
309         gobject_class->set_property = webtool_datasource_set_property;
310         gobject_class->get_property = webtool_datasource_get_property;
311
312         pspec = g_param_spec_string ("url",
313                                      "Template URL",
314                                      "Set the template URL",
315                                      VIKING_URL /* default value */,
316                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
317         g_object_class_install_property (gobject_class,
318                                          PROP_URL,
319                                          pspec);
320
321         pspec = g_param_spec_string ("url_format_code",
322                                      "Template URL Format Code",
323                                      "Set the template URL format code",
324                                      "LRBT", // default value
325                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
326         g_object_class_install_property (gobject_class,
327                                          PROP_URL_FORMAT_CODE,
328                                          pspec);
329
330         pspec = g_param_spec_string ("file_type",
331                                      "The file type expected",
332                                      "Set the file type",
333                                      NULL, // default value ~ equates to internal GPX reading
334                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
335         g_object_class_install_property (gobject_class,
336                                          PROP_FILE_TYPE,
337                                          pspec);
338
339         pspec = g_param_spec_string ("babel_filter_args",
340                                      "The command line filter options to pass to gpsbabel",
341                                      "Set the command line filter options for gpsbabel",
342                                      NULL, // default value 
343                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
344         g_object_class_install_property (gobject_class,
345                                          PROP_BABEL_FILTER_ARGS,
346                                          pspec);
347
348         pspec = g_param_spec_string ("input_label",
349                                      "The label for the user input box if input is required.",
350                                      "Set the label to be shown next to the user input box if an input term is required",
351                                      _("Search Term"),
352                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
353         g_object_class_install_property (gobject_class,
354                                          PROP_INPUT_LABEL,
355                                          pspec);
356
357         parent_class = g_type_class_peek_parent (klass);
358
359         base_class = VIK_WEBTOOL_CLASS ( klass );
360         base_class->get_url = webtool_datasource_get_url;
361
362         // Override default open function here:
363         VikExtToolClass *ext_tool_class = VIK_EXT_TOOL_CLASS ( klass );
364         ext_tool_class->open = webtool_datasource_open;
365
366         g_type_class_add_private (klass, sizeof (VikWebtoolDatasourcePrivate));
367 }
368
369 VikWebtoolDatasource *vik_webtool_datasource_new ()
370 {
371         return VIK_WEBTOOL_DATASOURCE ( g_object_new ( VIK_WEBTOOL_DATASOURCE_TYPE, NULL ) );
372 }
373
374 VikWebtoolDatasource *vik_webtool_datasource_new_with_members ( const gchar *label,
375                                                                 const gchar *url,
376                                                                 const gchar *url_format_code,
377                                                                 const gchar *file_type,
378                                                                 const gchar *babel_filter_args,
379                                                                 const gchar *input_label )
380 {
381         VikWebtoolDatasource *result = VIK_WEBTOOL_DATASOURCE ( g_object_new ( VIK_WEBTOOL_DATASOURCE_TYPE,
382                                                                 "label", label,
383                                                                 "url", url,
384                                                                 "url_format_code", url_format_code,
385                                                                 "file_type", file_type,
386                                                                 "babel_filter_args", babel_filter_args,
387                                                             "input_label", input_label,
388                                                                 NULL ) );
389
390         return result;
391 }
392
393 static void vik_webtool_datasource_init ( VikWebtoolDatasource *self )
394 {
395         VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE (self);
396         priv->url = NULL;
397         priv->url_format_code = NULL;
398         priv->file_type = NULL;
399         priv->babel_filter_args = NULL;
400     priv->input_label = NULL;
401         priv->user_string = NULL;
402 }
403
404 static void webtool_datasource_finalize ( GObject *gob )
405 {
406         VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( gob );
407         g_free ( priv->url ); priv->url = NULL;
408         g_free ( priv->url_format_code ); priv->url_format_code = NULL;
409         g_free ( priv->file_type ); priv->file_type = NULL;
410         g_free ( priv->babel_filter_args ); priv->babel_filter_args = NULL;
411         g_free ( priv->input_label ); priv->input_label = NULL;
412         g_free ( priv->user_string); priv->user_string = NULL;
413         G_OBJECT_CLASS(parent_class)->finalize(gob);
414 }
415
416 #define MAX_NUMBER_CODES 7
417
418 /**
419  * Calculate individual elements (similarly to the VikWebtool Bounds & Center) for *all* potential values
420  * Then only values specified by the URL format are used in parameterizing the URL
421  */
422 static gchar *webtool_datasource_get_url ( VikWebtool *self, VikWindow *vw )
423 {
424         VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
425         VikViewport *viewport = vik_window_viewport ( vw );
426
427         // Get top left and bottom right lat/lon pairs from the viewport
428         gdouble min_lat, max_lat, min_lon, max_lon;
429         gchar sminlon[G_ASCII_DTOSTR_BUF_SIZE];
430         gchar smaxlon[G_ASCII_DTOSTR_BUF_SIZE];
431         gchar sminlat[G_ASCII_DTOSTR_BUF_SIZE];
432         gchar smaxlat[G_ASCII_DTOSTR_BUF_SIZE];
433         vik_viewport_get_min_max_lat_lon ( viewport, &min_lat, &max_lat, &min_lon, &max_lon );
434
435         // Cannot simply use g_strdup_printf and gdouble due to locale.
436         // As we compute an URL, we have to think in C locale.
437         g_ascii_dtostr (sminlon, G_ASCII_DTOSTR_BUF_SIZE, min_lon);
438         g_ascii_dtostr (smaxlon, G_ASCII_DTOSTR_BUF_SIZE, max_lon);
439         g_ascii_dtostr (sminlat, G_ASCII_DTOSTR_BUF_SIZE, min_lat);
440         g_ascii_dtostr (smaxlat, G_ASCII_DTOSTR_BUF_SIZE, max_lat);
441
442         // Center values
443         const VikCoord *coord = vik_viewport_get_center ( viewport );
444         struct LatLon ll;
445         vik_coord_to_latlon ( coord, &ll );
446
447         gchar scenterlat[G_ASCII_DTOSTR_BUF_SIZE];
448         gchar scenterlon[G_ASCII_DTOSTR_BUF_SIZE];
449         g_ascii_dtostr (scenterlat, G_ASCII_DTOSTR_BUF_SIZE, ll.lat);
450         g_ascii_dtostr (scenterlon, G_ASCII_DTOSTR_BUF_SIZE, ll.lon);
451
452         guint8 zoom = 17; // A zoomed in default
453         // zoom - ideally x & y factors need to be the same otherwise use the default
454         if ( vik_viewport_get_xmpp ( viewport ) == vik_viewport_get_ympp ( viewport ) )
455                 zoom = map_utils_mpp_to_zoom_level ( vik_viewport_get_zoom ( viewport ) );
456
457         gchar szoom[G_ASCII_DTOSTR_BUF_SIZE];
458         g_snprintf ( szoom, G_ASCII_DTOSTR_BUF_SIZE, "%d", zoom );
459
460         gint len = 0;
461         if ( priv->url_format_code )
462                 len = strlen ( priv->url_format_code );
463         if ( len > MAX_NUMBER_CODES )
464                 len = MAX_NUMBER_CODES;
465
466         gchar* values[MAX_NUMBER_CODES];
467         int i;
468         for ( i = 0; i < MAX_NUMBER_CODES; i++ ) {
469                 values[i] = '\0';
470         }
471
472         for ( i = 0; i < len; i++ ) {
473                 switch ( g_ascii_toupper ( priv->url_format_code[i] ) ) {
474                 case 'L': values[i] = g_strdup ( sminlon ); break;
475                 case 'R': values[i] = g_strdup ( smaxlon ); break;
476                 case 'B': values[i] = g_strdup ( sminlat ); break;
477                 case 'T': values[i] = g_strdup ( smaxlat ); break;
478                 case 'A': values[i] = g_strdup ( scenterlat ); break;
479                 case 'O': values[i] = g_strdup ( scenterlon ); break;
480                 case 'Z': values[i] = g_strdup ( szoom ); break;
481                 case 'S': values[i] = g_strdup ( priv->user_string ); break;
482                 default: break;
483                 }
484         }
485
486         gchar *url = g_strdup_printf ( priv->url, values[0], values[1], values[2], values[3], values[4], values[5], values[6] );
487
488         for ( i = 0; i < MAX_NUMBER_CODES; i++ ) {
489                 if ( values[i] != '\0' )
490                         g_free ( values[i] );
491         }
492         
493         return url;
494 }
495
496 // NB Only works for ascii strings
497 char* strcasestr2(const char *dst, const char *src)
498 {
499         if ( !dst || !src )
500                 return NULL;
501
502         if(src[0] == '\0')
503                 return (char*)dst;
504
505         int len = strlen(src) - 1;
506         char sc = tolower(src[0]);
507         for(char dc = *dst; (dc = *dst); dst++) {
508                 dc = tolower(dc);
509                 if(sc == dc && (len == 0 || !strncasecmp(dst+1, src+1, len)))
510                         return (char*)dst;
511         }
512
513         return NULL;
514 }
515
516 /**
517  * Returns true if the URL format contains 'S' -- that is, a search term entry
518  * box needs to be displayed
519  */
520 static gboolean webtool_needs_user_string ( VikWebtool *self )
521 {
522         VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
523         // For some reason (my) Windows build gets built with -D_GNU_SOURCE
524 #if (_GNU_SOURCE && !WINDOWS)
525         return (strcasestr(priv->url_format_code, "S") != NULL);
526 #else
527         return (strcasestr2(priv->url_format_code, "S") != NULL);
528 #endif
529 }