You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2022/06/20 15:12:41 UTC

[sis] 03/03: 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.

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 <ma...@geomatys.com>
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));
     }
 
     /**