[gnome-maps] Refactor location related code into separate classes



commit d64ca9196892feac50a6df5f612f9d2df196acc2
Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
Date:   Fri Apr 12 03:10:23 2013 +0300

    Refactor location related code into separate classes
    
    MapView class is very quickly becoming bloated so better do something
    about it already before it becomes totally unmanageable.

 src/Makefile-js.am  |    4 +-
 src/mapLocation.js  |   89 ++++++++++++++++++++++
 src/mapView.js      |  206 +++++++++++----------------------------------------
 src/userLocation.js |  119 +++++++++++++++++++++++++++++
 4 files changed, 256 insertions(+), 162 deletions(-)
---
diff --git a/src/Makefile-js.am b/src/Makefile-js.am
index d80eaa4..2fe2c1c 100644
--- a/src/Makefile-js.am
+++ b/src/Makefile-js.am
@@ -3,10 +3,12 @@ dist_js_DATA = \
     application.js \
     main.js \
     mainWindow.js \
+    mapLocation.js \
     mapView.js \
     path.js \
     properties.js \
-    utils.js
+    utils.js \
+    userLocation.js
 
 BUILT_SOURCES += \
     path.js \
diff --git a/src/mapLocation.js b/src/mapLocation.js
new file mode 100644
index 0000000..104f7ab
--- /dev/null
+++ b/src/mapLocation.js
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2011, 2012, 2013 Red Hat, Inc.
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ */
+
+const Clutter = imports.gi.Clutter;
+const Champlain = imports.gi.Champlain;
+const Geocode = imports.gi.GeocodeGlib;
+
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+
+const Utils = imports.utils;
+const Path = imports.path;
+const _ = imports.gettext.gettext;
+
+const MapLocation = new Lang.Class({
+    Name: 'MapLocation',
+
+    _init: function(geocodeLocation, mapView) {
+        this._mapView = mapView;
+        this._view = mapView.view;
+        this.latitude = geocodeLocation.latitude;
+        this.longitude = geocodeLocation.longitude;
+        this.description = geocodeLocation.description;
+        this.accuracy = geocodeLocation.accuracy;
+    },
+
+    goTo: function(animate) {
+        log("Going to " + this.description);
+
+        let zoom = Utils.getZoomLevelForAccuracy(this.accuracy);
+
+        if (!animate) {
+            this._view.center_on(this.latitude, this.longitude);
+            this._view.set_zoom_level(zoom);
+
+            return;
+        }
+
+        /* Lets first ensure that both current and destination location are visible
+         * before we start the animated journey towards destination itself. We do this
+         * to create the zoom-out-then-zoom-in effect that many map implementations
+         * do. This not only makes the go-to animation look a lot better visually but
+         * also give user a good idea of where the destination is compared to current
+         * location.
+         */
+        let locations = new Array();
+        locations[0] = new Geocode.Location({ latitude: this._view.get_center_latitude(),
+                                              longitude: this._view.get_center_longitude() });
+        locations[1] = this;
+        this._mapView.ensureVisible(locations);
+
+        let anim_completed_id = this._view.connect("animation-completed::go-to", Lang.bind(this,
+            function() {
+                // Apparently the signal is called before animation is really complete so if we don't
+                // zoom in idle, we get a crash. Perhaps a bug in libchamplain?
+                Mainloop.idle_add(Lang.bind(this,
+                    function() {
+                        this._view.set_zoom_level(zoom);
+                    }));
+                this._view.disconnect(anim_completed_id);
+            }));
+        this._view.go_to(this.latitude, this.longitude);
+    },
+
+    addMarker: function(layer) {
+        let marker = new Champlain.Label();
+        marker.set_text(this.description);
+        marker.set_location(this.latitude, this.longitude);
+        layer.add_marker(marker);
+        log("Added marker at " + this.latitude + ", " + this.longitude);
+    },
+});
diff --git a/src/mapView.js b/src/mapView.js
index 9c63f9e..0a047d2 100644
--- a/src/mapView.js
+++ b/src/mapView.js
@@ -35,6 +35,8 @@ const Application = imports.application;
 const Properties = imports.properties;
 const Utils = imports.utils;
 const Path = imports.path;
+const MapLocation = imports.mapLocation;
+const UserLocation = imports.userLocation;
 const _ = imports.gettext.gettext;
 
 const MapType = {
@@ -52,20 +54,20 @@ const MapView = new Lang.Class({
         this.parent();
 
         this.actor = this.get_view();
-        this._view = this.actor;
+        this.view = this.actor;
 
         this._properties = new Properties.Properties(this);
-        this._view.bin_layout_add(this._properties.actor,
-                                  Clutter.BinAlignment.FILL,
-                                  Clutter.BinAlignment.FILL);
+        this.view.bin_layout_add(this._properties.actor,
+                                 Clutter.BinAlignment.FILL,
+                                 Clutter.BinAlignment.FILL);
 
         this._markerLayer = new Champlain.MarkerLayer();
         this._markerLayer.set_selection_mode(Champlain.SelectionMode.SINGLE);
-        this._view.add_layer(this._markerLayer);
+        this.view.add_layer(this._markerLayer);
 
         this._userLocationLayer = new Champlain.MarkerLayer();
         this._userLocationLayer.set_selection_mode(Champlain.SelectionMode.NONE);
-        this._view.add_layer(this._userLocationLayer);
+        this.view.add_layer(this._userLocationLayer);
 
         this._factory = Champlain.MapSourceFactory.dup_default();
         this.setMapType(MapType.STREET);
@@ -75,7 +77,7 @@ const MapView = new Lang.Class({
 
     setMapType: function(mapType) {
         let source = this._factory.create_cached_source(mapType);
-        this._view.set_map_source(source);
+        this.view.set_map_source(source);
     },
 
     geocodeSearch: function(string) {
@@ -86,128 +88,44 @@ const MapView = new Lang.Class({
                 try {
                     let locations = forward.search_finish(res);
                     log (locations.length + " locations found");
-                    this._showLocations(locations);
+                    let mapLocations = new Array();
+                    locations.forEach(Lang.bind(this,
+                        function(location) {
+                            let mapLocation = new UserLocation.UserLocation(location, this);
+                            mapLocations.push(mapLocation);
+                        }));
+                    this._showLocations(mapLocations);
                 } catch (e) {
                     log ("Failed to search '" + string + "': " + e.message);
                 }
             }));
     },
 
-    _gotoLocation: function(location, animate) {
-        log(location.description);
-
-        let zoom = Utils.getZoomLevelForAccuracy(location.accuracy);
-
-        if (!animate) {
-            this._view.center_on(location.latitude, location.longitude);
-            this._view.set_zoom_level(zoom);
-
-            return;
-        }
-
-        /* Lets first ensure that both current and destination location are visible
-         * before we start the animated journey towards destination itself. We do this
-         * to create the zoom-out-then-zoom-in effect that many map implementations
-         * do. This not only makes the go-to animation look a lot better visually but
-         * also give user a good idea of where the destination is compared to current
-         * location.
-         */
-        let locations = new Array();
-        locations[0] = new Geocode.Location({ latitude: this._view.get_center_latitude(),
-                                              longitude: this._view.get_center_longitude() });
-        locations[1] = location;
-        this._ensureVisible(locations);
-
-        let anim_completed_id = this._view.connect("animation-completed::go-to", Lang.bind(this,
-            function() {
-                // Apparently the signal is called before animation is really complete so if we don't
-                // zoom in idle, we get a crash. Perhaps a bug in libchamplain?
-                Mainloop.idle_add(Lang.bind(this,
-                    function() {
-                        this._view.set_zoom_level(zoom);
-                    }));
-                this._view.disconnect(anim_completed_id);
-            }));
-        this._view.go_to(location.latitude, location.longitude);
-    },
-
-    _showUserLocation: function(location, animate) {
-        if (location.accuracy == Geocode.LOCATION_ACCURACY_UNKNOWN)
-            return;
-
-        this._gotoLocation(location, animate);
-
-        this._userLocationLayer.remove_all();
-
-        let locationMarker = new Champlain.CustomMarker();
-        try {
-            let pixbuf = GdkPixbuf.Pixbuf.new_from_file(Path.ICONS_DIR + "/pin.svg");
-            let image = new Clutter.Image();
-            image.set_data(pixbuf.get_pixels(),
-                           Cogl.PixelFormat.RGBA_8888,
-                           pixbuf.get_width(),
-                           pixbuf.get_height(),
-                           pixbuf.get_rowstride());
-
-            locationMarker.set_location(location.latitude, location.longitude);
-            locationMarker.set_reactive(false);
-            // FIXME: Using deprecated function here cause I failed to get the same result
-            //        with locationMarker.set_pivot_point(0.5, 0).
-            locationMarker.set_anchor_point_from_gravity(Clutter.Gravity.SOUTH);
-            let actor = new Clutter.Actor();
-            actor.set_content(image);
-            actor.set_size(pixbuf.get_width(), pixbuf.get_height());
-            locationMarker.add_actor(actor);
-        } catch(e) {
-            log("Failed to load image: " + e.message);
-            return;
-        }
-
-        if (location.accuracy == 0) {
-            this._userLocationLayer.add_marker(locationMarker);
-            return;
-        }
-
-        // FIXME: Perhaps this is a misuse of Champlain.Point class and we
-        // should draw the cirlce ourselves using Champlain.CustomMarker?
-        // Although for doing so we'll need to add a C lib as cairo isn't
-        // exactly introspectable.
-        let accuracyMarker = new Champlain.Point();
-        accuracyMarker.set_color(new Clutter.Color({ red: 0,
-                                                     blue: 255,
-                                                     green: 0,
-                                                     alpha: 50 }));
-        accuracyMarker.set_location(location.latitude, location.longitude);
-        accuracyMarker.set_reactive(false);
+    ensureVisible: function(locations) {
+        let min_latitude = 90;
+        let max_latitude = -90;
+        let min_longitude = 180;
+        let max_longitude = -180;
 
-        let allocSize = Lang.bind(this,
-            function(zoom) {
-                let source = this._view.get_map_source();
-                let metersPerPixel = source.get_meters_per_pixel(zoom, location.latitude, 
location.longitude);
-                let size = location.accuracy / metersPerPixel;
-                let viewWidth = this._view.get_width();
-                let viewHeight = this._view.get_height();
-                // Ensure we don't endup creating way too big texture/canvas,
-                // otherwise we easily end up with bus error
-                if ((viewWidth > 0 && viewHeight > 0) &&
-                    (size > viewWidth && size > viewHeight))
-                    // Pythagorean theorem to get diagonal length of the view
-                    size = Math.sqrt(Math.pow(viewWidth, 2) + Math.pow(viewHeight, 2));
+        locations.forEach(Lang.bind(this,
+            function(location) {
+                if (location.latitude > max_latitude)
+                    max_latitude = location.latitude;
+                if (location.latitude < min_latitude)
+                    min_latitude = location.latitude;
+                if (location.longitude > max_longitude)
+                    max_longitude = location.longitude;
+                if (location.longitude < min_longitude)
+                    min_longitude = location.longitude;
+                }));
 
-                accuracyMarker.set_size(size);
-            });
-        let zoom = Utils.getZoomLevelForAccuracy(location.accuracy);
-        allocSize(zoom);
-        this._userLocationLayer.add_marker(accuracyMarker);
-        this._userLocationLayer.add_marker(locationMarker);
+        let bbox = new Champlain.BoundingBox();
+        bbox.left = min_longitude;
+        bbox.right = max_longitude;
+        bbox.bottom = min_latitude;
+        bbox.top = max_latitude;
 
-        if (this._zoomLevelId > 0)
-            this._view.disconnect(this._zoomLevelId);
-        this._zoomLevelId = this._view.connect("notify::zoom-level", Lang.bind(this,
-            function() {
-                let zoom = this._view.get_zoom_level();
-                allocSize(zoom);
-            }));
+        this.view.ensure_visible(bbox, true);
     },
 
     _gotoUserLocation: function() {
@@ -223,7 +141,8 @@ const MapView = new Lang.Class({
             let lastLocationDescription = Application.settings.get_string('last-location-description');
             location.set_description(lastLocationDescription);
 
-            this._showUserLocation(location, false);
+            let userLocation = new UserLocation.UserLocation(location, this);
+            userLocation.show(false, this._userLocationLayer);
         }
 
         let ipclient = new Geocode.Ipclient();
@@ -234,7 +153,8 @@ const MapView = new Lang.Class({
                 try {
                     let location = ipclient.search_finish(res);
 
-                    this._showUserLocation(location, true);
+                    let userLocation = new UserLocation.UserLocation(location, this);
+                    userLocation.show(true, this._userLocationLayer);
 
                     let variant = GLib.Variant.new('ad', [location.latitude, location.longitude, 
location.accuracy]);
                     Application.settings.set_value('last-location', variant);
@@ -252,48 +172,12 @@ const MapView = new Lang.Class({
 
         locations.forEach(Lang.bind(this,
             function(location) {
-                this._addMarker(location);
+                location.addMarker(this._markerLayer);
             }));
 
         if (locations.length == 1)
-            this._gotoLocation(locations[0], true);
+            locations[0].goTo(true);
         else
-            this._ensureVisible(locations);
+            this.ensureVisible(locations);
     },
-
-    _addMarker: function(location) {
-        log ("location: " + location);
-        let marker = new Champlain.Label();
-        marker.set_text(location.description);
-        marker.set_location(location.latitude, location.longitude);
-        this._markerLayer.add_marker(marker);
-        log ("Added marker at " + location.latitude + ", " + location.longitude);
-    },
-
-    _ensureVisible: function(locations) {
-        let min_latitude = 90;
-        let max_latitude = -90;
-        let min_longitude = 180;
-        let max_longitude = -180;
-
-        locations.forEach(Lang.bind(this,
-            function(location) {
-                if (location.latitude > max_latitude)
-                    max_latitude = location.latitude;
-                if (location.latitude < min_latitude)
-                    min_latitude = location.latitude;
-                if (location.longitude > max_longitude)
-                    max_longitude = location.longitude;
-                if (location.longitude < min_longitude)
-                    min_longitude = location.longitude;
-                }));
-
-        let bbox = new Champlain.BoundingBox();
-        bbox.left = min_longitude;
-        bbox.right = max_longitude;
-        bbox.bottom = min_latitude;
-        bbox.top = max_latitude;
-
-        this._view.ensure_visible(bbox, true);
-    }
 });
diff --git a/src/userLocation.js b/src/userLocation.js
new file mode 100644
index 0000000..486be5a
--- /dev/null
+++ b/src/userLocation.js
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2011, 2012, 2013 Red Hat, Inc.
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ */
+
+const Clutter = imports.gi.Clutter;
+const Cogl = imports.gi.Cogl;
+const GdkPixbuf = imports.gi.GdkPixbuf;
+const Champlain = imports.gi.Champlain;
+const Geocode = imports.gi.GeocodeGlib;
+
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+
+const MapLocation = imports.mapLocation;
+const Utils = imports.utils;
+const Path = imports.path;
+const _ = imports.gettext.gettext;
+
+const UserLocation = new Lang.Class({
+    Name: 'UserLocation',
+    Extends: MapLocation.MapLocation,
+
+    show: function(animate, layer) {
+        if (this.accuracy == Geocode.LOCATION_ACCURACY_UNKNOWN)
+            return;
+
+        this.goTo(animate);
+
+        layer.remove_all();
+
+        let locationMarker = new Champlain.CustomMarker();
+        try {
+            let pixbuf = GdkPixbuf.Pixbuf.new_from_file(Path.ICONS_DIR + "/pin.svg");
+            let image = new Clutter.Image();
+            image.set_data(pixbuf.get_pixels(),
+                           Cogl.PixelFormat.RGBA_8888,
+                           pixbuf.get_width(),
+                           pixbuf.get_height(),
+                           pixbuf.get_rowstride());
+
+            locationMarker.set_location(this.latitude, this.longitude);
+            locationMarker.set_reactive(false);
+            // FIXME: Using deprecated function here cause I failed to get the same result
+            //        with locationMarker.set_pivot_point(0.5, 0).
+            locationMarker.set_anchor_point_from_gravity(Clutter.Gravity.SOUTH);
+            let actor = new Clutter.Actor();
+            actor.set_content(image);
+            actor.set_size(pixbuf.get_width(), pixbuf.get_height());
+            locationMarker.add_actor(actor);
+        } catch(e) {
+            log("Failed to load image: " + e.message);
+            return;
+        }
+
+        if (this.accuracy == 0) {
+            layer.add_marker(locationMarker);
+            return;
+        }
+
+        // FIXME: Perhaps this is a misuse of Champlain.Point class and we
+        // should draw the cirlce ourselves using Champlain.CustomMarker?
+        // Although for doing so we'll need to add a C lib as cairo isn't
+        // exactly introspectable.
+        let accuracyMarker = new Champlain.Point();
+        accuracyMarker.set_color(new Clutter.Color({ red: 0,
+                                                     blue: 255,
+                                                     green: 0,
+                                                     alpha: 50 }));
+        accuracyMarker.set_location(this.latitude, this.longitude);
+        accuracyMarker.set_reactive(false);
+
+        let allocSize = Lang.bind(this,
+            function(zoom) {
+                let source = this._view.get_map_source();
+                let metersPerPixel = source.get_meters_per_pixel(zoom,
+                                                                 this.latitude,
+                                                                 this.longitude);
+                let size = this.accuracy / metersPerPixel;
+                let viewWidth = this._view.get_width();
+                let viewHeight = this._view.get_height();
+                // Ensure we don't endup creating way too big texture/canvas,
+                // otherwise we easily end up with bus error
+                if ((viewWidth > 0 && viewHeight > 0) &&
+                    (size > viewWidth && size > viewHeight))
+                    // Pythagorean theorem to get diagonal length of the view
+                    size = Math.sqrt(Math.pow(viewWidth, 2) + Math.pow(viewHeight, 2));
+
+                accuracyMarker.set_size(size);
+            });
+        let zoom = Utils.getZoomLevelForAccuracy(this.accuracy);
+        allocSize(zoom);
+        layer.add_marker(accuracyMarker);
+        layer.add_marker(locationMarker);
+
+        if (this._zoomLevelId > 0)
+            this._view.disconnect(this._zoomLevelId);
+        this._zoomLevelId = this._view.connect("notify::zoom-level", Lang.bind(this,
+            function() {
+                let zoom = this._view.get_zoom_level();
+                allocSize(zoom);
+            }));
+    },
+});


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