sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/04: Show the geographic coordinates of pixel under mouse pointer.
Date Mon, 02 Mar 2020 19:33:16 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

commit ab5f51619be302a03a71132b0a961c131441221b
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Mon Mar 2 18:57:34 2020 +0100

    Show the geographic coordinates of pixel under mouse pointer.
---
 .../org/apache/sis/gui/coverage/CoverageView.java  | 58 ++++++++++++++++++----
 .../org/apache/sis/gui/coverage/GridViewSkin.java  |  5 +-
 .../org/apache/sis/gui/coverage/StatusBar.java     | 42 ++++++++++++----
 3 files changed, 86 insertions(+), 19 deletions(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageView.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageView.java
index 1b46e1d..3f54ac3 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageView.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageView.java
@@ -23,6 +23,7 @@ import java.nio.IntBuffer;
 import java.awt.Graphics2D;
 import java.awt.GraphicsConfiguration;
 import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
 import java.awt.image.BufferedImage;
 import java.awt.image.DataBufferInt;
 import java.awt.image.RenderedImage;
@@ -35,12 +36,14 @@ import javafx.scene.image.WritableImage;
 import javafx.scene.paint.Color;
 import javafx.scene.layout.Pane;
 import javafx.scene.layout.Region;
+import javafx.scene.layout.BorderPane;
 import javafx.scene.layout.Background;
 import javafx.scene.layout.BackgroundFill;
 import javafx.beans.value.ObservableValue;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.concurrent.Task;
+import javafx.scene.input.MouseEvent;
 import javafx.util.Callback;
 import org.opengis.referencing.datum.PixelInCell;
 import org.apache.sis.coverage.grid.GridCoverage;
@@ -52,6 +55,7 @@ import org.apache.sis.internal.gui.ExceptionReporter;
 import org.apache.sis.internal.gui.ImageRenderings;
 import org.apache.sis.internal.map.PlanarCanvas;
 import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.util.collection.BackingStoreException;
 
 
 /**
@@ -151,7 +155,12 @@ final class CoverageView extends PlanarCanvas {
      * {@linkplain #image} to show, but can also contain additional nodes for geometric shapes,
      * texts, <i>etc</i>.
      */
-    private final Pane view;
+    private final Pane imageRegion;
+
+    /**
+     * The image together with the status bar.
+     */
+    private final BorderPane imageAndStatus;
 
     /**
      * The transform from {@link #data} pixel coordinates to {@link #buffer} (and {@link
#image})
@@ -162,7 +171,7 @@ final class CoverageView extends PlanarCanvas {
     private final AffineTransform dataToImage;
 
     /**
-     * Incremented when {@link #data} changed.
+     * Incremented when {@link #data} or {@link #dataToImage} changed.
      *
      * @see #renderedDataStamp
      */
@@ -177,6 +186,11 @@ final class CoverageView extends PlanarCanvas {
     private int renderedDataStamp;
 
     /**
+     * The bar where to format the coordinates below mouse cursor.
+     */
+    private final StatusBar statusBar;
+
+    /**
      * Creates a new two-dimensional canvas for {@link RenderedImage}.
      */
     public CoverageView() {
@@ -186,7 +200,7 @@ final class CoverageView extends PlanarCanvas {
         dataAlternatives       = new EnumMap<>(RangeType.class);
         dataToImage            = new AffineTransform();
         currentDataAlternative = RangeType.DECLARED;
-        view = new Pane() {
+        imageRegion = new Pane() {
             @Override protected void layoutChildren() {
                 super.layoutChildren();
                 repaint();
@@ -194,7 +208,10 @@ final class CoverageView extends PlanarCanvas {
         };
         image = new ImageView();
         image.setPreserveRatio(true);
-        view.getChildren().add(image);
+        imageRegion.getChildren().add(image);
+        imageAndStatus = new BorderPane(imageRegion);
+        statusBar = new StatusBar(this::toImageCoordinates);
+        imageAndStatus.setBottom(statusBar);
         /*
          * Do not set a preferred size, otherwise `repaint()` is invoked twice: once with
the preferred size
          * and once with the actual size of the parent window. Actually the `repaint()` method
appears to be
@@ -203,6 +220,9 @@ final class CoverageView extends PlanarCanvas {
          */
         coverageProperty   .addListener(this::onImageSpecified);
         sliceExtentProperty.addListener(this::onImageSpecified);
+        imageRegion.setOnMouseMoved(this::onMouveMoved);
+        imageRegion.setOnMouseEntered(statusBar);
+        imageRegion.setOnMouseExited (statusBar);
     }
 
     /**
@@ -220,7 +240,7 @@ final class CoverageView extends PlanarCanvas {
      * @return the region to show.
      */
     public final Region getView() {
-        return view;
+        return imageAndStatus;
     }
 
     /**
@@ -303,6 +323,7 @@ final class CoverageView extends PlanarCanvas {
             bufferWrapper = null;
         } else {
             final GridExtent sliceExtent = getSliceExtent();
+            statusBar.setCoordinateConversion(coverage.getGridGeometry(), sliceExtent);
             execute(new Task<RenderedImage>() {
                 /** Invoked in background thread for fetching the image. */
                 @Override protected RenderedImage call() {
@@ -374,7 +395,7 @@ final class CoverageView extends PlanarCanvas {
         if (alt != data) {
             data = alt;
             dataChangeCount++;
-            view.requestLayout();
+            imageRegion.requestLayout();
         }
     }
 
@@ -382,7 +403,7 @@ final class CoverageView extends PlanarCanvas {
      * Sets the background, as a color for now but more patterns my be allowed in a future
version.
      */
     final void setBackground(final Color color) {
-        view.setBackground(new Background(new BackgroundFill(color, null, null)));
+        imageRegion.setBackground(new Background(new BackgroundFill(color, null, null)));
     }
 
     /**
@@ -399,8 +420,8 @@ final class CoverageView extends PlanarCanvas {
         if (data == null) {
             return;
         }
-        final int width  = Numerics.clamp(Math.round(view.getWidth()));
-        final int height = Numerics.clamp(Math.round(view.getHeight()));
+        final int width  = Numerics.clamp(Math.round(imageRegion.getWidth()));
+        final int height = Numerics.clamp(Math.round(imageRegion.getHeight()));
         if (width <= 0 || height <= 0) {
             return;
         }
@@ -578,4 +599,23 @@ final class CoverageView extends PlanarCanvas {
     private void errorOccurred(final Throwable ex) {
         ExceptionReporter.show(null, null, ex);
     }
+
+    /**
+     * Invoked when the mouse moved. This method update the coordinates below mouse cursor.
+     */
+    private void onMouveMoved(final MouseEvent event) {
+        statusBar.setCoordinates((int) Math.round(event.getX()),
+                                 (int) Math.round(event.getY()));
+    }
+
+    /**
+     * Converts pixel indices in the window to pixel indices in the image.
+     */
+    private void toImageCoordinates(final double[] indices) {
+        try {
+            dataToImage.inverseTransform(indices, 0, indices, 0, 1);
+        } catch (NoninvertibleTransformException e) {
+            throw new BackingStoreException(e);         // Will be unwrapped by the caller
+        }
+    }
 }
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java
index a1323c7..1e2b46d 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java
@@ -181,8 +181,10 @@ final class GridViewSkin extends VirtualContainerBase<GridView, GridRow>
impleme
         flow.setOnMouseExited(this::hideSelection);
         /*
          * The status bar where to show coordinates of selected cell.
+         * Mouse exit event is handled by `hideSelection(…)`.
          */
-        statusBar = new StatusBar(view);
+        statusBar = new StatusBar(view::toImageCoordinates);
+        flow.setOnMouseEntered(statusBar);
         /*
          * The list of children is initially empty. We need to
          * add the virtual flow, otherwise nothing will appear.
@@ -230,6 +232,7 @@ final class GridViewSkin extends VirtualContainerBase<GridView, GridRow>
impleme
         selection     .setVisible(false);
         selectedRow   .setVisible(false);
         selectedColumn.setVisible(false);
+        statusBar     .handle    (null);        // Hide the coordinates.
     }
 
     /**
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/StatusBar.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/StatusBar.java
index 7526659..f7e92d0 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/StatusBar.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/StatusBar.java
@@ -17,12 +17,15 @@
 package org.apache.sis.gui.coverage;
 
 import java.util.Locale;
+import java.util.function.Consumer;
 import javax.measure.Unit;
 import javafx.geometry.Insets;
 import javafx.geometry.Pos;
 import javafx.scene.layout.HBox;
 import javafx.scene.control.Label;
 import javafx.scene.control.Tooltip;
+import javafx.scene.input.MouseEvent;
+import javafx.event.EventHandler;
 import org.opengis.referencing.datum.PixelInCell;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.operation.Matrix;
@@ -37,22 +40,30 @@ import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.measure.Units;
 import org.apache.sis.util.Classes;
+import org.apache.sis.util.Exceptions;
 
 
 /**
  * A status bar showing coordinates of a grid cell.
  * The number of fraction digits is adjusted according pixel resolution for each coordinate
to format.
  *
+ * <p>Callers can register this object to {@link javafx.scene.Node#setOnMouseEntered(EventHandler)}
and
+ * {@link javafx.scene.Node#setOnMouseExited(EventHandler)} for showing or hiding the coordinate
values
+ * when the mouse enter or exit the region of interest.</p>
+ *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.1
  * @since   1.1
  * @module
  */
-final class StatusBar extends HBox {
+final class StatusBar extends HBox implements EventHandler<MouseEvent> {
     /**
-     * The view for which we are showing geographic or projected coordinates of selected
cell.
+     * A function which take cell indices in input and gives pixel coordinates in output.
+     * The input and output coordinates are in the given array, which is updated in-place.
+     *
+     * @see GridView#toImageCoordinates(double[])
      */
-    private final GridView view;
+    private final Consumer<double[]> toImageCoordinates;
 
     /**
      * Zero-based cell coordinates currently formatted in the {@link #coordinates} field.
@@ -97,8 +108,8 @@ final class StatusBar extends HBox {
     /**
      * Creates a new status bar.
      */
-    StatusBar(final GridView view) {
-        this.view = view;
+    StatusBar(final Consumer<double[]> toImageCoordinates) {
+        this.toImageCoordinates = toImageCoordinates;
         format = new CoordinateFormat();
         coordinates = new Label();
         setAlignment(Pos.CENTER_RIGHT);
@@ -190,9 +201,9 @@ final class StatusBar extends HBox {
         if (x != column || y != row) {
             sourceCoordinates[0] = column = x;
             sourceCoordinates[1] = row    = y;
-            view.toImageCoordinates(sourceCoordinates);
             String text;
             try {
+                toImageCoordinates.accept(sourceCoordinates);
                 Matrix derivative;
                 try {
                     derivative = MathTransforms.derivativeAndTransform(gridToCRS,
@@ -230,16 +241,29 @@ final class StatusBar extends HBox {
                 }
                 format.setPrecisions(precisions);
                 text = format.format(targetCoordinates);
-            } catch (TransformException e) {
+            } catch (TransformException | RuntimeException e) {
                 /*
                  * If even the fallback without derivative failed, show the error message.
                  */
-                text = e.getLocalizedMessage();
+                Throwable cause = Exceptions.unwrap(e);
+                text = cause.getLocalizedMessage();
                 if (text == null) {
-                    text = Classes.getShortClassName(e);
+                    text = Classes.getShortClassName(cause);
                 }
             }
             coordinates.setText(text);
         }
     }
+
+    /**
+     * Shows or hide the coordinates depending on whether the mouse entered or exited
+     * the region for which this status bar is providing information.
+     *
+     * <p>For the convenience of {@link GridViewSkin}, a null value is equivalent to
+     * a mouse exit event.</p>
+     */
+    @Override
+    public final void handle(final MouseEvent event) {
+        coordinates.setVisible(event != null && event.getEventType() != MouseEvent.MOUSE_EXITED);
+    }
 }


Mime
View raw message