[gnome-clocks/wip/geolocation] Initial commit



commit 346c94021c5875b8e91b698cc49a4a3db90abec9
Author: Evgeny Bobkin <evgen ibqn gmail com>
Date:   Sun Aug 25 07:11:32 2013 +0200

    Initial commit

 Makefile.am                  |    7 ++
 configure.ac                 |    4 +-
 src/geolocation-info.vala    |   59 +++++++++++++++++
 src/geolocation-monitor.vala |  143 ++++++++++++++++++++++++++++++++++++++++++
 src/geolocation-utils.vala   |   53 ++++++++++++++++
 src/world.vala               |   38 +++++++++++
 6 files changed, 303 insertions(+), 1 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index a531af5..6c85fc0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -88,6 +88,10 @@ AM_VALAFLAGS = \
        --pkg gtk+-3.0 \
        --pkg libcanberra \
        --pkg libnotify \
+       --pkg gio-2.0 \
+       --pkg libsoup-2.4 \
+       --pkg json-glib-1.0 \
+       --thread \
        --gresources  $(top_srcdir)/data/gnome-clocks.gresource.xml
 
 bin_PROGRAMS = gnome-clocks
@@ -109,6 +113,9 @@ VALA_SOURCES = \
        src/timer.vala \
        src/utils.vala \
        src/widgets.vala \
+       src/geolocation-monitor.vala \
+       src/geolocation-info.vala \
+       src/geolocation-utils.vala \
        src/main.vala
 
 gnome_clocks_SOURCES = \
diff --git a/configure.ac b/configure.ac
index cb40301..6a2df26 100644
--- a/configure.ac
+++ b/configure.ac
@@ -49,13 +49,15 @@ LT_INIT([disable-static])
 PKG_PROG_PKG_CONFIG([0.22])
 
 PKG_CHECK_MODULES(CLOCKS, [
-    gio-2.0 >= 2.30.0
+    gio-2.0 >= 2.36
     glib-2.0 >= 2.36
     gtk+-3.0 >= 3.9.11
     libcanberra >= 0.30
     gweather-3.0 >= 3.9.3
     gnome-desktop-3.0 >= 3.7.90
     libnotify >= 0.7.0
+    libsoup-2.4 >= 2.43.90
+    json-glib-1.0 >= 0.16.1
 ])
 
 AC_CONFIG_FILES([
diff --git a/src/geolocation-info.vala b/src/geolocation-info.vala
new file mode 100644
index 0000000..3790eb9
--- /dev/null
+++ b/src/geolocation-info.vala
@@ -0,0 +1,59 @@
+namespace GeoInfo {
+
+public enum LocationAccuracy {
+    UNKNOWN   = -1,
+    STREET    =  1000,    /*    1 km */
+    CITY      =  15000,   /*   15 km */
+    REGION    =  50000,   /*   50 km */
+    COUNTRY   =  300000,  /*  300 km */
+    CONTINENT = 3000000,  /* 3000 km */
+}
+
+public class LocationInfo : GLib.Object {
+    public static const double EARTH_RADIUS = 6372.795;
+    
+    private double _longitude;
+    private double _latitude;
+
+    public double longitude {
+        get {
+            return _longitude;
+        }
+
+        set {
+            if (-180.0 <= value && value <= 180.0) {
+                _longitude = value;
+            } else {
+                warning ("Longitude is out of range");
+            }
+        }
+
+        default = 0.0;
+    }
+    
+    public double latitude {
+        get {
+            return _latitude;
+        }
+
+        set {
+            if (-90 <= value && value <= 90.0) {
+                _latitude = value;
+            } else {
+                warning ("Latitude is out of range");
+            }
+        }
+
+        default = 0.0;
+    }
+            
+    public LocationAccuracy accuracy { get; set; default = LocationAccuracy.UNKNOWN; }
+    public string? description { get; set; default = null; }
+    public uint64 timestamp { get; set; default = 0; }
+    
+    public LocationInfo (double lat, double lon, LocationAccuracy acc = LocationAccuracy.UNKNOWN) {
+        Object (latitude: lat, longitude: lon, accuracy: acc);
+    }
+
+}   
+} // GeoInfo
diff --git a/src/geolocation-monitor.vala b/src/geolocation-monitor.vala
new file mode 100644
index 0000000..7171d0c
--- /dev/null
+++ b/src/geolocation-monitor.vala
@@ -0,0 +1,143 @@
+namespace GeoInfo {
+
+public class LocationMonitor : GLib.Object {
+    private string? ip_address;
+    private Soup.SessionAsync session;
+
+    private string _server;
+    public string server { 
+        get { return _server; }
+        set {
+            if (value.has_prefix ("http://";) ||
+                value.has_prefix ("https://";)) {
+                _server = value;
+            } else {
+                warning ("Server path does not begins with \"http://\"; either with \"https://\"";);
+            }
+        }
+    }
+
+    public bool compatibility_mode { get; set; }
+    
+    public LocationMonitor () {
+        ip_address = null;
+        server = "http://freegeoip.net/json/";;
+        compatibility_mode = true;
+        
+        session = new Soup.SessionAsync ();
+        session.use_thread_context = true;
+    }
+
+    public async GeoInfo.LocationInfo? search () {
+        Soup.Message query = get_search_query ();
+        GLib.InputStream stream;
+        try {
+            stream = yield session.send_async (query);
+        } catch (Error e) {
+            warning ("Unable to sent message: %s", e.message);
+            return null;
+        }
+
+        var parser = new Json.Parser ();
+ 
+        try {
+            parser.load_from_stream (stream);
+        } catch (Error e) {
+            warning ("Failed to load data from stream: %s", e.message);
+            return null;
+        }
+
+        var root_object = parser.get_root ().get_object ();
+          
+        if (root_object.has_member ("error_code")) {
+            warning ("Response from the server contains an error");
+            return null;
+        }
+
+        double latitude = root_object.get_double_member ("latitude");
+        double longitude = root_object.get_double_member ("longitude");
+        LocationAccuracy accuracy = parse_accuracy (root_object);
+            
+        var location = new GeoInfo.LocationInfo (latitude, longitude, accuracy);
+        
+        location.description = parse_description (root_object);
+        
+        return location;
+    }
+
+    private LocationAccuracy parse_accuracy (Json.Object object) {
+        if (object.has_member ("accuracy")) {
+                string str = object.get_string_member ("accuracy");
+                return get_accuracy_from_string (str);
+        } else if (object.has_member ("street")) {
+                return LocationAccuracy.STREET;
+        } else if (object.has_member ("city")) {
+                return LocationAccuracy.CITY;
+        } else if (object.has_member ("region_name")) {
+                return LocationAccuracy.REGION;
+        } else if (object.has_member ("country_name")) {
+                return LocationAccuracy.COUNTRY;
+        } else if (object.has_member ("continent")) {
+                return LocationAccuracy.CONTINENT;
+        } else {
+                return LocationAccuracy.UNKNOWN;
+        }
+    }
+
+    private LocationAccuracy get_accuracy_from_string (string str)
+    {
+        switch (str) {
+        case "street":
+            return LocationAccuracy.STREET;
+        case "city":
+            return LocationAccuracy.CITY;
+        case "region":
+            return LocationAccuracy.REGION;
+        case "country":
+            return LocationAccuracy.COUNTRY;
+        case "continent":
+            return LocationAccuracy.CONTINENT;
+        default:
+            return LocationAccuracy.UNKNOWN;
+        }
+    }
+
+    private string? parse_description (Json.Object object) {
+        string? desc = null;
+        if (object.has_member ("country_name")) {
+            if (object.has_member ("region_name")) {
+                if (object.has_member ("city")) {
+                    desc = "%s, %s, %s".printf (object.get_string_member ("city"),
+                                                object.get_string_member ("region_name"),
+                                                object.get_string_member ("country_name"));
+                } else {
+                    desc = "%s, %s".printf (object.get_string_member ("region_name"),
+                                            object.get_string_member ("country_name"));
+                }
+            } else {
+                desc = object.get_string_member ("country_name");
+            }
+        }
+        return desc;
+    }
+
+    private Soup.Message get_search_query () {
+        string uri;
+
+        if (ip_address != null) {
+            if (compatibility_mode) {
+                string escaped = Soup.URI.encode (ip_address, null);
+                uri = @"$server/$escaped";
+            } else {
+                var ht = new HashTable<string, string> (GLib.str_hash, GLib.str_equal);
+                ht.insert ("ip", ip_address);
+                string query = Soup.Form.encode_hash (ht);
+                uri = @"$server?$query";
+            }
+        } else
+            uri = server;
+
+        return new Soup.Message ("GET", uri);
+    }
+}    
+} // GeoInfo
diff --git a/src/geolocation-utils.vala b/src/geolocation-utils.vala
new file mode 100644
index 0000000..8ae6cd5
--- /dev/null
+++ b/src/geolocation-utils.vala
@@ -0,0 +1,53 @@
+namespace GeoInfo {
+namespace Utils {
+private double deg_to_rad (double deg) {
+    return Math.PI / 180.0d * deg;
+}
+
+private double get_distance (double latitude1, double longitude1, double latitude2, double longitude2) {
+    const double radius = 6372.795;
+
+    double lat1 = deg_to_rad (latitude1);
+    double lat2 = deg_to_rad (latitude2);
+    double lon1 = deg_to_rad (longitude1);
+    double lon2 = deg_to_rad (longitude2);
+
+    return Math.acos (Math.cos (lat1) * Math.cos (lat2) * Math.cos (lon1 - lon2) + Math.sin (lat1) * 
Math.sin (lat2)) * radius;
+}
+
+private void search_locations_helper (double lat, double lon, GWeather.Location location, ref double 
minimal_distance,  ref GWeather.Location? found_location) {
+    GWeather.Location? [] locations = location.get_children ();
+
+    if (locations != null) {
+        for (int i = 0; i < locations.length; i++) {
+            if (locations[i].get_level () == GWeather.LocationLevel.CITY) {
+                if (locations[i].has_coords ()) {
+                    double latitude, longitude, distance;
+
+                    locations[i].get_coords (out latitude, out longitude);
+                    distance = get_distance (lat, lon, latitude, longitude);
+
+                    if (distance < minimal_distance) {
+                        found_location = locations[i];
+                        minimal_distance = distance;
+                    }
+                }
+            }
+
+            search_locations_helper (lat, lon, locations[i], ref minimal_distance, ref found_location);
+        }
+    }
+}
+
+public GWeather.Location? search_locations (double lat, double lon) {
+    GWeather.Location locations = new GWeather.Location.world (true);
+    GWeather.Location? found_location = null;
+    double minimal_distance = 1000.0d;
+
+    search_locations_helper (lat, lon, locations, ref minimal_distance, ref found_location);
+
+    return found_location;
+}
+
+}// Utils
+}// GeoIp
diff --git a/src/world.vala b/src/world.vala
index 0be9319..f4b0824 100644
--- a/src/world.vala
+++ b/src/world.vala
@@ -303,6 +303,10 @@ public class MainPanel : Gtk.Stack, Clocks.Clock {
         add (standalone);
 
         load ();
+        
+        use_geolocation.begin ((obj, res) => {
+            use_geolocation.end (res);
+        });
 
         notify["visible-child"].connect (() => {
             if (visible_child == content_view) {
@@ -343,6 +347,40 @@ public class MainPanel : Gtk.Stack, Clocks.Clock {
         }
         settings.set_value ("world-clocks", builder.end ());
     }
+    
+    private async void use_geolocation () {
+        GeoInfo.LocationInfo? geo_location = null;
+        GWeather.Location? found_location = null;
+
+        GeoInfo.LocationMonitor monitor = new GeoInfo.LocationMonitor ();
+
+        try {
+            geo_location = yield monitor.search ();
+
+            found_location = GeoInfo.Utils.search_locations (geo_location.latitude, 
+                                                             geo_location.longitude);
+        } catch (IOError e) {
+            warning ("obtaining geolocation: %s", e.message);
+        }
+
+        if (found_location != null) {
+            bool not_included = true;
+
+            foreach (Item i in locations) {
+                if (i.location == found_location) {
+                    not_included = false;
+                }
+            }
+            
+            if (not_included) {
+                var item = new Item (found_location);
+
+                locations.append (item);
+                content_view.add_item (item);
+                save ();
+            }
+        }
+    }
 
     public void activate_new () {
         var dialog = new LocationDialog ((Gtk.Window) get_toplevel ());


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]