From a14f46cfdb425dbe47f9086f7a38c1d209ac6572 Mon Sep 17 00:00:00 2001 From: Rob Norris Date: Fri, 24 May 2013 00:34:27 +0100 Subject: [PATCH] Enable an optional automatic IP to location startup mode - mainly for new users to get going more easily. The location is (attempted to be) determined by http:/www.hostip.info/ --- src/globals.c | 2 +- src/globals.h | 1 + src/vikgoto.c | 214 ++++++++++++++++++++++++++++++++++++++++++---- src/vikgoto.h | 1 + src/vikviewport.c | 2 - src/vikwindow.c | 76 +++++++++++++++- 6 files changed, 275 insertions(+), 21 deletions(-) diff --git a/src/globals.c b/src/globals.c index 7d6eeba8..6a921ebd 100644 --- a/src/globals.c +++ b/src/globals.c @@ -111,7 +111,7 @@ static VikLayerParam prefs_advanced[] = { { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_ADVANCED_NAMESPACE "create_track_tooltip", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Show Tooltip during Track Creation:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, NULL }, }; -static gchar * params_startup_methods[] = {N_("Home Location"), N_("Last Location"), N_("Specified File"), NULL}; +static gchar * params_startup_methods[] = {N_("Home Location"), N_("Last Location"), N_("Specified File"), N_("Auto Location"), NULL}; static VikLayerParam startup_prefs[] = { { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_STARTUP_NAMESPACE "restore_window_state", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Restore Window Setup:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, diff --git a/src/globals.h b/src/globals.h index c3783efb..5fb8c826 100644 --- a/src/globals.h +++ b/src/globals.h @@ -180,6 +180,7 @@ typedef enum { VIK_STARTUP_METHOD_HOME_LOCATION, VIK_STARTUP_METHOD_LAST_LOCATION, VIK_STARTUP_METHOD_SPECIFIED_FILE, + VIK_STARTUP_METHOD_AUTO_LOCATION, } vik_startup_method_t; vik_startup_method_t a_vik_get_startup_method ( ); diff --git a/src/vikgoto.c b/src/vikgoto.c index 22585198..27e5b3fa 100644 --- a/src/vikgoto.c +++ b/src/vikgoto.c @@ -35,6 +35,11 @@ #include "vikgototool.h" #include "vikgoto.h" +/* Compatibility */ +#if ! GLIB_CHECK_VERSION(2,22,0) +#define g_mapped_file_unref g_mapped_file_free +#endif + static gchar *last_goto_str = NULL; static VikCoord *last_coord = NULL; static gchar *last_successful_goto_str = NULL; @@ -113,6 +118,28 @@ static void find_provider (gpointer elem, gpointer user_data) } } +/** + * Setup last_goto_tool value + */ +static void get_provider () +{ + // Use setting for the provider if available + if ( last_goto_tool < 0 ) { + find_entry = -1; + wanted_entry = -1; + gchar *provider = NULL; + if ( a_settings_get_string ( VIK_SETTINGS_GOTO_PROVIDER, &provider ) ) { + // Use setting + if ( provider ) + g_list_foreach (goto_tools_list, find_provider, provider); + // If not found set it to the first entry, otherwise use the entry + last_goto_tool = ( wanted_entry < 0 ) ? 0 : wanted_entry; + } + else + last_goto_tool = 0; + } +} + static gchar *a_prompt_for_goto_string(VikWindow *vw) { GtkWidget *dialog = NULL; @@ -132,21 +159,7 @@ static gchar *a_prompt_for_goto_string(VikWindow *vw) current = g_list_next (current); } - // Use setting for the provider if available - if ( last_goto_tool < 0 ) { - find_entry = -1; - wanted_entry = -1; - gchar *provider = NULL; - if ( a_settings_get_string ( VIK_SETTINGS_GOTO_PROVIDER, &provider ) ) { - // Use setting - if ( provider ) - g_list_foreach (goto_tools_list, find_provider, provider); - // If not found set it to the first entry, otherwise use the entry - last_goto_tool = ( wanted_entry < 0 ) ? 0 : wanted_entry; - } - else - last_goto_tool = 0; - } + get_provider (); gtk_combo_box_set_active ( GTK_COMBO_BOX( tool_list ), last_goto_tool ); GtkWidget *goto_label = gtk_label_new(_("Enter address or place name:")); @@ -172,6 +185,7 @@ static gchar *a_prompt_for_goto_string(VikWindow *vw) return NULL; } + // TODO check if list is empty last_goto_tool = gtk_combo_box_get_active ( GTK_COMBO_BOX(tool_list) ); gchar *provider = vik_goto_tool_get_label ( g_list_nth_data (goto_tools_list, last_goto_tool) ); a_settings_set_string ( VIK_SETTINGS_GOTO_PROVIDER, provider ); @@ -189,6 +203,26 @@ static gchar *a_prompt_for_goto_string(VikWindow *vw) return(goto_str); /* goto_str needs to be freed by caller */ } +/** + * Goto a place when we already have a string to search on + * + * Returns: %TRUE if a successful lookup + */ +static gboolean vik_goto_place ( VikWindow *vw, VikViewport *vvp, gchar* name, VikCoord *vcoord ) +{ + // Ensure last_goto_tool is given a value + get_provider (); + + if ( goto_tools_list ) { + VikGotoTool *gototool = g_list_nth_data ( goto_tools_list, last_goto_tool ); + if ( gototool ) { + if ( vik_goto_tool_get_coord ( gototool, vw, vvp, name, vcoord ) == 0 ) + return TRUE; + } + } + return FALSE; +} + void a_vik_goto(VikWindow *vw, VikViewport *vvp) { VikCoord new_center; @@ -224,3 +258,153 @@ void a_vik_goto(VikWindow *vw, VikViewport *vvp) g_free(s_str); } while (more); } + +#define HOSTIP_LATITUDE_PATTERN "\"lat\":\"" +#define HOSTIP_LONGITUDE_PATTERN "\"lng\":\"" +#define HOSTIP_CITY_PATTERN "\"city\":\"" +#define HOSTIP_COUNTRY_PATTERN "\"country_name\":\"" + +/** + * Automatic attempt to find out where you are using: + * 1. http://www.hostip.info ++ + * 2. if not specific enough fallback to using the default goto tool with a country name + * ++ Using returned JSON information + * c.f. with googlesearch.c - similar implementation is used here + * + * returns: + * 0 if failed to locate anything + * 1 if exact latitude/longitude found + * 2 if position only as precise as a city + * 3 if position only as precise as a country + * @name: Contains the name of place found. Free this string after use. + */ +gint a_vik_goto_where_am_i ( VikViewport *vvp, struct LatLon *ll, gchar **name ) +{ + gint result = 0; + *name = NULL; + + gchar *tmpname = a_download_uri_to_tmp_file ( "http://api.hostip.info/get_json.php?position=true", NULL ); + //gchar *tmpname = g_strdup ("../test/hostip2.json"); + if (!tmpname) { + return result; + } + + ll->lat = 0.0; + ll->lon = 0.0; + + gchar *pat; + GMappedFile *mf; + gchar *ss; + gint fragment_len; + + gchar lat_buf[32], lon_buf[32]; + lat_buf[0] = lon_buf[0] = '\0'; + gchar *country; + gchar *city; + + if ((mf = g_mapped_file_new(tmpname, FALSE, NULL)) == NULL) { + g_critical(_("couldn't map temp file")); + goto tidy; + } + + gsize len = g_mapped_file_get_length(mf); + gchar *text = g_mapped_file_get_contents(mf); + + if ((pat = g_strstr_len(text, len, HOSTIP_COUNTRY_PATTERN))) { + pat += strlen(HOSTIP_COUNTRY_PATTERN); + fragment_len = 0; + ss = pat; + while (*pat != '"') { + fragment_len++; + pat++; + } + country = g_strndup(ss, fragment_len); + } + + if ((pat = g_strstr_len(text, len, HOSTIP_CITY_PATTERN))) { + pat += strlen(HOSTIP_CITY_PATTERN); + fragment_len = 0; + ss = pat; + while (*pat != '"') { + fragment_len++; + pat++; + } + city = g_strndup(ss, fragment_len); + } + + if ((pat = g_strstr_len(text, len, HOSTIP_LATITUDE_PATTERN))) { + pat += strlen(HOSTIP_LATITUDE_PATTERN); + ss = lat_buf; + if (*pat == '-') + *ss++ = *pat++; + while ((ss < (lat_buf + sizeof(lat_buf))) && (pat < (text + len)) && + (g_ascii_isdigit(*pat) || (*pat == '.'))) + *ss++ = *pat++; + *ss = '\0'; + ll->lat = g_ascii_strtod(lat_buf, NULL); + } + + if ((pat = g_strstr_len(text, len, HOSTIP_LONGITUDE_PATTERN))) { + pat += strlen(HOSTIP_LONGITUDE_PATTERN); + ss = lon_buf; + if (*pat == '-') + *ss++ = *pat++; + while ((ss < (lon_buf + sizeof(lon_buf))) && (pat < (text + len)) && + (g_ascii_isdigit(*pat) || (*pat == '.'))) + *ss++ = *pat++; + *ss = '\0'; + ll->lon = g_ascii_strtod(lon_buf, NULL); + } + + if ( ll->lat != 0.0 && ll->lon != 0.0 ) { + if ( ll->lat > -90.0 && ll->lat < 90.0 && ll->lon > -180.0 && ll->lon < 180.0 ) { + // Found a 'sensible' & 'precise' location + result = 1; + *name = g_strdup ( _("Locality") ); //Albeit maybe not known by an actual name! + } + } + else { + // Hopefully city name is unique enough to lookup position on + // Maybe for American places where hostip appends the State code on the end + // But if the country code is not appended if could easily get confused + // e.g. 'Portsmouth' could be at least + // Portsmouth, Hampshire, UK or + // Portsmouth, Viginia, USA. + + // Try city name lookup + if ( city ) { + g_debug ( "%s: found city %s", __FUNCTION__, city ); + if ( strcmp ( city, "(Unknown city)" ) != 0 ) { + VikCoord new_center; + if ( vik_goto_place ( NULL, vvp, city, &new_center ) ) { + // Got something + vik_coord_to_latlon ( &new_center, ll ); + result = 2; + *name = city; + goto tidy; + } + } + } + + // Try country name lookup + if ( country ) { + g_debug ( "%s: found country %s", __FUNCTION__, country ); + if ( strcmp ( country, "(Unknown Country)" ) != 0 ) { + VikCoord new_center; + if ( vik_goto_place ( NULL, vvp, country, &new_center ) ) { + // Finally got something + vik_coord_to_latlon ( &new_center, ll ); + result = 3; + *name = country; + goto tidy; + } + } + } + } + + tidy: + g_mapped_file_unref ( mf ); + g_remove ( tmpname ); + g_free ( tmpname ); + return result; +} diff --git a/src/vikgoto.h b/src/vikgoto.h index 7bc69a2d..49019aeb 100644 --- a/src/vikgoto.h +++ b/src/vikgoto.h @@ -31,6 +31,7 @@ G_BEGIN_DECLS void vik_goto_register (VikGotoTool *tool); void vik_goto_unregister_all (void); +gint a_vik_goto_where_am_i ( VikViewport *vvp, struct LatLon *ll, gchar **name ); void a_vik_goto(VikWindow *vw, VikViewport *vvp); gchar * a_vik_goto_get_search_string_for_this_place(VikWindow *vw); diff --git a/src/vikviewport.c b/src/vikviewport.c index 716489a8..28216d9d 100644 --- a/src/vikviewport.c +++ b/src/vikviewport.c @@ -43,7 +43,6 @@ #include "vikcoord.h" #include "vikwindow.h" #include "vikviewport.h" - #include "mapcoord.h" /* for ALTI_TO_MPP */ @@ -180,7 +179,6 @@ vik_viewport_init ( VikViewport *vvp ) a_coords_latlon_to_utm ( &ll, &utm ); - /* TODO: not static */ vvp->xmpp = zoom_x; vvp->ympp = zoom_y; vvp->xmfactor = MERCATOR_FACTOR (vvp->xmpp); diff --git a/src/vikwindow.c b/src/vikwindow.c index fe627a5d..4b74fc1d 100644 --- a/src/vikwindow.c +++ b/src/vikwindow.c @@ -178,6 +178,7 @@ struct _VikWindow { gchar *filename; gboolean modified; + VikLoadType_t loaded_type; GtkWidget *open_dia, *save_dia; GtkWidget *save_img_dia, *save_img_dir_dia; @@ -358,6 +359,59 @@ VikWindow *vik_window_new_window () return NULL; } +/** + * determine_location_thread: + * @vw: The window that will get updated + * @threaddata: Data used by our background thread mechanism + * + * Use the features in vikgoto to determine where we are + * Then set up the viewport: + * 1. To goto the location + * 2. Set an appropriate level zoom for the location type + * 3. Some statusbar message feedback + */ +static int determine_location_thread ( VikWindow *vw, gpointer threaddata ) +{ + struct LatLon ll; + gchar *name = NULL; + gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name ); + + int result = a_background_thread_progress ( threaddata, 1.0 ); + if ( result != 0 ) { + vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO ); + return -1; /* Abort thread */ + } + + if ( ans ) { + // Zoom out a little + gdouble zoom = 16.0; + + if ( ans == 2 ) { + // Position found with city precision - so zoom out more + zoom = 128.0; + } + else if ( ans == 3 ) { + // Position found via country name search - so zoom wayyyy out + zoom = 2048.0; + } + + vik_viewport_set_zoom ( vw->viking_vvp, zoom ); + vik_viewport_set_center_latlon ( vw->viking_vvp, &ll ); + + gchar *message = g_strdup_printf ( _("Location found: %s"), name ); + vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO ); + g_free ( name ); + g_free ( message ); + + // Signal to redraw from the background + vik_layers_panel_emit_update ( vw->viking_vlp ); + } + else + vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO ); + + return 0; +} + /** * Steps to be taken once initial loading has completed */ @@ -381,6 +435,22 @@ void vik_window_new_window_finish ( VikWindow *vw ) draw_update ( vw ); } + + // If not loaded any file, maybe try the location lookup + if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) { + if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) { + + vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") ); + + a_background_thread ( GTK_WINDOW(vw), + _("Determining location"), + (vik_thr_func) determine_location_thread, + vw, + NULL, + NULL, + 1 ); + } + } } static void open_window ( VikWindow *vw, GSList *files ) @@ -624,7 +694,7 @@ static void vik_window_init ( VikWindow *vw ) gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) ); vw->filename = NULL; - + vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none vw->modified = FALSE; vw->only_updating_coord_mode_ui = FALSE; @@ -2456,8 +2526,8 @@ void vik_window_clear_busy_cursor ( VikWindow *vw ) void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename ) { vik_window_set_busy_cursor ( vw ); - - switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) ) + vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ); + switch ( vw->loaded_type ) { case LOAD_TYPE_READ_FAILURE: a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") ); -- 2.39.5