You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2019/01/13 19:23:47 UTC
[sis] branch geoapi-4.0 updated (8a7eee3 -> bd65fed)
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a change to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git.
from 8a7eee3 Replace some GridGeometry methods by a Modifier inner class.
new 2404323 Renamed SubgridCalculator as GridDerivation, modify() as derive() and apply() as build().
new bd65fed Refactor GridGeometry.Modifier as methods provided directly in GridDerivation.
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../org/apache/sis/coverage/grid/GridCoverage.java | 6 +-
.../apache/sis/coverage/grid/GridDerivation.java | 462 +++++++++++++++++++++
.../org/apache/sis/coverage/grid/GridExtent.java | 22 +-
.../org/apache/sis/coverage/grid/GridGeometry.java | 245 +----------
.../sis/coverage/grid/SubgridCalculator.java | 261 ------------
.../org/apache/sis/internal/raster/Resources.java | 5 +
.../sis/internal/raster/Resources.properties | 1 +
.../sis/internal/raster/Resources_fr.properties | 1 +
.../sis/coverage/grid/GridDerivationTest.java | 237 +++++++++++
.../apache/sis/coverage/grid/GridGeometryTest.java | 195 +--------
.../org/apache/sis/test/suite/RasterTestSuite.java | 1 +
11 files changed, 741 insertions(+), 695 deletions(-)
create mode 100644 core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java
delete mode 100644 core/sis-raster/src/main/java/org/apache/sis/coverage/grid/SubgridCalculator.java
create mode 100644 core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridDerivationTest.java
[sis] 01/02: Renamed SubgridCalculator as GridDerivation,
modify() as derive() and apply() as build().
Posted by de...@apache.org.
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 2404323d4ce69fe6839eb1a1013b7c46b0c1cb40
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sun Jan 13 14:59:19 2019 +0100
Renamed SubgridCalculator as GridDerivation, modify() as derive() and apply() as build().
---
.../org/apache/sis/coverage/grid/GridCoverage.java | 6 +++---
...{SubgridCalculator.java => GridDerivation.java} | 6 +++---
.../org/apache/sis/coverage/grid/GridGeometry.java | 24 +++++++++++-----------
.../apache/sis/coverage/grid/GridGeometryTest.java | 16 +++++++--------
4 files changed, 26 insertions(+), 26 deletions(-)
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
index 52e9709..b794f2b 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
@@ -146,9 +146,9 @@ public abstract class GridCoverage {
*
* Then:
*
- * <blockquote><code>sliceExtent = {@linkplain #getGridGeometry()}.{@linkplain GridGeometry#modify()
- * modify()}.{@linkplain GridGeometry.Modifier#slice(DirectPosition)
- * slice}(slicePoint).{@linkplain GridGeometry.Modifier#apply() apply()};</code></blockquote>
+ * <blockquote><code>sliceExtent = {@linkplain #getGridGeometry()}.{@link GridGeometry#derive()
+ * derive()}.{@linkplain GridGeometry.Modifier#slice(DirectPosition)
+ * slice}(slicePoint).{@linkplain GridGeometry.Modifier#build() build()};</code></blockquote>
*
* If the {@code slicePoint} CRS is different than this grid coverage CRS (except for the number of dimensions),
* a coordinate transformation will be applied as needed.
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/SubgridCalculator.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java
similarity index 98%
rename from core/sis-raster/src/main/java/org/apache/sis/coverage/grid/SubgridCalculator.java
rename to core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java
index 4d21eff..961311b 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/SubgridCalculator.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java
@@ -49,7 +49,7 @@ import org.opengis.coverage.PointOutsideCoverageException;
* @since 1.0
* @module
*/
-final class SubgridCalculator {
+final class GridDerivation {
/**
* The sub-extent computed by the constructor.
*/
@@ -80,7 +80,7 @@ final class SubgridCalculator {
* @throws TransformException if an error occurred while converting the envelope coordinates to grid coordinates.
* @throws PointOutsideCoverageException if the given point is outside the grid extent.
*/
- SubgridCalculator(final GridGeometry grid, MathTransform cornerToCRS, final DirectPosition slicePoint)
+ GridDerivation(final GridGeometry grid, MathTransform cornerToCRS, final DirectPosition slicePoint)
throws TransformException
{
try {
@@ -116,7 +116,7 @@ final class SubgridCalculator {
* @param rounding controls behavior of rounding from floating point values to integers.
* @throws TransformException if an error occurred while converting the envelope coordinates to grid coordinates.
*/
- SubgridCalculator(final GridGeometry grid, MathTransform cornerToCRS, final Envelope areaOfInterest,
+ GridDerivation(final GridGeometry grid, MathTransform cornerToCRS, final Envelope areaOfInterest,
double[] resolution, final GridRoundingMode rounding) throws TransformException, FactoryException
{
/*
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
index cfd50af..73d7d25 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
@@ -987,8 +987,8 @@ public class GridGeometry implements Serializable {
* {@preformat java
* GridGeometry gg = ...;
* Envelope areaOfInterest = ...;
- * gg = gg.modify().rounding(GridRoundingMode.ENCLOSING)
- * .subgrid(areaOfInterest).apply();
+ * gg = gg.derive().rounding(GridRoundingMode.ENCLOSING)
+ * .subgrid(areaOfInterest).build();
* }
* </div>
*
@@ -996,20 +996,20 @@ public class GridGeometry implements Serializable {
*
* @return an object for deriving a grid geometry from {@code this}.
*/
- public Modifier modify() {
+ public Modifier derive() {
return new Modifier();
}
/**
* Creates a new grid geometry derived from the enclosing grid geometry with different extent or resolution.
- * {@code Modifier} are created by calls to {@link GridGeometry#modify()}. Properties of the desired grid
+ * {@code Modifier} are created by calls to {@link GridGeometry#derive()}. Properties of the desired grid
* geometry can be specified by {@link #rounding rounding}, {@link #subgrid subgrid}, {@link #slice slice}
- * or {@link #reduce reduce} methods, and the grid geometry is created by {@link #apply()}.
+ * or {@link #reduce reduce} methods, and the grid geometry is created by {@link #build()}.
*
* @author Martin Desruisseaux (Geomatys)
* @version 1.0
*
- * @see GridGeometry#modify()
+ * @see GridGeometry#derive()
*
* @since 1.0
* @module
@@ -1018,7 +1018,7 @@ public class GridGeometry implements Serializable {
/**
* Builder of grid geometry based on a slice of enclosing grid geometry, or {@code null} if none.
*/
- private SubgridCalculator subgrid;
+ private GridDerivation subgrid;
/**
* The grid dimension to keep, or {@code null} if no filtering is applied.
@@ -1034,7 +1034,7 @@ public class GridGeometry implements Serializable {
* Creates a new modifier based on the enclosing grid geometry.
* This constructor is for subclasses only.
*
- * @see GridGeometry#modify()
+ * @see GridGeometry#derive()
*/
protected Modifier() {
rounding = GridRoundingMode.NEAREST;
@@ -1094,7 +1094,7 @@ public class GridGeometry implements Serializable {
ensureBeforeSubgrid();
requireGridToCRS();
try {
- subgrid = new SubgridCalculator(GridGeometry.this, cornerToCRS, areaOfInterest, targetResolution, rounding);
+ subgrid = new GridDerivation(GridGeometry.this, cornerToCRS, areaOfInterest, targetResolution, rounding);
} catch (FactoryException | TransformException e) {
throw new IllegalGridGeometryException(e, "areaOfInterest");
}
@@ -1133,7 +1133,7 @@ public class GridGeometry implements Serializable {
ensureBeforeSubgrid();
requireGridToCRS();
try {
- subgrid = new SubgridCalculator(GridGeometry.this, cornerToCRS, slicePoint);
+ subgrid = new GridDerivation(GridGeometry.this, cornerToCRS, slicePoint);
} catch (TransformException e) {
throw new IllegalGridGeometryException(e, "slicePoint");
}
@@ -1170,7 +1170,7 @@ public class GridGeometry implements Serializable {
/**
* Returns the extent of the modified grid geometry. This method is more efficient than
- * {@link #apply()} if only the grid extent is desired instead than the full grid geometry.
+ * {@link #build()} if only the grid extent is desired instead than the full grid geometry.
*
* @return the modified grid geometry extent.
*/
@@ -1187,7 +1187,7 @@ public class GridGeometry implements Serializable {
*
* @return the modified grid geometry. May be the enclosing geometry if no change apply.
*/
- public GridGeometry apply() {
+ public GridGeometry build() {
GridGeometry gg = GridGeometry.this;
String cause = null;
try {
diff --git a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
index 709f86a..7c51585 100644
--- a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
+++ b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
@@ -265,7 +265,7 @@ public final strictfp class GridGeometryTest extends TestCase {
envelope.setRange(0, -70.001, +80.002);
envelope.setRange(1, 4.997, 15.003);
assertExtentEquals(new long[] {370, 40, 4},
- new long[] {389, 339, 10}, grid.modify().subgrid(envelope).extent());
+ new long[] {389, 339, 10}, grid.derive().subgrid(envelope).extent());
}
/**
@@ -299,7 +299,7 @@ public final strictfp class GridGeometryTest extends TestCase {
final GeneralEnvelope envelope = new GeneralEnvelope(HardCodedCRS.WGS84);
envelope.setRange(0, -70.001, +80.002);
envelope.setRange(1, -4.997, 15.003);
- final GridExtent actual = grid.modify().subgrid(envelope).extent();
+ final GridExtent actual = grid.derive().subgrid(envelope).extent();
assertEquals(extent.getAxisType(0), actual.getAxisType(0));
assertExtentEquals(new long[] { 56, 69, 2},
new long[] {130, 73, 4}, actual);
@@ -329,7 +329,7 @@ public final strictfp class GridGeometryTest extends TestCase {
*/
envelope.setRange(0, -50, +30);
envelope.setRange(1, 8, 12);
- grid = grid.modify().subgrid(envelope, 1, 2).apply();
+ grid = grid.derive().subgrid(envelope, 1, 2).build();
assertExtentEquals(new long[] {94, 40}, new long[] {95, 119}, grid.getExtent());
assertEnvelopeEquals(envelope, grid.getEnvelope(), STRICT);
assertMatrixEquals("gridToCRS", new Matrix3(
@@ -341,7 +341,7 @@ public final strictfp class GridGeometryTest extends TestCase {
* It will force GridGeometry to adjust the translation term to compensate. We verify that the adustment
* is correct by verifying that we still get the same envelope.
*/
- grid = grid.modify().subgrid(envelope, 3, 2).apply();
+ grid = grid.derive().subgrid(envelope, 3, 2).build();
assertExtentEquals(new long[] {94, 13}, new long[] {95, 39}, grid.getExtent());
assertEnvelopeEquals(envelope, grid.getEnvelope(), STRICT);
MathTransform cornerToCRS = grid.getGridToCRS(PixelInCell.CELL_CORNER);
@@ -372,7 +372,7 @@ public final strictfp class GridGeometryTest extends TestCase {
/*
* There is two ways to ask for a slice. The first way is to set some coordinates to NaN.
*/
- GridGeometry slice = grid.modify().slice(new GeneralDirectPosition(Double.NaN, Double.NaN, 15)).apply();
+ GridGeometry slice = grid.derive().slice(new GeneralDirectPosition(Double.NaN, Double.NaN, 15)).build();
assertNotSame(grid, slice);
assertSame("gridToCRS", grid.gridToCRS, slice.gridToCRS);
final long[] expectedLow = {336, 20, 6};
@@ -384,7 +384,7 @@ public final strictfp class GridGeometryTest extends TestCase {
*/
GeneralDirectPosition p = new GeneralDirectPosition(HardCodedCRS.ELLIPSOIDAL_HEIGHT_cm);
p.setOrdinate(0, 1500);
- slice = grid.modify().slice(p).apply();
+ slice = grid.derive().slice(p).build();
assertNotSame(grid, slice);
assertSame("gridToCRS", grid.gridToCRS, slice.gridToCRS);
assertExtentEquals(expectedLow, expectedHigh, slice.getExtent());
@@ -405,7 +405,7 @@ public final strictfp class GridGeometryTest extends TestCase {
/*
* Tests on the two first dimensions.
*/
- GridGeometry reduced = grid.modify().reduce(0, 1).apply();
+ GridGeometry reduced = grid.derive().reduce(0, 1).build();
assertNotSame(grid, reduced);
assertExtentEquals(new long[] {336, 20}, new long[] {401, 419}, reduced.getExtent());
assertSame("CRS", HardCodedCRS.WGS84, reduced.getCoordinateReferenceSystem());
@@ -417,7 +417,7 @@ public final strictfp class GridGeometryTest extends TestCase {
/*
* Tests on the last dimension.
*/
- reduced = grid.modify().reduce(2).apply();
+ reduced = grid.derive().reduce(2).build();
assertNotSame(grid, reduced);
assertExtentEquals(new long[] {4}, new long[] {10}, reduced.getExtent());
assertSame("CRS", HardCodedCRS.GRAVITY_RELATED_HEIGHT, reduced.getCoordinateReferenceSystem());
[sis] 02/02: Refactor GridGeometry.Modifier as methods provided
directly in GridDerivation.
Posted by de...@apache.org.
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 bd65feded38b31a7f0f7d6bd724c62487d2f462d
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sun Jan 13 17:38:39 2019 +0100
Refactor GridGeometry.Modifier as methods provided directly in GridDerivation.
---
.../org/apache/sis/coverage/grid/GridCoverage.java | 4 +-
.../apache/sis/coverage/grid/GridDerivation.java | 455 +++++++++++++++------
.../org/apache/sis/coverage/grid/GridExtent.java | 22 +-
.../org/apache/sis/coverage/grid/GridGeometry.java | 241 +----------
.../org/apache/sis/internal/raster/Resources.java | 5 +
.../sis/internal/raster/Resources.properties | 1 +
.../sis/internal/raster/Resources_fr.properties | 1 +
...idGeometryTest.java => GridDerivationTest.java} | 216 +---------
.../apache/sis/coverage/grid/GridGeometryTest.java | 195 +--------
.../org/apache/sis/test/suite/RasterTestSuite.java | 1 +
10 files changed, 379 insertions(+), 762 deletions(-)
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
index b794f2b..7d19398 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
@@ -147,8 +147,8 @@ public abstract class GridCoverage {
* Then:
*
* <blockquote><code>sliceExtent = {@linkplain #getGridGeometry()}.{@link GridGeometry#derive()
- * derive()}.{@linkplain GridGeometry.Modifier#slice(DirectPosition)
- * slice}(slicePoint).{@linkplain GridGeometry.Modifier#build() build()};</code></blockquote>
+ * derive()}.{@linkplain GridDerivation#slice(DirectPosition)
+ * slice}(slicePoint).{@linkplain GridDerivation#build() build()};</code></blockquote>
*
* If the {@code slicePoint} CRS is different than this grid coverage CRS (except for the number of dimensions),
* a coordinate transformation will be applied as needed.
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java
index 961311b..51c18b5 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java
@@ -40,26 +40,54 @@ import org.opengis.coverage.PointOutsideCoverageException;
/**
- * Helper class for computing the grid extent of a sub-area of a given grid geometry.
- * This class provides the {@link MathTransform} converting source grid coordinates to target grid coordinates,
- * where the source is the given {@link GridGeometry} instance and the target is the subsampled grid.
+ * Creates a new grid geometry derived from a base grid geometry with different extent or resolution.
+ * {@code GridDerivation} are created by calls to {@link GridGeometry#derive()}.
+ * Properties of the desired grid geometry can be specified by calls to the following methods:
+ *
+ * <ul>
+ * <li>{@link #rounding(GridRoundingMode)} — if invoked, should be first.</li>
+ * <li>At most one of the following methods:<ul>
+ * <li>{@link #subgrid(Envelope, double...)}</li>
+ * <li>{@link #slice(DirectPosition)}</li>
+ * </ul></li>
+ * <li>{@link #reduce(int...)}</li>
+ * </ul>
+ *
+ * Then the grid geometry is created by a call to {@link #build()}.
+ * Alternatively, {@link #extent()} can be invoked if only the {@link GridExtent} is desired
+ * instead than the full {@link GridGeometry}.
*
* @author Martin Desruisseaux (Geomatys)
* @version 1.0
- * @since 1.0
+ *
+ * @see GridGeometry#derive()
+ *
+ * @since 1.0
* @module
*/
-final class GridDerivation {
+public class GridDerivation {
+ /**
+ * The base grid geometry from which to derive a new grid geometry.
+ */
+ protected final GridGeometry base;
+
+ /**
+ * If {@link #slice(DirectPosition)} or {@link #subgrid(Envelope, double...)} has been invoked, the method name.
+ * This is used for preventing those methods to be invoked twice, which is currently not supported.
+ */
+ private String subGridSetter;
+
/**
- * The sub-extent computed by the constructor.
+ * The sub-extent computed by a {@link #slice(DirectPosition)} or {@link #subgrid(Envelope, double...)} methods,
+ * or {@code null} if no slice or sub-grid has been requested.
*/
- GridExtent extent;
+ private GridExtent subExtent;
/**
* The conversion from the original grid to the subsampled grid, or {@code null} if no subsampling is applied.
- * This is computed by the constructor.
+ * This is computed by {@link #subgrid(Envelope, double...)}.
*/
- MathTransform toSubsampled;
+ private MathTransform toSubsampled;
/**
* List of grid dimensions that are modified by the {@code cornerToCRS} transform, or null for all dimensions.
@@ -69,23 +97,104 @@ final class GridDerivation {
private int[] modifiedDimensions;
/**
- * Computes the sub-grid for a slice at the given slice point. The given position can be given in any CRS.
- * The position should not define a coordinate for all dimensions, otherwise the sub-grid would degenerate
- * to a single point. Dimensions can be left unspecified either by assigning to the position a CRS without
- * those dimensions, or by assigning the NaN value to some coordinates.
+ * The grid dimension to keep, or {@code null} if no filtering is applied.
+ * Those values are set by {@link #reduce(int...)}.
+ */
+ private int[] selectedDimensions;
+
+ /**
+ * Controls behavior of rounding from floating point values to integers.
+ */
+ private GridRoundingMode rounding;
+
+ /**
+ * Creates a new builder for deriving a grid geometry from the specified base.
+ *
+ * @param base the base to use as a template for deriving a new grid geometry.
+ *
+ * @see GridGeometry#derive()
+ */
+ protected GridDerivation(final GridGeometry base) {
+ ArgumentChecks.ensureNonNull("base", base);
+ this.base = base;
+ rounding = GridRoundingMode.NEAREST;
+ }
+
+ /**
+ * Controls behavior of rounding from floating point values to integers.
+ * This setting modifies computations performed by the following methods
+ * (it has no effect on other methods in this {@code GridDerivation} class):
+ * <ul>
+ * <li>{@link #slice(DirectPosition)}</li>
+ * <li>{@link #subgrid(Envelope, double...)}</li>
+ * </ul>
+ *
+ * If this method is never invoked, the default value is {@link GridRoundingMode#NEAREST}.
+ * If this method is invoked too late, an {@link IllegalStateException} is thrown.
+ *
+ * @param mode the new rounding mode.
+ * @return {@code this} for method call chaining.
+ * @throws IllegalStateException if {@link #slice(DirectPosition)} or
+ * {@link #subgrid(Envelope, double...)} has already been invoked.
+ */
+ public GridDerivation rounding(final GridRoundingMode mode) {
+ ArgumentChecks.ensureNonNull("mode", mode);
+ ensureSubgridNotSet();
+ rounding = mode;
+ return this;
+ }
+
+ /**
+ * Verifies that a sub-grid has not yet been defined.
+ */
+ private void ensureSubgridNotSet() {
+ if (subGridSetter != null) {
+ throw new IllegalStateException(Resources.format(Resources.Keys.CanNotSetDerivedGridProperty_1, subGridSetter));
+ }
+ }
+
+ /**
+ * Requests a grid geometry for a slice at the given point.
+ * The given position can be expressed in any coordinate reference system (CRS).
+ * The position should not define a coordinate for all dimensions, otherwise the slice would degenerate
+ * to a single point. Dimensions can be left unspecified either by assigning to {@code slicePoint} a CRS
+ * without those dimensions, or by assigning the NaN value to some coordinates.
+ *
+ * <div class="note"><b>Example:</b>
+ * if the {@linkplain GridGeometry#getCoordinateReferenceSystem() coordinate reference system} of base grid geometry has
+ * (<var>longitude</var>, <var>latitude</var>, <var>time</var>) axes, then a (<var>longitude</var>, <var>latitude</var>)
+ * slice at time <var>t</var> can be created with one of the following two positions:
+ * <ul>
+ * <li>A three-dimensional position with ({@link Double#NaN}, {@link Double#NaN}, <var>t</var>) coordinates.</li>
+ * <li>A one-dimensional position with (<var>t</var>) coordinate and the coordinate reference system set to
+ * {@linkplain org.apache.sis.referencing.CRS#getTemporalComponent(CoordinateReferenceSystem) the temporal component}
+ * of the grid geometry CRS.</li>
+ * </ul></div>
+ *
+ * <p>Notes:</p>
+ * <ul>
+ * <li>This method can be invoked only once.</li>
+ * <li>This method can not be used together with {@link #subgrid(Envelope, double...)}.</li>
+ * <li>If a non-default rounding mode is desired, it should be {@linkplain #rounding(GridRoundingMode) specified}
+ * before to invoke this method.</li>
+ * <li>This method does not reduce the number of dimensions of the grid geometry.
+ * For dimensionality reduction, see {@link #reduce(int...)}.</li>
+ * </ul>
*
- * @param grid the enclosing grid geometry (mandatory).
- * @param cornerToCRS the transform from cell corners to grid CRS (mandatory).
* @param slicePoint the coordinates where to get a slice.
- * @throws TransformException if an error occurred while converting the envelope coordinates to grid coordinates.
+ * @return {@code this} for method call chaining.
+ * @throws IncompleteGridGeometryException if the base grid geometry has no extent or no "grid to CRS" transform.
+ * @throws IllegalGridGeometryException if an error occurred while converting the point coordinates to grid coordinates.
* @throws PointOutsideCoverageException if the given point is outside the grid extent.
*/
- GridDerivation(final GridGeometry grid, MathTransform cornerToCRS, final DirectPosition slicePoint)
- throws TransformException
- {
+ public GridDerivation slice(final DirectPosition slicePoint) {
+ ArgumentChecks.ensureNonNull("slicePoint", slicePoint);
+ ensureSubgridNotSet();
+ MathTransform cornerToCRS = base.requireGridToCRS();
+ subGridSetter = "slice";
try {
- if (grid.envelope != null) {
- final CoordinateReferenceSystem sourceCRS = grid.envelope.getCoordinateReferenceSystem();
+ if (base.envelope != null) {
+ final CoordinateReferenceSystem sourceCRS = base.envelope.getCoordinateReferenceSystem();
if (sourceCRS != null) {
final CoordinateReferenceSystem targetCRS = slicePoint.getCoordinateReferenceSystem();
if (targetCRS != null) {
@@ -97,114 +206,141 @@ final class GridDerivation {
final int dimension = cornerToCRS.getTargetDimensions();
ArgumentChecks.ensureDimensionMatches("slicePoint", dimension, slicePoint);
cornerToCRS = dropUnusedDimensions(cornerToCRS, dimension);
+ subExtent = base.extent.slice(cornerToCRS.inverse().transform(slicePoint, null), modifiedDimensions);
} catch (FactoryException e) {
- throw new TransformException(Resources.format(Resources.Keys.CanNotMapToGridDimensions), e);
+ throw new IllegalGridGeometryException(Resources.format(Resources.Keys.CanNotMapToGridDimensions), e);
+ } catch (TransformException e) {
+ throw new IllegalGridGeometryException(e, "slicePoint");
}
- extent = grid.extent.slice(cornerToCRS.inverse().transform(slicePoint, null), modifiedDimensions);
+ return this;
}
/**
- * Computes the sub-grid over the given area of interest with the given resolution.
- * At least one of {@code areaOfInterest} and {@code resolution} shall be non-null.
- * It is caller's responsibility to ensure that {@link GridGeometry#extent} is non-null.
+ * Requests a grid geometry over a sub-region of the base grid geometry and optionally with subsampling.
+ * The given envelope does not need to be expressed in the same coordinate reference system (CRS)
+ * than {@linkplain GridGeometry#getCoordinateReferenceSystem() the CRS of the base grid geometry};
+ * coordinate conversions or transformations will be applied as needed.
+ * That envelope CRS may have fewer dimensions than the base grid geometry CRS,
+ * in which case grid dimensions not mapped to envelope dimensions will be returned unchanged.
+ * The target resolution, if provided, shall be in same units and same order than the given envelope axes.
+ * If the length of {@code resolution} array is less than the number of dimensions of {@code areaOfInterest},
+ * then no subsampling will be applied on the missing dimensions.
+ *
+ * <p>Notes:</p>
+ * <ul>
+ * <li>This method can be invoked only once.</li>
+ * <li>This method can not be used together with {@link #slice(DirectPosition)}.</li>
+ * <li>If a non-default rounding mode is desired, it should be {@linkplain #rounding(GridRoundingMode) specified}
+ * before to invoke this method.</li>
+ * <li>This method does not reduce the number of dimensions of the grid geometry.
+ * For dimensionality reduction, see {@link #reduce(int...)}.</li>
+ * </ul>
+ *
+ * @param areaOfInterest the desired spatiotemporal region in any CRS (transformations will be applied as needed),
+ * or {@code null} for not restricting the sub-grid to a sub-area.
+ * @param resolution the desired resolution in the same units and order than the axes of the given envelope,
+ * or {@code null} or an empty array if no subsampling is desired.
+ * @return {@code this} for method call chaining.
+ * @throws IncompleteGridGeometryException if the base grid geometry has no extent or no "grid to CRS" transform.
+ * @throws IllegalGridGeometryException if an error occurred while converting the envelope coordinates to grid coordinates.
*
- * @param grid the enclosing grid geometry (mandatory).
- * @param cornerToCRS the transform from cell corners to grid CRS (mandatory).
- * @param areaOfInterest the desired spatiotemporal region in any CRS, or {@code null} for the whole area.
- * @param resolution the desired resolution in the same units and order than the axes of the AOI envelope,
- * or {@code null} or an empty array if no subsampling is desired.
- * @param rounding controls behavior of rounding from floating point values to integers.
- * @throws TransformException if an error occurred while converting the envelope coordinates to grid coordinates.
+ * @see GridExtent#subsample(int[])
*/
- GridDerivation(final GridGeometry grid, MathTransform cornerToCRS, final Envelope areaOfInterest,
- double[] resolution, final GridRoundingMode rounding) throws TransformException, FactoryException
- {
- /*
- * If the envelope CRS is different than the expected CRS, concatenate the envelope transformation
- * to the 'gridToCRS' transform. We should not transform the envelope here - only concatenate the
- * transforms - because transforming envelopes twice would add errors.
- */
- final CoordinateOperation operation = Envelopes.findOperation(grid.envelope, areaOfInterest);
- if (operation != null) {
- cornerToCRS = MathTransforms.concatenate(cornerToCRS, operation.getMathTransform());
- }
- /*
- * If the envelope dimensions does not encompass all grid dimensions, the envelope is probably non-invertible.
- * We need to reduce the number of grid dimensions in the transform for having a one-to-one relationship.
- */
- int dimension = cornerToCRS.getTargetDimensions();
- ArgumentChecks.ensureDimensionMatches("areaOfInterest", dimension, areaOfInterest);
- cornerToCRS = dropUnusedDimensions(cornerToCRS, dimension);
- /*
- * Compute the sub-extent for the given Area Of Interest (AOI), ignoring for now the subsampling.
- * If no area of interest has been specified, or if the result is identical to the original extent,
- * then we will keep the reference to the original GridExtent (i.e. we share existing instances).
- */
- extent = grid.extent;
- dimension = extent.getDimension();
- GeneralEnvelope indices = null;
- if (areaOfInterest != null) {
- indices = Envelopes.transform(cornerToCRS.inverse(), areaOfInterest);
- setExtent(indices, extent, rounding);
- }
- if (indices == null || indices.getDimension() != dimension) {
- indices = new GeneralEnvelope(dimension);
- }
- for (int i=0; i<dimension; i++) {
- indices.setRange(i, extent.getLow(i), extent.getHigh(i) + 1.0);
- }
- /*
- * Convert the target resolutions to grid cell subsamplings and adjust the extent consequently.
- * We perform this conversion by handling the resolution has a small translation vector located
- * at the point of interest, and converting it to a translation vector in grid coordinates. The
- * conversion is done by a multiplication with the "CRS to grid" derivative at that point.
- *
- * The subsampling will be rounded in such a way that the difference in grid size is less than
- * one half of cell. Demonstration:
- *
- * e = Math.getExponent(span) → 2^e ≦ span
- * a = e+1 → 2^a > span → 1/2^a < 1/span
- * Δs = (s - round(s)) / 2^a
- * (s - round(s)) ≦ 0.5 → Δs ≦ 0.5/2^a < 0.5/span
- * Δs < 0.5/span → Δs⋅span < 0.5 cell.
- */
- if (resolution != null && resolution.length != 0) {
- resolution = ArraysExt.resize(resolution, cornerToCRS.getTargetDimensions());
- final int[] modifiedDimensions = this.modifiedDimensions; // Will not change anymore.
- Matrix m = cornerToCRS.derivative(new DirectPositionView.Double(getPointOfInterest(modifiedDimensions)));
- resolution = Matrices.inverse(m).multiply(resolution);
- boolean modified = false;
- for (int k=0; k<resolution.length; k++) {
- double s = Math.abs(resolution[k]);
- if (s > 1) { // Also for skipping NaN values.
- final int i = (modifiedDimensions != null) ? modifiedDimensions[k] : k;
- final int accuracy = Math.max(0, Math.getExponent(indices.getSpan(i))) + 1; // Power of 2.
- s = Math.scalb(Math.rint(Math.scalb(s, accuracy)), -accuracy);
- indices.setRange(i, indices.getLower(i) / s, indices.getUpper(i) / s);
- modified = true;
- }
- resolution[k] = s;
+ public GridDerivation subgrid(final Envelope areaOfInterest, double... resolution) {
+ ensureSubgridNotSet();
+ MathTransform cornerToCRS = base.requireGridToCRS();
+ subGridSetter = "subgrid";
+ try {
+ /*
+ * If the envelope CRS is different than the expected CRS, concatenate the envelope transformation
+ * to the 'gridToCRS' transform. We should not transform the envelope here - only concatenate the
+ * transforms - because transforming envelopes twice would add errors.
+ */
+ final CoordinateOperation operation = Envelopes.findOperation(base.envelope, areaOfInterest);
+ if (operation != null) {
+ cornerToCRS = MathTransforms.concatenate(cornerToCRS, operation.getMathTransform());
+ }
+ /*
+ * If the envelope dimensions does not encompass all grid dimensions, the envelope is probably non-invertible.
+ * We need to reduce the number of grid dimensions in the transform for having a one-to-one relationship.
+ */
+ int dimension = cornerToCRS.getTargetDimensions();
+ ArgumentChecks.ensureDimensionMatches("areaOfInterest", dimension, areaOfInterest);
+ cornerToCRS = dropUnusedDimensions(cornerToCRS, dimension);
+ /*
+ * Compute the sub-extent for the given Area Of Interest (AOI), ignoring for now the subsampling.
+ * If no area of interest has been specified, or if the result is identical to the original extent,
+ * then we will keep the reference to the original GridExtent (i.e. we share existing instances).
+ */
+ subExtent = base.extent;
+ dimension = subExtent.getDimension();
+ GeneralEnvelope indices = null;
+ if (areaOfInterest != null) {
+ indices = Envelopes.transform(cornerToCRS.inverse(), areaOfInterest);
+ setSubExtent(indices, subExtent);
+ }
+ if (indices == null || indices.getDimension() != dimension) {
+ indices = new GeneralEnvelope(dimension);
+ }
+ for (int i=0; i<dimension; i++) {
+ indices.setRange(i, subExtent.getLow(i), subExtent.getHigh(i) + 1.0);
}
/*
- * If at least one subsampling is effective, build a scale from the old grid coordinates to the new
- * grid coordinates. If we had no rounding, the conversion would be only a scale. But because of rounding,
- * we need a small translation for the difference between the "real" coordinate and the integer coordinate.
+ * Convert the target resolutions to grid cell subsamplings and adjust the extent consequently.
+ * We perform this conversion by handling the resolutions as a small translation vector located
+ * at the point of interest, and converting it to a translation vector in grid coordinates. The
+ * conversion is done by a multiplication with the "CRS to grid" derivative at that point.
+ *
+ * The subsampling will be rounded in such a way that the difference in grid size is less than
+ * one half of cell. Demonstration:
+ *
+ * e = Math.getExponent(span) → 2^e ≦ span
+ * a = e+1 → 2^a > span → 1/2^a < 1/span
+ * Δs = (s - round(s)) / 2^a
+ * (s - round(s)) ≦ 0.5 → Δs ≦ 0.5/2^a < 0.5/span
+ * Δs < 0.5/span → Δs⋅span < 0.5 cell.
*/
- if (modified) {
- final GridExtent unscaled = extent;
- setExtent(indices, null, rounding);
- m = Matrices.createIdentity(dimension + 1);
+ if (resolution != null && resolution.length != 0) {
+ resolution = ArraysExt.resize(resolution, cornerToCRS.getTargetDimensions());
+ Matrix m = cornerToCRS.derivative(new DirectPositionView.Double(getPointOfInterest()));
+ resolution = Matrices.inverse(m).multiply(resolution);
+ final int[] modifiedDimensions = this.modifiedDimensions; // Will not change anymore.
+ boolean modified = false;
for (int k=0; k<resolution.length; k++) {
- final double s = resolution[k];
- if (s > 1) { // Also for skipping NaN values.
+ double s = Math.abs(resolution[k]);
+ if (s > 1) { // Also for skipping NaN values.
final int i = (modifiedDimensions != null) ? modifiedDimensions[k] : k;
- m.setElement(i, i, s);
- m.setElement(i, dimension, unscaled.getLow(i) - extent.getLow(i) * s);
+ final int accuracy = Math.max(0, Math.getExponent(indices.getSpan(i))) + 1; // Power of 2.
+ s = Math.scalb(Math.rint(Math.scalb(s, accuracy)), -accuracy);
+ indices.setRange(i, indices.getLower(i) / s, indices.getUpper(i) / s);
+ modified = true;
}
+ resolution[k] = s;
+ }
+ /*
+ * If at least one subsampling is effective, build a scale from the old grid coordinates to the new
+ * grid coordinates. If we had no rounding, the conversion would be only a scale. But because of rounding,
+ * we need a small translation for the difference between the "real" coordinate and the integer coordinate.
+ */
+ if (modified) {
+ final GridExtent unscaled = subExtent;
+ setSubExtent(indices, null);
+ m = Matrices.createIdentity(dimension + 1);
+ for (int k=0; k<resolution.length; k++) {
+ final double s = resolution[k];
+ if (s > 1) { // Also for skipping NaN values.
+ final int i = (modifiedDimensions != null) ? modifiedDimensions[k] : k;
+ m.setElement(i, i, s);
+ m.setElement(i, dimension, unscaled.getLow(i) - subExtent.getLow(i) * s);
+ }
+ }
+ toSubsampled = MathTransforms.linear(m);
}
- toSubsampled = MathTransforms.linear(m);
}
+ } catch (FactoryException | TransformException e) {
+ throw new IllegalGridGeometryException(e, "areaOfInterest");
}
+ return this;
}
/**
@@ -231,31 +367,96 @@ final class GridDerivation {
}
/**
- * Sets {@link #extent} to the given envelope.
+ * Returns the point of interest of current {@link #subExtent}, keeping only the remaining
+ * dimensions after {@link #dropUnusedDimensions(MathTransform, int)} execution.
+ */
+ private double[] getPointOfInterest() {
+ final double[] pointOfInterest = subExtent.getPointOfInterest();
+ if (modifiedDimensions == null) {
+ return pointOfInterest;
+ }
+ final double[] filtered = new double[modifiedDimensions.length];
+ for (int i=0; i<filtered.length; i++) {
+ filtered[i] = pointOfInterest[modifiedDimensions[i]];
+ }
+ return filtered;
+ }
+
+ /**
+ * Sets {@link #subExtent} to the given envelope.
*
* @param indices the envelope to use for setting the grid extent.
* @param enclosing the enclosing grid extent if a subsampling is not yet applied, {@code null} otherwise.
- * @param rounding controls behavior of rounding from floating point values to integers.
*/
- private void setExtent(final GeneralEnvelope indices, final GridExtent enclosing, final GridRoundingMode rounding) {
+ private void setSubExtent(final GeneralEnvelope indices, final GridExtent enclosing) {
final GridExtent sub = new GridExtent(indices, rounding, null, enclosing, modifiedDimensions);
- if (!sub.equals(extent)) {
- extent = sub;
+ if (!sub.equals(subExtent)) {
+ subExtent = sub;
}
}
/**
- * Returns the point of interest of current {@link #extent}.
+ * Requests a grid geometry that encompass only some dimensions of the grid extent.
+ * The specified dimensions will be copied into a new grid geometry.
+ * The selection is applied on {@linkplain GridGeometry#getExtent() grid extent} dimensions;
+ * they are not necessarily the same than the {@linkplain GridGeometry#getEnvelope() envelope} dimensions.
+ * The given dimensions must be in strictly ascending order without duplicated values.
+ * The number of dimensions of the sub grid geometry will be {@code dimensions.length}.
+ *
+ * <p>This method performs a <cite>dimensionality reduction</cite>.
+ * This method can not be used for changing dimension order.</p>
+ *
+ * @param dimensions the grid (not CRS) dimensions to select, in strictly increasing order.
+ * @return {@code this} for method call chaining.
+ * @throws IndexOutOfBoundsException if an index is out of bounds.
+ *
+ * @see GridExtent#getSubspaceDimensions(int)
+ * @see GridExtent#reduce(int...)
+ * @see org.apache.sis.referencing.CRS#reduce(CoordinateReferenceSystem, int...)
*/
- private double[] getPointOfInterest(final int[] modifiedDimensions) {
- final double[] pointOfInterest = extent.getPointOfInterest();
- if (modifiedDimensions == null) {
- return pointOfInterest;
+ public GridDerivation reduce(final int... dimensions) {
+ if (selectedDimensions != null) {
+ throw new IllegalStateException(Resources.format(Resources.Keys.CanNotSetDerivedGridProperty_1, "reduce"));
}
- final double[] filtered = new double[modifiedDimensions.length];
- for (int i=0; i<filtered.length; i++) {
- filtered[i] = pointOfInterest[modifiedDimensions[i]];
+ selectedDimensions = GridExtent.verifyDimensions(dimensions, base.getDimension());
+ return this;
+ }
+
+ /**
+ * Returns the extent of the modified grid geometry. This method is more efficient than
+ * {@link #build()} if only the grid extent is desired instead than the full grid geometry.
+ *
+ * @return the modified grid geometry extent.
+ */
+ public GridExtent extent() {
+ GridExtent extent = (subExtent != null) ? subExtent : base.getExtent();
+ if (selectedDimensions != null) {
+ extent = extent.reduce(selectedDimensions);
}
- return filtered;
+ return extent;
+ }
+
+ /**
+ * Builds a grid geometry with the configuration specified by the other methods in this {@code GridDerivation} class.
+ *
+ * @return the modified grid geometry. May be the {@linkplain #base} grid geometry if no change apply.
+ */
+ public GridGeometry build() {
+ GridGeometry grid = base;
+ GridExtent extent = (subExtent != null) ? subExtent : base.extent;
+ String cause = null;
+ try {
+ if (toSubsampled != null || extent != base.extent) {
+ cause = "subgrid";
+ grid = new GridGeometry(grid, extent, toSubsampled);
+ }
+ if (selectedDimensions != null) {
+ cause = "dimensions";
+ grid = new GridGeometry(grid, selectedDimensions);
+ }
+ } catch (FactoryException | TransformException e) {
+ throw new IllegalGridGeometryException(e, cause);
+ }
+ return grid;
}
}
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
index c1d7230..0ada60f 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
@@ -856,14 +856,18 @@ public class GridExtent implements Serializable {
*
* @param dimensions the user-supplied argument to validate.
* @param limit maximal number of dimensions, exclusive.
- * @return {@code true} if the caller can return {@code this}.
+ * @return a clone of the given array, or {@code null} if the caller can return {@code this}.
*/
- static boolean verifyDimensions(final int[] dimensions, final int limit) {
+ static int[] verifyDimensions(int[] dimensions, final int limit) {
ArgumentChecks.ensureNonNull("dimensions", dimensions);
final int n = dimensions.length;
if (n == 0) {
throw new IllegalArgumentException(Errors.format(Errors.Keys.EmptyArgument_1, "dimensions"));
}
+ if (n > limit) {
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.TooManyOccurrences_2, limit, "dimension"));
+ }
+ dimensions = dimensions.clone();
if (!ArraysExt.isSorted(dimensions, true)) {
throw new IllegalArgumentException(Resources.format(Resources.Keys.NotStrictlyOrderedDimensions));
}
@@ -871,7 +875,7 @@ public class GridExtent implements Serializable {
if (d >= 0) {
d = dimensions[n - 1];
if (d < limit) {
- return n == limit;
+ return (n != limit) ? dimensions : null;
}
}
throw new IndexOutOfBoundsException(Errors.format(Errors.Keys.IndexOutOfBounds_1, d));
@@ -891,11 +895,12 @@ public class GridExtent implements Serializable {
* @return the sub-envelope, or {@code this} if the given array contains all dimensions of this grid extent.
* @throws IndexOutOfBoundsException if an index is out of bounds.
*
- * @see GridGeometry.Modifier#reduce(int...)
+ * @see GridDerivation#reduce(int...)
*/
- public GridExtent reduce(final int... dimensions) {
+ public GridExtent reduce(int... dimensions) {
final int sd = getDimension();
- if (verifyDimensions(dimensions, sd)) {
+ dimensions = verifyDimensions(dimensions, sd);
+ if (dimensions == null) {
return this;
}
final int td = dimensions.length;
@@ -935,7 +940,7 @@ public class GridExtent implements Serializable {
* @return the subsampled extent, or {@code this} is subsampling results in the same extent.
* @throws IllegalArgumentException if a period is not greater than zero.
*
- * @see GridGeometry.Modifier#subgrid(Envelope, double...)
+ * @see GridDerivation#subgrid(Envelope, double...)
*/
public GridExtent subsample(final int... periods) {
ArgumentChecks.ensureNonNull("periods", periods);
@@ -964,8 +969,7 @@ public class GridExtent implements Serializable {
* Creates a new grid extent which represent a slice of this grid at the given point.
* The given point may have less dimensions than this grid extent, in which case the
* dimensions must be specified in the {@code modifiedDimensions} array. Coordinates
- * in the given point will be rounded to nearest integer. This method does not reduce
- * the number of dimensions of the grid extent.
+ * in the given point will be rounded to nearest integer.
*
* <p>This method does not reduce the number of dimensions of the grid extent.
* For dimensionality reduction, see {@link #reduce(int...)}.</p>
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
index 73d7d25..a33d55e 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
@@ -26,7 +26,6 @@ import java.awt.image.RenderedImage; // For javadoc only.
import org.opengis.util.FactoryException;
import org.opengis.metadata.Identifier;
import org.opengis.geometry.Envelope;
-import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.Matrix;
@@ -60,9 +59,6 @@ import org.apache.sis.util.Classes;
import org.apache.sis.util.Debug;
import org.apache.sis.io.TableAppender;
-// Branch-dependent imports
-import org.opengis.coverage.PointOutsideCoverageException;
-
/**
* Valid extent of grid coordinates together with the transform from those grid coordinates
@@ -242,7 +238,7 @@ public class GridGeometry implements Serializable {
/**
* Creates a new grid geometry derived from the given grid geometry with a new extent and a modified transform.
* This constructor is used for creating a grid geometry over a subregion (for example with the grid extent
- * computed by {@link Modifier#subgrid(Envelope, double...)}) or grid geometry for a subsampled raster.
+ * computed by {@link GridDerivation#subgrid(Envelope, double...)}) or grid geometry for a subsampled raster.
*
* <p>If {@code toOther} is non-null, it should be a transform from the given {@code extent} coordinates to the
* {@code other} grid coordinates. That transform should be merely a {@linkplain MathTransforms#scale(double...)
@@ -261,7 +257,7 @@ public class GridGeometry implements Serializable {
* @throws NullPointerException if {@code extent} is {@code null} and the other grid geometry contains no other information.
* @throws TransformException if the math transform can not compute the geospatial envelope from the grid extent.
*
- * @see Modifier#subgrid(Envelope, double...)
+ * @see GridDerivation#subgrid(Envelope, double...)
*/
GridGeometry(final GridGeometry other, final GridExtent extent, final MathTransform toOther) throws TransformException {
final int dimension = other.getDimension();
@@ -509,9 +505,9 @@ public class GridGeometry implements Serializable {
* @param dimensions the dimensions to select, in strictly increasing order (this may not be verified).
* @throws FactoryException if an error occurred while separating the "grid to CRS" transform.
*
- * @see Modifier#reduce(int...)
+ * @see GridDerivation#reduce(int...)
*/
- private GridGeometry(final GridGeometry other, int[] dimensions) throws FactoryException {
+ GridGeometry(final GridGeometry other, int[] dimensions) throws FactoryException {
extent = (other.extent != null) ? other.extent.reduce(dimensions) : null;
final int n = dimensions.length;
if (other.gridToCRS != null) {
@@ -630,7 +626,7 @@ public class GridGeometry implements Serializable {
* {@linkplain #getGridToCRS(PixelInCell) transformed} to the "real world" coordinate system.
* The initial envelope encompasses all cell surfaces, from the left border of leftmost cell
* to the right border of the rightmost cell and similarly along other axes.
- * If this grid geometry is a {@linkplain Modifier#subgrid(Envelope, double...) subgrid}, then the envelope is also
+ * If this grid geometry is a {@linkplain GridDerivation#subgrid(Envelope, double...) subgrid}, then the envelope is also
* {@linkplain GeneralEnvelope#intersect(Envelope) clipped} to the envelope of the original (non subsampled) grid geometry.
*
* @return the bounding box in "real world" coordinates (never {@code null}).
@@ -936,14 +932,17 @@ public class GridGeometry implements Serializable {
/**
* Verifies that this grid geometry defines an {@linkplain #extent} and a {@link #cornerToCRS} transform.
* They are the information required for mapping the grid to a spatiotemporal envelope.
+ *
+ * @return {@link #cornerToCRS}.
*/
- private void requireGridToCRS() throws IncompleteGridGeometryException {
+ final MathTransform requireGridToCRS() throws IncompleteGridGeometryException {
if (extent == null) {
throw incomplete(EXTENT, Resources.Keys.UnspecifiedGridExtent);
}
if (cornerToCRS == null) {
throw incomplete(GRID_TO_CRS, Resources.Keys.UnspecifiedTransform);
}
+ return cornerToCRS;
}
/**
@@ -977,9 +976,9 @@ public class GridGeometry implements Serializable {
/**
* Returns an object that can be used for creating a new grid geometry derived from this grid geometry.
- * Despite its name, {@code Modifier} does not change the state of this {@code GridGeometry} but instead
- * creates new instances as needed. Examples of modifications include clipping to a sub-area, applying a
- * sub-sampling, or selecting some grid dimensions.
+ * Despite its name, {@code GridDerivation} does not change the state of this {@code GridGeometry} but
+ * instead creates new instances as needed. Examples of modifications include clipping to a sub-area,
+ * applying a sub-sampling, or selecting some grid dimensions.
*
* <div class="note"><b>Example:</b>
* for clipping this grid geometry to a sub-area, one can use:
@@ -992,222 +991,12 @@ public class GridGeometry implements Serializable {
* }
* </div>
*
- * Each {@code Modifier} instance can be used only once.
+ * Each {@code GridDerivation} instance can be used only once and should be used in a single thread.
*
* @return an object for deriving a grid geometry from {@code this}.
*/
- public Modifier derive() {
- return new Modifier();
- }
-
- /**
- * Creates a new grid geometry derived from the enclosing grid geometry with different extent or resolution.
- * {@code Modifier} are created by calls to {@link GridGeometry#derive()}. Properties of the desired grid
- * geometry can be specified by {@link #rounding rounding}, {@link #subgrid subgrid}, {@link #slice slice}
- * or {@link #reduce reduce} methods, and the grid geometry is created by {@link #build()}.
- *
- * @author Martin Desruisseaux (Geomatys)
- * @version 1.0
- *
- * @see GridGeometry#derive()
- *
- * @since 1.0
- * @module
- */
- public class Modifier {
- /**
- * Builder of grid geometry based on a slice of enclosing grid geometry, or {@code null} if none.
- */
- private GridDerivation subgrid;
-
- /**
- * The grid dimension to keep, or {@code null} if no filtering is applied.
- */
- private int[] dimensions;
-
- /**
- * Controls behavior of rounding from floating point values to integers.
- */
- private GridRoundingMode rounding;
-
- /**
- * Creates a new modifier based on the enclosing grid geometry.
- * This constructor is for subclasses only.
- *
- * @see GridGeometry#derive()
- */
- protected Modifier() {
- rounding = GridRoundingMode.NEAREST;
- }
-
- /**
- * Verifies that a sub-grid has not yet been defined.
- */
- private void ensureBeforeSubgrid() {
- if (subgrid != null) {
- throw new IllegalStateException();
- }
- }
-
- /**
- * Controls behavior of rounding from floating point values to integers.
- * This method can be invoked before any method expecting an {@link Envelope} argument.
- * If this method is never invoked, the default value is {@link GridRoundingMode#NEAREST}.
- * If this method is invoked too late, an {@link IllegalStateException} is thrown.
- *
- * @param mode the new rounding mode.
- * @return {@code this} for method call chaining.
- * @throws IllegalStateException if {@link #subgrid(Envelope, double...)} has already been invoked.
- */
- public Modifier rounding(final GridRoundingMode mode) {
- ArgumentChecks.ensureNonNull("mode", mode);
- ensureBeforeSubgrid();
- rounding = mode;
- return this;
- }
-
- /**
- * Returns a grid geometry over a sub-region of this grid geometry and optionally with subsampling.
- * The given envelope does not need to be expressed in the same coordinate reference system (CRS)
- * than {@linkplain #getCoordinateReferenceSystem() the CRS of this grid geometry};
- * coordinate conversions or transformations will be applied as needed.
- * That envelope CRS may have fewer dimensions than this grid geometry CRS,
- * in which case grid dimensions not mapped to envelope dimensions will be returned unchanged.
- * The target resolution, if provided, shall be in same units and same order than the given envelope axes.
- * If the length of {@code targetResolution} array is less than the number of dimensions of {@code areaOfInterest},
- * then no subsampling will be applied on the missing dimensions.
- *
- * <p>This method does not reduce the number of dimensions of this grid geometry.
- * For dimensionality reduction, see {@link #reduce(int...)}.</p>
- *
- * @param areaOfInterest the desired spatiotemporal region in any CRS (transformations will be applied as needed),
- * or {@code null} for not restricting the sub-grid to a sub-area.
- * @param targetResolution the desired resolution in the same units and order than the axes of the given envelope,
- * or {@code null} or an empty array if no subsampling is desired.
- * @return {@code this} for method call chaining.
- * @throws IncompleteGridGeometryException if this grid geometry has no extent or no "grid to CRS" transform.
- * @throws IllegalGridGeometryException if an error occurred while converting the envelope coordinates to grid coordinates.
- *
- * @see GridExtent#subsample(int[])
- */
- public Modifier subgrid(final Envelope areaOfInterest, double... targetResolution) {
- ensureBeforeSubgrid();
- requireGridToCRS();
- try {
- subgrid = new GridDerivation(GridGeometry.this, cornerToCRS, areaOfInterest, targetResolution, rounding);
- } catch (FactoryException | TransformException e) {
- throw new IllegalGridGeometryException(e, "areaOfInterest");
- }
- return this;
- }
-
- /**
- * Requests a grid geometry for a slice at the given point.
- * The given position can be expressed in any coordinate reference system (CRS).
- * The position should not define a coordinate for all dimensions, otherwise the slice would degenerate
- * to a single point. Dimensions can be left unspecified either by assigning to {@code slicePoint} a CRS
- * without those dimensions, or by assigning the NaN value to some coordinates.
- *
- * <div class="note"><b>Example:</b>
- * if the {@linkplain #getCoordinateReferenceSystem() coordinate reference system} of current grid geometry has
- * (<var>longitude</var>, <var>latitude</var>, <var>time</var>) axes, then a (<var>longitude</var>, <var>latitude</var>)
- * slice at time <var>t</var> can be created with one of the following two positions:
- * <ul>
- * <li>A three-dimensional position with ({@link Double#NaN}, {@link Double#NaN}, <var>t</var>) coordinates.</li>
- * <li>A one-dimensional position with (<var>t</var>) coordinate and the coordinate reference system set to
- * {@linkplain org.apache.sis.referencing.CRS#getTemporalComponent(CoordinateReferenceSystem) the temporal component}
- * of the grid geometry CRS.</li>
- * </ul></div>
- *
- * This method does not reduce the number of dimensions of this grid geometry.
- * For dimensionality reduction, see {@link #reduce(int...)}.
- *
- * @param slicePoint the coordinates where to get a slice.
- * @return {@code this} for method call chaining.
- * @throws IncompleteGridGeometryException if this grid geometry has no extent or no "grid to CRS" transform.
- * @throws IllegalGridGeometryException if an error occurred while converting the point coordinates to grid coordinates.
- * @throws PointOutsideCoverageException if the given point is outside the grid extent.
- */
- public Modifier slice(final DirectPosition slicePoint) {
- ArgumentChecks.ensureNonNull("slicePoint", slicePoint);
- ensureBeforeSubgrid();
- requireGridToCRS();
- try {
- subgrid = new GridDerivation(GridGeometry.this, cornerToCRS, slicePoint);
- } catch (TransformException e) {
- throw new IllegalGridGeometryException(e, "slicePoint");
- }
- return this;
- }
-
- /**
- * Requests a grid geometry that encompass only some dimensions of the grid extent.
- * The specified dimensions will be copied into a new grid geometry.
- * The selection is applied on {@linkplain #getExtent() grid extent} dimensions;
- * they are not necessarily the same than the {@linkplain #getEnvelope() envelope} dimensions.
- * The given dimensions must be in strictly ascending order without duplicated values.
- * The number of dimensions of the sub grid geometry will be {@code dimensions.length}.
- *
- * <p>This method performs a <cite>dimensionality reduction</cite>.
- * This method can not be used for changing dimension order.</p>
- *
- * @param dimensions the grid (not CRS) dimensions to select, in strictly increasing order.
- * @return {@code this} for method call chaining.
- * @throws IndexOutOfBoundsException if an index is out of bounds.
- *
- * @see GridExtent#getSubspaceDimensions(int)
- * @see GridExtent#reduce(int...)
- * @see org.apache.sis.referencing.CRS#reduce(CoordinateReferenceSystem, int...)
- */
- public Modifier reduce(int... dimensions) {
- if (GridExtent.verifyDimensions(dimensions, getDimension())) {
- this.dimensions = null;
- } else {
- this.dimensions = dimensions.clone();
- }
- return this;
- }
-
- /**
- * Returns the extent of the modified grid geometry. This method is more efficient than
- * {@link #build()} if only the grid extent is desired instead than the full grid geometry.
- *
- * @return the modified grid geometry extent.
- */
- public GridExtent extent() {
- GridExtent e = (subgrid != null) ? subgrid.extent : getExtent();
- if (dimensions != null) {
- e = e.reduce(dimensions);
- }
- return e;
- }
-
- /**
- * Builds a grid geometry with the configuration specified by the other methods in this {@code Modifier} class.
- *
- * @return the modified grid geometry. May be the enclosing geometry if no change apply.
- */
- public GridGeometry build() {
- GridGeometry gg = GridGeometry.this;
- String cause = null;
- try {
- cause = "subgrid";
- if (subgrid != null) {
- final GridExtent slice = subgrid.extent;
- final MathTransform toSubsampled = subgrid.toSubsampled;
- if (toSubsampled != null || slice != extent) {
- gg = new GridGeometry(gg, slice, toSubsampled);
- }
- }
- cause = "dimensions";
- if (dimensions != null) {
- gg = new GridGeometry(gg, dimensions);
- }
- } catch (FactoryException | TransformException e) {
- throw new IllegalGridGeometryException(e, cause);
- }
- return gg;
- }
+ public GridDerivation derive() {
+ return new GridDerivation(this);
}
/**
diff --git a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.java b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.java
index ffc6eb3..36da41b 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.java
@@ -69,6 +69,11 @@ public final class Resources extends IndexedResourceBundle {
public static final short CanNotMapToGridDimensions = 12;
/**
+ * Can not set this derived grid property after a call to “{0}” method.
+ */
+ public static final short CanNotSetDerivedGridProperty_1 = 32;
+
+ /**
* Can not simplify transfer function of sample dimension “{0}”.
*/
public static final short CanNotSimplifyTransferFunction_1 = 19;
diff --git a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.properties b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.properties
index 157b261..b2e3fc3 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.properties
+++ b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.properties
@@ -21,6 +21,7 @@
#
CanNotEnumerateValuesInRange_1 = Can not enumerate values in the {0} range.
CanNotMapToGridDimensions = Some envelope dimensions can not be mapped to grid dimensions.
+CanNotSetDerivedGridProperty_1 = Can not set this derived grid property after a call to \u201c{0}\u201d method.
CanNotSimplifyTransferFunction_1 = Can not simplify transfer function of sample dimension \u201c{0}\u201d.
CategoryRangeOverlap_4 = The two categories \u201c{0}\u201d and \u201c{2}\u201d have overlapping ranges: {1} and {3} respectively.
GridCoordinateOutsideCoverage_4 = Indices ({3}) are outside grid coverage. The value at dimension {0} shall be between {1,number} and {2,number} inclusive.
diff --git a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources_fr.properties b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources_fr.properties
index ba0f80c..1fcfe9b 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources_fr.properties
+++ b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources_fr.properties
@@ -26,6 +26,7 @@
#
CanNotEnumerateValuesInRange_1 = Ne peut pas \u00e9num\u00e9rer les valeurs dans la plage {0}.
CanNotMapToGridDimensions = Certaines dimensions de l\u2019enveloppe ne correspondent pas \u00e0 des dimensions de la grille.
+CanNotSetDerivedGridProperty_1 = Ne peut pas d\u00e9finir cette propri\u00e9t\u00e9 de la grille d\u00e9riv\u00e9e apr\u00e8s un appel \u00e0 la m\u00e9thode \u00ab\u202f{0}\u202f\u00bb.
CanNotSimplifyTransferFunction_1 = Ne peut pas simplifier la fonction de transfert de la dimension d\u2019\u00e9chantillonnage \u00ab\u202f{0}\u202f\u00bb.
CategoryRangeOverlap_4 = Les deux cat\u00e9gories \u00ab\u202f{0}\u202f\u00bb et \u00ab\u202f{2}\u202f\u00bb ont des plages de valeurs qui se chevauchent\u2008: {1} et {3} respectivement.
GridCoordinateOutsideCoverage_4 = Les indices ({3}) sont en dehors du domaine de la grille. La valeur \u00e0 la dimension {0} doit \u00eatre entre {1,number} et {2,number} inclusivement.
diff --git a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridDerivationTest.java
similarity index 51%
copy from core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
copy to core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridDerivationTest.java
index 7c51585..109d334 100644
--- a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
+++ b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridDerivationTest.java
@@ -23,7 +23,6 @@ import org.opengis.referencing.operation.TransformException;
import org.apache.sis.referencing.operation.matrix.Matrix2;
import org.apache.sis.referencing.operation.matrix.Matrix3;
import org.apache.sis.referencing.operation.matrix.Matrix4;
-import org.apache.sis.referencing.operation.matrix.Matrices;
import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.referencing.crs.HardCodedCRS;
import org.apache.sis.geometry.DirectPosition2D;
@@ -35,214 +34,23 @@ import org.apache.sis.test.TestCase;
import org.junit.Test;
import static org.apache.sis.test.ReferencingAssert.*;
+import static org.apache.sis.coverage.grid.GridGeometryTest.assertExtentEquals;
/**
- * Tests the {@link GridGeometry} implementation.
+ * Tests the {@link GridDerivation} implementation.
*
- * @author Martin Desruisseaux (IRD, Geomatys)
+ * @author Martin Desruisseaux (Geomatys)
* @version 1.0
* @since 1.0
* @module
*/
-@DependsOn(GridExtentTest.class)
-public final strictfp class GridGeometryTest extends TestCase {
+@DependsOn(GridGeometryTest.class)
+public final strictfp class GridDerivationTest extends TestCase {
/**
- * Verifies grid extent coordinates.
- */
- private static void assertExtentEquals(final long[] low, final long[] high, final GridExtent extent) {
- assertArrayEquals("extent.low", low, extent.getLow() .getCoordinateValues());
- assertArrayEquals("extent.high", high, extent.getHigh().getCoordinateValues());
- }
-
- /**
- * Tests construction with an identity transform mapping pixel corner.
- */
- @Test
- public void testFromPixelCorner() {
- final long[] low = new long[] {100, 300, 3, 6};
- final long[] high = new long[] {200, 400, 4, 7};
- final GridExtent extent = new GridExtent(null, low, high, true);
- final MathTransform identity = MathTransforms.identity(4);
- final GridGeometry grid = new GridGeometry(extent, PixelInCell.CELL_CORNER, identity, null);
- /*
- * Verify properties that should be stored "as-is".
- */
- final MathTransform trCorner = grid.getGridToCRS(PixelInCell.CELL_CORNER);
- assertSame("gridToCRS", identity, trCorner);
- assertExtentEquals(low, high, grid.getExtent());
- /*
- * Verify computed math transform.
- */
- final MathTransform trCenter = grid.getGridToCRS(PixelInCell.CELL_CENTER);
- assertNotSame(trCenter, trCorner);
- assertFalse ("gridToCRS.isIdentity", trCenter.isIdentity());
- assertEquals("gridToCRS.sourceDimensions", 4, trCenter.getSourceDimensions());
- assertEquals("gridToCRS.targetDimensions", 4, trCenter.getTargetDimensions());
- assertMatrixEquals("gridToCRS", Matrices.create(5, 5, new double[] {
- 1, 0, 0, 0, 0.5,
- 0, 1, 0, 0, 0.5,
- 0, 0, 1, 0, 0.5,
- 0, 0, 0, 1, 0.5,
- 0, 0, 0, 0, 1}), MathTransforms.getMatrix(trCenter), STRICT);
- /*
- * Verify the envelope, which should have been computed using the given math transform as-is.
- */
- assertEnvelopeEquals(new GeneralEnvelope(
- new double[] {100, 300, 3, 6},
- new double[] {201, 401, 5, 8}), grid.getEnvelope(), STRICT);
- /*
- * Verify other computed properties.
- */
- assertArrayEquals("resolution", new double[] {1, 1, 1, 1}, grid.getResolution(false), STRICT);
- assertTrue("isConversionLinear", grid.isConversionLinear(0, 1, 2, 3));
- }
-
- /**
- * Tests construction with an identity transform mapping pixel center.
- * This results a 0.5 pixel shifts in the "real world" envelope.
- */
- @Test
- public void testFromPixelCenter() {
- final long[] low = new long[] { 0, 0, 2};
- final long[] high = new long[] {99, 199, 4};
- final GridExtent extent = new GridExtent(null, low, high, true);
- final MathTransform identity = MathTransforms.identity(3);
- final GridGeometry grid = new GridGeometry(extent, PixelInCell.CELL_CENTER, identity, null);
- /*
- * Verify properties that should be stored "as-is".
- */
- final MathTransform trCenter = grid.getGridToCRS(PixelInCell.CELL_CENTER);
- assertSame("gridToCRS", identity, trCenter);
- assertExtentEquals(low, high, grid.getExtent());
- /*
- * Verify computed math transform.
- */
- final MathTransform trCorner = grid.getGridToCRS(PixelInCell.CELL_CORNER);
- assertNotSame(trCenter, trCorner);
- assertFalse ("gridToCRS.isIdentity", trCorner.isIdentity());
- assertEquals("gridToCRS.sourceDimensions", 3, trCorner.getSourceDimensions());
- assertEquals("gridToCRS.targetDimensions", 3, trCorner.getTargetDimensions());
- assertMatrixEquals("gridToCRS", new Matrix4(
- 1, 0, 0, -0.5,
- 0, 1, 0, -0.5,
- 0, 0, 1, -0.5,
- 0, 0, 0, 1), MathTransforms.getMatrix(trCorner), STRICT);
- /*
- * Verify the envelope, which should have been computed using the math transform shifted by 0.5.
- */
- assertEnvelopeEquals(new GeneralEnvelope(
- new double[] {-0.5, -0.5, 1.5},
- new double[] {99.5, 199.5, 4.5}), grid.getEnvelope(), STRICT);
- /*
- * Verify other computed properties.
- */
- assertArrayEquals("resolution", new double[] {1, 1, 1}, grid.getResolution(false), STRICT);
- assertTrue("isConversionLinear", grid.isConversionLinear(0, 1, 2));
- }
-
- /**
- * Tests the {@link GridGeometry#GridGeometry(GridGeometry, GridExtent, MathTransform)} constructor.
- *
- * @throws TransformException if an error occurred while using the "grid to CRS" transform.
- */
- @Test
- public void testFromOther() throws TransformException {
- long[] low = new long[] { 1, 3, 2};
- long[] high = new long[] {101, 203, 4};
- GridExtent extent = new GridExtent(null, low, high, false);
- MathTransform gridToCRS = MathTransforms.translation(5, 7, 8);
- GridGeometry grid = new GridGeometry(extent, PixelInCell.CELL_CENTER, gridToCRS, null);
-
- low = new long[] { 11, 35, 20};
- high = new long[] {120, 250, 39};
- extent = new GridExtent(null, low, high, false);
- grid = new GridGeometry(grid, extent, MathTransforms.scale(2, 1, 3));
- assertSame(extent, grid.getExtent());
- assertMatrixEquals("gridToCRS", new Matrix4(
- 2, 0, 0, 5,
- 0, 1, 0, 7, // Combination of above scales (diagonal) and translation (last column).
- 0, 0, 3, 8,
- 0, 0, 0, 1), MathTransforms.getMatrix(grid.getGridToCRS(PixelInCell.CELL_CENTER)), STRICT);
- }
-
- /**
- * Tests construction from a <cite>grid to CRS</cite> having a 0.5 pixel translation.
- * This translation happens in transform mapping <cite>pixel center</cite> when the
- * corresponding <cite>pixel corner</cite> transformation is identity.
- */
- @Test
- public void testShifted() {
- final long[] low = new long[] {100, 300};
- final long[] high = new long[] {200, 400};
- final GridExtent extent = new GridExtent(null, low, high, true);
- final MathTransform identity = MathTransforms.linear(new Matrix3(
- 1, 0, 0.5,
- 0, 1, 0.5,
- 0, 0, 1));
- final GridGeometry grid = new GridGeometry(extent, PixelInCell.CELL_CENTER, identity, null);
- assertTrue("gridToCRS.isIdentity", grid.getGridToCRS(PixelInCell.CELL_CORNER).isIdentity());
- }
-
- /**
- * Tests construction with a non-linear component in the transform.
- */
- @Test
- public void testNonLinear() {
- final GridExtent extent = new GridExtent(
- new DimensionNameType[] {
- DimensionNameType.COLUMN,
- DimensionNameType.ROW,
- DimensionNameType.VERTICAL,
- DimensionNameType.TIME
- },
- new long[] { 0, 0, 2, 6},
- new long[] {100, 200, 3, 9}, false);
- final MathTransform horizontal = MathTransforms.linear(new Matrix3(
- 0.5, 0, 12,
- 0, 0.25, -2,
- 0, 0, 1));
- final MathTransform vertical = MathTransforms.interpolate(null, new double[] {1, 2, 4, 10});
- final MathTransform temporal = MathTransforms.linear(3600, 60);
- final MathTransform gridToCRS = MathTransforms.compound(horizontal, vertical, temporal);
- final GridGeometry grid = new GridGeometry(extent, PixelInCell.CELL_CENTER, gridToCRS, null);
- assertArrayEquals("resolution", new double[] {0.5, 0.25, 6.0, 3600}, grid.getResolution(true), STRICT);
- assertArrayEquals("resolution", new double[] {0.5, 0.25, Double.NaN, 3600}, grid.getResolution(false), STRICT);
- assertFalse("isConversionLinear", grid.isConversionLinear(0, 1, 2, 3));
- assertTrue ("isConversionLinear", grid.isConversionLinear(0, 1, 3));
- }
-
- /**
- * Tests the construction from a geospatial envelope.
- */
- @Test
- public void testFromGeospatialEnvelope() {
- final GeneralEnvelope envelope = new GeneralEnvelope(HardCodedCRS.WGS84_φλ);
- envelope.setRange(0, -70.001, +80.002);
- envelope.setRange(1, 4.997, 15.003);
- final MathTransform gridToCRS = MathTransforms.linear(new Matrix3(
- 0, 0.5, -90,
- 0.5, 0, -180,
- 0, 0, 1));
- final GridGeometry grid = new GridGeometry(PixelInCell.CELL_CORNER, gridToCRS, envelope, GridRoundingMode.NEAREST);
- assertExtentEquals(
- new long[] {370, 40},
- new long[] {389, 339}, grid.getExtent());
- assertEnvelopeEquals(new GeneralEnvelope(
- new double[] {-70, 5},
- new double[] {+80, 15}), grid.getEnvelope(), STRICT);
- assertArrayEquals("resolution", new double[] {0.5, 0.5}, grid.getResolution(false), STRICT);
- assertMatrixEquals("gridToCRS", new Matrix3(
- 0, 0.5, -89.75,
- 0.5, 0, -179.75,
- 0, 0, 1), MathTransforms.getMatrix(grid.getGridToCRS(PixelInCell.CELL_CENTER)), STRICT);
- }
-
- /**
- * Tests {@link GridGeometry.Modifier#subgrid(Envelope, double...)}.
+ * Tests {@link GridDerivation#subgrid(Envelope, double...)}.
*/
@Test
- @DependsOnMethod("testFromGeospatialEnvelope")
public void testSubExtent() {
GeneralEnvelope envelope = new GeneralEnvelope(HardCodedCRS.WGS84_3D);
envelope.setRange(0, -80, 120);
@@ -269,10 +77,10 @@ public final strictfp class GridGeometryTest extends TestCase {
}
/**
- * Tests {@link GridGeometry.Modifier#subgrid(Envelope, double...)} with a non-linear "grid to CRS" transform.
+ * Tests {@link GridDerivation#subgrid(Envelope, double...)} with a non-linear "grid to CRS" transform.
*/
@Test
- @DependsOnMethod({"testNonLinear", "testSubExtent"})
+ @DependsOnMethod("testSubExtent")
public void testSubExtentNonLinear() {
final GridExtent extent = new GridExtent(
new DimensionNameType[] {
@@ -306,12 +114,12 @@ public final strictfp class GridGeometryTest extends TestCase {
}
/**
- * Tests {@link GridGeometry.Modifier#subgrid(Envelope, double...)}.
+ * Tests {@link GridDerivation#subgrid(Envelope, double...)}.
*
* @throws TransformException if an error occurred during computation.
*/
@Test
- @DependsOnMethod({"testFromGeospatialEnvelope", "testSubExtent"})
+ @DependsOnMethod("testSubExtent")
public void testSubgrid() throws TransformException {
final GeneralEnvelope envelope = new GeneralEnvelope(HardCodedCRS.WGS84_φλ);
envelope.setRange(0, -70, +80);
@@ -358,7 +166,7 @@ public final strictfp class GridGeometryTest extends TestCase {
}
/**
- * Tests {@link GridGeometry.Modifier#slice(DirectPosition)}.
+ * Tests {@link GridDerivation#slice(DirectPosition)}.
*/
@Test
public void testSlice() {
@@ -391,7 +199,7 @@ public final strictfp class GridGeometryTest extends TestCase {
}
/**
- * Tests {@link GridGeometry.Modifier#reduce(int...)}.
+ * Tests {@link GridDerivation#reduce(int...)}.
*/
@Test
public void testReduce() {
diff --git a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
index 7c51585..7907a22 100644
--- a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
+++ b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
@@ -20,16 +20,12 @@ import org.opengis.metadata.spatial.DimensionNameType;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
-import org.apache.sis.referencing.operation.matrix.Matrix2;
import org.apache.sis.referencing.operation.matrix.Matrix3;
import org.apache.sis.referencing.operation.matrix.Matrix4;
import org.apache.sis.referencing.operation.matrix.Matrices;
import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.referencing.crs.HardCodedCRS;
-import org.apache.sis.geometry.DirectPosition2D;
-import org.apache.sis.geometry.GeneralDirectPosition;
import org.apache.sis.geometry.GeneralEnvelope;
-import org.apache.sis.test.DependsOnMethod;
import org.apache.sis.test.DependsOn;
import org.apache.sis.test.TestCase;
import org.junit.Test;
@@ -50,7 +46,7 @@ public final strictfp class GridGeometryTest extends TestCase {
/**
* Verifies grid extent coordinates.
*/
- private static void assertExtentEquals(final long[] low, final long[] high, final GridExtent extent) {
+ static void assertExtentEquals(final long[] low, final long[] high, final GridExtent extent) {
assertArrayEquals("extent.low", low, extent.getLow() .getCoordinateValues());
assertArrayEquals("extent.high", high, extent.getHigh().getCoordinateValues());
}
@@ -237,193 +233,4 @@ public final strictfp class GridGeometryTest extends TestCase {
0.5, 0, -179.75,
0, 0, 1), MathTransforms.getMatrix(grid.getGridToCRS(PixelInCell.CELL_CENTER)), STRICT);
}
-
- /**
- * Tests {@link GridGeometry.Modifier#subgrid(Envelope, double...)}.
- */
- @Test
- @DependsOnMethod("testFromGeospatialEnvelope")
- public void testSubExtent() {
- GeneralEnvelope envelope = new GeneralEnvelope(HardCodedCRS.WGS84_3D);
- envelope.setRange(0, -80, 120);
- envelope.setRange(1, -12, 21);
- envelope.setRange(2, 10, 25);
- final MathTransform gridToCRS = MathTransforms.linear(new Matrix4(
- 0, 0.5, 0, -90,
- 0.5, 0, 0, -180,
- 0, 0, 2, 3,
- 0, 0, 0, 1));
- final GridGeometry grid = new GridGeometry(PixelInCell.CELL_CORNER, gridToCRS, envelope, GridRoundingMode.NEAREST);
- assertExtentEquals(
- new long[] {336, 20, 4},
- new long[] {401, 419, 10}, grid.getExtent());
- /*
- * Set the region of interest as a two-dimensional envelope. The vertical dimension is omitted.
- * The result should be that all grid indices in the vertical dimension are kept unchanged.
- */
- envelope = new GeneralEnvelope(HardCodedCRS.WGS84);
- envelope.setRange(0, -70.001, +80.002);
- envelope.setRange(1, 4.997, 15.003);
- assertExtentEquals(new long[] {370, 40, 4},
- new long[] {389, 339, 10}, grid.derive().subgrid(envelope).extent());
- }
-
- /**
- * Tests {@link GridGeometry.Modifier#subgrid(Envelope, double...)} with a non-linear "grid to CRS" transform.
- */
- @Test
- @DependsOnMethod({"testNonLinear", "testSubExtent"})
- public void testSubExtentNonLinear() {
- final GridExtent extent = new GridExtent(
- new DimensionNameType[] {
- DimensionNameType.COLUMN,
- DimensionNameType.ROW,
- DimensionNameType.VERTICAL
- },
- new long[] { 0, 0, 2},
- new long[] {180, 90, 5}, false);
- final MathTransform linear = MathTransforms.linear(new Matrix4(
- 2, 0, 0, -180,
- 0, 2, 0, -90,
- 0, 0, 5, 10,
- 0, 0, 0, 1));
- final MathTransform latitude = MathTransforms.interpolate(new double[] {0, 20, 50, 70, 90}, new double[] {-90, -45, 0, 45, 90});
- final MathTransform gridToCRS = MathTransforms.concatenate(linear, MathTransforms.passThrough(1, latitude, 1));
- final GridGeometry grid = new GridGeometry(extent, PixelInCell.CELL_CENTER, gridToCRS, HardCodedCRS.WGS84_3D);
- /*
- * Following tests is similar to the one executed in testSubExtent(). Expected values are only
- * anti-regression values, except the vertical range which is expected to cover all cells. The
- * main purpose of this test is to verify that TransformSeparator has been able to extract the
- * two-dimensional transform despite its non-linear component.
- */
- final GeneralEnvelope envelope = new GeneralEnvelope(HardCodedCRS.WGS84);
- envelope.setRange(0, -70.001, +80.002);
- envelope.setRange(1, -4.997, 15.003);
- final GridExtent actual = grid.derive().subgrid(envelope).extent();
- assertEquals(extent.getAxisType(0), actual.getAxisType(0));
- assertExtentEquals(new long[] { 56, 69, 2},
- new long[] {130, 73, 4}, actual);
- }
-
- /**
- * Tests {@link GridGeometry.Modifier#subgrid(Envelope, double...)}.
- *
- * @throws TransformException if an error occurred during computation.
- */
- @Test
- @DependsOnMethod({"testFromGeospatialEnvelope", "testSubExtent"})
- public void testSubgrid() throws TransformException {
- final GeneralEnvelope envelope = new GeneralEnvelope(HardCodedCRS.WGS84_φλ);
- envelope.setRange(0, -70, +80);
- envelope.setRange(1, 5, 15);
- final MathTransform gridToCRS = MathTransforms.linear(new Matrix3(
- 0, 0.5, -90,
- 0.5, 0, -180,
- 0, 0, 1));
- GridGeometry grid = new GridGeometry(PixelInCell.CELL_CORNER, gridToCRS, envelope, GridRoundingMode.NEAREST);
- assertExtentEquals(new long[] {370, 40}, new long[] {389, 339}, grid.getExtent());
- assertEnvelopeEquals(envelope, grid.getEnvelope(), STRICT);
- /*
- * Set a sub-region. The grid extent and "grid to CRS" transform shall be adjusted
- * in such a way that envelope computed from the new grid geometry is the same.
- */
- envelope.setRange(0, -50, +30);
- envelope.setRange(1, 8, 12);
- grid = grid.derive().subgrid(envelope, 1, 2).build();
- assertExtentEquals(new long[] {94, 40}, new long[] {95, 119}, grid.getExtent());
- assertEnvelopeEquals(envelope, grid.getEnvelope(), STRICT);
- assertMatrixEquals("gridToCRS", new Matrix3(
- 0, 1, -90,
- 2, 0, -180,
- 0, 0, 1), MathTransforms.getMatrix(grid.getGridToCRS(PixelInCell.CELL_CORNER)), STRICT);
- /*
- * A sub-region again but with a requested resolution which is not a divisor of the actual resolution.
- * It will force GridGeometry to adjust the translation term to compensate. We verify that the adustment
- * is correct by verifying that we still get the same envelope.
- */
- grid = grid.derive().subgrid(envelope, 3, 2).build();
- assertExtentEquals(new long[] {94, 13}, new long[] {95, 39}, grid.getExtent());
- assertEnvelopeEquals(envelope, grid.getEnvelope(), STRICT);
- MathTransform cornerToCRS = grid.getGridToCRS(PixelInCell.CELL_CORNER);
- assertMatrixEquals("gridToCRS", new Matrix3(
- 0, 3, -89,
- 2, 0, -180,
- 0, 0, 1), MathTransforms.getMatrix(cornerToCRS), STRICT);
-
- DirectPosition2D src = new DirectPosition2D();
- DirectPosition2D tgt = new DirectPosition2D();
- DirectPosition2D exp = new DirectPosition2D();
- src.x = 94; src.y = 13; exp.x = -50; exp.y = 8; assertEquals("Lower corner", exp, cornerToCRS.transform(src, tgt));
- src.x = 96; src.y = 40; exp.x = +31; exp.y = 12; assertEquals("Upper corner", exp, cornerToCRS.transform(src, tgt));
- }
-
- /**
- * Tests {@link GridGeometry.Modifier#slice(DirectPosition)}.
- */
- @Test
- public void testSlice() {
- final GridGeometry grid = new GridGeometry(
- new GridExtent(null, new long[] {336, 20, 4}, new long[] {401, 419, 10}, true),
- PixelInCell.CELL_CORNER, MathTransforms.linear(new Matrix4(
- 0, 0.5, 0, -90,
- 0.5, 0, 0, -180,
- 0, 0, 2, 3,
- 0, 0, 0, 1)), HardCodedCRS.WGS84_3D);
- /*
- * There is two ways to ask for a slice. The first way is to set some coordinates to NaN.
- */
- GridGeometry slice = grid.derive().slice(new GeneralDirectPosition(Double.NaN, Double.NaN, 15)).build();
- assertNotSame(grid, slice);
- assertSame("gridToCRS", grid.gridToCRS, slice.gridToCRS);
- final long[] expectedLow = {336, 20, 6};
- final long[] expectedHigh = {401, 419, 6};
- assertExtentEquals(expectedLow, expectedHigh, slice.getExtent());
- /*
- * Same test, but using a one-dimensional slice point instead than NaN values.
- * Opportunistically use different units for testing conversions.
- */
- GeneralDirectPosition p = new GeneralDirectPosition(HardCodedCRS.ELLIPSOIDAL_HEIGHT_cm);
- p.setOrdinate(0, 1500);
- slice = grid.derive().slice(p).build();
- assertNotSame(grid, slice);
- assertSame("gridToCRS", grid.gridToCRS, slice.gridToCRS);
- assertExtentEquals(expectedLow, expectedHigh, slice.getExtent());
- }
-
- /**
- * Tests {@link GridGeometry.Modifier#reduce(int...)}.
- */
- @Test
- public void testReduce() {
- final GridGeometry grid = new GridGeometry(
- new GridExtent(null, new long[] {336, 20, 4}, new long[] {401, 419, 10}, true),
- PixelInCell.CELL_CORNER, MathTransforms.linear(new Matrix4(
- 0, 0.5, 0, -90,
- 0.5, 0, 0, -180,
- 0, 0, 2, 3,
- 0, 0, 0, 1)), HardCodedCRS.GEOID_3D);
- /*
- * Tests on the two first dimensions.
- */
- GridGeometry reduced = grid.derive().reduce(0, 1).build();
- assertNotSame(grid, reduced);
- assertExtentEquals(new long[] {336, 20}, new long[] {401, 419}, reduced.getExtent());
- assertSame("CRS", HardCodedCRS.WGS84, reduced.getCoordinateReferenceSystem());
- assertArrayEquals("resolution", new double[] {0.5, 0.5}, reduced.getResolution(false), STRICT);
- assertMatrixEquals("gridToCRS", new Matrix3(
- 0, 0.5, -90,
- 0.5, 0, -180,
- 0, 0, 1), MathTransforms.getMatrix(reduced.getGridToCRS(PixelInCell.CELL_CORNER)), STRICT);
- /*
- * Tests on the last dimension.
- */
- reduced = grid.derive().reduce(2).build();
- assertNotSame(grid, reduced);
- assertExtentEquals(new long[] {4}, new long[] {10}, reduced.getExtent());
- assertSame("CRS", HardCodedCRS.GRAVITY_RELATED_HEIGHT, reduced.getCoordinateReferenceSystem());
- assertArrayEquals("resolution", new double[] {2}, reduced.getResolution(false), STRICT);
- assertMatrixEquals("gridToCRS", new Matrix2(
- 2, 3,
- 0, 1), MathTransforms.getMatrix(reduced.getGridToCRS(PixelInCell.CELL_CORNER)), STRICT);
- }
}
diff --git a/core/sis-raster/src/test/java/org/apache/sis/test/suite/RasterTestSuite.java b/core/sis-raster/src/test/java/org/apache/sis/test/suite/RasterTestSuite.java
index 1aac8e2..2a8b4ee 100644
--- a/core/sis-raster/src/test/java/org/apache/sis/test/suite/RasterTestSuite.java
+++ b/core/sis-raster/src/test/java/org/apache/sis/test/suite/RasterTestSuite.java
@@ -34,6 +34,7 @@ import org.junit.BeforeClass;
org.apache.sis.coverage.grid.PixelTranslationTest.class,
org.apache.sis.coverage.grid.GridExtentTest.class,
org.apache.sis.coverage.grid.GridGeometryTest.class,
+ org.apache.sis.coverage.grid.GridDerivationTest.class,
org.apache.sis.coverage.grid.GridChangeTest.class,
org.apache.sis.coverage.CategoryTest.class,
org.apache.sis.coverage.CategoryListTest.class,