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 6dd5c5754d1b5b6a0c6bf611134a4cb24a57f87a Author: Martin Desruisseaux <[email protected]> AuthorDate: Thu May 26 19:39:34 2022 +0200 Add a `PixelInCell` argument to the `GridExtent.getPointOfInterest()` method. It matter when we use that method for getting the coordinates of a slice. --- .../coverage/grid/CoordinateOperationFinder.java | 2 +- .../sis/coverage/grid/GridCoverageProcessor.java | 2 +- .../apache/sis/coverage/grid/GridDerivation.java | 7 ++++-- .../apache/sis/coverage/grid/GridEvaluator.java | 2 +- .../org/apache/sis/coverage/grid/GridExtent.java | 29 ++++++++++++++++++++-- .../org/apache/sis/coverage/grid/GridGeometry.java | 17 +++++++------ .../sis/coverage/grid/ResampledGridCoverage.java | 9 ++++--- .../apache/sis/coverage/grid/SliceGeometry.java | 7 +++--- .../sis/internal/coverage/CoverageCombiner.java | 4 +-- .../main/java/org/apache/sis/portrayal/Canvas.java | 2 +- .../org/apache/sis/portrayal/CanvasExtent.java | 10 +++++--- .../org/apache/sis/portrayal/package-info.java | 2 +- .../sis/storage/geotiff/MultiResolutionImage.java | 6 ++--- 13 files changed, 67 insertions(+), 32 deletions(-) diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/CoordinateOperationFinder.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/CoordinateOperationFinder.java index 11f1c65f34..2b2fefbc6d 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/CoordinateOperationFinder.java +++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/CoordinateOperationFinder.java @@ -645,7 +645,7 @@ apply: if (forwardChangeOfCRS == null) { @SuppressWarnings("ReturnOfCollectionOrArrayField") private double[] coordinates() { if (coordinates == null) try { - final double[] poi = grid.getExtent().getPointOfInterest(); + final double[] poi = grid.getExtent().getPointOfInterest(PixelInCell.CELL_CENTER); MathTransform tr = grid.getGridToCRS(PixelInCell.CELL_CENTER); if (changeOfCRS != null) { tr = MathTransforms.concatenate(tr, changeOfCRS); 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 0e855321e1..d00e268bc1 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 @@ -273,7 +273,7 @@ public class GridCoverageProcessor implements Cloneable { * </tr><tr> * <td>{@linkplain GridGeometry#getExtent() Grid extent}</td> * <td>A default size preserving resolution at source - * {@linkplain GridExtent#getPointOfInterest() point of interest}.</td> + * {@linkplain GridExtent#getPointOfInterest(PixelInCell) point of interest}.</td> * </tr><tr> * <td>{@linkplain GridGeometry#getGridToCRS Grid to CRS transform}</td> * <td>Whatever it takes for fitting data inside the supplied extent.</td> diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java index cbf5981039..44772cba57 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java +++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java @@ -520,7 +520,7 @@ public class GridDerivation { throw new IllegalGridGeometryException(e, "areaOfInterest"); } // The `domain` extent must be the source of the `mapCenters` transform. - scales = GridGeometry.resolution(mapCenters, domain); + scales = GridGeometry.resolution(mapCenters, domain, PixelInCell.CELL_CENTER); } /* * The subsampling will determine the scale factors in the transform from the given desired grid geometry @@ -771,9 +771,12 @@ public class GridDerivation { * Returns the point of interest of current {@link #baseExtent}, keeping only the remaining * dimensions after {@link #dropUnusedDimensions(MathTransform, int)} execution. * The position is in units of {@link #base} grid coordinates. + * + * <p>This method assumes that the transform will be used with a "cell corner to CRS" transform + * instead of the usual "cell center to CRS".</p> */ private double[] getPointOfInterest() { - final double[] pointOfInterest = baseExtent.getPointOfInterest(); + final double[] pointOfInterest = baseExtent.getPointOfInterest(PixelInCell.CELL_CORNER); if (modifiedDimensions == null) { return pointOfInterest; } diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridEvaluator.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridEvaluator.java index 4fd6641959..8feb79b4ef 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridEvaluator.java +++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridEvaluator.java @@ -187,7 +187,7 @@ public class GridEvaluator implements GridCoverage.Evaluator { final GridExtent extent = gridGeometry.getExtent(); MathTransform gridToCRS = gridGeometry.getGridToCRS(PixelInCell.CELL_CENTER); gridToWraparound = MathTransforms.concatenate(gridToCRS, f.preferredToSpecified.inverse()); - final Matrix m = gridToWraparound.derivative(new DirectPositionView.Double(extent.getPointOfInterest())); + final Matrix m = gridToWraparound.derivative(new DirectPositionView.Double(extent.getPointOfInterest(PixelInCell.CELL_CENTER))); /* * `gridToWraparound` is the transform from grid coordinates to a CRS where wraparound axes exist. * It may be the coverage CRS or its base CRS. The wraparound axes are identified by `periods`. 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 5ff27e6569..3376c00fe0 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 @@ -33,6 +33,7 @@ import org.opengis.metadata.spatial.DimensionNameType; import org.opengis.referencing.cs.AxisDirection; import org.opengis.referencing.cs.CoordinateSystem; import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.datum.PixelInCell; import org.opengis.referencing.operation.Matrix; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; @@ -785,23 +786,47 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable return Numerics.toUnsignedDouble(size); } + /** + * @deprecated Replaced by {@link #getPointOfInterest(PixelInCell)}. + * + * @return the grid coordinates of a representative point. + */ + @Deprecated + public double[] getPointOfInterest() { + return getPointOfInterest(PixelInCell.CELL_CORNER); + } + /** * Returns the grid coordinates of a representative point. * This point may be used for estimating a {@linkplain GridGeometry#getResolution(boolean) grid resolution}. * The default implementation returns the median (or center) coordinates of this grid extent, * but subclasses can override this method if another point is considered more representative. * + * <p>The {@code anchpr} argument tells {@linkplain GridGeometry#getGridToCRS(PixelInCell) which transform} + * the caller intend to use for converting the grid coordinates to "real world" coordinates. + * With the default implementation, the coordinate values returned with {@code CELL_CORNER} + * are 0.5 cell units higher than the coordinate values returned with {@code CELL_CENTER}. + * Subclasses are free to ignore this argument.</p> + * + * @param anchor the convention to be used for conversion to "real world" coordinates. * @return the grid coordinates of a representative point. + * + * @since 1.3 */ - public double[] getPointOfInterest() { + public double[] getPointOfInterest(final PixelInCell anchor) { final int dimension = getDimension(); final double[] center = new double[dimension]; + final boolean isCorner = PixelInCell.CELL_CORNER.equals(anchor); for (int i=0; i<dimension; i++) { /* * We want the average of (low + hi+1). However for the purpose of computing an average, it does * not matter if we add 1 to `low` or `hi`. So we add 1 to `low` because it should not overflow. */ - center[i] = MathFunctions.average(Math.incrementExact(coordinates[i]), coordinates[i + dimension]); + long low = coordinates[i]; + if (isCorner) { + low = Math.incrementExact(coordinates[i]); + } + center[i] = MathFunctions.average(low, coordinates[i + dimension]); } return center; } diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java index 7a9d5c481d..d7108e3167 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java +++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java @@ -129,7 +129,7 @@ import static org.apache.sis.referencing.CRS.findOperation; * The same instance can be shared by different {@link GridCoverage} instances. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.2 + * @version 1.3 * @since 1.0 * @module */ @@ -367,12 +367,12 @@ public class GridGeometry implements LenientComparable, Serializable { MathTransforms.uniformTranslation(dimension, -0.5)); cornerToCRS = MathTransforms.concatenate(toOther, other.cornerToCRS); gridToCRS = MathTransforms.concatenate(centerShift, other.gridToCRS); - resolution = resolution(gridToCRS, extent); + resolution = resolution(gridToCRS, extent, PixelInCell.CELL_CENTER); nonLinears = findNonLinearTargets(gridToCRS); } else { cornerToCRS = null; gridToCRS = null; - resolution = resolution(toOther, extent); // Save resolution even if `gridToCRS` is null. + resolution = resolution(toOther, extent, PixelInCell.CELL_CENTER); // Save resolution even if `gridToCRS` is null. nonLinears = findNonLinearTargets(toOther); } } @@ -445,7 +445,7 @@ public class GridGeometry implements LenientComparable, Serializable { this.gridToCRS = PixelTranslation.translate(gridToCRS, anchor, PixelInCell.CELL_CENTER); this.cornerToCRS = PixelTranslation.translate(gridToCRS, anchor, PixelInCell.CELL_CORNER); this.envelope = computeEnvelope(gridToCRS, crs, null); // `gridToCRS` specified by the user, not `this.gridToCRS`. - this.resolution = resolution(gridToCRS, extent); // `gridToCRS` or `cornerToCRS` does not matter here. + this.resolution = resolution(gridToCRS, extent, anchor); // `gridToCRS` or `cornerToCRS` does not matter here. this.nonLinears = findNonLinearTargets(gridToCRS); } catch (TransformException e) { throw new IllegalGridGeometryException(e, "gridToCRS"); @@ -541,7 +541,7 @@ public class GridGeometry implements LenientComparable, Serializable { this.envelope = new ImmutableEnvelope(env); if (scales == null) try { // `gridToCRS` can not be null if `cornerToCRS` is non-null. - scales = gridToCRS.derivative(new DirectPositionView.Double(extent.getPointOfInterest())); + scales = gridToCRS.derivative(new DirectPositionView.Double(extent.getPointOfInterest(anchor))); numToIgnore = 0; } catch (TransformException e) { recoverableException("<init>", e); @@ -1029,7 +1029,7 @@ public class GridGeometry implements LenientComparable, Serializable { * <ul> * <li>{@link Double#NaN} if {@code allowEstimates} is {@code false}.</li> * <li>An arbitrary representative resolution otherwise. - * Current implementation computes the resolution at {@link GridExtent#getPointOfInterest() grid center}, + * Current implementation computes the resolution at {@link GridExtent#getPointOfInterest(PixelInCell) grid center}, * but different implementations may use alternative algorithms.</li> * </ul> * @@ -1067,14 +1067,15 @@ public class GridGeometry implements LenientComparable, Serializable { * @param gridToCRS a transform for which to compute the resolution, or {@code null} if none. * @param domain the domain for which to get a resolution, or {@code null} if none. * If non-null, must be the source of {@code gridToCRS}. + * @param anchor the pixel corner versus pixel center convention to use. * @return the resolutions as positive numbers. May contain NaN values. */ - static double[] resolution(final MathTransform gridToCRS, final GridExtent domain) { + static double[] resolution(final MathTransform gridToCRS, final GridExtent domain, final PixelInCell anchor) { final Matrix matrix = MathTransforms.getMatrix(gridToCRS); if (matrix != null) { return resolution(matrix, 1); } else if (domain != null && gridToCRS != null) try { - return resolution(gridToCRS.derivative(new DirectPositionView.Double(domain.getPointOfInterest())), 0); + return resolution(gridToCRS.derivative(new DirectPositionView.Double(domain.getPointOfInterest(anchor))), 0); } catch (TransformException e) { recoverableException("resolution", e); } 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 d095047bd1..6df6d5d925 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 @@ -52,7 +52,7 @@ import org.apache.sis.util.Utilities; * * @author Martin Desruisseaux (Geomatys) * @author Johann Sorel (Geomatys) - * @version 1.2 + * @version 1.3 * @since 1.1 * @module */ @@ -134,7 +134,7 @@ final class ResampledGridCoverage extends GridCoverage { * For a source dimension <var>i</var>, {@code dependentDimensions[i]} is a bitmask with bits set to 1 * for each target dimension which require the source dimension <var>i</var> for its calculation. * - * @param mt the transform for which to determine dimension dependencies. + * @param mt the transform (mapping pixel centers) for which to determine dimension dependencies. * @param domain domain of this {@code}. * @return for each source dimension, a bitmask of target dependent dimensions. * May be {@code null} if the mapping can not be computed or if it is not needed. @@ -144,7 +144,8 @@ final class ResampledGridCoverage extends GridCoverage { if (srcDim <= BIDIMENSIONAL) return null; // Dimension mapping not needed. Matrix derivative = MathTransforms.getMatrix(mt); if (derivative == null) try { - derivative = mt.derivative(new DirectPositionView.Double(domain.getExtent().getPointOfInterest())); + derivative = mt.derivative(new DirectPositionView.Double( + domain.getExtent().getPointOfInterest(PixelInCell.CELL_CENTER))); } catch (TransformException e) { GridCoverageProcessor.recoverableException("resample", e); // Public caller of this method. return null; @@ -306,7 +307,7 @@ final class ResampledGridCoverage extends GridCoverage { * `originToPOI` vector (the integer digits do not matter; they will be cancelled later). */ final GridExtent sourceExtent = sourceGG.getExtent(); - final double[] sourcePOI = sourceExtent.getPointOfInterest(); + final double[] sourcePOI = sourceExtent.getPointOfInterest(PixelInCell.CELL_CENTER); final double[] targetPOI = new double[sourceCenterToCRS.getTargetDimensions()]; final MatrixSIS vectors = MatrixSIS.castOrCopy(MathTransforms.derivativeAndTransform( sourceCenterToCRS, sourcePOI, 0, targetPOI, 0)); diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/SliceGeometry.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/SliceGeometry.java index 0279bacbe4..18e56c8b0e 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/SliceGeometry.java +++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/SliceGeometry.java @@ -20,6 +20,7 @@ import java.util.function.Function; import java.awt.image.RenderedImage; import java.awt.image.ImagingOpException; import org.opengis.util.FactoryException; +import org.opengis.referencing.datum.PixelInCell; import org.opengis.referencing.operation.Matrix; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; @@ -47,7 +48,7 @@ import org.apache.sis.util.resources.Errors; * That function is invoked (indirectly) by {@link org.apache.sis.internal.coverage.j2d.TiledImage#getProperty(String)}.</p> * * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @version 1.3 * @since 1.1 * @module */ @@ -221,7 +222,7 @@ final class SliceGeometry implements Function<RenderedImage, GridGeometry> { * from the original grid geometry. */ if (useSubExtent || resolution == null) { - resolution = GridGeometry.resolution(gridToCRS, extent); + resolution = GridGeometry.resolution(gridToCRS, extent, PixelInCell.CELL_CENTER); } else if (resolution.length != n) { resolution = new double[n]; for (int i=0; i<n; i++) { @@ -296,7 +297,7 @@ final class SliceGeometry implements Function<RenderedImage, GridGeometry> { Matrix derivative = MathTransforms.getMatrix(gridToCRS); if (derivative == null) { if (extent != null) try { - derivative = gridToCRS.derivative(new DirectPositionView.Double(extent.getPointOfInterest())); + derivative = gridToCRS.derivative(new DirectPositionView.Double(extent.getPointOfInterest(PixelInCell.CELL_CENTER))); } catch (TransformException e) { // GridGeometry.reduce(…) is the public method invoking indirectly this method. GridGeometry.recoverableException("reduce", e); diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/CoverageCombiner.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/CoverageCombiner.java index e8c4b09818..e47aa35218 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/CoverageCombiner.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/CoverageCombiner.java @@ -65,7 +65,7 @@ import static org.apache.sis.internal.util.Numerics.saturatingSubtract; * be outside the bounds of destination coverage.</p> * * @author Martin Desruisseaux (Geomatys) - * @version 1.2 + * @version 1.3 * * @see ImageCombiner * @@ -233,7 +233,7 @@ next: for (int j=0; j<sources.length; j++) { */ final long[] minSliceIndices = minIndices.clone(); final long[] maxSliceIndices = maxIndices.clone(); - final double[] centerIndices = targetEx.getPointOfInterest(); + final double[] centerIndices = targetEx.getPointOfInterest(PixelInCell.CELL_CENTER); final Dimension margin = processor.getInterpolation().getSupportSize(); margin.width = ((margin.width + 1) >> 1) + 1; margin.height = ((margin.height + 1) >> 1) + 1; diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/Canvas.java b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/Canvas.java index 26b29e0cc0..20f03709a0 100644 --- a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/Canvas.java +++ b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/Canvas.java @@ -1079,7 +1079,7 @@ public class Canvas extends Observable implements Localized { crs = null; newPOI = new GeneralDirectPosition(gridToCRS.getTargetDimensions()); } - gridToCRS.transform(extent.getPointOfInterest(), 0, newPOI.coordinates, 0, 1); + gridToCRS.transform(extent.getPointOfInterest(PixelInCell.CELL_CORNER), 0, newPOI.coordinates, 0, 1); /* * Get the CRS component in the dimensions shown by this canvas. * diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasExtent.java b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasExtent.java index 8ef655ce5f..2f0ba14db3 100644 --- a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasExtent.java +++ b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasExtent.java @@ -18,6 +18,7 @@ package org.apache.sis.portrayal; import java.util.List; import org.opengis.geometry.DirectPosition; +import org.opengis.referencing.datum.PixelInCell; import org.opengis.referencing.operation.Matrix; import org.opengis.referencing.cs.AxisDirection; import org.opengis.referencing.cs.CoordinateSystem; @@ -40,7 +41,7 @@ import org.apache.sis.internal.util.Numerics; * This class contains also static help functions for the construction of {@link GridGeometry}. * * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @version 1.3 * @since 1.1 * @module */ @@ -54,7 +55,7 @@ final class CanvasExtent extends GridExtent { * The grid coordinates of a representative point. The {@code CanvasExtent} point of interest * is the {@code Canvas} point of interest converted to (typically) pixel coordinates. * - * @see #getPointOfInterest() + * @see #getPointOfInterest(PixelInCell) */ private final double[] pointOfInterest; @@ -75,10 +76,13 @@ final class CanvasExtent extends GridExtent { * Returns the grid coordinates of a representative point. * This is the canvas point of interest converted to (typically) pixel coordinates. * + * @param anchor the convention to be used for conversion to "real world" coordinates. + * @return the grid coordinates of a representative point. + * * @see Canvas#getPointOfInterest(boolean) */ @Override - public double[] getPointOfInterest() { + public double[] getPointOfInterest(final PixelInCell anchor) { return pointOfInterest.clone(); } diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/package-info.java b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/package-info.java index 2b8bbb237f..18b7fc3e2e 100644 --- a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/package-info.java +++ b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/package-info.java @@ -25,7 +25,7 @@ * Synchronization, if desired, must be done by the caller. * * @author Johann Sorel (Geomatys) - * @version 1.1 + * @version 1.3 * @since 1.1 * @module */ diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/MultiResolutionImage.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/MultiResolutionImage.java index 8ca1b4d1ba..d5cf951dbe 100644 --- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/MultiResolutionImage.java +++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/MultiResolutionImage.java @@ -152,8 +152,8 @@ final class MultiResolutionImage extends GridResourceWrapper { } image.initReducedResolution(base, scales); if (geometry.isDefined(GridGeometry.GRID_TO_CRS)) { - final MatrixSIS gridToCRS = MatrixSIS.castOrCopy(geometry.getGridToCRS(PixelInCell.CELL_CENTER) - .derivative(new DirectPositionView.Double(fullExtent.getPointOfInterest()))); + DirectPosition poi = new DirectPositionView.Double(fullExtent.getPointOfInterest(PixelInCell.CELL_CENTER)); + MatrixSIS gridToCRS = MatrixSIS.castOrCopy(geometry.getGridToCRS(PixelInCell.CELL_CENTER).derivative(poi)); resolution = gridToCRS.multiply(scales); } else { // Assume an identity transform for the `gridToCRS` of full resolution image. @@ -211,7 +211,7 @@ final class MultiResolutionImage extends GridResourceWrapper { * If the `domain` grid geometry has a resolution and an envelope, then it should have * an extent and a "grid to CRS" transform (otherwise it may be a `GridGeometry` bug) */ - DirectPosition poi = new DirectPositionView.Double(domain.getExtent().getPointOfInterest()); + DirectPosition poi = new DirectPositionView.Double(domain.getExtent().getPointOfInterest(PixelInCell.CELL_CENTER)); poi = domain.getGridToCRS(PixelInCell.CELL_CENTER).transform(poi, null); final MatrixSIS derivative = MatrixSIS.castOrCopy(sourceToCoverage.derivative(poi)); resolution = derivative.multiply(resolution);
