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 b0e8264  Refactor StatusBar API with some method renaming, property 
definitions and javadoc completionS. No change in functionalities.
b0e8264 is described below

commit b0e82649c846ec32c630cd698ce57c46d7816443
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sat Apr 25 20:57:59 2020 +0200

    Refactor StatusBar API with some method renaming, property definitions and 
javadoc completionS.
    No change in functionalities.
---
 .../apache/sis/gui/coverage/CoverageControls.java  |   2 +-
 .../org/apache/sis/gui/coverage/ImageRequest.java  |   9 +-
 .../java/org/apache/sis/gui/map/StatusBar.java     | 371 +++++++++++----------
 3 files changed, 193 insertions(+), 189 deletions(-)

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 d62a0e7..5bef42d 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
@@ -80,7 +80,7 @@ final class CoverageControls extends Controls {
         view = new CoverageCanvas();
         view.setBackground(background);
         final StatusBar statusBar = new StatusBar(referenceSystems);
-        statusBar.setCanvas(view);
+        statusBar.canvas.set(view);
         imageAndStatus = new BorderPane(view.getView());
         imageAndStatus.setBottom(statusBar.getView());
         /*
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 7310a21..8147858 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
@@ -251,7 +251,7 @@ public class ImageRequest {
 
     /**
      * Configures the given status bar with the geometry of the grid coverage 
we have just read.
-     * This method is invoked by{@link 
GridView#onImageLoaded(WorkerStateEvent)} in JavaFX thread
+     * This method is invoked by {@link 
GridView#onImageLoaded(WorkerStateEvent)} in JavaFX thread
      * after {@link ImageLoader} successfully loaded in background thread a 
new image.
      */
     final void configure(final StatusBar bar) {
@@ -261,15 +261,16 @@ public class ImageRequest {
         /*
          * By `GridCoverage.render(GridExtent)` contract, the `RenderedImage` 
pixel coordinates are relative
          * to the requested `GridExtent`. Consequently we need to translate 
the image coordinates so that it
-         * become the coordinates of the original `GridGeometry` before to 
apply `gridToCRS`.
+         * become the coordinates of the original `GridGeometry` before to 
apply `gridToCRS`. It is okay to
+         * modify `StatusBar.localToObjectiveCRS` because we do not associate 
it to a `MapCanvas`.
          */
         if (request != null) {
             final double[] origin = new double[request.getDimension()];
             for (int i=0; i<origin.length; i++) {
                 origin[i] = request.getLow(i);
             }
-            bar.setLocalToObjectiveCRS(MathTransforms.concatenate(
-                    MathTransforms.translation(origin), 
bar.getLocalToObjectiveCRS()));
+            bar.localToObjectiveCRS.set(MathTransforms.concatenate(
+                    MathTransforms.translation(origin), 
bar.localToObjectiveCRS.get()));
         }
     }
 }
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 c418b5a..36ed7ce 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
@@ -37,6 +37,9 @@ import javafx.event.EventHandler;
 import javafx.event.EventType;
 import javafx.beans.value.ObservableValue;
 import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ObjectPropertyBase;
+import javafx.beans.property.ReadOnlyObjectProperty;
+import javafx.beans.property.ReadOnlyObjectPropertyBase;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.beans.value.ChangeListener;
 import javafx.collections.ListChangeListener;
@@ -138,14 +141,35 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
     private double lastX, lastY;
 
     /**
+     * The canvas that this status bar is tracking. The property value is 
{@code null} if this status bar is not
+     * associated to a canvas. After a non-null value has been set, this 
{@code StatusBar} will show coordinates
+     * (usually geographic or projected) of mouse cursor position when the 
mouse is over that canvas.
+     *
+     * <p>Note that if this property is set to a non-null value, then the 
{@link #localToObjectiveCRS} property
+     * value will be overwritten at any time (for example every time that a 
gesture event such as pan, zoom or
+     * rotation happens).</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>
+     *
+     * @see MapCanvas
+     */
+    public final ObjectProperty<MapCanvas> canvas;
+
+    /**
      * The manager of reference systems chosen by user, or {@code null} if 
none.
      * The {@link RecentReferenceSystems#areaOfInterest} property is used for
-     * computing {@link #objectiveToFormatCRS}.
+     * computing {@link #objectiveToPositionCRS}.
      */
     private final RecentReferenceSystems systemChooser;
 
     /**
-     * The selected reference system, or {@code null} if there is no such 
property.
+     * The selected reference system, or {@code null} if there is no such 
property. This property is provided
+     * by {@link RecentReferenceSystems}. It usually has the same value than 
{@link #positionReferenceSystem},
+     * but the values may temporarily differ between the time a CRS is 
selected and when it became applied.
+     *
+     * @see #positionReferenceSystem
      */
     private final ObjectProperty<ReferenceSystem> selectedSystem;
 
@@ -154,8 +178,6 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
      * 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 #getFormatReferenceSystem()
      * @see MapCanvas#getObjectiveCRS()
      */
     private CoordinateReferenceSystem objectiveCRS;
@@ -165,35 +187,66 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
      * The {@linkplain CoordinateOperation#getSourceCRS() source CRS} is 
{@link #objectiveCRS} and
      * the {@linkplain CoordinateOperation#getTargetCRS() target CRS} is 
{@link CoordinateFormat#getDefaultCRS()}.
      * This coordinate operation may be null if there is no CRS change to apply
-     * (in which case {@link #localToFormatCRS} is the same instance than 
{@link #localToObjectiveCRS})
+     * (in which case {@link #localToPositionCRS} is the same instance than 
{@link #localToObjectiveCRS})
      * or if the target is not a CRS (for example it may be a Military Grid 
Reference System (MGRS) code).
+     *
+     * @see #updateLocalToPositionCRS()
      */
-    private CoordinateOperation objectiveToFormatCRS;
+    private CoordinateOperation objectiveToPositionCRS;
 
     /**
      * 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.
+     * The local coordinates are the coordinates of the JavaFX view, as given 
for example in {@link MouseEvent}
+     * The objective coordinates are geographic or projected of rendered data, 
ignoring all CRS changes that may
+     * result from user selecting a different CRS in the contextual menu. 
Consequently while this conversion is often
+     * the conversion from pixel coordinates to the coordinates shown in this 
status bar, this is not always the case.
      *
-     * @see #getLocalToObjectiveCRS()
+     * <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 is normally the inverse of {@linkplain 
#canvas}
+     * {@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>
+     *
+     * <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>
+     *
+     * @see MapCanvas#getObjectiveCRS()
      * @see MapCanvas#getObjectiveToDisplay()
      */
-    private MathTransform localToObjectiveCRS;
+    public final ObjectProperty<MathTransform> localToObjectiveCRS;
+
+    /**
+     * The reference systems used by the coordinates shown in this status bar.
+     * This is initially the <cite>objective CRS</cite>, but may become 
different
+     * if the user selects another reference system through contextual menu.
+     *
+     * @see #position
+     */
+    public final ReadOnlyObjectProperty<ReferenceSystem> 
positionReferenceSystem;
 
     /**
      * Conversion from local coordinates to geographic or projected 
coordinates shown in this status bar.
-     * This is the concatenation of {@link #localToObjectiveCRS} with {@link 
#objectiveToFormatCRS} transform.
+     * This is the concatenation of {@link #localToObjectiveCRS} with {@link 
#objectiveToPositionCRS} transform.
      * The result is a transform to the user-selected CRS for coordinates 
shown 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 
CoordinateOperation#getTargetCRS()} on
-     * {@link #objectiveToFormatCRS} or by {@link 
CoordinateFormat#getDefaultCRS()}.</p>
+     * {@link #objectiveToPositionCRS} or by {@link 
CoordinateFormat#getDefaultCRS()}.</p>
      */
-    private MathTransform localToFormatCRS;
+    private MathTransform localToPositionCRS;
 
     /**
      * The source local indices before conversion to geospatial coordinates.
@@ -206,19 +259,18 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
     private double[] sourceCoordinates;
 
     /**
-     * Coordinates after conversion to the CRS. The number of dimensions 
depends on
-     * the target CRS. This object is reused during each coordinate 
transformation.
-     * Shall never be {@code null}.
+     * Coordinates after conversion to the CRS. The number of dimensions 
depends on the target CRS.
+     * This object is reused during each coordinate transformation. Shall 
never be {@code null}.
      *
      * @see #sourceCoordinates
      * @see #position
-     * @see #setTargetCRS(CoordinateReferenceSystem)
+     * @see #setPositionCRS(CoordinateReferenceSystem)
      */
     private GeneralDirectPosition targetCoordinates;
 
     /**
      * The desired precisions for each dimension in the {@link 
#targetCoordinates} to format.
-     * It may vary for each position if the {@link #localToFormatCRS} 
transform is non-linear.
+     * It may vary for each position if the {@link #localToPositionCRS} 
transform is non-linear.
      * This array is initially {@code null} and created when first needed.
      */
     private double[] precisions;
@@ -241,10 +293,12 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
 
     /**
      * The labels where to format the cursor position, either as coordinate 
values or other representations.
-     * The text is usually the result of formatting {@link #targetCoordinates} 
as numerical values,
+     * The text is usually the result of formatting coordinate values as 
numerical values,
      * but may also be other representations such as Military Grid Reference 
System (MGRS) codes.
+     *
+     * @see #positionReferenceSystem
      */
-    private final Label position;
+    protected final Label position;
 
     /**
      * The {@link #position} text to show when the mouse is outside the canvas 
area.
@@ -255,20 +309,11 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
     private String outsideText;
 
     /**
-     * The canvas that this status bar is tracking.
-     * The property value is {@code null} if there is none.
-     *
-     * @see #getCanvas()
-     * @see #setCanvas(MapCanvas)
-     */
-    public final ObjectProperty<MapCanvas> canvasProperty;
-
-    /**
      * The listener registered on {@link MapCanvas#renderingProperty()}, or 
{@code null} if the
      * listener has not yet been registered. This listener is remembered for 
allowing removal.
      *
-     * @see #setCanvas(MapCanvas)
-     * @see #onCanvasSpecified(ObservableValue, MapCanvas, MapCanvas)
+     * @see #canvas
+     * @see #onCanvasSpecified(MapCanvas, MapCanvas)
      */
     private ChangeListener<Boolean> renderingListener;
 
@@ -286,14 +331,15 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
      * @param  systemChooser  the manager of reference systems chosen by user, 
or {@code null} if none.
      */
     public StatusBar(final RecentReferenceSystems systemChooser) {
-        localToObjectiveCRS = MathTransforms.identity(BIDIMENSIONAL);
-        localToFormatCRS    = localToObjectiveCRS;
-        targetCoordinates   = new GeneralDirectPosition(BIDIMENSIONAL);
-        sourceCoordinates   = targetCoordinates.coordinates;
-        lastX = lastY       = Double.NaN;
-        format              = new CoordinateFormat();
-        position            = new Label();
-        message             = new Label();
+        positionReferenceSystem = new PositionSystem();
+        localToObjectiveCRS     = new LocalToObjective();
+        localToPositionCRS      = localToObjectiveCRS.get();
+        targetCoordinates       = new GeneralDirectPosition(BIDIMENSIONAL);
+        sourceCoordinates       = targetCoordinates.coordinates;
+        lastX = lastY           = Double.NaN;
+        format                  = new CoordinateFormat();
+        position                = new Label();
+        message                 = new Label();
         message.setVisible(false);                      // Waiting for getting 
a message to display.
         message.setTextFill(Styles.ERROR_TEXT);
         message.setMaxWidth(Double.POSITIVE_INFINITY);
@@ -303,8 +349,8 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
         view.setAlignment(Pos.CENTER_RIGHT);
         position.setAlignment(Pos.CENTER_RIGHT);
         position.setTextAlignment(TextAlignment.RIGHT);
-        canvasProperty = new SimpleObjectProperty<>(this, "canvas");
-        canvasProperty.addListener(this::onCanvasSpecified);
+        canvas = new SimpleObjectProperty<>(this, "canvas");
+        canvas.addListener((p,o,n) -> onCanvasSpecified(o,n));
         this.systemChooser = systemChooser;
         if (systemChooser == null) {
             selectedSystem = null;
@@ -321,7 +367,7 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
             systemChooser.getItems().addListener((ListChangeListener.Change<? 
extends ReferenceSystem> change) -> {
                 while (change.next()) {
                     if (change.wasAdded() || change.wasReplaced()) {
-                        setReplaceableTargetCRS(format.getDefaultCRS());
+                        setReplaceablePositionCRS(format.getDefaultCRS());
                         break;
                     }
                 }
@@ -329,11 +375,11 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
             /*
              * Create a contextual menu offering to user a choice of CRS in 
which to display the coordinates.
              * The CRS choices are controlled by `RecentReferenceSystems`. 
Selection of a new CRS causes the
-             * `setTargetCRS(…)` method to be invoked. Contextual menu can be 
invoked anywhere on the HBox;
+             * `setPositionCRS(…)` method to be invoked. Contextual menu can 
be invoked anywhere on the HBox;
              * we do not register this menu to `position` only because it is a 
relatively small area.
              */
             final Menu choices = systemChooser.createMenuItems((property, 
oldValue, newValue) -> {
-                setTargetCRS(newValue instanceof CoordinateReferenceSystem ? 
(CoordinateReferenceSystem) newValue : null);
+                setPositionCRS(newValue instanceof CoordinateReferenceSystem ? 
(CoordinateReferenceSystem) newValue : null);
             });
             selectedSystem = 
RecentReferenceSystems.getSelectedProperty(choices);
             final ContextMenu menu = new ContextMenu(choices);
@@ -356,38 +402,12 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
     }
 
     /**
-     * Returns the canvas that this status bar is tracking.
-     *
-     * @return canvas that this status bar is tracking, or {@code null} if 
none.
-     *
-     * @see #canvasProperty
-     */
-    public final MapCanvas getCanvas() {
-        return canvasProperty.get();
-    }
-
-    /**
-     * Sets the canvas that this status bar is tracking. After this method has 
been invoked,
-     * this {@code StatusBar} will show coordinates (usually geographic or 
projected) below
-     * mouse cursor when the mouse is over that canvas.
-     *
-     * @param  canvas  the canvas to track, or {@code null} if none.
-     *
-     * @see #canvasProperty
-     */
-    public final void setCanvas(final MapCanvas canvas) {
-        canvasProperty.set(canvas);
-    }
-
-    /**
-     * Invoked when a new value is set on {@link #canvasProperty}. Previous 
listeners (if any) are removed
+     * Invoked when a new value is set on {@link #canvas}. Previous listeners 
(if any) are removed
      * but new mouse listeners may not be added immediately. Instead if the 
canvas seems uninitialized, we
      * will wait for the first call to {@link #apply(GridGeometry)} before to 
add the listener. We do that
      * for avoiding to show irrelevant coordinate values.
      */
-    private void onCanvasSpecified(final ObservableValue<? extends MapCanvas> 
property,
-                                   final MapCanvas previous, final MapCanvas 
value)
-    {
+    private void onCanvasSpecified(final MapCanvas previous, final MapCanvas 
value) {
         if (previous != null) {
             previous.floatingPane.removeEventHandler(MouseEvent.MOUSE_ENTERED, 
this);
             previous.floatingPane.removeEventHandler(MouseEvent.MOUSE_EXITED,  
this);
@@ -432,7 +452,7 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
     /**
      * Listener notified when {@link MapCanvas} completed its rendering. This 
listener sets
      * {@link StatusBar#localToObjectiveCRS} to the inverse of {@link 
MapCanvas#objectiveToDisplay}.
-     * It assumes that even if the JavaFX local coordinates and {@link 
#localToFormatCRS} transform
+     * It assumes that even if the JavaFX local coordinates and {@link 
#localToPositionCRS} transform
      * changed, the "real world" coordinates under the mouse cursor is still 
the same. This assumption
      * should be true if this listener is notified as a result of zoom, 
translation or rotation events.
      */
@@ -441,7 +461,7 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
                                       final Boolean previous, final Boolean 
value)
         {
             if (!value) try {
-                apply(getCanvas().getGridGeometry());
+                apply(canvas.get().getGridGeometry());
                 /*
                  * Do not hide `position` since we assume that "real world" 
coordinates are still valid.
                  * Do not try to rewrite position neither since `lastX` and 
`lastY` are not valid anymore.
@@ -460,13 +480,13 @@ 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 local to the canvas 
to coordinate values in the CRS
-     *       (the {@linkplain #getLocalToObjectiveCRS() local to objective 
CRS} conversion).</li>
+     *       (the {@linkplain #localToObjectiveCRS 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 objective CRS" 
conversion can be updated
-     * after this method call with {@link 
#setLocalToObjectiveCRS(MathTransform)}.
+     * after this method call by setting the {@link #localToObjectiveCRS} 
property.
      *
      * @param  geometry  geometry of the coverage shown in {@link MapCanvas}, 
or {@code null}.
      *
@@ -537,6 +557,7 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
             }
         }
         final boolean sameCRS = Utilities.equalsIgnoreMetadata(objectiveCRS, 
crs);
+        ((LocalToObjective) localToObjectiveCRS).setNoCheck(localToCRS);
         /*
          * 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
@@ -555,18 +576,17 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
             sourceCoordinates = targetCoordinates.coordinates;      // Okay to 
share array if same dimension.
         }
         objectiveCRS        = crs;
-        localToObjectiveCRS = localToCRS;
-        localToFormatCRS    = localToCRS;                           // May be 
updated again below.
+        localToPositionCRS  = localToCRS;                           // May be 
updated again below.
         inflatePrecisions   = inflate;
         precisions          = null;
         lastX = lastY       = Double.NaN;                           // Not 
valid anymove — see above block comment.
         if (sameCRS) {
-            updateLocalToFormatCRS();
-            // Keep the format CRS unchanged since we made `localToFormatCRS` 
consistent with its value.
+            updateLocalToPositionCRS();
+            // Keep the format CRS unchanged since we made 
`localToPositionCRS` consistent with its value.
         } else {
-            objectiveToFormatCRS = null;
+            objectiveToPositionCRS = null;
             setFormatCRS(crs);                                      // Should 
be invoked before to set precision.
-            crs = setReplaceableTargetCRS(crs);                     // May 
invoke later setFormatCRS(…) again.
+            crs = setReplaceablePositionCRS(crs);                   // May 
invoke later setFormatCRS(…) again.
         }
         format.setGroundPrecision(Quantities.create(resolution, unit));
         /*
@@ -576,7 +596,7 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
          * irrelevant coordinates.
          */
         if (geometry != null && !isMouseListenerRegistered) {
-            registerMouseListeners(canvasProperty.getValue());
+            registerMouseListeners(canvas.get());
         }
         /*
          * If the CRS changed, we may need to update the selected menu item. 
It happens when this method
@@ -589,19 +609,20 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
     }
 
     /**
-     * Computes {@link #localToFormatCRS} after a change of {@link 
#localToObjectiveCRS}.
-     * Other properties, in particular {@link #objectiveToFormatCRS}, must be 
valid.
+     * Computes {@link #localToPositionCRS} after a change of {@link 
#localToObjectiveCRS}.
+     * Other properties, in particular {@link #objectiveToPositionCRS}, must 
be valid.
      */
-    private void updateLocalToFormatCRS() {
-        if (objectiveToFormatCRS != null) {
-            localToFormatCRS = MathTransforms.concatenate(localToObjectiveCRS, 
objectiveToFormatCRS.getMathTransform());
+    private void updateLocalToPositionCRS() {
+        if (objectiveToPositionCRS != null) {
+            localToPositionCRS = MathTransforms.concatenate(
+                    localToObjectiveCRS.get(), 
objectiveToPositionCRS.getMathTransform());
         }
     }
 
     /**
      * Sets the CRS of the position shown in this status bar after replacement 
by one of the available CRS
      * if a match is found. This method compares the given CRS with the list 
of choices before to delegate
-     * to {@link #setTargetCRS(CoordinateReferenceSystem)} possibly with 
different axis order. The typical
+     * to {@link #setPositionCRS(CoordinateReferenceSystem)} possibly with 
different axis order. A typical
      * scenario is {@link #apply(GridGeometry)} invoked with 
(<var>longitude</var>, <var>latitude</var>)
      * axis order, and this method swapping axes to standard 
(<var>latitude</var>, <var>longitude</var>)
      * axis order for coordinates display purpose.
@@ -611,7 +632,7 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
      *         and units than the specified CRS. This is the CRS that {@link 
CoordinateFormat#getDefaultCRS()}
      *         will return a little bit later (pending completion of a 
background task).
      */
-    private CoordinateReferenceSystem 
setReplaceableTargetCRS(CoordinateReferenceSystem crs) {
+    private CoordinateReferenceSystem 
setReplaceablePositionCRS(CoordinateReferenceSystem crs) {
         if (crs != null) {
             final ComparisonMode mode = 
systemChooser.duplicationCriterion.get();
             for (final ReferenceSystem system : systemChooser.getItems()) {
@@ -622,7 +643,7 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
             }
         }
         if (crs != format.getDefaultCRS()) {
-            setTargetCRS(crs);
+            setPositionCRS(crs);
         }
         return crs;
     }
@@ -635,22 +656,16 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
      *
      * @param  crs  the new CRS, or {@code null} for {@link #objectiveCRS}.
      */
-    private void setTargetCRS(final CoordinateReferenceSystem crs) {
+    private void setPositionCRS(final CoordinateReferenceSystem crs) {
         if (crs != null && objectiveCRS != null && objectiveCRS != crs) {
             position.setTextFill(Styles.OUTDATED_TEXT);
             final Envelope aoi = (systemChooser != null) ? 
systemChooser.areaOfInterest.get() : null;
-            BackgroundThreads.execute(new Task<MathTransform>() {
-                /**
-                 * The new {@link StatusBar#objectiveToFormatCRS} value if 
successful.
-                 */
-                private CoordinateOperation operation;
-
+            BackgroundThreads.execute(new Task<CoordinateOperation>() {
                 /**
                  * 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.
+                 * The potentially costly part is {@code CRS.findOperation(…)}.
                  */
-                @Override protected MathTransform call() throws 
FactoryException {
+                @Override protected CoordinateOperation call() throws 
FactoryException {
                     DefaultGeographicBoundingBox bbox = null;
                     if (aoi != null) try {
                         bbox = new DefaultGeographicBoundingBox();
@@ -658,20 +673,20 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
                     } catch (TransformException e) {
                         bbox = null;
                         
Logging.recoverableException(Logging.getLogger(Modules.APPLICATION),
-                                StatusBar.class, "setTargetCRS", e);
+                                StatusBar.class, "setPositionCRS", e);
                     }
-                    operation = CRS.findOperation(objectiveCRS, crs, bbox);
-                    return MathTransforms.concatenate(localToObjectiveCRS, 
operation.getMathTransform());
+                    return CRS.findOperation(objectiveCRS, crs, bbox);
                 }
 
                 /**
-                 * Invoked in JavaFX thread on success. The {@link 
StatusBar#localToFormatCRS} transform
+                 * Invoked in JavaFX thread on success. The {@link 
StatusBar#localToPositionCRS} 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 CoordinateOperation operation = getValue();
                     final CoordinateReferenceSystem targetCRS = 
operation.getTargetCRS();
-                    setFormatCRS(targetCRS != null ? targetCRS : crs, 
operation, getValue());
+                    setPositionCRS(targetCRS != null ? targetCRS : crs, 
operation);
                 }
 
                 /**
@@ -682,13 +697,13 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
                     final Locale locale = 
format.getLocale(Locale.Category.DISPLAY);
                     
setErrorMessage(Resources.forLocale(locale).getString(Resources.Keys.CanNotUseRefSys_1,
                                     IdentifiedObjects.getDisplayName(crs, 
locale)), getException());
-                    resetFormatCRS(Styles.ERROR_TEXT);
+                    resetPositionCRS(Styles.ERROR_TEXT);
                 }
             });
         } 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 
`localToFormatCRS`.
+             * 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.
@@ -700,7 +715,7 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
                 objectiveCRS = crs;
             }
             position.setMinWidth(0);
-            resetFormatCRS(Styles.NORMAL_TEXT);
+            resetPositionCRS(Styles.NORMAL_TEXT);
         }
     }
 
@@ -711,15 +726,12 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
      * only the format CRS has been updated and not {@link 
#localToObjectiveCRS}.
      *
      * @param  crs        the new CRS. Should not be {@code null}.
-     * @param  operation  the new value to assign to {@link 
#objectiveToFormatCRS}
-     * @param  complete   the concatenation of {@link #localToObjectiveCRS} 
with {@code operation}.
+     * @param  operation  the new value to assign to {@link 
#objectiveToPositionCRS}
      */
-    private void setFormatCRS(final CoordinateReferenceSystem crs,
-            final CoordinateOperation operation, final MathTransform complete)
-    {
+    private void setPositionCRS(final CoordinateReferenceSystem crs, final 
CoordinateOperation operation) {
         setFormatCRS(crs);
-        objectiveToFormatCRS = operation;
-        localToFormatCRS = complete;
+        objectiveToPositionCRS = operation;
+        updateLocalToPositionCRS();
 //      TODO: CRS.getLinearAccuracy(op);
         position.setTextFill(Styles.NORMAL_TEXT);
         position.setMinWidth(0);
@@ -736,11 +748,13 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
 
     /**
      * Sets the {@link CoordinateFormat} default CRS together with the tool 
tip text.
-     * Caller is responsible to setup transforms ({@link #localToFormatCRS}, 
<i>etc</i>).
+     * Caller is responsible to setup transforms ({@link #localToPositionCRS}, 
<i>etc</i>).
+     * For the method that apply required changes on transforms before to set 
the format CRS,
+     * see {@link #setPositionCRS(CoordinateReferenceSystem)}.
      *
      * @param  crs  the new {@link #format} reference system.
      *
-     * @see #getFormatReferenceSystem()
+     * @see #positionReferenceSystem
      */
     private void setFormatCRS(final CoordinateReferenceSystem crs) {
         format.setDefaultCRS(crs);
@@ -786,86 +800,75 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
             position.setText(text);
         }
         outsideText = text;
+        ((PositionSystem) positionReferenceSystem).fireValueChangedEvent();
     }
 
     /**
-     * Resets {@link #localToFormatCRS} to its default value. This is invoked 
either when the
-     * target CRS is {@link #objectiveCRS}, or when an attempt to use another 
CRS failed.
+     * Implementation of {@link #positionReferenceSystem} property.
      */
-    private void resetFormatCRS(final Color textFill) {
-        objectiveToFormatCRS = null;
-        localToFormatCRS = localToObjectiveCRS;
-        setFormatCRS(objectiveCRS);
-        position.setTextFill(textFill);
+    private final class PositionSystem extends 
ReadOnlyObjectPropertyBase<ReferenceSystem> {
+        @Override public Object          getBean()       {return 
StatusBar.this;}
+        @Override public String          getName()       {return 
"positionReferenceSystem";}
+        @Override public ReferenceSystem get()           {return 
format.getDefaultCRS();}
+        @Override protected void fireValueChangedEvent() 
{super.fireValueChangedEvent();}
     }
 
     /**
-     * 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.
+     * Resets {@link #localToPositionCRS} to its default value. This is 
invoked either when the
+     * target CRS is {@link #objectiveCRS}, or when an attempt to use another 
CRS failed.
      */
-    public final Optional<ReferenceSystem> getFormatReferenceSystem() {
-        return Optional.ofNullable(format.getDefaultCRS());
+    private void resetPositionCRS(final Color textFill) {
+        objectiveToPositionCRS = null;
+        localToPositionCRS = localToObjectiveCRS.get();
+        setFormatCRS(objectiveCRS);
+        position.setTextFill(textFill);
     }
 
     /**
-     * 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.
+     * Implementation of {@link #localToObjectiveCRS} property performing 
argument check before to change its value.
+     * When a new value is set, the given transform must have the same number 
of source and target dimensions than
+     * the previous value. 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.
      *
-     * @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()
+     * @see #localToObjectiveCRS
      */
-    public final Optional<CoordinateReferenceSystem> getObjectiveCRS() {
-        return Optional.ofNullable(objectiveCRS);
-    }
+    private final class LocalToObjective extends 
ObjectPropertyBase<MathTransform> {
+        LocalToObjective() {super(MathTransforms.identity(BIDIMENSIONAL));}
 
-    /**
-     * 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 
of rendered data.
-     *         This is not necessarily the conversion to coordinates shown in 
the status bar.
-     *
-     * @see MapCanvas#getObjectiveToDisplay()
-     */
-    public final MathTransform getLocalToObjectiveCRS() {
-        return localToObjectiveCRS;
-    }
+        @Override public Object getBean() {return StatusBar.this;}
+        @Override public String getName() {return "localToObjectiveCRS";}
 
-    /**
-     * 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 of rendered data.
-     * @throws MismatchedDimensionException if the number of dimensions is not 
the same than previous conversion.
-     */
-    public final void setLocalToObjectiveCRS(final MathTransform conversion) {
-        ArgumentChecks.ensureNonNull("conversion", conversion);
-        int expected = localToObjectiveCRS.getSourceDimensions();
-        int actual   = conversion.getSourceDimensions();
-        if (expected == actual) {
-            expected = localToObjectiveCRS.getTargetDimensions();
-            actual   = conversion.getTargetDimensions();
+        /**
+         * Overwrite previous value without any check. This method is invoked 
when the {@link #objectiveCRS}
+         * is changed in same time the {@link #localToObjectiveCRS} transform.
+         */
+        final void setNoCheck(final MathTransform newValue) {
+            super.set(newValue);
+        }
+
+        /**
+         * Sets the conversion from local coordinates to geographic or 
projected coordinates of rendered data.
+         *
+         * @param  newValue  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.
+         */
+        @Override public void set(final MathTransform newValue) {
+            ArgumentChecks.ensureNonNull("newValue", newValue);
+            final MathTransform oldValue = get();
+            int expected = oldValue.getSourceDimensions();
+            int actual   = newValue.getSourceDimensions();
             if (expected == actual) {
-                localToObjectiveCRS = conversion;
-                updateLocalToFormatCRS();
-                return;
+                expected = oldValue.getTargetDimensions();
+                actual   = newValue.getTargetDimensions();
+                if (expected == actual) {
+                    super.set(newValue);
+                    updateLocalToPositionCRS();
+                    return;
+                }
             }
+            throw new MismatchedDimensionException(Errors.format(
+                    Errors.Keys.MismatchedDimension_3, "newValue", expected, 
actual));
         }
-        throw new MismatchedDimensionException(Errors.format(
-                Errors.Keys.MismatchedDimension_3, "conversion", expected, 
actual));
     }
 
     /**
@@ -898,7 +901,7 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
             try {
                 Matrix derivative;
                 try {
-                    derivative = 
MathTransforms.derivativeAndTransform(localToFormatCRS,
+                    derivative = 
MathTransforms.derivativeAndTransform(localToPositionCRS,
                             sourceCoordinates, 0, 
targetCoordinates.coordinates, 0);
                 } catch (TransformException ignore) {
                     /*
@@ -906,7 +909,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(…)`).
                      */
-                    localToFormatCRS.transform(sourceCoordinates, 0, 
targetCoordinates.coordinates, 0, 1);
+                    localToPositionCRS.transform(sourceCoordinates, 0, 
targetCoordinates.coordinates, 0, 1);
                     derivative = null;
                 }
                 if (derivative == null) {

Reply via email to