Improve gnome-clocks world view (pictures, geolocation)

By Evgeny Bobkin

Abstract

The use of new GNOME 3 design pattern has already been proven to turn the user experience into a pleasure. Working in a close collaboration with the GNOME Design Team the main goal of this project is on one side to improve gnome-clocks world view by implementing the support for custom city images as well as the integration with geolocation and on the another side to use a framework of gnome-clocks App to test and to implement new design ideas. The wip/geoclue branch contains a port of the same code to the geoclue service.

Design Ideas

The tentative geolocation design with the discussion of the implementation ideas and issues can be found here: GeolocationIntegration.

Geolocation support

On the wip/geolocation branch there is a working implementation of the design that directly queries the http provider, this was done to not block the implementation on the ongoing changes to the geoclue module.

geolocation.png

The work on geolocation support took place in the wip/geolocation and wip/geoinfo branches. Afterwards It was pushed to the master branch. Thank to my mentor, this featute, therefore, will be part of the 3.10 release. \o/ Feel free to try it out!

City Images Support

The following Figure shows a screenshot of the city images support in Clocks. The city image changes dynamically corresponding to the day and night time, respectively.

city-images-overview.png

Once, you one of the cities is selected you are forwarded to the standalone mode, in which the image of the city is shown in the background, as illustrated in the picture below:

city-images-standalone.png

Flickr group has been created for sharing city images between users. The corresponding code is developed in the wip/cityimages branch. Currently only three experimental provider were implemented: Default fallback provider, Local image provider and the Flickr group provider, respectively. They are all inherited from the following interface:

   1 public interface ImageProvider : GLib.Object {
   2     public virtual Gdk.Pixbuf? get_image (string name) {
   3         return null;
   4     }
   5 
   6     public signal void image_updated (string name);
   7 }
  • The default fallback provider is only capable to provide two statically stored image for a day and night as illustrated in the first screenshot with the shown geolocation support.
  • The Local image provider transmits the images saved in the directory:

~/.local/share/gnome-clocks/city-images

The file names should follow the name convention: <city name>-<country name>-<{day|night}>.jpg (everything in lower case), like for example:

berlin-germany-day.jpg
tokyo-japan-night.jpg 
london-united kingdom-day.jpg
  • The Flickr group provider is responsible for downloading of images from the gnome-clocks group and save them in ~/.local/share/gnome-clocks/city-images/flickr directory. For the testing phase it only contains images of the following cities: tokyo, berlin, moscow, rome.

How to test

Here, below you can find some code snippets, which I was using during the geolocation test phase. Therefore I heve ported a small class to Vala, which is able to obtain and parse the geolocation related data from services like: http://freegeoip.net/json/. The output is a Json string:

{"ip":"78.52.9.161","country_code":"DE","country_name":"Germany","region_code":"16","region_name":"Berlin","city":"Berlin","zipcode":"","latitude":52.5167,"longitude":13.4,"metro_code":"","areacode":""}

   1 MainLoop main_loop;
   2 
   3 public static void main () {
   4     var monitor = new GeoInfo.LocationLookUp ();
   5     monitor.server = "https://geoip.fedoraproject.org/city";
   6     monitor.search.begin ((obj, res) => {
   7         GeoInfo.LocationInfo? location = monitor.search.end (res);
   8 
   9         if (location != null ) {
  10             stdout.printf ("Latitude: %lf\nLongitude: %lf\n",
  11                            location.latitude,
  12                            location.longitude);
  13 
  14             string? str = location.country_name;
  15             if (str != null) {
  16                 stdout.printf ("Country name: %s\n", str);
  17             }
  18             
  19             str = location.country_code;
  20             if (str != null) {
  21                 stdout.printf ("Country code: %s\n", str);
  22             }
  23             
  24             str = location.time_zone;
  25             if (str != null) {
  26                 stdout.printf ("Time zone: %s\n", str);
  27             }
  28         }
  29         
  30         main_loop.quit ();
  31     });
  32     
  33     main_loop = new MainLoop ();
  34     main_loop.run ();
  35 }

I was using the latest libsoup Vala binding for the asynchronous calls, so better to be on the safe side try it out under the jhbuild with the latest corresponding libraries and Vala versions installed.

$ jhbuild shell
$ make
valac  --thread --enable-experimental --pkg libsoup-2.4 location-lookup.vala location-info.vala main.vala --target-glib=2.38 --pkg gio-2.0 --pkg json-glib-1.0
$ ./location-lookup 
Latitude: 52.516701
Longitude: 13.400000
Country name: Germany
Country code: DE
Timezone: Europe/Berlin

The source code of this sample can be found here: location-lookup.tar.gz.

Helper Tools

To find matches in the libgweather database between the city names and the corresponding timezone on one side with the longitude and latitude data on the other side the following helper tool was written:

helper-tool.png

It's source code can be found here: helper-tool.tar.gz.

External libraries in Clocks

Libgweather

The full source code can be found here libgweather-vala.tar.gz

Geoclue

   1 [DBus (name = "org.freedesktop.GeoClue2.Manager")]
   2 interface Manager : Object {
   3     public abstract async void get_client (out string client_path) throws IOError;
   4 }
   5 
   6 [DBus (name = "org.freedesktop.GeoClue2.Client")]
   7 interface Client : Object {
   8     public abstract string location { owned get; }
   9     public abstract uint distance_threshold { get; set; }
  10     public abstract async void start () throws IOError;
  11     public abstract async void stop () throws IOError;
  12     public signal void location_updated (string old_path, string new_path);
  13 }
  14 
  15 [DBus (name = "org.freedesktop.GeoClue2.Location")]
  16 interface Location : Object {
  17     public abstract double latitude { get; }
  18     public abstract double longitude { get; }
  19     public abstract double accuracy { get; }
  20     public abstract string description { owned get; }
  21 }
  22 
  23 class FindLocation : Object {
  24     public static MainLoop main_loop = new MainLoop ();
  25 
  26     public FindLocation () {
  27         Environment.set_application_name ("Find Location");
  28     }
  29 
  30     public async void seek () {
  31         Manager manager;
  32         Client client;
  33 
  34         string? client_path = null;
  35 
  36         try {
  37             manager = yield Bus.get_proxy (GLib.BusType.SYSTEM,
  38                                            "org.freedesktop.GeoClue2",
  39                                            "/org/freedesktop/GeoClue2/Manager");
  40         } catch (IOError e) {
  41             error ("Failed to connect to GeoClue2 Manager service: %s", e.message); 
  42         }
  43 
  44         try {
  45             yield manager.get_client (out client_path);
  46         } catch (IOError e) {
  47             error ("Failed to connect to GeoClue2 Manager service: %s", e.message); 
  48         }
  49 
  50         if (client_path == null) {
  51             error ("The client path is not set");
  52         }
  53 
  54         stdout.printf ("Client object: %s\n", client_path);
  55 
  56         try {
  57             client = yield Bus.get_proxy (GLib.BusType.SYSTEM,
  58                                           "org.freedesktop.GeoClue2",
  59                                           client_path);
  60         } catch (IOError e) {
  61             error ("Failed to connect to GeoClue2 Client service: %s", e.message); 
  62         }
  63 
  64         client.location_updated.connect (on_location_updated);
  65 
  66         try {               
  67             yield client.start ();
  68         } catch (IOError e) {
  69             error ("Failed to start client: %s", e.message);
  70         }
  71     } 
  72 
  73     public async void on_location_updated (string old_path, string new_path) {
  74         Location location;
  75         try {
  76             location = yield Bus.get_proxy (GLib.BusType.SYSTEM,
  77                                             "org.freedesktop.GeoClue2",
  78                                             new_path);
  79         } catch (Error e) {
  80             error ("Failed to connect to GeoClue2 Location service: %s", e.message); 
  81         }
  82 
  83         stdout.printf ("Latitude: %lf\nLongitude: %lf\nAccuracy (in meters): %lf\n",
  84                        location.latitude,
  85                        location.longitude,
  86                        location.accuracy);
  87 
  88         string desc = location.description;
  89         if (desc != null)
  90             stdout.printf ("Description: %s\n", desc);
  91 
  92         main_loop.quit ();
  93     }
  94  
  95     public static void main () {
  96         var fl = new FindLocation ();
  97         fl.seek.begin ();
  98 
  99         main_loop.run ();
 100     }
 101 }

$ jhbuild shell
under jhbuild
$ make
valac find-location.vala --target-glib=2.38 --pkg gio-2.0
$ ./find-location 
Client object: /org/freedesktop/GeoClue2/Client/1
Latitude: 52.516700
Longitude: 13.400000
Accuracy (in meters): 15000.000000
Description: Berlin, Berlin, Germany

GeocodeGlib

   1 void main () {
   2     var main_loop = new MainLoop ();
   3 
   4     var location = new Geocode.Location (46.2000, 10.1330, Geocode.LOCATION_ACCURACY_UNKNOWN);
   5     var reverse = new Geocode.Reverse.for_location (location);
   6    
   7     reverse.resolve_async.begin (null, (obj, res) => {
   8         try {
   9             Geocode.Place place = reverse.resolve_async.end (res);
  10 
  11             stdout.printf ("country code: %s\n", place.get_country_code ());
  12         } catch (Error e) {
  13             error ("Failed to reverse location");
  14         }
  15         main_loop.quit ();
  16     });
  17 
  18     main_loop.run ();
  19 }

$ jhbuild shell
under jhbuild
$ make
valac --target-glib=2.38 reverse.vala --pkg geocode-glib-1.0
$ ./reverse 
country code: it

GUADEC

Within the frame of the GSOC 2013 project I have participated in the GUADEC conference, which took place in Brno, Czech Republic.

guadec.jpg

Picture is take from aakash's blog post.

My travel to the conference was sponsored by the GNOME Foundation.

sponsored-badge-simple.png

Outreach/SummerOfCode/2013/Projects/EvgenyBobkin_GnomeClocks (last edited 2013-12-03 18:34:15 by WilliamJonMcCann)