diff --git a/bower.json b/bower.json
index 498c7e07368e8d2d6a741b959171c81338922bb6..c0d0fc0a32eecb853d7020ffb56c0fcf943cd408 100644
--- a/bower.json
+++ b/bower.json
@@ -15,7 +15,10 @@
     "angular-bind-notifier": "^1.1.7",
     "angular-image-crop": "^2.0.0",
     "ng-idle": "^1.3.2",
-    "chart.js": "chartjs#^2.6.0"
+    "chart.js": "chartjs#^2.6.0",
+    "Leaflet.awesome-markers": "^2.0.2",
+    "ui-leaflet": "^2.0.0",
+    "leaflet-search": "^2.7.2"
   },
   "resolutions": {
     "angular-sanitize": "1.5.3",
diff --git a/ionic.project b/ionic.project
index d6981951ccdd19d2d442ef460f1685ab85ced8dd..e7acc66003ab5468b51261c8dbb130969cdd44b3 100644
--- a/ionic.project
+++ b/ionic.project
@@ -26,4 +26,4 @@
       "version": "12.41.296.5"
     }
   ]
-}
+}
\ No newline at end of file
diff --git a/www/index.html b/www/index.html
index 73a164150fe260a5d3bae72222446a4f7b637c9e..9ad01f166f913868a68460ac1f84e494db1f89ab 100644
--- a/www/index.html
+++ b/www/index.html
@@ -18,11 +18,19 @@
   <!--endRemoveIf(device)-->
 
   <!--removeIf(no-plugin)-->
-  <link href="dist/dist_css/plugins/es/css/style.css" rel="stylesheet">
-  <link href="dist/dist_css/plugins/graph/css/style.css" rel="stylesheet">
-
-
+  <link rel="stylesheet" type="text/css" href="lib/leaflet/dist/leaflet.css">
+  <link rel="stylesheet" type="text/css" href="lib/Leaflet.awesome-markers/dist/leaflet.awesome-markers.css">
+  <!--removeIf(no-device)-->
+  <!--<link rel="stylesheet" type="text/css" href="lib/leaflet-search/dist/leaflet-search.mobile.src.css">-->
+  <!--endRemoveIf(no-device)-->
+  <!--removeIf(device)-->
+  <link rel="stylesheet" type="text/css" href="lib/leaflet-search/dist/leaflet-search.src.css">
+  <!--endRemoveIf(device)-->
+  <link rel="stylesheet" type="text/css" href="dist/dist_css/plugins/es/css/style.css">
+  <link rel="stylesheet" type="text/css" href="dist/dist_css/plugins/graph/css/style.css">
+  <link rel="stylesheet" type="text/css" href="dist/dist_css/plugins/map/css/style.css">
   <!--endRemoveIf(no-plugin)-->
+
   <!-- endbuild -->
   <!-- build:js dist_js/vendor.js -->
   <!-- vendor js -->
@@ -72,6 +80,15 @@
   <script src="js/vendor/sha256.min.js" async></script>
   <script src="js/vendor/ng-cordova.min.js"></script>
   <!--endRemoveIf(no-device)-->
+
+  <!--removeIf(no-plugin)-->
+  <script src="lib/leaflet/dist/leaflet.js"></script>
+  <script src="lib/angular-simple-logger/dist/angular-simple-logger.js"></script>
+  <script src="lib/ui-leaflet/dist/ui-leaflet.js"></script>
+  <script src="lib/Leaflet.awesome-markers/dist/leaflet.awesome-markers.js"></script>
+  <script src="lib/leaflet-search/dist/leaflet-search.src.js"></script>
+  <!--endRemoveIf(no-plugin)-->
+
   <!-- endbuild -->
 
   <!--removeIf(no-device)-->
@@ -149,6 +166,7 @@
   <script src="dist/dist_js/plugins/es/js/services/invitation-services.js"></script>
   <script src="dist/dist_js/plugins/es/js/services/subscription-services.js"></script>
   <script src="dist/dist_js/plugins/es/js/services/wallet-services.js"></script>
+  <script src="dist/dist_js/plugins/es/js/services/geo-services.js"></script>
   <script src="dist/dist_js/plugins/es/js/controllers/common-controllers.js"></script>
   <script src="dist/dist_js/plugins/es/js/controllers/app-controllers.js"></script>
   <script src="dist/dist_js/plugins/es/js/controllers/settings-controllers.js"></script>
@@ -165,7 +183,6 @@
   <script src="dist/dist_js/plugins/es/js/controllers/invitation-controllers.js"></script>
   <script src="dist/dist_js/plugins/es/js/controllers/subscription-controllers.js"></script>
 
-
   <!-- Graph plugin -->
   <!--removeIf(ubuntu)--> <!-- FIXME: issue #463 -->
   <script src="dist/dist_js/plugins/graph/js/plugin.js"></script>
@@ -179,6 +196,9 @@
   <script src="dist/dist_js/plugins/graph/js/controllers/account-controllers.js"></script>
   <!--endRemoveIf(ubuntu)-->
 
+  <!-- Map plugin -->
+  <script src="dist/dist_js/plugins/map/js/plugin.js"></script>
+
   <!-- RML9 plugin -->
   <!--<script src="dist/dist_js/plugins/rml9/plugin-01-add_button.js"></script>-->
   <!--<script src="dist/dist_js/plugins/rml9/plugin-02-add_view.js"></script>-->
@@ -186,16 +206,9 @@
   <!--<script src="dist/dist_js/plugins/rml9/plugin-04-chart.js"></script>-->
   <!--<script src="dist/dist_js/plugins/rml9/plugin-05-service_api.js"></script>-->
   <!--<script src="dist/dist_js/plugins/rml9/plugin-06-settings.js"></script>-->
+  <!--<script src="dist/dist_js/plugins/rml9/plugin-07-add_map.js"></script>-->
   <!--<script src="dist/dist_js/plugins/rml9/plugin-final.js"></script>-->
 
-  <!-- RML9 plugin: add a map
-  <link rel="stylesheet" type="text/css" href="lib/leaflet/dist/leaflet.css">
-  <link rel="stylesheet" type="text/css" href="dist/dist_css/plugins/rml9/css/style.css">
-  <script src="lib/leaflet/dist/leaflet.js"></script>
-  <script src="lib/angular-simple-logger/dist/angular-simple-logger.js"></script>
-  <script src="lib/ui-leaflet/dist/ui-leaflet.js"></script>
-  <script src="dist/dist_js/plugins/rml9/plugin-07-add_map.js"></script>-->
-
   <!--endRemoveIf(no-plugin)-->
 
   <!-- App -->
diff --git a/www/js/app.js b/www/js/app.js
index 28447db712818754395a15c8df31c9dc8995446c..6d607a1b09981d5cd92c58f7ec586be6364de05b 100644
--- a/www/js/app.js
+++ b/www/js/app.js
@@ -5,8 +5,7 @@
 // the 2nd parameter is an array of 'requires'
 // 'starter.controllers' is found in controllers.js
 angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'pascalprecht.translate',
-  'ngApi', 'angular-cache', 'angular.screenmatch', 'angular.bind.notifier', 'ImageCropper',
-  //'ui-leaflet',
+  'ngApi', 'angular-cache', 'angular.screenmatch', 'angular.bind.notifier', 'ImageCropper', 'ui-leaflet',
   // removeIf(no-device)
   'ngCordova',
   // endRemoveIf(no-device)
diff --git a/www/js/plugins.js b/www/js/plugins.js
index b3bbfa6ae5776eb1de0b5c69c3c14abb6631aab4..9dcbb1a6d1ab4804bc6162436255fa3556f3526b 100644
--- a/www/js/plugins.js
+++ b/www/js/plugins.js
@@ -17,6 +17,9 @@ angular.module('cesium.plugins', [
   //'cesium.rml9.plugin',
 
   // ES plugin (Cesium+):
-  'cesium.es.plugin'
+  'cesium.es.plugin',
+
+  // Map plugin (Cesium+):
+  'cesium.map.plugin'
   ])
 ;
diff --git a/www/plugins/es/i18n/locale-fr-FR.json b/www/plugins/es/i18n/locale-fr-FR.json
index 729e02932b205300650dee74148e9592e99f2fca..08e041fb15fa5a5d665c308a18ee26724171c9f7 100644
--- a/www/plugins/es/i18n/locale-fr-FR.json
+++ b/www/plugins/es/i18n/locale-fr-FR.json
@@ -374,6 +374,8 @@
     "NO_PROFILE_DEFINED": "Aucun profil Cesium+",
     "BTN_ADD": "Saisir mon profil",
     "BTN_EDIT": "Editer mon profil",
+    "BTN_GEOLOC_CITY": "Localiser la ville",
+    "BTN_GEOLOC_ME": "Me localiser",
     "UID": "Pseudonyme",
     "TITLE": "Nom, Prénom",
     "TITLE_HELP": "Nom, Prénom",
@@ -387,6 +389,11 @@
     "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",
+    "LONGITUDE_HELP": "Longitude",
     "TECHNICAL_DIVIDER": "Informations techniques",
     "MODAL_AVATAR": {
       "TITLE": "Photo de profil",
@@ -399,7 +406,8 @@
       "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"
+      "IMAGE_RESIZE_FAILED": "Erreur lors du redimensionnement de l'image",
+      "GEO_LOCATION_FAILED": "Echec de la récupération de votre position"
     },
     "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 8cd37f2dffdb81d10b627a245aba0ad62c8a5174..5322dd8a50a762f711beaf197b366fc996754319 100644
--- a/www/plugins/es/js/controllers/profile-controllers.js
+++ b/www/plugins/es/js/controllers/profile-controllers.js
@@ -25,7 +25,7 @@ angular.module('cesium.es.profile.controllers', ['cesium.es.services'])
 ;
 
 function ESViewEditProfileController($scope, $rootScope, $timeout, $state, $focus, $translate, $ionicHistory,
-                           UIUtils, esHttp, esProfile, ModalUtils, Device) {
+                           UIUtils, esHttp, esProfile, esGeo, ModalUtils, Device) {
   'ngInject';
 
   $scope.loading = true;
@@ -34,7 +34,8 @@ function ESViewEditProfileController($scope, $rootScope, $timeout, $state, $focu
   $scope.formData = {
     title: null,
     description: null,
-    socials: []
+    socials: [],
+    geoPoint: {}
   };
   $scope.avatar = null;
   $scope.existing = false;
@@ -144,11 +145,11 @@ function ESViewEditProfileController($scope, $rootScope, $timeout, $state, $focu
     console.debug('[ES] [profile] Saving user profile...');
     $scope.saving = true;
 
-    // removeIf(device)
+    // removeIf(no-device)
     if (!silent) {
       UIUtils.loading.show();
     }
-    // endRemoveIf(device)
+    // endRemoveIf(no-device)
 
     var onError = function(message) {
       return function(err) {
@@ -174,9 +175,9 @@ function ESViewEditProfileController($scope, $rootScope, $timeout, $state, $focu
 
     var showSuccessToast = function() {
       if (!silent) {
-        // removeIf(device)
+        // removeIf(no-device)
         UIUtils.loading.hide();
-        // endRemoveIf(device)
+        // endRemoveIf(no-device)
 
         return $translate('PROFILE.INFO.PROFILE_SAVED')
           .then(function(message){
@@ -289,6 +290,50 @@ function ESViewEditProfileController($scope, $rootScope, $timeout, $state, $focu
         $scope.rotating = false;
       });
   };
+
+  $scope.localizeByCity = function() {
+    if (!$scope.formData.city) {
+      return;
+    }
+
+    var address = angular.copy($scope.formData.city);
+
+    return esGeo.point.searchByAddress(address)
+      .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;
+          }
+        }
+      });
+  };
+
+
+  $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'));
+  };
+
+  $scope.onCityChanged = function() {
+    var hasGeoPoint = $scope.formData.geoPoint && $scope.formData.geoPoint.lat && $scope.formData.geoPoint.lon;
+    if (!hasGeoPoint) {
+      return $scope.localizeByCity();
+    }
+  };
+  $scope.$watch('formData.city', $scope.onCityChanged, true);
+
 }
 
 
diff --git a/www/plugins/es/js/services.js b/www/plugins/es/js/services.js
index 2597433ff17798c952b2a4a1c9056488de705065..61a0824da0a2b220fad5072703d2d7ebcbb5a863 100644
--- a/www/plugins/es/js/services.js
+++ b/www/plugins/es/js/services.js
@@ -15,6 +15,7 @@ angular.module('cesium.es.services', [
     'cesium.es.group.services',
     'cesium.es.wallet.services',
     'cesium.es.invitation.services',
-    'cesium.es.subscription.services'
+    'cesium.es.subscription.services',
+    'cesium.es.geo.services'
   ])
 ;
diff --git a/www/plugins/es/js/services/geo-services.js b/www/plugins/es/js/services/geo-services.js
new file mode 100644
index 0000000000000000000000000000000000000000..87314b32216cf923a01ad177dfadeb71fc223ea2
--- /dev/null
+++ b/www/plugins/es/js/services/geo-services.js
@@ -0,0 +1,76 @@
+angular.module('cesium.es.geo.services', ['cesium.services', 'cesium.es.http.services'])
+  .config(function(PluginServiceProvider, csConfig) {
+    'ngInject';
+
+    var enable = csConfig.plugins && csConfig.plugins.es;
+    if (enable) {
+      // Will force to load this service
+      PluginServiceProvider.registerEagerLoadingService('esGeo');
+    }
+
+  })
+
+  .factory('esGeo', function($q, csHttp) {
+    'ngInject';
+
+    var
+      that = this;
+
+    that.raw = {
+      searchByAddress: csHttp.get('nominatim.openstreetmap.org', 80, '/search.php?format=json&q=:query'),
+      searchByIP: csHttp.get('freegeoip.net', 80, '/json/:ip')
+    };
+
+    function searchPositionByAddress(queryString) {
+
+      var now = new Date();
+      console.debug('[ES] [geo] Searching address position [{0}]...'.format(queryString));
+
+      return that.raw.searchByAddress({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 getCurrentPosition() {
+      var defer = $q.defer();
+      if (navigator.geolocation) {
+        navigator.geolocation.getCurrentPosition(function(position) {
+          if (!position || !position.coords) {
+            console.error('[ES] [geo] navigator geolocation > Unknown format:', position);
+            return;
+          }
+          defer.resolve({
+            lat: position.coords.latitude,
+            lon: position.coords.longitude
+          });
+        }, function(error) {
+          defer.reject(error);
+        },{timeout:5000});
+      }else{
+        defer.reject();
+      }
+      return defer.promise;
+    }
+
+    function searchPositionByIP(ip) {
+
+      var now = new Date();
+      console.debug('[ES] [geo] Searching IP position [{0}]...'.format(ip));
+
+      return that.raw.searchByIP({ip: ip})
+        .then(function(res) {
+          console.debug('[ES] [geo] Found IP {0} position in {0}ms'.format(res ? 1 : 0, new Date().getTime() - now.getTime()));
+          return res ? {lat: res.latitude,lng: res.longitude} : undefined;
+        });
+    }
+
+    return {
+      point: {
+        current: getCurrentPosition,
+        searchByAddress: searchPositionByAddress,
+        searchByIP: searchPositionByIP
+      }
+    };
+  });
diff --git a/www/plugins/es/js/services/group-services.js b/www/plugins/es/js/services/group-services.js
index aac5eb2cad35eb94e443b1ee01c069b2f9b85493..6fd028275b55e6e743aa12b018ac89095be2d24a 100644
--- a/www/plugins/es/js/services/group-services.js
+++ b/www/plugins/es/js/services/group-services.js
@@ -14,219 +14,214 @@ angular.module('cesium.es.group.services', ['cesium.platform', 'cesium.es.http.s
 .factory('esGroup', function($q, $rootScope, csPlatform, csSettings, esHttp, CryptoUtils, csWot, csWallet, esNotification, esComment) {
   'ngInject';
 
-  function EsGroup() {
-
-    var
-      listeners,
-      defaultLoadSize = 50,
-      fields = {
-        list: ["issuer", "title"],
-        commons: ["issuer", "title", "description", "creationTime", "time", "signature"],
-        notifications: ["issuer", "time", "hash", "read_signature"]
-      },
-      exports = {
-        _internal: {}
-      };
+  var
+    listeners,
+    defaultLoadSize = 50,
+    fields = {
+      list: ["issuer", "title"],
+      commons: ["issuer", "title", "description", "creationTime", "time", "signature"],
+      notifications: ["issuer", "time", "hash", "read_signature"]
+    },
+    exports = {
+      _internal: {}
+    };
 
-    function onWalletInit(data) {
-      data.groups = data.groups || {};
-      data.groups.unreadCount = null;
-    }
+  function onWalletInit(data) {
+    data.groups = data.groups || {};
+    data.groups.unreadCount = null;
+  }
 
-    function onWalletReset(data) {
-      if (data.groups) {
-        delete data.groups;
-      }
+  function onWalletReset(data) {
+    if (data.groups) {
+      delete data.groups;
     }
+  }
 
-    function onWalletLogin(data, deferred) {
-      deferred = deferred || $q.defer();
-      if (!data || !data.pubkey) {
-        deferred.resolve();
-        return deferred.promise;
-      }
-
-      // Count unread notifications
-      esNotification.unreadCount(data.pubkey, {codes: {
-        includes: ['GROUP_INVITATION'],
-        excludes: []
-      }})
-        .then(function(unreadCount){
-          data.groups = data.groups || {};
-          data.groups.unreadCount = unreadCount;
-          console.debug('[ES] [group] Detecting ' + unreadCount + ' unread notifications');
-          deferred.resolve(data);
-        })
-        .catch(function(err){
-          console.error('Error while counting group notifications: ' + (err.message ? err.message : err));
-          deferred.resolve(data);
-        });
+  function onWalletLogin(data, deferred) {
+    deferred = deferred || $q.defer();
+    if (!data || !data.pubkey) {
+      deferred.resolve();
       return deferred.promise;
     }
 
-    function readRecordFromHit(hit, html) {
-      if (!hit) return;
-      var record = hit._source;
-      if (html && hit.highlight) {
-        if (hit.highlight.title) {
-          record.title = hit.highlight.title[0];
-        }
-        if (hit.highlight.description) {
-          record.description = hit.highlight.description[0];
-        }
-        if (hit.highlight.location) {
-          record.location = hit.highlight.location[0];
-        }
-        if (hit.highlight.tags) {
-          data.tags = hit.highlight.tags.reduce(function(res, tag){
-            return res.concat(tag.replace('<em>', '').replace('</em>', ''));
-          },[]);
-        }
-      }
+    // Count unread notifications
+    esNotification.unreadCount(data.pubkey, {codes: {
+      includes: ['GROUP_INVITATION'],
+      excludes: []
+    }})
+      .then(function(unreadCount){
+        data.groups = data.groups || {};
+        data.groups.unreadCount = unreadCount;
+        console.debug('[ES] [group] Detecting ' + unreadCount + ' unread notifications');
+        deferred.resolve(data);
+      })
+      .catch(function(err){
+        console.error('Error while counting group notifications: ' + (err.message ? err.message : err));
+        deferred.resolve(data);
+      });
+    return deferred.promise;
+  }
 
-      // description
-      if (html) {
-        record.description = esHttp.util.trustAsHtml(record.description);
+  function readRecordFromHit(hit, html) {
+    if (!hit) return;
+    var record = hit._source;
+    if (html && hit.highlight) {
+      if (hit.highlight.title) {
+        record.title = hit.highlight.title[0];
       }
-
-      // thumbnail
-      record.thumbnail = esHttp.image.fromHit(hit, 'thumbnail');
-
-      // pictures
-      if (hit._source.pictures && hit._source.pictures.reduce) {
-        record.pictures = hit._source.pictures.reduce(function(res, pic) {
-          return res.concat(esHttp.image.fromAttachment(pic.file));
-        }, []);
+      if (hit.highlight.description) {
+        record.description = hit.highlight.description[0];
+      }
+      if (hit.highlight.location) {
+        record.location = hit.highlight.location[0];
+      }
+      if (hit.highlight.tags) {
+        data.tags = hit.highlight.tags.reduce(function(res, tag){
+          return res.concat(tag.replace('<em>', '').replace('</em>', ''));
+        },[]);
       }
-
-      return record;
     }
 
-    exports._internal.search = esHttp.post('/group/record/_search');
+    // description
+    if (html) {
+      record.description = esHttp.util.trustAsHtml(record.description);
+    }
 
-    function searchGroups(options) {
-      if (!csWallet.isLogin()) {
-        return $q.when([]);
-      }
+    // thumbnail
+    record.thumbnail = esHttp.image.fromHit(hit, 'thumbnail');
 
-      options = options || {};
-      options.from = options.from || 0;
-      options.size = options.size || defaultLoadSize;
-      options._source = options._source || fields.list;
-      var request = {
-        sort: {
-          "time" : "desc"
-        },
-        from: options.from,
-        size: options.size,
-        _source: options._source
-      };
-
-      return exports._internal.search(request)
-        .then(function(res) {
-          if (!res || !res.hits || !res.hits.total) {
-            return [];
-          }
-          var groups = res.hits.hits.reduce(function(res, hit) {
-            var record = readRecordFromHit(hit, true/*html*/);
-            record.id = hit._id;
-            return record ? res.concat(record) : res;
-          }, []);
-
-          console.debug('[ES] [group] Loading {0} {1} messages'.format(groups.length, options.type));
-
-          return groups;
-        });
+    // pictures
+    if (hit._source.pictures && hit._source.pictures.reduce) {
+      record.pictures = hit._source.pictures.reduce(function(res, pic) {
+        return res.concat(esHttp.image.fromAttachment(pic.file));
+      }, []);
     }
 
-    exports._internal.get = esHttp.get('/group/record/:id');
-    exports._internal.getCommons = esHttp.get('/group/record/:id?_source=' + fields.commons.join(','));
-
-    function loadData(id, options) {
-      options = options || {};
-      options.fecthPictures = angular.isDefined(options.fetchPictures) ? options.fetchPictures : false;
-      options.html = angular.isDefined(options.html) ? options.html : true;
-
-      // Do get source
-      var promise = options.fecthPictures ?
-        exports._internal.get({id: id}) :
-        exports._internal.getCommons({id: id});
-
-      return promise
-        .then(function(hit) {
-          var record = readRecordFromHit(hit, options.html);
-
-          // Load issuer (avatar, name, uid, etc.)
-          return csWot.extend({pubkey: record.issuer})
-            .then(function(issuer) {
-              return {
-                id: hit._id,
-                issuer: issuer,
-                record: record
-              };
-            });
-        });
-    }
+    return record;
+  }
 
-    function removeListeners() {
-      _.forEach(listeners, function(remove){
-        remove();
-      });
-      listeners = [];
-    }
+  exports._internal.search = esHttp.post('/group/record/_search');
 
-    function addListeners() {
-      // Extend csWallet.loadData()
-      listeners = [
-        csWallet.api.data.on.login($rootScope, onWalletLogin, this),
-        csWallet.api.data.on.init($rootScope, onWalletInit, this),
-        csWallet.api.data.on.reset($rootScope, onWalletReset, this)
-      ];
+  function searchGroups(options) {
+    if (!csWallet.isLogin()) {
+      return $q.when([]);
     }
 
-    function refreshState() {
-      var enable = esHttp.alive;
-      if (!enable && listeners && listeners.length > 0) {
-        console.debug("[ES] [group] Disable");
-        removeListeners();
-        if (csWallet.isLogin()) {
-          onWalletReset(csWallet.data);
-        }
-      }
-      else if (enable && (!listeners || listeners.length === 0)) {
-        console.debug("[ES] [group] Enable");
-        addListeners();
-        if (csWallet.isLogin()) {
-          onWalletLogin(csWallet.data);
+    options = options || {};
+    options.from = options.from || 0;
+    options.size = options.size || defaultLoadSize;
+    options._source = options._source || fields.list;
+    var request = {
+      sort: {
+        "time" : "desc"
+      },
+      from: options.from,
+      size: options.size,
+      _source: options._source
+    };
+
+    return exports._internal.search(request)
+      .then(function(res) {
+        if (!res || !res.hits || !res.hits.total) {
+          return [];
         }
-      }
-    }
+        var groups = res.hits.hits.reduce(function(res, hit) {
+          var record = readRecordFromHit(hit, true/*html*/);
+          record.id = hit._id;
+          return record ? res.concat(record) : res;
+        }, []);
+
+        console.debug('[ES] [group] Loading {0} {1} messages'.format(groups.length, options.type));
+
+        return groups;
+      });
+  }
 
-    // Default actions
-    csPlatform.ready().then(function() {
-      esHttp.api.node.on.start($rootScope, refreshState, this);
-      esHttp.api.node.on.stop($rootScope, refreshState, this);
-      return refreshState();
+  exports._internal.get = esHttp.get('/group/record/:id');
+  exports._internal.getCommons = esHttp.get('/group/record/:id?_source=' + fields.commons.join(','));
+
+  function loadData(id, options) {
+    options = options || {};
+    options.fecthPictures = angular.isDefined(options.fetchPictures) ? options.fetchPictures : false;
+    options.html = angular.isDefined(options.html) ? options.html : true;
+
+    // Do get source
+    var promise = options.fecthPictures ?
+      exports._internal.get({id: id}) :
+      exports._internal.getCommons({id: id});
+
+    return promise
+      .then(function(hit) {
+        var record = readRecordFromHit(hit, options.html);
+
+        // Load issuer (avatar, name, uid, etc.)
+        return csWot.extend({pubkey: record.issuer})
+          .then(function(issuer) {
+            return {
+              id: hit._id,
+              issuer: issuer,
+              record: record
+            };
+          });
+      });
+  }
+
+  function removeListeners() {
+    _.forEach(listeners, function(remove){
+      remove();
     });
+    listeners = [];
+  }
 
-    return {
-      record: {
-        search: searchGroups,
-        load: loadData,
-        add: esHttp.record.post('/group/record'),
-        update: esHttp.record.post('/group/record/:id/_update'),
-        remove: esHttp.record.remove('group', 'record'),
-        fields: {
-          commons: fields.commons
-        },
-        picture: {
-          all: esHttp.get('/group/record/:id?_source=pictures')
-        },
-        comment: esComment.instance('group')
+  function addListeners() {
+    // Extend csWallet.loadData()
+    listeners = [
+      csWallet.api.data.on.login($rootScope, onWalletLogin, this),
+      csWallet.api.data.on.init($rootScope, onWalletInit, this),
+      csWallet.api.data.on.reset($rootScope, onWalletReset, this)
+    ];
+  }
+
+  function refreshState() {
+    var enable = esHttp.alive;
+    if (!enable && listeners && listeners.length > 0) {
+      console.debug("[ES] [group] Disable");
+      removeListeners();
+      if (csWallet.isLogin()) {
+        onWalletReset(csWallet.data);
       }
-    };
+    }
+    else if (enable && (!listeners || listeners.length === 0)) {
+      console.debug("[ES] [group] Enable");
+      addListeners();
+      if (csWallet.isLogin()) {
+        onWalletLogin(csWallet.data);
+      }
+    }
   }
 
-  return EsGroup();
+  // Default actions
+  csPlatform.ready().then(function() {
+    esHttp.api.node.on.start($rootScope, refreshState, this);
+    esHttp.api.node.on.stop($rootScope, refreshState, this);
+    return refreshState();
+  });
+
+  return {
+    record: {
+      search: searchGroups,
+      load: loadData,
+      add: esHttp.record.post('/group/record'),
+      update: esHttp.record.post('/group/record/:id/_update'),
+      remove: esHttp.record.remove('group', 'record'),
+      fields: {
+        commons: fields.commons
+      },
+      picture: {
+        all: esHttp.get('/group/record/:id?_source=pictures')
+      },
+      comment: esComment.instance('group')
+    }
+  };
 })
 ;
diff --git a/www/plugins/es/templates/user/edit_profile.html b/www/plugins/es/templates/user/edit_profile.html
index 46d77aeaa75d4ac71d83887d18c3319f463558d6..72e4cb4c7edd622b7403d313e2b39eda34101d0f 100644
--- a/www/plugins/es/templates/user/edit_profile.html
+++ b/www/plugins/es/templates/user/edit_profile.html
@@ -112,6 +112,60 @@
                      ng-model-options="{ debounce: 350 }">
             </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">
+
+              <!-- lat -->
+              <div class="col no-padding">
+                <label class="item item-input item-floating-label">
+                  <span class="input-label" translate>PROFILE.LATITUDE</span>
+                  <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>
+                </label>
+              </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">
+                  <span class="input-label" translate>PROFILE.LONGITUDE</span>
+                  <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>
+                </label>
+              </div>
+
+              <div class="col no-padding">
+                <div class="item no-padding text-center">
+                  <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()">
+                  </a>
+
+                  <a class="button button-stable button-small-padding icon ion-pinpoint"
+                     title="{{'PROFILE.BTN_GEOLOC_ME'|translate}}"
+                     ng-click="localizeMe()">
+                  </a>
+                </div>
+              </div>
+            </div>
+
             <!-- social networks -->
             <ng-include src="'plugins/es/templates/common/edit_socials.html'"></ng-include>
 
diff --git a/www/plugins/map/css/style.css b/www/plugins/map/css/style.css
new file mode 100644
index 0000000000000000000000000000000000000000..1c02c1383769f8a5b38e1a0942580e0f5e8e966b
--- /dev/null
+++ b/www/plugins/map/css/style.css
@@ -0,0 +1,60 @@
+.leaflet-top  {
+  top: 44px !important;
+}
+
+.legend {
+  font: 14px/16px Arial, Helvetica, sans-serif;
+  background: rgba(255,255,255, 0.9);
+  box-shadow: 0 0 15px rgba(0,0,0,0.2);
+  border-radius: 5px;
+  padding: 6px 8px;
+  width: 180px;
+  line-height: 18px;
+  color: #555;
+}
+
+.legend .outline {
+  border: 0;
+}
+.legend i {
+  width: 16px;
+  height: 16px;
+  float: left;
+  margin-right: 8px;
+  opacity: 0.7;
+}
+
+
+/* Control: Seach - see */
+/*.leaflet-marker-icon {*/
+  /*color: #fff;*/
+  /*font-size: 16px;*/
+  /*line-height: 16px;*/
+  /*text-align: center;*/
+  /*vertical-align: middle;*/
+  /*box-shadow: 2px 1px 4px rgba(0,0,0,0.3);*/
+  /*border-radius: 8px;*/
+  /*border:1px solid #fff;*/
+/*}*/
+.search-tip {
+  white-space: nowrap;
+}
+.search-tip b {
+  color: #fff;
+}
+.search-tip.member b {
+  background: #38aadd; /* blue */
+}
+.search-tip.wallet b {
+  background: #a3a3a3; /* lightgray */
+}
+.search-tip.group b {
+  background: #71ae26; /* lightgray */
+}
+.search-tip b {
+  display: inline-block;
+  clear: left;
+  float: right;
+  padding: 0;
+  margin-left: 4px;
+}
diff --git a/www/plugins/map/i18n/locale-fr-FR.json b/www/plugins/map/i18n/locale-fr-FR.json
new file mode 100644
index 0000000000000000000000000000000000000000..52c1f83cffe8bb89b7403c92e4523b9ae8c13a92
--- /dev/null
+++ b/www/plugins/map/i18n/locale-fr-FR.json
@@ -0,0 +1,18 @@
+{
+  "MAP": {
+    "WOT": {
+      "BTN_SHOW_IDENTITY": "Consulter la fiche",
+      "LOOKUP": {
+        "BTN_SHOW_MAP": "Carte"
+      },
+      "VIEW": {
+        "TITLE": "Carte des membres",
+        "SEARCH_DOTS": "Rechercher...",
+        "LEGEND": {
+          "MEMBER": "Membre",
+          "WALLET": "Non-membres"
+        }
+      }
+    }
+  }
+}
diff --git a/www/plugins/map/js/plugin.js b/www/plugins/map/js/plugin.js
new file mode 100644
index 0000000000000000000000000000000000000000..0a9b7e8a055d0687ab9aa216b55a03c2eb3feff5
--- /dev/null
+++ b/www/plugins/map/js/plugin.js
@@ -0,0 +1,388 @@
+
+angular.module('cesium.map.plugin', ['cesium.services'])
+
+  .config(function($stateProvider, PluginServiceProvider, csConfig) {
+    'ngInject';
+
+    var enable = csConfig.plugins && csConfig.plugins.es;
+    if (enable) {
+
+      PluginServiceProvider
+
+        // Extension de la vue d'une identité: ajout d'un bouton
+        .extendState('app.wot_lookup', {
+          points: {
+            'filter-buttons': {
+              templateUrl: "plugins/map/templates/wot/lookup_extend.html"
+            }
+          }
+        });
+
+      // [NEW] Ajout d'une nouvelle page #/app/wot/map
+      $stateProvider
+        .state('app.view_wot_map', {
+          url: "/wot/map?lat&lng&zoom",
+          cache: false,
+          views: {
+            'menuContent': {
+              templateUrl: "plugins/map/templates/wot/map.html",
+              controller: 'MapWotViewCtrl'
+            }
+          }
+        });
+    }
+
+    L.AwesomeMarkers.Icon.prototype.options.prefix = 'ion';
+  })
+
+  // [NEW] Manage events from the page #/app/wot/map
+  .controller('MapWotViewCtrl', function($scope, $q, $translate, $state, $filter, $templateCache, $timeout, $ionicHistory, UIUtils, MapData, leafletData) {
+    'ngInject';
+
+    $scope.loading = true;
+
+    var constants = {
+      FRANCE: {
+        lat: 47.35, lng: 5.65, zoom: 6
+      }
+    };
+    constants.DEFAULT_CENTER = constants.FRANCE;
+
+    $scope.map = {
+      center: angular.copy(constants.DEFAULT_CENTER),
+      defaults: {
+        scrollWheelZoom: true
+      },
+      layers: {
+        baselayers: {
+          openStreetMap: {
+            name: 'OpenStreetMap',
+              type: 'xyz',
+              url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
+          }
+        },
+        overlays: {
+          member: {
+            type: 'group',
+            name: '',
+            visible: true
+          },
+          wallet: {
+            type: 'group',
+            name: '',
+            visible: true
+          }
+        }
+      },
+      legend: {}
+    };
+
+    var icons = {
+      member: {
+        type: 'awesomeMarker',
+        icon: 'person',
+        markerColor: 'blue'
+      },
+      wallet: {
+        type: 'awesomeMarker',
+        icon: 'key',
+        markerColor: 'lightgray'
+      },
+      group: {
+        type: 'awesomeMarker',
+        icon: 'person-stalker',
+        markerColor: 'green'
+      },
+      registry: {
+        type: 'awesomeMarker',
+        icon: 'person-stalker', // TODO
+        markerColor: 'green' // TODO
+      }
+    };
+
+    var markersLayer = L.layerGroup({
+      visible: false
+    });
+
+    //$scope.$on()
+    //$scope.map.controls.custom.push(searchControl);
+
+    // [NEW] When opening the view
+    $scope.$on('$ionicView.enter', function(e, state) {
+
+      if ($scope.loading) {
+        console.log("[map] Opening the view... (first time)");
+
+        // remember state, to be able to refresh location
+        $scope.stateName = state && state.stateName;
+        $scope.stateParams = angular.copy(state && state.stateParams||{});
+
+        var center = angular.copy(constants.DEFAULT_CENTER);
+        if (state.stateParams) {
+          if (state.stateParams && state.stateParams.lat) {
+            center.lat = parseFloat(state.stateParams.lat);
+          }
+          if (state.stateParams && state.stateParams.lng) {
+            center.lng = parseFloat(state.stateParams.lng);
+          }
+          if (state.stateParams && state.stateParams.zoom) {
+            center.zoom = parseFloat(state.stateParams.zoom);
+          }
+        }
+
+        $scope.load(center);
+      }
+      else {
+        console.log("[map] Opening the view... NOT the first time !");
+      }
+    });
+
+    $scope.load = function(center) {
+      center = center||constants.DEFAULT_CENTER;
+
+      $scope.loading = true;
+
+      // removeIf(no-device)
+      UIUtils.loading.show();
+      // endRemoveIf(no-device)
+
+      $q.all([
+        $translate(['MAP.WOT.VIEW.LEGEND.MEMBER', 'MAP.WOT.VIEW.LEGEND.WALLET', 'MAP.WOT.VIEW.SEARCH_DOTS', 'COMMON.SEARCH_NO_RESULT']),
+        MapData.load()
+      ])
+      .then(function(res) {
+        var translations = res[0];
+        res = res[1];
+        if (!res || !res.length) return;
+
+        var overlaysNames = {
+          member: translations['MAP.WOT.VIEW.LEGEND.MEMBER'],
+          wallet: translations['MAP.WOT.VIEW.LEGEND.WALLET']
+        };
+
+        $scope.map.layers.overlays.member.name=overlaysNames.member;
+        $scope.map.layers.overlays.wallet.name=overlaysNames.wallet;
+
+        var formatPubkey = $filter('formatPubkey');
+        var markerTemplate = $templateCache.get('plugins/map/templates/wot/popup_marker.html');
+
+        // Sort with member first
+        res = _.sortBy(res, function(hit) {
+          var score = 0;
+          score += (!hit.uid) ? 100 : 0;
+          return -score;
+        });
+
+        var markers = res.reduce(function(res, hit) {
+          var type = hit.uid ? 'member' : 'wallet';
+          var shortPubkey = formatPubkey(hit.issuer);
+          var marker = {
+            layer: type,
+            icon: icons[type],
+            title: hit.title + ' | ' + shortPubkey,
+            lat: hit.geoPoint.lat,
+            lng: hit.geoPoint.lon,
+            getMessageScope: function() {
+              var scope = $scope.$new();
+              scope.hit = hit;
+              return scope;
+            },
+            focus: false,
+            message: markerTemplate
+          };
+          res[hit.issuer] = marker;
+
+          // Create a search marker (will be hide)
+          var searchText = hit.title + ((hit.uid && hit.uid != hit.title) ? (' | ' + hit.uid) : '') + ' | ' + shortPubkey;
+          var searchMarker = angular.merge({
+              type: type,
+              opacity: 0,
+              icon: L.divIcon({
+                className: type + ' ng-hide',
+                iconSize: L.point(0, 0)
+              })
+            }, {title: searchText, shortPubkey: shortPubkey, uid: hit.uid, name: hit.title});
+          markersLayer.addLayer(new L.Marker({
+              lat: hit.geoPoint.lat,
+              lng: hit.geoPoint.lon
+            },
+            searchMarker));
+          return res;
+        }, {});
+
+        $scope.map.markers = markers;
+        angular.merge($scope.map.center, center);
+
+
+        $scope.loading = false;
+        UIUtils.loading.hide();
+        leafletData.getMap().then(function(map) {
+
+          // Control: search
+          L.control.search({
+            layer: markersLayer,
+            initial: false,
+            marker: false,
+            propertyName: 'title',
+            position: 'topleft',
+            zoom: 13,
+            buildTip: function(text, val) {
+              var marker = val.layer.options;
+              var title = marker.name != marker.uid ? marker.name +' ' : '';
+              if (marker.type == 'member') {
+                return '<a href="#" class="'+marker.type+'">'+title+'<span class="positive"><i class="icon ion-person"></i> '+marker.uid+'</span></a>';
+              }
+              else {
+                return '<a href="#" class="'+marker.type+'">'+title+'<span class="gray"><i class="icon ion-key"></i> '+marker.shortPubkey+'</span></a>';
+              }
+
+              return '<a href="#" class="'+marker.type+'">'+title+'</a>';
+            },
+            textPlaceholder: translations['MAP.WOT.VIEW.SEARCH_DOTS'],
+            textErr: translations['COMMON.SEARCH_NO_RESULT'],
+            markerLocation: true
+          }).addTo(map);
+
+          L.control.search({
+            url: 'search.php?q={s}',
+            jsonpParam:'callback',
+            position: 'topright',
+            hideMarkerOnCollapse: true,
+            marker: {
+              icon: new L.Icon({iconUrl:'data/custom-icon.png', iconSize: [20,20]}),
+              circle: {
+                radius: 20,
+                color: '#0a0',
+                opacity: 1
+              }
+            }
+          }).addTo(map);
+
+          map.invalidateSize();
+          map._resetView(map.getCenter(), map.getZoom(), true);
+        });
+        /*
+        leafletData.getMap().then(function(map) {
+          // Show map
+          $scope.loading = false;
+          $timeout(function() {
+            map.invalidateSize();
+            UIUtils.loading.hide();
+          }, 300);
+        });*/
+
+      });
+    };
+
+    $scope.updateLocation = function() {
+      $ionicHistory.nextViewOptions({
+        disableAnimate: true,
+        disableBack: true,
+        historyRoot: true
+      });
+
+      $scope.stateParams = $scope.stateParams || {};
+      $scope.stateParams.lat = ($scope.map.center.lat != constants.DEFAULT_CENTER.lat) ? $scope.map.center.lat : undefined;
+      $scope.stateParams.lng = ($scope.map.center.lng != constants.DEFAULT_CENTER.lng) ? $scope.map.center.lng : undefined;
+      $scope.stateParams.zoom = ($scope.map.center.zoom != constants.DEFAULT_CENTER.zoom) ? $scope.map.center.zoom : undefined;
+
+      $state.go($scope.stateName, $scope.stateParams, {
+        reload: false,
+        inherit: true,
+        notify: false}
+      );
+    };
+
+    $scope.centerMe = function() {
+      $scope.map.center.autoDiscover = true;
+      /*esGeo.point.current()
+        .then(function() {
+
+        })*/
+    };
+
+    $scope.onMapCenterChanged = function() {
+      if (!$scope.loading) {
+        $timeout($scope.updateLocation, 500);
+      }
+    };
+    $scope.$watch('map.center', $scope.onMapCenterChanged, true);
+
+  })
+
+  // [NEW] Manage events from the page #/app/wot/map
+  .factory('MapData', function(csHttp, esHttp, csWot) {
+    'ngInject';
+
+    var
+      that = this,
+      constants = {
+        DEFAULT_LOAD_SIZE: 1000
+      },
+      fields = {
+        profile: ["issuer", "title", "description", "geoPoint"]
+      };
+
+    that.raw = {
+      profile: {
+        postSearch: esHttp.post('/user/profile/_search')
+      }
+    };
+
+    function createFilterQuery(options) {
+      var query = {
+        bool: {
+          must: [
+            {exists: {field: "geoPoint"}}
+          ]
+        }
+      };
+
+      return query;
+    }
+
+    function load(options) {
+      options = options || {};
+      options.from = options.from || 0;
+      options.size = options.size || constants.DEFAULT_LOAD_SIZE;
+
+      var request = {
+        query: createFilterQuery(options),
+        from: options.from,
+        size: options.size,
+        _source: fields.profile
+      };
+
+      return that.raw.profile.postSearch(request)
+        .then(function(res) {
+          if (!res.hits || !res.hits.total) return [];
+
+          var commaRegexp = new RegExp('[,]');
+
+          res = res.hits.hits.reduce(function(res, hit) {
+            var item = hit._source;
+
+            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.lon && typeof item.geoPoint.lon === 'string') {
+              item.geoPoint.lon = parseFloat(item.geoPoint.lon.replace(commaRegexp, '.'));
+            }
+
+            return res.concat(item);
+          }, []);
+
+          return csWot.extendAll(res, 'issuer');
+        });
+    }
+
+    return {
+      load: load
+    };
+
+  });
+
+
diff --git a/www/plugins/map/templates/wot/lookup_extend.html b/www/plugins/map/templates/wot/lookup_extend.html
new file mode 100644
index 0000000000000000000000000000000000000000..e6dcccc743a53431f6a9e4c957bef5674ce8324a
--- /dev/null
+++ b/www/plugins/map/templates/wot/lookup_extend.html
@@ -0,0 +1,5 @@
+<a class="button button-text button-small ink hidden-sm hidden-xs"
+        ui-sref="app.view_wot_map">
+  <i class="icon ion-ios-location"></i>
+  {{'MAP.WOT.LOOKUP.BTN_SHOW_MAP' | translate}}
+</a>
diff --git a/www/plugins/map/templates/wot/map.html b/www/plugins/map/templates/wot/map.html
new file mode 100644
index 0000000000000000000000000000000000000000..4c1903d1f3c5393a8be4f40f86a9805c4325db91
--- /dev/null
+++ b/www/plugins/map/templates/wot/map.html
@@ -0,0 +1,7 @@
+
+<leaflet id="map-wot"
+         center="map.center"
+         markers="map.markers"
+         layers="map.layers"
+         controls="map.controls">
+</leaflet>
diff --git a/www/plugins/map/templates/wot/popup_marker.html b/www/plugins/map/templates/wot/popup_marker.html
new file mode 100644
index 0000000000000000000000000000000000000000..7e31eecb1861814fa30689a0b2f244ba0867f726
--- /dev/null
+++ b/www/plugins/map/templates/wot/popup_marker.html
@@ -0,0 +1,18 @@
+<div class="item no-border no-padding" ng-class="::{'item-avatar': hit.avatar}">
+  <img ng-if="::hit.avatar" class="avatar" ng-src="{{::hit.avatar.src}}">
+  <div class="item-content item-avatar-left-padding padding-top">
+    <h2 class="dark"><a ui-sref="app.wot_identity({pubkey: hit.issuer, uid: hit.uid})">{{::hit.title}}</a></h2>
+    <h4>
+      <span ng-if="::hit.uid" class="positive">
+        <i class="icon ion-person"></i>
+        {{::hit.uid}}
+      </span>
+      <span class="assertive" ng-if="!hit.uid">
+        {{::'WOT.NOT_MEMBER_PARENTHESIS'|translate}}
+      </span>
+    </h4>
+    <h4>
+      <span class="gray" title="{{::hit.issuer}}"><i class="icon ion-key"></i> {{::hit.issuer|formatPubkey}}</span>
+    </h4>
+  </div>
+</div>
diff --git a/www/plugins/rml9/css/style.css b/www/plugins/rml9/css/style.css
index 24f86b1eea8bd636d35453a450e889f96ad7d018..6e8d8bc7fb8eeed4d9f263ec95ef7fd6b5bd1f1f 100644
--- a/www/plugins/rml9/css/style.css
+++ b/www/plugins/rml9/css/style.css
@@ -1,3 +1,3 @@
-.pane.has-header {
+.leaflet-top  {
   top: 44px !important;
 }
diff --git a/www/plugins/rml9/templates/07-view.html b/www/plugins/rml9/templates/07-view.html
index 3842a3198e3580efdb7eb34e4970d27fce47a959..6143da3579cb924447658a1e1d82479300eb30ff 100644
--- a/www/plugins/rml9/templates/07-view.html
+++ b/www/plugins/rml9/templates/07-view.html
@@ -1 +1 @@
-<leaflet class="has-header" id="map-geojson" center="map.center" geojson="map.geojson"></leaflet>
+<leaflet id="map-geojson" center="map.center" geojson="map.geojson"></leaflet>
diff --git a/www/templates/wot/lookup_form.html b/www/templates/wot/lookup_form.html
index 2ba711744d4df50fedec3a37b1997ff8e19c4c71..40329d6b131c4a70d3798b9e820154e60a4bb1d6 100644
--- a/www/templates/wot/lookup_form.html
+++ b/www/templates/wot/lookup_form.html
@@ -64,12 +64,15 @@
       </a>
       &nbsp;
       <a ng-if="enableFilter"
-         class="button button-text button-small"
+         class="button button-text button-small ink"
          ng-class="{'button-text-positive': search.type=='pending'}"
          ng-click="doGetPending()" class="badge-balanced">
         <i class="icon ion-clock"></i>
         {{'WOT.LOOKUP.BTN_PENDING' | translate}}
       </a>
+
+      <!-- Allow extension here -->
+      <cs-extension-point name="filter-buttons"></cs-extension-point>
       &nbsp;
       <button class="button button-small button-stable ink"
               ng-click="doSearch()">