sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/03: Handle formatting of cell values in a separated class, for making easier to share with the table of sample dimensions (to be provided in a future commit).
Date Sat, 01 Feb 2020 14:07:50 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

commit e6f51e886428d94999e4679ea97f65ac36406ddc
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sat Feb 1 12:50:42 2020 +0100

    Handle formatting of cell values in a separated class, for making easier to share with
the table of sample dimensions (to be provided in a future commit).
---
 .../org/apache/sis/gui/coverage/CellFormat.java    | 183 +++++++++++++++++++++
 .../java/org/apache/sis/gui/coverage/GridView.java | 150 ++---------------
 2 files changed, 197 insertions(+), 136 deletions(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CellFormat.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CellFormat.java
new file mode 100644
index 0000000..0b0eeeb
--- /dev/null
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CellFormat.java
@@ -0,0 +1,183 @@
+/*
+ * 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.gui.coverage;
+
+import java.lang.reflect.Array;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.text.FieldPosition;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import org.apache.sis.image.PlanarImage;
+import org.apache.sis.math.DecimalFunctions;
+import org.apache.sis.util.Numbers;
+import org.apache.sis.util.Workaround;
+
+
+/**
+ * Formatter for cell values with a number of fraction digits determined from the sample
value resolution.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+final class CellFormat {
+    /**
+     * The "classic" number format pattern (as opposed to scientific notation). This is non-null
only after
+     * {@link #cellFormat} switched to scientific notation and is used for switching back
to classic notation.
+     * This is a workaround for the absence of `DecimalFormat.useScientificNotation(boolean)`
method.
+     */
+    @Workaround(library="JDK", version="13")
+    private String classicFormatPattern;
+
+    /**
+     * The formatter to use for writing sample values.
+     */
+    private final NumberFormat cellFormat;
+
+    /**
+     * Required when invoking {@link #cellFormat} methods but not used by this class.
+     * This argument is not allowed to be {@code null}, so we create an instance once
+     * and reuse it at each method call.
+     */
+    private final FieldPosition formatField;
+
+    /**
+     * A buffer for writing sample values with {@link #cellFormat}, reused for each value
to format.
+     */
+    private final StringBuffer buffer;
+
+    /**
+     * The last value formatted by {@link #cellFormat}. We keep this information because
it happens often
+     * that the same value is repeated for many cells, especially in area containing fill
or missing values.
+     * If the value is the same, we will reuse the {@link #lastValueAsText}.
+     *
+     * <p>Note: the use of {@code double} is sufficient since rendered images can not
store {@code long} values,
+     * so there is no precision lost that we could have with conversions from {@code long}
to {@code double}.</p>
+     */
+    private double lastValue;
+
+    /**
+     * The formatting of {@link #lastValue}.
+     */
+    private String lastValueAsText;
+
+    /**
+     * Whether the sample values are integers. We use this flag for deciding which {@code
Raster.getSampleValue(…)}
+     * method to invoke, which {@code NumberFormat.format(…)} method to invoke, and whether
to set a format pattern
+     * with fraction digits.
+     */
+    boolean dataTypeisInteger;
+
+    /**
+     * Creates a new cell formatter.
+     */
+    CellFormat() {
+        cellFormat  = NumberFormat.getInstance();
+        formatField = new FieldPosition(0);
+        buffer      = new StringBuffer();
+    }
+
+    /**
+     * Invoked when the {@link #cellFormat} configuration needs to be updated.
+     * Callers should invoke {@link GridView#contentChanged(boolean)} after this method.
+     *
+     * @param  image  the source image (shall be non-null).
+     * @param  band   index of the band to show in this grid view.
+     */
+    final void configure(final RenderedImage image, final int band) {
+        if (dataTypeisInteger) {
+            cellFormat.setMaximumFractionDigits(0);
+        } else {
+            int n = 1;          // Default value if we can not determine the number of fraction
digits.
+            final Object property = image.getProperty(PlanarImage.SAMPLE_RESOLUTIONS_KEY);
+            if (property != null) {
+                final int c = Numbers.getEnumConstant(property.getClass().getComponentType());
+                if (c >= Numbers.BYTE && c <= Numbers.BIG_DECIMAL &&
band < Array.getLength(property)) {
+                    final double resolution = Math.abs(((Number) Array.get(property, band)).doubleValue());
+                    if (resolution > 0 && resolution <= Double.MAX_VALUE) {
    // Non-zero, non-NaN and finite.
+                        n = DecimalFunctions.fractionDigitsForDelta(resolution, false);
+                        if (n > 6 || n < -9) {      // Arbitrary threshold for switching
to scientific notation.
+                            if (cellFormat instanceof DecimalFormat) {
+                                if (classicFormatPattern == null) {
+                                    final DecimalFormat df = (DecimalFormat) cellFormat;
+                                    classicFormatPattern = df.toPattern();
+                                    df.applyPattern("0.###E00");
+                                }
+                                n = 3;
+                            }
+                        } else if (classicFormatPattern != null) {
+                            ((DecimalFormat) cellFormat).applyPattern(classicFormatPattern);
+                            classicFormatPattern = null;
+                        }
+                        if (n < 0) n = 0;
+                    }
+                }
+            }
+            cellFormat.setMinimumFractionDigits(n);
+            cellFormat.setMaximumFractionDigits(n);
+        }
+        buffer.setLength(0);
+        format(lastValue);
+    }
+
+    /**
+     * Get the desired sample value from the specified tile and formats its string representation.
+     * As a slight optimization, we reuse the previous string representation if the number
is the same.
+     * It may happen in particular with fill values.
+     */
+    final String format(final Raster tile, final int x, final int y, final int b) {
+        buffer.setLength(0);
+        if (dataTypeisInteger) {
+            final int  integer = tile.getSample(x, y, b);
+            final double value = integer;
+            if (Double.doubleToRawLongBits(value) != Double.doubleToRawLongBits(lastValue))
{
+                // The `format` method invoked here is not the same than in `double` case.
+                lastValueAsText = cellFormat.format(integer, buffer, formatField).toString();
+                lastValue = value;
+            }
+        } else {
+            final double value = tile.getSampleDouble(x, y, b);
+            if (Double.doubleToRawLongBits(value) != Double.doubleToRawLongBits(lastValue))
{
+                format(value);
+                lastValue = value;
+            }
+        }
+        return lastValueAsText;
+    }
+
+    /**
+     * Formats the given sample value and stores the result in {@link #lastValueAsText}.
+     */
+    private void format(final double value) {
+        if (Double.isNaN(value)) {
+            lastValueAsText = "⬚";
+        } else {
+            lastValueAsText = cellFormat.format(value, buffer, formatField).toString();
+        }
+    }
+
+    /**
+     * Formats the given value using the given formatter. This is used for indices
+     * in row header or column header, which have their own formatter.
+     */
+    final String format(final NumberFormat headerFormat, final long value) {
+        buffer.setLength(0);
+        return headerFormat.format(value, buffer, formatField).toString();
+    }
+}
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 e9cec04..a6a091e 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
@@ -16,10 +16,7 @@
  */
 package org.apache.sis.gui.coverage;
 
-import java.lang.reflect.Array;
 import java.text.NumberFormat;
-import java.text.DecimalFormat;
-import java.text.FieldPosition;
 import java.awt.Rectangle;
 import java.awt.image.Raster;
 import java.awt.image.RenderedImage;
@@ -38,11 +35,7 @@ import javafx.scene.control.Control;
 import javafx.scene.control.Skin;
 import javafx.scene.paint.Color;
 import javafx.scene.paint.Paint;
-import org.apache.sis.util.Numbers;
-import org.apache.sis.util.Workaround;
 import org.apache.sis.util.ArgumentChecks;
-import org.apache.sis.math.DecimalFunctions;
-import org.apache.sis.image.PlanarImage;
 import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.internal.gui.BackgroundThreads;
 
@@ -183,56 +176,15 @@ public class GridView extends Control {
      */
     public final ObjectProperty<Paint> headerBackground;
 
-
-    // ════ All fields below are for the formatting of sample values as text. ════════════════════════
-
-
-    /**
-     * The "classic" number format pattern (as opposed to scientific notation). This is non-null
only after
-     * {@link #cellFormat} switched to scientific notation and is used for switching back
to classic notation.
-     * This is a workaround for the absence of `DecimalFormat.useScientificNotation(boolean)`
method.
-     */
-    @Workaround(library="JDK", version="13")
-    private String classicFormatPattern;
-
-    /**
-     * The formatter to use for writing header values (row and column numbers) or the sample
values.
-     */
-    private final NumberFormat headerFormat, cellFormat;
-
-    /**
-     * Required when invoking {@link #cellFormat} methods but not used by this class.
-     * This argument is not allowed to be {@code null}, so we create an instance once
-     * and reuse it at each method call.
-     */
-    private final FieldPosition formatField;
-
-    /**
-     * A buffer for writing sample values with {@link #cellFormat}, reused for each value
to format.
-     */
-    private final StringBuffer buffer;
-
-    /**
-     * The last value formatted by {@link #cellFormat}. We keep this information because
it happens often
-     * that the same value is repeated for many cells, especially in area containing fill
or missing values.
-     * If the value is the same, we will reuse the {@link #lastValueAsText}.
-     *
-     * <p>Note: the use of {@code double} is sufficient since rendered images can not
store {@code long} values,
-     * so there is no precision lost that we could have with conversions from {@code long}
to {@code double}.</p>
-     */
-    private double lastValue;
-
     /**
-     * The formatting of {@link #lastValue}.
+     * The formatter to use for writing header values (row and column numbers).
      */
-    private String lastValueAsText;
+    private final NumberFormat headerFormat;
 
     /**
-     * Whether the sample values are integers. We use this flag for deciding which {@code
Raster.getSampleValue(…)}
-     * method to invoke, which {@code NumberFormat.format(…)} method to invoke, and whether
to set a format pattern
-     * with fraction digits.
+     * The formatter to use for writing sample values.
      */
-    private boolean dataTypeisInteger;
+    private final CellFormat cellFormat;
 
     /**
      * Creates an initially empty grid view. The content can be set after
@@ -247,9 +199,7 @@ public class GridView extends Control {
         cellSpacing      = new SimpleDoubleProperty  (this, "cellSpacing",  4);
         headerBackground = new SimpleObjectProperty<>(this, "headerBackground", Color.GAINSBORO);
         headerFormat     = NumberFormat.getIntegerInstance();
-        cellFormat       = NumberFormat.getInstance();
-        formatField      = new FieldPosition(0);
-        buffer           = new StringBuffer();
+        cellFormat       = new CellFormat();
         tileWidth        = 1;
         tileHeight       = 1;       // For avoiding division by zero.
 
@@ -373,7 +323,7 @@ public class GridView extends Control {
             tileHeight        = Math.max(1, image.getTileHeight());
             tileGridXOffset   = Math.subtractExact(image.getTileGridXOffset(), minX);
             tileGridYOffset   = Math.subtractExact(image.getTileGridYOffset(), minY);
-            dataTypeisInteger = false;                      // To be kept consistent with
`cellFormat` pattern.
+            cellFormat.dataTypeisInteger = false;           // To be kept consistent with
`cellFormat` pattern.
             final SampleModel sm = image.getSampleModel();
             if (sm != null) {                               // Should never be null, but
we are paranoiac.
                 final int numBands = sm.getNumBands();
@@ -381,9 +331,9 @@ public class GridView extends Control {
                     bandProperty.set(numBands - 1);
                 }
                 final int dataType = sm.getDataType();
-                dataTypeisInteger = (dataType >= DataBuffer.TYPE_BYTE && dataType
<= DataBuffer.TYPE_INT);
+                cellFormat.dataTypeisInteger = (dataType >= DataBuffer.TYPE_BYTE &&
dataType <= DataBuffer.TYPE_INT);
             }
-            configureCellFormat(image, bandProperty.getValue());
+            cellFormat.configure(image, bandProperty.getValue());
         }
         contentChanged(true);
     }
@@ -399,54 +349,11 @@ public class GridView extends Control {
     private void onBandSpecified(final ObservableValue<? extends Number> property,
                                  final Number previous, final Number band)
     {
-        configureCellFormat(getImage(), band.intValue());
+        cellFormat.configure(getImage(), band.intValue());
         contentChanged(false);
     }
 
     /**
-     * Invoked when the {@link #cellFormat} configuration needs to be updated.
-     * Callers should invoke {@link #contentChanged(boolean)} after this method.
-     *
-     * @param  image  the source image (shall be non-null).
-     * @param  band   index of the band to show in this grid view.
-     */
-    private void configureCellFormat(final RenderedImage image, final int band) {
-        if (dataTypeisInteger) {
-            cellFormat.setMaximumFractionDigits(0);
-        } else {
-            int n = 1;          // Default value if we can not determine the number of fraction
digits.
-            final Object property = image.getProperty(PlanarImage.SAMPLE_RESOLUTIONS_KEY);
-            if (property != null) {
-                final int c = Numbers.getEnumConstant(property.getClass().getComponentType());
-                if (c >= Numbers.BYTE && c <= Numbers.BIG_DECIMAL &&
band < Array.getLength(property)) {
-                    final double resolution = Math.abs(((Number) Array.get(property, band)).doubleValue());
-                    if (resolution > 0 && resolution <= Double.MAX_VALUE) {
    // Non-zero, non-NaN and finite.
-                        n = DecimalFunctions.fractionDigitsForDelta(resolution, false);
-                        if (n > 6 || n < -9) {      // Arbitrary threshold for switching
to scientific notation.
-                            if (cellFormat instanceof DecimalFormat) {
-                                if (classicFormatPattern == null) {
-                                    final DecimalFormat df = (DecimalFormat) cellFormat;
-                                    classicFormatPattern = df.toPattern();
-                                    df.applyPattern("0.###E00");
-                                }
-                                n = 3;
-                            }
-                        } else if (classicFormatPattern != null) {
-                            ((DecimalFormat) cellFormat).applyPattern(classicFormatPattern);
-                            classicFormatPattern = null;
-                        }
-                        if (n < 0) n = 0;
-                    }
-                }
-            }
-            cellFormat.setMinimumFractionDigits(n);
-            cellFormat.setMaximumFractionDigits(n);
-        }
-        buffer.setLength(0);
-        formatSampleValue(lastValue);
-    }
-
-    /**
      * Invoked when the content may have changed. If {@code all} is {@code true}, then everything
      * may have changed including the number of rows and columns. If {@code all} is {@code
false}
      * then the number of rows and columns is assumed the same.
@@ -555,37 +462,10 @@ public class GridView extends Control {
          * As a slight optimization, we reuse the previous string representation if the number
is the same.
          * It may happen in particular with fill values.
          */
-        final int x = Math.addExact(column, minX);
-        final int y = Math.addExact(row,    minY);
-        final int b = getBand();
-        buffer.setLength(0);
-        if (dataTypeisInteger) {
-            final int  integer = tile.getSample(x, y, b);
-            final double value = integer;
-            if (Double.doubleToRawLongBits(value) != Double.doubleToRawLongBits(lastValue))
{
-                // The `format` method invoked here is not the same than in `double` case.
-                lastValueAsText = cellFormat.format(integer, buffer, formatField).toString();
-                lastValue = value;
-            }
-        } else {
-            final double value = tile.getSampleDouble(x, y, b);
-            if (Double.doubleToRawLongBits(value) != Double.doubleToRawLongBits(lastValue))
{
-                formatSampleValue(value);
-                lastValue = value;
-            }
-        }
-        return lastValueAsText;
-    }
-
-    /**
-     * Formats the given sample value and stores the result in {@link #lastValueAsText}.
-     */
-    private void formatSampleValue(final double value) {
-        if (Double.isNaN(value)) {
-            lastValueAsText = "⬚";
-        } else {
-            lastValueAsText = cellFormat.format(value, buffer, formatField).toString();
-        }
+        return cellFormat.format(tile,
+                Math.addExact(column, minX),
+                Math.addExact(row,    minY),
+                getBand());
     }
 
     /**
@@ -596,9 +476,7 @@ public class GridView extends Control {
      */
     final String formatHeaderValue(final int index, final boolean vertical) {
         if (index >= 0 && index < (vertical ? height : width)) {
-            final long value = index + (long) (vertical ? minY : minX);
-            buffer.setLength(0);
-            return headerFormat.format(value, buffer, formatField).toString();
+            return cellFormat.format(headerFormat, index + (long) (vertical ? minY : minX));
         }
         return OUT_OF_BOUNDS;
     }


Mime
View raw message