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: Zoom out for showing the full coverage.
Date Wed, 04 Mar 2020 12:00:51 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 5d63cb1  Zoom out for showing the full coverage.
5d63cb1 is described below

commit 5d63cb137098eb4973890d16ce45e5de657f7b68
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Wed Mar 4 13:00:35 2020 +0100

    Zoom out for showing the full coverage.
---
 .../org/apache/sis/gui/coverage/CoverageView.java  | 84 +++++++++++++++++-----
 .../org/apache/sis/coverage/grid/GridExtent.java   | 21 +++++-
 .../sis/referencing/operation/matrix/Matrices.java | 53 ++++++++++++++
 3 files changed, 139 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 746c5c2..57b1a40 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
@@ -33,14 +33,21 @@ import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.concurrent.Task;
 import javafx.scene.input.MouseEvent;
+import org.opengis.geometry.Envelope;
 import org.opengis.referencing.datum.PixelInCell;
+import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.internal.gui.ImageRenderings;
-import org.apache.sis.util.collection.BackingStoreException;
+import org.apache.sis.internal.map.RenderException;
 import org.apache.sis.gui.map.MapCanvas;
+import org.apache.sis.util.collection.BackingStoreException;
+import org.apache.sis.referencing.operation.matrix.Matrices;
+import org.apache.sis.referencing.operation.matrix.MatrixSIS;
 import org.apache.sis.referencing.operation.matrix.AffineTransforms2D;
+import org.apache.sis.referencing.operation.transform.LinearTransform;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
 
 
 /**
@@ -122,6 +129,12 @@ final class CoverageView extends MapCanvas {
     private final StatusBar statusBar;
 
     /**
+     * Whether {@link #objectiveToDisplay} needs to be recomputed.
+     * We differ this recomputation until all parameters are known.
+     */
+    private boolean invalidObjectiveToDisplay;
+
+    /**
      * Creates a new two-dimensional canvas for {@link RenderedImage}.
      */
     public CoverageView() {
@@ -209,6 +222,13 @@ final class CoverageView extends MapCanvas {
     }
 
     /**
+     * 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)));
+    }
+
+    /**
      * Starts a background task for loading data, computing slice or rendering the data in
a {@link CoverageView}.
      *
      * <p>Tasks need to be careful to not use any {@link CoverageView} field in their
{@link Task#call()}
@@ -314,6 +334,8 @@ final class CoverageView extends MapCanvas {
     /**
      * Invoked when a new image has been successfully loaded.
      *
+     * @todo Needs to handle non-affine transform.
+     *
      * @param  image        the image to load.
      * @param  geometry     the grid geometry of the coverage that produced the image.
      * @param  sliceExtent  the extent that were requested.
@@ -324,24 +346,17 @@ final class CoverageView extends MapCanvas {
         statusBar.setCoordinateConversion(geometry, sliceExtent);
         try {
             gridToCRS = AffineTransforms2D.castOrCopy(geometry.getGridToCRS(PixelInCell.CELL_CENTER));
-
-            // TODO: scale according the display bounds.
-            setObjectiveToDisplay(new org.apache.sis.internal.referencing.j2d.AffineTransform2D(gridToCRS.createInverse()));
-        } catch (Exception e) {
+        } catch (RuntimeException e) {                      // Conversion not defined or
not affine.
             gridToCRS = null;
-            errorOccurred(e);               // Conversion not defined or not affine.
+            errorOccurred(e);
         }
-    }
-
-    /**
-     * 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)));
+        invalidObjectiveToDisplay = true;
     }
 
     /**
      * Invoked in JavaFX thread for creating a renderer to be executed in a background thread.
+     * This method prepares the information needed but does not start the rendering itself.
+     * The rendering will be done later by a call to {@link Renderer#paint(Graphics2D)}.
      */
     @Override
     protected Renderer createRenderer(){
@@ -349,6 +364,41 @@ final class CoverageView extends MapCanvas {
         if (data == null) {
             return null;
         }
+        /*
+         * Compute the `objectiveToDisplay` only before the first rendering, because the
display
+         * bounds may not be known before (it may be zero at the time `setImage(…)` is
invoked).
+         * This code is executed only once for a new image.
+         */
+        if (invalidObjectiveToDisplay) try {
+            invalidObjectiveToDisplay = false;
+            final Envelope bounds;
+            final GridGeometry geometry = getCoverage().getGridGeometry();
+            if (gridToCRS != null && geometry.isDefined(GridGeometry.ENVELOPE)) {
+                bounds = geometry.getEnvelope();
+            } else if (geometry.isDefined(GridGeometry.EXTENT)) {
+                final GridExtent extent = geometry.getExtent();
+                bounds = extent.toEnvelope(MathTransforms.identity(extent.getDimension()));
+            } else {
+                bounds = null;
+            }
+            LinearTransform tr;
+            if (bounds != null) {
+                final MatrixSIS m = Matrices.createTransform(bounds, getDisplayBounds());
+                Matrices.forceUniformScale(m, 0);
+                tr = MathTransforms.linear(m);
+            } else {
+                tr = MathTransforms.identity(ImageLoader.BIDIMENSIONAL);
+            }
+            setObjectiveToDisplay(tr);
+        } catch (RenderException | TransformException e) {
+            errorOccurred(e);
+        }
+        /*
+         * At each rendering operation, compute the transform from `data` cell coordinates
to pixel coordinates
+         * of the image shown in this view. We do this computation every times because `objectiveToDisplay`
may
+         * vary at any time, and also because we need a new `AffineTransform` instance anyway
(we can not reuse
+         * an existing instance, because it needs to be stable for use by the background
thread).
+         */
         final AffineTransform tr = new AffineTransform(objectiveToDisplay);
         if (gridToCRS != null) {
             tr.concatenate(gridToCRS);
@@ -389,14 +439,12 @@ final class CoverageView extends MapCanvas {
      * Converts pixel indices in the window to pixel indices in the image.
      */
     private boolean toImageCoordinates(final double[] indices) {
-        if (gridToDisplay == null) {
-            return false;
-        }
-        try {
+        if (gridToDisplay != null) try {
             gridToDisplay.inverseTransform(indices, 0, indices, 0, 1);
+            return true;
         } catch (NoninvertibleTransformException e) {
             throw new BackingStoreException(e);         // Will be unwrapped by the caller
         }
-        return true;
+        return false;
     }
 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
index 787e67c..9ce3ad2 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
@@ -361,7 +361,7 @@ public class GridExtent implements GridEnvelope, Serializable {
      * @see #slice(DirectPosition, int[])
      */
     GridExtent(final AbstractEnvelope envelope, final GridRoundingMode rounding, final int[]
margin,
-            final GridExtent enclosing, final int[] modifiedDimensions)
+               final GridExtent enclosing, final int[] modifiedDimensions)
     {
         final int dimension = envelope.getDimension();
         coordinates = (enclosing != null) ? enclosing.coordinates.clone() : allocate(dimension);
@@ -815,10 +815,29 @@ public class GridExtent implements GridEnvelope, Serializable {
      * The transform shall map <em>cell corner</em> to real world coordinates.
      *
      * @param  cornerToCRS  a transform from <em>cell corners</em> to real world
coordinates.
+     * @return this grid extent in real world coordinates.
+     * @throws TransformException if the envelope can not be computed with the given transform.
+     *
+     * @see GridGeometry#getEnvelope()
+     * @see org.opengis.referencing.datum.PixelInCell#CELL_CORNER
+     *
+     * @since 1.1
+     */
+    public GeneralEnvelope toEnvelope(final MathTransform cornerToCRS) throws TransformException
{
+        ArgumentChecks.ensureNonNull("cornerToCRS", cornerToCRS);
+        return toCRS(cornerToCRS, cornerToCRS, null);
+    }
+
+    /**
+     * Transforms this grid extent to a "real world" envelope using the given transform.
+     * The transform shall map <em>cell corner</em> to real world coordinates.
+     *
+     * @param  cornerToCRS  a transform from <em>cell corners</em> to real world
coordinates.
      * @param  gridToCRS    the transform specified by the user. May be the same as {@code
cornerToCRS}.
      *                      If different, then this is assumed to map cell centers instead
than cell corners.
      * @param  fallback     bounds to use if some values still NaN, or {@code null} if none.
      * @return this grid extent in real world coordinates.
+     * @throws TransformException if the envelope can not be computed with the given transform.
      *
      * @see #GridExtent(AbstractEnvelope, GridRoundingMode, int[], GridExtent, int[])
      */
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
index 85bb19f..aefa7dc 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
@@ -32,6 +32,7 @@ import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.StringBuilders;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.math.DecimalFunctions;
+import org.apache.sis.math.MathFunctions;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.internal.util.DoubleDouble;
 import org.apache.sis.internal.referencing.AxisDirections;
@@ -814,6 +815,58 @@ public final class Matrices extends Static {
     }
 
     /**
+     * Forces the matrix coefficients of the given matrix to a uniform scale factor, assuming
an affine transform.
+     * The uniformization is applied on a row-by-row basis (ignoring the last row and last
column), i.e.:
+     *
+     * <ul>
+     *   <li>All coefficients (excluding translation term) in the same row are multiplied
by the same factor.</li>
+     *   <li>After rescaling, each row (excluding translation column) have the same
+     *       {@linkplain MathFunctions#magnitude(double...) magnitude}.</li>
+     * </ul>
+     *
+     * The scale factors are adjusted for the smallest magnitude if {@code sp} is 0, the
largest magnitude
+     * if {@code sp} is 1, or an intermediate value if {@code sp} has any value between 0
and 1.
+     *
+     * @param  matrix  the matrix in which to uniformize scale factors. Will be modified
in-place.
+     * @param  sp      0 for smallest magnitude, 1 for largest magnitude, or any intermediate
value.
+     * @return {@code true} if the given matrix changed as a result of this method call.
+     *
+     * @since 1.1
+     */
+    public static boolean forceUniformScale(final Matrix matrix, final double sp) {
+        ArgumentChecks.ensureNonNull("matrix", matrix);
+        ArgumentChecks.ensureBetween("sp", 0, 1, sp);
+        final int srcDim = matrix.getNumCol() - 1;
+        final int tgtDim = matrix.getNumRow() - 1;
+        final double[] row = new double[srcDim];
+        final double[] mgn = new double[tgtDim];
+        double min = Double.POSITIVE_INFINITY;
+        double max = Double.NEGATIVE_INFINITY;
+        for (int j=0; j<tgtDim; j++) {
+            for (int i=0; i<srcDim; i++) {
+                row[i] = matrix.getElement(j, i);
+            }
+            final double m = MathFunctions.magnitude(row);
+            if (m < min) min = m;
+            if (m > max) max = m;
+            mgn[j] = m;
+        }
+        boolean changed = false;
+        if (min < max) {
+            final double scale = (1 - sp)*min + sp*max;
+            for (int j=0; j<tgtDim; j++) {
+                final double rescale = scale / mgn[j];
+                for (int i=0; i<srcDim; i++) {
+                    double e = matrix.getElement(j, i);
+                    changed |= (e != (e *= rescale));
+                    matrix.setElement(j, i, e);
+                }
+            }
+        }
+        return changed;
+    }
+
+    /**
      * Returns {@code true} if the given matrix is likely to use extended precision.
      * A value of {@code true} is not a guarantee that the matrix uses extended precision,
      * but a value of {@code false} is a guarantee that it does not.


Mime
View raw message