sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Allow selection of interpolation method.
Date Mon, 18 May 2020 11:04:43 GMT
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 3974735  Allow selection of interpolation method.
3974735 is described below

commit 39747356392aace3e8db46e4fab81d54a3786212
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Mon May 18 13:03:28 2020 +0200

    Allow selection of interpolation method.
---
 .../java/org/apache/sis/gui/coverage/Controls.java | 24 +++++-
 .../apache/sis/gui/coverage/CoverageCanvas.java    | 54 ++++++++++--
 .../apache/sis/gui/coverage/CoverageControls.java  | 95 +++++++++++++++++++---
 .../org/apache/sis/gui/coverage/GridControls.java  | 11 +--
 .../org/apache/sis/gui/coverage/RenderingData.java | 11 +++
 .../org/apache/sis/gui/referencing/CRSChooser.java |  2 +-
 .../java/org/apache/sis/internal/gui/Styles.java   |  5 +-
 .../org/apache/sis/util/resources/Vocabulary.java  | 15 ++++
 .../sis/util/resources/Vocabulary.properties       |  3 +
 .../sis/util/resources/Vocabulary_fr.properties    |  3 +
 10 files changed, 191 insertions(+), 32 deletions(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java
index 00764a8..be38dee 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java
@@ -48,7 +48,7 @@ abstract class Controls {
     /**
      * Margin to keep around captions after the first one.
      */
-    static final Insets NEXT_CAPTION_MARGIN = new Insets(30, 0, 9, 0);
+    private static final Insets NEXT_CAPTION_MARGIN = new Insets(30, 0, 9, 0);
 
     /**
      * The border to use for grouping some controls together.
@@ -73,11 +73,13 @@ abstract class Controls {
      * The controls must be associated to the given labels by {@link Label#getLabelFor()}.
      * If a label is {@code null}, then no row is created for that label.
      *
+     * @param  row       index of the first row. If different than 0, then it is caller responsibility
+     *                   to provide controls for all rows before the specified index.
      * @param  controls  (label, control) pairs to layout in rows.
      * @return a pane with each (label, control) pair on a row.
      */
-    static GridPane createControlGrid(final Label... controls) {
-        final GridPane gp = Styles.createControlGrid(controls);
+    static GridPane createControlGrid(final int row, final Label... controls) {
+        final GridPane gp = Styles.createControlGrid(row, controls);
         Styles.setAllRowToSameHeight(gp);
         gp.setBorder(GROUP_BORDER);
         return gp;
@@ -103,6 +105,22 @@ abstract class Controls {
     }
 
     /**
+     * Creates a label with the specified text associated to the given group of controls.
+     *
+     * @param  vocabulary  the resources from which to get the text.
+     * @param  key         {@code vocabulary} key of the text to put in the label.
+     * @param  group       the group of controls to associate to the label.
+     * @param  isFirst     whether the given group is the first group in the pane.
+     * @return label associated to the given group of controls.
+     */
+    static Label labelOfGroup(final Vocabulary vocabulary, final short key, final Region
group, final boolean isFirst) {
+        final Label label = new Label(vocabulary.getLabel(key));
+        label.setPadding(isFirst ? CAPTION_MARGIN : NEXT_CAPTION_MARGIN);
+        label.setLabelFor(group);
+        return label;
+    }
+
+    /**
      * Returns the main component, which is showing coverage data or image.
      * This is the component to shown on the right (largest) part of the split pane.
      */
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
index aeb414d..d76cf16 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
@@ -44,6 +44,7 @@ import org.apache.sis.referencing.operation.transform.LinearTransform;
 import org.apache.sis.referencing.IdentifiedObjects;
 import org.apache.sis.geometry.Envelope2D;
 import org.apache.sis.image.PlanarImage;
+import org.apache.sis.image.Interpolation;
 import org.apache.sis.gui.map.MapCanvas;
 import org.apache.sis.gui.map.MapCanvasAWT;
 import org.apache.sis.internal.gui.Resources;
@@ -84,6 +85,14 @@ public class CoverageCanvas extends MapCanvasAWT {
     public final ObjectProperty<GridExtent> sliceExtentProperty;
 
     /**
+     * The interpolation method to use for resampling the image.
+     *
+     * @see #getInterpolation()
+     * @see #setInterpolation(Interpolation)
+     */
+    public final ObjectProperty<Interpolation> interpolationProperty;
+
+    /**
      * The {@code RenderedImage} to draw together with transform from pixel coordinates to
display coordinates.
      * Shall never be {@code null} but may be {@linkplain RenderingData#isEmpty() empty}.
This instance shall be
      * read and modified in JavaFX thread only and cloned if those data needed by a background
thread.
@@ -104,12 +113,14 @@ public class CoverageCanvas extends MapCanvasAWT {
      */
     public CoverageCanvas() {
         super(Locale.getDefault());
-        coverageProperty    = new SimpleObjectProperty<>(this, "coverage");
-        sliceExtentProperty = new SimpleObjectProperty<>(this, "sliceExtent");
-        resampledImages     = new EnumMap<>(Stretching.class);
-        data                = new RenderingData();
-        coverageProperty   .addListener((p,o,n) -> onImageSpecified());
-        sliceExtentProperty.addListener((p,o,n) -> onImageSpecified());
+        data                  = new RenderingData();
+        resampledImages       = new EnumMap<>(Stretching.class);
+        coverageProperty      = new SimpleObjectProperty<>(this, "coverage");
+        sliceExtentProperty   = new SimpleObjectProperty<>(this, "sliceExtent");
+        interpolationProperty = new SimpleObjectProperty<>(this, "interpolation", data.getInterpolation());
+        coverageProperty     .addListener((p,o,n) -> onImageSpecified());
+        sliceExtentProperty  .addListener((p,o,n) -> onImageSpecified());
+        interpolationProperty.addListener((p,o,n) -> onInterpolationSpecified(n));
     }
 
     /**
@@ -173,6 +184,28 @@ public class CoverageCanvas extends MapCanvasAWT {
     }
 
     /**
+     * Gets the interpolation method used during resample operations.
+     *
+     * @return the current interpolation method.
+     *
+     * @see #interpolationProperty
+     */
+    public final Interpolation getInterpolation() {
+        return interpolationProperty.get();
+    }
+
+    /**
+     * Sets the interpolation method to use during resample operations.
+     *
+     * @param interpolation the new interpolation method.
+     *
+     * @see #interpolationProperty
+     */
+    public final void setInterpolation(final Interpolation interpolation) {
+        interpolationProperty.set(interpolation);
+    }
+
+    /**
      * Sets the background, as a color for now but more patterns may be allowed in a future
version.
      */
     final void setBackground(final Color color) {
@@ -247,6 +280,15 @@ public class CoverageCanvas extends MapCanvasAWT {
     }
 
     /**
+     * Invoked when a new interpolation has been specified.
+     */
+    private void onInterpolationSpecified(final Interpolation interpolation) {
+        data.setInterpolation(interpolation);
+        resampledImages.clear();
+        requestRepaint();
+    }
+
+    /**
      * Invoked in JavaFX thread for creating a renderer to be executed in a background thread.
      * This method prepares the information needed but does not start the rendering itself.
      * The rendering will be done later by a call to {@link Renderer#paint(Graphics2D)}.
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageControls.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageControls.java
index 91b0284..a5103fc 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageControls.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageControls.java
@@ -16,12 +16,13 @@
  */
 package org.apache.sis.gui.coverage;
 
+import java.util.Locale;
+import java.util.Objects;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import javafx.scene.control.Accordion;
 import javafx.scene.control.ColorPicker;
 import javafx.scene.control.Control;
-import javafx.scene.control.Label;
 import javafx.scene.control.TitledPane;
 import javafx.scene.layout.BorderPane;
 import javafx.scene.layout.GridPane;
@@ -30,11 +31,13 @@ import javafx.scene.layout.VBox;
 import javafx.beans.property.ObjectProperty;
 import javafx.scene.control.ChoiceBox;
 import javafx.scene.paint.Color;
+import javafx.util.StringConverter;
 import org.opengis.referencing.ReferenceSystem;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.gui.referencing.RecentReferenceSystems;
 import org.apache.sis.gui.map.StatusBar;
+import org.apache.sis.image.Interpolation;
 import org.apache.sis.util.resources.Vocabulary;
 
 
@@ -87,29 +90,32 @@ final class CoverageControls extends Controls implements PropertyChangeListener
         /*
          * "Display" section with the following controls:
          *    - Coordinate reference system
+         *    - Interpolation
          *    - Color stretching
          *    - Background color
          */
         final VBox displayPane;
         {   // Block for making variables locale to this scope.
-            final ChoiceBox<ReferenceSystem> systems = referenceSystems.createChoiceBox((p,o,n)
-> {
+            final GridPane referencing = createControlGrid(1,
+                label(vocabulary, Vocabulary.Keys.Interpolation, createInterpolationButton(vocabulary.getLocale()))
+            );
+            final ChoiceBox<ReferenceSystem> systemChoices = referenceSystems.createChoiceBox((p,o,n)
-> {
                 if (n instanceof CoordinateReferenceSystem) {
                     view.setObjectiveCRS((CoordinateReferenceSystem) n, p);
                 }
             });
-            systems.setMaxWidth(Double.POSITIVE_INFINITY);
-            referenceSystem = systems.valueProperty();
-            final Label systemLabel = new Label(vocabulary.getLabel(Vocabulary.Keys.ReferenceSystem));
-            systemLabel.setPadding(CAPTION_MARGIN);
-            systemLabel.setLabelFor(systems);
-            final GridPane gp = createControlGrid(
+            systemChoices.setMaxWidth(Double.POSITIVE_INFINITY);
+            GridPane.setConstraints(systemChoices, 0, 0, 2, 1);     // First row and column,
span 2 columns.
+            referencing.getChildren().add(systemChoices);
+            referenceSystem = systemChoices.valueProperty();
+
+            final GridPane colors = createControlGrid(0,
                 label(vocabulary, Vocabulary.Keys.Stretching, Stretching.createButton((p,o,n)
-> view.setStretching(n))),
                 label(vocabulary, Vocabulary.Keys.Background, createBackgroundButton(background))
             );
-            final Label label = new Label(vocabulary.getLabel(Vocabulary.Keys.Colors));
-            label.setPadding(NEXT_CAPTION_MARGIN);
-            label.setLabelFor(gp);
-            displayPane = new VBox(systemLabel, systems, label, gp);
+            displayPane = new VBox(
+                labelOfGroup(vocabulary, Vocabulary.Keys.ReferenceSystem, referencing, true),
referencing,
+                labelOfGroup(vocabulary, Vocabulary.Keys.Colors,          colors,     false),
colors);
         }
         /*
          * Put all sections together and have the first one expanded by default.
@@ -124,6 +130,71 @@ final class CoverageControls extends Controls implements PropertyChangeListener
     }
 
     /**
+     * Creates the controls for choosing an interpolation method.
+     */
+    private ChoiceBox<Interpolation> createInterpolationButton(final Locale locale)
{
+        final ChoiceBox<Interpolation> b = new ChoiceBox<>();
+        b.setConverter(new InterpolationConverter(locale));
+        b.getItems().setAll(InterpolationConverter.INTERPOLATIONS);
+        b.getSelectionModel().select(view.getInterpolation());
+        view.interpolationProperty.bind(b.getSelectionModel().selectedItemProperty());
+        return b;
+    }
+
+    /**
+     * Gives a localized {@link String} instance for a given {@link Interpolation} and conversely.
+     */
+    private static final class InterpolationConverter extends StringConverter<Interpolation>
{
+        /** The interpolation supported by this converter. */
+        static final Interpolation[] INTERPOLATIONS = {
+            Interpolation.NEAREST, Interpolation.BILINEAR, Interpolation.LANCZOS
+        };
+
+        /** Keys of localized names for each {@link #INTERPOLATIONS} element. */
+        private static final short[] VOCABULARIES = {
+            Vocabulary.Keys.NearestNeighbor, Vocabulary.Keys.Bilinear, 0
+        };
+
+        /** The locale to use for string representation. */
+        private final Locale locale;
+
+        /** Creates a new converter for the given locale. */
+        InterpolationConverter(final Locale locale) {
+            this.locale = locale;
+        }
+
+        /** Returns a string representation of the given item. */
+        @Override public String toString(final Interpolation item) {
+            for (int i=0; i<INTERPOLATIONS.length; i++) {
+                if (INTERPOLATIONS[i].equals(item)) {
+                    final short key = VOCABULARIES[i];
+                    if (key != 0) {
+                        return Vocabulary.getResources(locale).getString(key);
+                    } else if (item == Interpolation.LANCZOS) {
+                        return "Lanczos";
+                    }
+                }
+            }
+            return Objects.toString(item);
+        }
+
+        /** Returns the interpolation for the given text. */
+        @Override public Interpolation fromString(final String text) {
+            final Vocabulary vocabulary = Vocabulary.getResources(locale);
+            for (int i=0; i<VOCABULARIES.length; i++) {
+                final short key = VOCABULARIES[i];
+                final Interpolation item = INTERPOLATIONS[i];
+                if ((key != 0 && vocabulary.getString(key).equalsIgnoreCase(text))
+                                        || item.toString().equalsIgnoreCase(text))
+                {
+                    return item;
+                }
+            }
+            return null;
+        }
+    }
+
+    /**
      * Creates the button for selecting a background color.
      */
     private ColorPicker createBackgroundButton(final Color background) {
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridControls.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridControls.java
index f569356..260a21b 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridControls.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridControls.java
@@ -75,9 +75,7 @@ final class GridControls extends Controls {
          */
         final VBox coveragePane;
         {   // Block for making variables locale to this scope.
-            final Label label = new Label(vocabulary.getLabel(Vocabulary.Keys.SampleDimensions));
-            label.setPadding(CAPTION_MARGIN);
-            label.setLabelFor(sampleDimensions);
+            final Label label = labelOfGroup(vocabulary, Vocabulary.Keys.SampleDimensions,
sampleDimensions, true);
             coveragePane = new VBox(label, sampleDimensions);
         }
         /*
@@ -87,15 +85,12 @@ final class GridControls extends Controls {
          */
         final VBox displayPane;
         {   // Block for making variables locale to this scope.
-            final GridPane gp = createControlGrid(
+            final GridPane gp = createControlGrid(0,
                 label(vocabulary, Vocabulary.Keys.Width,  createSlider(view.cellWidth,  30,
200)),
                 label(vocabulary, Vocabulary.Keys.Height, createSlider(view.cellHeight, 10,
 50)),
                 label(vocabulary, Vocabulary.Keys.Format, view.cellFormat.createEditor())
             );
-            final Label label = new Label(vocabulary.getLabel(Vocabulary.Keys.Cells));
-            label.setPadding(CAPTION_MARGIN);
-            label.setLabelFor(gp);
-            displayPane = new VBox(label, gp);
+            displayPane = new VBox(labelOfGroup(vocabulary, Vocabulary.Keys.Cells, gp, true),
gp);
         }
         /*
          * Put all sections together and have the first one expanded by default.
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
index 8de11f5..1ef639e 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
@@ -195,7 +195,18 @@ final class RenderingData implements Cloneable {
     }
 
     /**
+     * Gets the interpolation method to use during resample operations.
+     *
+     * @see CoverageCanvas#getInterpolation()
+     */
+    final Interpolation getInterpolation() {
+        return processor.getInterpolation();
+    }
+
+    /**
      * Sets the interpolation method to use during resample operations.
+     *
+     * @see CoverageCanvas#setInterpolation(Interpolation)
      */
     final void setInterpolation(final Interpolation newValue) {
         processor = processor.clone();          // Previous processor may be in use by background
thread.
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSChooser.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSChooser.java
index 1a26e67..8ff7d95 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSChooser.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSChooser.java
@@ -224,7 +224,7 @@ public class CRSChooser extends Dialog<CoordinateReferenceSystem>
{
             final Label ld = new Label(vocabulary.getLabel(Vocabulary.Keys.Domain));
             lt.setLabelFor(type   = new Label());
             ld.setLabelFor(domain = new Label());
-            summary = Styles.createControlGrid(lt, ld);
+            summary = Styles.createControlGrid(0, lt, ld);
             final Tooltip tp = new Tooltip();
             tp.setShowDelay(Duration.seconds(0.5));
             tp.setShowDuration(Duration.minutes(1));
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Styles.java
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Styles.java
index 225d50e..d681eeb 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Styles.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Styles.java
@@ -165,10 +165,12 @@ public final class Styles extends Static {
      * The controls must be associated to the given labels by {@link Label#getLabelFor()}.
      * If a label is {@code null}, then no row is created for that label.
      *
+     * @param  row       index of the first row. If different than 0, then it is caller responsibility
+     *                   to provide controls for all rows before the specified index.
      * @param  controls  (label, control) pairs to layout in rows.
      * @return a pane with each (label, control) pair on a row.
      */
-    public static GridPane createControlGrid(final Label... controls) {
+    public static GridPane createControlGrid(int row, final Label... controls) {
         final GridPane gp = new GridPane();
         final ColumnConstraints labelColumn   = new ColumnConstraints();
         final ColumnConstraints controlColumn = new ColumnConstraints();
@@ -194,7 +196,6 @@ public final class Styles extends Static {
         gp.setPadding(FORM_INSETS);
         gp.setVgap(9);
         gp.setHgap(9);
-        int row = 0;
         for (final Label label : controls) {
             if (label != null) {
                 final Node control = label.getLabelFor();
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
index d670e6a..3e97eb0 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
@@ -135,6 +135,11 @@ public final class Vocabulary extends IndexedResourceBundle {
         public static final short BarometricAltitude = 16;
 
         /**
+         * Bilinear
+         */
+        public static final short Bilinear = 230;
+
+        /**
          * Black
          */
         public static final short Black = 17;
@@ -590,6 +595,11 @@ public final class Vocabulary extends IndexedResourceBundle {
         public static final short Index = 106;
 
         /**
+         * Interpolation
+         */
+        public static final short Interpolation = 231;
+
+        /**
          * Invalid
          */
         public static final short Invalid = 107;
@@ -755,6 +765,11 @@ public final class Vocabulary extends IndexedResourceBundle {
         public static final short Name = 139;
 
         /**
+         * Nearest neighbor
+         */
+        public static final short NearestNeighbor = 232;
+
+        /**
          * No data
          */
         public static final short Nodata = 140;
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
index c3a53b0..6b157c2 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
@@ -30,6 +30,7 @@ Azimuth                 = Azimuth
 Background              = Background
 Band_1                  = Band {0}
 BarometricAltitude      = Barometric altitude
+Bilinear                = Bilinear
 Black                   = Black
 Blue                    = Blue
 Cardinality             = Cardinality
@@ -121,6 +122,7 @@ ImageLayout             = Image layout
 Implementation          = Implementation
 InBetweenWords          = \u2002in\u2002
 Index                   = Index
+Interpolation           = Interpolation
 Invalid                 = Invalid
 InverseOperation        = Inverse operation
 JavaExtensions          = Java extensions
@@ -154,6 +156,7 @@ ModifiedJulian          = Modified Julian
 More_1                  = \u2026 {0} more\u2026
 Multiplicity            = Multiplicity
 Name                    = Name
+NearestNeighbor         = Nearest neighbor
 Nodata                  = No data
 None                    = None
 Note                    = Note
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
index f2eeb0e..0c89b49 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
@@ -37,6 +37,7 @@ Azimuth                 = Azimut
 Background              = Arri\u00e8re plan
 Band_1                  = Bande {0}
 BarometricAltitude      = Altitude barom\u00e9trique
+Bilinear                = Bilin\u00e9aire
 Black                   = Noir
 Blue                    = Bleu
 Cardinality             = Cardinalit\u00e9
@@ -128,6 +129,7 @@ ImageLayout             = Agencement de l\u2019image
 Implementation          = Impl\u00e9mentation
 InBetweenWords          = \u2002dans\u2002
 Index                   = Index
+Interpolation           = Interpolation
 Invalid                 = Invalide
 InverseOperation        = Op\u00e9ration inverse
 JavaExtensions          = Extensions du Java
@@ -161,6 +163,7 @@ ModifiedJulian          = Julien modifi\u00e9
 More_1                  = \u2026 {0} de plus\u2026
 Multiplicity            = Multiplicit\u00e9
 Name                    = Nom
+NearestNeighbor         = Plus proche voisin
 Nodata                  = Absence de donn\u00e9es
 None                    = Aucun
 Note                    = Note


Mime
View raw message