[gnome-clocks/wip/geolocation] Initial commit
- From: Evgeny Bobkin <ebobkin src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-clocks/wip/geolocation] Initial commit
- Date: Mon, 26 Aug 2013 07:46:35 +0000 (UTC)
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]