diff --git a/www/js/app.js b/www/js/app.js
index bfb9c484e036898af47ab1bd1374cb7a84a7bffe..d5682e660087311dfa8219cf66353d16d9a8dde0 100644
--- a/www/js/app.js
+++ b/www/js/app.js
@@ -137,6 +137,9 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'pascalprecht
   }
   // endRemoveIf(device)
 
+  // Must be done before any other $stateChangeStart listeners
+  csPlatform.disableChangeState();
+
   // removeIf(android)
   // removeIf(ios)
   // removeIf(firefoxos)
@@ -274,7 +277,7 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'pascalprecht
     }
 
     // Make sure platform is started
-    return csPlatform.start();
+    return csPlatform.ready();
   });
 })
 ;
diff --git a/www/js/platform.js b/www/js/platform.js
index f126da8e19aa4ba53174731f4698b0e4820af195..2c9d9e1ef3328952987f647575058965584d6a45 100644
--- a/www/js/platform.js
+++ b/www/js/platform.js
@@ -10,11 +10,12 @@ angular.module('cesium.platform', ['cesium.config', 'cesium.services'])
       defaultSettingsNode,
       started = false,
       startPromise,
-      readyDeffered,
       listeners,
       removeChangeStateListener;
 
     function disableChangeState() {
+      if (removeChangeStateListener) return; // make sure to call this once
+
       var remove = $rootScope.$on('$stateChangeStart', function (event, next, nextParams, fromState) {
         if (!event.defaultPrevented && next.name !== 'app.home' && next.name !== 'app.settings') {
           event.preventDefault();
@@ -87,10 +88,7 @@ angular.module('cesium.platform', ['cesium.config', 'cesium.services'])
 
     function ready() {
       if (started) return $q.when();
-      if (startPromise) return startPromise;
-      if (readyDeffered) return readyDeffered.promise;
-      readyDeffered = $q.defer();
-      return readyDeffered.promise;
+      return startPromise || start();
     }
 
     function restart() {
@@ -102,7 +100,6 @@ angular.module('cesium.platform', ['cesium.config', 'cesium.services'])
     }
 
     function start() {
-      enableChangeState();
 
       // Avoid change state
       disableChangeState();
@@ -134,8 +131,6 @@ angular.module('cesium.platform', ['cesium.config', 'cesium.services'])
           addListeners();
           startPromise = null;
           started = true;
-          if (readyDeffered) readyDeffered.resolve();
-          readyDeffered = null;
         })
         .catch(function(err) {
           startPromise = null;
@@ -143,8 +138,6 @@ angular.module('cesium.platform', ['cesium.config', 'cesium.services'])
           if($state.current.name !== 'app.home') {
             $state.go('app.home', {error: 'peer'});
           }
-          if (readyDeffered) readyDeffered.reject(err);
-          readyDeffered = null;
           throw err;
         });
 
@@ -167,6 +160,7 @@ angular.module('cesium.platform', ['cesium.config', 'cesium.services'])
     }
 
     return  {
+      disableChangeState: disableChangeState,
       isStarted: isStarted,
       ready: ready,
       restart: restart,
diff --git a/www/plugins/es/js/services/geo-services.js b/www/plugins/es/js/services/geo-services.js
index b92660f9a9feb6d33d480ed34527afe1d7f3305e..c2e77408cd79de42fa33968879912e7a4d02a0be 100644
--- a/www/plugins/es/js/services/geo-services.js
+++ b/www/plugins/es/js/services/geo-services.js
@@ -22,17 +22,15 @@ angular.module('cesium.es.geo.services', ['cesium.services', 'cesium.es.http.ser
         searchByQuery: csHttp.get('nominatim.openstreetmap.org', 443, '/search.php?format=json')
       },
       google: {
+        apiKey: csConfig.plugins && csConfig.plugins.es && csConfig.plugins.es.googleApiKey,
         search: csHttp.get('maps.google.com', 443, '/maps/api/geocode/json')
       },
       searchByIP: csHttp.get('freegeoip.net', 80, '/json/:ip')
     };
 
-    function _fallbackSearchPositionByString(err, address) {
+    function googleSearchPositionByString(address) {
 
-      console.debug('[ES] [geo] Search position failed on [OSM]. Trying [google] service');
-      var apiKey = csConfig.plugins && csConfig.plugins.es && csConfig.plugins.googleApiKey;
-
-      return that.raw.google.search({address: address, key: apiKey})
+      return that.raw.google.search({address: address, key: that.raw.google.apiKey})
         .then(function(res) {
           if (!res || !res.results || !res.results.length) return;
           return res.results.reduce(function(res, hit) {
@@ -44,10 +42,17 @@ angular.module('cesium.es.geo.services', ['cesium.services', 'cesium.es.http.ser
               lon: hit.geometry && hit.geometry.location && hit.geometry.location.lng
             });
           }, []);
-        })
-        .catch(function() {
+        });
+    }
+
+    function _fallbackSearchPositionByString(osmErr, address) {
+
+      console.debug('[ES] [geo] Search position failed on [OSM]. Trying [google] service');
+
+      return googleSearchPositionByString(address)
+        .catch(function(googleErr) {
           console.debug('[ES] [geo] Search position failed on [google] service');
-          throw err; // throw first error (OMS error)
+          throw osmErr || googleErr; // throw first OMS error if exists
         });
     }
 
@@ -126,6 +131,12 @@ angular.module('cesium.es.geo.services', ['cesium.services', 'cesium.es.http.ser
         current: getCurrentPosition,
         searchByAddress: searchhPositionByQuery,
         searchByIP: searchPositionByIP
+      },
+      google: {
+        hasApiKey : function() {
+          return !!that.raw.google.apiKey;
+        },
+        searchByAddress: googleSearchPositionByString
       }
     };
   });
diff --git a/www/plugins/map/js/controllers/wot-controllers.js b/www/plugins/map/js/controllers/wot-controllers.js
index 65e415ab663c304907bc705bed08f0ab58db3651..391cf86def6600ccc160493f4c44f8dccf203d29 100644
--- a/www/plugins/map/js/controllers/wot-controllers.js
+++ b/www/plugins/map/js/controllers/wot-controllers.js
@@ -159,7 +159,7 @@ angular.module('cesium.map.wot.controllers', ['cesium.services', 'cesium.map.ser
       $scope.loading = true;
 
       // Load wot data
-      return mapWot.load({bounds: $scope.map.bounds})
+      return mapWot.load()
 
         .then(function(res) {
           if (res && res.length) {
@@ -192,7 +192,7 @@ angular.module('cesium.map.wot.controllers', ['cesium.services', 'cesium.map.ser
                 focus: false,
                 message: markerTemplate
               };
-              var id = hit.uid ? (hit.uid + ':' + hit.pubkey) : hit.pubkey;
+              var id = (hit.uid ? (hit.uid + ':' + hit.pubkey) : hit.pubkey).replace(/-/g, '_');
               var wasExisting = !!$scope.map.markers[id];
               $scope.map.markers[id] = marker;
 
@@ -235,7 +235,6 @@ angular.module('cesium.map.wot.controllers', ['cesium.services', 'cesium.map.ser
     // Update the browser location, to be able to refresh the page
     $scope.$on("centerUrlHash", function(event, centerHash) {
       if (!$scope.loading) {
-        $scope.load();
 
         return $timeout(function() {
           $scope.updateLocationHref(centerHash);
diff --git a/www/plugins/map/js/services/wot-services.js b/www/plugins/map/js/services/wot-services.js
index e38f4ba5c3f9dd38d23eb9cdedc9c060b8926251..a4f71a2c65926861be1cad72014bcab8e023efe3 100644
--- a/www/plugins/map/js/services/wot-services.js
+++ b/www/plugins/map/js/services/wot-services.js
@@ -1,7 +1,7 @@
 
 angular.module('cesium.map.wot.services', ['cesium.services'])
 
-.factory('mapWot', function($q, csHttp, esHttp, csWot, BMA) {
+.factory('mapWot', function($q, csHttp, esHttp, csWot, BMA, esGeo) {
   'ngInject';
 
   var
@@ -10,7 +10,7 @@ angular.module('cesium.map.wot.services', ['cesium.services'])
       DEFAULT_LOAD_SIZE: 1000
     },
     fields = {
-      profile: ["title", "geoPoint", "avatar._content_type", "city", "description"]
+      profile: ["title", "geoPoint", "avatar._content_type", "address", "city", "description"]
     };
 
   that.raw = {
@@ -20,29 +20,38 @@ angular.module('cesium.map.wot.services', ['cesium.services'])
   };
 
   function createFilterQuery(options) {
+    options = options || {};
     var query = {
-      bool: {
-        must: [
-          {exists: {field: "geoPoint"}}
-        ]
-      }
+      bool: {}
     };
 
+    // Limit to profile with geo point
+    if (esGeo.google.hasApiKey()) {
+      query.bool.should = [
+        {exists: {field: "geoPoint"}},
+        {exists: {field: "address"}},
+        {exists: {field: "city"}}
+      ];
+    }
+    else {
+      query.bool.must= [
+        {exists: {field: "geoPoint"}}
+      ];
+    }
+
     // Filter on bounding box
     // see https://www.elastic.co/guide/en/elasticsearch/reference/2.4/geo-point.html
-    if (options && options.bounds) {
-
-      query.bool.filter = {
-        "geo_bounding_box" : {
-          "geoPoint" : {
-            "top_left" : {
-              "lat" : Math.max(Math.min(options.bounds.northEast.lat, 90), -90),
-              "lon" : Math.max(Math.min(options.bounds.southWest.lng, 180), -180)
-            },
-            "bottom_right" : {
-              "lat" : Math.max(Math.min(options.bounds.southWest.lat, 90), -90),
-              "lon" : Math.max(Math.min(options.bounds.northEast.lng, 180), -180)
-            }
+    if (options.bounds && options.bounds.northEast && options.bounds.southWest) {
+      query.bool.should = query.bool.should || {};
+      query.bool.should.geo_bounding_box = {
+        "geoPoint" : {
+          "top_left" : {
+            "lat" : Math.max(Math.min(options.bounds.northEast.lat, 90), -90),
+            "lon" : Math.max(Math.min(options.bounds.southWest.lng, 180), -180)
+          },
+          "bottom_right" : {
+            "lat" : Math.max(Math.min(options.bounds.southWest.lat, 90), -90),
+            "lon" : Math.max(Math.min(options.bounds.northEast.lng, 180), -180)
           }
         }
       };
@@ -98,23 +107,32 @@ angular.module('cesium.map.wot.services', ['cesium.services'])
 
         // Transform profile hits
         var commaRegexp = new RegExp('[,]');
-        res = res.hits.hits.reduce(function(res, hit) {
+        var noPositionItems = [];
+        var items = res.hits.hits.reduce(function(res, hit) {
           var pubkey =  hit._id;
 
           var uid = uids[pubkey];
           var item = uid && {uid: uid} || memberships[pubkey] || {};
           item.pubkey = pubkey;
 
+          // City & address
+          item.city = hit._source.city;
+          item.fullAddress = item.city && ((hit._source.address ? hit._source.address+ ', ' : '') + item.city);
+
           // Set geo point
           item.geoPoint = hit._source.geoPoint;
-          if (!item.geoPoint || !item.geoPoint.lat || !item.geoPoint.lon) return res;
-
-          // Convert lat/lon to float (if need)
-          if (item.geoPoint.lat && typeof item.geoPoint.lat === 'string') {
-            item.geoPoint.lat = parseFloat(item.geoPoint.lat.replace(commaRegexp, '.'));
+          if (!item.geoPoint || !item.geoPoint.lat || !item.geoPoint.lon) {
+            if (!item.fullAddress) return res; // no city: exclude this item
+            noPositionItems.push(item);
           }
-          if (item.geoPoint.lon && typeof item.geoPoint.lon === 'string') {
-            item.geoPoint.lon = parseFloat(item.geoPoint.lon.replace(commaRegexp, '.'));
+          else {
+            // Convert lat/lon to float (if need)
+            if (item.geoPoint.lat && typeof item.geoPoint.lat === 'string') {
+              item.geoPoint.lat = parseFloat(item.geoPoint.lat.replace(commaRegexp, '.'));
+            }
+            if (item.geoPoint.lon && typeof item.geoPoint.lon === 'string') {
+              item.geoPoint.lon = parseFloat(item.geoPoint.lon.replace(commaRegexp, '.'));
+            }
           }
 
           // Avatar
@@ -130,13 +148,33 @@ angular.module('cesium.map.wot.services', ['cesium.services'])
           // Description
           item.description = esHttp.util.trustAsHtml(hit._source.description);
 
-          // City
-          item.city = hit._source.city;
-
-          return res.concat(item);
+          return item.geoPoint ? res.concat(item) : res;
         }, []);
 
-        return csWot.extendAll(res, 'pubkey');
+        // Resolve missing positions by addresses (only if google API enable)
+        if (noPositionItems.length && esGeo.google.hasApiKey()) {
+          var now = new Date().getTime();
+          console.log('[map] [wot] Search positions of {0} addresses...'.format(noPositionItems.length));
+          var counter = 0;
+
+          return $q.all(noPositionItems.reduce(function(res, item) {
+            return res.concat(esGeo.google.searchByAddress(item.fullAddress)
+              .then(function(res) {
+                if (!res || !res.length) return;
+                item.geoPoint = res[0];
+                delete item.fullAddress;
+                items.push(item);
+                counter++;
+              })
+              .catch(function() {/*silent*/}));
+            }, []))
+              .then(function(){
+                console.log('[map] [wot] Resolved {0}/{1} addresses in {2}ms'.format(counter, noPositionItems.length, new Date().getTime()-now));
+                return items;
+              });
+        }
+
+        return items;
       });
   }