diff --git a/config/pmd-suppressions.properties b/config/pmd-suppressions.properties
index a22f2263faba8628c510bb2b0f9cde2bb32e2e57..8f5ce8b2fb93589a2736f1b5043d69127e0ce32d 100644
--- a/config/pmd-suppressions.properties
+++ b/config/pmd-suppressions.properties
@@ -25,6 +25,7 @@ fr.agrometinfo.www.shared.dto.SummaryDTOBeanJsonDeserializerImpl=UnnecessaryImpo
 fr.agrometinfo.www.shared.dto.SummaryDTOBeanJsonSerializerImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.SummaryDTO_MapperImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.service.ApplicationServiceFactory=UnnecessaryImport
+fr.agrometinfo.www.shared.service.GeometryServiceFactory=UnnecessaryImport
 fr.agrometinfo.www.shared.service.IndicatorServiceFactory=UnnecessaryImport
 fr.agrometinfo.www.shared.service.SurveyFormServiceFactory=UnnecessaryImport
 fr.agrometinfo.www.shared.service.SurveyQuestionDTO_MapperImpl=UnnecessaryImport
diff --git a/pom.xml b/pom.xml
index b522238141af8ee204601d46c878f2b2ba0a1e6a..602cb75c0dd180966575f035f8ae3423752bac98 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>fr.agrometinfo</groupId>
   <artifactId>www</artifactId>
-  <version>2.0.1</version>
+  <version>2.0.2-SNAPSHOT</version>
   <packaging>pom</packaging>
   <name>AgroMetInfo web app</name>
   <description>Web application for AgroMetInfo.</description>
diff --git a/www-client/pom.xml b/www-client/pom.xml
index 2a510777411ea54976ec1d5a678edd6cd636cc83..9788bc5496809ec36260502d1ba4e97cc2166c94 100644
--- a/www-client/pom.xml
+++ b/www-client/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>fr.agrometinfo</groupId>
         <artifactId>www</artifactId>
-        <version>2.0.1</version>
+        <version>2.0.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>www-client</artifactId>
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/MapMessages.java b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/MapMessages.java
new file mode 100644
index 0000000000000000000000000000000000000000..31e435a28b1427405ffc18183a8294353587298e
--- /dev/null
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/MapMessages.java
@@ -0,0 +1,25 @@
+package fr.agrometinfo.www.client.i18n;
+
+import java.util.Date;
+
+import com.google.gwt.i18n.client.Messages;
+
+/**
+ * Internationalization messages.
+ *
+ * @author Olivier Maury
+ */
+public interface MapMessages extends Messages {
+
+    /**
+     * @param indicator indicator name
+     * @param value     indicator value
+     * @param unit      unit
+     * @param pra       Petite région agricole
+     * @param startDate start date of period
+     * @param endDate   date of last computation
+     * @return translation
+     */
+    @DefaultMessage("{0}: {1,number}&nbsp;{2}<br/>PRA: {3}<br/>Period: {4,date,medium} − {5,date,medium}")
+    String popupContent(String indicator, Double value, String unit, String pra, Date startDate, Date endDate);
+}
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LayoutPresenter.java b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LayoutPresenter.java
index bde9471faf24eb0f18a51e5b4b91b8f1bd91435f..8f82944a02e1c59c488589a27a56bd429654c179 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LayoutPresenter.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LayoutPresenter.java
@@ -101,5 +101,6 @@ public final class LayoutPresenter implements Presenter {
 
         rightPanelPresenter.setLayoutView(view);
         rightPanelPresenter.start();
+        GWT.log("LayoutPresenter.start() end");
     }
 }
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/MapPresenter.java b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/MapPresenter.java
index 58bb65c008598b41fadde55a36460da63ea4d32a..46bb0c246e482c45f9e7746b686f9290f35cba9e 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/MapPresenter.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/MapPresenter.java
@@ -2,6 +2,7 @@ package fr.agrometinfo.www.client.presenter;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 import org.dominokit.domino.ui.utils.DominoElement;
 import org.dominokit.rest.JsRestfulRequestFactory;
@@ -23,6 +24,7 @@ import fr.agrometinfo.www.client.view.MapView;
 import fr.agrometinfo.www.shared.dto.ChoiceDTO;
 import fr.agrometinfo.www.shared.dto.FeatureLevel;
 import fr.agrometinfo.www.shared.dto.IndicatorDTO;
+import fr.agrometinfo.www.shared.service.GeometryServiceFactory;
 import fr.agrometinfo.www.shared.service.IndicatorService;
 import fr.agrometinfo.www.shared.service.IndicatorServiceFactory;
 
@@ -47,6 +49,11 @@ public final class MapPresenter implements Presenter {
          */
         void setGeoJson(String geoJSON, IndicatorDTO indicator);
 
+        /**
+         * @param values PRA code ⮕ PRA name.
+         */
+        void setPraNames(Map<String, String> values);
+
         /**
          * @param lines the title splitted in lines
          */
@@ -150,6 +157,9 @@ public final class MapPresenter implements Presenter {
         IndicatorServiceFactory.INSTANCE.getLastModification()
         .onSuccess(date -> lastModification = MSGS.computedOn(date)).send();
         view = new MapView(container);
+        GeometryServiceFactory.INSTANCE.getPraAsJSON() //
+        .onSuccess(view::setPraNames) //
+        .send();
         view.setPresenter(this);
         view.init();
     }
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/ui/map/CanvasWidget.java b/www-client/src/main/java/fr/agrometinfo/www/client/ui/map/CanvasWidget.java
index e56dd761911681f293685693ac5e8fdadba943a4..ede6f9bc65510cfe950b452dbc84d0b844bb029e 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/ui/map/CanvasWidget.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/ui/map/CanvasWidget.java
@@ -3,6 +3,7 @@ package fr.agrometinfo.www.client.ui.map;
 import com.google.gwt.dom.client.CanvasElement;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NodeList;
+import com.google.gwt.user.client.Timer;
 
 import ol.Map;
 
@@ -16,6 +17,10 @@ abstract class CanvasWidget {
      * The same font-family as in style.css.
      */
     protected static final String FONT_FAMILY = "\"Roboto\", Arial, Tahoma, sans-serif";
+    /**
+     * Delay (milliseconds) before drawing.
+     */
+    private static final int DRAWING_TIMER_DELAY = 200;
 
     /**
      * OpenLayers map.
@@ -27,6 +32,11 @@ abstract class CanvasWidget {
      */
     private CanvasElement canvas;
 
+    /**
+     * Timer to handle draw and avoid multiple drawings on map changes.
+     */
+    private Timer drawingTimer;
+
     /**
      * Constructor.
      *
@@ -39,8 +49,17 @@ abstract class CanvasWidget {
         // tiles have finished loading for the current viewport, and all tiles are faded
         // in.
         openLayersMap.on("rendercomplete", e -> {
-            canvas = null;
-            this.draw();
+            if (drawingTimer != null) {
+                drawingTimer.cancel();
+            }
+            drawingTimer = new Timer() {
+                @Override
+                public void run() {
+                    CanvasWidget.this.canvas = null;
+                    CanvasWidget.this.draw();
+                }
+            };
+            drawingTimer.schedule(DRAWING_TIMER_DELAY);
         });
     }
 
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/ui/map/ToolTip.java b/www-client/src/main/java/fr/agrometinfo/www/client/ui/map/ToolTip.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a00393cbb98033d12e7e7f74c00af408eef34ed
--- /dev/null
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/ui/map/ToolTip.java
@@ -0,0 +1,123 @@
+package fr.agrometinfo.www.client.ui.map;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.DOM;
+
+import ol.Coordinate;
+import ol.Extent;
+import ol.Map;
+import ol.Overlay;
+import ol.OverlayOptions;
+import ol.Pixel;
+import ol.Size;
+
+/**
+ * Tooltip widget
+ *
+ * At bottom right of over PRA.
+ *
+ * @see http://openlayers.org/en/latest/examples/popup.html
+ * @author Olivier Maury
+ */
+public final class ToolTip {
+    /**
+     * DIV element for HTML content of tooltip, displayed in the overlay.
+     */
+    private final Element content = DOM.createDiv();
+
+    /**
+     * If tooltip can be displayed.
+     */
+    private boolean enabled = true;
+
+    /**
+     * map where the tooltip will be added.
+     */
+    private final Map olMap;
+
+    /**
+     * Element displayed over the map and attached to a single map location, at
+     * bottom right of PRA.
+     */
+    private final Overlay overlay;
+
+    /**
+     * Constructor.
+     *
+     * @param map the map where the tooltip will be added.
+     */
+    public ToolTip(final Map map) {
+        this.olMap = map;
+        // Create tooltip element in a new popup layer.
+        final OverlayOptions overlayOptions = new OverlayOptions();
+        overlayOptions.setElement(content);
+        overlayOptions.setAutoPan(true);
+        overlayOptions.setId("toolTipOverlay");
+        overlayOptions.setAutoPan(false);
+        overlay = new Overlay(overlayOptions);
+        olMap.addOverlay(overlay);
+    }
+
+    public void disable() {
+        enabled = false;
+    }
+
+    public void enable() {
+        enabled = true;
+    }
+
+    /**
+     * Hide tooltip.
+     */
+    public void hide() {
+        overlay.setPosition(null);
+    }
+
+    /**
+     *
+     * @param message
+     */
+    public void setContent(final String message) {
+        content.setInnerHTML(message);
+    }
+
+    /**
+     *
+     * @param extent if null the popup is hidden
+     */
+    public void setPosition(final Extent extent) {
+        if (enabled) {
+            final Coordinate position = new Coordinate(//
+                    extent.getLowerLeftX() + extent.getWidth(), //
+                    extent.getUpperRightY() - extent.getHeight()//
+                    );
+            final Pixel posPixel = olMap.getPixelFromCoordinate(position);
+            final int posX = posPixel.getX();
+            final int posY = posPixel.getY();
+            final Size mapSize = olMap.getSize();
+            final int width = content.getClientWidth();
+            final int height = content.getClientHeight();
+            final String xClassName;
+            if (posX + width > mapSize.getWidth()) {
+                xClassName = "left";
+                position.setX(extent.getLowerLeftX());
+            } else {
+                xClassName = "right";
+            }
+            final String yClassName;
+            if (posY + height > mapSize.getHeight()) {
+                yClassName = "top";
+                position.setY(extent.getUpperRightY());
+            } else {
+                yClassName = "bottom";
+            }
+            content.setClassName("map-tooltip");
+            content.addClassName(xClassName);
+            content.addClassName(yClassName);
+
+            overlay.setPosition(position);
+        } else {
+            hide();
+        }
+    }
+}
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/util/DateUtils.java b/www-client/src/main/java/fr/agrometinfo/www/client/util/DateUtils.java
index 459d8b8c0bac957e69634910adf2ec95e36a22db..f49f56bd09c6b6f6513255d61f36302d9e7a5de9 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/util/DateUtils.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/util/DateUtils.java
@@ -10,6 +10,11 @@ import java.util.Date;
  * @author Olivier Maury
  */
 public interface DateUtils {
+    /**
+     * ISO-8601 date format.
+     */
+    String ISO_8601_DATE_FORMAT = "yyyy-MM-dd";
+
     /**
      * @return the current year.
      */
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/MapView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/MapView.java
index 5c82b53e2eb6cc7f26ff28bbb8d5f58a94fde68c..9bb1d36561af407ef2fbd809e1d49e3b6ac6f267 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/MapView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/MapView.java
@@ -1,6 +1,8 @@
 package fr.agrometinfo.www.client.view;
 
 import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
@@ -12,6 +14,7 @@ import org.jboss.elemento.HtmlContentBuilder;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.core.client.JsonUtils;
+import com.google.gwt.i18n.client.DateTimeFormat;
 
 import elemental2.dom.HTMLDivElement;
 import elemental2.dom.HTMLElement;
@@ -20,16 +23,21 @@ import fr.agrometinfo.www.client.event.FeatureSelectEvent;
 import fr.agrometinfo.www.client.event.FeatureSelectHandler;
 import fr.agrometinfo.www.client.event.MapClickEvent;
 import fr.agrometinfo.www.client.i18n.MapConstants;
+import fr.agrometinfo.www.client.i18n.MapMessages;
 import fr.agrometinfo.www.client.presenter.MapPresenter;
 import fr.agrometinfo.www.client.ui.map.CanvasAttributions;
 import fr.agrometinfo.www.client.ui.map.CanvasTitle;
 import fr.agrometinfo.www.client.ui.map.ControlSuppliers;
 import fr.agrometinfo.www.client.ui.map.TileSuppliers;
+import fr.agrometinfo.www.client.ui.map.ToolTip;
 import fr.agrometinfo.www.client.util.ApplicationUtils;
+import fr.agrometinfo.www.client.util.DateUtils;
 import fr.agrometinfo.www.client.util.color.ColorInterval;
 import fr.agrometinfo.www.client.util.color.ColorSequenceManager;
 import fr.agrometinfo.www.shared.dto.FeatureLevel;
+import fr.agrometinfo.www.shared.dto.FeatureProperty;
 import fr.agrometinfo.www.shared.dto.IndicatorDTO;
+import jsinterop.base.Any;
 import ol.Collection;
 import ol.Coordinate;
 import ol.Extent;
@@ -40,6 +48,7 @@ import ol.OLFactory;
 import ol.View;
 import ol.ViewOptions;
 import ol.color.Color;
+import ol.event.EventListener;
 import ol.events.condition.Condition;
 import ol.format.GeoJson;
 import ol.format.GeoJsonFeatureOptions;
@@ -97,6 +106,11 @@ public final class MapView extends HtmlContentBuilder<HTMLElement> implements Fe
      */
     public static final String MAP_CONTAINER_ID = "mapContainer";
 
+    /**
+     * I18N messages.
+     */
+    private static final MapMessages MSGS = GWT.create(MapMessages.class);
+
     /**
      * z-index for overlays.
      */
@@ -191,21 +205,75 @@ public final class MapView extends HtmlContentBuilder<HTMLElement> implements Fe
         return vectorLayer;
     }
 
-    private static double getValue(final Feature f) {
-        return Double.parseDouble(f.getProperties().getAsAny("value").asString());
+    /**
+     * @param feature feature
+     * @param property feature property
+     * @return string representation of a feature property
+     */
+    private static String getProperty(final Feature feature, final FeatureProperty property) {
+        final String propertyName = property.toCamelCase();
+        if (feature.getProperties().has(propertyName)) {
+            return feature.getProperties().get(propertyName).toString();
+        }
+        return "";
     }
 
-    private static Feature[] parseGeoJson(final String geoJson) {
-        GWT.log("MapView.parseGeoJson()");
-        final JavaScriptObject dataGeoJson = JsonUtils.unsafeEval(geoJson);
-        // create Feature and after geojson layer (Vector layer)
-        final GeoJson geoJsonFormat = OLFactory.createGeoJSON();
+    /**
+     * @param feature  feature
+     * @param property feature property
+     * @return string representation of a feature property
+     */
+    private static String getProperty(final org.geojson.Feature feature, final FeatureProperty property) {
+        return feature.getProperties().getOrDefault(property.toCamelCase(), "");
+    }
 
-        final ProjectionOptions projOpt = new ProjectionOptions();
-        projOpt.setCode(EPSG_3857);
-        final GeoJsonFeatureOptions geoJsonFeatureOptions = new GeoJsonFeatureOptions();
-        geoJsonFeatureOptions.setFeatureProjection(new Projection(projOpt));
-        return geoJsonFormat.readFeatures(dataGeoJson, geoJsonFeatureOptions);
+    /**
+     * @param feature  feature
+     * @param property feature property
+     * @return date representation of a feature property
+     */
+    private static Date getPropertyAsDate(final Feature feature, final FeatureProperty property) {
+        final String str = getProperty(feature, property);
+        if (str == null) {
+            return null;
+        }
+        final DateTimeFormat dtf = DateTimeFormat.getFormat(DateUtils.ISO_8601_DATE_FORMAT);
+        return dtf.parse(str);
+    }
+
+    /**
+     * @param feature  feature
+     * @param property feature property
+     * @return date representation of a feature property
+     */
+    private static Date getPropertyAsDate(final org.geojson.Feature feature, final FeatureProperty property) {
+        final String str = getProperty(feature, property);
+        if (str == null) {
+            return null;
+        }
+        final DateTimeFormat dtf = DateTimeFormat.getFormat(DateUtils.ISO_8601_DATE_FORMAT);
+        return dtf.parse(str);
+    }
+
+    /**
+     * @param feature  feature
+     * @param property feature property
+     * @return double representation of a feature property
+     */
+    private static Double getPropertyAsDouble(final Feature feature, final FeatureProperty property) {
+        final String str = getProperty(feature, property);
+        if (str == null) {
+            return null;
+        }
+        return Double.valueOf(str);
+    }
+
+    private static double getValue(final Feature f) {
+        final Any value = f.getProperties().getAsAny(FeatureProperty.VALUE.toCamelCase());
+        if (value == null || value.asString() == null) {
+            return 0d;
+        }
+        return Double.parseDouble(value.asString());
     }
 
     /**
@@ -227,6 +295,21 @@ public final class MapView extends HtmlContentBuilder<HTMLElement> implements Fe
      */
     private List<ColorInterval> colorIntervals;
 
+    /**
+     * Name of selected indicator.
+     */
+    private String indicatorName;
+
+    /**
+     * Start date of selected period.
+     */
+    private Date periodStartDate;
+
+    /**
+     * Unit of selected indicator.
+     */
+    private String indicatorUnit;
+
     /**
      * map.
      */
@@ -237,6 +320,11 @@ public final class MapView extends HtmlContentBuilder<HTMLElement> implements Fe
      */
     private MapPresenter presenter;
 
+    /**
+     * Tooltip displayed over PRA.
+     */
+    private ToolTip tooltip;
+
     /**
      * Layer with cells.
      */
@@ -252,6 +340,11 @@ public final class MapView extends HtmlContentBuilder<HTMLElement> implements Fe
      */
     private String selectedFeatureId = null;
 
+    /**
+     * PRA code ⮕ PRA name.
+     */
+    private final java.util.Map<String, String> praNames = new HashMap<>();
+
     /**
      * Constructor.
      *
@@ -278,6 +371,43 @@ public final class MapView extends HtmlContentBuilder<HTMLElement> implements Fe
         map.on("singleclick", event -> App.getEventBus().fireEvent(MapClickEvent.of()));
     }
 
+    /**
+     * Mouse move interaction to display values in a popup.
+     */
+    private void addMouseMoveInteraction() {
+        final SelectOptions selectOptions = new SelectOptions();
+        selectOptions.setCondition(Condition.getPointerMove());
+        final Select featureSelect = new Select(selectOptions);
+        map.addInteraction(featureSelect);
+
+        tooltip = new ToolTip(map);
+
+        final EventListener<Select.Event> selectListener = event -> {
+            if (featureSelect.getFeatures() != null //
+                    && !featureSelect.getFeatures().isEmpty() //
+                    && featureSelect.getFeatures().item(0) != null) {
+
+                final Feature feature = featureSelect.getFeatures().item(0);
+                final Date date = getPropertyAsDate(feature, FeatureProperty.DATE);
+                final Double value = getPropertyAsDouble(feature, FeatureProperty.VALUE);
+                final String praName = praNames.getOrDefault(feature.getId(), feature.getId() + "/" + praNames.size());
+                final String content = MSGS.popupContent(indicatorName, value, indicatorUnit, praName, periodStartDate,
+                        date);
+
+                final Extent extent = feature.getGeometry().getExtent();
+                tooltip.setPosition(extent);
+                tooltip.setContent(content);
+            } else {
+                tooltip.hide();
+            }
+
+        };
+
+        featureSelect.on("select", selectListener);
+        map.on("movestart", e -> tooltip.disable());
+        map.on("moveend", e -> tooltip.enable());
+    }
+
     private Feature[] colorizeFeatures(final Feature[] features) {
         GWT.log("MapView.colorizeFeatures()");
         for (final Feature f : features) {
@@ -302,6 +432,7 @@ public final class MapView extends HtmlContentBuilder<HTMLElement> implements Fe
      * @return style
      */
     private Style[] createCellStyleForSelection(final Feature feature) {
+        GWT.log("MapView.createCellStyleForSelection()");
         final int strokeWidth = 3;
         final Double value = getValue(feature);
         final String color = "#" + ColorSequenceManager.getColorForValue(colorIntervals, value);
@@ -368,6 +499,7 @@ public final class MapView extends HtmlContentBuilder<HTMLElement> implements Fe
         // add some interactions
         removeContextMenuRightClick();
         addClickInteractions();
+        addMouseMoveInteraction();
 
         App.getEventBus().addHandler(FeatureSelectEvent.TYPE, this);
     }
@@ -390,6 +522,33 @@ public final class MapView extends HtmlContentBuilder<HTMLElement> implements Fe
         }
     }
 
+    private Feature[] parseGeoJson(final String geoJson) {
+        GWT.log("MapView.parseGeoJson()");
+        final JavaScriptObject dataGeoJson = JsonUtils.unsafeEval(geoJson);
+        // create Feature and after geojson layer (Vector layer)
+        final GeoJson geoJsonFormat = OLFactory.createGeoJSON();
+
+        final ProjectionOptions projOpt = new ProjectionOptions();
+        projOpt.setCode(EPSG_3857);
+        final GeoJsonFeatureOptions geoJsonFeatureOptions = new GeoJsonFeatureOptions();
+        geoJsonFeatureOptions.setFeatureProjection(new Projection(projOpt));
+        final Feature[] allFeatures = geoJsonFormat.readFeatures(dataGeoJson, geoJsonFeatureOptions);
+        final Feature propertiesFeature = allFeatures[0];
+        if (propertiesFeature.getId() == null) {
+            indicatorName = getProperty(propertiesFeature, FeatureProperty.INDICATOR_NAME);
+            indicatorUnit = getProperty(propertiesFeature, FeatureProperty.INDICATOR_UNIT);
+            periodStartDate = getPropertyAsDate(propertiesFeature, FeatureProperty.PERIOD_FIRST_DAY);
+
+            final Feature[] filtered = new Feature[allFeatures.length - 1];
+            for (int i = 1; i < allFeatures.length; i++) {
+                filtered[i - 1] = allFeatures[i];
+            }
+            return filtered;
+        } else {
+            return allFeatures;
+        }
+    }
+
     /**
      * Remove context menu on right click on the map.
      */
@@ -399,12 +558,19 @@ public final class MapView extends HtmlContentBuilder<HTMLElement> implements Fe
 
     @Override
     public void setFeatureCollection(final FeatureCollection collection) {
+        GWT.log("MapView.setFeatureCollection()");
         final List<org.geojson.Feature> list = collection.getFeatures();
+        final org.geojson.Feature fakeFeature = list.remove(0);
+        indicatorName = getProperty(fakeFeature, FeatureProperty.INDICATOR_NAME);
+        indicatorUnit = getProperty(fakeFeature, FeatureProperty.INDICATOR_UNIT);
+        periodStartDate = getPropertyAsDate(fakeFeature, FeatureProperty.PERIOD_FIRST_DAY);
         final Feature[] features = list.toArray(new Feature[list.size()]);
         setFeatures(features);
     }
+
     private void setFeatures(final Feature[] features) {
         GWT.log("MapView.setFeatures()");
+        tooltip.hide();
         if (vectorLayer != null) {
             map.removeLayer(vectorLayer);
         }
@@ -451,13 +617,22 @@ public final class MapView extends HtmlContentBuilder<HTMLElement> implements Fe
         setFeatures(features);
     }
 
+    @Override
+    public void setPraNames(final java.util.Map<String, String> values) {
+        GWT.log("MapView.setPraNames() " + values.size());
+        praNames.clear();
+        praNames.putAll(values);
+    }
+
     @Override
     public void setPresenter(final MapPresenter p) {
+        GWT.log("MapView.setPresenter()");
         presenter = p;
     }
 
     @Override
     public void setTitle(final List<String> lines) {
+        GWT.log("MapView.setTitle()");
         canvasTitle.setTitle(lines, colorIntervals);
     }
 
diff --git a/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/MapMessages_fr.properties b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/MapMessages_fr.properties
new file mode 100644
index 0000000000000000000000000000000000000000..0fb4df9ee9fbc886b6226dc31a40f8a04e1f5e5a
--- /dev/null
+++ b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/MapMessages_fr.properties
@@ -0,0 +1,2 @@
+# Ce fichier est encodé en UTF-8.
+popupContent={0}\u00a0: {1,number}\u00a0{2}<br/>PRA\u00a0: {3}<br/>Période\u00a0: {4,date,medium} − {5,date,medium}
diff --git a/www-client/src/main/resources/fr/agrometinfo/www/client/public/style.css b/www-client/src/main/resources/fr/agrometinfo/www/client/public/style.css
index 7e88ddb8240adcc42c8c27b8ccd2e0b1b9b38087..6fd7ba6f463695871a09ac3708b9db1c7dea99bb 100644
--- a/www-client/src/main/resources/fr/agrometinfo/www/client/public/style.css
+++ b/www-client/src/main/resources/fr/agrometinfo/www/client/public/style.css
@@ -306,9 +306,43 @@ body > .modal-backdrop {
     top: auto;
     bottom: 2em;
 }
+
+.map-tooltip {
+    background-color: rgba(255, 255, 255, 0.7);
+    border: 1px solid #818181;
+    color: #1b1b1b;
+    font-family: "Roboto",sans-serif;
+    height: var(--map-tooltip-height);
+    padding: 0.5em;
+    width: var(--map-tooltip-width);
+}
+.map-tooltip.left {
+	transform: translate(calc(-1 * var(--map-tooltip-width)), 0);
+}
+.map-tooltip.top {
+	transform: translate(0, calc(-1 * var(--map-tooltip-height)));
+}
+.map-tooltip.left.top {
+	transform: translate(calc(-1 * var(--map-tooltip-width)), calc(-1 * var(--map-tooltip-height)));
+}
+.map-tooltip.bottom.left {
+    border-radius: 1em 0 1em 1em;
+}
+.map-tooltip.bottom.right {
+    border-radius: 0 1em 1em 1em;
+}
+.map-tooltip.top.left {
+    border-radius: 1em 1em 0 1em;
+}
+.map-tooltip.top.right {
+    border-radius: 1em 1em 1em 0;
+}
+
 :root {
     --footer-logo-height: 32px;
     --logo-height: 50px;
+    --map-tooltip-height: 8em;
+    --map-tooltip-width: 22em;
 }
 @media screen and (max-width: 380px) {
     .navbar-brand > .version {
diff --git a/www-server/pom.xml b/www-server/pom.xml
index 3feb066b99e4b934bb9233267a6290b7e413ee82..617c3f10710583d8428ad36eb99a41a5ae8ce709 100644
--- a/www-server/pom.xml
+++ b/www-server/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>fr.agrometinfo</groupId>
     <artifactId>www</artifactId>
-    <version>2.0.1</version>
+    <version>2.0.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>www-server</artifactId>
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/GeometryResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/GeometryResource.java
index e72872240258a149c20cc098437728eb4a1152fa..8332567afe53b5f1d92097e9c81295c944fe1c2e 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/GeometryResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/GeometryResource.java
@@ -1,36 +1,35 @@
 package fr.agrometinfo.www.server.rs;
 
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 import org.geojson.Feature;
 import org.geojson.FeatureCollection;
 
 import fr.agrometinfo.www.server.dao.PraDao;
 import fr.agrometinfo.www.server.model.Pra;
+import fr.agrometinfo.www.server.service.CacheService;
 import fr.agrometinfo.www.server.util.GeometryUtils;
+import fr.agrometinfo.www.shared.service.GeometryService;
 import jakarta.enterprise.context.RequestScoped;
 import jakarta.inject.Inject;
 import jakarta.ws.rs.GET;
 import jakarta.ws.rs.Path;
 import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Request;
 
 /**
  * Resource to expose geometries.
  *
  * @author Olivier Maury
  */
-@Path(GeometryResource.PATH)
+@Path(GeometryService.PATH)
 @RequestScoped
-public class GeometryResource {
-    /**
-     * Service base path.
-     */
-    public static final String PATH = "geometry";
-
-    /**
-     * Path for PRA geometries.
-     */
-    public static final String PATH_PRA = "pra";
+public class GeometryResource implements GeometryService {
 
     private static Feature toFeature(final Pra pra) {
         final Feature feature = GeometryUtils.toFeature(pra);
@@ -39,19 +38,37 @@ public class GeometryResource {
         return feature;
     }
 
+    /**
+     * Cache service for server-side and browser-side.
+     */
+    @Inject
+    private CacheService cacheService;
+
     /**
      * DAO for {@link Pra}.
      */
     @Inject
     private PraDao praDao;
 
+    /**
+     * JAX-RS request.
+     */
+    @Context
+    private Request request;
+
+    /**
+     * HTTP headers for response.
+     */
+    @Context
+    private HttpHeaders httpHeaders;
+
     /**
      * @return Geometry of PRA with name and code
      */
     @GET
-    @Path(PATH_PRA)
+    @Path(GeometryService.PATH_PRA)
     @Produces("application/geo+json")
-    public FeatureCollection getPra() {
+    public FeatureCollection getPraAsGeoJSON() {
         final List<Pra> values = praDao.findAll();
         final FeatureCollection collection = new FeatureCollection();
         values.stream()//
@@ -59,4 +76,31 @@ public class GeometryResource {
         .forEach(collection::add);
         return collection;
     }
+
+    /**
+     * @return PRA code ⮕ PRA name
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    @GET
+    @Path(GeometryService.PATH_PRA_NAMES)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Map<String, String> getPraAsJSON() {
+        // HTTP cache headers
+        cacheService.setCacheKey(GeometryService.PATH, GeometryService.PATH_PRA);
+        cacheService.setHeaders(httpHeaders);
+        if (!cacheService.needsResponse(request)) {
+            return Map.of();
+        }
+        // cached response
+        if (cacheService.isCached()) {
+            return (Map<String, String>) cacheService.getCache();
+        }
+        //
+        final List<Pra> values = praDao.findAll();
+        final var response = values.stream()//
+                .collect(Collectors.toMap(Pra::getCode, Pra::getName));
+        cacheService.setCache(response);
+        return response;
+    }
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java
index 9c66f37d9c53db2f13092260628272c5fd2011ee..6ca575c9873f2dba0524db738480c19a19c7f2d2 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java
@@ -14,6 +14,7 @@ import java.util.TreeMap;
 
 import org.geojson.Feature;
 import org.geojson.FeatureCollection;
+import org.geojson.MultiPolygon;
 
 import fr.agrometinfo.www.server.dao.IndicatorDao;
 import fr.agrometinfo.www.server.dao.PraDailyValueDao;
@@ -29,6 +30,7 @@ import fr.agrometinfo.www.server.util.LocaleUtils;
 import fr.agrometinfo.www.shared.dto.ChoiceDTO;
 import fr.agrometinfo.www.shared.dto.ErrorResponseDTO;
 import fr.agrometinfo.www.shared.dto.FeatureLevel;
+import fr.agrometinfo.www.shared.dto.FeatureProperty;
 import fr.agrometinfo.www.shared.dto.IndicatorDTO;
 import fr.agrometinfo.www.shared.dto.PeriodDTO;
 import fr.agrometinfo.www.shared.dto.SimpleFeature;
@@ -171,6 +173,26 @@ public class IndicatorResource implements IndicatorService {
     @Context
     private HttpHeaders httpHeaders;
 
+    /**
+     * @param collection collection to annotate
+     * @param indicator  indicator with metadata to add
+     * @param year       to get the first day of indicator period
+     * @param locale     locale of descriptions
+     */
+    private void addMetadata(final FeatureCollection collection, final Indicator indicator,
+            final Integer year, final Locale locale) {
+        final var periodFirstDay = getDate(year, indicator.getPeriod().getFirstDay());
+        final Feature feature = new Feature();
+        final MultiPolygon multiPolygon = new MultiPolygon();
+        feature.setGeometry(multiPolygon);
+        feature.setProperty(FeatureProperty.INDICATOR_NAME.toCamelCase(),
+                getTranslation(indicator.getDescriptions(), locale));
+        feature.setProperty(FeatureProperty.INDICATOR_UNIT.toCamelCase(), indicator.getUnit());
+        feature.setProperty(FeatureProperty.PERIOD_FIRST_DAY.toCamelCase(),
+                DateUtils.toIso8601Date(periodFirstDay));
+        collection.add(feature);
+    }
+
     /**
      * Ensure the value of query parameter is not null and not blank.
      *
@@ -313,47 +335,47 @@ public class IndicatorResource implements IndicatorService {
         final SimpleFeature parentFeature;
         final String featureName;
         switch (level) {
-            case REGION -> {
-                Integer regionId;
-                final Region region;
-                try {
-                    regionId = Integer.valueOf(id);
-                } catch (final NumberFormatException e) {
-                    regionId = null;
-                }
-                if (regionId != null) {
-                    region = regionDao.find(regionId);
-                } else {
-                    region = null;
-                }
-                if (region == null) {
-                    // Metropolitan France
-                    featureName = null;
-                    averageValue = praDailyValueDao.findAverageComputedValue(indicator, date);
-                    comparedValue = praDailyValueDao.findAverageComparedValue(indicator, date);
-                    tmpDailyValues = praDailyValueDao.findDailyValues(indicator, firstDay, lastDay);
-                    parentFeature = null;
-                } else {
-                    averageValue = praDailyValueDao.findAverageComputedValue(indicator, date, regionId);
-                    comparedValue = praDailyValueDao.findAverageComparedValue(indicator, date, regionId);
-                    tmpDailyValues = praDailyValueDao.findDailyValues(indicator, firstDay, lastDay, region);
-                    featureName = region.getName();
-                    parentFeature = null;
-                }
+        case REGION -> {
+            Integer regionId;
+            final Region region;
+            try {
+                regionId = Integer.valueOf(id);
+            } catch (final NumberFormatException e) {
+                regionId = null;
+            }
+            if (regionId != null) {
+                region = regionDao.find(regionId);
+            } else {
+                region = null;
             }
-            case PRA -> {
-                final Pra pra = praDao.findByCode(id);
-                final PraDailyValue value = praDailyValueDao.find(indicator, date, pra);
-                averageValue = value.getComputedValue().doubleValue();
-                comparedValue = value.getComparedValue().doubleValue();
-                tmpDailyValues = praDailyValueDao.findDailyValues(indicator, firstDay, lastDay, pra);
-                featureName = pra.getName();
-                parentFeature = new SimpleFeature();
-                parentFeature.setId(String.valueOf(pra.getDepartment().getRegion().getId()));
-                parentFeature.setLevel(FeatureLevel.REGION);
-                parentFeature.setName(pra.getDepartment().getRegion().getName());
+            if (region == null) {
+                // Metropolitan France
+                featureName = null;
+                averageValue = praDailyValueDao.findAverageComputedValue(indicator, date);
+                comparedValue = praDailyValueDao.findAverageComparedValue(indicator, date);
+                tmpDailyValues = praDailyValueDao.findDailyValues(indicator, firstDay, lastDay);
+                parentFeature = null;
+            } else {
+                averageValue = praDailyValueDao.findAverageComputedValue(indicator, date, regionId);
+                comparedValue = praDailyValueDao.findAverageComparedValue(indicator, date, regionId);
+                tmpDailyValues = praDailyValueDao.findDailyValues(indicator, firstDay, lastDay, region);
+                featureName = region.getName();
+                parentFeature = null;
             }
-            default -> throw new UnsupportedOperationException("Level not handled: " + level);
+        }
+        case PRA -> {
+            final Pra pra = praDao.findByCode(id);
+            final PraDailyValue value = praDailyValueDao.find(indicator, date, pra);
+            averageValue = value.getComputedValue().doubleValue();
+            comparedValue = value.getComparedValue().doubleValue();
+            tmpDailyValues = praDailyValueDao.findDailyValues(indicator, firstDay, lastDay, pra);
+            featureName = pra.getName();
+            parentFeature = new SimpleFeature();
+            parentFeature.setId(String.valueOf(pra.getDepartment().getRegion().getId()));
+            parentFeature.setLevel(FeatureLevel.REGION);
+            parentFeature.setName(pra.getDepartment().getRegion().getName());
+        }
+        default -> throw new UnsupportedOperationException("Level not handled: " + level);
         }
         tmpDailyValues.forEach((d, v) -> dailyValues.put(DateUtils.toDate(d), v));
 
@@ -394,9 +416,10 @@ public class IndicatorResource implements IndicatorService {
         checkRequired(indicatorUid, "indicator");
         checkRequired(periodCode, "period");
         checkRequired(year, "year");
-        // HTTP cache headers
+        final var locale = LocaleUtils.getLocale(httpServletRequest);
         cacheService.setCacheKey(IndicatorService.PATH, IndicatorService.PATH_VALUES, indicatorUid, periodCode,
-                regionId, year, comparison);
+                regionId, year, comparison, locale);
+        // HTTP cache headers
         cacheService.setHeaders(httpHeaders);
         if (!cacheService.needsResponse(request)) {
             return null;
@@ -408,6 +431,7 @@ public class IndicatorResource implements IndicatorService {
         //
         final FeatureCollection collection = new FeatureCollection();
         final Indicator indicator = indicatorDao.findByCodeAndPeriod(indicatorUid, periodCode);
+        addMetadata(collection, indicator, year, locale);
         final LocalDate date = praDailyValueDao.findLastDate(indicator, year);
         final Region region;
         if (regionId == null) {
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/util/DateUtils.java b/www-server/src/main/java/fr/agrometinfo/www/server/util/DateUtils.java
index 39153e6bbae0c8796f60dbe6499ce3be2f980913..c7c35af4085a284b79ffb31ea13fc8e40f3e70fa 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/util/DateUtils.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/util/DateUtils.java
@@ -2,6 +2,8 @@ package fr.agrometinfo.www.server.util;
 
 import java.time.LocalDate;
 import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
 import java.util.Date;
 
 import lombok.NonNull;
@@ -12,6 +14,11 @@ import lombok.NonNull;
  * @author Olivier Maury
  */
 public interface DateUtils {
+    /**
+     * ISO-8601 date format.
+     */
+    String ISO_8601_DATE_FORMAT = "yyyy-MM-dd";
+
     /**
      * @param localDate LocalDate to convert
      * @return converted Date
@@ -20,6 +27,16 @@ public interface DateUtils {
         return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
     }
 
+    /**
+     * @param localDate LocalDate to format
+     * @return formatted date as ISO 8601
+     * @see https://en.wikipedia.org/wiki/ISO_8601
+     */
+    static String toIso8601Date(@NonNull final LocalDate localDate) {
+        final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(ISO_8601_DATE_FORMAT);
+        return localDate.atStartOfDay().atOffset(ZoneOffset.UTC).format(formatter);
+    }
+
     /**
      * @param date Date
      * @return Java8 LocalDate
diff --git a/www-server/src/main/resources/log4j2.xml b/www-server/src/main/resources/log4j2.xml
index eca62907518f74ac1dc6321212feed0ac1af04a2..15540a5b8e7ab55f291c88c76c3fc140ac259ab6 100644
--- a/www-server/src/main/resources/log4j2.xml
+++ b/www-server/src/main/resources/log4j2.xml
@@ -29,6 +29,7 @@
                         <AppenderRef ref="file" level="trace" />
                 </Root>
                 <Logger name="fr.agrometinfo" level="trace" />
+                <Logger name="fr.agrometinfo.www.server.dao" level="info" />
                 <Logger name="org.hibernate" level="warn" />
                 <Logger name="org.jboss" level="warn" />
         </Loggers>
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/GeometryResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/GeometryResourceTest.java
index 36082e13736038c5e76773f6675644f0c9575ec7..4759b00604777e6eeb867235dc7a2f7379442b36 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/GeometryResourceTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/GeometryResourceTest.java
@@ -3,15 +3,24 @@ package fr.agrometinfo.www.server.rs;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Comparator;
+
 import org.geojson.FeatureCollection;
 import org.glassfish.hk2.utilities.binding.AbstractBinder;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
 import fr.agrometinfo.www.server.dao.PraDao;
 import fr.agrometinfo.www.server.dao.PraDaoHibernate;
 import fr.agrometinfo.www.server.dao.PraDaoHibernateTest;
+import fr.agrometinfo.www.server.dao.SimulationDaoHibernateTest;
+import fr.agrometinfo.www.server.service.CacheService;
 import jakarta.ws.rs.core.Application;
 
 /**
@@ -24,13 +33,27 @@ public class GeometryResourceTest extends JerseyTest {
      * Path separator.
      */
     private static final String SEP = "/";
+    /**
+     * Temporary directory for cache.
+     */
+    private static Path cacheDir;
+
+    @BeforeAll
+    static void createCacheDir() throws IOException {
+        cacheDir = Files.createTempDirectory(IndicatorResourceTest.class.getName());
+    }
+
 
     @Override
     protected final Application configure() {
+        final CacheService cacheService = new CacheService();
+        cacheService.setLastModification(SimulationDaoHibernateTest.LAST_MODIFICATION);
+        cacheService.setCacheDirectory(cacheDir.toString());
         final PraDao praDao = new PraDaoHibernate();
         return new ResourceConfig(GeometryResource.class).register(new AbstractBinder() {
             @Override
             public void configure() {
+                bind(cacheService).to(CacheService.class);
                 bind(praDao).to(PraDao.class);
             }
         });
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java
index 6c56b7df2f6fba5d59dbf7e5889d65f6229940e1..ffa0ece7c19df0578542c57a8dbb95a54b83e3a2 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java
@@ -121,9 +121,10 @@ class IndicatorResourceTest extends JerseyTest {
                 .request()//
                 .get(FeatureCollection.class);
         assertNotNull(actual);
-        final Integer expected = 1;
+        // first feature is an empty feature with metadata properties
+        final Integer expected = 2;
         assertEquals(expected, actual.getFeatures().size());
-        assertEquals("59325", actual.getFeatures().get(0).getId());
-        assertEquals("2023-01-04", actual.getFeatures().get(0).getProperty("date"));
+        assertEquals("59325", actual.getFeatures().get(1).getId());
+        assertEquals("2023-01-04", actual.getFeatures().get(1).getProperty("date"));
     }
 }
diff --git a/www-shared/pom.xml b/www-shared/pom.xml
index ad792519e0b82c73a4e66dd76f7065164f6ffeda..0cbdbc9b475bd2afcba28890c87f43845390af4c 100644
--- a/www-shared/pom.xml
+++ b/www-shared/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>fr.agrometinfo</groupId>
     <artifactId>www</artifactId>
-    <version>2.0.1</version>
+    <version>2.0.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>www-shared</artifactId>
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/FeatureProperty.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/FeatureProperty.java
new file mode 100644
index 0000000000000000000000000000000000000000..04ea6ca294365df5db95bdff597d3f53a5e5e599
--- /dev/null
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/FeatureProperty.java
@@ -0,0 +1,41 @@
+package fr.agrometinfo.www.shared.dto;
+
+/**
+ * Keys for feature properties, some of them are used in the first feature as
+ * metadata for FeatureCollection.
+ *
+ * @author Olivier Maury
+ */
+public enum FeatureProperty {
+    /**
+     * Feature property.
+     */
+    DATE,
+    /**
+     * Feature property.
+     */
+    VALUE,
+    /**
+     * Collection property.
+     */
+    INDICATOR_NAME,
+    /**
+     * Collection property.
+     */
+    INDICATOR_UNIT,
+    /**
+     * Collection property.
+     */
+    PERIOD_FIRST_DAY;
+
+    /**
+     * @return enum name as camel case format
+     */
+    public String toCamelCase() {
+        String str = name().toLowerCase();
+        while (str.contains("_")) {
+            str = str.replaceFirst("_[a-z]", String.valueOf(Character.toUpperCase(str.charAt(str.indexOf("_") + 1))));
+        }
+        return str;
+    }
+}
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/GeometryService.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/GeometryService.java
new file mode 100644
index 0000000000000000000000000000000000000000..3c90c4a6159e1bf21480406986bca91c100d4481
--- /dev/null
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/GeometryService.java
@@ -0,0 +1,42 @@
+package fr.agrometinfo.www.shared.service;
+
+import java.util.Map;
+
+import org.dominokit.rest.shared.request.service.annotations.RequestFactory;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+
+/**
+ * Interface for client and server resource.
+ *
+ * @author Olivier Maury
+ */
+@RequestFactory
+@Path(GeometryService.PATH)
+public interface GeometryService {
+    /**
+     * Service base path.
+     */
+    String PATH = "geometry";
+
+    /**
+     * Path for PRA geometries.
+     */
+    String PATH_PRA = "pra";
+
+    /**
+     * Path for PRA names.
+     */
+    String PATH_PRA_NAMES = "pra_names";
+
+    /**
+     * @return PRA id ⮕ PRA name
+     */
+    @GET
+    @Path(GeometryService.PATH_PRA_NAMES)
+    @Produces(MediaType.APPLICATION_JSON)
+    Map<String, String> getPraAsJSON();
+}
diff --git a/www-shared/src/test/java/fr/agrometinfo/www/shared/dto/FeaturePropertyTest.java b/www-shared/src/test/java/fr/agrometinfo/www/shared/dto/FeaturePropertyTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e6130cbd54d4c77624b79a0a5a49391149a69ba3
--- /dev/null
+++ b/www-shared/src/test/java/fr/agrometinfo/www/shared/dto/FeaturePropertyTest.java
@@ -0,0 +1,27 @@
+package fr.agrometinfo.www.shared.dto;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test methods of {@link FeatureProperty}.
+ *
+ * @author Olivier Maury
+ */
+class FeaturePropertyTest {
+
+    /**
+     * Test camel case formatting.
+     */
+    @Test
+    void toCamelCase() {
+        var actual = FeatureProperty.INDICATOR_NAME.toCamelCase();
+        var expected = "indicatorName";
+        assertEquals(expected, actual);
+
+        actual = FeatureProperty.PERIOD_FIRST_DAY.toCamelCase();
+        expected = "periodFirstDay";
+        assertEquals(expected, actual);
+    }
+}