This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit a334b6967ed1f2b044b165e5ff0252991de8f53f Author: Martin Desruisseaux <[email protected]> AuthorDate: Wed Apr 22 11:49:48 2026 +0200 Fix a wrong transformation of coordinates after a change of CRS in the status bar. Documentation improvement as a side-effect of the search for the bug. --- .../sis/geometry/AbstractDirectPosition.java | 2 + .../apache/sis/gui/coverage/GridSliceSelector.java | 2 +- .../main/org/apache/sis/gui/internal/Styles.java | 2 +- .../main/org/apache/sis/gui/map/StatusBar.java | 102 +++++++++++++-------- 4 files changed, 66 insertions(+), 42 deletions(-) diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/AbstractDirectPosition.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/AbstractDirectPosition.java index 7f317671cd..eabb56d1bd 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/AbstractDirectPosition.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/AbstractDirectPosition.java @@ -191,6 +191,8 @@ public abstract class AbstractDirectPosition extends FormattableObject implement continue; } coordinate -= shift; + } else { + continue; } setCoordinate(i, coordinate); changed = true; diff --git a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/GridSliceSelector.java b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/GridSliceSelector.java index 28bd73e208..cca5a333b9 100644 --- a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/GridSliceSelector.java +++ b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/GridSliceSelector.java @@ -458,7 +458,7 @@ public class GridSliceSelector extends Widget { final void setPosition(final boolean adjusting, final long position) { final StatusBar bar = status; if (bar != null) { - bar.setInfoMessage(adjusting ? toString(position, true) : null); + bar.setProgressMessage(adjusting ? toString(position, true) : null); } if (!adjusting) { final GridExtent extent = selectedExtent.get(); diff --git a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Styles.java b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Styles.java index 1cfe119d32..e8c8a3494e 100644 --- a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Styles.java +++ b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Styles.java @@ -72,7 +72,7 @@ public final class Styles { * Color of text for information purpose but which are not the main topic. * This is using a lighter color for avoiding to be a source of distraction. */ - public static final Color FAINT_TEXT = Color.GRAY; + public static final Color FAINT_TEXT = Color.DIMGRAY; /** * Color of the text saying that data are in process of being loaded. diff --git a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/StatusBar.java b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/StatusBar.java index 61a9603443..5a349925da 100644 --- a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/StatusBar.java +++ b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/StatusBar.java @@ -171,7 +171,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> { /** * The tool tip to show on the default message, or {@code null} if none. */ - private Tooltip defaultMessageTeooltip; + private Tooltip defaultMessageTooltip; /** * Message to write in the middle of the status bar. @@ -179,7 +179,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> { * It takes all the space before {@link #position}. * * @see #getMessage() - * @see #setInfoMessage(String) + * @see #setProgressMessage(String) * @see #setErrorMessage(String, Throwable) * @see #setDefaultMessage(String, Tooltip) */ @@ -245,30 +245,34 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> { /** * Conversion from local coordinates to geographic or projected coordinates of rendered data. - * The local coordinates are the coordinates of the JavaFX view, as given for example in {@link MouseEvent} - * The objective coordinates are geographic or projected coordinates of rendered data, ignoring all CRS changes - * that may result from user selecting a different CRS in the contextual menu. Consequently while this transform - * is often the conversion from pixel coordinates to the coordinates shown in this status bar, - * this is not always the case. + * The local coordinates are the coordinates of the JavaFX view, as given for example in {@link MouseEvent}. + * The objective coordinates are the (usually geographic or projected) coordinates of the image shown in the + * {@link MapCanvas} or other control associated with this status bar. Objective coordinates are not + * necessarily the coordinates shown in the status bar, because another transformation step may happen + * if the user selected a different <abbr>CRS</abbr> in the contextual menu of the status bar. * * <p>This transform shall never be null. It is initially an identity transform and is modified by * {@link #applyCanvasGeometry(GridGeometry)}. The transform is usually (but not necessarily) affine * and should have no {@linkplain CoordinateOperation#getCoordinateOperationAccuracy() inaccuracy} - * (ignoring rounding error). This transform is normally the inverse of {@linkplain #canvas} + * (ignoring rounding error). This transform is normally the inverse of the canvas's * {@linkplain MapCanvas#getObjectiveToDisplay() objective to display} transform, * but temporary mismatches may exist during gesture events such as pans, zooms and rotations.</p> * - * <p>If this transform is set to a new value, the given transform must have the same number of source - * and target dimensions than the previous value (if a change in the number of dimension is desired, - * use {@link #applyCanvasGeometry(GridGeometry)} instead). The status bar is updated as if the new - * conversion was applied <em>before</em> any CRS changes resulting from user selecting a different - * CRS in the contextual menu. Note however that any specified transform may be overwritten if some - * {@linkplain #canvas} gesture events happen later; setting an explicit transform is more useful - * when this {@code StatusBar} is <em>not</em> associated to a {@link MapCanvas} - * (for example it may be used with a {@link org.apache.sis.gui.coverage.GridView} instead).</p> + * <p>This transform may change continuously as a result of gesture events such as zooms and pans. + * It is possible to set explicitly a value to this property, but the specified value may be overwritten + * by the next gesture event. Therefore, setting an explicit value is more useful when this status bar is + * <em>not</em> associated to a {@link MapCanvas}. For example, it may be associated to a + * {@link org.apache.sis.gui.coverage.GridView} instead).</p> + * + * <h4>Setting a value explicitly</h4> + * If this transform is set to a new value, the given transform must have the same number of source + * and target dimensions as the previous value (if a change in the number of dimensions is desired, + * use {@link #applyCanvasGeometry(GridGeometry)} instead). + * The status bar is updated as if the specified transform was applied <em>before</em> transformation + * to the <abbr>CRS</abbr> specified through the contextual menu. * * <h4>API note</h4> - * We do not provide getter/setter for this property; use {@link ObjectProperty#set(Object)} + * We do not provide getter/setter for this property. Use {@link ObjectProperty#set(Object)} * directly instead. We omit the "Property" suffix for making this operation more natural. * * @see MapCanvas#getObjectiveCRS() @@ -522,7 +526,6 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> { lowestAccuracy = new SimpleObjectProperty<>(this, "lowestAccuracy"); message = new Label(); - message.setVisible(false); // Waiting for getting a message to display. message.setMaxWidth(Double.POSITIVE_INFINITY); HBox.setHgrow(message, Priority.ALWAYS); @@ -841,11 +844,14 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> { if (localToCRS == null) { localToCRS = MathTransforms.identity(BIDIMENSIONAL); } + final MathTransform localToWritten; if (sameCRS && objectiveToPositionCRS != null) { - localToCRS = MathTransforms.concatenate(localToCRS, objectiveToPositionCRS); + localToWritten = MathTransforms.concatenate(localToCRS, objectiveToPositionCRS); + } else { + localToWritten = localToCRS; } - final int srcDim = Math.max(localToCRS.getSourceDimensions(), BIDIMENSIONAL); - final int tgtDim = localToCRS.getTargetDimensions(); + final int srcDim = Math.max(localToWritten.getSourceDimensions(), BIDIMENSIONAL); + final int tgtDim = localToWritten.getTargetDimensions(); /* * Remaining code should not fail, so we can start modifying the `StatusBar` fields. * The buffers for source and target coordinates are recreated because the number of @@ -859,7 +865,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> { sourceCoordinates = Arrays.copyOf(pointOfInterest, srcDim); targetCoordinates = new GeneralDirectPosition(tgtDim); objectiveCRS = crs; - localToPositionCRS = localToCRS; + localToPositionCRS = localToWritten; inflatePrecisions = inflate; precisions = null; lastX = lastY = Double.NaN; // Not valid anymove — see above block comment. @@ -926,7 +932,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> { * a background thread for computing the coordinate operation. That task may be long * the first time that it is executed, but should be fast on subsequent invocations. * - * @param crs the new CRS, or {@code null} for {@link #objectiveCRS}. + * @param crs the new <abbr>CRS</abbr>, or {@code null} for {@link #objectiveCRS}. * * @see #setPositionRID(ReferencingByIdentifiers) * @see #getPositionCRS() @@ -989,13 +995,11 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> { } else { /* * If the requested CRS is the objective CRS, avoid above costly operation. - * The work that we need to do is to cancel the effect of `localToPositionCRS`. - * As a special case if `objectiveCRS` was unknown before this method call, - * set it to the given value. This is needed for initializing the format CRS - * to the first reference system listed in `RecentReferenceSystems` choices. - * We could not do this work at construction time because the CRS choices may - * be computed in a background thread, in which case it became known only a - * little bit later and given to `StatusBar` through listeners. + * Instead, we only need to remove the `localToPositionCRS` transform step. + * If `objectiveCRS` was unknown before this method call, use the given value. + * This is needed for initializing the format CRS to the first reference system + * listed in `RecentReferenceSystems` choices. This initialization could not be + * done at construction time because the CRS choices may be computed in a background thread. */ if (objectiveCRS == null) { objectiveCRS = crs; @@ -1529,7 +1533,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> { private boolean isPositionVisible() { if (position.isVisible()) { final String text = position.getText(); - return text != null && text != outsideText; // Identity comparison is okay for that value. + return text != null && !text.isBlank() && text != outsideText; // Identity comparison is okay for that value. } return false; } @@ -1633,11 +1637,12 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> { } /** - * Returns the message currently shown. It may be an error message or an informative message. + * Returns the message currently shown. It may be an error message, a message for an operation in progress + * or a default message to show when there is no error or operation in progress. * * @return the current message, or an empty value if none. * - * @see #setInfoMessage(String) + * @see #setProgressMessage(String) * @see #setErrorMessage(String, Throwable) * @see #setDefaultMessage(String, Tooltip) * @@ -1658,19 +1663,19 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> { if (text == null) { text = defaultMessage; textFill = Styles.FAINT_TEXT; - tooltip = defaultMessageTeooltip; + tooltip = defaultMessageTooltip; } - message.setVisible(text != null); message.setText(text); message.setTextFill(textFill); message.setTooltip(tooltip); } /** - * Sets the default message to show when there is no error or operation in progress. - * If {@link #setInfoMessage(String)} or {@link #setErrorMessage(String, Throwable)} + * Sets the default message to show when there is no error and no operation in progress. + * If {@link #setProgressMessage(String)} or {@link #setErrorMessage(String, Throwable)} * is invoked, the informative or error message has precedence over this default message. - * When the informative or error message is reset to {@code null}, the default message is restored. + * When the informative and error message are reset to {@code null}, + * this default message is restored. * * @param text message to show when there is no other kind of message, or {@code null} if none. * @param tooltip optional tool tip to show on the default message, or {@code null} if none. @@ -1680,9 +1685,9 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> { public void setDefaultMessage(String text, final Tooltip tooltip) { text = Strings.trimOrNull(text); defaultMessage = text; - defaultMessageTeooltip = tooltip; + defaultMessageTooltip = tooltip; // Use the text fill as a way to identify that the message is not an error or information. - if (message.getText() == null || message.getTextFill() == Styles.FAINT_TEXT) { + if (Strings.isNullOrEmpty(message.getText()) || message.getTextFill() == Styles.FAINT_TEXT) { message.setTextFill(Styles.FAINT_TEXT); message.setText(text); message.setTooltip(tooltip); @@ -1696,8 +1701,25 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> { * @param text the message to show, or {@code null} if none. * * @since 1.3 + * @deprecated Renamed {@link #setProgressMessage(String)}. */ + @Deprecated(since = "1.7", forRemoval = true) public void setInfoMessage(String text) { + setProgressMessage(text); + } + + /** + * Shows or hides a message for an operation in progress. + * The given text should be a temporary message, for example for telling that a loading is in progress. + * If {@code text} is non-null, it replaces the {@linkplain #setDefaultMessage default message}. + * If {@code text} is null (typically after completion of the operation which was in progress), + * the default message is restored. + * + * @param text the message to show, or {@code null} for restoring the default message. + * + * @since 1.7 + */ + public void setProgressMessage(String text) { setMessage(Strings.trimOrNull(text), Styles.LOADING_TEXT); message.setGraphic(null); }
