This is an automated email from the ASF dual-hosted git repository.

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

commit c773a3c2e7c61464a13731675b56dc5a70050e13
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sat Aug 8 02:25:45 2026 +0200

    Provided more information about the `RenderedImage` layout in its 
`toString()` representation.
    This is an attempt to provide more debugging help.
---
 .../apache/sis/coverage/grid/GridCoverage2D.java   |  56 +---
 .../apache/sis/coverage/grid/GridDerivation.java   |   2 +-
 .../org/apache/sis/coverage/grid/GridExtent.java   |   4 +-
 .../org/apache/sis/feature/internal/Resources.java |  15 ++
 .../sis/feature/internal/Resources.properties      |   3 +
 .../sis/feature/internal/Resources_fr.properties   |   3 +
 .../main/org/apache/sis/image/PlanarImage.java     |  58 +----
 .../image/internal/shared/ColorModelFactory.java   |  54 +++-
 .../sis/image/internal/shared/ImageUtilities.java  |  30 ---
 .../image/internal/shared/ScaledColorSpace.java    |  11 +-
 .../sis/image/internal/shared/Summarizer.java      | 288 +++++++++++++++++++++
 .../apache/sis/coverage/grid/GridExtentTest.java   |  36 ++-
 .../sis/util/resources/IndexedResourceBundle.java  |  14 +-
 .../org/apache/sis/util/resources/Vocabulary.java  |  10 +-
 .../sis/util/resources/Vocabulary.properties       |   2 +-
 .../sis/util/resources/Vocabulary_fr.properties    |   4 +-
 16 files changed, 408 insertions(+), 182 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage2D.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage2D.java
index 3ff94f38e6..8c9e4362e0 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage2D.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage2D.java
@@ -22,10 +22,6 @@ import java.util.ArrayList;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicReference;
-import java.text.NumberFormat;
-import java.text.FieldPosition;
-import java.io.IOException;
-import java.io.UncheckedIOException;
 import java.awt.Rectangle;
 import java.awt.image.BufferedImage;
 import java.awt.image.RenderedImage;
@@ -48,6 +44,7 @@ import org.apache.sis.image.DataType;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.image.internal.shared.ImageUtilities;
 import org.apache.sis.image.internal.shared.ReshapedImage;
+import org.apache.sis.image.internal.shared.Summarizer;
 import org.apache.sis.feature.internal.Resources;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.Debug;
@@ -710,7 +707,7 @@ public class GridCoverage2D extends GridCoverage {
     }
 
     /**
-     * Appends a "data layout" branch (if it exists) to the tree 
representation of this coverage.
+     * Appends a "data layout" branch to the tree representation of this 
coverage.
      * That branch will be inserted between "coverage domain" and "sample 
dimensions" branches.
      *
      * @param  root        root of the tree where to add a branch.
@@ -719,50 +716,9 @@ public class GridCoverage2D extends GridCoverage {
      */
     @Debug
     @Override
-    void appendDataLayout(final TreeTable.Node root, final Vocabulary 
vocabulary, final TableColumn<CharSequence> column) {
-        final TreeTable.Node branch = root.newChild();
-        branch.setValue(column, 
vocabulary.getString(Vocabulary.Keys.ImageLayout));
-        final var nf = NumberFormat.getIntegerInstance(vocabulary.getLocale());
-        final var pos = new FieldPosition(0);
-        final var buffer = new StringBuffer();
-write:  for (int item=0; ; item++) try {
-            switch (item) {
-                case 0: {
-                    vocabulary.appendLabel(Vocabulary.Keys.Origin, buffer);
-                    nf.format(data.getMinX(), buffer.append(' '),  pos);
-                    nf.format(data.getMinY(), buffer.append(", "), pos);
-                    break;
-                }
-                case 1: {
-                    final int tx = data.getTileWidth();
-                    final int ty = data.getTileHeight();
-                    if (tx == data.getWidth() && ty == data.getHeight()) 
continue;
-                    vocabulary.appendLabel(Vocabulary.Keys.TileSize, buffer);
-                    nf.format(tx, buffer.append( ' ' ), pos);
-                    nf.format(ty, buffer.append(" × "), pos);
-                    break;
-                }
-                case 2: {
-                    final String type = 
ImageUtilities.getDataTypeName(data.getSampleModel());
-                    if (type == null) continue;
-                    vocabulary.appendLabel(Vocabulary.Keys.DataType, buffer);
-                    buffer.append(' ').append(type);
-                    break;
-                }
-                case 3: {
-                    final short t = 
ImageUtilities.getTransparencyDescription(data.getColorModel());
-                    if (t != 0) {
-                        final String desc = 
Resources.forLocale(vocabulary.getLocale()).getString(t);
-                        branch.newChild().setValue(column, desc);
-                    }
-                    continue;
-                }
-                default: break write;
-            }
-            branch.newChild().setValue(column, buffer.toString());
-            buffer.setLength(0);
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);      // Should never happen 
since we are writing to StringBuilder.
-        }
+    void appendDataLayout(TreeTable.Node root, final Vocabulary vocabulary, 
final TableColumn<CharSequence> column) {
+        root = root.newChild();
+        root.setValue(column, 
vocabulary.getString(Vocabulary.Keys.RenderedImage));
+        new Summarizer(root, vocabulary, column).layout(data);
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
index 9babcebf44..bfdcf66b50 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
@@ -1419,7 +1419,7 @@ public class GridDerivation {
 
     /**
      * Returns the extent of the modified grid geometry, ignoring subsampling 
or changes in resolution.
-     * This is the intersection of the {@link #base} grid geometry with the 
(grid or geospatial) envelope
+     * This is the {@link #base} grid geometry intersected with the grid 
extent or geospatial) envelope
      * given to a {@link #subgrid(Envelope, double...) subgrid(…)} method,
      * expanded by the {@linkplain #margin(int...) specified margin} (if any)
      * and potentially with some {@linkplain GridExtent#getSize(int) grid 
sizes} set to 1
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
index d990d7b7bc..6c2ab5285b 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
@@ -1821,7 +1821,7 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
             if (s > 1) {
                 final int j = i + m;
                 long low  = coordinates[i];
-                long size = coordinates[j] - low + 1;                      // 
Result is an unsigned number.
+                long size = coordinates[j] - low + 1;       // Result handled 
as an unsigned number.
                 if (size == 0) {
                     throw new 
ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Long.SIZE));
                 }
@@ -2484,7 +2484,7 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
             final long lower = coordinates[i];
             final long upper = coordinates[i + dimension];
             table.setCellAlignment(TableAppender.ALIGN_LEFT);
-            table.append(name).append(": ").nextColumn();
+            table.append(vocabulary.toLabel(name)).append(' ').nextColumn();
             table.append('[').nextColumn();
             table.setCellAlignment(TableAppender.ALIGN_RIGHT);
             table.append(Long.toString(lower)).append(" … ").nextColumn();
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java
index ab67b06ea4..9981631559 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java
@@ -78,6 +78,11 @@ public class Resources extends IndexedResourceBundle {
          */
         public static final short AmbiguousGridAxisOmission_1 = 84;
 
+        /**
+         * {0,number,integer} band{0,choice,1#|2#s}
+         */
+        public static final short BandCount_1 = 94;
+
         /**
          * Cannot assign characteristics to the “{0}” property.
          */
@@ -422,6 +427,11 @@ public class Resources extends IndexedResourceBundle {
          */
         public static final short OutOfIteratorDomain_2 = 57;
 
+        /**
+         * Palette of {0,number,integer} colors
+         */
+        public static final short PaletteOfColors_1 = 96;
+
         /**
          * Point ({0}) is outside the coverage domain.
          */
@@ -447,6 +457,11 @@ public class Resources extends IndexedResourceBundle {
          */
         public static final short SourceImagesDoNotIntersect = 80;
 
+        /**
+         * {0} tiles of {1} pixels
+         */
+        public static final short TileCountAndSize_2 = 95;
+
         /**
          * Tile ({0}, {1}) is unavailable because of error in a previous 
calculation attempt.
          */
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.properties
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.properties
index 5f66a5a6ea..c7aa83a018 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.properties
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.properties
@@ -21,6 +21,7 @@
 #
 AbstractFeatureType_1             = Feature type \u2018{0}\u2019 is abstract.
 AmbiguousGridAxisOmission_1       = Omission of the \u201c{0}\u201d grid axes 
would create ambiguity.
+BandCount_1                       = {0,number,integer} band{0,choice,1#|2#s}
 CanNotAssignCharacteristics_1     = Cannot assign characteristics to the 
\u201c{0}\u201d property.
 CanNotBuildGridCoverage           = Cannot build the grid coverage.
 CanNotComputeTile_2               = Cannot compute tile ({0}, {1}).
@@ -88,11 +89,13 @@ NotStrictlyOrderedDimensions      = The specified 
dimensions are not in strictly
 OperationRequiresSingleBand       = This operation requires an image with only 
one band.
 OptionalLibraryNotFound_2         = The {0} optional library is not available. 
Geometric operations will ignore that library.\nCause is {1}.
 OutOfIteratorDomain_2             = The ({0,number}, {1,number}) pixel 
coordinate is outside iterator domain.
+PaletteOfColors_1                 = Palette of {0,number,integer} colors
 PointOutsideCoverageDomain_1      = Point ({0}) is outside the coverage domain.
 PropertyAlreadyExists_2           = Property \u201c{1}\u201d already exists in 
feature \u201c{0}\u201d.
 PropertyNameCannotBeXPath_1       = Property name \u201c{0}\u201d is invalid 
because names cannot be XPath.
 PropertyNotFound_2                = No property named \u201c{1}\u201d has been 
found in \u201c{0}\u201d feature.
 SourceImagesDoNotIntersect        = Source images do not intersect.
+TileCountAndSize_2                = {0} tiles of {1} pixels
 TileErrorFlagSet_2                = Tile ({0}, {1}) is unavailable because of 
error in a previous calculation attempt.
 TileNotWritable_2                 = Tile ({0}, {1}) is not writable.
 TooManyQualitatives               = Too many qualitative categories.
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources_fr.properties
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources_fr.properties
index 9ec75f2a56..d3d959c150 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources_fr.properties
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources_fr.properties
@@ -26,6 +26,7 @@
 #
 AbstractFeatureType_1             = Le type d\u2019entit\u00e9 \u2018{0}\u2019 
est abstrait.
 AmbiguousGridAxisOmission_1       = L\u2019omission de l\u2019axe 
\u201c{0}\u201d de la grille cr\u00e9e une ambigu\u00eft\u00e9.
+BandCount_1                       = {0,number,integer} bande{0,choice,1#|2#s}
 CanNotAssignCharacteristics_1     = Ne peut pas assigner des 
caract\u00e9ristiques \u00e0 la propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb.
 CanNotBuildGridCoverage           = Ne peut pas construire la couverture de 
donn\u00e9es.
 CanNotComputeTile_2               = Ne peut pas calculer la tuile ({0}, {1}).
@@ -93,11 +94,13 @@ NotStrictlyOrderedDimensions      = Les dimensions 
sp\u00e9cifi\u00e9es ne sont
 OperationRequiresSingleBand       = Cette op\u00e9ration n\u00e9cessite une 
image avec une seule bande.
 OptionalLibraryNotFound_2         = La biblioth\u00e8que optionnelle {0} 
n\u2019est pas disponible. Les op\u00e9rations g\u00e9om\u00e9triques 
ignoreront cette biblioth\u00e8que.\nLa cause est {1}.
 OutOfIteratorDomain_2             = La coordonn\u00e9e pixel ({0,number}, 
{1,number}) est en dehors du domaine de l\u2019it\u00e9rateur.
+PaletteOfColors_1                 = Palette de {0,number,integer} couleurs
 PointOutsideCoverageDomain_1      = Le point ({0}) est en dehors du domaine de 
la couverture de donn\u00e9es.
 PropertyAlreadyExists_2           = La propri\u00e9t\u00e9 
\u00ab\u202f{1}\u202f\u00bb existe d\u00e9j\u00e0 dans l\u2019entit\u00e9 
\u00ab\u202f{0}\u202f\u00bb.
 PropertyNameCannotBeXPath_1       = Le nom de propri\u00e9t\u00e9 
\u00ab\u202f{0}\u202f\u00bb est invalide parce que ces noms ne doivent pas 
\u00eatre des XPaths.
 PropertyNotFound_2                = Aucune propri\u00e9t\u00e9 nomm\u00e9e 
\u00ab\u202f{1}\u202f\u00bb n\u2019a \u00e9t\u00e9 trouv\u00e9e dans 
l\u2019entit\u00e9 \u00ab\u202f{0}\u202f\u00bb.
 SourceImagesDoNotIntersect        = Des images sources ne s\u2019intersectent 
pas.
+TileCountAndSize_2                = {0} tuiles de {1} pixels
 TileErrorFlagSet_2                = La tuile ({0}, {1}) est indisponible pour 
cause d\u2019erreur lors d\u2019une tentative ant\u00e9rieure de calcul.
 TileNotWritable_2                 = La tuile ({0}, {1}) n\u2019est pas 
d\u00e9clar\u00e9e en \u00e9criture.
 TooManyQualitatives               = Trop de cat\u00e9gories qualitatives.
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PlanarImage.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PlanarImage.java
index 38545fbc49..c6e50e198b 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PlanarImage.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PlanarImage.java
@@ -20,7 +20,6 @@ import java.awt.Image;
 import java.awt.Shape;
 import java.awt.Rectangle;
 import java.awt.image.ColorModel;
-import java.awt.image.IndexColorModel;
 import java.awt.image.SampleModel;
 import java.awt.image.Raster;
 import java.awt.image.WritableRaster;
@@ -33,12 +32,11 @@ import static java.lang.Math.multiplyFull;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.Disposable;
 import org.apache.sis.util.resources.Errors;
-import org.apache.sis.util.resources.Messages;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.coverage.grid.GridGeometry;       // For javadoc
 import org.apache.sis.image.internal.shared.ImageUtilities;
 import org.apache.sis.image.internal.shared.TileOpExecutor;
-import org.apache.sis.image.internal.shared.ColorModelFactory;
+import org.apache.sis.image.internal.shared.Summarizer;
 import org.apache.sis.feature.internal.Resources;
 import org.apache.sis.pending.jdk.JDK18;
 
@@ -734,59 +732,7 @@ public abstract class PlanarImage implements RenderedImage 
{
      */
     @Override
     public String toString() {
-        final var buffer = new 
StringBuilder(100).append(Classes.getShortClassName(this)).append('[');
-        final Object name = getProperty(SOURCE_NAME_KEY);
-        if (name != null && name != Image.UndefinedProperty) {
-            buffer.append('“').append(name).append("”: ");
-        }
-        buffer.append('(').append(getWidth()).append(" × 
").append(getHeight()).append(") pixels starting at ")
-              .append('(').append(getMinX()) .append(", ") 
.append(getMinY()).append(')');
-        final SampleModel sm = getSampleModel();
-        if (sm != null) {
-            buffer.append(" in ").append(sm.getNumBands()).append(" bands");
-            final String type = ImageUtilities.getDataTypeName(sm);
-            if (type != null) {
-                buffer.append(" of type ").append(type);
-            }
-        }
-        /*
-         * Write details about color model only if there is "useful" 
information for a geospatial raster.
-         * The main category of interest are "color palette" versus "gray 
scale" versus everything else,
-         * and whether the image may have transparent pixels.
-         */
-        final ColorModel cm = getColorModel();
-colors: if (cm != null) {
-            buffer.append(". Colors: ");
-            if (cm instanceof IndexColorModel) {
-                buffer.append(((IndexColorModel) cm).getMapSize()).append(" 
indexed colors");
-            } else {
-                ColorModelFactory.formatDescription(cm.getColorSpace(), 
buffer);
-            }
-            final String transparency;
-            switch (cm.getTransparency()) {
-                case ColorModel.OPAQUE:      transparency = "opaque"; break;
-                case ColorModel.TRANSLUCENT: transparency = "translucent"; 
break;
-                case ColorModel.BITMASK:     transparency = "bitmask 
transparency"; break;
-                default: break colors;
-            }
-            buffer.append("; ").append(transparency);
-        }
-        /*
-         * Tiling information last because it is usually a secondary aspect 
compared to above information.
-         * If a warning is emitted, it will usually be a tiling problem so it 
is useful to keep it close.
-         */
-        final int tx = getNumXTiles();
-        final int ty = getNumYTiles();
-        if (tx != 1 || ty != 1) {
-            buffer.append("; ").append(tx).append(" × ").append(ty).append(" 
tiles");
-        }
-        buffer.append(']');
-        final String error = verify();
-        if (error != null) {
-            buffer.append(System.lineSeparator()).append("└─")
-                  
.append(Messages.format(Messages.Keys.PossibleInconsistency_1, error));
-        }
-        return buffer.toString();
+        return Summarizer.toString(this);
     }
 
     /*
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ColorModelFactory.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ColorModelFactory.java
index da40bae51a..b127ef326b 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ColorModelFactory.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ColorModelFactory.java
@@ -41,6 +41,8 @@ import org.apache.sis.util.internal.shared.Numerics;
 import org.apache.sis.util.internal.shared.Strings;
 import org.apache.sis.util.collection.WeakHashSet;
 import org.apache.sis.util.collection.WeakValueHashMap;
+import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.feature.internal.Resources;
 
 
 /**
@@ -715,21 +717,55 @@ public final class ColorModelFactory {
     }
 
     /**
-     * Appends a description of the given color space in the given buffer.
+     * Appends a description of the given color model.
      * This is used for {@code toString()} method implementations.
      *
-     * @param  cs      the color space to describe, or {@code null}.
-     * @param  buffer  where to append the description.
+     * @param  cm         the color model to describe, or {@code null}.
+     * @param  resources  resources in the desired locale.
+     * @param  buffer     where to append the description if a buffer is 
needed.
+     * @return the description as {@code buffer} or as a {@link String}, or 
{@code null} if none.
      */
     @Debug
-    public static void formatDescription(final ColorSpace cs, final 
StringBuilder buffer) {
-        if (cs != null) {
-            if (cs instanceof ScaledColorSpace) {
-                ((ScaledColorSpace) cs).formatRange(buffer.append("showing "));
-            } else if (cs.getType() == ColorSpace.TYPE_GRAY) {
-                buffer.append("grayscale");
+    public static CharSequence formatDescription(final ColorModel cm, final 
Resources resources, final StringBuilder buffer) {
+        if (cm != null) {
+            if (cm instanceof IndexColorModel) {
+                return resources.getString(Resources.Keys.PaletteOfColors_1, 
((IndexColorModel) cm).getMapSize());
+            }
+            final ColorSpace cs = cm.getColorSpace();
+            if (cs != null) {
+                if (cs instanceof ScaledColorSpace) {
+                    return ((ScaledColorSpace) 
cs).formatRange(buffer.append("showing "));
+                } else if (cs.getType() == ColorSpace.TYPE_GRAY) {
+                    return 
Vocabulary.forLocale(resources.getLocale()).getString(Vocabulary.Keys.Grayscale);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the key of a localizable text that describes the transparency.
+     *
+     * @param  cm  the color model from which to get the transparency, or 
{@code null}.
+     * @param  resources  resources in the desired locale.
+     * @return a description of the transparency, or {@code null} if unknown.
+     */
+    public static String describeTransparency(final ColorModel cm, final 
Resources resources) {
+        if (cm == null) {
+            return null;
+        }
+        final short key;
+        if (cm.hasAlpha()) {
+            key = Resources.Keys.ImageHasAlphaChannel;
+        } else {
+            switch (cm.getTransparency()) {
+                case ColorModel.TRANSLUCENT:
+                case ColorModel.BITMASK: key = 
Resources.Keys.ImageAllowsTransparency; break;
+                case ColorModel.OPAQUE:  key = Resources.Keys.ImageIsOpaque; 
break;
+                default: return null;
             }
         }
+        return resources.getString(key);
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ImageUtilities.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ImageUtilities.java
index e52c1a543b..137751b326 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ImageUtilities.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ImageUtilities.java
@@ -35,7 +35,6 @@ import static java.lang.Math.rint;
 import static java.lang.Math.floorDiv;
 import static java.lang.Math.toIntExact;
 import static java.lang.Math.multiplyFull;
-import org.apache.sis.feature.internal.Resources;
 import org.apache.sis.image.DataType;
 import org.apache.sis.image.PlanarImage;
 import org.apache.sis.system.Modules;
@@ -204,35 +203,6 @@ public final class ImageUtilities {
         return null;
     }
 
-    /**
-     * Returns the key of a localizable text that describes the transparency.
-     * This method returns one of the following values:
-     * <ul>
-     *   <li>{@link Resources.Keys#ImageAllowsTransparency}</li>
-     *   <li>{@link Resources.Keys#ImageHasAlphaChannel}</li>
-     *   <li>{@link Resources.Keys#ImageIsOpaque}</li>
-     *   <li>0 if the transparency is unknown.</li>
-     * </ul>
-     *
-     * @param  cm  the color model from which to get the transparency, or 
{@code null}.
-     * @return a {@link Resources.Keys} value for the transparency, or 0 if 
unknown.
-     *
-     * @see #hasAlpha(RenderedImage)
-     */
-    public static short getTransparencyDescription(final ColorModel cm) {
-        if (cm != null) {
-            if (cm.hasAlpha()) {
-                return Resources.Keys.ImageHasAlphaChannel;
-            }
-            switch (cm.getTransparency()) {
-                case ColorModel.TRANSLUCENT:
-                case ColorModel.BITMASK: return 
Resources.Keys.ImageAllowsTransparency;
-                case ColorModel.OPAQUE:  return Resources.Keys.ImageIsOpaque;
-            }
-        }
-        return 0;
-    }
-
     /**
      * Returns names of bands based on inspection of the sample model and 
color model.
      * The bands are identified by {@link Vocabulary.Keys} values for
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ScaledColorSpace.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ScaledColorSpace.java
index 6ec5cdbb07..b5cb1e923a 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ScaledColorSpace.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ScaledColorSpace.java
@@ -176,9 +176,7 @@ final class ScaledColorSpace extends ColorSpace {
      */
     @Override
     public String toString() {
-        final StringBuilder buffer = new 
StringBuilder(20).append(getClass().getSimpleName());
-        formatRange(buffer);
-        return buffer.toString();
+        return formatRange(new 
StringBuilder(20).append(getClass().getSimpleName())).toString();
     }
 
     /**
@@ -186,10 +184,11 @@ final class ScaledColorSpace extends ColorSpace {
      * This method is used for {@link #toString()} implementation and may 
change in any future version.
      *
      * @param  buffer  where to append the range of values.
+     * @return {@code buffer}.
      */
     @Debug
-    final void formatRange(final StringBuilder buffer) {
-        buffer.append('[').append(offset)
+    final StringBuilder formatRange(final StringBuilder buffer) {
+        return buffer.append('[').append(offset)
             .append(" … ").append(maximum)
             .append(" in band ").append(visibleBand).append(']');
     }
@@ -210,7 +209,7 @@ final class ScaledColorSpace extends ColorSpace {
     @Override
     public boolean equals(final Object obj) {
         if (obj instanceof ScaledColorSpace) {
-            final ScaledColorSpace that = (ScaledColorSpace) obj;
+            final var that = (ScaledColorSpace) obj;
             return Numerics.equals(scale,   that.scale)             &&
                    Numerics.equals(offset,  that.offset)            &&
                    Numerics.equals(maximum, that.maximum)           &&
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/Summarizer.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/Summarizer.java
new file mode 100644
index 0000000000..87b25accda
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/Summarizer.java
@@ -0,0 +1,288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.image.internal.shared;
+
+import java.util.Locale;
+import java.text.NumberFormat;
+import java.awt.Image;
+import java.awt.image.RenderedImage;
+import org.apache.sis.image.PlanarImage;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.Classes;
+import org.apache.sis.util.Localized;
+import org.apache.sis.util.collection.DefaultTreeTable;
+import org.apache.sis.util.collection.TableColumn;
+import org.apache.sis.util.collection.TreeTable;
+import org.apache.sis.util.resources.Messages;
+import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.feature.internal.Resources;
+
+
+/**
+ * Helper class for formatting a description of an image.
+ * Used mostly for implementation of {@code toString()} methods for debugging 
purposes.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+public final class Summarizer implements Localized {
+    /**
+     * Root of the tree where to add a branch.
+     */
+    private final TreeTable.Node root;
+
+    /**
+     * Localized resources for vocabulary.
+     */
+    private final Vocabulary vocabulary;
+
+    /**
+     * The single column where to write texts.
+     */
+    private final TableColumn<CharSequence> column;
+
+    /**
+     * Creates a new formatter for appending the image description in an 
existing tree.
+     *
+     * @param  root        root of the tree where to add a branch.
+     * @param  vocabulary  localized resources for vocabulary.
+     * @param  column      the single column where to write texts.
+     */
+    public Summarizer(final TreeTable.Node root, final Vocabulary vocabulary, 
final TableColumn<CharSequence> column) {
+        this.root       = root;
+        this.vocabulary = vocabulary;
+        this.column     = column;
+    }
+
+    /**
+     * Returns the locale used by this formatter.
+     *
+     * @return the locale.
+     */
+    @Override
+    public final Locale getLocale() {
+        return vocabulary.getLocale();
+    }
+
+    /**
+     * A row to format.
+     */
+    private static final class Row {
+        /** A {@link Vocabulary.Keys} constant, or 0 if none. */
+        private final short key;
+
+        /** Label created from the vocabulary key. */
+        private String label;
+
+        /** Description to write after the label. */
+        private final String desc;
+
+        /** Optional note as a child of the description. */
+        String note;
+
+        /** Prepares a new node. */
+        Row(final short key, final CharSequence desc) {
+            this.key  = key;
+            this.desc = desc.toString();
+        }
+
+        /** Resolves {@code #label} and returns its length. */
+        final int formatLabel(final Vocabulary vocabulary) {
+            if (key == 0) return 0;
+            label = vocabulary.getLabel(key);
+            return label.length();
+        }
+
+        /** Returns a string representation using the given buffer as a 
temporary buffer. */
+        final String toString(final StringBuilder buffer, final int margin) {
+            if (label != null) {
+                buffer.append(label).append(CharSequences.spaces(margin - 
label.length()));
+            }
+            return buffer.append(desc).toString();
+        }
+    }
+
+    /**
+     * Appends a "data layout" branch to the tree representation of an image 
description.
+     * Contains information about image location, image size, tile size, 
sample model, color model.
+     *
+     * @param  data  the image to describe.
+     */
+    public void layout(final RenderedImage data) {
+        final int width     = data.getWidth();
+        final int height    = data.getHeight();
+        final var model     = data.getSampleModel();
+        final var nf        = NumberFormat.getIntegerInstance(getLocale());
+        final var resources = Resources.forLocale(getLocale());
+        final var buffer    = new StringBuilder();
+        final var rows      = new Row[8];
+        int count = 0;
+        for (int c = 0; c < rows.length; c++) {
+            final Row row;
+            switch (c) {
+                /*
+                 * Image name defined by a property (optional).
+                 */
+                case 0: {
+                    final Object name = 
data.getProperty(PlanarImage.SOURCE_NAME_KEY);
+                    if (name == null || name == Image.UndefinedProperty) 
continue;
+                    row = new Row(Vocabulary.Keys.Name,
+                                  
vocabulary.getString(Vocabulary.Keys.Quoted_1, name));
+                    break;
+                }
+                /*
+                 * Image location, omitted in the common case of image 
starting at (0, 0).
+                 */
+                case 1: {
+                    final int x = data.getMinX();
+                    final int y = data.getMinY();
+                    if ((x | y) == 0) continue;
+                    row = new Row(Vocabulary.Keys.Origin,
+                                  buffer.append(nf.format(x)).append(", ")
+                                        .append(nf.format(y)));
+                    break;
+                }
+                /*
+                 * Image size and number of bands, always shown.
+                 * The sample model should never be null, but we check anyway 
for safety.
+                 */
+                case 2: {
+                    appendCount(buffer, nf, width, height).append(" pixels");
+                    if (model != null) {
+                        buffer.append(" × ")
+                              
.append(resources.getString(Resources.Keys.BandCount_1, model.getNumBands()));
+                    }
+                    row = new Row(Vocabulary.Keys.ImageSize, buffer);
+                    break;
+                }
+                /*
+                 * Image tiling, omitted if the properties are consistent with 
an untiled image.
+                 */
+                case 3: {
+                    final int nx = data.getNumXTiles();
+                    final int ny = data.getNumYTiles();
+                    final int tx = data.getTileWidth();
+                    final int ty = data.getTileHeight();
+                    if (nx == 1 && ny == 1 && tx == width && ty == height) 
continue;
+                    final String n = appendCount(buffer, nf, nx, 
ny).toString(); buffer.setLength(0);
+                    final String s = appendCount(buffer, nf, tx, 
ty).toString();
+                    row = new Row(Vocabulary.Keys.Tiling, 
resources.getString(Resources.Keys.TileCountAndSize_2, n, s));
+                    break;
+                }
+                /*
+                 * Number of bits for each band and the type of number where 
they are stored.
+                 */
+                case 4: {
+                    if (model == null) continue;
+                    int[] size = model.getSampleSize();
+                    buffer.append('{');
+                    for (int i=0; i < size.length; i++) {
+                        if (i != 0) buffer.append(", ");
+                        buffer.append(nf.format(size[i]));
+                    }
+                    buffer.append("} bits");
+                    String type = ImageUtilities.getDataTypeName(model);
+                    if (type != null) {
+                        
buffer.append(vocabulary.getString(Vocabulary.Keys.InBetweenWords))
+                              .append('‘').append(type).append('’');
+                    }
+                    row = new Row(Vocabulary.Keys.DataType, buffer);
+                    break;
+                }
+                /*
+                 * Sample model (mandatory). If the sample model is 
nevertheless missing,
+                 * some placeholder will be shown for suggesting that there is 
a problem.
+                 */
+                case 5: {
+                    row = new Row(Vocabulary.Keys.Layout,
+                                  
CharSequences.camelCaseToSentence(Classes.getShortClassName(model)));
+                    break;
+                }
+                /*
+                 * Color model (optional) with a note about transparency,
+                 * which is an important information for images overlay.
+                 */
+                case 6: {
+                    final var cm = data.getColorModel();
+                    CharSequence desc = 
ColorModelFactory.formatDescription(cm, resources, buffer);
+                    String note = ColorModelFactory.describeTransparency(cm, 
resources);
+                    if (desc == null) {
+                        if (note == null) continue;
+                        desc = note;
+                        note = null;
+                    }
+                    row = new Row(Vocabulary.Keys.Colors, desc);
+                    row.note = note;
+                    break;
+                }
+                /*
+                 * Warning about potential inconsistency.
+                 */
+                case 7: {
+                    if (!(data instanceof PlanarImage)) continue;
+                    String warning = ((PlanarImage) data).verify();
+                    if (warning == null) continue;
+                    row = new Row((short) 0,
+                            
Messages.forLocale(getLocale()).getString(Messages.Keys.PossibleInconsistency_1,
 warning));
+                    break;
+                }
+                /*
+                 * End of rows.
+                 */
+                default: continue;
+            }
+            rows[count++] = row;
+            buffer.setLength(0);
+        }
+        int margin = 0;
+        for (int i=0; i<count; i++) {
+            margin = Math.max(margin, rows[i].formatLabel(vocabulary));
+        }
+        margin++;
+        for (int i=0; i<count; i++) {
+            final Row row = rows[i];
+            final TreeTable.Node child = root.newChild();
+            child.setValue(column, row.toString(buffer, margin));
+            buffer.setLength(0);
+            if (row.note != null) {
+                child.newChild().setValue(column, row.note);
+            }
+        }
+    }
+
+    /**
+     * Appends an image size, tile size or number of tiles.
+     */
+    private static StringBuilder appendCount(StringBuilder buffer, 
NumberFormat nf, int nx, int ny) {
+        return buffer.append(  '(').append(nf.format(nx))
+                     .append(" × ").append(nf.format(ny)).append(')');
+    }
+
+    /**
+     * Returns a summary of the given image as a tree.
+     *
+     * @param  image  the image to summarize.
+     * @return a tree presentation of the properties of the image.
+     */
+    public static String toString(final RenderedImage image) {
+        final TableColumn<CharSequence> column = TableColumn.VALUE_AS_TEXT;
+        final var tree = new DefaultTreeTable(column);
+        final var c = new Summarizer(tree.getRoot(), 
Vocabulary.forLocale(null), column);
+        c.root.setValue(column, Classes.getShortClassName(image));
+        c.layout(image);
+        return tree.toString();
+    }
+}
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridExtentTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridExtentTest.java
index 1eea817b3c..e6d97c55e4 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridExtentTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridExtentTest.java
@@ -84,6 +84,16 @@ public final class GridExtentTest extends TestCase {
                 false);
     }
 
+    /**
+     * Verifies that the given extent has the dimension name types specified 
by {@link #create3D()}.
+     */
+    private static void verifyDimensions(final GridExtent extent) {
+        assertEquals(3, extent.getDimension(), "dimension");
+        assertEquals(DimensionNameType.COLUMN, extent.getAxisType(0).get());
+        assertEquals(DimensionNameType.ROW,    extent.getAxisType(1).get());
+        assertEquals(DimensionNameType.TIME,   extent.getAxisType(2).get());
+    }
+
     /**
      * Verifies the low and high values in the specified dimension of the 
given extent
      */
@@ -99,11 +109,12 @@ public final class GridExtentTest extends TestCase {
      */
     @Test
     public void testSubsample() {
-        GridExtent extent = create3D();
+        GridExtent extent = create3D();             // low = (100, 200, 40) 
and size = (400 × 600 × 10).
         extent = extent.subsample(4, 3, 9);
-        assertExtentEquals(extent, 0, 25, 124);                 // 100 cells
-        assertExtentEquals(extent, 1, 66, 265);                 // 200 cells
-        assertExtentEquals(extent, 2,  4,   5);                 //   2 cells
+        verifyDimensions(extent);
+        assertExtentEquals(extent, 0, 25, 124);     // 100 cells, exact 
division of everything.
+        assertExtentEquals(extent, 1, 66, 265);     // 200 cells, exact 
division of size, truncation of low coordinate.
+        assertExtentEquals(extent, 2,  4,   5);     //   2 cells, size rounded 
upward, low coordinate truncated.
     }
 
     /**
@@ -111,11 +122,12 @@ public final class GridExtentTest extends TestCase {
      */
     @Test
     public void testUpsample() {
-        GridExtent extent = create3D();
+        GridExtent extent = create3D();             // low = (100, 200, 40) 
and size = (400 × 600 × 10).
         extent = extent.upsample(4, 3, 9);
-        assertExtentEquals(extent, 0, 400, 1999);               // 1600 cells
-        assertExtentEquals(extent, 1, 600, 2399);               // 1800 cells
-        assertExtentEquals(extent, 2, 360,  449);               //   90 cells
+        verifyDimensions(extent);
+        assertExtentEquals(extent, 0, 400, 1999);   // 1600 cells.
+        assertExtentEquals(extent, 1, 600, 2399);   // 1800 cells.
+        assertExtentEquals(extent, 2, 360,  449);   //   90 cells.
     }
 
     /**
@@ -232,6 +244,7 @@ public final class GridExtentTest extends TestCase {
         GridExtent extent = create3D();
         assertSame(extent, extent.withRange(1, 200, 799));
         extent = extent.withRange(2, 30, 60);
+        verifyDimensions(extent);
         assertExtentEquals(extent, 0, 100, 499);
         assertExtentEquals(extent, 1, 200, 799);
         assertExtentEquals(extent, 2,  30,  60);
@@ -245,6 +258,7 @@ public final class GridExtentTest extends TestCase {
         GridExtent extent = create3D();
         assertSame(extent, extent.expand(new long[3]));
         extent = extent.expand(20, -10);            // One less dimension than 
`exent` dimension.
+        verifyDimensions(extent);
         assertExtentEquals(extent, 0,  80, 519);
         assertExtentEquals(extent, 1, 210, 789);
         assertExtentEquals(extent, 2,  40,  49);
@@ -257,6 +271,7 @@ public final class GridExtentTest extends TestCase {
     public void testForChunkSize() {
         GridExtent extent = create3D();
         extent = extent.forChunkSize(300, 200, 15);
+        verifyDimensions(extent);
         assertExtentEquals(extent, 0,   0, 599);
         assertExtentEquals(extent, 1, 200, 799);
         assertExtentEquals(extent, 2,  30,  59);
@@ -269,6 +284,7 @@ public final class GridExtentTest extends TestCase {
     public void testResize() {
         GridExtent extent = create3D();
         extent = extent.resize(200, 150);
+        verifyDimensions(extent);
         assertExtentEquals(extent, 0, 50, 249);
         assertExtentEquals(extent, 1, 50, 199);
         assertExtentEquals(extent, 2, 40,  49);
@@ -322,6 +338,7 @@ public final class GridExtentTest extends TestCase {
     public void testIntersect() {
         final GridExtent domain = createOther();
         final GridExtent extent = create3D().intersect(domain);
+        verifyDimensions(extent);
         assertExtentEquals(extent, 0, 150, 399);
         assertExtentEquals(extent, 1, 220, 799);
         assertExtentEquals(extent, 2, 40,  46);
@@ -339,6 +356,7 @@ public final class GridExtentTest extends TestCase {
     public void testUnion() {
         final GridExtent domain = createOther();
         final GridExtent extent = create3D().union(domain);
+        verifyDimensions(extent);
         assertExtentEquals(extent, 0, 100, 499);
         assertExtentEquals(extent, 1, 200, 819);
         assertExtentEquals(extent, 2, 35,  49);
@@ -389,7 +407,7 @@ public final class GridExtentTest extends TestCase {
         final var slicePoint = new GeneralDirectPosition(226.7, 47.2);
         final GridExtent extent = create3D();
         final GridExtent slice  = extent.slice(slicePoint, new int[] {1, 2});
-        assertEquals(3, slice.getDimension(), "dimension");
+        verifyDimensions(slice);
         assertExtentEquals(slice, 0, 100, 499);
         assertExtentEquals(slice, 1, 227, 227);
         assertExtentEquals(slice, 2,  47,  47);
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/IndexedResourceBundle.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/IndexedResourceBundle.java
index 8e5dc79eec..034f41e792 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/IndexedResourceBundle.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/IndexedResourceBundle.java
@@ -482,13 +482,7 @@ public abstract class IndexedResourceBundle extends 
ResourceBundle implements Lo
      * @throws IOException if an error occurred while writing to the given 
destination.
      */
     public final void appendLabel(final short key, final Appendable 
toAppendTo) throws IOException {
-        toAppendTo.append(getString(key));
-        final String colon = colon();
-        if (colon != null) {
-            toAppendTo.append(colon);
-        } else {
-            toAppendTo.append(':');
-        }
+        toAppendTo.append(getString(key)).append(colon());
     }
 
     /**
@@ -510,9 +504,7 @@ public abstract class IndexedResourceBundle extends 
ResourceBundle implements Lo
      * @return localized string followed by a colon.
      */
     public final String getLabel(final short key) {
-        final String text = getString(key);
-        final String colon = colon();
-        return (colon != null) ? (text + colon) : (text + ':');
+        return toLabel(getString(key));
     }
 
     /**
@@ -521,7 +513,7 @@ public abstract class IndexedResourceBundle extends 
ResourceBundle implements Lo
      * @todo Should be a localized resource by itself.
      */
     private String colon() {
-        return Locale.FRENCH.getLanguage().equals(getLocale().getLanguage()) ? 
"\u00A0:" : null;
+        return Locale.FRENCH.getLanguage().equals(getLocale().getLanguage()) ? 
"\u00A0:" : ":";
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.java
index 5621f15b1d..d054704d1d 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.java
@@ -653,11 +653,6 @@ public class Vocabulary extends IndexedResourceBundle {
          */
         public static final short Image = 102;
 
-        /**
-         * Image layout
-         */
-        public static final short ImageLayout = 103;
-
         /**
          * Image size
          */
@@ -1138,6 +1133,11 @@ public class Vocabulary extends IndexedResourceBundle {
          */
         public static final short RemoteConfiguration = 170;
 
+        /**
+         * Rendered image
+         */
+        public static final short RenderedImage = 103;
+
         /**
          * Representative value
          */
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.properties
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.properties
index 2ce3cf511b..39ccbb51ac 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.properties
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.properties
@@ -134,7 +134,6 @@ Identity                = Identity
 Image                   = Image
 Image_1                 = Image #{0}
 ImageSize               = Image size
-ImageLayout             = Image layout
 Implementation          = Implementation
 InBetweenWords          = \u2002in\u2002
 Index                   = Index
@@ -229,6 +228,7 @@ Reflectance             = Reflectance
 Reflective              = Reflective
 Remarks                 = Remarks
 RemoteConfiguration     = Remote configuration
+RenderedImage           = Rendered image
 RepresentativeValue     = Representative value
 Resolution              = Resolution
 ResourceIdentification  = Resource identification
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary_fr.properties
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary_fr.properties
index 0a28ebff61..fee589028b 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary_fr.properties
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary_fr.properties
@@ -141,7 +141,6 @@ Identity                = Identit\u00e9
 Image                   = Image
 Image_1                 = Image {0}
 ImageSize               = Taille de l\u2019image
-ImageLayout             = Agencement de l\u2019image
 Implementation          = Impl\u00e9mentation
 InBetweenWords          = \u2002dans\u2002
 Index                   = Index
@@ -157,7 +156,7 @@ Julian                  = Julien
 Latitude                = Latitude
 Longitude               = Longitude
 Layers                  = Couches
-Layout                  = Disposition
+Layout                  = Agencement
 Legend                  = L\u00e9gende
 Level                   = Niveau
 Libraries               = Biblioth\u00e8ques
@@ -236,6 +235,7 @@ Reflectance             = R\u00e9flectance
 Reflective              = R\u00e9fl\u00e9chissant
 Remarks                 = Remarques
 RemoteConfiguration     = Configuration distante
+RenderedImage           = Image rendue
 RepresentativeValue     = Valeur repr\u00e9sentative
 Resolution              = R\u00e9solution
 ResourceIdentification  = Identification de la ressource

Reply via email to