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

commit b4543619f15551bb5b6921caa6271b1015af47b5
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Mon Jun 20 12:08:27 2022 +0200

    Use the mouse position as the point where change in target canvas should be 
the same (in "real world" units) as the change in source canvas.
---
 .../org/apache/sis/gui/map/GestureFollower.java    | 41 +++++++++++++++++++---
 .../org/apache/sis/portrayal/CanvasFollower.java   | 38 +++++++++++++++-----
 .../org/apache/sis/geometry/DirectPosition2D.java  | 14 +++++++-
 .../transform/ConcatenatedTransform2D.java         |  2 +-
 .../transform/PassThroughTransform2D.java          |  2 +-
 5 files changed, 82 insertions(+), 15 deletions(-)

diff --git 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/GestureFollower.java
 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/GestureFollower.java
index b5744dba25..ea294276ef 100644
--- 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/GestureFollower.java
+++ 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/GestureFollower.java
@@ -19,6 +19,7 @@ package org.apache.sis.gui.map;
 import java.awt.geom.Point2D;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.NoninvertibleTransformException;
+import java.util.Optional;
 import java.util.logging.Logger;
 import javafx.scene.layout.Pane;
 import javafx.scene.paint.Color;
@@ -98,6 +99,12 @@ public class GestureFollower extends CanvasFollower 
implements EventHandler<Mous
      */
     public final BooleanProperty cursorEnabled;
 
+    /**
+     * Whether the {@link #cursorSourcePosition} is valid.
+     * If {@code true}, then {@link #cursor} shall be non-null and should be 
visible.
+     */
+    private boolean cursorSourceValid;
+
     /**
      * Cursor position of the mouse over source canvas, expressed in 
coordinates of the source and target canvas.
      */
@@ -125,8 +132,8 @@ public class GestureFollower extends CanvasFollower 
implements EventHandler<Mous
     public GestureFollower(final MapCanvas source, final MapCanvas target) {
         super(source, target);
         super.setDisabled(true);
-        cursorSourcePosition = new Point2D.Double(Double.NaN, Double.NaN);
-        cursorTargetPosition = new Point2D.Double(Double.NaN, Double.NaN);
+        cursorSourcePosition = new Point2D.Double();
+        cursorTargetPosition = new Point2D.Double();
         transformEnabled = new SimpleBooleanProperty(this, "transformEnabled");
         cursorEnabled    = new SimpleBooleanProperty(this, "cursorEnabled");
         transformEnabled.addListener((p,o,n) -> setDisabled(!n));
@@ -156,7 +163,9 @@ public class GestureFollower extends CanvasFollower 
implements EventHandler<Mous
             pane.addEventHandler(MouseEvent.MOUSE_EXITED,  this);
             pane.addEventHandler(MouseEvent.MOUSE_MOVED,   this);
             pane.addEventHandler(MouseEvent.MOUSE_DRAGGED, this);
+            cursorSourceValid = true;
         } else {
+            cursorSourceValid = false;
             pane.removeEventHandler(MouseEvent.MOUSE_ENTERED, this);
             pane.removeEventHandler(MouseEvent.MOUSE_EXITED,  this);
             pane.removeEventHandler(MouseEvent.MOUSE_MOVED,   this);
@@ -167,6 +176,26 @@ public class GestureFollower extends CanvasFollower 
implements EventHandler<Mous
         }
     }
 
+    /**
+     * Returns the position for the mouse cursor in the source canvas if that 
position is known.
+     * This information is used when the source and target canvases do not use 
the same CRS.
+     * {@code GestureFollower} tries to transform the canvas views in such a 
way that the
+     * "real world" change is the same for both canvas at that location.
+     *
+     * <p>The returned value is "live"; it may change with mouse and gesture 
events.
+     * Callers should not modify that value, and copy it if they need to keep 
it.</p>
+     *
+     * @return mouse position in source canvas where displacements, zooms and 
rotations
+     *         applied on the source canvas should be mirrored exactly on the 
target canvas.
+     */
+    @Override
+    public Optional<Point2D> getSourceDisplayPOI() {
+        if (cursorSourceValid) {
+            return Optional.of(cursorSourcePosition);
+        }
+        return super.getSourceDisplayPOI();
+    }
+
     /**
      * Invoked when the mouse position changed. This method should be invoked 
only if
      * {@link #cursorEnabled} is {@code true} (this is not verified by this 
method).
@@ -177,6 +206,7 @@ public class GestureFollower extends CanvasFollower 
implements EventHandler<Mous
     public void handle(final MouseEvent event) {
         cursorSourcePosition.x = event.getX();
         cursorSourcePosition.y = event.getY();
+        cursorSourceValid = true;
         final EventType<? extends MouseEvent> type = event.getEventType();
         if (type == MouseEvent.MOUSE_MOVED) {
             updateCursorPosition();
@@ -199,8 +229,9 @@ public class GestureFollower extends CanvasFollower 
implements EventHandler<Mous
             cursor.setTranslateX(p.getX());
             cursor.setTranslateY(p.getY());
         } catch (TransformException e) {
-            
Logging.recoverableException(Logger.getLogger(Modules.APPLICATION), 
GestureFollower.class, "handle", e);
+            cursorSourceValid = false;
             cursor.setVisible(false);
+            
Logging.recoverableException(Logger.getLogger(Modules.APPLICATION), 
GestureFollower.class, "handle", e);
         }
     }
 
@@ -231,9 +262,10 @@ public class GestureFollower extends CanvasFollower 
implements EventHandler<Mous
     @Override
     protected void transformedSource(final TransformChangeEvent event) {
         super.transformedSource(event);
-        if (cursor != null) {
+        if (cursorSourceValid) {
             final AffineTransform change = 
event.getDisplayChange2D().orElse(null);
             if (change == null) {
+                cursorSourceValid = false;
                 cursor.setVisible(false);
             } else if (event.getReason() != 
TransformChangeEvent.Reason.INTERIM) {
                 change.transform(cursorSourcePosition, cursorSourcePosition);
@@ -242,6 +274,7 @@ public class GestureFollower extends CanvasFollower 
implements EventHandler<Mous
                 change.inverseTransform(cursorSourcePosition, 
cursorSourcePosition);
                 updateCursorPosition();
             } catch (NoninvertibleTransformException e) {
+                cursorSourceValid = false;
                 cursor.setVisible(false);
             }
         }
diff --git 
a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasFollower.java 
b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasFollower.java
index c8c0880d31..ffae15f06c 100644
--- 
a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasFollower.java
+++ 
b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasFollower.java
@@ -18,6 +18,7 @@ package org.apache.sis.portrayal;
 
 import java.util.Optional;
 import java.util.logging.Logger;
+import java.awt.geom.Point2D;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.NoninvertibleTransformException;
 import java.beans.PropertyChangeEvent;
@@ -34,6 +35,7 @@ import org.apache.sis.util.Disposable;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.NullArgumentException;
 import org.apache.sis.util.logging.Logging;
+import org.apache.sis.geometry.DirectPosition2D;
 import org.apache.sis.internal.system.Modules;
 import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
 import org.apache.sis.referencing.operation.matrix.AffineTransforms2D;
@@ -246,15 +248,12 @@ public class CanvasFollower implements 
PropertyChangeListener, Disposable {
      * at that location only. At all other locations, the "real world" 
coordinate changes
      * may differ because of map projection deformations.
      *
-     * <p>The default implementation is as below. Subclasses can override this 
method for
-     * using a different point of interest, for example at the location of 
mouse cursor.</p>
+     * <p>The default implementation computes the value from {@link 
#getSourceDisplayPOI()}
+     * if present, or fallback on {@code source.getPointOfInterest(true)} 
otherwise.
+     * Subclasses can override this method for using a different point of 
interest.</p>
      *
-     * {@preformat java
-     *     return source.getPointOfInterest(true);
-     * }
-     *
-     * The CRS associated to the position shall be {@link 
PlanarCanvas#getObjectiveCRS()}.
-     * For performance reason, this is not verified by this {@code 
CanvasFollower} class.
+     * <p>The CRS associated to the position shall be {@link 
PlanarCanvas#getObjectiveCRS()}.
+     * For performance reason, this is not verified by this {@code 
CanvasFollower} class.</p>
      *
      * @return objective coordinates in source canvas where displacements, 
zooms and rotations
      *         applied on the source canvas should be mirrored exactly on the 
target canvas.
@@ -262,9 +261,32 @@ public class CanvasFollower implements 
PropertyChangeListener, Disposable {
      * @see PlanarCanvas#getPointOfInterest(boolean)
      */
     public DirectPosition getSourceObjectivePOI() {
+        final Point2D p = getSourceDisplayPOI().orElse(null);
+        if (p != null) try {
+            final DirectPosition2D poi = new DirectPosition2D(p);
+            source.objectiveToDisplay.inverseTransform(poi, poi);
+            return poi;
+        } catch (NoninvertibleTransformException e) {
+            canNotCompute("getSourceObjectivePOI", e);
+        }
         return source.getPointOfInterest(true);
     }
 
+    /**
+     * Returns the display coordinates of the Point Of Interest (POI) in 
source canvas.
+     * This method provides the same information than {@link 
#getSourceObjectivePOI()},
+     * but in units that are more convenient for expressing the location of 
mouse cursor
+     * for example.
+     *
+     * <p>The default implementation returns an empty value.</p>
+     *
+     * @return display coordinates in source canvas where displacements, zooms 
and rotations
+     *         applied on the source canvas should be mirrored exactly on the 
target canvas.
+     */
+    public Optional<Point2D> getSourceDisplayPOI() {
+        return Optional.empty();
+    }
+
     /**
      * Returns the transform from source display coordinates to target display 
coordinates.
      * This transform may change every time that a zoom; translation or 
rotation is applied
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition2D.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition2D.java
index f07f7951dc..2e488f5c05 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition2D.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition2D.java
@@ -59,7 +59,7 @@ import static 
org.apache.sis.util.ArgumentChecks.ensureDimensionMatches;
  * Collections that do not rely on hash codes, like {@code ArrayList}, are 
safe in all cases.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.3
+ * @version 1.3
  *
  * @see DirectPosition1D
  * @see GeneralDirectPosition
@@ -96,6 +96,18 @@ public class DirectPosition2D extends Point2D.Double 
implements DirectPosition,
         this.crs = crs;
     }
 
+    /**
+     * Constructs a 2D position from the coordinates of the specified point.
+     * The CRS is initialized to {@code null}.
+     *
+     * @param p  the point from which to copy the coordinate values.
+     *
+     * @since 1.3
+     */
+    public DirectPosition2D(final Point2D p) {
+        super(p.getX(), p.getY());
+    }
+
     /**
      * Constructs a 2D position from the specified coordinates. Despite their 
names,
      * the (<var>x</var>,<var>y</var>) coordinates don't need to be oriented 
toward
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform2D.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform2D.java
index 61207e1347..f7710f05de 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform2D.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform2D.java
@@ -100,7 +100,7 @@ final class ConcatenatedTransform2D extends 
ConcatenatedTransform implements Mat
     @Override
     public Matrix derivative(final Point2D point) throws TransformException {
         return super.derivative(point instanceof DirectPosition ?
-                (DirectPosition) point : new DirectPosition2D(point.getX(), 
point.getY()));
+                (DirectPosition) point : new DirectPosition2D(point));
     }
 
     /**
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform2D.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform2D.java
index 4915513f4d..cbec22f28b 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform2D.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform2D.java
@@ -89,7 +89,7 @@ final class PassThroughTransform2D extends 
PassThroughTransform implements MathT
     @Override
     public Matrix derivative(final Point2D point) throws TransformException {
         return super.derivative(point instanceof DirectPosition ?
-                (DirectPosition) point : new DirectPosition2D(point.getX(), 
point.getY()));
+                (DirectPosition) point : new DirectPosition2D(point));
     }
 
     /**

Reply via email to