sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Provide a new "Operations" panel in `CoverageControls` with possibility to select the "Positional errors" for current coverage. Use weak reference for the image of positional errors.
Date Thu, 11 Jun 2020 16:51:43 GMT
This is an automated email from the ASF dual-hosted git repository.

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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 800f3c7  Provide a new "Operations" panel in `CoverageControls` with possibility
to select the "Positional errors" for current coverage. Use weak reference for the image of
positional errors.
800f3c7 is described below

commit 800f3c76c2222810396d836d22fca8a9ec691876
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Thu Jun 11 11:01:02 2020 +0200

    Provide a new "Operations" panel in `CoverageControls` with possibility to select the
"Positional errors" for current coverage.
    Use weak reference for the image of positional errors.
---
 .../java/org/apache/sis/gui/coverage/Controls.java |  6 +-
 .../apache/sis/gui/coverage/CoverageCanvas.java    | 50 ++++++-----
 .../apache/sis/gui/coverage/CoverageControls.java  | 17 +++-
 .../apache/sis/gui/coverage/ImageDerivative.java   | 97 ++++++++++++++++++++++
 .../apache/sis/gui/coverage/ImageOperation.java    | 92 ++++++++++++++++++++
 .../org/apache/sis/gui/coverage/RenderingData.java | 40 +++++----
 .../org/apache/sis/gui/coverage/Stretching.java    |  2 +
 .../java/org/apache/sis/gui/map/StatusBar.java     |  2 +-
 .../org/apache/sis/gui/map/ValuesUnderCursor.java  | 12 +++
 .../org/apache/sis/internal/gui/Resources.java     | 10 +++
 .../apache/sis/internal/gui/Resources.properties   |  2 +
 .../sis/internal/gui/Resources_fr.properties       |  2 +
 .../java/org/apache/sis/image/ComputedImage.java   | 10 +++
 .../org/apache/sis/image/PositionalErrorImage.java |  7 +-
 .../java/org/apache/sis/image/ResampledImage.java  | 16 ++--
 .../internal/coverage/j2d/ColorModelFactory.java   |  9 +-
 16 files changed, 322 insertions(+), 52 deletions(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java
index be38dee..bbebf32 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java
@@ -27,7 +27,7 @@ import javafx.scene.layout.GridPane;
 import javafx.scene.layout.Region;
 import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.internal.gui.Styles;
-import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.util.resources.IndexedResourceBundle;
 
 
 /**
@@ -94,7 +94,7 @@ abstract class Controls {
      * @param  control     the control to associate to the label, or {@code null} if none.
      * @return label associated to the given control, or {@code null} if the given control
was null.
      */
-    static Label label(final Vocabulary vocabulary, final short key, final Control control)
{
+    static Label label(final IndexedResourceBundle vocabulary, final short key, final Control
control) {
         if (control == null) {
             return null;
         }
@@ -113,7 +113,7 @@ abstract class Controls {
      * @param  isFirst     whether the given group is the first group in the pane.
      * @return label associated to the given group of controls.
      */
-    static Label labelOfGroup(final Vocabulary vocabulary, final short key, final Region
group, final boolean isFirst) {
+    static Label labelOfGroup(final IndexedResourceBundle vocabulary, final short key, final
Region group, final boolean isFirst) {
         final Label label = new Label(vocabulary.getLabel(key));
         label.setPadding(isFirst ? CAPTION_MARGIN : NEXT_CAPTION_MARGIN);
         label.setLabelFor(group);
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
index d76cf16..b44f104 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
@@ -16,8 +16,9 @@
  */
 package org.apache.sis.gui.coverage;
 
+import java.util.Map;
+import java.util.HashMap;
 import java.util.Locale;
-import java.util.EnumMap;
 import java.awt.Graphics2D;
 import java.awt.image.RenderedImage;
 import java.awt.geom.AffineTransform;
@@ -95,7 +96,7 @@ public class CoverageCanvas extends MapCanvasAWT {
     /**
      * The {@code RenderedImage} to draw together with transform from pixel coordinates to
display coordinates.
      * Shall never be {@code null} but may be {@linkplain RenderingData#isEmpty() empty}.
This instance shall be
-     * read and modified in JavaFX thread only and cloned if those data needed by a background
thread.
+     * read and modified in JavaFX thread only and cloned if those data are needed by a background
thread.
      *
      * @see Worker
      */
@@ -103,10 +104,10 @@ public class CoverageCanvas extends MapCanvasAWT {
 
     /**
      * The {@link #data} resampled to a CRS which can easily be mapped to {@linkplain #getDisplayCRS()
display CRS}.
-     * The different values are slight variants of the values associated to {@link Stretching#NONE},
with only the
-     * color map changed.
+     * The different values are variants of the values associated to {@link ImageDerivative#NONE},
with color ramp
+     * changed or other operation applied.
      */
-    private final EnumMap<Stretching,RenderedImage> resampledImages;
+    private final Map<ImageDerivative,RenderedImage> resampledImages;
 
     /**
      * Creates a new two-dimensional canvas for {@link RenderedImage}.
@@ -114,7 +115,7 @@ public class CoverageCanvas extends MapCanvasAWT {
     public CoverageCanvas() {
         super(Locale.getDefault());
         data                  = new RenderingData();
-        resampledImages       = new EnumMap<>(Stretching.class);
+        resampledImages       = new HashMap<>();
         coverageProperty      = new SimpleObjectProperty<>(this, "coverage");
         sliceExtentProperty   = new SimpleObjectProperty<>(this, "sliceExtent");
         interpolationProperty = new SimpleObjectProperty<>(this, "interpolation", data.getInterpolation());
@@ -331,13 +332,14 @@ public class CoverageCanvas extends MapCanvasAWT {
         private RenderedImage resampledImage;
 
         /**
-         * The resampled image after stretching.
+         * The resampled image after color ramp stretching or other operation applied.
          */
-        private RenderedImage stretchedImage;
+        private RenderedImage filteredImage;
 
         /**
-         * The stretched image with tiles computed in advance. The set of prefetched
-         * tiles may differ at each rendering event. This image should not be cached.
+         * The filtered image with tiles computed in advance. The set of prefetched
+         * tiles may differ at each rendering event. This image should not be cached
+         * after rendering operation is completed.
          */
         private RenderedImage prefetchedImage;
 
@@ -362,7 +364,7 @@ public class CoverageCanvas extends MapCanvasAWT {
             displayBounds      = canvas.getDisplayBounds();
             if (data.validateCRS(objectiveCRS)) {
                 resampledImage = canvas.resampledImages.get(Stretching.NONE);
-                stretchedImage = canvas.resampledImages.get(data.selectedStretching);
+                filteredImage  = canvas.resampledImages.get(data.selectedDerivative);
             }
         }
 
@@ -382,14 +384,14 @@ public class CoverageCanvas extends MapCanvasAWT {
                         & ~(AffineTransform.TYPE_IDENTITY | AffineTransform.TYPE_TRANSLATION))
== 0;
             }
             if (!isResampled) {
-                stretchedImage = null;
+                filteredImage = null;
                 resampledImage = data.resample(objectiveCRS, objectiveToDisplay);
                 resampledToDisplay = data.getTransform(objectiveToDisplay);
             }
-            if (stretchedImage == null) {
-                stretchedImage = data.stretch(resampledImage);
+            if (filteredImage == null) {
+                filteredImage = data.filter(resampledImage);
             }
-            prefetchedImage = data.prefetch(stretchedImage, resampledToDisplay, displayBounds);
+            prefetchedImage = data.prefetch(filteredImage, resampledToDisplay, displayBounds);
         }
 
         /**
@@ -418,19 +420,27 @@ public class CoverageCanvas extends MapCanvasAWT {
     private void cacheRenderingData(final Worker worker) {
         data = worker.data;
         final RenderedImage newValue = worker.resampledImage;
-        final RenderedImage oldValue = resampledImages.put(Stretching.NONE, newValue);
+        final RenderedImage oldValue = resampledImages.put(ImageDerivative.NONE, newValue);
         if (oldValue != newValue && oldValue != null) {
             resampledImages.clear();
-            resampledImages.put(Stretching.NONE, newValue);
+            resampledImages.put(ImageDerivative.NONE, newValue);
         }
-        resampledImages.put(data.selectedStretching, worker.stretchedImage);
+        resampledImages.put(data.selectedDerivative, worker.filteredImage);
+    }
+
+    /**
+     * Invoked when the user selected a new operation.
+     */
+    final void setOperation(final ImageOperation selection) {
+        data.selectedDerivative = data.selectedDerivative.setOperation(selection);
+        requestRepaint();
     }
 
     /**
      * Invoked when the user selected a new color stretching mode.
      */
-    final void setStretching(final Stretching type) {
-        data.selectedStretching = type;
+    final void setStyling(final Stretching selection) {
+        data.selectedDerivative = data.selectedDerivative.setStyling(selection);
         requestRepaint();
     }
 
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageControls.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageControls.java
index e8973da..13672c4 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageControls.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageControls.java
@@ -38,6 +38,7 @@ import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.gui.referencing.RecentReferenceSystems;
 import org.apache.sis.gui.map.StatusBar;
 import org.apache.sis.image.Interpolation;
+import org.apache.sis.internal.gui.Resources;
 import org.apache.sis.util.resources.Vocabulary;
 
 
@@ -109,7 +110,7 @@ final class CoverageControls extends Controls implements PropertyChangeListener
             referenceSystem = systemChoices.valueProperty();
 
             final GridPane colors = createControlGrid(0,
-                label(vocabulary, Vocabulary.Keys.Stretching, Stretching.createButton((p,o,n)
-> view.setStretching(n))),
+                label(vocabulary, Vocabulary.Keys.Stretching, Stretching.createButton((p,o,n)
-> view.setStyling(n))),
                 label(vocabulary, Vocabulary.Keys.Background, createBackgroundButton(background))
             );
             displayPane = new VBox(
@@ -117,10 +118,22 @@ final class CoverageControls extends Controls implements PropertyChangeListener
                 labelOfGroup(vocabulary, Vocabulary.Keys.Colors,          colors,     false),
colors);
         }
         /*
+         * "Operation" section with the following controls:
+         *    - List of predefined operations.
+         */
+        final VBox operationPane;
+        {   // Block for making variables locale to this scope.
+            final Resources resources = Resources.forLocale(vocabulary.getLocale());
+            final Region operations = ImageOperation.list((p,o,n) -> view.setOperation(n));
+            operationPane = new VBox(
+                labelOfGroup(resources, Resources.Keys.PredefinedFilters, operations, true),
operations);
+        }
+        /*
          * Put all sections together and have the first one expanded by default.
          */
         controls = new Accordion(
-            new TitledPane(vocabulary.getString(Vocabulary.Keys.Display), displayPane)
+            new TitledPane(vocabulary.getString(Vocabulary.Keys.Display),    displayPane),
+            new TitledPane(vocabulary.getString(Vocabulary.Keys.Operations), operationPane)
             // TODO: more controls to be added in a future version.
         );
         controls.setExpandedPane(controls.getPanes().get(0));
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImageDerivative.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImageDerivative.java
new file mode 100644
index 0000000..fbb5f8e
--- /dev/null
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImageDerivative.java
@@ -0,0 +1,97 @@
+/*
+ * 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 org.apache.sis.internal.util.Strings;
+
+
+/**
+ * Information about how to render an image.
+ * This is a combination of {@link ImageOperation} with {@link Stretching}.
+ * Note that {@link Stretching} is a temporary enumeration to be deleted after SIS provides
styling support.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+final class ImageDerivative {
+    /**
+     * The key when no operation is applied on the image.
+     */
+    static final ImageDerivative NONE = new ImageDerivative(ImageOperation.NONE, Stretching.NONE);
+
+    /**
+     * The operation applied on the image.
+     */
+    final ImageOperation operation;
+
+    /**
+     * Key of the currently selected alternative in {@link CoverageCanvas#resampledImages}
map.
+     */
+    final Stretching styling;
+
+    /**
+     * Creates a new combination of operation and styling.
+     */
+    private ImageDerivative(final ImageOperation operation, final Stretching styling) {
+        this.operation = operation;
+        this.styling   = styling;
+    }
+
+    /**
+     * Returns a key with the same styling than this key but a different operation.
+     */
+    final ImageDerivative setOperation(final ImageOperation selection) {
+        return (selection != operation) ? new ImageDerivative(selection, styling) : this;
+    }
+
+    /**
+     * Returns a key with the same operation than this key but a different styling.
+     */
+    final ImageDerivative setStyling(final Stretching selection) {
+        return (selection != styling) ? new ImageDerivative(operation, selection) : this;
+    }
+
+    /**
+     * Compares this key with given object for equality.
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj instanceof ImageDerivative) {
+            final ImageDerivative other = (ImageDerivative) obj;
+            return (operation == other.operation) && (styling == other.styling);
+        }
+        return false;
+    }
+
+    /**
+     * Returns a hash code value for this key.
+     */
+    @Override
+    public int hashCode() {
+        return operation.hashCode() + 11 * styling.hashCode();
+    }
+
+    /**
+     * Returns a string representation for debugging purpose.
+     */
+    @Override
+    public String toString() {
+        return Strings.toString(getClass(), "operation", operation, "styling", styling);
+    }
+}
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImageOperation.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImageOperation.java
new file mode 100644
index 0000000..9faba4e
--- /dev/null
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImageOperation.java
@@ -0,0 +1,92 @@
+/*
+ * 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.awt.image.RenderedImage;
+import javafx.scene.control.ListView;
+import javafx.beans.value.ChangeListener;
+import javafx.scene.control.MultipleSelectionModel;
+import org.apache.sis.internal.gui.Resources;
+import org.apache.sis.util.resources.Vocabulary;
+
+import static org.apache.sis.image.ResampledImage.POSITIONAL_ERRORS_KEY;
+
+
+/**
+ * Predefined operations that can be applied on image.
+ * The resulting images use the same coordinate system than the original image.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+enum ImageOperation {
+    /**
+     * No operation applied.
+     */
+    NONE(Vocabulary.format(Vocabulary.Keys.None)),
+
+    /**
+     * Produces an image showing an estimation of positional error for each pixel.
+     */
+    POSITIONAL_ERROR(Resources.format(Resources.Keys.PositionalErrors)) {
+        @Override final RenderedImage apply(final RenderedImage source) {
+            final Object value = source.getProperty(POSITIONAL_ERRORS_KEY);
+            return (value instanceof RenderedImage) ? (RenderedImage) value : null;
+        }
+    };
+
+    /**
+     * The label to show in menu.
+     */
+    private final String label;
+
+    /**
+     * Creates a new operation.
+     */
+    private ImageOperation(final String label) {
+        this.label = label;
+    }
+
+    /**
+     * Creates the widget to be shown in {@link CoverageControls} for selecting an operation.
+     */
+    static ListView<ImageOperation> list(final ChangeListener<ImageOperation>
listener) {
+        final ListView<ImageOperation> list = new ListView<>();
+        list.getItems().setAll(values());
+        final MultipleSelectionModel<ImageOperation> select = list.getSelectionModel();
+        select.select(0);
+        select.selectedItemProperty().addListener(listener);
+        return list;
+    }
+
+    /**
+     * Applies the operation on given image.
+     */
+    RenderedImage apply(final RenderedImage source) {
+        return source;
+    }
+
+    /**
+     * Returns the label to show in menu.
+     */
+    @Override
+    public String toString() {
+        return label;
+    }
+}
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
index 1ef639e..f7a38d0 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
@@ -128,7 +128,7 @@ final class RenderingData implements Cloneable {
     /**
      * Key of the currently selected alternative in {@link CoverageCanvas#resampledImages}
map.
      */
-    Stretching selectedStretching;
+    ImageDerivative selectedDerivative;
 
     /**
      * Statistics on pixel values of current {@link #data}, or {@code null} if none or not
yet computed.
@@ -147,7 +147,7 @@ final class RenderingData implements Cloneable {
      * @todo Listen to logging messages. We need to create a logging panel first.
      */
     RenderingData() {
-        selectedStretching = Stretching.NONE;
+        selectedDerivative = ImageDerivative.NONE;
         processor = new ImageProcessor();
         processor.setErrorAction(ImageProcessor.ErrorAction.LOG);
     }
@@ -265,19 +265,29 @@ final class RenderingData implements Cloneable {
     }
 
     /**
-     * Creates the stretched image from the given resampled image.
+     * Applies the image operation (if any) on the given resampled image, than stretches
the color ramp.
      *
      * @param  resampledImage  the image computed by {@link #resample(CoordinateReferenceSystem,
LinearTransform)}.
-     * @return image with color ramp stretched. May be the same instance than given image.
+     * @return image with operation applied and color ramp stretched. May be the same instance
than given image.
      */
-    final RenderedImage stretch(final RenderedImage resampledImage) {
-        if (selectedStretching != Stretching.NONE) {
-            if (statistics == null) {
-                statistics = processor.getStatistics(data);
-            }
+    final RenderedImage filter(RenderedImage resampledImage) {
+        resampledImage = selectedDerivative.operation.apply(resampledImage);
+        if (selectedDerivative.styling != Stretching.NONE) {
             final Map<String,Object> modifiers = new HashMap<>(4);
-            modifiers.put("statistics", statistics);
-            if (selectedStretching == Stretching.AUTOMATIC) {
+            /*
+             * If no operation is applied, select the original image as the source of statistics.
+             * It saves computation time (no need to recompute the statistics when the projection
+             * is changed) and provides more stable visual output (color ramp computed from
same
+             * standard deviation in "automatic" mode). If an operation is applied, the resulting
+             * image can be anything so we let `stretchColorRamp(…)` uses statistics on
that image.
+             */
+            if (selectedDerivative.operation == ImageOperation.NONE) {
+                if (statistics == null) {
+                    statistics = processor.getStatistics(data);
+                }
+                modifiers.put("statistics", statistics);
+            }
+            if (selectedDerivative.styling == Stretching.AUTOMATIC) {
                 modifiers.put("MultStdDev", 3);
             }
             return processor.stretchColorRamp(resampledImage, modifiers);
@@ -289,20 +299,20 @@ final class RenderingData implements Cloneable {
      * Computes immediately, possibly using many threads, the tiles that are going to be
displayed.
      * The returned instance should be used only for current rendering event; it should not
be cached.
      *
-     * @param  stretchedImage      the image computed by {@link #stretch(RenderedImage)}.
+     * @param  filteredImage       the image computed by {@link #filter(RenderedImage)}.
      * @param  resampledToDisplay  the transform computed by {@link #getTransform(LinearTransform)}.
      * @param  displayBounds       size and location of the display device, in pixel units.
      * @return a temporary image with tiles intersecting the display region already computed.
      */
-    final RenderedImage prefetch(final RenderedImage stretchedImage, final AffineTransform
resampledToDisplay,
+    final RenderedImage prefetch(final RenderedImage filteredImage, final AffineTransform
resampledToDisplay,
                                  final Envelope2D displayBounds)
     {
         try {
-            return processor.prefetch(stretchedImage, (Rectangle) AffineTransforms2D.transform(
+            return processor.prefetch(filteredImage, (Rectangle) AffineTransforms2D.transform(
                         resampledToDisplay.createInverse(), displayBounds, new Rectangle()));
         } catch (NoninvertibleTransformException e) {
             recoverableException(e);
-            return stretchedImage;
+            return filteredImage;
         }
     }
 
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Stretching.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Stretching.java
index d99818b..deb9a91 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Stretching.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Stretching.java
@@ -25,6 +25,8 @@ import org.apache.sis.util.resources.Vocabulary;
 /**
  * The kind of color ramp stretching for scaling the color palette of an image.
  *
+ * This enumeration is temporary and will be deleted after SIS provides more complete styling
support.
+ *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.1
  * @since   1.1
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java
index 90f6281..4246f04 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java
@@ -1134,7 +1134,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
             sampleValues.setText(prototype);
             sampleValues.setPrefWidth(Label.USE_COMPUTED_SIZE);                 // Enable
`prefWidth(…)` computation.
             double width = sampleValues.prefWidth(sampleValues.getHeight());
-            final double max = Math.max(width * 1.25, 100);                     // Arbitrary
limit.
+            final double max = Math.max(width * 1.25, 200);                     // Arbitrary
limit.
             for (final String other : others) {
                 sampleValues.setText(other);
                 final double cw = sampleValues.prefWidth(sampleValues.getHeight());
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/ValuesUnderCursor.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/ValuesUnderCursor.java
index 4baf8a9..ce26f94 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/ValuesUnderCursor.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/ValuesUnderCursor.java
@@ -273,6 +273,18 @@ public abstract class ValuesUnderCursor {
         }
 
         /**
+         * Returns the grid coverage used as the source of sample values.
+         * This is usually the value of {@link CoverageCanvas#coverageProperty}.
+         *
+         * @return the source coverage, or {@code null} if none.
+         *
+         * @see CoverageCanvas#coverageProperty
+         */
+        public final GridCoverage getCoverage() {
+            return (evaluator != null) ? evaluator.getCoverage() : null;
+        }
+
+        /**
          * Returns {@code true} if all bands are unselected.
          */
         @Override
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
index fadf98c..72840fa 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
@@ -196,6 +196,16 @@ public final class Resources extends IndexedResourceBundle {
         public static final short OpenDataFile = 29;
 
         /**
+         * Positional errors
+         */
+        public static final short PositionalErrors = 35;
+
+        /**
+         * Predefined filters
+         */
+        public static final short PredefinedFilters = 36;
+
+        /**
          * Select a coordinate reference system
          */
         public static final short SelectCRS = 30;
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
index f4f63ba..d0e4fa4 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
@@ -48,6 +48,8 @@ NewWindow              = New window
 NoFeatureTypeInfo      = No feature type information.
 Open                   = Open\u2026
 OpenDataFile           = Open data file
+PositionalErrors       = Positional errors
+PredefinedFilters      = Predefined filters
 SelectCRS              = Select a coordinate reference system
 SendTo                 = Send to
 StandardErrorStream    = Standard error stream
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
index 9225439..9cb8dba 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
@@ -53,6 +53,8 @@ NewWindow              = Nouvelle fen\u00eatre
 NoFeatureTypeInfo      = Pas d\u2019information sur le type d\u2019entit\u00e9.
 Open                   = Ouvrir\u2026
 OpenDataFile           = Ouvrir un fichier de donn\u00e9es
+PositionalErrors       = Erreurs de positionnement
+PredefinedFilters      = Filtres pr\u00e9d\u00e9finis
 SelectCRS              = Choisir un syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es
 SendTo                 = Envoyer vers
 StandardErrorStream    = Flux d\u2019erreur standard
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/ComputedImage.java b/core/sis-feature/src/main/java/org/apache/sis/image/ComputedImage.java
index dd03046..f95b244 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ComputedImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ComputedImage.java
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Vector;
+import java.lang.ref.Reference;
 import java.awt.Insets;
 import java.awt.Point;
 import java.awt.Rectangle;
@@ -223,6 +224,15 @@ public abstract class ComputedImage extends PlanarImage implements Disposable
{
     }
 
     /**
+     * Returns a weak reference to this image. Using weak reference instead than strong reference
may help to
+     * reduce memory usage when recomputing the image is cheap. This method should not be
public because the
+     * returned instance implements public interfaces that caller could invoke.
+     */
+    final Reference<ComputedImage> reference() {
+        return reference;
+    }
+
+    /**
      * Returns the source at the given index.
      *
      * @param  index  index of the desired source.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/PositionalErrorImage.java
b/core/sis-feature/src/main/java/org/apache/sis/image/PositionalErrorImage.java
index 8e4d7fe..77ee5a1 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/PositionalErrorImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/PositionalErrorImage.java
@@ -154,10 +154,9 @@ final class PositionalErrorImage extends ComputedImage {
             }
             toSource.transform(buffer, 0, buffer, 0, scanline);
             toTarget.transform(buffer, 0, buffer, 0, scanline);
-            for (int i=0, x = tileMinX; x < tileMaxX; x++) {
-                final int t = i;
-                buffer[t] = Math.hypot(buffer[i++] - x,
-                                       buffer[i++] - y);
+            for (int t=0,i=0, x = tileMinX; x < tileMaxX; x++) {
+                buffer[t++] = Math.hypot(buffer[i++] - x,
+                                         buffer[i++] - y);
             }
             tile.setSamples(tileMinX, y, scanline, 1, 0, buffer);
         }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java b/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java
index 4b5ed54..9506d0c 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java
@@ -20,6 +20,7 @@ import java.util.Set;
 import java.util.Arrays;
 import java.util.Objects;
 import java.util.Collections;
+import java.lang.ref.Reference;
 import java.nio.DoubleBuffer;
 import java.awt.Dimension;
 import java.awt.Rectangle;
@@ -145,7 +146,7 @@ public class ResampledImage extends ComputedImage {
      * @see #getPositionalErrors()
      * @see #getProperty(String)
      */
-    private RenderedImage positionalErrors;
+    private Reference<ComputedImage> positionalErrors;
 
     /**
      * Creates a new image which will resample the given image. The resampling operation
is defined
@@ -272,18 +273,23 @@ public class ResampledImage extends ComputedImage {
     }
 
     /**
-     * Computes the {@link #POSITIONAL_ERRORS_KEY} value.
+     * Computes the {@link #POSITIONAL_ERRORS_KEY} value. This method is invoked by {@link
#getProperty(String)}
+     * when the {@link #POSITIONAL_ERRORS_KEY} property value is requested. The result is
saved by weak reference
+     * since recomputing this image is rarely requested, and if needed can be recomputed
easily.
      */
     private synchronized RenderedImage getPositionalErrors() throws TransformException {
-        if (positionalErrors == null) {
+        ComputedImage image = (positionalErrors != null) ? positionalErrors.get() : null;
+        if (image == null) {
+            positionalErrors = null;
             final Dimension s = interpolation.getSupportSize();
             final double[] offset = new double[toSource.getSourceDimensions()];
             offset[0] = -interpolationSupportOffset(s.width);
             offset[1] = -interpolationSupportOffset(s.height);
             final MathTransform tr = MathTransforms.concatenate(toSource, MathTransforms.translation(offset));
-            positionalErrors = new PositionalErrorImage(this, tr);
+            image = new PositionalErrorImage(this, tr);
+            positionalErrors = image.reference();
         }
-        return positionalErrors;
+        return image;
     }
 
     /**
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 5d98a56..73cd32e 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
@@ -439,8 +439,13 @@ public final class ColorModelFactory {
             case DataBuffer.TYPE_USHORT: signed = false; break;
             case DataBuffer.TYPE_SHORT:
             case DataBuffer.TYPE_INT:    signed = true; break;
-            case DataBuffer.TYPE_FLOAT:
-            case DataBuffer.TYPE_DOUBLE: return ((float) minimum) == 0f && ((float)
maximum) == 1f;
+            /*
+             * The standard range of floating point types is [0 … 1], but we never return
`true`
+             * for those types even if the specified minimum and maximum values match that
range
+             * because we have no guarantees that the actual sample values will never be
greater.
+             * Values out-of-bounds cause exceptions to be thrown at rendering time with
standard
+             * color space.
+             */
             default: return false;
         }
         final int numBits = DataBuffer.getDataTypeSize(dataType);


Mime
View raw message