diff --git a/www/js/directives.js b/www/js/directives.js
index 4871350e7f92a2d94fabd285f30e9aa339e795c2..7a78f61bb7d320d16dc22dc094249aa13027f529 100644
--- a/www/js/directives.js
+++ b/www/js/directives.js
@@ -43,8 +43,8 @@ angular.module('cesium.directives', [])
       require: '?ngModel',
       link: function(scope, element, attributes, ngModel) {
         if (ngModel) {
-          ngModel.$validators.numberFloat = function(modelValue) {
-            return ngModel.$isEmpty(modelValue) || NUMBER_REGEXP.test(modelValue);
+          ngModel.$validators.numberFloat = function(value) {
+            return ngModel.$isEmpty(value) || NUMBER_REGEXP.test(value);
           };
         }
       }
@@ -79,6 +79,19 @@ angular.module('cesium.directives', [])
     };
   })
 
+  .directive('geoPointRequired', function() {
+    return {
+      require: '?ngModel',
+      link: function(scope, element, attributes, ngModel) {
+        if (ngModel) {
+          ngModel.$validators.required = function(value) {
+            return ngModel.$isEmpty(value) || !value.lat || !value.lon ;
+          };
+        }
+      }
+    };
+  })
+
   // Add a copy-on-click directive
   .directive('copyOnClick', function ($window, $document, Device, UIUtils) {
     'ngInject';
diff --git a/www/plugins/es/css/style.css b/www/plugins/es/css/style.css
index 1845a1d8602f9d2d580dee206b73e82714f744b5..7991c6df30ed32a01b69d61a5ba4cd537a866179 100644
--- a/www/plugins/es/css/style.css
+++ b/www/plugins/es/css/style.css
@@ -150,9 +150,9 @@
   line-height: 88px;
 }
 
-/**
-* Message screen
-*/
+/**********
+   Message screen
+**********/
 
 #composeMessage .list .item {
   border-bottom: solid 1px #ccc;
@@ -177,9 +177,23 @@
   padding-bottom: 8px;
 }
 
-/**
-* Add specific Icons
-*/
+/**********
+   Message screen
+**********/
+
+.modal-search-location .bar-footer {
+  height: 30px;
+}
+.modal-search-location .bar-footer .copyright {
+  font-size: 12px;
+  line-height: 30px;
+}
+
+
+/**********
+   Add specific Icons
+**********/
+
 
 /* Association */
 .cion-page-association:before {
diff --git a/www/plugins/es/i18n/locale-en-GB.json b/www/plugins/es/i18n/locale-en-GB.json
index 22c10e74ee6fc57a99ad5413ec385d7cb422a45c..acbf0184e8e21f9895aab8f52b96a2190ab846c9 100644
--- a/www/plugins/es/i18n/locale-en-GB.json
+++ b/www/plugins/es/i18n/locale-en-GB.json
@@ -17,6 +17,10 @@
       "SETTINGS": "Paramètres",
       "SHOW_ALL": "Show all",
       "LOAD_NOTIFICATIONS_FAILED": "Could not load notifications"
+    },
+    "ERROR": {
+      "REQUIRED_FOR_LOCATION": "Required field to appear on the map",
+      "INVALID_FOR_LOCATION": "Unknown address"
     }
   },
   "MENU": {
@@ -279,9 +283,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",
+    "BTN_GEOLOC_ADDRESS": "Find my address on the map",
+    "USE_GEO_POINT": "Appear on the members map?",
+    "LOADING_LOCATION": "Searching address...",
     "UID": "Pseudonym",
     "TITLE": "Lastname, FirstName",
     "TITLE_HELP": "Name",
@@ -290,7 +294,7 @@
     "ADDRESS": "Address",
     "ADDRESS_HELP": "Address (optional)",
     "CITY": "City",
-    "CITY_HELP": "City (optional)",
+    "CITY_HELP": "City, Country",
     "SOCIAL_HELP": "http://...",
     "GENERAL_DIVIDER": "General data",
     "LOCATION_DIVIDER": "Localisation",
@@ -307,16 +311,18 @@
       "RESIZE_HELP": "<b>Re-crop the image</b> if necessary. A click on the image allows to move it. Click on the area at the bottom left to zoom in.",
       "RESULT_HELP": "<b>Here is the result</b> as seen on your profile:"
     },
-    "MODAL_LOCATIONS": {
-      "TITLE": "Validation de l'adresse",
-      "RESULT_DIVIDER": "Résultat(s) pour <b>{{address}}</b> :"
+    "MODAL_LOCATION": {
+      "TITLE": "Search position",
+      "SEARCH_HELP": "City, Country",
+      "ALTERNATIVE_RESULT_DIVIDER": "Alternative results for <b>{{address}}</b>:",
+      "POSITION": "lat/lon : {{lat}} {{lon}}"
     },
     "ERROR": {
       "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",
-      "GEO_LOCATION_FAILED": "Unable to retrieve your current position",
+      "GEO_LOCATION_FAILED": "Unable to retrieve your current position. Please use the search button.",
       "ADDRESS_LOCATION_FAILED": "Unable to retrieve the address position"
     },
     "INFO": {
@@ -328,6 +334,7 @@
   },
   "SUBSCRIPTION": {
     "SUBSCRIPTION_DIVIDER": "Online services",
+    "SUBSCRIPTION_DIVIDER_HELP": "Online services offer optional additional services, delegated to a third party.",
     "BTN_ADD": "Add a service",
     "BTN_EDIT": "Manage my services",
     "NO_SUBSCRIPTION": "No service defined",
diff --git a/www/plugins/es/i18n/locale-en.json b/www/plugins/es/i18n/locale-en.json
index 22c10e74ee6fc57a99ad5413ec385d7cb422a45c..acbf0184e8e21f9895aab8f52b96a2190ab846c9 100644
--- a/www/plugins/es/i18n/locale-en.json
+++ b/www/plugins/es/i18n/locale-en.json
@@ -17,6 +17,10 @@
       "SETTINGS": "Paramètres",
       "SHOW_ALL": "Show all",
       "LOAD_NOTIFICATIONS_FAILED": "Could not load notifications"
+    },
+    "ERROR": {
+      "REQUIRED_FOR_LOCATION": "Required field to appear on the map",
+      "INVALID_FOR_LOCATION": "Unknown address"
     }
   },
   "MENU": {
@@ -279,9 +283,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",
+    "BTN_GEOLOC_ADDRESS": "Find my address on the map",
+    "USE_GEO_POINT": "Appear on the members map?",
+    "LOADING_LOCATION": "Searching address...",
     "UID": "Pseudonym",
     "TITLE": "Lastname, FirstName",
     "TITLE_HELP": "Name",
@@ -290,7 +294,7 @@
     "ADDRESS": "Address",
     "ADDRESS_HELP": "Address (optional)",
     "CITY": "City",
-    "CITY_HELP": "City (optional)",
+    "CITY_HELP": "City, Country",
     "SOCIAL_HELP": "http://...",
     "GENERAL_DIVIDER": "General data",
     "LOCATION_DIVIDER": "Localisation",
@@ -307,16 +311,18 @@
       "RESIZE_HELP": "<b>Re-crop the image</b> if necessary. A click on the image allows to move it. Click on the area at the bottom left to zoom in.",
       "RESULT_HELP": "<b>Here is the result</b> as seen on your profile:"
     },
-    "MODAL_LOCATIONS": {
-      "TITLE": "Validation de l'adresse",
-      "RESULT_DIVIDER": "Résultat(s) pour <b>{{address}}</b> :"
+    "MODAL_LOCATION": {
+      "TITLE": "Search position",
+      "SEARCH_HELP": "City, Country",
+      "ALTERNATIVE_RESULT_DIVIDER": "Alternative results for <b>{{address}}</b>:",
+      "POSITION": "lat/lon : {{lat}} {{lon}}"
     },
     "ERROR": {
       "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",
-      "GEO_LOCATION_FAILED": "Unable to retrieve your current position",
+      "GEO_LOCATION_FAILED": "Unable to retrieve your current position. Please use the search button.",
       "ADDRESS_LOCATION_FAILED": "Unable to retrieve the address position"
     },
     "INFO": {
@@ -328,6 +334,7 @@
   },
   "SUBSCRIPTION": {
     "SUBSCRIPTION_DIVIDER": "Online services",
+    "SUBSCRIPTION_DIVIDER_HELP": "Online services offer optional additional services, delegated to a third party.",
     "BTN_ADD": "Add a service",
     "BTN_EDIT": "Manage my services",
     "NO_SUBSCRIPTION": "No service defined",
diff --git a/www/plugins/es/i18n/locale-es-ES.json b/www/plugins/es/i18n/locale-es-ES.json
index e681babdc099d25f605102c92dc7c0396bbde002..8dadf4204cda92091b813d5b410b3804b7179edd 100644
--- a/www/plugins/es/i18n/locale-es-ES.json
+++ b/www/plugins/es/i18n/locale-es-ES.json
@@ -17,6 +17,10 @@
       "SETTINGS": "configuraciónes",
       "SHOW_ALL": "Ver todo",
       "LOAD_NOTIFICATIONS_FAILED": "Fracaso en la carga de las notificaciónes"
+    },
+    "ERROR": {
+      "REQUIRED_FOR_LOCATION": "Campo obligatorio para aparecer en el mapa",
+      "INVALID_FOR_LOCATION": "Dirección desconocida"
     }
   },
   "MENU": {
@@ -237,7 +241,8 @@
     },
     "VIEW": {
       "POPOVER_SHARE_TITLE": "{{title}}",
-      "MENU_TITLE": "Opciones"
+      "MENU_TITLE": "Opciones",
+      "REMOVE_CONFIRMATION" : "Seguro que quieres eliminar este grupo?<br/><br/>Esta operación es irreversible."
     },
     "EDIT": {
       "TITLE": "Grupo",
@@ -249,6 +254,10 @@
     },
     "ERROR": {
       "SEARCH_GROUPS_FAILED": "Fracaso en la búsqueda de grupos"
+      "REMOVE_RECORD_FAILED": "Error al eliminar el grupo"
+    },
+    "INFO": {
+      "RECORD_REMOVED" : "Grupo eliminado"
     }
   },
   "REGISTRY": {
@@ -326,8 +335,8 @@
     "BTN_ADD": "Ingresar mi perfil",
     "BTN_EDIT": "Editar mi perfil",
     "BTN_GEOLOC_ADDRESS": "Actualizar desde la dirección",
-    "BTN_GEOLOC_ME": "localizarme",
-    "BTN_REMOVE_GEOLOC": "Posición de eliminar",
+    "USE_GEO_POINT": "Aparecer en la tarjeta de membresía?",
+    "LOADING_LOCATION": "Encontrar la dirección ...",
     "UID": "Seudónimo",
     "TITLE": "Nombre, Apellido",
     "TITLE_HELP": "Nombre, Apellido",
@@ -336,7 +345,7 @@
     "ADDRESS": "Calle",
     "ADDRESS_HELP": "Calle, complemento de dirección...",
     "CITY": "Ciudad",
-    "CITY_HELP": "Ciudad (opcional)",
+    "CITY_HELP": "Ciudad, País",
     "SOCIAL_HELP": "http://...",
     "GENERAL_DIVIDER": "Informaciónes generales",
     "LOCATION_DIVIDER": "Dirección",
@@ -353,9 +362,11 @@
       "RESIZE_HELP": "<b>Encuadra la imagen</b>, si es necesario. Un clic mantenido sobre la imagen permite desplazarla. Hace un clic sobre la zona abajo a la izquierda para hacer zoom.",
       "RESULT_HELP": "<b>Aquí está el resultado</b> tal como está visible sobre su perfil :"
     },
-    "MODAL_LOCATIONS": {
-      "TITLE": "Validación de direcciones",
-      "RESULT_DIVIDER": "Resultado(s) para <b>{{address}}</b>:"
+    "MODAL_LOCATION": {
+      "TITLE": "Búsqueda de dirección",
+      "SEARCH_HELP": "Ciudad, País",
+      "ALTERNATIVE_RESULT_DIVIDER": "Resultados alternativos para <b>{{address}}</b> :",
+      "POSITION": "Latitud/Longitud : {{lat}}  {{lon}}"
     },
     "ERROR": {
       "LOAD_PROFILE_FAILED": "Fracaso en la carga del perfil usuario.",
@@ -374,6 +385,7 @@
   },
   "SUBSCRIPTION": {
     "SUBSCRIPTION_DIVIDER": "Servicios en línea",
+    "SUBSCRIPTION_DIVIDER_HELP": "Los servicios en línea ofrecen servicios adicionales opcionales, delegados a un tercero.",
     "BTN_ADD": "Agregar un servicio",
     "BTN_EDIT": "Administrar mis servicios",
     "NO_SUBSCRIPTION": "Ningún servicio definido",
@@ -399,7 +411,7 @@
     },
     "MODAL_EMAIL": {
       "TITLE" : "Notificación por correo electrónico",
-      "HELP" : "Rellene este formulario para <b> ser notificado por correo electrónico </ b> de los eventos de su cuenta. <br/> Su dirección de correo electrónico se cifrará únicamente para que sea visible para el proveedor de servicios.",
+      "HELP" : "Rellene este formulario para <b>ser notificado por correo electrónico</b> de los eventos de su cuenta. <br/> Su dirección de correo electrónico se cifrará únicamente para que sea visible para el proveedor de servicios.",
       "EMAIL_LABEL" : "Tu correo electrónico :",
       "EMAIL_HELP": "carlos@dominio.com",
       "FREQUENCY_LABEL": "Frecuencia de las notificaciones :",
diff --git a/www/plugins/es/i18n/locale-fr-FR.json b/www/plugins/es/i18n/locale-fr-FR.json
index ca737a6b88af804adb0d49d39e49081336ca46e8..639b0dc9afa19cefc7f862494628d47add37fb9a 100644
--- a/www/plugins/es/i18n/locale-fr-FR.json
+++ b/www/plugins/es/i18n/locale-fr-FR.json
@@ -17,6 +17,10 @@
       "SETTINGS": "Paramètres",
       "SHOW_ALL": "Voir tout",
       "LOAD_NOTIFICATIONS_FAILED": "Erreur de chargement des notifications"
+    },
+    "ERROR": {
+      "REQUIRED_FOR_LOCATION": "Champ obligatoire pour apparaître sur la carte",
+      "INVALID_FOR_LOCATION": "Adresse inconnue"
     }
   },
   "MENU": {
@@ -330,9 +334,9 @@
     "NO_PROFILE_DEFINED": "Aucun profil Cesium+",
     "BTN_ADD": "Saisir mon profil",
     "BTN_EDIT": "Editer mon profil",
-    "BTN_GEOLOC_ADDRESS": "Mettre à jour à partir de l'adresse",
-    "BTN_GEOLOC_ME": "Me localiser",
-    "BTN_REMOVE_GEOLOC": "Supprimer la position",
+    "BTN_GEOLOC_ADDRESS": "Trouver mon adresse sur la carte",
+    "USE_GEO_POINT": "Apparaître sur la carte des membres ?",
+    "LOADING_LOCATION": "Recherche de l'adresse...",
     "UID": "Pseudonyme",
     "TITLE": "Nom, Prénom",
     "TITLE_HELP": "Nom, Prénom",
@@ -358,16 +362,18 @@
       "RESIZE_HELP": "<b>Recadrez l'image</b>, si besoin. Un clic maintenu sur l'image permet de la déplacer. Cliquez sur la zone en bas à gauche pour zoomer.",
       "RESULT_HELP": "<b>Voici le résultat</b> tel que visible sur votre profil :"
     },
-    "MODAL_LOCATIONS": {
-      "TITLE": "Validation de l'addresse",
-      "RESULT_DIVIDER": "Résultat(s) pour <b>{{address}}</b> :"
+    "MODAL_LOCATION": {
+      "TITLE": "Recherche de l'adresse",
+      "SEARCH_HELP": "Ville, Pays",
+      "ALTERNATIVE_RESULT_DIVIDER": "Résultats alternatifs pour <b>{{address}}</b> :",
+      "POSITION": "Latitude/Longitude : {{lat}}  {{lon}}"
     },
     "ERROR": {
       "LOAD_PROFILE_FAILED": "Erreur de chargement du profil utilisateur.",
       "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": "Impossible de récupérer votre position actuelle",
+      "GEO_LOCATION_FAILED": "Impossible de récupérer votre position. Veuillez utiliser le bouton de recherche.",
       "ADDRESS_LOCATION_FAILED": "Impossible de récupérer la position à partir de l'adresse"
     },
     "INFO": {
diff --git a/www/plugins/es/js/controllers/common-controllers.js b/www/plugins/es/js/controllers/common-controllers.js
index 12d368f71a6d77c5312c34cf070e7687a6f2a3b2..12241291cfc78be29e56f65990c54ccb5d413e17 100644
--- a/www/plugins/es/js/controllers/common-controllers.js
+++ b/www/plugins/es/js/controllers/common-controllers.js
@@ -16,6 +16,9 @@ angular.module('cesium.es.common.controllers', ['ngResource', 'cesium.es.service
 
  .controller('ESPositionEditCtrl', ESPositionEditController)
 
+  .controller('ESSearchPositionModalCtrl', ESSearchPositionModalController)
+
+
 ;
 
 
@@ -136,7 +139,7 @@ function ESCategoryModalController($scope, UIUtils, $timeout, parameters) {
 
 
 
-function ESCommentsController($scope, $timeout, $filter, $state, $focus, UIUtils) {
+function ESCommentsController($scope, $filter, $state, $focus, UIUtils) {
   'ngInject';
 
   $scope.loading = true;
@@ -420,139 +423,211 @@ function ESAvatarModalController($scope) {
 }
 
 
-function ESPositionEditController($scope, $q, $translate,
-                                  csConfig, UIUtils, esGeo, ModalUtils) {
+function ESPositionEditController($scope, $timeout, csConfig, esGeo, ModalUtils) {
   'ngInject';
 
   // The default country used for address localisation
   var defaultCountry = csConfig.plugins && csConfig.plugins.es && csConfig.plugins.es.defaultCountry;
 
-  //$scope.formData = $scope.formData || {};
-  //$scope.formData.geoPoint = $scope.formData.geoPoint || {};
-
-  $scope.localizeByAddress = function() {
+  var loadingCurrentPosition = false;
+  $scope.loadingPosition = false;
+
+  $scope.tryToLocalize = function() {
+    if ($scope.loadingPosition || loadingCurrentPosition) return;
+
+    var searchText = $scope.getAddressToSearch();
+
+    // No address, so try to localize by device
+    if (!searchText) {
+      loadingCurrentPosition = true;
+      return esGeo.point.current()
+        .then($scope.updateGeoPoint)
+        .then(function() {
+          loadingCurrentPosition = false;
+        })
+        .catch(function(err) {
+          console.error(err); // Silent
+          loadingCurrentPosition = false;
+          $scope.form.geoPoint.$setValidity('required', false);
+        });
+    }
 
-    return UIUtils.loading.show()
-      .then($scope.searchPositions)
+    $scope.loadingPosition = true;
+    return esGeo.point.searchByAddress(searchText)
       .then(function(res) {
-        UIUtils.loading.hide();
-
-        if (!res) return; // no result, or city value just changed
-        if (res.length == 1) {
-          return res[0];
+        if (res && res.length == 1) {
+          return $scope.updateGeoPoint(res[0]);
         }
-
-        return ModalUtils.show('plugins/es/templates/common/modal_category.html', 'ESCategoryModalCtrl as ctrl',
-          {
-            categories : res,
-            title: 'PROFILE.MODAL_LOCATIONS.TITLE'
-          },
-          {focusFirstInput: true}
-        );
+        return $scope.openSearchLocationModal({
+          text: searchText,
+          results: res||[],
+          forceFallback: !res || !res.length // force fallback search first
+        });
       })
-      .then(function(res) {
-        if (res && res.lat && res.lon) {
-          $scope.formData.geoPoint = $scope.formData.geoPoint || {};
-          $scope.formData.geoPoint.lat =  parseFloat(res.lat);
-          $scope.formData.geoPoint.lon =  parseFloat(res.lon);
-        }
+      .then(function() {
+        $scope.loadingPosition = false;
       })
-      .catch(UIUtils.onError('PROFILE.ERROR.ADDRESS_LOCATION_FAILED'));
+      .catch(function(err) {
+        console.error(err); // Silent
+        $scope.loadingPosition = false;
+      });
+  };
+
+  $scope.onCityChanged = function() {
+    if ($scope.loading) return;
+    if ($scope.form) {
+      $scope.form.$valid = undefined;
+    }
+    if ($scope.formData.enableGeoPoint) {
+      return $scope.tryToLocalize();
+    }
+  };
+
+  $scope.onUseGeopointChanged = function() {
+    if ($scope.loading) return;
+    if (!$scope.formData.enableGeoPoint) {
+      if ($scope.formData.geoPoint) {
+        $scope.formData.geoPoint.lat = null;
+        $scope.formData.geoPoint.lon = null;
+        $scope.form.geoPoint.$setValidity('required', true);
+        $scope.dirty = true;
+      }
+    }
+    else {
+      $scope.tryToLocalize();
+    }
+  };
+
+  $scope.onGeopointChanged = function() {
+    if ($scope.loading) {
+      $scope.formData.enableGeoPoint = $scope.formData.geoPoint && !!$scope.formData.geoPoint.lat && !!$scope.formData.geoPoint.lon;
+    }
   };
+  $scope.$watch('formData.geoPoint', $scope.onGeopointChanged);
 
-  $scope.searchPositions = function(query) {
+  $scope.getAddressToSearch = function() {
+    return $scope.formData.address && $scope.formData.city ?
+      [$scope.formData.address.trim(), $scope.formData.city.trim()].join(', ') :
+    $scope.formData.city || $scope.formData.address;
+  };
 
-    // Build the query
-    if (!query) {
-      if (!$scope.formData.city) {
-        return $q.when(); // nothing to search
+  $scope.updateGeoPoint = function(res) {
+    // user cancel
+    if (!res || !res.lat || !res.lon) {
+      // Force use GeoPoint as invalid (if not already a position)
+      if ($scope.formData.enableGeoPoint && (!$scope.formData.geoPoint || !$scope.formData.geoPoint.lat)) {
+        $scope.form.geoPoint.$setValidity('required', false);
       }
+      return;
+    }
 
-      var cityPart = $scope.formData.city.split(',');
-      var city = cityPart[0];
+    $scope.dirty = true;
+    $scope.formData.geoPoint = $scope.formData.geoPoint || {};
+    $scope.formData.geoPoint.lat =  parseFloat(res.lat);
+    $scope.formData.geoPoint.lon =  parseFloat(res.lon);
+    $scope.form.geoPoint.$setValidity('required', true);
 
-      var country = cityPart.length > 1 ? cityPart[1].trim() : defaultCountry;
-      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]);
-          });
+    if (res.address && res.address.city) {
+      var cityParts = [res.address.city];
+      if (res.address.postcode) {
+        cityParts.push(res.address.postcode);
       }
-      else {
-        return $scope.searchPositions({
-          city: city,
-          country: country
-        });
+      if (res.address.country != defaultCountry) {
+        cityParts.push(res.address.country);
       }
+      $scope.formData.city = cityParts.join(', ');
     }
+  };
 
-    var queryString = (query.street ? query.street + ', ' : '') +
-      query.city +
-      (query.country ? ', ' + query.country : '');
-    // Execute the given query
-    return $q.all([
-      $translate('PROFILE.MODAL_LOCATIONS.RESULT_DIVIDER', {address: queryString}),
-      esGeo.point.searchByAddress(query)
-    ])
-      .then(function(res) {
-        var dividerText = res[0];
-        res = res[1];
-        if (!res) return $q.when(); // no result
-
-        // Ask user to choose
-        var parent = {name: dividerText};
-        var hits = res.reduce(function(res, hit){
-          if (hit.class == 'waterway') return res;
-          return res.concat({
-            name: hit.display_name,
-            parent: parent,
-            lat: hit.lat,
-            lon: hit.lon
-          });
-        }, [parent]);
+  /* -- modal -- */
 
-        if (hits.length == 1) return $q.when(); // no result (after filtering)
+  $scope.openSearchLocationModal = function(options) {
 
-        return hits;
-      });
-  };
+    options = options || {};
 
-  $scope.localizeMe = function() {
-    return esGeo.point.current()
-      .then(function(position) {
-        if (!position || !position.lat || !position.lon) return;
-        $scope.formData.geoPoint = $scope.formData.geoPoint || {};
-        $scope.formData.geoPoint.lat =  parseFloat(position.lat);
-        $scope.formData.geoPoint.lon =  parseFloat(position.lon);
-      })
-      .catch(UIUtils.onError('PROFILE.ERROR.GEO_LOCATION_FAILED'));
+    var parameters = {
+      text: options.text || $scope.getAddressToSearch(),
+      results: options.results,
+      fallbackText: options.fallbackText || $scope.formData.city,
+      forceFallback: angular.isDefined(options.forceFallback) ? options.forceFallback : undefined
+    };
+
+    return ModalUtils.show(
+        'plugins/es/templates/common/modal_location.html',
+        'ESSearchPositionModalCtrl',
+        parameters,
+        {
+          focusFirstInput: true
+          //,scope: $scope
+        }
+      )
+      .then($scope.updateGeoPoint);
   };
+}
 
-  $scope.removeLocalisation = function() {
-    if ($scope.formData.geoPoint) {
-      $scope.formData.geoPoint.lat = null;
-      $scope.formData.geoPoint.lon = null;
-    }
+function ESSearchPositionModalController($scope, $q, $translate, esGeo, parameters) {
+  'ngInject';
+
+  $scope.search = {
+    text: parameters.text || '',
+    fallbackText: parameters.fallbackText || undefined,
+    forceFallback: angular.isDefined(parameters.forceFallback) ? parameters.forceFallback : false,
+    loading: false,
+    results: parameters.results || undefined
   };
 
-  $scope.onCityChanged = function() {
-    if ($scope.loading) return;
-    var hasGeoPoint = $scope.formData.geoPoint && $scope.formData.geoPoint.lat && $scope.formData.geoPoint.lon;
-    if (!hasGeoPoint) {
-      return $scope.localizeByAddress();
+  $scope.$on('modal.shown', function() {
+    // Load search
+    $scope.doSearch(true/*first search*/);
+  });
+
+  $scope.doSearch = function(firstSearch) {
+
+    var text = $scope.search.text && $scope.search.text.trim();
+    if (!text) {
+      return $q.when(); // nothing to search
     }
+
+    $scope.search.loading = true;
+
+    // Compute alternative query text
+    var fallbackText = firstSearch && $scope.search.fallbackText && $scope.search.fallbackText.trim();
+    fallbackText = fallbackText && fallbackText != text ? fallbackText : undefined;
+
+    // Execute the given query
+    return ((firstSearch && $scope.search.forceFallback && $scope.search.results) ?
+      $q.when($scope.search.results) :
+      esGeo.point.searchByAddress(text)
+    )
+      .then(function(res) {
+        if (res && res.length || !fallbackText) return res;
+
+        // Fallback search
+        return $q.all([
+          $translate('PROFILE.MODAL_LOCATION.ALTERNATIVE_RESULT_DIVIDER', {address: fallbackText}),
+          esGeo.point.searchByAddress(fallbackText)
+        ])
+          .then(function (res) {
+            var dividerText = res[0];
+            var res = res[1];
+            if (!res || !res.length) return res;
+
+            return [{name: dividerText}].concat(res);
+          });
+      })
+      .then(function(res) {
+        $scope.search.loading = false;
+        $scope.search.results = res||[];
+
+        $scope.license = res && res.length && res[0].license;
+      })
+      .catch(function(err) {
+        $scope.search.loading = false;
+        $scope.search.results = [];
+        $scope.license = undefined;
+        throw err;
+      })
+      ;
   };
 
 }
diff --git a/www/plugins/es/js/controllers/profile-controllers.js b/www/plugins/es/js/controllers/profile-controllers.js
index 3ca009adb1bd3938adb9986649b5095e5831fb4c..ed21fbad6bcd101785a787733d59d042114ba6a9 100644
--- a/www/plugins/es/js/controllers/profile-controllers.js
+++ b/www/plugins/es/js/controllers/profile-controllers.js
@@ -171,6 +171,7 @@ function ESViewEditProfileController($scope, $rootScope, $q, $timeout, $state, $
       }, 650);
     }
 
+
     $scope.saving = true;
     console.debug('[ES] [profile] Saving user profile...');
 
diff --git a/www/plugins/es/js/services/geo-services.js b/www/plugins/es/js/services/geo-services.js
index b338f93888622907022ccc9c881bf950d41c8ed6..e7ad142477b4f8accb24e9383933ae76d0bf2160 100644
--- a/www/plugins/es/js/services/geo-services.js
+++ b/www/plugins/es/js/services/geo-services.js
@@ -18,7 +18,11 @@ angular.module('cesium.es.geo.services', ['cesium.services', 'cesium.es.http.ser
 
     that.raw = {
       osm: {
-        search: csHttp.get('nominatim.openstreetmap.org', 443, '/search.php?format=json')
+        search: csHttp.get('nominatim.openstreetmap.org', 443, '/search.php?format=json'),
+        license: {
+          name: 'OpenStreetMap',
+          url: 'https://www.openstreetmap.org/copyright'
+        }
       },
       google: {
         apiKey: undefined,
@@ -27,6 +31,16 @@ angular.module('cesium.es.geo.services', ['cesium.services', 'cesium.es.http.ser
       searchByIP: csHttp.get('freegeoip.net', 443, '/json/:ip')
     };
 
+    function _normalizeAddressString(text) {
+      // Remove line break
+      var searchText = text.trim().replace(/\n/g, ',');
+      // Remove zip code
+      searchText = searchText.replace(/(?:^|[\t\n\r\s ])([A−Z09-]+)(?:$|[\t\n\r\s ])/g, '');
+      // Remove redundant comma
+      searchText = searchText.replace(/,[ ,]+/g, ', ');
+      return searchText;
+    }
+
     function googleSearchPositionByString(address) {
 
       return that.raw.google.search({address: address, key: that.raw.google.apiKey})
@@ -61,13 +75,44 @@ angular.module('cesium.es.geo.services', ['cesium.services', 'cesium.es.http.ser
         query = {q: query};
       }
 
+      // Normalize query string
+      if (query.q) {
+        query.q = _normalizeAddressString(query.q);
+      }
+
+      query.addressdetails = 1; // need address field
+
       var now = new Date();
       //console.debug('[ES] [geo] Searching position...', query);
 
       return that.raw.osm.search(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;
+          //console.debug('[ES] [geo] Received {0} results from OSM'.format(res && res.length || 0), res);
+          if (!res) return; // no result
+
+          // Filter on city/town/village
+          res = res.reduce(function(res, hit){
+            if (hit.class == 'waterway' || !hit.address) return res;
+            hit.address.city =  hit.address.city || hit.address.village || hit.address.town || hit.address.postcode;
+            hit.address.road =  hit.address.road || hit.address.suburb || hit.address.hamlet;
+            if (hit.address.postcode && hit.address.city == hit.address.postcode) {
+              delete hit.address.postcode;
+            }
+            if (!hit.address.city) return res;
+            return res.concat({
+              id: hit.place_id,
+              name: hit.display_name,
+              address: hit.address,
+              lat: hit.lat,
+              lon: hit.lon,
+              class: hit.class,
+              license: that.raw.osm.license
+            });
+          }, []);
+
+          console.debug('[ES] [geo] Found {0} address position(s)'.format(res && res.length || 0, new Date().getTime() - now.getTime()), res);
+
+          return res.length ? res : undefined;
         })
 
         // Fallback service
diff --git a/www/plugins/es/templates/common/edit_position.html b/www/plugins/es/templates/common/edit_position.html
index 04f495c3246d1f94327724a5ae637e825b998221..1d12151d26fc481dd82cc37b5232961badc78f60 100644
--- a/www/plugins/es/templates/common/edit_position.html
+++ b/www/plugins/es/templates/common/edit_position.html
@@ -11,92 +11,61 @@
 </ion-item>
 
 <!-- city -->
-<div class="item item-input item-floating-label">
+<div class="item item-input item-floating-label"
+     ng-class="{'item-input-error': form.$submitted && form.geoPoint.$invalid}">
   <span class="input-label" translate>PROFILE.CITY</span>
   <input type="text" placeholder="{{'PROFILE.CITY_HELP'|translate}}"
          ng-model="formData.city"
          ng-model-options="{ updateOn: 'blur' }"
          ng-change="onCityChanged()">
 </div>
+<input type="hidden"
+       name="geoPoint"
+       ng-model="formData.geoPoint"
+       geo-point-required>
+<div class="form-errors"
+     ng-show="form.$submitted && form.geoPoint.$error"
+     ng-messages="form.geoPoint.$error">
+  <div class="form-error" ng-message="required">
+    <span translate="COMMON.ERROR.REQUIRED_FOR_LOCATION" ng-if="!formData.city"></span>
+    <span translate="COMMON.ERROR.INVALID_FOR_LOCATION" ng-if="formData.city"></span>
+  </div>
+</div>
+
 
 <!-- Position (lat/lon) -->
-<div class="row responsive-md responsive-sm no-padding">
+<div class="item row responsive-md responsive-sm no-padding">
 
-  <!-- lat -->
   <div class="col no-padding">
-    <label class="item item-input item-floating-label"
-           ng-class="{'item-input-error': form.$submitted && form.latitude.$invalid}">
-      <span class="input-label" translate>PROFILE.LATITUDE</span>
-      <input class="no-padding-right"
-             name="latitude"
-             type="number" placeholder="{{'PROFILE.LATITUDE_HELP'|translate}}"
-             ng-model="formData.geoPoint.lat"
-             ng-model-options="{ debounce: 350 }"
-             ng-change="onFormDataChanged()"
-             min="-90" max="90">
-    </label>
-    <div class="form-errors"
-         ng-show="form.$submitted && form.latitude.$error"
-         ng-messages="form.latitude.$error">
-      <div class="form-error" ng-message="min">
-        <span translate="ERROR.FIELD_MIN" translate-values="{min: -90}"></span>
-      </div>
-      <div class="form-error" ng-message="max">
-        <span translate="ERROR.FIELD_MAX" translate-values="{max: 90}"></span>
-      </div>
-    </div>
-  </div>
-
-  <div class="col-10 no-padding hidden-xs">
-    &nbsp;
-  </div>
 
-  <!-- lon -->
-  <div class="col no-padding">
-    <label class="item item-input item-floating-label"
-           ng-class="{'item-input-error': form.$submitted && form.longitude.$invalid}">
-      <span class="input-label" translate>PROFILE.LONGITUDE</span>
-      <input class="no-padding-right"
-             name="longitude"
-             type="number" placeholder="{{'PROFILE.LONGITUDE_HELP'|translate}}"
-             ng-model="formData.geoPoint.lon"
-             ng-model-options="{ debounce: 350 }"
-             ng-change="onFormDataChanged()"
-             min="-180" max="180">
-    </label>
-    <div class="form-errors"
-         ng-show="form.$submitted && form.longitude.$error"
-         ng-messages="form.longitude.$error">
-      <div class="form-error" ng-message="min">
-        <span translate="ERROR.FIELD_MIN" translate-values="{min: -180}"></span>
+    <!-- appear on map ? -->
+    <ion-checkbox ng-model="formData.enableGeoPoint"
+                  ng-change="onUseGeopointChanged()"
+                  class="item item-border-large done in">
+      <div class="item-content">
+        <span translate>PROFILE.USE_GEO_POINT</span>
+        <h4 class="gray" ng-if="loadingPosition">
+          <ion-spinner class="icon ion-spinner-small" icon="android"></ion-spinner>
+          {{'PROFILE.LOADING_LOCATION'|translate}}
+        </h4>
       </div>
-      <div class="form-error" ng-message="max">
-        <span translate="ERROR.FIELD_MAX" translate-values="{max: 180}"></span>
-      </div>
-    </div>
+    </ion-checkbox>
   </div>
 
-  <div class="col col-40">
+  <div class="col col-10 no-padding">
     <div class="row text-center">
 
-      <a class="col button  button-stable button-small-padding icon ion-refresh"
+      <a class="button button-stable button-small-padding"
          title="{{'PROFILE.BTN_GEOLOC_ADDRESS'|translate}}"
-         ng-disabled="!formData.city"
-         ng-click="localizeByAddress()">
+         ng-disabled="!formData.enableGeoPoint"
+         ng-click="openSearchLocationModal()">
+        <i class=" icon ion-home" style="left: 15px;"></i>
+        <b class=" icon-secondary ion-search" style="top: -9px; left:32px; font-size: 18px;"></b>
       </a>
 
-      <a class="col button button-stable button-small-padding icon ion-android-locate"
-         title="{{'PROFILE.BTN_GEOLOC_ME'|translate}}"
-         ng-click="localizeMe()">
-      </a>
-
-      <a class="col 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>
diff --git a/www/plugins/es/templates/common/modal_location.html b/www/plugins/es/templates/common/modal_location.html
new file mode 100644
index 0000000000000000000000000000000000000000..b8987bb6f8ca3df69c6cf13111cf6953126f7610
--- /dev/null
+++ b/www/plugins/es/templates/common/modal_location.html
@@ -0,0 +1,90 @@
+<ion-modal-view class="modal-full-height modal-search-location">
+  <ion-header-bar class="bar-positive">
+      <button class="button button-clear" ng-click="closeModal()" translate>COMMON.BTN_CANCEL</button>
+      <h1 class="title" translate>PROFILE.MODAL_LOCATION.TITLE</h1>
+  </ion-header-bar>
+
+  <ion-content class="padding no-padding-xs" scroll="true">
+
+
+    <!-- search text -->
+    <div class="item item-input">
+      <i class="icon ion-search placeholder-icon"></i>
+
+      <input type="text"
+             class="visible-xs visible-sm"
+             placeholder="{{'PROFILE.MODAL_LOCATION.SEARCH_HELP'|translate}}"
+             ng-model="search.text"
+             ng-model-options="{ debounce: 650 }"
+             ng-change="doSearch()">
+      <input type="text"
+             class="hidden-xs hidden-sm"
+              placeholder="{{'PROFILE.MODAL_LOCATION.SEARCH_HELP'|translate}}"
+             ng-model="search.text"
+             on-return="doSearch()">
+    </div>
+
+    <div class="padding-top padding-xs" style="display: block; height: 60px;">
+      <div class="pull-left" ng-if="!search.loading && search.results">
+        <h4 translate>COMMON.RESULTS_LIST</h4>
+      </div>
+
+      <div class="pull-right hidden-xs hidden-sm">
+        <button class="button button-small button-stable ink"
+                ng-click="doSearch()">
+          {{'COMMON.BTN_SEARCH' | translate}}
+        </button>
+      </div>
+
+    </div>
+
+    <div class="text-center" ng-if="search.loading">
+      <ion-spinner icon="android"></ion-spinner>
+    </div>
+
+    <div ng-if="!search.loading && search.results && (!search.results.length || !search.results[0].address)"
+         class="assertive padding">
+      <span translate>COMMON.SEARCH_NO_RESULT</span>
+    </div>
+
+    <ion-list ng-if="!search.loading"
+              class="padding-top {{::motion.ionListClass}}">
+      <div ng-repeat="res in search.results"
+         class="item item-border-large  item-text-wrap  ink"
+         ng-class="::{'item-divider': !res.address, 'item-icon-left item-icon-right': res.address}"
+         ng-click="res.address ? closeModal(res) : false">
+
+        <!-- if divider -->
+        <h4 class="text-italic" ng-if="::!res.address" ng-bind-html="res.name"></h4>
+
+        <!-- if divider -->
+        <ng-if ng-if="::res.address">
+
+          <i class="icon ion-location"></i>
+
+          <h2 ng-if="res.address.road">
+            {{::res.address.road}}
+          </h2>
+          <h3>
+            <span ng-if="res.address.postcode">{{::res.address.postcode}}</span>
+            {{::res.address.city||res.address.village}}
+            <span class="gray">| {{::res.address.country}}</span>
+          </h3>
+          <h5 class="gray">
+             {{'PROFILE.MODAL_LOCATION.POSITION'|translate:res }}
+          </h5>
+
+          <i class="icon ion-ios-arrow-right"></i>
+        </ng-if>
+
+      </div>
+    </ion-list>
+  </ion-content>
+
+  <ion-footer-bar class="stable-bg padding-left padding-right block" ng-if="license">
+    <div class="pull-right copyright">
+      <span class="dark">© </span>
+      <a class="positive" href="{{license.url}}" target="_blank">{{license.name}}</a>
+    </div>
+  </ion-footer-bar>
+</ion-modal-view>
diff --git a/www/plugins/es/templates/user/edit_profile.html b/www/plugins/es/templates/user/edit_profile.html
index 5a49ea3ccec6ef13443df5e394ac5af11af7a299..296b9268e2bdfb6acd559de70cbb98abc0398da3 100644
--- a/www/plugins/es/templates/user/edit_profile.html
+++ b/www/plugins/es/templates/user/edit_profile.html
@@ -93,7 +93,7 @@
             </ion-item>
 
             <!-- position -->
-            <ng-include src="'plugins/es/templates/common/edit_position.html'" ng-controller="ESPositionEditCtrl"></ng-include>
+            <ng-include src="'plugins/es/templates/common/edit_position.html'" ng-controller="ESPositionEditCtrl as ctrl"></ng-include>
 
             <!-- social networks -->
             <ng-include src="'plugins/es/templates/common/edit_socials.html'" ng-controller="ESSocialsEditCtrl"></ng-include>
diff --git a/www/plugins/map/i18n/locale-fr-FR.json b/www/plugins/map/i18n/locale-fr-FR.json
index 8070a7d00798e80525d4d43a1bfc48d6dc2c9d20..a7df1a09e968a66b36e3124a6b583229eea7e8f4 100644
--- a/www/plugins/map/i18n/locale-fr-FR.json
+++ b/www/plugins/map/i18n/locale-fr-FR.json
@@ -32,7 +32,7 @@
       }
     },
     "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."
+      "MARKER_HELP": "<b>Glissez-déposez</b> ce marqueur pour <b>mettre<br/>à jour votre position</b> sur la carte, ou utilisez le bouton<br/>de recherche au dessus de la carte."
     },
     "ERROR": {
       "LOCALIZE_ME_FAILED": "Impossible de récupérer votre position actuelle"
diff --git a/www/plugins/map/js/controllers/user-controllers.js b/www/plugins/map/js/controllers/user-controllers.js
index 457941574c44ee86d39e45036a8ceadfcfb24c8d..246a4711fdb56c8c441732496f32ed33ef99bf69 100644
--- a/www/plugins/map/js/controllers/user-controllers.js
+++ b/www/plugins/map/js/controllers/user-controllers.js
@@ -30,11 +30,6 @@ angular.module('cesium.map.user.controllers', ['cesium.services', 'cesium.map.se
       markers: {},
       center: {
         zoom: 13
-      },
-      defaults: {
-        tileLayerOptions: {
-          attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
-        }
       }
     });
     $scope.loading = true;
diff --git a/www/plugins/map/js/services/utils-services.js b/www/plugins/map/js/services/utils-services.js
index 10aad7631c66ed724f5e6f9f4df541a7f82169bf..aad671082571a0d664a830af3fe8f149ccf424a3 100644
--- a/www/plugins/map/js/services/utils-services.js
+++ b/www/plugins/map/js/services/utils-services.js
@@ -26,7 +26,10 @@ angular.module('cesium.map.utils.services', ['cesium.services', 'ui-leaflet'])
       center: angular.copy(constants.DEFAULT_CENTER),
       cache: false,
       defaults: {
-        scrollWheelZoom: true
+        scrollWheelZoom: true,
+        tileLayerOptions: {
+          attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
+        }
       },
       layers: {
         baselayers: {
diff --git a/www/templates/wot/lookup_form.html b/www/templates/wot/lookup_form.html
index cde0230d1a8be0fe62d214ff086fe18e3551db3a..2f0f12786bd8fe4dea0f6205ed8fcd78682c3c03 100644
--- a/www/templates/wot/lookup_form.html
+++ b/www/templates/wot/lookup_form.html
@@ -54,7 +54,7 @@
       </h4>
     </div>
 
-    <div class=" pull-right hidden-xs hidden-sm">
+    <div class="pull-right hidden-xs hidden-sm">
       <a ng-if="enableFilter"
          class="button button-text button-small ink"
          ng-class="{'button-text-positive': search.type=='newcomers'}"