This is an automated email from the ASF dual-hosted git repository. jsorel 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 95ab3d9cd6 Add upsample methods on GridExtent and GridGeometry 95ab3d9cd6 is described below commit 95ab3d9cd627f8d9f64d9ca68431d197d7b3a0db Author: jsorel <johann.so...@geomatys.com> AuthorDate: Thu Sep 8 15:12:08 2022 +0200 Add upsample methods on GridExtent and GridGeometry --- .../org/apache/sis/coverage/grid/GridExtent.java | 43 +++++++++++++++++ .../org/apache/sis/coverage/grid/GridGeometry.java | 56 ++++++++++++++++++++++ .../apache/sis/coverage/grid/GridExtentTest.java | 12 +++++ .../apache/sis/coverage/grid/GridGeometryTest.java | 34 +++++++++++++ 4 files changed, 145 insertions(+) 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 a27bbcf327..d0488546cb 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 @@ -1510,6 +1510,49 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable return Arrays.equals(coordinates, sub.coordinates) ? this : sub; } + /** + * Creates a new grid extent upsampled by the given amount of cells along each grid dimensions. + * This method multiplies {@linkplain #getLow(int) low coordinates} and {@linkplain #getSize(int) grid sizes} + * by the given periods. + * + * <div class="note"><b>Note:</b> + * The envelope computed from a grid extent is preserved after upsampling. + * </div> + * + * This method does not change the number of dimensions of the grid extent. + * + * <h4>Number of arguments</h4> + * The {@code periods} array length should be equal to the {@linkplain #getDimension() number of dimensions}. + * If the array is shorter, missing values default to 1 (i.e. samplings in unspecified dimensions are unchanged). + * If the array is longer, extraneous values are ignored. + * + * @param periods the upsampling. Length shall be equal to the number of dimension and all values shall be greater than zero. + * @return the upsampled extent, or {@code this} is upsampling results in the same extent. + * @throws IllegalArgumentException if a period is not greater than zero. + */ + public GridExtent upsample(int... periods) { + ArgumentChecks.ensureNonNull("periods", periods); + final int m = getDimension(); + final int length = Math.min(m, periods.length); + final GridExtent sub = new GridExtent(this); + for (int i=0; i<length; i++) { + final long s = periods[i]; + if (s > 1) { + final int j = i + m; + long low = coordinates[i]; + long size = coordinates[j] - low + 1; // Result is an unsigned number. + if (size == 0) { + throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Long.SIZE)); + } + sub.coordinates[i] = low * s; + sub.coordinates[j] = sub.coordinates[i] + size * s -1; + } else if (s <= 0) { + throw new IllegalArgumentException(Errors.format(Errors.Keys.ValueNotGreaterThanZero_2, Strings.toIndexed("periods", i), s)); + } + } + return Arrays.equals(coordinates, sub.coordinates) ? this : sub; + } + /** * Returns a slice of this grid extent computed by a ratio between 0 and 1 inclusive. * This is a helper method for {@link GridDerivation#sliceByRatio(double, int...)} implementation. 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 6456f230c1..8ebb22fb70 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 @@ -1470,6 +1470,62 @@ public class GridGeometry implements LenientComparable, Serializable { return this; } + /** + * Creates a new grid geometry upsampling the GridExtent by the given amount of cells along each grid dimensions. + * This method multiplies {@linkplain GridExtent#getLow(int) low coordinates} and {@linkplain GridExtent#getSize(int) grid sizes} + * by the given periods. + * + * <div class="note"><b>Note:</b> + * The envelope of the new grid geometry is preserved after upsampling. + * </div> + * + * This method does not change the number of dimensions of the grid geometry. + * + * <h4>Number of arguments</h4> + * The {@code periods} array length should be equal to the {@linkplain #getDimension() number of dimensions}. + * If the array is shorter, missing values default to 1 (i.e. samplings in unspecified dimensions are unchanged). + * If the array is longer, extraneous values are ignored. + * + * @param periods the upsampling. Length shall be equal to the number of dimension and all values shall be greater than zero. + * @return the upsampled grid geometry, or {@code this} is upsampling results in the same extent. + * @throws IllegalArgumentException if a period is not greater than zero. + * + * @see GridExtent#upsample(int...) + */ + public GridGeometry upsample(int... periods) { + + final GridExtent extent = getExtent(); + final GridExtent upExtent = extent.upsample(periods); + if (upExtent == extent) { + //unchanged + return this; + } + final int dimension = upExtent.getDimension(); + + final MathTransform gridToCrs = getGridToCRS(PixelInCell.CELL_CORNER); + final MathTransform upGridToCrs; + if (gridToCrs instanceof LinearTransform) { + /* + By dividing the matrix elements directly we avoid some numeric errors. + */ + final LinearTransform lnt = (LinearTransform) gridToCrs; + final MatrixSIS matrix = Matrices.copy(lnt.getMatrix()); + for (int i = 0; i < dimension; i++) { + for (int k = 0; k < dimension; k++) { + matrix.setElement(k, i, matrix.getElement(k, i) / periods[i]); + } + } + upGridToCrs = MathTransforms.linear(matrix); + } else { + final double[] scaling = new double[dimension]; + for (int i = 0; i < dimension; i++) { + scaling[i] = 1.0 / periods[i]; + } + upGridToCrs = MathTransforms.concatenate(MathTransforms.scale(scaling), gridToCrs); + } + return new GridGeometry(upExtent, PixelInCell.CELL_CORNER, upGridToCrs, getCoordinateReferenceSystem()); + } + /** * Creates a one-, two- or three-dimensional coordinate reference system for cell indices in the grid. * This method returns a CRS which is derived from the "real world" CRS or a subset of it. diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java index 65cec4bcf4..430d76647d 100644 --- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java +++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java @@ -83,6 +83,18 @@ public final strictfp class GridExtentTest extends TestCase { assertExtentEquals(extent, 2, 4, 5); // 2 cells } + /** + * Tests the {@link GridExtent#upsample(int...)}. + */ + @Test + public void testUpsample() { + GridExtent extent = create3D(); + extent = extent.upsample(4, 3, 9); + assertExtentEquals(extent, 0, 400, 1999); // 1600 cells + assertExtentEquals(extent, 1, 600, 2399); // 1800 cells + assertExtentEquals(extent, 2, 360, 449); // 90 cells + } + /** * Tests the {@link GridExtent#GridExtent(AbstractEnvelope, * GridRoundingMode, int[], int[], GridExtent, int[])} constructor. diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java index 87ef02e1ac..02e8b6a86b 100644 --- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java +++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java @@ -561,6 +561,40 @@ public final strictfp class GridGeometryTest extends TestCase { verifyGridToCRS(grid); } + /** + * Tests {@link GridGeometry#upsample(int...)}. + */ + @Test + public void testUpsample() { + + final GridGeometry grid; + { //grid + final GridExtent extent = new GridExtent(null, new long[]{10,-8}, new long[]{100, 50}, false); + final Matrix3 mat = new Matrix3( + 1, 0, 10, + 0, -2, 50, + 0, 0, 1); + final MathTransform gridToCRS = MathTransforms.linear(mat); + grid = new GridGeometry(extent, PixelInCell.CELL_CENTER, gridToCRS, HardCodedCRS.CARTESIAN_2D); + } + + final GridGeometry surSampling = grid.upsample(4, 4); + + final GridGeometry expected; + { //expected grid + GridExtent extent = new GridExtent(null, new long[]{40,-32}, new long[]{400, 200}, false); + final Matrix3 mat = new Matrix3( + 0.25, 0, 9.625, + 0, -0.5, 50.750, + 0, 0, 1); + final MathTransform gridToCRS = MathTransforms.linear(mat); + expected = new GridGeometry(extent, PixelInCell.CELL_CENTER, gridToCRS, HardCodedCRS.CARTESIAN_2D); + } + + assertEquals(grid.getEnvelope(), surSampling.getEnvelope()); + assertEquals(expected, surSampling); + } + /** * Tests {@link GridGeometry#reduce(int...)} with a {@code gridToCRS} transform having a constant value * in one dimension. This method tests indirectly {@link SliceGeometry#findTargetDimensions(MathTransform,