diff --git a/www/index.html b/www/index.html
index 2f61aada146f0f95a7e721ef59a1bb99e7253daf..3ba552d6ae6bc873ac73138cf68a2a82ab4d238d 100644
--- a/www/index.html
+++ b/www/index.html
@@ -197,6 +197,7 @@
   <script src="dist/dist_js/plugins/map/js/services/utils-services.js"></script>
   <script src="dist/dist_js/plugins/map/js/controllers/wot-controllers.js"></script>
   <script src="dist/dist_js/plugins/map/js/controllers/network-controllers.js"></script>
+  <script src="dist/dist_js/plugins/map/js/controllers/user-controllers.js"></script>
 
   <!-- RML9 plugin -->
   <!--<script src="dist/dist_js/plugins/rml9/plugin-01-add_button.js"></script>-->
diff --git a/www/js/app.js b/www/js/app.js
index bcc256716449be2c3b46209c2d65d5d0b9c4ce43..c4ee9698cd4956458592d4dfb1aa9b58d4b4c567 100644
--- a/www/js/app.js
+++ b/www/js/app.js
@@ -281,6 +281,16 @@ if (typeof String.prototype.startsWith !== 'function') {
   };
 }
 
+// Workaround to add "".startsWith() if not present
+if (typeof String.prototype.trim !== 'function') {
+  console.debug("Adding String.prototype.trim() -> was missing on this platform");
+  // Make sure we trim BOM and NBSP
+  var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
+  String.prototype.trim = function() {
+    return this.replace(rtrim, '');
+  };
+}
+
 // Workaround to add Math.trunc() if not present - fix #144
 if (Math && typeof Math.trunc !== 'function') {
   console.debug("Adding Math.trunc() -> was missing on this platform");
diff --git a/www/js/controllers/network-controllers.js b/www/js/controllers/network-controllers.js
index bd0daf4174c3c312a5ae1d636345a274a8f4745f..8c07f40b02fb2c0b6b45c6083328db74cc5ce685 100644
--- a/www/js/controllers/network-controllers.js
+++ b/www/js/controllers/network-controllers.js
@@ -224,6 +224,7 @@ function NetworkLookupController($scope,  $state, $ionicHistory, $ionicPopover,
   };
 
   $scope.selectPeer = function(peer) {
+    if (!peer.online) return; // nothing to do
     var stateParams = {server: peer.getServer()};
     if (peer.isSsl()) {
       stateParams.ssl = true;
diff --git a/www/js/services/network-services.js b/www/js/services/network-services.js
index 4bf6deea3c9c178057176601bc016826da5cf457..2a49997c44072e208bae78a042fea73cc709144c 100644
--- a/www/js/services/network-services.js
+++ b/www/js/services/network-services.js
@@ -239,7 +239,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.bma.services', 'cesi
                 .then(function (refreshedPeer) {
                   if (existingPeer) {
                     // remove existing peers, when reject or offline
-                    if (!refreshedPeer || (refreshedPeer.online !== data.filter.online)) {
+                    if (!refreshedPeer || (refreshedPeer.online !== data.filter.online && data.filter.online !== 'all')) {
                       console.debug('[network] Peer [{0}] removed (cause: {1})'.format(peer.server, !refreshedPeer ? 'filtered' : (refreshedPeer.online ? 'UP': 'DOWN')));
                       data.peers.splice(data.peers.indexOf(existingPeer), 1);
                       hasUpdates = true;
@@ -249,15 +249,15 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.bma.services', 'cesi
                       hasUpdates = true;
                     }
                     else if (existingOnline !== refreshedPeer.online){
-                      console.debug('[network] Peer [{0}] is now UP'.format(refreshedPeer.server));
+                      console.debug('[network] Peer [{0}] is now {1}'.format(refreshedPeer.server, refreshedPeer.online ? 'UP' : 'DOWN'));
                       hasUpdates = true;
                     }
                     else {
                       console.debug("[network] Peer [{0}] unchanged".format(refreshedPeer.server));
                     }
                   }
-                  else if (refreshedPeer && (refreshedPeer.online === data.filter.online)) {
-                    console.debug("[network] Peer [{0}] is UP".format(refreshedPeer.server));
+                  else if (refreshedPeer && (refreshedPeer.online === data.filter.online || data.filter.online === 'all')) {
+                    console.debug("[network] Peer [{0}] is {1}".format(refreshedPeer.server, refreshedPeer.online ? 'UP' : 'DOWN'));
                     list.push(refreshedPeer);
                     hasUpdates = true;
                   }
@@ -308,14 +308,14 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.bma.services', 'cesi
         // Apply filter
         if (!applyPeerFilter(peer)) return $q.when();
 
-        if (!data.filter.online) {
+        if (!data.filter.online || (data.filter.online === 'all' && peer.status === 'DOWN')) {
           peer.online = false;
           return $q.when(peer);
         }
 
         // Cesium running in SSL: Do not try to access not SSL node,
         if (isHttpsMode && !peer.bma.useSsl) {
-          peer.online = (peer.status == 'UP');
+          peer.online = (peer.status === 'UP');
           peer.buid = constants.UNKNOWN_BUID;
           delete peer.version;
 
@@ -473,7 +473,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.bma.services', 'cesi
         });
         _.forEach(data.peers, function(peer){
           peer.hasMainConsensusBlock = peer.buid == mainBlock.buid;
-          peer.hasConsensusBlock = !peer.hasMainConsensusBlock && buids[peer.buid].count > 1;
+          peer.hasConsensusBlock = peer.buid && !peer.hasMainConsensusBlock && buids[peer.buid].count > 1;
           if (peer.hasConsensusBlock) {
             peer.consensusBlockDelta = buids[peer.buid].medianTime - mainBlock.medianTime;
           }
diff --git a/www/plugins/es/i18n/locale-en-GB.json b/www/plugins/es/i18n/locale-en-GB.json
index cf4c37c31311669ca0a4c83dc090c4ecaf6afdfd..a8fcc0de4aa904f23dba696fe7e58458434bd086 100644
--- a/www/plugins/es/i18n/locale-en-GB.json
+++ b/www/plugins/es/i18n/locale-en-GB.json
@@ -320,7 +320,9 @@
       "LOOKUP_RECORDS_FAILED": "Error while loading records.",
       "REMOVE_RECORD_FAILED": "Deleting failed",
       "SAVE_RECORD_FAILED": "Saving failed",
-      "RECORD_NOT_EXISTS": "Record not found"
+      "RECORD_NOT_EXISTS": "Record not found",
+      "FAILED_SAVE_COMMENT": "Saving comment failed",
+      "FAILED_REMOVE_COMMENT": "Deleting comment failed"
     },
     "INFO": {
       "RECORD_REMOVED" : "Reference successfully deleted"
@@ -331,6 +333,9 @@
     "NO_PROFILE_DEFINED": "No Cesium+ profile",
     "BTN_ADD": "Create my profile",
     "BTN_EDIT": "Edit my profile",
+    "BTN_GEOLOC_ADDRESS": "Update position from address",
+    "BTN_GEOLOC_ME": "Localize me",
+    "BTN_REMOVE_GEOLOC": "Remove position",
     "UID": "Pseudonym",
     "TITLE": "Lastname, FirstName",
     "TITLE_HELP": "Name",
@@ -344,6 +349,10 @@
     "GENERAL_DIVIDER": "General data",
     "LOCATION_DIVIDER": "Localisation",
     "SOCIAL_NETWORKS_DIVIDER": "Social networks and web site",
+    "LATITUDE": "Latitude",
+    "LATITUDE_HELP": "Latitude",
+    "LONGITUDE": "Longitude",
+    "LONGITUDE_HELP": "Longitude",
     "TECHNICAL_DIVIDER": "Technical data",
     "MODAL_AVATAR": {
       "TITLE": "Avatar",
@@ -356,7 +365,8 @@
       "LOAD_PROFILE_FAILED": "Could not load user profile.",
       "SAVE_PROFILE_FAILED": "Saving profile failed",
       "INVALID_SOCIAL_NETWORK_FORMAT": "Invalid format: please fill a valid Internet address.<br/><br/>Examples :<ul><li>- A Facebook page (https://www.facebook.com/user)</li><li>- A web page (http://www.domain.com)</li><li>- An email address (joe@dalton.com)</li></ul>",
-      "IMAGE_RESIZE_FAILED": "Error while resizing picture"
+      "IMAGE_RESIZE_FAILED": "Error while resizing picture",
+      "GEO_LOCATION_FAILED": "Unable to retrieve your current position"
     },
     "INFO": {
       "PROFILE_SAVED": "Profile saved"
@@ -437,6 +447,8 @@
     "NODE_BMA_UP": "Node <b>{{params[0]}}:{{params[1]}}</b> is reachable again.",
     "MEMBER_JOIN": "You are now a <b>member</b> of currency <b>{{params[0]}}</b>!",
     "MEMBER_LEAVE": "You are <b>not a member anymore</b> of currency <b>{{params[0]}}</b>!",
+    "MEMBER_EXCLUDE": "You are <b>not more member</b> of the currency <b>{{params[0]}}</b>, for lack of renewal or lack of certifications.",
+    "MEMBER_REVOKE": "Your account has been revoked. It will no longer be a member of the currency <b>{{params[0]}}</b>.",
     "MEMBER_ACTIVE": "Your membership to <b>{{params[0]}}</b> has been <b>renewed successfully</b>.",
     "TX_SENT": "Your payment to <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\" ><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> was executed.",
     "TX_SENT_MULTI": "Your payment to <b>{{params[1]}}</b> was executed.",
diff --git a/www/plugins/es/i18n/locale-en.json b/www/plugins/es/i18n/locale-en.json
index 587093f0fb851119ba11d8efa3cd588740025776..a8fcc0de4aa904f23dba696fe7e58458434bd086 100644
--- a/www/plugins/es/i18n/locale-en.json
+++ b/www/plugins/es/i18n/locale-en.json
@@ -333,6 +333,9 @@
     "NO_PROFILE_DEFINED": "No Cesium+ profile",
     "BTN_ADD": "Create my profile",
     "BTN_EDIT": "Edit my profile",
+    "BTN_GEOLOC_ADDRESS": "Update position from address",
+    "BTN_GEOLOC_ME": "Localize me",
+    "BTN_REMOVE_GEOLOC": "Remove position",
     "UID": "Pseudonym",
     "TITLE": "Lastname, FirstName",
     "TITLE_HELP": "Name",
@@ -346,6 +349,10 @@
     "GENERAL_DIVIDER": "General data",
     "LOCATION_DIVIDER": "Localisation",
     "SOCIAL_NETWORKS_DIVIDER": "Social networks and web site",
+    "LATITUDE": "Latitude",
+    "LATITUDE_HELP": "Latitude",
+    "LONGITUDE": "Longitude",
+    "LONGITUDE_HELP": "Longitude",
     "TECHNICAL_DIVIDER": "Technical data",
     "MODAL_AVATAR": {
       "TITLE": "Avatar",
@@ -358,7 +365,8 @@
       "LOAD_PROFILE_FAILED": "Could not load user profile.",
       "SAVE_PROFILE_FAILED": "Saving profile failed",
       "INVALID_SOCIAL_NETWORK_FORMAT": "Invalid format: please fill a valid Internet address.<br/><br/>Examples :<ul><li>- A Facebook page (https://www.facebook.com/user)</li><li>- A web page (http://www.domain.com)</li><li>- An email address (joe@dalton.com)</li></ul>",
-      "IMAGE_RESIZE_FAILED": "Error while resizing picture"
+      "IMAGE_RESIZE_FAILED": "Error while resizing picture",
+      "GEO_LOCATION_FAILED": "Unable to retrieve your current position"
     },
     "INFO": {
       "PROFILE_SAVED": "Profile saved"
diff --git a/www/plugins/es/i18n/locale-fr-FR.json b/www/plugins/es/i18n/locale-fr-FR.json
index 08e041fb15fa5a5d665c308a18ee26724171c9f7..c4d28a409385d340d557e18d636847f3cc0092fd 100644
--- a/www/plugins/es/i18n/locale-fr-FR.json
+++ b/www/plugins/es/i18n/locale-fr-FR.json
@@ -374,8 +374,9 @@
     "NO_PROFILE_DEFINED": "Aucun profil Cesium+",
     "BTN_ADD": "Saisir mon profil",
     "BTN_EDIT": "Editer mon profil",
-    "BTN_GEOLOC_CITY": "Localiser la ville",
+    "BTN_GEOLOC_ADDRESS": "Mettre à jour à partir de l'adresse",
     "BTN_GEOLOC_ME": "Me localiser",
+    "BTN_REMOVE_GEOLOC": "Supprimer la position",
     "UID": "Pseudonyme",
     "TITLE": "Nom, Prénom",
     "TITLE_HELP": "Nom, Prénom",
@@ -389,7 +390,6 @@
     "GENERAL_DIVIDER": "Informations générales",
     "LOCATION_DIVIDER": "Adresse",
     "SOCIAL_NETWORKS_DIVIDER": "Réseaux sociaux, sites web",
-    "GEO_POINT_DIVIDER": "Position GPS",
     "LATITUDE": "Latitude",
     "LATITUDE_HELP": "Latitude",
     "LONGITUDE": "Longitude",
@@ -407,7 +407,7 @@
       "SAVE_PROFILE_FAILED": "Erreur lors de la sauvegarde",
       "INVALID_SOCIAL_NETWORK_FORMAT": "Format non pris en compte : veuillez indiquer une adresse valide.<br/><br/>Exemples :<ul><li>- Une page Facebook (https://www.facebook.com/user)</li><li>- Une page web (http://www.monsite.fr)</li><li>- Une adresse email (joe@dalton.com)</li></ul>",
       "IMAGE_RESIZE_FAILED": "Erreur lors du redimensionnement de l'image",
-      "GEO_LOCATION_FAILED": "Echec de la récupération de votre position"
+      "GEO_LOCATION_FAILED": "Impossible de récupérer votre position actuelle"
     },
     "INFO": {
       "PROFILE_SAVED": "Profil sauvegardé"
diff --git a/www/plugins/es/js/controllers/profile-controllers.js b/www/plugins/es/js/controllers/profile-controllers.js
index 5322dd8a50a762f711beaf197b366fc996754319..67a9d396836a0ef960f133acb880ebb13497f42c 100644
--- a/www/plugins/es/js/controllers/profile-controllers.js
+++ b/www/plugins/es/js/controllers/profile-controllers.js
@@ -24,7 +24,7 @@ angular.module('cesium.es.profile.controllers', ['cesium.es.services'])
 
 ;
 
-function ESViewEditProfileController($scope, $rootScope, $timeout, $state, $focus, $translate, $ionicHistory,
+function ESViewEditProfileController($scope, $rootScope, $q, $timeout, $state, $focus, $translate, $ionicHistory,
                            UIUtils, esHttp, esProfile, esGeo, ModalUtils, Device) {
   'ngInject';
 
@@ -137,13 +137,20 @@ function ESViewEditProfileController($scope, $rootScope, $timeout, $state, $focu
   };
   $scope.$watch('formData', $scope.onFormDataChanged, true);
 
-  $scope.save = function(silent) {
+  $scope.save = function(silent, hasWaitDebounce) {
     if(!$scope.form.$valid || !$rootScope.walletData || $scope.saving) {
-      return;
+      return $q.reject();
+    }
+
+    if (!hasWaitDebounce) {
+      console.debug('[ES] [profile] Waiting debounce end, before saving...');
+      return $timeout(function() {
+        return $scope.save(silent, true);
+      }, 650);
     }
 
-    console.debug('[ES] [profile] Saving user profile...');
     $scope.saving = true;
+    console.debug('[ES] [profile] Saving user profile...');
 
     // removeIf(no-device)
     if (!silent) {
@@ -193,6 +200,19 @@ function ESViewEditProfileController($scope, $rootScope, $timeout, $state, $focu
           return social.url;
         });
       }
+
+      // Workaround for old data
+      if (formData.position) {
+        delete formData.position;
+      }
+      if ($scope.formData.geoPoint && $scope.formData.geoPoint.lat && $scope.formData.geoPoint.lon) {
+        $scope.formData.geoPoint.lat =  parseFloat($scope.formData.geoPoint.lat);
+        $scope.formData.geoPoint.lon =  parseFloat($scope.formData.geoPoint.lon);
+      }
+      else{
+        $scope.formData.geoPoint = null;
+      }
+
       if (!$scope.existing) {
         return esProfile.add(formData)
           .then(function() {
@@ -291,29 +311,97 @@ function ESViewEditProfileController($scope, $rootScope, $timeout, $state, $focu
       });
   };
 
-  $scope.localizeByCity = function() {
-    if (!$scope.formData.city) {
-      return;
-    }
+  $scope.localizeByAddress = function() {
 
-    var address = angular.copy($scope.formData.city);
-
-    return esGeo.point.searchByAddress(address)
+    return $scope.searchPositions()
       .then(function(res) {
-        if (address != $scope.formData.city) return; // user changed value again
-
-        if (res && res.length >= 1) {
-          var position = res[0];
-          if (position.lat && position.lon) {
-            $scope.formData.geoPoint = $scope.formData.geoPoint || {};
-            $scope.formData.geoPoint.lat =  parseFloat(position.lat);
-            $scope.formData.geoPoint.lon =  parseFloat(position.lon);
-            $scope.dirty = true;
-          }
+        if (!res) return; // no result, or city value just changed
+        if (res.length == 1) {
+          return res[0];
+        }
+
+        return ModalUtils.show('plugins/es/templates/common/modal_category.html', 'ESCategoryModalCtrl as ctrl',
+          {categories : res},
+          {focusFirstInput: true}
+        );
+      })
+      .then(function(position) {
+        if (position && position.lat && position.lon) {
+          $scope.formData.geoPoint = $scope.formData.geoPoint || {};
+          $scope.formData.geoPoint.lat =  parseFloat(position.lat);
+          $scope.formData.geoPoint.lon =  parseFloat(position.lon);
+
+          //$scope.dirty = true; // not need ??
         }
       });
   };
 
+  $scope.searchPositions = function(query) {
+
+    // Build the query
+    if (!query) {
+      if (!$scope.formData.city) {
+        return $q.when(); // nothing to search
+      }
+
+      var cityPart = $scope.formData.city.split(',');
+      var city = cityPart[0];
+      var country = cityPart.length > 1 ? cityPart[1].trim() : undefined;
+      var street = $scope.formData.address ? angular.copy($scope.formData.address.trim()) : undefined;
+      if (street) {
+        // Search with AND without street
+        return $q.all([
+          $scope.searchPositions({
+            street: street,
+            city: city,
+            country: country
+          }),
+          $scope.searchPositions({
+            city: city,
+            country: country
+          })
+        ])
+        .then(function(res){
+          return res[0].concat(res[1]);
+        });
+      }
+      else {
+        return $scope.searchPositions({
+          city: city,
+          country: country
+        });
+      }
+    }
+
+    // Execute the given query
+    return esGeo.point.searchByAddress(query)
+      .then(function(res) {
+        if (!res) return $q.when(); // no result
+
+        // Ask user to choose
+        var index = 0;
+        var parent = {name: 'Résultat pour <b>' +
+          (query.street ? query.street + ', ' : '') +
+          query.city +
+          (query.country ? ', ' + query.country : '')  +
+        '</b>', id: 0};
+        var hits = res.reduce(function(res, hit){
+          index++;
+          if (hit.class == 'waterway') return res;
+          return res.concat({
+            id: index,
+            name: hit.display_name,
+            parent: parent,
+            lat: hit.lat,
+            lon: hit.lon
+          });
+        }, [parent]);
+
+        if (hits.length == 1) return $q.when(); // no result (after filtering)
+
+        return hits;
+      });
+  };
 
   $scope.localizeMe = function() {
     return esGeo.point.current()
@@ -326,13 +414,20 @@ function ESViewEditProfileController($scope, $rootScope, $timeout, $state, $focu
       .catch(UIUtils.onError('PROFILE.ERROR.GEO_LOCATION_FAILED'));
   };
 
+  $scope.removeLocalisation = function() {
+    if ($scope.formData.geoPoint) {
+      $scope.formData.geoPoint.lat = null;
+      $scope.formData.geoPoint.lon = null;
+    }
+  };
+
   $scope.onCityChanged = function() {
+    if ($scope.loading) return;
     var hasGeoPoint = $scope.formData.geoPoint && $scope.formData.geoPoint.lat && $scope.formData.geoPoint.lon;
     if (!hasGeoPoint) {
-      return $scope.localizeByCity();
+      return $scope.localizeByAddress();
     }
   };
-  $scope.$watch('formData.city', $scope.onCityChanged, true);
 
 }
 
diff --git a/www/plugins/es/js/services/geo-services.js b/www/plugins/es/js/services/geo-services.js
index 86e67f8f0622080d3b6bc4ab6dd0482e8a4433b8..e05efc3570a8c9388d5ac65adaefd2168886d62a 100644
--- a/www/plugins/es/js/services/geo-services.js
+++ b/www/plugins/es/js/services/geo-services.js
@@ -17,22 +17,37 @@ angular.module('cesium.es.geo.services', ['cesium.services', 'cesium.es.http.ser
       that = this;
 
     that.raw = {
-      searchByAddress: csHttp.get('nominatim.openstreetmap.org', 80, '/search.php?format=json&q=:query'),
+      searchByString: csHttp.get('nominatim.openstreetmap.org', 80, '/search.php?format=json&q=:query'),
+      searchByQuery: csHttp.get('nominatim.openstreetmap.org', 80, '/search.php?format=json'),
       searchByIP: csHttp.get('freegeoip.net', 80, '/json/:ip')
     };
 
-    function searchPositionByAddress(queryString) {
+    function searchPositionByString(queryString) {
 
       var now = new Date();
-      console.debug('[ES] [geo] Searching address position [{0}]...'.format(queryString));
+      console.debug('[ES] [geo] Searching position by string query [{0}]...'.format(queryString));
 
-      return that.raw.searchByAddress({query: queryString})
+      return that.raw.searchByString({query: queryString})
         .then(function(res) {
           console.debug('[ES] [geo] Found {0} address position(s) in {0}ms'.format(res && res.length || 0, new Date().getTime() - now.getTime()));
           return res;
         });
     }
 
+    function searchhPositionByQuery(query) {
+
+      if (typeof query == 'string') return searchPositionByString(query);
+
+      var now = new Date();
+      console.debug('[ES] [geo] Searching position by query...', query);
+
+      return that.raw.searchByQuery(query)
+        .then(function(res) {
+          console.debug('[ES] [geo] Found {0} address position(s) in {0}ms'.format(res && res.length || 0, new Date().getTime() - now.getTime()), res);
+          return res;
+        });
+    }
+
     function getCurrentPosition() {
       var defer = $q.defer();
       if (navigator.geolocation) {
@@ -69,7 +84,7 @@ angular.module('cesium.es.geo.services', ['cesium.services', 'cesium.es.http.ser
     return {
       point: {
         current: getCurrentPosition,
-        searchByAddress: searchPositionByAddress,
+        searchByAddress: searchhPositionByQuery,
         searchByIP: searchPositionByIP
       }
     };
diff --git a/www/plugins/es/templates/user/edit_profile.html b/www/plugins/es/templates/user/edit_profile.html
index a6a1923108b9a3bb9644eedf0296e72a272dbbbc..0dc08b5e514741c84ef84541a82d213ea83cfe5a 100644
--- a/www/plugins/es/templates/user/edit_profile.html
+++ b/www/plugins/es/templates/user/edit_profile.html
@@ -109,15 +109,10 @@
               <span class="input-label" translate>PROFILE.CITY</span>
               <input type="text" placeholder="{{'PROFILE.CITY_HELP'|translate}}"
                      ng-model="formData.city"
-                     ng-model-options="{ debounce: 350 }">
+                     ng-model-options="{ updateOn: 'blur' }"
+                     ng-change="onCityChanged()">
             </div>
 
-            <!--<div class="item">
-              <span class="input-label">
-              {{'PROFILE.GEO_POINT_DIVIDER' | translate}}
-                </span>
-            </div>-->
-
             <!-- Position (lat/lon) -->
             <div class="row responsive-md responsive-sm no-padding">
 
@@ -128,8 +123,7 @@
                   <input class="no-padding-right" type="number" placeholder="{{'PROFILE.LATITUDE_HELP'|translate}}"
                          ng-model="formData.geoPoint.lat"
                          ng-model-options="{ debounce: 350 }"
-                         ng-change="onGeoPointChanged()"
-                         required>
+                         ng-change="onGeoPointChanged()">
                 </label>
               </div>
 
@@ -144,8 +138,7 @@
                   <input class="no-padding-right" type="number" placeholder="{{'PROFILE.LONGITUDE_HELP'|translate}}"
                          ng-model="formData.geoPoint.lon"
                          ng-model-options="{ debounce: 350 }"
-                         ng-change="onGeoPointChanged()"
-                         required>
+                         ng-change="onGeoPointChanged()">
                 </label>
               </div>
 
@@ -154,18 +147,27 @@
                   <span class="input-label"></span>
 
                   <a class="button  button-stable button-small-padding icon ion-refresh"
-                     title="{{'PROFILE.BTN_GEOLOC_CITY'|translate}}"
-                     ng-click="localizeByCity()">
+                     title="{{'PROFILE.BTN_GEOLOC_ADDRESS'|translate}}"
+                     ng-disabled="!formData.city"
+                     ng-click="localizeByAddress()">
                   </a>
 
                   <a class="button button-stable button-small-padding icon ion-android-locate"
                      title="{{'PROFILE.BTN_GEOLOC_ME'|translate}}"
                      ng-click="localizeMe()">
                   </a>
+
+                  <a class="button button-stable button-small-padding icon ion-close"
+                     title="{{'PROFILE.BTN_REMOVE_GEOLOC'|translate}}"
+                     ng-disabled="!formData.geoPoint || (!formData.geoPoint.lat && !formData.geoPoint.lon)"
+                     ng-click="removeLocalisation()">
+                  </a>
                 </div>
               </div>
             </div>
 
+            <cs-extension-point name="after-position"></cs-extension-point>
+
             <!-- social networks -->
             <ng-include src="'plugins/es/templates/common/edit_socials.html'"></ng-include>
 
diff --git a/www/plugins/map/i18n/locale-en-GB.json b/www/plugins/map/i18n/locale-en-GB.json
new file mode 100644
index 0000000000000000000000000000000000000000..4d23c5c79d9b9f615c470459e2bdf36963304c18
--- /dev/null
+++ b/www/plugins/map/i18n/locale-en-GB.json
@@ -0,0 +1,40 @@
+{
+  "MAP": {
+    "COMMON": {
+      "SEARCH_DOTS": "Search..."
+    },
+    "NETWORK": {
+      "LOOKUP": {
+        "BTN_MAP": "Peers map",
+        "BTN_MAP_HELP": "Open peers map"
+      },
+      "VIEW": {
+        "TITLE": "Peers map",
+        "LAYER": {
+          "MEMBER": "Member peers",
+          "MIRROR": "Mirror peers",
+          "OFFLINE": "Offline peers"
+        }
+      }
+    },
+    "WOT": {
+      "LOOKUP": {
+        "BTN_MAP": "Members map",
+        "BTN_MAP_HELP": "Open members map"
+      },
+      "VIEW": {
+        "TITLE": "Members map",
+        "LAYER": {
+          "MEMBER": "Members",
+          "WALLET": "Simple wallets"
+        }
+      }
+    },
+    "PROFILE": {
+      "MARKER_HELP": "<b>Drag and drop</b> this marker to <b>update<br/>your position</b>, or use the buttons<br/>on top of the map."
+    },
+    "ERROR": {
+      "LOCALIZE_ME_FAILED": "Unable to retrieve your current position"
+    }
+  }
+}
diff --git a/www/plugins/map/i18n/locale-en.json b/www/plugins/map/i18n/locale-en.json
new file mode 100644
index 0000000000000000000000000000000000000000..4d23c5c79d9b9f615c470459e2bdf36963304c18
--- /dev/null
+++ b/www/plugins/map/i18n/locale-en.json
@@ -0,0 +1,40 @@
+{
+  "MAP": {
+    "COMMON": {
+      "SEARCH_DOTS": "Search..."
+    },
+    "NETWORK": {
+      "LOOKUP": {
+        "BTN_MAP": "Peers map",
+        "BTN_MAP_HELP": "Open peers map"
+      },
+      "VIEW": {
+        "TITLE": "Peers map",
+        "LAYER": {
+          "MEMBER": "Member peers",
+          "MIRROR": "Mirror peers",
+          "OFFLINE": "Offline peers"
+        }
+      }
+    },
+    "WOT": {
+      "LOOKUP": {
+        "BTN_MAP": "Members map",
+        "BTN_MAP_HELP": "Open members map"
+      },
+      "VIEW": {
+        "TITLE": "Members map",
+        "LAYER": {
+          "MEMBER": "Members",
+          "WALLET": "Simple wallets"
+        }
+      }
+    },
+    "PROFILE": {
+      "MARKER_HELP": "<b>Drag and drop</b> this marker to <b>update<br/>your position</b>, or use the buttons<br/>on top of the map."
+    },
+    "ERROR": {
+      "LOCALIZE_ME_FAILED": "Unable to retrieve your current position"
+    }
+  }
+}
diff --git a/www/plugins/map/i18n/locale-fr-FR.json b/www/plugins/map/i18n/locale-fr-FR.json
index 97bbbd0a815b757a833b401fbade4560b5670565..4083d3d55a1b7e8b927536b7d1ca142f734a052f 100644
--- a/www/plugins/map/i18n/locale-fr-FR.json
+++ b/www/plugins/map/i18n/locale-fr-FR.json
@@ -31,8 +31,11 @@
         }
       }
     },
+    "PROFILE": {
+      "MARKER_HELP": "<b>Glissez-déposez</b> ce marqueur pour <b>mettre<br/>à jour votre position</b>, ou utilisez les boutons<br/>au dessus de la carte."
+    },
     "ERROR": {
-      "LOCALIZE_ME_FAILED": "Echec de la récupération de votre position"
+      "LOCALIZE_ME_FAILED": "Impossible de récupérer votre position actuelle"
     }
   }
 }
diff --git a/www/plugins/map/js/controllers/network-controllers.js b/www/plugins/map/js/controllers/network-controllers.js
index eaaeba27d356bade8f19a3ff1f034d480885636f..f511a09b4f1547710694af497e49a43381af82ab 100644
--- a/www/plugins/map/js/controllers/network-controllers.js
+++ b/www/plugins/map/js/controllers/network-controllers.js
@@ -9,7 +9,6 @@ angular.module('cesium.map.network.controllers', ['cesium.services', 'cesium.map
 
       PluginServiceProvider
 
-      // Extension de la vue d'une identité: ajout d'un bouton
         .extendState('app.network', {
           points: {
             'filter-buttons': {
@@ -18,7 +17,6 @@ angular.module('cesium.map.network.controllers', ['cesium.services', 'cesium.map
           }
         });
 
-      // [NEW] Ajout d'une nouvelle page #/app/wot/map
       $stateProvider
         .state('app.view_network_map', {
           url: "/network/map?c",
@@ -133,6 +131,13 @@ angular.module('cesium.map.network.controllers', ['cesium.services', 'cesium.map
     };
     $scope.$on('$ionicView.enter', $scope.enter);
 
+    var inheritedComputeOptions = $scope.computeOptions;
+    $scope.computeOptions = function() {
+      var options = inheritedComputeOptions();
+      options.filter.online = 'all';
+      return options;
+    };
+
     $scope.loadMap = function() {
       return leafletData.getMap($scope.mapId).then(function(map) {
         if (!$scope.map.loading) return map; // already loaded
diff --git a/www/plugins/map/js/controllers/user-controllers.js b/www/plugins/map/js/controllers/user-controllers.js
new file mode 100644
index 0000000000000000000000000000000000000000..457941574c44ee86d39e45036a8ceadfcfb24c8d
--- /dev/null
+++ b/www/plugins/map/js/controllers/user-controllers.js
@@ -0,0 +1,121 @@
+
+angular.module('cesium.map.user.controllers', ['cesium.services', 'cesium.map.services'])
+
+  .config(function(PluginServiceProvider, csConfig) {
+    'ngInject';
+
+    var enable = csConfig.plugins && csConfig.plugins.es;
+    if (enable) {
+
+      PluginServiceProvider
+
+        .extendState('app.user_edit_profile', {
+          points: {
+            'after-position': {
+              templateUrl: 'plugins/map/templates/user/edit_profile_extend.html',
+              controller: 'MapEditProfileViewCtrl'
+            }
+          }
+        });
+    }
+  })
+
+  // [NEW] Manage events from the page #/app/wot/map
+  .controller('MapEditProfileViewCtrl', function($scope, $timeout, $q, MapUtils, $translate) {
+    'ngInject';
+
+    var listeners = [];
+    $scope.mapId = 'map-user-profile-' + $scope.$id;
+    $scope.map = MapUtils.map({
+      markers: {},
+      center: {
+        zoom: 13
+      },
+      defaults: {
+        tileLayerOptions: {
+          attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
+        }
+      }
+    });
+    $scope.loading = true;
+
+    $scope.enter = function(e, state) {
+
+      // Wait parent controller load the profile
+      if (!$scope.formData || !$scope.formData.title) {
+        return $timeout($scope.enter, 500);
+      }
+
+      $scope.loading = true;
+      return $scope.load();
+    };
+    $scope.$on('$csExtension.enter', $scope.enter);
+    $scope.$on('$ionicParentView.enter', $scope.enter);
+
+    $scope.load = function() {
+
+      // no position define: remove existing listener
+      if (!$scope.formData.geoPoint || !$scope.formData.geoPoint.lat || !$scope.formData.geoPoint.lon) {
+        _.forEach(listeners, function(listener){
+          listener(); // unlisten
+        });
+        listeners = [];
+        delete $scope.map.markers.geoPoint;
+        $scope.loading = false;
+        return $q.when();
+      }
+
+      // If no marker exists on map: create it
+      if (!$scope.map.markers.geoPoint) {
+
+        return $translate('MAP.PROFILE.MARKER_HELP')
+          .then(function(helpText) {
+
+
+            $scope.map.markers.geoPoint = {
+              message: helpText,
+              lat: parseFloat($scope.formData.geoPoint.lat),
+              lng: parseFloat($scope.formData.geoPoint.lon),
+              draggable: true,
+              focus: true
+            };
+            angular.extend($scope.map.center, {
+              lat: $scope.map.markers.geoPoint.lat,
+              lng: $scope.map.markers.geoPoint.lng
+            });
+
+            // Listening changes
+            var listener = $scope.$watch('map.markers.geoPoint', function() {
+              if ($scope.loading) return;
+              if ($scope.map.markers.geoPoint && $scope.map.markers.geoPoint.lat && $scope.map.markers.geoPoint.lng) {
+                $scope.formData.geoPoint = $scope.formData.geoPoint || {};
+                $scope.formData.geoPoint.lat = $scope.map.markers.geoPoint.lat;
+                $scope.formData.geoPoint.lon = $scope.map.markers.geoPoint.lng;
+              }
+            }, true);
+            listeners.push(listener);
+
+
+            // Make sure map appear, if shown later
+            if (!$scope.ionItemClass) {
+              $scope.ionItemClass = 'done in';
+            }
+
+            $scope.loading = false;
+          });
+      }
+
+      // Marker exists: update lat/lon
+      else {
+        $scope.map.markers.geoPoint.lat = $scope.formData.geoPoint.lat;
+        $scope.map.markers.geoPoint.lng = $scope.formData.geoPoint.lon;
+      }
+    };
+
+
+    $scope.$watch('formData.geoPoint', function() {
+      if ($scope.loading) return;
+      $scope.load();
+    }, true);
+
+  });
diff --git a/www/plugins/map/js/controllers/wot-controllers.js b/www/plugins/map/js/controllers/wot-controllers.js
index d7dbe85130a0e8f63f025b2ed2db7d4a20becace..a5dc55dd1b69aeb8c7f396e16087f20d29651b83 100644
--- a/www/plugins/map/js/controllers/wot-controllers.js
+++ b/www/plugins/map/js/controllers/wot-controllers.js
@@ -9,7 +9,6 @@ angular.module('cesium.map.wot.controllers', ['cesium.services', 'cesium.map.ser
 
       PluginServiceProvider
 
-      // Extension de la vue d'une identité: ajout d'un bouton
         .extendState('app.wot_lookup', {
           points: {
             'filter-buttons': {
@@ -18,7 +17,6 @@ angular.module('cesium.map.wot.controllers', ['cesium.services', 'cesium.map.ser
           }
         });
 
-      // Wot map (default position)
       $stateProvider
         .state('app.view_wot_map', {
           url: "/wot/map?c&center",
@@ -43,7 +41,6 @@ angular.module('cesium.map.wot.controllers', ['cesium.services', 'cesium.map.ser
     var
       // Create a  hidden layer, to hold search markers
       markersSearchLayer = L.layerGroup({visible: false}),
-      loadingControl, searchControl, localizeMe,
       icons= {
         member: {
           type: 'awesomeMarker',
@@ -119,19 +116,19 @@ angular.module('cesium.map.wot.controllers', ['cesium.services', 'cesium.map.ser
         if (!$scope.map.loading) return map; // already loaded
 
         // Add loading control
-        loadingControl = L.Control.loading({
+        var loadingControl = L.Control.loading({
           position: 'topright',
           separate: true
         });
         loadingControl.addTo(map);
 
         // Add localize me control
-        localizeMe = MapUtils.control.localizeMe();
+        var localizeMe = MapUtils.control.localizeMe();
         localizeMe.addTo(map);
 
         // Add search control
         var searchTip = $interpolate($templateCache.get('plugins/map/templates/wot/item_search_tooltip.html'));
-        searchControl = MapUtils.control.search({
+        var searchControl = MapUtils.control.search({
           layer: markersSearchLayer,
           propertyName: 'title',
           buildTip: function (text, val) {
diff --git a/www/plugins/map/js/plugin.js b/www/plugins/map/js/plugin.js
index f3f2437335276cd885aed6b5554f675ce5991873..d5bef3326e9f246c3a391523c13e537fa277e5be 100644
--- a/www/plugins/map/js/plugin.js
+++ b/www/plugins/map/js/plugin.js
@@ -5,7 +5,8 @@ angular.module('cesium.map.plugin', [
     'cesium.map.services',
     // Controllers
     'cesium.map.wot.controllers',
-    'cesium.map.network.controllers'
+    'cesium.map.network.controllers',
+    'cesium.map.user.controllers'
   ])
 
   // Configure plugin
diff --git a/www/plugins/map/js/services/utils-services.js b/www/plugins/map/js/services/utils-services.js
index 37ce44f95b6c96372a4f5f8b5254564e130d03fd..1e19a0c8b2756888faf2ef9f670e081317cf0e46 100644
--- a/www/plugins/map/js/services/utils-services.js
+++ b/www/plugins/map/js/services/utils-services.js
@@ -108,14 +108,14 @@ angular.module('cesium.map.utils.services', ['cesium.services', 'ui-leaflet'])
     options.zoom = angular.isDefined(options.zoom) ? options.zoom : constants.LOCALIZE_ZOOM;
     options.markerLocation = angular.isDefined(options.markerLocation) ? options.markerLocation : true;
 
-    var translatePromise = $translate(['MAP.WOT.VIEW.SEARCH_DOTS', 'COMMON.SEARCH_NO_RESULT']);
+    var translatePromise = $translate(['MAP.COMMON.SEARCH_DOTS', 'COMMON.SEARCH_NO_RESULT']);
 
     return {
       // Simulate an addTo function, but wait for end of translations job
       addTo: function (map) {
         translatePromise.then(function (translations) {
           L.control.search(angular.merge(options, {
-            textPlaceholder: translations['MAP.WOT.VIEW.SEARCH_DOTS'],
+            textPlaceholder: translations['MAP.COMMON.SEARCH_DOTS'],
             textErr: translations['COMMON.SEARCH_NO_RESULT']
           })).addTo(map);
         });
diff --git a/www/plugins/map/templates/user/edit_profile_extend.html b/www/plugins/map/templates/user/edit_profile_extend.html
new file mode 100644
index 0000000000000000000000000000000000000000..94757d48b558941d886263b51128d8d4586037da
--- /dev/null
+++ b/www/plugins/map/templates/user/edit_profile_extend.html
@@ -0,0 +1,7 @@
+<div class="item no-padding {{ionItemClass}}" ng-if="formData.geoPoint && formData.geoPoint.lat && formData.geoPoint.lon">
+  <leaflet height="250px"
+           center="map.center"
+           markers="map.markers"
+           defaults="map.defaults">
+  </leaflet>
+</div>