From c0c5893fdbbc2de9b1199834ab3c192eba6df3d1 Mon Sep 17 00:00:00 2001 From: Rob Norris Date: Mon, 7 Apr 2014 23:16:33 +0100 Subject: [PATCH] Enable importing from GeoJSON files via the usage of the program 'togpx'. The togpx program will need to be detected before being made available for use in the GUI. https://www.npmjs.org/package/togpx --- help/C/viking.xml | 15 ++++ po/POTFILES.in | 1 + src/Makefile.am | 1 + src/datasource_geojson.c | 172 +++++++++++++++++++++++++++++++++++++++ src/datasources.h | 1 + src/geojson.c | 71 ++++++++++++++++ src/geojson.h | 3 + src/vikwindow.c | 18 ++++ 8 files changed, 282 insertions(+) create mode 100644 src/datasource_geojson.c diff --git a/help/C/viking.xml b/help/C/viking.xml index ddf095ab..73ad9755 100644 --- a/help/C/viking.xml +++ b/help/C/viking.xml @@ -574,6 +574,21 @@ You need to select the type of the file that is going to be returned, since ther +
+Import GeoJSON File + +FileAcquireImport GeoJSON File + + +This uses the program togpx to load .geojson files. +If the program is not detected on your system, then this option will not be available. +See here for the installation method. + + +The current version (1.4.4) of GPSBabel does not support the GeoJSON file format. + +
+
Print diff --git a/po/POTFILES.in b/po/POTFILES.in index 1ba1e13c..e546c3f1 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -17,6 +17,7 @@ src/datasource_bfilter.c src/datasource_file.c src/datasource_gc.c src/datasource_geotag.c +src/datasource_geojson.c src/datasource_gps.c src/datasource_osm.c src/datasource_osm_my_traces.c diff --git a/src/Makefile.am b/src/Makefile.am index a22c32a8..7c732dbe 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -117,6 +117,7 @@ libviking_a_SOURCES = \ babel.c babel.h \ babel_ui.c babel_ui.h \ datasource_file.c \ + datasource_geojson.c \ datasource_gps.c datasource_gps.h \ datasource_routing.c \ datasource_gc.c \ diff --git a/src/datasource_geojson.c b/src/datasource_geojson.c new file mode 100644 index 00000000..ec9fd78a --- /dev/null +++ b/src/datasource_geojson.c @@ -0,0 +1,172 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2014, Rob Norris + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include + +#include "viking.h" +#include "acquire.h" +#include "babel.h" +#include "geojson.h" + +typedef struct { + GtkWidget *files; + GSList *filelist; // Files selected +} datasource_geojson_user_data_t; + +// The last used directory +static gchar *last_folder_uri = NULL; + +static gpointer datasource_geojson_init ( acq_vik_t *avt ); +static void datasource_geojson_add_setup_widgets ( GtkWidget *dialog, VikViewport *vvp, gpointer user_data ); +static void datasource_geojson_get_cmd_string ( datasource_geojson_user_data_t *ud, gchar **cmd, gchar **input_file_type, DownloadMapOptions *options ); +static gboolean datasource_geojson_process ( VikTrwLayer *vtl, const gchar *cmd, const gchar *extra, BabelStatusFunc status_cb, acq_dialog_widgets_t *adw, gpointer not_used ); +static void datasource_geojson_cleanup ( gpointer data ); + +VikDataSourceInterface vik_datasource_geojson_interface = { + N_("Acquire from GeoJSON"), + N_("GeoJSON"), + VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT, + VIK_DATASOURCE_INPUTTYPE_NONE, + TRUE, + FALSE, // We should be able to see the data on the screen so no point in keeping the dialog open + FALSE, // Not thread method - open each file in the main loop + (VikDataSourceInitFunc) datasource_geojson_init, + (VikDataSourceCheckExistenceFunc) NULL, + (VikDataSourceAddSetupWidgetsFunc) datasource_geojson_add_setup_widgets, + (VikDataSourceGetCmdStringFunc) datasource_geojson_get_cmd_string, + (VikDataSourceProcessFunc) datasource_geojson_process, + (VikDataSourceProgressFunc) NULL, + (VikDataSourceAddProgressWidgetsFunc) NULL, + (VikDataSourceCleanupFunc) datasource_geojson_cleanup, + (VikDataSourceOffFunc) NULL, + NULL, + 0, + NULL, + NULL, + 0 +}; + +static gpointer datasource_geojson_init ( acq_vik_t *avt ) +{ + datasource_geojson_user_data_t *user_data = g_malloc(sizeof(datasource_geojson_user_data_t)); + user_data->filelist = NULL; + return user_data; +} + +static void datasource_geojson_add_setup_widgets ( GtkWidget *dialog, VikViewport *vvp, gpointer user_data ) +{ + datasource_geojson_user_data_t *ud = (datasource_geojson_user_data_t *)user_data; + + ud->files = gtk_file_chooser_widget_new ( GTK_FILE_CHOOSER_ACTION_OPEN ); + + // try to make it a nice size - otherwise seems to default to something impractically small + gtk_window_set_default_size ( GTK_WINDOW (dialog) , 600, 300 ); + + if ( last_folder_uri ) + gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(ud->files), last_folder_uri ); + + GtkFileChooser *chooser = GTK_FILE_CHOOSER ( ud->files ); + + // Add filters + GtkFileFilter *filter; + filter = gtk_file_filter_new (); + gtk_file_filter_set_name ( filter, _("All") ); + gtk_file_filter_add_pattern ( filter, "*" ); + gtk_file_chooser_add_filter ( chooser, filter ); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name ( filter, _("GeoJSON") ); + gtk_file_filter_add_pattern ( filter, "*.geojson" ); + gtk_file_chooser_add_filter ( chooser, filter ); + + // Default to geojson + gtk_file_chooser_set_filter ( chooser, filter ); + + // Allow selecting more than one + gtk_file_chooser_set_select_multiple ( chooser, TRUE ); + + // Packing all widgets + GtkBox *box = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))); + gtk_box_pack_start ( box, ud->files, TRUE, TRUE, 0 ); + + gtk_widget_show_all ( dialog ); +} + +static void datasource_geojson_get_cmd_string ( datasource_geojson_user_data_t *userdata, gchar **cmd, gchar **input_file_type, DownloadMapOptions *options ) +{ + // Retrieve the files selected + userdata->filelist = gtk_file_chooser_get_filenames ( GTK_FILE_CHOOSER(userdata->files) ); // Not reusable !! + + // Memorize the directory for later reuse + g_free ( last_folder_uri ); + last_folder_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(userdata->files) ); + last_folder_uri = g_strdup ( last_folder_uri ); + + // TODO Memorize the file filter for later reuse? + //GtkFileFilter *filter = gtk_file_chooser_get_filter ( GTK_FILE_CHOOSER(userdata->files) ); + + // return some value so *thread* processing will continue + *cmd = g_strdup ("fake command"); // Not really used, thus no translations +} + +/** + * Process selected files and try to generate waypoints storing them in the given vtl + */ +static gboolean datasource_geojson_process ( VikTrwLayer *vtl, const gchar *cmd, const gchar *extra, BabelStatusFunc status_cb, acq_dialog_widgets_t *adw, gpointer not_used ) +{ + datasource_geojson_user_data_t *user_data = (datasource_geojson_user_data_t *)adw->user_data; + + // Process selected files + GSList *cur_file = user_data->filelist; + while ( cur_file ) { + gchar *filename = cur_file->data; + + gchar *gpx_filename = a_geojson_import_to_gpx ( filename ); + if ( gpx_filename ) { + // Important that this process is run in the main thread + vik_window_open_file ( adw->vw, gpx_filename, FALSE ); + // Delete the temporary file + g_remove (gpx_filename); + g_free (gpx_filename); + } + else { + gchar* msg = g_strdup_printf ( _("Unable to import from: %s"), filename ); + vik_window_statusbar_update ( adw->vw, msg, VIK_STATUSBAR_INFO ); + g_free (msg); + } + + g_free ( filename ); + cur_file = g_slist_next ( cur_file ); + } + + // Free memory + g_slist_free ( user_data->filelist ); + + // No failure + return TRUE; +} + +static void datasource_geojson_cleanup ( gpointer data ) +{ + g_free ( data ); +} diff --git a/src/datasources.h b/src/datasources.h index bae9e3dc..71f938f4 100644 --- a/src/datasources.h +++ b/src/datasources.h @@ -42,6 +42,7 @@ extern VikDataSourceInterface vik_datasource_geotag_interface; extern VikDataSourceInterface vik_datasource_wikipedia_interface; #endif extern VikDataSourceInterface vik_datasource_url_interface; +extern VikDataSourceInterface vik_datasource_geojson_interface; G_END_DECLS diff --git a/src/geojson.c b/src/geojson.c index 7a04b930..2967e50f 100644 --- a/src/geojson.c +++ b/src/geojson.c @@ -115,3 +115,74 @@ const gchar* a_geojson_program_export ( void ) { return "togeojson"; } + +// +// https://github.com/tyrasd/togpx +// +// https://www.npmjs.org/package/togpx +// +// Tested with version 0.3.1 +const gchar* a_geojson_program_import ( void ) +{ + return "togpx"; +} + +/** + * a_geojson_import_to_gpx: + * + * @filename: The source GeoJSON file + * + * Returns: The name of newly created temporary GPX file + * This file should be removed once used and the string freed. + * If NULL then the process failed. + */ +gchar* a_geojson_import_to_gpx ( const gchar *filename ) +{ + gchar *gpx_filename = NULL; + GError *error = NULL; + // Opening temporary file + int fd = g_file_open_tmp("vik_geojson_XXXXXX.gpx", &gpx_filename, &error); + if (fd < 0) { + g_warning ( _("failed to open temporary file: %s"), error->message ); + g_clear_error ( &error ); + return NULL; + } + g_debug ( "%s: temporary file = %s", __FUNCTION__, gpx_filename ); + + GPid pid; + gint stdout; + + // geojson program should be on the $PATH + gchar **argv; + argv = g_new (gchar*, 3); + argv[0] = g_strdup (a_geojson_program_import()); + argv[1] = g_strdup (filename); + argv[2] = NULL; + + FILE *gpxfile = fdopen (fd, "w"); + + // TODO: monitor stderr? + if (!g_spawn_async_with_pipes (NULL, argv, NULL, (GSpawnFlags) G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL, &stdout, NULL, &error)) { + g_warning ("Async command failed: %s", error->message); + g_error_free(error); + } + else { + // Probably should use GIOChannels... + gchar line[512]; + FILE *fout = fdopen(stdout, "r"); + setvbuf(fout, NULL, _IONBF, 0); + + while (fgets(line, sizeof(line), fout)) { + fprintf ( gpxfile, line ); + } + + fclose(fout); + g_child_watch_add ( pid, (GChildWatchFunc) my_watch, NULL ); + } + + fclose (gpxfile); + + g_strfreev (argv); + + return gpx_filename; +} diff --git a/src/geojson.h b/src/geojson.h index fdee6c83..2c89a1aa 100644 --- a/src/geojson.h +++ b/src/geojson.h @@ -29,6 +29,9 @@ G_BEGIN_DECLS gboolean a_geojson_write_file ( VikTrwLayer *vtl, FILE *ff ); const gchar* a_geojson_program_export ( void ); +const gchar* a_geojson_program_import ( void ); + +gchar* a_geojson_import_to_gpx ( const gchar *filename ); G_END_DECLS diff --git a/src/vikwindow.c b/src/vikwindow.c index d84fa17e..b8fdc8b7 100644 --- a/src/vikwindow.c +++ b/src/vikwindow.c @@ -29,6 +29,7 @@ #include "background.h" #include "acquire.h" #include "datasources.h" +#include "geojson.h" #include "vikgoto.h" #include "dems.h" #include "mapcache.h" @@ -3169,6 +3170,11 @@ static void acquire_from_file ( GtkAction *a, VikWindow *vw ) my_acquire ( vw, &vik_datasource_file_interface ); } +static void acquire_from_geojson ( GtkAction *a, VikWindow *vw ) +{ + my_acquire ( vw, &vik_datasource_geojson_interface ); +} + static void acquire_from_routing ( GtkAction *a, VikWindow *vw ) { my_acquire ( vw, &vik_datasource_routing_interface ); @@ -3985,6 +3991,10 @@ static GtkActionEntry entries_gpsbabel[] = { { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml }, }; +static GtkActionEntry entries_geojson[] = { + { "AcquireGeoJSON", NULL, N_("Import Geo_JSON File..."), NULL, N_("Import GeoJSON file"), (GCallback)acquire_from_geojson }, +}; + /* Radio items */ /* FIXME use VIEWPORT_DRAWMODE values */ static GtkRadioActionEntry mode_entries[] = { @@ -4047,6 +4057,14 @@ static void window_create_ui( VikWindow *window ) gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window ); } + // GeoJSON import capability + if ( g_find_program_in_path ( a_geojson_program_import() ) ) { + if ( gtk_ui_manager_add_ui_from_string ( uim, + "", + -1, &error ) ) + gtk_action_group_add_actions ( action_group, entries_geojson, G_N_ELEMENTS (entries_geojson), window ); + } + icon_factory = gtk_icon_factory_new (); gtk_icon_factory_add_default (icon_factory); -- 2.39.5