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: Apply CRS choice on the coordinate values displayed.
Date Wed, 22 Apr 2020 22:50:27 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 dbec36d  Apply CRS choice on the coordinate values displayed.
dbec36d is described below

commit dbec36d6bf66abf99a7de0ebdb77ba043704dd3d
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Thu Apr 23 00:49:50 2020 +0200

    Apply CRS choice on the coordinate values displayed.
---
 .../java/org/apache/sis/gui/coverage/GridView.java |  25 +-
 .../org/apache/sis/gui/coverage/ImageRequest.java  |   3 +-
 .../java/org/apache/sis/gui/map/StatusBar.java     | 283 +++++++++++++++++----
 .../gui/referencing/RecentReferenceSystems.java    |  10 +-
 .../java/org/apache/sis/internal/gui/Styles.java   |   5 +
 .../org/apache/sis/geometry/CoordinateFormat.java  |   1 +
 6 files changed, 260 insertions(+), 67 deletions(-)

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 891f583..54aa1f4 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
@@ -142,8 +142,9 @@ public class GridView extends Control {
      * This size includes the {@linkplain #cellSpacing cell spacing}.
      * It shall be a number strictly greater than zero.
      *
-     * <p>We do not define getter/setter for this property; use {@link DoubleProperty#set(double)}
-     * directly instead. We omit the "Property" suffix for making this operation more natural.</p>
+     * <div class="note"><b>API note:</b>
+     * We do not provide getter/setter for this property; use {@link DoubleProperty#set(double)}
+     * directly instead. We omit the "Property" suffix for making this operation more natural.</div>
      */
     public final DoubleProperty headerWidth;
 
@@ -152,8 +153,9 @@ public class GridView extends Control {
      * This size includes the {@linkplain #cellSpacing cell spacing}.
      * It shall be a number strictly greater than zero.
      *
-     * <p>We do not define getter/setter for this property; use {@link DoubleProperty#set(double)}
-     * directly instead. We omit the "Property" suffix for making this operation more natural.</p>
+     * <div class="note"><b>API note:</b>
+     * We do not provide getter/setter for this property; use {@link DoubleProperty#set(double)}
+     * directly instead. We omit the "Property" suffix for making this operation more natural.</div>
      */
     public final DoubleProperty cellWidth;
 
@@ -161,8 +163,9 @@ public class GridView extends Control {
      * Height of all rows in the grid.
      * It shall be a number strictly greater than zero.
      *
-     * <p>We do not define getter/setter for this property; use {@link DoubleProperty#set(double)}
-     * directly instead. We omit the "Property" suffix for making this operation more natural.</p>
+     * <div class="note"><b>API note:</b>
+     * We do not provide getter/setter for this property; use {@link DoubleProperty#set(double)}
+     * directly instead. We omit the "Property" suffix for making this operation more natural.</div>
      */
     public final DoubleProperty cellHeight;
 
@@ -171,16 +174,18 @@ public class GridView extends Control {
      * There is no property for vertical cell spacing because increasing the
      * {@linkplain #cellHeight cell height} should be sufficient.
      *
-     * <p>We do not define getter/setter for this property; use {@link DoubleProperty#set(double)}
-     * directly instead. We omit the "Property" suffix for making this operation more natural.</p>
+     * <div class="note"><b>API note:</b>
+     * We do not provide getter/setter for this property; use {@link DoubleProperty#set(double)}
+     * directly instead. We omit the "Property" suffix for making this operation more natural.</div>
      */
     public final DoubleProperty cellSpacing;
 
     /**
      * The background color of row and column headers.
      *
-     * <p>We do not define getter/setter for this property; use {@link ObjectProperty#set(Object)}
-     * directly instead. We omit the "Property" suffix for making this operation more natural.</p>
+     * <div class="note"><b>API note:</b>
+     * 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.</div>
      */
     public final ObjectProperty<Paint> headerBackground;
 
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImageRequest.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImageRequest.java
index 9431c33..7310a21 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImageRequest.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImageRequest.java
@@ -268,7 +268,8 @@ public class ImageRequest {
             for (int i=0; i<origin.length; i++) {
                 origin[i] = request.getLow(i);
             }
-            bar.setLocalToCRS(MathTransforms.concatenate(MathTransforms.translation(origin),
bar.getLocalToCRS()));
+            bar.setLocalToObjectiveCRS(MathTransforms.concatenate(
+                    MathTransforms.translation(origin), bar.getLocalToObjectiveCRS()));
         }
     }
 }
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 5f4abe9..5a16356 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
@@ -36,15 +36,20 @@ import javafx.beans.value.ObservableValue;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.beans.value.ChangeListener;
+import javafx.concurrent.Task;
+import org.opengis.geometry.Envelope;
 import org.opengis.geometry.MismatchedDimensionException;
+import org.opengis.util.FactoryException;
 import org.opengis.referencing.ReferenceSystem;
 import org.opengis.referencing.datum.PixelInCell;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
+import org.opengis.referencing.operation.CoordinateOperation;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
 import org.apache.sis.geometry.GeneralDirectPosition;
 import org.apache.sis.geometry.CoordinateFormat;
 import org.apache.sis.coverage.grid.GridGeometry;
@@ -56,12 +61,16 @@ import org.apache.sis.measure.Units;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.gui.Widget;
 import org.apache.sis.gui.referencing.RecentReferenceSystems;
+import org.apache.sis.internal.gui.BackgroundThreads;
 import org.apache.sis.internal.gui.ExceptionReporter;
 import org.apache.sis.internal.gui.Resources;
 import org.apache.sis.internal.gui.Styles;
+import org.apache.sis.internal.system.Modules;
+import org.apache.sis.referencing.CRS;
 
 
 /**
@@ -126,10 +135,44 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
     private double lastX, lastY;
 
     /**
-     * Conversion from local coordinates to geographic or projected coordinates.
+     * The area of interest, or {@code null} if none. This is a reference to the
+     * {@link RecentReferenceSystems#areaOfInterest} property. We do not make this
+     * property public because it does not belong to this object.
+     */
+    private final ObjectProperty<Envelope> areaOfInterest;
+
+    /**
+     * The reference system used for rendering the data for which this status bar is providing
cursor coordinates.
+     * This is the "{@linkplain RecentReferenceSystems#setPreferred(boolean, ReferenceSystem)
preferred}" or native
+     * data CRS. It may not be the same than the CRS of coordinates actually shown in the
status bar.
+     *
+     * @see #getObjectiveCRS()
+     * @see MapCanvas#getObjectiveCRS()
+     */
+    private CoordinateReferenceSystem objectiveCRS;
+
+    /**
+     * Conversion from local coordinates to geographic or projected coordinates of rendered
data.
+     * This is not necessarily the conversion to the coordinates shown in this status bar.
      * This conversion shall never be null but may be the identity transform.
+     * It should have no {@linkplain CoordinateOperation#getCoordinateOperationAccuracy()
inaccuracy}
+     * (ignoring rounding error). This transform is usually (but not necessarily) affine.
+     *
+     * @see #getLocalToObjectiveCRS()
+     * @see MapCanvas#getObjectiveToDisplay()
      */
-    private MathTransform localToCRS;
+    private MathTransform localToObjectiveCRS;
+
+    /**
+     * Conversion from local coordinates to geographic or projected coordinates shown in
this status bar.
+     * This is the concatenation of {@link #localToObjectiveCRS} with the transform from
{@link #objectiveCRS}
+     * to the user-selected CRS for displaying in the status bar. This conversion shall never
be null but may be
+     * the identity transform. It is usually non-affine if the display CRS is not the same
than the objective CRS.
+     * This transform may have a {@linkplain CoordinateOperation#getCoordinateOperationAccuracy()
limited accuracy}.
+     *
+     * <p>The target CRS can be obtained by {@link CoordinateFormat#getDefaultCRS()}.</p>
+     */
+    private MathTransform localToTargetCRS;
 
     /**
      * The source local indices before conversion to geospatial coordinates.
@@ -147,7 +190,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
 
     /**
      * The desired precisions for each dimension in the {@link #targetCoordinates} to format.
-     * It may vary for each position if the {@link #localToCRS} transform is non-linear.
+     * It may vary for each position if the {@link #localToTargetCRS} transform is non-linear.
      * This array is initially {@code null} and created when first needed.
      */
     private double[] precisions;
@@ -196,24 +239,29 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
      * @param  referenceSystems  the manager of reference systems chosen by the user, or
{@code null} if none.
      */
     public StatusBar(final RecentReferenceSystems referenceSystems) {
-        localToCRS        = MathTransforms.identity(BIDIMENSIONAL);
-        targetCoordinates = new GeneralDirectPosition(BIDIMENSIONAL);
-        sourceCoordinates = targetCoordinates.coordinates;
-        lastX = lastY     = Double.NaN;
-        format            = new CoordinateFormat();
-        coordinates       = new Label();
-        message           = new Label();
-        progress          = new ProgressBar();
+        localToObjectiveCRS = MathTransforms.identity(BIDIMENSIONAL);
+        localToTargetCRS    = localToObjectiveCRS;
+        targetCoordinates   = new GeneralDirectPosition(BIDIMENSIONAL);
+        sourceCoordinates   = targetCoordinates.coordinates;
+        lastX = lastY       = Double.NaN;
+        format              = new CoordinateFormat();
+        coordinates         = new Label();
+        message             = new Label();
+        progress            = new ProgressBar();
         progress.setVisible(false);
-        message.setTextFill(Color.RED);
+        message.setTextFill(Styles.ERROR_TEXT);
         message.setMaxWidth(Double.POSITIVE_INFINITY);
         HBox.setHgrow(message, Priority.ALWAYS);
+        coordinates.minWidthProperty().bind(coordinates.widthProperty());
         view = new HBox(12, progress, message, coordinates);
         view.setPadding(PADDING);
         canvasProperty = new SimpleObjectProperty<>(this, "canvas");
         canvasProperty.addListener(this::onCanvasSpecified);
-        if (referenceSystems != null) {
-            final ContextMenu menu = new ContextMenu(referenceSystems.createMenuItems((e,o,n)
-> setDisplayCRS(n)));
+        if (referenceSystems == null) {
+            areaOfInterest = null;
+        } else {
+            areaOfInterest = referenceSystems.areaOfInterest;
+            final ContextMenu menu = new ContextMenu(referenceSystems.createMenuItems(this::onSelectCRS));
             view.setOnMousePressed((MouseEvent event) -> {
                 if (event.isSecondaryButtonDown()) {
                     menu.show((HBox) event.getSource(), event.getScreenX(), event.getScreenY());
@@ -245,8 +293,8 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
 
     /**
      * Sets the canvas that this status bar is tracking.
-     * This method register all necessary listeners.
-     * A value of {@code null} unregister all listeners.
+     * This method registers all necessary listeners.
+     * A value of {@code null} unregisters all listeners.
      *
      * @param  canvas  the canvas to track, or {@code null} if none.
      *
@@ -278,9 +326,8 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
     }
 
     /**
-     * Listener notified when {@link MapCanvas} completed its rendering.
-     * This listener set {@link StatusBar#localToCRS} to the inverse of
-     * {@link MapCanvas#objectiveToDisplay}.
+     * Listener notified when {@link MapCanvas} completed its rendering. This listener sets
+     * {@link StatusBar#localToObjectiveCRS} to the inverse of {@link MapCanvas#objectiveToDisplay}.
      */
     private final class RenderingListener implements ChangeListener<Boolean> {
         @Override public void changed(final ObservableValue<? extends Boolean> property,
@@ -303,32 +350,35 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
      *   <li>{@link GridGeometry#getCoordinateReferenceSystem()} defines the CRS of
the coordinates to format.</li>
      *   <li>{@link GridGeometry#getGridToCRS(PixelInCell) GridGeometry.getGridToCRS(PixelInCell.CELL_CENTER)}
      *       defines the conversion from coordinate values locale to the canvas to coordinate
values in the CRS
-     *       (the {@linkplain #getLocalToCRS() local to CRS} conversion).</li>
+     *       (the {@linkplain #getLocalToObjectiveCRS() local to objective CRS} conversion).</li>
      *   <li>{@link GridGeometry#getExtent()} provides the view size in pixels, used
for estimating a resolution.</li>
      *   <li>{@link GridGeometry#getResolution(boolean)} is also used for estimating
a resolution.</li>
      * </ul>
      *
-     * All above properties are optional.
-     * The "local to CRS" conversion can be updated after this method call with {@link #setLocalToCRS(MathTransform)}.
+     * All above properties are optional. The "local to objective CRS" conversion can be
updated
+     * after this method call with {@link #setLocalToObjectiveCRS(MathTransform)}.
      *
      * @param  geometry  geometry of the coverage shown in {@link MapCanvas}, or {@code null}.
      */
     public void applyCanvasGeometry(final GridGeometry geometry) {
-        localToCRS = null;
-        precisions = null;
-        inflatePrecisions = null;
+        /*
+         * Compute values in local variables without modifying `StatusBar` fields for now.
+         * The fields will be updated only after we know that this operation is successful.
+         */
+        MathTransform localToCRS = null;
         CoordinateReferenceSystem crs = null;
         double resolution = 1;
+        double[] inflate = null;
         Unit<?> unit = Units.PIXEL;
         if (geometry != null) {
+            if (geometry.isDefined(GridGeometry.CRS)) {
+                crs = geometry.getCoordinateReferenceSystem();
+            }
             if (geometry.isDefined(GridGeometry.GRID_TO_CRS)) {
                 localToCRS = geometry.getGridToCRS(PixelInCell.CELL_CENTER);
-                if (geometry.isDefined(GridGeometry.CRS)) {
-                    crs = geometry.getCoordinateReferenceSystem();
-                }
             }
             /*
-             * Computes the precision of coordinates to format. We use the finest resolution,
+             * Compute the precision of coordinates to format. We use the finest resolution,
              * looking only at axes having the same units of measurement than the first axis.
              * This will be used as a fallback if we can not compute the precision specific
              * to a coordinate, for example if we can not compute the derivative.
@@ -353,13 +403,14 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
             if (geometry.isDefined(GridGeometry.EXTENT)) {
                 final GridExtent extent = geometry.getExtent();
                 final int n = extent.getDimension();
-                inflatePrecisions = new double[n];
+                inflate = new double[n];
                 for (int i=0; i<n; i++) {
-                    inflatePrecisions[i] = (0.5 / extent.getSize(i)) + 1;
+                    inflate[i] = (0.5 / extent.getSize(i)) + 1;
                 }
             }
         }
         /*
+         * Remaining code should not fail, so we can modify `StatusBar` fields.
          * Prepare objects to be reused for each coordinate transformation.
          * Configure the `CoordinateFormat` with the CRS.
          */
@@ -371,53 +422,163 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
             targetCoordinates = new GeneralDirectPosition(BIDIMENSIONAL);
             sourceCoordinates = targetCoordinates.coordinates;      // Okay to share array
if same dimension.
         }
-        setDisplayCRS(crs);
+        objectiveCRS        = crs;
+        localToObjectiveCRS = localToTargetCRS = localToCRS;
+        inflatePrecisions   = inflate;
+        precisions          = null;
+        format.setDefaultCRS(crs);
         format.setGroundPrecision(Quantities.create(resolution, unit));
-        lastX = lastY = Double.NaN;
+        refresh();
     }
 
     /**
-     * Sets the coordinate reference systems to use for representing coordinates in status
bar.
+     * Invoked when the user selects a new reference system for the coordinates to show in
status bar.
      *
-     * @param  crs  the coordinate reference system to use for coordinates in the status
bar.
+     * @param  property  the {@link org.apache.sis.gui.referencing.MenuSync} property.
+     * @param  oldValue  the old reference system, or {@code null} if none.
+     * @param  newValue  the CRS to use for formatting coordinates in this status bar.
+     */
+    private void onSelectCRS(ObservableValue<? extends ReferenceSystem> property,
+                             ReferenceSystem oldValue, ReferenceSystem newValue)
+    {
+        setTargetCRS(newValue instanceof CoordinateReferenceSystem ? (CoordinateReferenceSystem)
newValue : null);
+    }
+
+    /**
+     * Sets the coordinate reference system of the coordinates shown in this status bar.
+     * The change may not appear immediately after method return; this method may use a
+     * background thread for computing the coordinate operation.
      */
-    private void setDisplayCRS(final ReferenceSystem crs) {
-        if (crs instanceof CoordinateReferenceSystem) {
-            format.setDefaultCRS((CoordinateReferenceSystem) crs);
+    private void setTargetCRS(final CoordinateReferenceSystem crs) {
+        if (objectiveCRS != null && objectiveCRS != crs) {
+            coordinates.setTextFill(Styles.OUTDATED_TEXT);
+            final Envelope aoi = (areaOfInterest != null) ? areaOfInterest.get() : null;
+            BackgroundThreads.execute(new Task<MathTransform>() {
+                /**
+                 * The operation used for computing the transform to target CRS.
+                 * This is used for configuring format with positional accuracy.
+                 */
+                private CoordinateOperation operation;
+
+                /**
+                 * Invoked in a background thread for fetching transformation to target CRS.
+                 * This operation may be long the first time that it is executed, but should
+                 * be fast on subsequent invocations.
+                 */
+                @Override protected MathTransform call() throws FactoryException {
+                    DefaultGeographicBoundingBox bbox = null;
+                    if (aoi != null) try {
+                        bbox = new DefaultGeographicBoundingBox();
+                        bbox.setBounds(aoi);
+                    } catch (TransformException e) {
+                        bbox = null;
+                        Logging.recoverableException(Logging.getLogger(Modules.APPLICATION),
+                                                     StatusBar.class, "setTargetCRS", e);
+                    }
+                    operation = CRS.findOperation(objectiveCRS, crs, bbox);
+                    return MathTransforms.concatenate(localToObjectiveCRS, operation.getMathTransform());
+                }
+
+                /**
+                 * Invoked in JavaFX thread on success. The {@link StatusBar#localToTargetCRS}
transform
+                 * is set to the transform that we computed in background and the {@link
CoordinateFormat}
+                 * is configured with auxiliary information such as positional accuracy.
+                 */
+                @Override protected void succeeded() {
+                    final CoordinateReferenceSystem targetCRS = operation.getTargetCRS();
+                    format.setDefaultCRS(targetCRS != null ? targetCRS : crs);
+                    localToTargetCRS = getValue();
+//                  TODO: CRS.getLinearAccuracy(op);
+                    coordinates.setTextFill(Styles.NORMAL_TEXT);
+                    refresh();
+                }
+
+                /**
+                 * Invoked in JavaFX thread on failure. The previous CRS is keep unchanged
but
+                 * the coordinates will appear in red for telling user that there is a problem.
+                 */
+                @Override protected void failed() {
+                    setErrorMessage(null, getException());
+                    resetTargetCRS(Styles.ERROR_TEXT);
+                }
+            });
         } else {
-            format.setDefaultCRS(null);
+            resetTargetCRS(Styles.NORMAL_TEXT);
         }
     }
 
     /**
-     * Returns the conversion from local coordinates to geographic or projected coordinates.
-     * The local coordinates are the coordinates of the view, as given for example in {@link
MouseEvent}.
+     * Resets {@link #localToTargetCRS} to its default value. This is invoked either when
the specified
+     * target CRS is {@link #objectiveCRS}, or when an attempt to use another CRS failed.
+     */
+    private void resetTargetCRS(final Color textFill) {
+        localToTargetCRS = localToObjectiveCRS;
+        format.setDefaultCRS(objectiveCRS);
+        coordinates.setTextFill(textFill);
+    }
+
+    /**
+     * Returns the reference systems used by the coordinates shown in this status bar.
+     * This is initially the same value than {@link #getObjectiveCRS()}, but may become
+     * different if the user selects another reference system through contextual menu.
+     *
+     * @return reference systems used by the coordinates shown in this status bar.
+     */
+    public final Optional<ReferenceSystem> getFormatReferenceSystem() {
+        return Optional.ofNullable(format.getDefaultCRS());
+    }
+
+    /**
+     * Returns the reference system used for rendering the data for which this status bar
is providing cursor coordinates.
+     * This is the "{@linkplain RecentReferenceSystems#setPreferred(boolean, ReferenceSystem)
preferred}" or native
+     * data CRS. It may not be the same than the CRS of coordinates actually shown in the
status bar.
+     *
+     * @return the reference system used for rendering the data for which this status bar
+     *         is providing cursor coordinates, or {@code null} if unknown.
+     *
+     * @see MapCanvas#getObjectiveCRS()
+     */
+    public final Optional<CoordinateReferenceSystem> getObjectiveCRS() {
+        return Optional.ofNullable(objectiveCRS);
+    }
+
+    /**
+     * Returns the 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}.
      * This is initially an identity transform and can be computed by {@link #applyCanvasGeometry(GridGeometry)}.
+     * This transform ignores all CRS changes resulting from user selecting a different CRS
in the contextual menu.
+     * This transform is usually (but not necessarily) affine.
      *
-     * @return conversion from local coordinates to "real world" coordinates.
+     * @return conversion from local coordinates to "real world" coordinates of rendered
data.
+     *         This is not necessarily the conversion to coordinates shown in the status
bar.
+     *
+     * @see MapCanvas#getObjectiveToDisplay()
      */
-    public final MathTransform getLocalToCRS() {
-        return localToCRS;
+    public final MathTransform getLocalToObjectiveCRS() {
+        return localToObjectiveCRS;
     }
 
     /**
-     * Sets the conversion from local coordinates to geographic or projected coordinates.
-     * The given value 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.
+     * Sets the conversion from local coordinates to geographic or projected coordinates
of rendered data.
+     * 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 conversion should have no {@linkplain CoordinateOperation#getCoordinateOperationAccuracy()
inaccuracy}
+     * (ignoring rounding error). 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.
      *
-     * @param  conversion  the new conversion from local coordinates to "real world" coordinates.
+     * @param  conversion  the new conversion from local coordinates to "real world" coordinates
of rendered data.
      * @throws MismatchedDimensionException if the number of dimensions is not the same than
previous conversion.
      */
-    public final void setLocalToCRS(final MathTransform conversion) {
+    public final void setLocalToObjectiveCRS(final MathTransform conversion) {
         ArgumentChecks.ensureNonNull("conversion", conversion);
-        int expected = localToCRS.getSourceDimensions();
+        int expected = localToObjectiveCRS.getSourceDimensions();
         int actual   = conversion.getSourceDimensions();
         if (expected == actual) {
-            expected = localToCRS.getTargetDimensions();
+            expected = localToObjectiveCRS.getTargetDimensions();
             actual   = conversion.getTargetDimensions();
             if (expected == actual) {
-                localToCRS = conversion;
+                localToObjectiveCRS = conversion;
+                setTargetCRS(format.getDefaultCRS());                           // Recompute
`localToTargetCRS`.
                 return;
             }
         }
@@ -439,6 +600,18 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
     }
 
     /**
+     * Rewrites the coordinates. This method is invoked after a change of coordinate reference
system.
+     */
+    private void refresh() {
+        final double x = lastX;
+        final double y = lastY;
+        lastX = lastY = Double.NaN;
+        if (!Double.isNaN(x) && !Double.isNaN(y)) {
+            setLocalCoordinates(x, y);
+        }
+    }
+
+    /**
      * Converts and formats the given pixel coordinates. Those coordinates will be automatically
      * converted to geographic or projected coordinates if a "local to CRS" conversion is
available.
      *
@@ -455,7 +628,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
             try {
                 Matrix derivative;
                 try {
-                    derivative = MathTransforms.derivativeAndTransform(localToCRS,
+                    derivative = MathTransforms.derivativeAndTransform(localToTargetCRS,
                             sourceCoordinates, 0, targetCoordinates.coordinates, 0);
                 } catch (TransformException ignore) {
                     /*
@@ -463,7 +636,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
                      * derivative calculation. Try again without derivative (the precision
will be set
                      * to the default resolution computed in `setCanvasGeometry(…)`).
                      */
-                    localToCRS.transform(sourceCoordinates, 0, targetCoordinates.coordinates,
0, 1);
+                    localToTargetCRS.transform(sourceCoordinates, 0, targetCoordinates.coordinates,
0, 1);
                     derivative = null;
                 }
                 if (derivative == null) {
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/RecentReferenceSystems.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/RecentReferenceSystems.java
index cba3feb..6355f7a 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/RecentReferenceSystems.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/RecentReferenceSystems.java
@@ -105,6 +105,10 @@ public class RecentReferenceSystems {
     /**
      * The area of interest, or {@code null} if none. This is used for filtering the reference
systems added by
      * {@code addAlternatives(…)} and for providing some guidance to user when {@link CRSChooser}
is shown.
+     *
+     * <div class="note"><b>API note:</b>
+     * 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.</div>
      */
     public final ObjectProperty<Envelope> areaOfInterest;
 
@@ -116,6 +120,10 @@ public class RecentReferenceSystems {
     /**
      * The comparison criterion for considering two reference systems as a duplication.
      * The default value is {@link ComparisonMode#ALLOW_VARIANT}, i.e. axis orders are ignored.
+     *
+     * <div class="note"><b>API note:</b>
+     * 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.</div>
      */
     public final ObjectProperty<ComparisonMode> duplicationCriterion;
 
@@ -182,7 +190,7 @@ public class RecentReferenceSystems {
 
     /**
      * {@code true} if {@code RecentReferenceSystems} is in the process of modifying {@link
#referenceSystems} list.
-     * In such we want to temporarily disable the {@link Listener}. This field is read and
updated in JavaFX thread.
+     * In such case we want to temporarily disable the {@link Listener}. This field is read
and updated in JavaFX thread.
      */
     private boolean isAdjusting;
 
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Styles.java
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Styles.java
index 735a77d..225d50e 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Styles.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Styles.java
@@ -81,6 +81,11 @@ public final class Styles extends Static {
     public static final Color CODE_TEXT = Color.LIGHTSLATEGREY;
 
     /**
+     * Color of text used for outdated information while a background thread is refreshing
data.
+     */
+    public static final Color OUTDATED_TEXT = Color.GRAY;
+
+    /**
      * Color of text shown in place of data that we failed to load.
      */
     public static final Color ERROR_TEXT = Color.RED;
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/geometry/CoordinateFormat.java
b/core/sis-referencing/src/main/java/org/apache/sis/geometry/CoordinateFormat.java
index b1e925d..db94513 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/geometry/CoordinateFormat.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/geometry/CoordinateFormat.java
@@ -319,6 +319,7 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
      * @param  crs  the default coordinate reference system, or {@code null} if none.
      */
     public void setDefaultCRS(final CoordinateReferenceSystem crs) {
+        isPrecisionApplied &= (crs == defaultCRS);
         defaultCRS = crs;
     }
 


Mime
View raw message