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.