This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new 575a9ed5be Add a `GridDerivation.project(Predicate)` method for
filtering the CRS axes of a grid geometry.
575a9ed5be is described below
commit 575a9ed5be3bd8a274d2f1e9b77c5e6e8f4da3ea
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Fri Jan 9 18:59:56 2026 +0100
Add a `GridDerivation.project(Predicate)` method for filtering the CRS axes
of a grid geometry.
---
.../apache/sis/coverage/grid/DimensionReducer.java | 105 +++++++++++++++++---
.../sis/coverage/grid/DimensionalityReduction.java | 2 +-
.../apache/sis/coverage/grid/GridDerivation.java | 106 +++++++++++++++------
.../org/apache/sis/coverage/grid/GridGeometry.java | 31 +++---
.../sis/coverage/grid/GridDerivationTest.java | 25 +++++
5 files changed, 213 insertions(+), 56 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/DimensionReducer.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/DimensionReducer.java
index 7f9bed10b1..daa2eebcc5 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/DimensionReducer.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/DimensionReducer.java
@@ -21,40 +21,52 @@ import org.opengis.util.FactoryException;
import org.opengis.geometry.Envelope;
import org.opengis.geometry.DirectPosition;
import org.opengis.referencing.cs.CoordinateSystem;
+import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.internal.shared.AxisDirections;
+import org.apache.sis.referencing.operation.transform.TransformSeparator;
import org.apache.sis.geometry.GeneralDirectPosition;
import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.geometry.ImmutableEnvelope;
/**
* An helper class for reducing the number of dimensions in a grid geometry,
an envelope or a position.
* This is used when the Area Of Interest has more dimensions than the grid
geometry, in which case the
- * transform from grid to AOI will fail if we do not discard the extra
dimensions.
+ * transform from grid to <abbr>AOI</abbr> will fail if we do not discard the
extra dimensions.
*
- * <p>This class works on the CRS dimensions.
+ * <p>This class works on the <abbr>CRS</abbr> dimensions.
* This is different than {@link DimensionalityReduction}, which works on grid
dimensions.</p>
*
* @author Martin Desruisseaux (Geomatys)
*/
final class DimensionReducer {
/**
- * The dimensions to keep, or {@code null} for keeping them all.
+ * The <abbr>CRS</abbr> dimensions to keep, or {@code null} for keeping
them all.
*/
private int[] dimensions;
/**
- * The CRS with only the {@linkplain #dimensions} to keep, or {@code null}
if no reduction.
+ * The <abbr>CRS</abbr> with only the {@linkplain #dimensions} to keep, or
{@code null} if no reduction.
*/
private CoordinateReferenceSystem reducedCRS;
/**
- * Creates a helper which will retain only the {@code targetCRS}
dimensions that are found in the base grid.
+ * Requests to retain only the axes in the specified <abbr>CRS</abbr>
dimensions.
+ *
+ * @param dimensions the <abbr>CRS</abbr> dimensions to keep, or {@code
null} for keeping them all.
+ */
+ DimensionReducer(final int... dimensions) {
+ this.dimensions = dimensions;
+ }
+
+ /**
+ * Requests to retain only the {@code targetCRS} dimensions that are found
in the base grid.
* This will be used by caller for creating a valid {@code sourceCRS} to
{@code targetCRS} transform.
*
* @param base the grid geometry which will be derived. Cannot be
null.
- * @param targetCRS CRS of the area or point of interest. Cannot be null.
+ * @param targetCRS <abbr>CRS</abbr> of the area or point of interest.
Cannot be null.
*/
DimensionReducer(final GridGeometry base, final CoordinateReferenceSystem
targetCRS) throws FactoryException {
if (base != null && base.envelope != null) {
@@ -74,14 +86,29 @@ final class DimensionReducer {
}
/**
- * Applies reduction on the given position.
+ * Applies dimension reduction on the given array of resolutions.
+ * If the resolution cannot be reduced, then it is returned as-is.
+ */
+ private double[] apply(final double[] target) {
+ if (target == null || dimensions == null) {
+ return target;
+ }
+ final var resolution = new double[dimensions.length];
+ for (int i=0; i < resolution.length; i++) {
+ resolution[i] = target[dimensions[i]];
+ }
+ return resolution;
+ }
+
+ /**
+ * Applies dimension reduction on the given position.
* If the position cannot be reduced, then it is returned as-is.
*/
final DirectPosition apply(final DirectPosition target) {
- if (dimensions == null) {
+ if (target == null || dimensions == null) {
return target;
}
- final GeneralDirectPosition position = new
GeneralDirectPosition(reducedCRS);
+ final var position = new GeneralDirectPosition(reducedCRS);
for (int i=0; i < dimensions.length; i++) {
position.coordinates[i] = target.getCoordinate(dimensions[i]);
}
@@ -89,20 +116,74 @@ final class DimensionReducer {
}
/**
- * Applies reduction on the given envelope.
+ * Applies dimension reduction on the given envelope.
* If the envelope cannot be reduced, then it is returned as-is.
*/
final Envelope apply(final Envelope target) {
- if (dimensions == null) {
+ if (target == null || dimensions == null) {
return target;
}
final DirectPosition lowerCorner = target.getLowerCorner();
final DirectPosition upperCorner = target.getUpperCorner();
- final GeneralEnvelope envelope = new GeneralEnvelope(reducedCRS);
+ final var envelope = new GeneralEnvelope(reducedCRS);
for (int i=0; i < dimensions.length; i++) {
final int s = dimensions[i];
envelope.setRange(i, lowerCorner.getCoordinate(s),
upperCorner.getCoordinate(s));
}
return envelope;
}
+
+ /**
+ * Applies dimension reduction on the given grid geometry.
+ * It may cause a reduction in the number of dimensions of the grid.
+ * If the grid cannot be reduced, then it is returned as-is.
+ */
+ final GridGeometry apply(final GridGeometry target) throws
FactoryException {
+ if (target == null || dimensions == null) {
+ return target;
+ }
+ /*
+ * Select the dimension to keep in the "grid to CRS" transform. We
repeat this operation for the
+ * "corner to CRS" transform rather than deriving the latter from the
former because we don't
+ * know in advance which one of the two transforms is more accurate
(has less NaN values).
+ * This separation determines the dimensions to keep in the grid.
+ */
+ int[] gridIndices = null;
+ MathTransform gridToCRS = target.gridToCRS;
+ if (gridToCRS != null) {
+ var ts = new TransformSeparator(gridToCRS);
+ ts.addTargetDimensions(dimensions);
+ gridToCRS = ts.separate();
+ gridIndices = ts.getSourceDimensions();
+ }
+ MathTransform cornerToCRS = target.cornerToCRS;
+ if (cornerToCRS != null) {
+ var ts = new TransformSeparator(cornerToCRS);
+ ts.addTargetDimensions(dimensions);
+ if (gridIndices != null) {
+ ts.addSourceDimensions(gridIndices);
+ cornerToCRS = ts.separate();
+ } else {
+ cornerToCRS = ts.separate();
+ gridIndices = ts.getSourceDimensions();
+ }
+ }
+ /*
+ * Reduces the number of dimensions of the extent, envelope and
resolution.
+ */
+ GridExtent extent = target.extent;
+ if (extent != null && gridIndices != null) {
+ extent = extent.selectDimensions(gridIndices);
+ }
+ reducedCRS =
CRS.selectDimensions(target.getCoordinateReferenceSystem(), dimensions);
+ final Envelope envelope = apply(target.envelope);
+ final double[] resolution = apply(target.resolution);
+ long nonLinears = 0;
+ for (int i=0; i < dimensions.length; i++) {
+ if ((target.nonLinears & (1L << dimensions[i])) != 0) {
+ nonLinears |= (1L << i);
+ }
+ }
+ return new GridGeometry(extent, gridToCRS, cornerToCRS,
ImmutableEnvelope.castOrCopy(envelope), resolution, nonLinears);
+ }
}
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/DimensionalityReduction.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/DimensionalityReduction.java
index f89da839af..7b2b4969af 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/DimensionalityReduction.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/DimensionalityReduction.java
@@ -681,7 +681,7 @@ public class DimensionalityReduction implements
UnaryOperator<GridCoverage>, Ser
/**
* Returns a grid geometry on which dimensionality reduction of the grid
extent has been applied.
- * It usually implies a reduction in the number of dimensions of the CRS
as well,
+ * It usually implies a reduction in the number of dimensions of the
<abbr>CRS</abbr> as well,
* but not necessarily in same order.
*
* <p>If the given source is {@code null}, then this method returns {@code
null}.
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
index 00e8185163..e4840736a9 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
@@ -17,9 +17,12 @@
package org.apache.sis.coverage.grid;
import java.util.Map;
+import java.util.Set;
+import java.util.BitSet;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
+import java.util.function.Predicate;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.math.RoundingMode;
@@ -30,6 +33,8 @@ import org.opengis.referencing.operation.Matrix;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.cs.CoordinateSystemAxis;
+import org.opengis.referencing.cs.CoordinateSystem;
import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.referencing.operation.transform.LinearTransform;
@@ -76,14 +81,14 @@ import org.opengis.coverage.PointOutsideCoverageException;
* The {@link #getIntersection()} method can also be invoked for the {@link
GridExtent} part without subsampling.
*
* <h2>Multi-dimensional grids</h2>
- * All methods in this class preserve the number of dimensions. For example,
the {@link #slice(DirectPosition)} method
- * sets the {@linkplain GridExtent#getSize(int) grid size} to 1 in all
dimensions specified by the <i>slice point</i>,
- * but does not remove those dimensions from the grid geometry.
+ * Unless specified otherwise in Javadoc, the methods in this class preserve
the number of dimensions.
+ * For example, the {@link #slice(DirectPosition)} method sets the {@linkplain
GridExtent#getSize(int) grid size} to 1
+ * in all dimensions specified by the <i>slice point</i>, but does not remove
those dimensions from the grid geometry.
* For dimensionality reduction, see {@link
GridGeometry#selectDimensions(int[])}.
*
* @author Martin Desruisseaux (Geomatys)
* @author Alexis Manin (Geomatys)
- * @version 1.5
+ * @version 1.6
*
* @see GridGeometry#derive()
* @see GridGeometry#selectDimensions(int[])
@@ -224,6 +229,14 @@ public class GridDerivation {
*/
private GeneralEnvelope intersection;
+ /**
+ * Indexes of <abbr>CRS</abbr> axes to keep, or {@code null} if no
filtering will be applied.
+ * A non-null value may cause a reduction in the number of dimensions of
the grid.
+ *
+ * @see #project(Set)
+ */
+ private BitSet dimensionsToKeepInCRS;
+
/**
* Creates a new builder for deriving a grid geometry from the specified
base.
*
@@ -1188,12 +1201,40 @@ public class GridDerivation {
return this;
}
- /*
- * RATIONAL FOR NOT PROVIDING reduce(int... dimensions) METHOD HERE: that
method would need to be the last method invoked,
- * otherwise it makes more complicated to implement other methods in this
class. Forcing users to invoke `build()` before
- * (s)he can invoke GridGeometry.reduce(…) makes that clear and avoid the
need for more flags in this GridDerivation class.
- * Furthermore, declaring the `reduce(…)` method in GridGeometry is more
consistent with `GridExtent.reduce(…)`.
+ /**
+ * Requests a projection where only a subset of the <abbr>CRS</abbr>
dimensions will be kept.
+ * The real world dimensions to keep are specified by a filter applied on
the coordinate system axes.
+ * This method may reduce the number of dimensions of the grid if, as a
result of this filtering,
+ * some grid dimensions become unrelated to any <abbr>CRS</abbr> axis.
+ *
+ * <h4>Example</h4>
+ * The following code removes the temporal dimension of a grid geometry:
+ *
+ * {@snippet lang="java" :
+ * gridGeometry.derive().project((axis) -> axis.getDirection() !=
AxisDirection.FUTURE).build();
+ * }
+ *
+ * @param filter a predicate which returns {@code true} for coordinate
system axes to keep.
+ * @return {@code this} for method call chaining.
+ * @throws IncompleteGridGeometryException if the base grid geometry has
no <abbr>CRS</abbr>.
+ *
+ * @since 1.6
*/
+ public GridDerivation project(final Predicate<CoordinateSystemAxis>
filter) {
+ final var dimensions = new BitSet();
+ final CoordinateSystem cs =
base.getCoordinateReferenceSystem().getCoordinateSystem();
+ for (int i = cs.getDimension(); --i >= 0;) {
+ if (filter.test(cs.getAxis(i))) {
+ dimensions.set(i);
+ }
+ }
+ if (dimensionsToKeepInCRS == null) {
+ dimensionsToKeepInCRS = dimensions;
+ } else {
+ dimensionsToKeepInCRS.and(dimensions);
+ }
+ return this;
+ }
/**
* Builds a grid geometry with the configuration specified by the other
methods in this {@code GridDerivation} class.
@@ -1229,31 +1270,37 @@ public class GridDerivation {
* need for envelope clipping performed by GridGeometry constructor.
*/
final GridExtent extent = (scaledExtent != null) ? scaledExtent :
getBaseExtentExpanded(false);
+ GridGeometry grid = base;
try {
- if (toBase != null || extent != base.extent) {
- return new GridGeometry(base, extent, toBase);
- }
- /*
- * Intersection should be non-null only if we have not been able
to compute more reliable properties
- * (grid extent and "grid to CRS" transform). It should happen
only if `gridToCRS` is null, but we
- * nevertheless pass that transform to the constructor as a matter
of principle.
- */
- if (intersection != null) {
- return new GridGeometry(PixelInCell.CELL_CENTER,
base.gridToCRS, intersection, rounding);
- }
- /*
- * Case when the only settings were a margin or a chunk size. It
is okay to test after `intersection`
- * because a non-null envelope intersection would have meant that
this `GridDerivation` does not have
- * required information for applying a margin anyway (no
`GridExtent` and no `gridToCRS`).
- */
- final GridExtent resized = getBaseExtentExpanded(false);
- if (resized != baseExtent) {
- return new GridGeometry(base, resized, null);
+ if (toBase != null || extent != grid.extent) {
+ grid = new GridGeometry(grid, extent, toBase);
+ } else if (intersection != null) {
+ /*
+ * Intersection should be non-null only if we have not been
able to compute more reliable properties
+ * (grid extent and "grid to CRS" transform). It should happen
only if `gridToCRS` is null, but we
+ * nevertheless pass that transform to the constructor as a
matter of principle.
+ */
+ grid = new GridGeometry(PixelInCell.CELL_CENTER,
grid.gridToCRS, intersection, rounding);
+ } else {
+ /*
+ * Case when the only settings were a margin or a chunk size.
It is okay to test after `intersection`
+ * because a non-null envelope intersection would have meant
that this `GridDerivation` does not have
+ * required information for applying a margin anyway (no
`GridExtent` and no `gridToCRS`).
+ */
+ final GridExtent resized = getBaseExtentExpanded(false);
+ if (resized != baseExtent) {
+ grid = new GridGeometry(grid, resized, null);
+ }
}
} catch (TransformException e) {
throw new IllegalGridGeometryException(e, "envelope");
}
- return base;
+ if (dimensionsToKeepInCRS != null) try {
+ grid = new
DimensionReducer(dimensionsToKeepInCRS.stream().toArray()).apply(grid);
+ } catch (FactoryException e) {
+ throw new IllegalGridGeometryException(e, "gridToCRS");
+ }
+ return grid;
}
/**
@@ -1315,6 +1362,7 @@ public class GridDerivation {
* the scale factors on the diagonal are the {@linkplain #getSubsampling()
subsampling values} and the
* translation terms in the last column are the {@linkplain
#getSubsamplingOffsets() subsampling offsets}.
*
+ * @param anchor whether to map the center or the corner of source and
target cells.
* @return transform of <em>grid</em> coordinates from the derived grid to
the {@linkplain #base} grid.
*
* @see #getSubsampling()
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
index 6aeef85771..3f8e687a46 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
@@ -227,6 +227,7 @@ public class GridGeometry implements LenientComparable,
Serializable {
* to real world coordinates, with the lower coordinates inclusive and the
upper coordinates exclusive.
* The Coordinate Reference System (CRS) of this envelope defines the
"real world" CRS of this grid geometry.
*
+ * @see #CRS
* @see #ENVELOPE
* @see #getEnvelope()
*/
@@ -237,7 +238,7 @@ public class GridGeometry implements LenientComparable,
Serializable {
* If non-null, the conversion shall map {@linkplain
PixelInCell#CELL_CENTER cell center}.
* This conversion is usually, but not necessarily, affine.
*
- * @see #CRS
+ * @see #GRID_TO_CRS
* @see #getGridToCRS(PixelInCell)
* @see PixelInCell#CELL_CENTER
*/
@@ -247,18 +248,21 @@ public class GridGeometry implements LenientComparable,
Serializable {
/**
* Same conversion as {@link #gridToCRS} but from {@linkplain
PixelInCell#CELL_CORNER cell corner}
* instead of center. This transform is preferable to {@code gridToCRS}
for transforming envelopes.
+ * This field may be more accurate than deriving this transform from
{@link #gridToCRS}.
*
- * @serial This field is serialized because it may be a value specified
explicitly at construction time,
- * in which case it can be more accurate than a computed value.
+ * @see #GRID_TO_CRS
+ * @see #getGridToCRS(PixelInCell)
+ * @see PixelInCell#CELL_CORNER
+ * @since 1.6
*/
@SuppressWarnings("serial") // Most SIS implementations are
serializable.
- final MathTransform cornerToCRS;
+ protected final MathTransform cornerToCRS;
/**
* An <em>estimation</em> of the grid resolution, in units of the CRS axes.
* Computed from {@link #gridToCRS}, eventually together with {@link
#extent}.
* May be {@code null} if unknown. If non-null, the array length is equal
to
- * the number of CRS dimensions.
+ * the number of <abbr>CRS</abbr> dimensions.
*
* @see #RESOLUTION
* @see #getResolution(boolean)
@@ -266,7 +270,7 @@ public class GridGeometry implements LenientComparable,
Serializable {
protected final double[] resolution;
/**
- * Whether the conversions from grid coordinates to the CRS are linear,
for each target axis.
+ * Whether the conversions from grid coordinates to the <abbr>CRS</abbr>
are linear, for each target axis.
* The bit located at {@code 1L << dimension} is set to 1 when the
conversion at that dimension is non-linear.
* The dimension indices are those of the CRS, not the grid. The use of
{@code long} type limits the capacity
* to 64 dimensions. But actually {@code GridGeometry} can contain more
dimensions provided that index of the
@@ -1704,22 +1708,21 @@ public class GridGeometry implements LenientComparable,
Serializable {
/**
* Returns a grid geometry that encompass only some dimensions of this
grid geometry.
- * The specified dimensions will be copied into a new grid geometry if
necessary.
- * The selection is applied on {@linkplain #getExtent() grid extent}
dimensions;
- * they are not necessarily the same as the {@linkplain #getEnvelope()
envelope} dimensions.
+ * The selection is applied on {@linkplain #getExtent() grid extent}
dimensions,
+ * which are not necessarily the same as 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}.
+ * The number of dimensions of the returned grid geometry will be {@code
dimensions.length}.
*
* <p>This method performs a <i>dimensionality reduction</i>.
- * This method cannot be used for changing dimension order.
- * The converse operation is the {@linkplain #GridGeometry(GridGeometry,
GridGeometry) concatenation}.</p>
+ * The converse of this operation is {@linkplain
#GridGeometry(GridGeometry, GridGeometry) concatenation}.
+ * This method cannot be used for changing dimension order.</p>
*
- * @param indices the grid (not CRS) dimensions to select, in strictly
increasing order.
+ * @param indices the grid (not <abbr>CRS</abbr>) dimensions to select,
in strictly increasing order.
* @return the sub-grid geometry, or {@code this} if the given array
contains all dimensions of this grid geometry.
* @throws IndexOutOfBoundsException if an index is out of bounds.
*
- * @see GridExtent#getSubspaceDimensions(int)
* @see GridExtent#selectDimensions(int[])
+ * @see GridExtent#getSubspaceDimensions(int)
* @see
org.apache.sis.referencing.CRS#selectDimensions(CoordinateReferenceSystem,
int[])
*
* @since 1.3
diff --git
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridDerivationTest.java
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridDerivationTest.java
index ecc073aad5..582e7566ad 100644
---
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridDerivationTest.java
+++
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridDerivationTest.java
@@ -17,11 +17,13 @@
package org.apache.sis.coverage.grid;
import java.util.Map;
+import java.util.function.Predicate;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.Envelope;
import org.opengis.metadata.spatial.DimensionNameType;
+import org.opengis.referencing.cs.AxisDirection;
import org.opengis.referencing.operation.Matrix;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
@@ -29,6 +31,7 @@ import org.apache.sis.geometry.Envelope2D;
import org.apache.sis.geometry.GeneralEnvelope;
import org.apache.sis.geometry.DirectPosition2D;
import org.apache.sis.geometry.GeneralDirectPosition;
+import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.CommonCRS;
import org.apache.sis.referencing.internal.shared.Formulas;
import org.apache.sis.referencing.internal.shared.AffineTransform2D;
@@ -735,6 +738,28 @@ public final class GridDerivationTest extends TestCase {
assertExtentEquals(expectedGridPoint, expectedGridPoint, slice.extent);
}
+ /**
+ * Tests {@link GridDerivation#project(Predicate)}.
+ */
+ @Test
+ public void testProject() {
+ final var 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);
+
+ GridGeometry projected = grid.derive().project((axis) ->
axis.getDirection() != AxisDirection.UP).build();
+ assertNotSame(grid, projected);
+ assertEquals(2, projected.getDimension());
+ assertTrue(CRS.equivalent(HardCodedCRS.WGS84,
projected.getCoordinateReferenceSystem()));
+ final long[] expectedLow = {336, 20};
+ final long[] expectedHigh = {401, 419};
+ assertExtentEquals(expectedLow, expectedHigh, projected.getExtent());
+ }
+
/**
* Tests deriving a grid geometry with a request crossing the antimeridian.
* The {@link GridGeometry} crossing the anti-meridian is the one given in