]> git.street.me.uk Git - andy/viking.git/commitdiff
Enable an optional automatic IP to location startup mode - mainly for new users to...
authorRob Norris <rw_norris@hotmail.com>
Thu, 23 May 2013 23:34:27 +0000 (00:34 +0100)
committerRob Norris <rw_norris@hotmail.com>
Thu, 13 Jun 2013 18:14:42 +0000 (19:14 +0100)
The location is (attempted to be) determined by http:/www.hostip.info/

src/globals.c
src/globals.h
src/vikgoto.c
src/vikgoto.h
src/vikviewport.c
src/vikwindow.c

index 7d6eeba86397a9801641afdd9b1de845a5635c49..6a921ebdaab4b0ae8069f391c0c991f81dc0621c 100644 (file)
@@ -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,
index c3783efbbd0bc8f9f7689116adafd7feeeab775b..5fb8c826a63c5976efd549eca21d42b93678ab40 100644 (file)
@@ -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 ( );
index 2258519852a6ea79483b9966543ce16d91e1065f..27e5b3fa6120965d170e98d82f12c13c1246b397 100644 (file)
 #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;
+}
index 7bc69a2da899efe2fdd3fba128150184e2d5ca0f..49019aeb9278f19e504b791c53595f14d1a9593f 100644 (file)
@@ -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);
 
index 716489a88bd152124813dcac8a1c0e30e4fc3674..28216d9d39bb41c3c4d54eddb4ff005171e0dd34 100644 (file)
@@ -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);
index fe627a5d82e96c1ab0cbfb2d7aece5dfe1b69531..4b74fc1d10f86304292a2c7c628f0527a92f4911 100644 (file)
@@ -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.") );