You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2020/02/23 16:47:15 UTC

[sis] branch geoapi-4.0 updated (2d4382a -> 3195e47)

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

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


    from 2d4382a  Add transparency support in images backed by ScaledColorSpace.
     new 0b64c9b  Consolidation of ScaledColorSpace / ScaledColorModel.
     new 3195e47  Reduce the number of Locale object we carry. They are not needed at this time since the application is shown in default locale only, and the information is tedious to transfer between various objects.

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../apache/sis/gui/coverage/CoverageExplorer.java  |   7 +-
 .../org/apache/sis/gui/coverage/CoverageView.java  |   6 +-
 .../java/org/apache/sis/gui/coverage/GridView.java |   2 +-
 .../org/apache/sis/gui/coverage/StatusBar.java     |   3 +-
 .../org/apache/sis/gui/dataset/DataWindow.java     |  11 +--
 .../org/apache/sis/gui/dataset/FeatureTable.java   |  11 +--
 .../org/apache/sis/gui/dataset/ResourceTree.java   |   2 +-
 .../apache/sis/gui/metadata/MetadataSummary.java   |  14 +--
 .../org/apache/sis/gui/metadata/MetadataTree.java  |  11 +--
 .../org/apache/sis/internal/gui/ToolbarButton.java |   4 +-
 .../internal/coverage/j2d/ColorModelFactory.java   |  37 ++------
 .../internal/coverage/j2d/ScaledColorModel.java    | 102 +++++++--------------
 .../internal/coverage/j2d/ScaledColorSpace.java    |  79 ++++++----------
 .../java/org/apache/sis/internal/map/Canvas.java   |   2 +-
 14 files changed, 89 insertions(+), 202 deletions(-)


[sis] 01/02: Consolidation of ScaledColorSpace / ScaledColorModel.

Posted by de...@apache.org.
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

commit 0b64c9b5b179dc7564d1ae4023a93a1320c601b8
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sun Feb 23 17:04:53 2020 +0100

    Consolidation of ScaledColorSpace / ScaledColorModel.
---
 .../internal/coverage/j2d/ColorModelFactory.java   |  37 ++------
 .../internal/coverage/j2d/ScaledColorModel.java    | 102 +++++++--------------
 .../internal/coverage/j2d/ScaledColorSpace.java    |  79 ++++++----------
 3 files changed, 70 insertions(+), 148 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java
index 90d0296..2bc73d4 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java
@@ -227,14 +227,7 @@ public final class ColorModelFactory {
          * fallback on a generic (but very slow!) color model.
          */
         if (type != DataBuffer.TYPE_BYTE && type != DataBuffer.TYPE_USHORT) {
-            final ColorSpace colors = createColorSpace(numBands, visibleBand, minimum, maximum);
-            final ComponentColorModel cm;
-            if (colors instanceof ScaledColorSpace) {
-                cm = new ScaledColorModel((ScaledColorSpace) colors, minimum, maximum, type);
-            } else {
-                cm = new ComponentColorModel(colors, false, false, Transparency.OPAQUE, type);
-            }
-            return unique(cm);
+            return createGrayScale(type, numBands, visibleBand, minimum, maximum);
         }
         /*
          * If there is no category, constructs a gray scale palette.
@@ -406,34 +399,22 @@ public final class ColorModelFactory {
      * @param  visibleBand    the band to use for computing colors.
      * @param  minimum        the minimal sample value expected.
      * @param  maximum        the maximal sample value expected.
-     * @return the color space for the given range of values.
+     * @return the color model for the given range of values.
      *
      * @see RasterFactory#createGrayScaleImage(int, int, int, int, int, double, double)
      */
     public static ColorModel createGrayScale(final int dataType, final int numComponents,
             final int visibleBand, final double minimum, final double maximum)
     {
-        final ColorSpace cs = createColorSpace(numComponents, visibleBand, minimum, maximum);
-        return new ComponentColorModel(cs, false, false, Transparency.OPAQUE, dataType);
-    }
-
-    /**
-     * Returns a color space for images storing pixels as real numbers. The color space can have an arbitrary number of bands,
-     * but in current implementation only one band is used. Current implementation create a gray scale.
-     *
-     * <p>The use of this color space is very slow. It should be used only when no standard color space can be used.</p>
-     *
-     * @param  numComponents  the number of components.
-     * @param  visibleBand    the band to use for computing colors.
-     * @param  minimum        the minimal sample value expected.
-     * @param  maximum        the maximal sample value expected.
-     * @return the color space for the given range of values.
-     */
-    public static ColorSpace createColorSpace(final int numComponents, final int visibleBand, final double minimum, final double maximum) {
+        final ColorModel cm;
         if (numComponents == 1 && minimum == 0 && maximum == 1) {
-            return ColorSpace.getInstance(ColorSpace.CS_GRAY);
+            final ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
+            cm = new ComponentColorModel(cs, false, false, Transparency.OPAQUE, dataType);
+        } else {
+            final ScaledColorSpace cs = new ScaledColorSpace(numComponents, visibleBand, minimum, maximum);
+            cm = new ScaledColorModel(cs, dataType);
         }
-        return new ScaledColorSpace(numComponents, visibleBand, minimum, maximum).unique();
+        return unique(cm);
     }
 
     /**
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorModel.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorModel.java
index e63c920..ab6fc8a 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorModel.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorModel.java
@@ -20,7 +20,6 @@ import java.awt.Transparency;
 import java.awt.image.DataBuffer;
 import java.awt.image.ComponentColorModel;
 import java.awt.image.RasterFormatException;
-import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.internal.feature.Resources;
 
 
@@ -29,7 +28,7 @@ import org.apache.sis.internal.feature.Resources;
  * This color model is slightly more efficient than the default {@link ComponentColorModel} by
  * reducing the amount of object allocations, made possible by the knowledge that we use only
  * one sample value and returns only one color component (the gray).
- * In addition, this class render the {@link Float#NaN} values as transparent.
+ * In addition, this class renders the {@link Float#NaN} values as transparent.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.1
@@ -38,65 +37,45 @@ import org.apache.sis.internal.feature.Resources;
  */
 final class ScaledColorModel extends ComponentColorModel {
     /**
-     * Index of the band to display. This is a copy of {@link ScaledColorSpace#visibleBand}.
+     * The mask to apply for getting a single color component.
+     * This is also the maximum (inclusive) color/alpha value.
      */
-    private final int visibleBand;
+    private static final int MASK = 0xFF;
 
     /**
-     * The scaling factor from sample values to RGB normalized values.
-     * This information duplicates {@link ScaledColorSpace#scale} but
-     * for a scale from 0 to 256 instead than 0 to 1.
+     * Size of the range of color values. Since minimum value is zero,
+     * this range is also the maximum (exclusive) color/alpha value.
      */
-    private final double scale;
+    static final int RANGE = 0x100;
 
     /**
-     * The offset to subtract from sample values before to apply the {@linkplain #scale} factor.
-     * This information duplicates {@link ScaledColorSpace#offset} but for a scale from 0 to 256
-     * instead than 0 to 1.
+     * The object from which to get the coefficients for converting values to colors.
+     * This is a reference to the same object than {@link #getColorSpace()}, but stored
+     * as a {@link ScaledColorSpace} for avoiding the need to perform casts.
      */
-    private final double offset;
+    private final ScaledColorSpace cs;
 
     /**
-     * Creates a new color model.
+     * Creates a new color model for the range of values in the given color space.
      *
-     * @param  colorSpace  the color space to use with this color model.
-     * @param  minimum     the minimal sample value expected, inclusive.
-     * @param  maximum     the maximal sample value expected, exclusive.
-     * @param  type        one of the {@link DataBuffer} constants.
+     * @param  colorSpace   the color space to use with this color model.
+     * @param  type         one of the {@link DataBuffer} constants.
      */
-    ScaledColorModel(final ScaledColorSpace colorSpace, final double minimum, final double maximum, final int type) {
+    ScaledColorModel(final ScaledColorSpace colorSpace, final int type) {
         super(colorSpace, false, false, Transparency.BITMASK, type);
-        visibleBand = colorSpace.visibleBand;
-        scale  = 0x100 / (maximum - minimum);
-        offset = minimum;
+        cs = colorSpace;
     }
 
     /**
-     * Returns the red component of the given value.
      * Defined for consistency but should not be used.
      */
-    @Override
-    public int getRed(final Object inData) {
-        return getRGB(inData) & 0xFF;
-    }
-
-    /**
-     * Returns the green component of the given value.
-     * Defined for consistency but should not be used.
-     */
-    @Override
-    public int getGreen(final Object inData) {
-        return (getRGB(inData) >>> Byte.SIZE) & 0xFF;
-    }
-
-    /**
-     * Returns the green component of the given value.
-     * Defined for consistency but should not be used.
-     */
-    @Override
-    public int getBlue(final Object inData) {
-        return (getRGB(inData) >>> 2*Byte.SIZE) & 0xFF;
-    }
+    @Override public int getRed  (int    value) {return (getRGB(value) >>> 2*Byte.SIZE) & MASK;}
+    @Override public int getRed  (Object value) {return (getRGB(value) >>> 2*Byte.SIZE) & MASK;}
+    @Override public int getGreen(int    value) {return (getRGB(value) >>>   Byte.SIZE) & MASK;}
+    @Override public int getGreen(Object value) {return (getRGB(value) >>>   Byte.SIZE) & MASK;}
+    @Override public int getBlue (int    value) {return  getRGB(value)                  & MASK;}
+    @Override public int getBlue (Object value) {return  getRGB(value)                  & MASK;}
+    @Override public int getAlpha(int    value) {return                                   MASK;}
 
     /**
      * Returns the alpha value for the given sample values.
@@ -104,10 +83,11 @@ final class ScaledColorModel extends ComponentColorModel {
      */
     @Override
     public int getAlpha(final Object inData) {
+        final int visibleBand = cs.visibleBand;
         switch (transferType) {
-            case DataBuffer.TYPE_FLOAT:  return Float .isNaN(((float[])  inData)[visibleBand]) ? 0 : 0xFF;
-            case DataBuffer.TYPE_DOUBLE: return Double.isNaN(((double[]) inData)[visibleBand]) ? 0 : 0xFF;
-            default: return 0xFF;
+            case DataBuffer.TYPE_FLOAT:  return Float .isNaN(((float[])  inData)[visibleBand]) ? 0 : MASK;
+            case DataBuffer.TYPE_DOUBLE: return Double.isNaN(((double[]) inData)[visibleBand]) ? 0 : MASK;
+            default: return MASK;
         }
     }
 
@@ -116,6 +96,7 @@ final class ScaledColorModel extends ComponentColorModel {
      */
     @Override
     public int getRGB(final Object inData) {
+        final int visibleBand = cs.visibleBand;
         final double value;
         switch (transferType) {
             case DataBuffer.TYPE_BYTE:   value = Byte .toUnsignedInt(((byte[])   inData)[visibleBand]); break;
@@ -129,31 +110,16 @@ final class ScaledColorModel extends ComponentColorModel {
         if (Double.isNaN(value)) {
             return 0;                                           // Transparent pixel.
         }
-        final int c = Math.max(0, Math.min(0xFF, (int) ((value - offset) * scale)));
-        return c | (c << Byte.SIZE) | (c << 2*Byte.SIZE) | 0xFF000000;
-    }
-
-    /**
-     * Returns a hash code value for this color model.
-     */
-    @Override
-    public int hashCode() {
-        return Long.hashCode(Double.doubleToLongBits(scale)
-                      + 31 * Double.doubleToLongBits(offset))
-                      +  7 * getNumComponents() + visibleBand;
+        final int c = Math.max(0, Math.min(MASK, (int) ((value - cs.offset) * cs.scale)));
+        return c | (c << Byte.SIZE) | (c << 2*Byte.SIZE) | (MASK << 3*Byte.SIZE);
     }
 
     /**
-     * Compares this color model with the given object for equality.
+     * Returns the color/alpha components of the pixel.
      */
     @Override
-    public boolean equals(final Object other) {
-        if (other instanceof ScaledColorModel && super.equals(other)) {
-            final ScaledColorModel that = (ScaledColorModel) other;
-            return visibleBand == that.visibleBand
-                    && Numerics.equals(scale,  that.scale)
-                    && Numerics.equals(offset, that.offset);
-        }
-        return false;
+    public int getRGB(final int value) {
+        final int c = Math.max(0, Math.min(MASK, (int) ((value - cs.offset) * cs.scale)));
+        return c | (c << Byte.SIZE) | (c << 2*Byte.SIZE) | (MASK << 3*Byte.SIZE);
     }
 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorSpace.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorSpace.java
index 9722fb4..13ccfa3 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorSpace.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorSpace.java
@@ -19,13 +19,11 @@ package org.apache.sis.internal.coverage.j2d;
 import java.awt.color.ColorSpace;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.util.Debug;
-import org.apache.sis.util.collection.WeakHashSet;
 
 
 /**
- * Color space for images storing pixels as real numbers. The color space can have an
- * arbitrary number of bands, but in current implementation only one band is used.
- * Current implementation creates a gray scale.
+ * Color space for images storing pixels as real numbers. This color space can have an arbitrary number of bands,
+ * but only one band is shown. Current implementation produces grayscale image only, but it may change in future.
  *
  * <p>The use of this color space is very slow.
  * It should be used only when no standard color space can be used.</p>
@@ -34,7 +32,7 @@ import org.apache.sis.util.collection.WeakHashSet;
  * @version 1.1
  *
  * @see ScaledColorModel
- * @see ColorModelFactory#createColorSpace(int, int, double, double)
+ * @see ColorModelFactory#createGrayScale(int, int, int, double, double)
  *
  * @since 1.0
  * @module
@@ -43,41 +41,28 @@ final class ScaledColorSpace extends ColorSpace {
     /**
      * For cross-version compatibility.
      */
-    private static final long serialVersionUID = 438226855772441165L;
+    private static final long serialVersionUID = -6635165959083590494L;
 
     /**
-     * Shared instances of {@link ScaledColorSpace}s.
+     * The scaling factor from sample values to RGB values. The target RGB values will be in range 0
+     * to 255 inclusive. Note that the target range is different than the range of {@link ColorSpace}
+     * normalized values; methods in this class has to divide values by {@value ScaledColorModel#RANGE}.
      */
-    private static final WeakHashSet<ScaledColorSpace> POOL = new WeakHashSet<>(ScaledColorSpace.class);
-
-    /**
-     * Minimal normalized RGB value.
-     */
-    private static final float MIN_VALUE = 0f;
-
-    /**
-     * Maximal normalized RGB value.
-     */
-    private static final float MAX_VALUE = 1f;
-
-    /**
-     * The scaling factor from sample values to RGB normalized values.
-     */
-    private final float scale;
+    final double scale;
 
     /**
      * The offset to subtract from sample values before to apply the {@linkplain #scale} factor.
      */
-    private final float offset;
+    final double offset;
 
     /**
-     * Index of the band to display.
+     * Index of the band to display, from 0 inclusive to {@link #getNumComponents()} exclusive.
      */
     final int visibleBand;
 
     /**
      * Creates a color model for the given range of values.
-     * Callers should invoke {@link #unique()} on the newly created instance.
+     * The given range does not need to be exact; values outside that range will be clamped.
      *
      * @param  numComponents  the number of components.
      * @param  visibleBand    the band to use for computing colors.
@@ -87,9 +72,8 @@ final class ScaledColorSpace extends ColorSpace {
     ScaledColorSpace(final int numComponents, final int visibleBand, final double minimum, final double maximum) {
         super(TYPE_GRAY, numComponents);
         this.visibleBand = visibleBand;
-        final double scale  = (MAX_VALUE - MIN_VALUE) / (maximum - minimum);
-        this.scale  = (float) scale;
-        this.offset = (float) (minimum - MIN_VALUE / scale);
+        scale  = ScaledColorModel.RANGE / (maximum - minimum);
+        offset = minimum;
     }
 
     /**
@@ -100,12 +84,8 @@ final class ScaledColorSpace extends ColorSpace {
      */
     @Override
     public float[] toRGB(final float[] samples) {
-        float value = (samples[visibleBand] - offset) * scale;
-        if (!(value >= MIN_VALUE)) {                            // Use '!' for catching NaN.
-            value = MIN_VALUE;
-        } else if (value > MAX_VALUE) {
-            value = MAX_VALUE;
-        }
+        float value = Math.min(1, (float) ((samples[visibleBand] - offset) * (1d/ScaledColorModel.RANGE * scale)));
+        if (!(value >= 0)) value = 0;                   // Use '!' for replacing NaN.
         return new float[] {value, value, value};
     }
 
@@ -118,7 +98,8 @@ final class ScaledColorSpace extends ColorSpace {
     @Override
     public float[] fromRGB(final float[] color) {
         final float[] values = new float[getNumComponents()];
-        values[visibleBand] = (color[0] + color[1] + color[2]) / (3 * scale) + offset;
+        values[visibleBand] = (float) ((color[0] + color[1] + color[2])
+                            / (3d/ScaledColorModel.RANGE * scale) + offset);
         return values;
     }
 
@@ -145,7 +126,8 @@ final class ScaledColorSpace extends ColorSpace {
     @Override
     public float[] fromCIEXYZ(final float[] color) {
         final float[] values = new float[getNumComponents()];
-        values[visibleBand] = (color[0] / 0.9642f + color[1] + color[2] / 0.8249f) / (3 * scale) + offset;
+        values[visibleBand] = (float) ((color[0] / 0.9642f + color[1] + color[2] / 0.8249f)
+                            / (3d/ScaledColorModel.RANGE * scale) + offset);
         return values;
     }
 
@@ -157,7 +139,7 @@ final class ScaledColorSpace extends ColorSpace {
      */
     @Override
     public float getMinValue(final int component) {
-        return MIN_VALUE / scale + offset;
+        return (float) offset;
     }
 
     /**
@@ -168,7 +150,7 @@ final class ScaledColorSpace extends ColorSpace {
      */
     @Override
     public float getMaxValue(final int component) {
-        return MAX_VALUE / scale + offset;
+        return (float) (ScaledColorModel.RANGE / scale + offset);
     }
 
     /**
@@ -197,32 +179,25 @@ final class ScaledColorSpace extends ColorSpace {
     }
 
     /**
-     * Returns a unique instance of this color space. May be {@code this}.
-     */
-    final ScaledColorSpace unique() {
-        return POOL.unique(this);
-    }
-
-    /**
      * Returns a hash code value for this color space.
-     * Defined for implementation of {@link #unique()}.
      */
     @Override
     public int hashCode() {
-        return Float.floatToIntBits(scale) + 31 * Float.floatToIntBits(offset) + 7 * getNumComponents() + visibleBand;
+        return Long.hashCode(Double.doubleToLongBits(scale)
+                      + 31 * Double.doubleToLongBits(offset))
+                      +  7 * getNumComponents() + visibleBand;
     }
 
     /**
      * Compares this color space with the given object for equality.
-     * Defined for implementation of {@link #unique()}.
      */
     @Override
     public boolean equals(final Object obj) {
         if (obj instanceof ScaledColorSpace) {
             final ScaledColorSpace that = (ScaledColorSpace) obj;
-            return Numerics.equals(scale,  that.scale)  &&
-                   Numerics.equals(offset, that.offset) &&
-                   visibleBand         ==  that.visibleBand &&
+            return Numerics.equals(scale,  that.scale)             &&
+                   Numerics.equals(offset, that.offset)            &&
+                   visibleBand         ==  that.visibleBand        &&
                    getNumComponents()  ==  that.getNumComponents() &&
                    getType()           ==  that.getType();
         }


[sis] 02/02: Reduce the number of Locale object we carry. They are not needed at this time since the application is shown in default locale only, and the information is tedious to transfer between various objects.

Posted by de...@apache.org.
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

commit 3195e47572da31c3bcaf034b1e349080efb12a0f
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sun Feb 23 17:35:54 2020 +0100

    Reduce the number of Locale object we carry. They are not needed at this time since the application is shown in default locale only, and the information is tedious to transfer between various objects.
---
 .../java/org/apache/sis/gui/coverage/CoverageExplorer.java |  7 +++----
 .../java/org/apache/sis/gui/coverage/CoverageView.java     |  6 ++----
 .../main/java/org/apache/sis/gui/coverage/GridView.java    |  2 +-
 .../main/java/org/apache/sis/gui/coverage/StatusBar.java   |  3 +--
 .../main/java/org/apache/sis/gui/dataset/DataWindow.java   | 11 ++---------
 .../main/java/org/apache/sis/gui/dataset/FeatureTable.java | 11 ++---------
 .../main/java/org/apache/sis/gui/dataset/ResourceTree.java |  2 +-
 .../java/org/apache/sis/gui/metadata/MetadataSummary.java  | 14 +++-----------
 .../java/org/apache/sis/gui/metadata/MetadataTree.java     | 11 ++---------
 .../java/org/apache/sis/internal/gui/ToolbarButton.java    |  4 +---
 .../src/main/java/org/apache/sis/internal/map/Canvas.java  |  2 +-
 11 files changed, 19 insertions(+), 54 deletions(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageExplorer.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageExplorer.java
index 158c106..9a31c49 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageExplorer.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageExplorer.java
@@ -114,8 +114,7 @@ public class CoverageExplorer {
      * Creates an initially empty explorer.
      */
     public CoverageExplorer() {
-        final Locale locale = Locale.getDefault(Locale.Category.DISPLAY);
-        final Vocabulary vocabulary = Vocabulary.getResources(locale);
+        final Vocabulary vocabulary = Vocabulary.getResources((Locale) null);
         gridView         = new GridView();
         sampleDimensions = new CategoryCellFactory(gridView.cellFormat).createSampleDimensionTable(vocabulary);
         sampleDimensions.getSelectionModel().selectedIndexProperty().addListener(new BandSelectionListener(gridView.bandProperty));
@@ -203,8 +202,8 @@ addRows:    for (int row = 0;; row++) {
             }
 
             /** Creates a visualization of the coverage. */
-            @Override public Region createView(final Locale locale) {
-                final CoverageView view = new CoverageView(locale);
+            @Override public Region createView() {
+                final CoverageView view = new CoverageView();
                 view.coverageProperty.bind(coverageProperty);
                 return view.getView();
             }
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageView.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageView.java
index 2bcfb14..62a448c 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageView.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageView.java
@@ -125,11 +125,9 @@ final class CoverageView extends PlanarCanvas {
 
     /**
      * Creates a new two-dimensional canvas for {@link RenderedImage}.
-     *
-     * @param  locale  the locale to use for labels and some messages, or {@code null} for default.
      */
-    public CoverageView(final Locale locale) {
-        super(locale);
+    public CoverageView() {
+        super(Locale.getDefault());
         coverageProperty    = new SimpleObjectProperty<>(this, "coverage");
         sliceExtentProperty = new SimpleObjectProperty<>(this, "sliceExtent");
         dataToImage = new AffineTransform();
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridView.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridView.java
index b4b171d..2ac1464 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridView.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridView.java
@@ -186,7 +186,7 @@ public class GridView extends Control {
     private final NumberFormat headerFormat;
 
     /**
-     * The formatter to use for writing sample values. This is also the the property for the localized format pattern.
+     * The formatter to use for writing sample values. This is also the property for the localized format pattern.
      * Note that this pattern depends on current locale. It is provided for user interactions (i.e. in a GUI control)
      * instead than programmatic action.
      *
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/StatusBar.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/StatusBar.java
index 72f0b14..70c76cf 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/StatusBar.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/StatusBar.java
@@ -17,7 +17,6 @@
 package org.apache.sis.gui.coverage;
 
 import java.util.Locale;
-import java.util.TimeZone;
 import javax.measure.Unit;
 import javafx.geometry.Insets;
 import javafx.geometry.Pos;
@@ -86,7 +85,7 @@ final class StatusBar extends HBox {
      */
     StatusBar(final GridView view) {
         this.view = view;
-        format = new CoordinateFormat(Locale.getDefault(Locale.Category.FORMAT), TimeZone.getDefault());
+        format = new CoordinateFormat();
         coordinates = new Label();
         setAlignment(Pos.CENTER_RIGHT);
         getChildren().setAll(coordinates);
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/DataWindow.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/DataWindow.java
index 59ca2cc..4a7ac4a 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/DataWindow.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/DataWindow.java
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.gui.dataset;
 
-import java.util.Locale;
 import javafx.geometry.Rectangle2D;
 import javafx.stage.Screen;
 import javafx.stage.Stage;
@@ -46,11 +45,6 @@ import org.apache.sis.internal.gui.ToolbarButton;
  */
 final class DataWindow extends Stage {
     /**
-     * The locale for texts and tooltips in this window.
-     */
-    private final Locale locale;
-
-    /**
      * The tools bar. Removed from the pane when going in full screen mode, and reinserted
      * when exiting full screen mode. The first button in this toolbar shall be the "home"
      * button (for showing the main window on front) — if a different position is desired,
@@ -96,7 +90,6 @@ final class DataWindow extends Stage {
      * @param  localized  {@link Resources} instance, provided because often known by the caller.
      */
     private DataWindow(final Region content, final Resources localized) {
-        locale = localized.getLocale();
         /*
          * Build the tools bar. This bar will be hidden in full screen mode. Note that above
          * method assumes that the "home" button created below is the first one in the toolbar.
@@ -185,8 +178,8 @@ final class DataWindow extends Stage {
         public void handle(final ActionEvent event) {
             if (creator != null) {
                 final DataWindow originator = window;
-                final Resources  localized  = Resources.forLocale(originator.locale);
-                final Region     content    = creator.createView(originator.locale);
+                final Resources  localized  = Resources.forLocale(null);
+                final Region     content    = creator.createView();
                 final Button     backButton = creator.createBackButton(localized);
                 backButton.setOnAction((e) -> {originator.show(); originator.toFront();});
                 ToolbarButton.insert(content, new ToolbarButton() {
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/FeatureTable.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/FeatureTable.java
index 94c4a90..548fe79 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/FeatureTable.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/FeatureTable.java
@@ -83,17 +83,12 @@ import org.apache.sis.internal.gui.ExceptionReporter;
 @DefaultProperty("features")
 public class FeatureTable extends TableView<Feature> {
     /**
-     * The locale to use for texts.
+     * The locale to use for texts. This is usually {@link Locale#getDefault()}.
+     * This value is given to {@link InternationalString#toString(Locale)} calls.
      */
     final Locale textLocale;
 
     /**
-     * The locale to use for dates/numbers.
-     * This is often the same than {@link #textLocale}.
-     */
-    private final Locale dataLocale;
-
-    /**
      * The type of features, or {@code null} if not yet determined.
      * This type determines the columns that will be shown.
      *
@@ -124,7 +119,6 @@ public class FeatureTable extends TableView<Feature> {
     public FeatureTable() {
         super(new FeatureList());
         textLocale = Locale.getDefault(Locale.Category.DISPLAY);
-        dataLocale = Locale.getDefault(Locale.Category.FORMAT);
         initialize();
     }
 
@@ -140,7 +134,6 @@ public class FeatureTable extends TableView<Feature> {
         other.isSharingList = true;
         isSharingList = true;
         textLocale    = other.textLocale;
-        dataLocale    = other.dataLocale;
         featureType   = other.featureType;
         setFeatures(other.getFeatures());           // Shall be invoked before to install the listener.
         initialize();                               // Install listener.
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
index 97657f8..7a73429 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
@@ -100,7 +100,7 @@ public class ResourceTree extends TreeView<Resource> {
      * For showing a resource, invoke {@link #setResource(Resource)} after construction.
      */
     public ResourceTree() {
-        localized = Resources.forLocale(Locale.getDefault(Locale.Category.DISPLAY));
+        localized = Resources.forLocale(null);
         setCellFactory(ResourceTree::newCell);
         setOnDragOver(ResourceTree::onDragOver);
         setOnDragDropped(this::load);
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataSummary.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataSummary.java
index f4f3f42..920fcb5 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataSummary.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataSummary.java
@@ -20,7 +20,6 @@ import java.text.DateFormat;
 import java.text.NumberFormat;
 import java.util.Collection;
 import java.util.ArrayList;
-import java.util.Locale;
 import java.util.StringJoiner;
 import javafx.application.Platform;
 import javafx.beans.DefaultProperty;
@@ -76,12 +75,6 @@ public class MetadataSummary {
     final Resources localized;
 
     /**
-     * The locale to use for date/number formatters.
-     * This is often the same than {@link #localized}.
-     */
-    final Locale dataLocale;
-
-    /**
      * The format to use for writing numbers, created when first needed.
      *
      * @see #getNumberFormat()
@@ -147,8 +140,7 @@ public class MetadataSummary {
      * Creates an initially empty metadata overview.
      */
     public MetadataSummary() {
-        localized   = Resources.forLocale(Locale.getDefault(Locale.Category.DISPLAY));
-        dataLocale  = Locale.getDefault(Locale.Category.FORMAT);
+        localized   = Resources.forLocale(null);
         information = new TitledPane[] {
             new TitledPane(localized.getString(Resources.Keys.ResourceIdentification), new IdentificationInfo(this)),
             new TitledPane(localized.getString(Resources.Keys.SpatialRepresentation),  new RepresentationInfo(this))
@@ -182,7 +174,7 @@ public class MetadataSummary {
      */
     final NumberFormat getNumberFormat() {
         if (numberFormat == null) {
-            numberFormat = NumberFormat.getInstance(dataLocale);
+            numberFormat = NumberFormat.getInstance();
         }
         return numberFormat;
     }
@@ -192,7 +184,7 @@ public class MetadataSummary {
      */
     final DateFormat getDateFormat() {
         if (dateFormat == null) {
-            dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, dataLocale);
+            dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM);
         }
         return dateFormat;
     }
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java
index 9b964b2..5b7c3c7 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java
@@ -113,17 +113,12 @@ public class MetadataTree extends TreeTableView<TreeTable.Node> {
     }
 
     /**
-     * The locale to use for texts.
+     * The locale to use for texts. This is usually {@link Locale#getDefault()}.
+     * This value is given to {@link InternationalString#toString(Locale)} calls.
      */
     final Locale textLocale;
 
     /**
-     * The locale to use for dates/numbers.
-     * This is often the same than {@link #textLocale}.
-     */
-    private final Locale dataLocale;
-
-    /**
      * Creates a new initially empty metadata tree.
      */
     public MetadataTree() {
@@ -150,10 +145,8 @@ public class MetadataTree extends TreeTableView<TreeTable.Node> {
     MetadataTree(final MetadataSummary controller, final boolean standard) {
         if (controller != null) {
             textLocale = controller.localized.getLocale();
-            dataLocale = controller.dataLocale;
         } else {
             textLocale = Locale.getDefault(Locale.Category.DISPLAY);
-            dataLocale = Locale.getDefault(Locale.Category.FORMAT);
         }
         contentProperty = new ContentProperty(this);
         nameColumn      = new TreeTableColumn<>(TableColumn.NAME .getHeader().toString(textLocale));
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ToolbarButton.java b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ToolbarButton.java
index d3f2109..06e588f 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ToolbarButton.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ToolbarButton.java
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.internal.gui;
 
-import java.util.Locale;
 import javafx.scene.Node;
 import javafx.scene.control.Button;
 import javafx.scene.control.Tooltip;
@@ -148,9 +147,8 @@ public abstract class ToolbarButton {
          * This method is invoked only on the first click. For all subsequent clicks,
          * the existing window will be shown again.
          *
-         * @param  locale  locale of the window creating a new window.
          * @return content of the window to show.
          */
-        public abstract Region createView(Locale locale);
+        public abstract Region createView();
     }
 }
diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Canvas.java b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Canvas.java
index d70de30..1fa4edc 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Canvas.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Canvas.java
@@ -375,7 +375,7 @@ public class Canvas extends Observable implements Localized {
     private final DefaultCoordinateOperationFactory coordinateOperationFactory;
 
     /**
-     * The locale for labels or error messages.
+     * The locale for labels or error messages, or {@code null} for the default.
      */
     private final Locale locale;