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.
|