]> git.street.me.uk Git - andy/viking.git/blame_incremental - src/vikwebtool_datasource.c
Carto CSS support to generate Mapnik XML.
[andy/viking.git] / src / vikwebtool_datasource.c
... / ...
CommitLineData
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, 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
39static GObjectClass *parent_class;
40static GHashTable *last_user_strings = NULL;
41
42static void webtool_datasource_finalize ( GObject *gob );
43
44static gchar *webtool_datasource_get_url ( VikWebtool *self, VikWindow *vw );
45
46static gboolean webtool_needs_user_string ( VikWebtool *self );
47
48typedef struct _VikWebtoolDatasourcePrivate VikWebtoolDatasourcePrivate;
49
50struct _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
64G_DEFINE_TYPE (VikWebtoolDatasource, vik_webtool_datasource, VIK_WEBTOOL_TYPE)
65
66enum
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
76static 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
123static 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
146typedef struct {
147 VikExtTool *self;
148 VikWindow *vw;
149 VikViewport *vvp;
150 GtkWidget *user_string;
151} datasource_t;
152
153
154static 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
164static 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
173static 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
180static 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
190static 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
220static void datasource_get_cmd_string ( gpointer user_data, gchar **cmd, gchar **extra, DownloadMapOptions *options )
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 *cmd = 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 *extra = g_strdup ( parts[0] );
249 else
250 *extra = NULL;
251 g_strfreev ( parts );
252
253 options = NULL;
254}
255
256static gboolean datasource_process ( VikTrwLayer *vtl, const gchar *cmd, const gchar *extra, BabelStatusFunc status_cb, acq_dialog_widgets_t *adw, DownloadMapOptions *options )
257{
258 datasource_t *data = (datasource_t *)adw->user_data;
259 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( data->self );
260 // Dependent on the ExtTool / what extra has been set to...
261 // When extra is NULL - then it interprets results as a GPX
262 gboolean result = a_babel_convert_from_url_filter ( vtl, cmd, extra, priv->babel_filter_args, status_cb, adw, options);
263 return result;
264}
265
266static void cleanup ( gpointer data )
267{
268 g_free ( data );
269}
270
271static void webtool_datasource_open ( VikExtTool *self, VikWindow *vw )
272{
273 gboolean search = webtool_needs_user_string ( VIK_WEBTOOL ( self ) );
274
275 // Use VikDataSourceInterface to give thready goodness controls of downloading stuff (i.e. can cancel the request)
276
277 // Can now create a 'VikDataSourceInterface' on the fly...
278 VikDataSourceInterface *vik_datasource_interface = g_malloc(sizeof(VikDataSourceInterface));
279
280 // An 'easy' way of assigning values
281 VikDataSourceInterface data = {
282 vik_ext_tool_get_label (self),
283 vik_ext_tool_get_label (self),
284 VIK_DATASOURCE_ADDTOLAYER,
285 VIK_DATASOURCE_INPUTTYPE_NONE,
286 FALSE, // Maintain current view - rather than setting it to the acquired points
287 TRUE,
288 TRUE,
289 (VikDataSourceInitFunc) datasource_init,
290 (VikDataSourceCheckExistenceFunc) NULL,
291 (VikDataSourceAddSetupWidgetsFunc) (search ? datasource_add_setup_widgets : NULL),
292 (VikDataSourceGetCmdStringFunc) datasource_get_cmd_string,
293 (VikDataSourceProcessFunc) datasource_process,
294 (VikDataSourceProgressFunc) NULL,
295 (VikDataSourceAddProgressWidgetsFunc) NULL,
296 (VikDataSourceCleanupFunc) cleanup,
297 (VikDataSourceOffFunc) NULL,
298 NULL,
299 0,
300 NULL,
301 NULL,
302 0
303 };
304 memcpy ( vik_datasource_interface, &data, sizeof(VikDataSourceInterface) );
305
306 a_acquire ( vw, vik_window_layers_panel(vw), vik_window_viewport (vw), data.mode, vik_datasource_interface, self, cleanup );
307}
308
309static void vik_webtool_datasource_class_init ( VikWebtoolDatasourceClass *klass )
310{
311 GObjectClass *gobject_class;
312 VikWebtoolClass *base_class;
313 GParamSpec *pspec;
314
315 gobject_class = G_OBJECT_CLASS (klass);
316
317 gobject_class->finalize = webtool_datasource_finalize;
318 gobject_class->set_property = webtool_datasource_set_property;
319 gobject_class->get_property = webtool_datasource_get_property;
320
321 pspec = g_param_spec_string ("url",
322 "Template URL",
323 "Set the template URL",
324 VIKING_URL /* default value */,
325 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
326 g_object_class_install_property (gobject_class,
327 PROP_URL,
328 pspec);
329
330 pspec = g_param_spec_string ("url_format_code",
331 "Template URL Format Code",
332 "Set the template URL format code",
333 "LRBT", // default value
334 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
335 g_object_class_install_property (gobject_class,
336 PROP_URL_FORMAT_CODE,
337 pspec);
338
339 pspec = g_param_spec_string ("file_type",
340 "The file type expected",
341 "Set the file type",
342 NULL, // default value ~ equates to internal GPX reading
343 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
344 g_object_class_install_property (gobject_class,
345 PROP_FILE_TYPE,
346 pspec);
347
348 pspec = g_param_spec_string ("babel_filter_args",
349 "The command line filter options to pass to gpsbabel",
350 "Set the command line filter options for gpsbabel",
351 NULL, // default value
352 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
353 g_object_class_install_property (gobject_class,
354 PROP_BABEL_FILTER_ARGS,
355 pspec);
356
357 pspec = g_param_spec_string ("input_label",
358 "The label for the user input box if input is required.",
359 "Set the label to be shown next to the user input box if an input term is required",
360 _("Search Term"),
361 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
362 g_object_class_install_property (gobject_class,
363 PROP_INPUT_LABEL,
364 pspec);
365
366 parent_class = g_type_class_peek_parent (klass);
367
368 base_class = VIK_WEBTOOL_CLASS ( klass );
369 base_class->get_url = webtool_datasource_get_url;
370
371 // Override default open function here:
372 VikExtToolClass *ext_tool_class = VIK_EXT_TOOL_CLASS ( klass );
373 ext_tool_class->open = webtool_datasource_open;
374
375 g_type_class_add_private (klass, sizeof (VikWebtoolDatasourcePrivate));
376}
377
378VikWebtoolDatasource *vik_webtool_datasource_new ()
379{
380 return VIK_WEBTOOL_DATASOURCE ( g_object_new ( VIK_WEBTOOL_DATASOURCE_TYPE, NULL ) );
381}
382
383VikWebtoolDatasource *vik_webtool_datasource_new_with_members ( const gchar *label,
384 const gchar *url,
385 const gchar *url_format_code,
386 const gchar *file_type,
387 const gchar *babel_filter_args,
388 const gchar *input_label )
389{
390 VikWebtoolDatasource *result = VIK_WEBTOOL_DATASOURCE ( g_object_new ( VIK_WEBTOOL_DATASOURCE_TYPE,
391 "label", label,
392 "url", url,
393 "url_format_code", url_format_code,
394 "file_type", file_type,
395 "babel_filter_args", babel_filter_args,
396 "input_label", input_label,
397 NULL ) );
398
399 return result;
400}
401
402static void vik_webtool_datasource_init ( VikWebtoolDatasource *self )
403{
404 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE (self);
405 priv->url = NULL;
406 priv->url_format_code = NULL;
407 priv->file_type = NULL;
408 priv->babel_filter_args = NULL;
409 priv->input_label = NULL;
410 priv->user_string = NULL;
411}
412
413static void webtool_datasource_finalize ( GObject *gob )
414{
415 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( gob );
416 g_free ( priv->url ); priv->url = NULL;
417 g_free ( priv->url_format_code ); priv->url_format_code = NULL;
418 g_free ( priv->file_type ); priv->file_type = NULL;
419 g_free ( priv->babel_filter_args ); priv->babel_filter_args = NULL;
420 g_free ( priv->input_label ); priv->input_label = NULL;
421 g_free ( priv->user_string); priv->user_string = NULL;
422 G_OBJECT_CLASS(parent_class)->finalize(gob);
423}
424
425#define MAX_NUMBER_CODES 7
426
427/**
428 * Calculate individual elements (similarly to the VikWebtool Bounds & Center) for *all* potential values
429 * Then only values specified by the URL format are used in parameterizing the URL
430 */
431static gchar *webtool_datasource_get_url ( VikWebtool *self, VikWindow *vw )
432{
433 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
434 VikViewport *viewport = vik_window_viewport ( vw );
435
436 // Get top left and bottom right lat/lon pairs from the viewport
437 gdouble min_lat, max_lat, min_lon, max_lon;
438 gchar sminlon[G_ASCII_DTOSTR_BUF_SIZE];
439 gchar smaxlon[G_ASCII_DTOSTR_BUF_SIZE];
440 gchar sminlat[G_ASCII_DTOSTR_BUF_SIZE];
441 gchar smaxlat[G_ASCII_DTOSTR_BUF_SIZE];
442 vik_viewport_get_min_max_lat_lon ( viewport, &min_lat, &max_lat, &min_lon, &max_lon );
443
444 // Cannot simply use g_strdup_printf and gdouble due to locale.
445 // As we compute an URL, we have to think in C locale.
446 g_ascii_dtostr (sminlon, G_ASCII_DTOSTR_BUF_SIZE, min_lon);
447 g_ascii_dtostr (smaxlon, G_ASCII_DTOSTR_BUF_SIZE, max_lon);
448 g_ascii_dtostr (sminlat, G_ASCII_DTOSTR_BUF_SIZE, min_lat);
449 g_ascii_dtostr (smaxlat, G_ASCII_DTOSTR_BUF_SIZE, max_lat);
450
451 // Center values
452 const VikCoord *coord = vik_viewport_get_center ( viewport );
453 struct LatLon ll;
454 vik_coord_to_latlon ( coord, &ll );
455
456 gchar scenterlat[G_ASCII_DTOSTR_BUF_SIZE];
457 gchar scenterlon[G_ASCII_DTOSTR_BUF_SIZE];
458 g_ascii_dtostr (scenterlat, G_ASCII_DTOSTR_BUF_SIZE, ll.lat);
459 g_ascii_dtostr (scenterlon, G_ASCII_DTOSTR_BUF_SIZE, ll.lon);
460
461 guint8 zoom = 17; // A zoomed in default
462 // zoom - ideally x & y factors need to be the same otherwise use the default
463 if ( vik_viewport_get_xmpp ( viewport ) == vik_viewport_get_ympp ( viewport ) )
464 zoom = map_utils_mpp_to_zoom_level ( vik_viewport_get_zoom ( viewport ) );
465
466 gchar szoom[G_ASCII_DTOSTR_BUF_SIZE];
467 g_snprintf ( szoom, G_ASCII_DTOSTR_BUF_SIZE, "%d", zoom );
468
469 gint len = 0;
470 if ( priv->url_format_code )
471 len = strlen ( priv->url_format_code );
472 if ( len > MAX_NUMBER_CODES )
473 len = MAX_NUMBER_CODES;
474
475 gchar* values[MAX_NUMBER_CODES];
476 int i;
477 for ( i = 0; i < MAX_NUMBER_CODES; i++ ) {
478 values[i] = '\0';
479 }
480
481 for ( i = 0; i < len; i++ ) {
482 switch ( g_ascii_toupper ( priv->url_format_code[i] ) ) {
483 case 'L': values[i] = g_strdup ( sminlon ); break;
484 case 'R': values[i] = g_strdup ( smaxlon ); break;
485 case 'B': values[i] = g_strdup ( sminlat ); break;
486 case 'T': values[i] = g_strdup ( smaxlat ); break;
487 case 'A': values[i] = g_strdup ( scenterlat ); break;
488 case 'O': values[i] = g_strdup ( scenterlon ); break;
489 case 'Z': values[i] = g_strdup ( szoom ); break;
490 case 'S': values[i] = g_strdup ( priv->user_string ); break;
491 default: break;
492 }
493 }
494
495 gchar *url = g_strdup_printf ( priv->url, values[0], values[1], values[2], values[3], values[4], values[5], values[6] );
496
497 for ( i = 0; i < MAX_NUMBER_CODES; i++ ) {
498 if ( values[i] != '\0' )
499 g_free ( values[i] );
500 }
501
502 return url;
503}
504
505// NB Only works for ascii strings
506char* strcasestr2(const char *dst, const char *src)
507{
508 if ( !dst || !src )
509 return NULL;
510
511 if(src[0] == '\0')
512 return (char*)dst;
513
514 int len = strlen(src) - 1;
515 char sc = tolower(src[0]);
516 for(char dc = *dst; (dc = *dst); dst++) {
517 dc = tolower(dc);
518 if(sc == dc && (len == 0 || !strncasecmp(dst+1, src+1, len)))
519 return (char*)dst;
520 }
521
522 return NULL;
523}
524
525/**
526 * Returns true if the URL format contains 'S' -- that is, a search term entry
527 * box needs to be displayed
528 */
529static gboolean webtool_needs_user_string ( VikWebtool *self )
530{
531 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
532 // For some reason (my) Windows build gets built with -D_GNU_SOURCE
533#if (_GNU_SOURCE && !WINDOWS)
534 return (strcasestr(priv->url_format_code, "S") != NULL);
535#else
536 return (strcasestr2(priv->url_format_code, "S") != NULL);
537#endif
538}