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

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 cell corner to real world coordinates. * * @param cornerToCRS a transform from cell corners 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 cell corner to real world coordinates. + * + * @param cornerToCRS a transform from cell corners 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.: + * + *

+ * + * 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 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