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 c094a3a01586f727b832a6f034084e1dc8517f2b Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Wed Aug 17 16:56:32 2022 +0200 Optimization which replaces a "resample" operation by a translation can be applied only if the source and target coverages has the same size. --- .../sis/coverage/grid/GridCoverageProcessor.java | 6 ++-- .../org/apache/sis/coverage/grid/GridExtent.java | 37 ++++++++++++++++++---- .../sis/coverage/grid/ResampledGridCoverage.java | 36 +++++++++++---------- .../sis/coverage/grid/TranslatedGridCoverage.java | 9 +++++- 4 files changed, 63 insertions(+), 25 deletions(-) diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageProcessor.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageProcessor.java index 031942f67f..67a458a3d0 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageProcessor.java +++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageProcessor.java @@ -164,8 +164,8 @@ public class GridCoverageProcessor implements Cloneable { * but may change other aspects (in a compatible way) such as the {@link GridCoverage} subclass * returned or the size of the underlying rendered images. * - * <p>By default all optimizations are enabled. Users may want to disable some optimizations - * for example in order to get more predictable results.</p> + * <p>By default the {@link #REPLACE_OPERATION} and {@link #REPLACE_SOURCE} optimizations are enabled. + * Users may want to disable some optimizations for example in order to get more predictable results.</p> * * @author Martin Desruisseaux (Geomatys) * @version 1.3 @@ -178,6 +178,7 @@ public class GridCoverageProcessor implements Cloneable { public enum Optimization { /** * Allows the replacement of an operation by a more efficient one. + * This optimization is enabled by default. * * <div class="note"><b>Example:</b> * if the {@link #resample(GridCoverage, GridGeometry) resample(…)} method is invoked with parameter values @@ -189,6 +190,7 @@ public class GridCoverageProcessor implements Cloneable { /** * Allows the replacement of source parameter by a more fundamental source. + * This optimization is enabled by default. * * <div class="note"><b>Example:</b> * if the {@link #resample(GridCoverage, GridGeometry) resample(…)} method is invoked with a source 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 e71d284659..af34fe7c5a 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 @@ -1703,14 +1703,28 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable } /** - * Returns a hash value for this grid extent. This value needs not to remain - * consistent between different implementations of the same class. + * Returns whether this grid extent has the same size than the given extent. + * If the given extent is {@code null} or has a different number of dimensions, + * then this method returns {@code false}. * - * @return a hash value for this grid extent. + * <p>This method is not public because we do not yet have a policy + * about whether we should verify if axis {@link #types} match.</p> + * + * @param other the other extent to compare with this extent. Can be {@code null}. + * @return whether the two extents has the same size. */ - @Override - public int hashCode() { - return Arrays.hashCode(coordinates) + Arrays.hashCode(types) ^ (int) serialVersionUID; + final boolean isSameSize(final GridExtent other) { + if (other == null || coordinates.length != other.coordinates.length) { + return false; + } + final int dimension = getDimension(); + final long[] oc = other.coordinates; + for (int i=0; i<dimension; i++) { + if (coordinates[i+dimension] - coordinates[i] != oc[i+dimension] - oc[i]) { + return false; + } + } + return true; } /** @@ -1755,6 +1769,17 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable return false; } + /** + * Returns a hash value for this grid extent. This value needs not to remain + * consistent between different implementations of the same class. + * + * @return a hash value for this grid extent. + */ + @Override + public int hashCode() { + return Arrays.hashCode(coordinates) + Arrays.hashCode(types) ^ (int) serialVersionUID; + } + /** * Returns a string representation of this grid extent. The returned string * is implementation dependent and is provided for debugging purposes only. diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java index 92ffd45d8a..e3114ab04a 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java +++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java @@ -184,10 +184,10 @@ final class ResampledGridCoverage extends DerivedGridCoverage { * If this coverage can be represented as a {@link GridCoverage2D} instance, * returns such instance. Otherwise returns {@code this}. * - * @param isGeometryExplicit whether grid extent or "grid to CRS" transform have been explicitly - * specified by user. In such case, this method will not be allowed to change those values. + * @param allowGeometryReplacement whether to allow the replacement of grid geometry in the target coverage. + * @param allowOperationReplacement whether to allow the replacement of this operation by a more efficient one. */ - private GridCoverage specialize(final boolean isGeometryExplicit, final boolean allowOperationReplacement) + private GridCoverage specialize(final boolean allowGeometryReplacement, final boolean allowOperationReplacement) throws TransformException { if (allowOperationReplacement) { @@ -196,7 +196,8 @@ final class ResampledGridCoverage extends DerivedGridCoverage { (translation = getIntegerTranslation(toSourceCorner)) != null) { // No need to allow source replacement because it is already done by caller. - return TranslatedGridCoverage.create(source, gridGeometry, translation, false); + GridCoverage c = TranslatedGridCoverage.create(source, gridGeometry, translation, false); + if (c != null) return c; } } GridExtent extent = gridGeometry.getExtent(); @@ -210,7 +211,7 @@ final class ResampledGridCoverage extends DerivedGridCoverage { * (i.e. user specified only a target CRS), keep same image with a different `gridToCRS` transform instead * than doing a resampling. The intent is to avoid creating a new image if user apparently doesn't care. */ - if (!isGeometryExplicit && toSourceCorner instanceof LinearTransform) { + if (allowGeometryReplacement && toSourceCorner instanceof LinearTransform) { MathTransform gridToCRS = gridGeometry.getGridToCRS(PixelInCell.CELL_CORNER); if (gridToCRS instanceof LinearTransform) { final GridGeometry sourceGG = source.getGridGeometry(); @@ -287,8 +288,10 @@ final class ResampledGridCoverage extends DerivedGridCoverage { * result.</li> * </ul> * - * @param source the grid coverage to resample. - * @param target the desired geometry of returned grid coverage. May be incomplete. + * @param source the grid coverage to resample. + * @param target the desired geometry of returned grid coverage. May be incomplete. + * @param processor the processor to use for executing the resample operation on images. + * @param allowOperationReplacement whether to allow the replacement of this operation by a more efficient one. * @return a grid coverage with the characteristics specified in the given grid geometry. * @throws IncompleteGridGeometryException if the source grid geometry is missing an information. * @throws TransformException if some coordinates can not be transformed to the specified target. @@ -452,33 +455,34 @@ final class ResampledGridCoverage extends DerivedGridCoverage { * Build the final target GridGeometry if any components were missing. * If an envelope is defined, resample only that sub-region. */ - GridGeometry resampled = target; + GridGeometry complete = target; ComparisonMode mode = ComparisonMode.IGNORE_METADATA; if (!target.isDefined(GridGeometry.EXTENT | GridGeometry.GRID_TO_CRS | GridGeometry.CRS)) { final CoordinateReferenceSystem targetCRS = changeOfCRS.getTargetCRS(); - resampled = new GridGeometry(targetExtent, PixelInCell.CELL_CENTER, targetCenterToCRS, targetCRS); + complete = new GridGeometry(targetExtent, PixelInCell.CELL_CENTER, targetCenterToCRS, targetCRS); mode = ComparisonMode.APPROXIMATE; if (target.isDefined(GridGeometry.ENVELOPE)) { - final MathTransform targetCornerToCRS = resampled.getGridToCRS(PixelInCell.CELL_CORNER); - GeneralEnvelope bounds = new GeneralEnvelope(resampled.getEnvelope()); + final MathTransform targetCornerToCRS = complete.getGridToCRS(PixelInCell.CELL_CORNER); + GeneralEnvelope bounds = new GeneralEnvelope(complete.getEnvelope()); bounds.intersect(target.getEnvelope()); bounds = Envelopes.transform(targetCornerToCRS.inverse(), bounds); targetExtent = new GridExtent(bounds, GridRoundingMode.NEAREST, GridClippingMode.STRICT, null, null, targetExtent, null); - resampled = new GridGeometry(targetExtent, PixelInCell.CELL_CENTER, targetCenterToCRS, targetCRS); + complete = new GridGeometry(targetExtent, PixelInCell.CELL_CENTER, targetCenterToCRS, targetCRS); isGeometryExplicit = true; } } - if (sourceGG.equals(resampled, mode)) { + if (sourceGG.equals(complete, mode)) { return source; } /* * Complete the "target to source" transform. */ - final MathTransform targetCornerToCRS = resampled.getGridToCRS(PixelInCell.CELL_CORNER); - return new ResampledGridCoverage(source, resampled, + final MathTransform targetCornerToCRS = complete.getGridToCRS(PixelInCell.CELL_CORNER); + final ResampledGridCoverage resampled = new ResampledGridCoverage(source, complete, MathTransforms.concatenate(targetCornerToCRS, crsToSourceCorner), MathTransforms.concatenate(targetCenterToCRS, crsToSourceCenter), - changeOfCRS, processor).specialize(isGeometryExplicit, allowOperationReplacement); + changeOfCRS, processor); + return resampled.specialize(!isGeometryExplicit, allowOperationReplacement); } /** diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/TranslatedGridCoverage.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/TranslatedGridCoverage.java index 81bb354bbe..c6e2dae659 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/TranslatedGridCoverage.java +++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/TranslatedGridCoverage.java @@ -42,6 +42,7 @@ final class TranslatedGridCoverage extends DerivedGridCoverage { /** * Constructs a new grid coverage which will delegate the rendering operation to the given source. * This coverage will take the same sample dimensions than the source. + * The {@code domain} size must be the same than the source grid geometry size. * * @param source the source on which to delegate rendering operations. * @param domain the grid extent, CRS and conversion from cell indices to CRS. @@ -56,10 +57,14 @@ final class TranslatedGridCoverage extends DerivedGridCoverage { * Returns a grid coverage which will use the {@code domain} grid geometry. * This coverage will take the same sample dimensions than the source. * + * <p>If {@code domain} is non-null, then it should have the same size than the source grid geometry size. + * If this is not the case, then this method returns {@code null}.</p> + * * @param source the source on which to delegate rendering operations. * @param domain the geometry of the grid coverage to return, or {@code null} for automatic. * @param translation translation to apply on the argument given to {@link #render(GridExtent)}. - * @return the coverage. May be the {@code source} returned as-is. + * @return the coverage, or {@code null} if {@code domain} is non-null but does not have the expected size. + * May be the {@code source} returned as-is. */ static GridCoverage create(GridCoverage source, GridGeometry domain, long[] translation, final boolean allowSourceReplacement) @@ -81,6 +86,8 @@ final class TranslatedGridCoverage extends DerivedGridCoverage { final GridGeometry gridGeometry = source.getGridGeometry(); if (domain == null) { domain = gridGeometry.translate(translation); + } else if (!domain.extent.isSameSize(gridGeometry.extent)) { + return null; } if (domain.equals(gridGeometry)) { return source; // All (potentially updated) translation terms are zero.