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 f25739200b Various bug fixes related to the navigation in 
two-dimensional slices: - Random `MismatchedDimensionException` in the status 
bar. - Slice not updated when navigating using keyboard. - Map projection and 
zoom level lost when changing slice. - `CoverageExplorer` resource and coverage 
properties set to null.
f25739200b is described below

commit f25739200b167de5adabe86b954c88653b6ea0eb
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Tue May 31 12:36:46 2022 +0200

    Various bug fixes related to the navigation in two-dimensional slices:
    - Random `MismatchedDimensionException` in the status bar.
    - Slice not updated when navigating using keyboard.
    - Map projection and zoom level lost when changing slice.
    - `CoverageExplorer` resource and coverage properties set to null.
---
 .../apache/sis/gui/coverage/CoverageCanvas.java    | 36 ++++++++++++++----
 .../apache/sis/gui/coverage/CoverageControls.java  | 11 ++----
 .../apache/sis/gui/coverage/GridSliceSelector.java | 36 ++++++++----------
 .../java/org/apache/sis/gui/coverage/GridView.java | 12 ++++--
 .../apache/sis/gui/coverage/ViewAndControls.java   | 43 +++++++++++++---------
 .../java/org/apache/sis/gui/map/MapCanvas.java     |  2 +-
 6 files changed, 82 insertions(+), 58 deletions(-)

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 57eb233337..b21bdc0f40 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
@@ -175,7 +175,7 @@ public class CoverageCanvas extends MapCanvasAWT {
      * @see #setSliceExtent(GridExtent)
      * @see GridCoverage#render(GridExtent)
      */
-    final ObjectProperty<GridExtent> sliceExtentProperty;
+    public final ObjectProperty<GridExtent> sliceExtentProperty;
 
     /**
      * The interpolation method to use for resampling the image.
@@ -371,7 +371,7 @@ public class CoverageCanvas extends MapCanvasAWT {
      * @see #sliceExtentProperty
      * @see GridCoverage#render(GridExtent)
      */
-    final GridExtent getSliceExtent() {
+    public final GridExtent getSliceExtent() {
         return sliceExtentProperty.get();
     }
 
@@ -385,7 +385,7 @@ public class CoverageCanvas extends MapCanvasAWT {
      * @see #sliceExtentProperty
      * @see GridCoverage#render(GridExtent)
      */
-    final void setSliceExtent(final GridExtent sliceExtent) {
+    public final void setSliceExtent(final GridExtent sliceExtent) {
         sliceExtentProperty.set(sliceExtent);
         // Will indirectly invoke `onPropertySpecified(…)`.
     }
@@ -543,6 +543,11 @@ public class CoverageCanvas extends MapCanvasAWT {
         }
         if (resource == null && coverage == null) {
             runAfterRendering(this::clear);
+        } else if (controls != null && controls.isAdjustingSlice) {
+            runAfterRendering(() -> {
+                clearRenderedImage();
+                requestRepaint();
+            });
         } else {
             BackgroundThreads.execute(new Task<GridGeometry>() {
                 /** Information about all bands. */
@@ -607,6 +612,21 @@ public class CoverageCanvas extends MapCanvasAWT {
         }
     }
 
+    /**
+     * Clears the rendered image but keep the resource, coverage, grid 
geometry and sample dimensions unchanged.
+     * Invoking this method alone is useful when only the selected 
two-dimensional slice changed.
+     * If the {@link StyledRenderingData#clear()} method is not invoked, then 
the map projection,
+     * zoom, <i>etc.</i> are preserved.
+     *
+     * @see #clear()
+     */
+    private void clearRenderedImage() {
+        clearError();
+        clearIsolines();
+        resampledImage = null;
+        derivedImages.clear();
+    }
+
     /**
      * Invoked when a new resource or coverage has been specified.
      * Caller should invoke {@link #requestRepaint()} after this method
@@ -622,20 +642,20 @@ public class CoverageCanvas extends MapCanvasAWT {
         if (TRACE) {
             trace("setNewSource(…): the new domain of data is:%n\t%s", domain);
         }
-        clearError();
-        clearIsolines();
-        resampledImage = null;
-        derivedImages.clear();
+        clearRenderedImage();
         data.clear();
         /*
          * Configure the `GridSliceSelector`, which will compute a new slice 
extent as a side effect.
          * It will overwrite the previous value of `sliceExtent` property in 
this class, which needs
          * to be done before to start the `Worker` process in a background 
thread.
+         *
+         * Note: we do not configure that status bar here, because `StatucBar` 
configures itself by
+         * listening to `MapCanvas` rendering events.
          */
         int[] xyDimensions;
         if (controls != null) try {
             isCoverageAdjusting = true;
-            setSliceExtent(controls.setGeometry(domain));
+            setSliceExtent(controls.configureSliceSelector(domain));
             xyDimensions = controls.sliceSelector.getXYDimensions();
         } finally {
             isCoverageAdjusting = false;
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 8952454ddd..a40dbed2c1 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
@@ -160,6 +160,9 @@ final class CoverageControls extends ViewAndControls {
      * @param  coverage  the new coverage, or {@code null} if none.
      */
     private void notifyDataChanged(final GridCoverageResource resource, final 
GridCoverage coverage) {
+        if (isAdjustingSlice) {
+            return;
+        }
         final ObservableList<Category> items = categoryTable.getItems();
         if (coverage == null) {
             items.clear();
@@ -170,14 +173,6 @@ final class CoverageControls extends ViewAndControls {
         owner.notifyDataChanged(resource, coverage);
     }
 
-    /**
-     * Returns the grid coverage shown in the view, or {@code null} if none.
-     */
-    @Override
-    GridCoverage getCoverage() {
-        return view.getCoverage();
-    }
-
     /**
      * Sets the view content to the given coverage.
      * This method is invoked when a new source of data (either a resource or 
a coverage) is specified,
diff --git 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridSliceSelector.java
 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridSliceSelector.java
index c30fd9d819..b7040e2c32 100644
--- 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridSliceSelector.java
+++ 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridSliceSelector.java
@@ -240,8 +240,8 @@ public class GridSliceSelector extends Widget {
                     converter = new Converter();
                     slider.setLabelFormatter(converter);
                     slider.widthProperty().addListener((p,o,n) -> 
converter.setTickSpacing(slider, n.doubleValue()));
-                    slider.valueProperty().addListener((p,o,n) -> 
converter.formatMessage(Math.rint(n.doubleValue())));
-                    slider.valueChangingProperty().addListener(converter);
+                    slider.valueProperty().addListener(converter);
+                    slider.valueChangingProperty().addListener((p,o,n) -> 
converter.setPosition(n, Math.round(slider.getValue())));
                 }
                 /*
                  * Configure the slider for the current grid axis.
@@ -280,7 +280,7 @@ public class GridSliceSelector extends Widget {
      * We take the change only after the user finished to drag the slider
      * in order to avoid causing to many load requests.
      */
-    private final class Converter extends StringConverter<Double> implements 
ChangeListener<Boolean> {
+    private final class Converter extends StringConverter<Double> implements 
ChangeListener<Number> {
         /**
          * Index of the grid axis where the position changed.
          */
@@ -459,17 +459,12 @@ public class GridSliceSelector extends Widget {
          * Invoked when the user begins of finished to adjust the slider 
position.
          * The grid extent is updated only after the user finished to adjust.
          */
-        @Override
-        public void changed(final ObservableValue<? extends Boolean> property, 
final Boolean oldValue, final Boolean newValue) {
-            final Slider slider = (Slider) ((ReadOnlyProperty<?>) 
property).getBean();
-            final long position = Math.round(slider.getValue());
-            if (newValue) {
-                formatMessage(position);
-            } else {
-                final StatusBar bar = status;
-                if (bar != null) {
-                    bar.setInfoMessage(null);           // Clear the position 
that we wrote in the status bar.
-                }
+        final void setPosition(final boolean adjusting, final long position) {
+            final StatusBar bar = status;
+            if (bar != null) {
+                bar.setInfoMessage(adjusting ? toString(position, true) : 
null);
+            }
+            if (!adjusting) {
                 final GridExtent extent = selectedExtent.get();
                 if (extent != null && position != extent.getLow(dimension)) {
                     selectedExtent.set(extent.withRange(dimension, position, 
position));
@@ -478,14 +473,13 @@ public class GridSliceSelector extends Widget {
         }
 
         /**
-         * Invoked when the slider changed its position.
-         * This method updates the message in the status bar.
+         * Invoked when the slider position changed. If the user is adjusting 
the position,
+         * we only update the message on the status bar. Otherwise the slice 
extent is updated.
          */
-        final void formatMessage(final double position) {
-            final StatusBar bar = status;
-            if (bar != null) {
-                bar.setInfoMessage(toString(position, true));
-            }
+        @Override
+        public void changed(final ObservableValue<? extends Number> property, 
final Number oldValue, final Number newValue) {
+            final Slider slider = (Slider) ((ReadOnlyProperty<?>) 
property).getBean();
+            setPosition(slider.isValueChanging(), 
Math.round(newValue.doubleValue()));
         }
 
         /**
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 00ee0eb64a..ceec7c6613 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
@@ -337,7 +337,7 @@ public class GridView extends Control {
     private void setLoadedImage(GridCoverageResource resource, GridCoverage 
coverage, RenderedImage image) {
         loader = null;          // Must be first for preventing cancellation.
         setImage(image);
-        if (controls != null) {
+        if (controls != null && !controls.isAdjustingSlice) {
             controls.notifyDataChanged(resource, coverage);
         }
     }
@@ -369,7 +369,8 @@ public class GridView extends Control {
         }
 
         /**
-         * Invoked in a background thread for loading the image.
+         * Invoked in a background thread for loading the coverage and 
rendering the image.
+         * The slice selector and the status bar will be updated as soon as 
the coverage is available.
          *
          * @return the image loaded from the source given at construction time.
          * @throws DataStoreException if an error occurred while loading the 
grid coverage.
@@ -386,7 +387,12 @@ public class GridView extends Control {
                 final GridControls c = controls;
                 if (c != null) {
                     final GridGeometry gg = coverage.getGridGeometry();
-                    slice = BackgroundThreads.runAndWait(() -> 
c.setGeometry(gg));
+                    slice = BackgroundThreads.runAndWait(() -> {
+                        final GridExtent s = c.configureSliceSelector(gg);
+                        final int[] xydims = c.sliceSelector.getXYDimensions();
+                        c.status.applyCanvasGeometry(gg, s, xydims[0], 
xydims[1]);
+                        return s;
+                    });
                 }
                 return coverage.render(slice);
             } finally {
diff --git 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ViewAndControls.java
 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ViewAndControls.java
index 89a993c829..6f8084555f 100644
--- 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ViewAndControls.java
+++ 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ViewAndControls.java
@@ -105,6 +105,9 @@ abstract class ViewAndControls {
 
     /**
      * The control for selecting a slice in a <var>n</var>-dimensional data 
cube.
+     *
+     * @see #isAdjustingSlice
+     * @see #configureSliceSelector(GridGeometry)
      */
     protected final GridSliceSelector sliceSelector;
 
@@ -120,6 +123,13 @@ abstract class ViewAndControls {
      */
     protected final CoverageExplorer owner;
 
+    /**
+     * Whether a repaint event is requested as a consequence of a change in 
{@link #sliceSelector}.
+     * In such case, the resource, the coverage and the sample dimensions 
should be considered the same.
+     * This is important for avoiding to set {@link 
CoverageExplorer#resourceProperty} value to {@code null}.
+     */
+    boolean isAdjustingSlice;
+
     /**
      * Creates a new view-control pair.
      *
@@ -130,12 +140,7 @@ abstract class ViewAndControls {
         status = new StatusBar(owner.referenceSystems);
         sliceSelector = new GridSliceSelector(owner.getLocale());
         viewAndNavigation = new VBox();
-        sliceSelector.selectedExtentProperty().addListener((p,o,n) -> {
-            final GridCoverage coverage = getCoverage();
-            if (coverage != null) {
-                load(new ImageRequest(coverage, n));    // Show a new slice of 
data.
-            }
-        });
+        sliceSelector.selectedExtentProperty().addListener((p,o,n) -> 
onSliceChanged(n));
     }
 
     /**
@@ -169,10 +174,17 @@ abstract class ViewAndControls {
     }
 
     /**
-     * Returns the grid coverage shown in the view, or {@code null} if none.
+     * Invoked when the two-dimensional slice to show has changed
+     * as a result of user interaction with {@link #sliceSelector}.
      */
-    GridCoverage getCoverage() {
-        return owner.getCoverage();
+    private void onSliceChanged(final GridExtent slice) {
+        final GridCoverage coverage = owner.getCoverage();
+        if (coverage != null) try {
+            isAdjustingSlice = true;
+            load(new ImageRequest(coverage, slice));        // Show a new 
slice of data.
+        } finally {
+            isAdjustingSlice = false;
+        }
     }
 
     /**
@@ -186,13 +198,13 @@ abstract class ViewAndControls {
 
     /**
      * Invoked when a new coverage or coverage resource has been specified.
-     * This method configures the status bar, adjusts the sliders and returns
-     * the new selected slice. This method shall be invoked in JavaFX thread.
+     * This method configures adjusts the sliders and returns the new selected 
slice.
+     * This method shall be invoked in JavaFX thread.
      *
-     * @param  geometry  grid geometry of the coverage or resource, or {@code 
null} if none.
+     * @param  geometry   grid geometry of the coverage or resource, or {@code 
null} if none.
      * @return new slice to take as the currently selected slice.
      */
-    final GridExtent setGeometry(final GridGeometry geometry) {
+    final GridExtent configureSliceSelector(final GridGeometry geometry) {
         sliceSelector.gridGeometry.set(geometry);
         final ObservableList<Node> components = 
viewAndNavigation.getChildren();
         final int count = components.size();
@@ -206,10 +218,7 @@ abstract class ViewAndControls {
             }
         }
         // The selected slice changed as a result of new grid geometry.
-        final GridExtent slice = 
sliceSelector.selectedExtentProperty().getValue();
-        final int[] xyDimensions = sliceSelector.getXYDimensions();
-        status.applyCanvasGeometry(geometry, slice, xyDimensions[0], 
xyDimensions[1]);
-        return slice;
+        return sliceSelector.selectedExtentProperty().getValue();
     }
 
 
diff --git 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java
index 97b8fa2e7c..a4740cec9d 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java
@@ -163,7 +163,7 @@ public abstract class MapCanvas extends PlanarCanvas {
      *
      * @see #renderingStartTime
      */
-    private static final long WAIT_CURSOR_DELAY = (1000 - REPAINT_DELAY) * 
NANOS_PER_MILLISECOND;
+    private static final long WAIT_CURSOR_DELAY = (500 - REPAINT_DELAY) * 
NANOS_PER_MILLISECOND;
 
     /**
      * The pane showing the map and any other JavaFX nodes to scale and 
translate together with the map.

Reply via email to