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 2020/10/09 11:07:08 UTC
[sis] 04/04: Replace `GridOrientation` enumeration by a more
configurable class.
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 0885edcc7cc54f0ade6575c874190e9991d56678
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Fri Oct 9 13:05:33 2020 +0200
Replace `GridOrientation` enumeration by a more configurable class.
---
.../sis/coverage/grid/GridCoverageBuilder.java | 19 +-
.../org/apache/sis/coverage/grid/GridExtent.java | 6 +-
.../org/apache/sis/coverage/grid/GridGeometry.java | 55 +++---
.../apache/sis/coverage/grid/GridOrientation.java | 210 ++++++++++++++++++---
.../sis/coverage/grid/GridCoverageBuilderTest.java | 8 +-
.../sis/coverage/grid/GridDerivationTest.java | 6 +-
.../apache/sis/coverage/grid/GridGeometryTest.java | 2 +-
.../sis/coverage/grid/GridOrientationTest.java | 75 ++++++++
.../coverage/grid/ResampledGridCoverageTest.java | 3 +-
.../apache/sis/test/suite/FeatureTestSuite.java | 1 +
10 files changed, 314 insertions(+), 71 deletions(-)
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java
index c598725..cf5fa01 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java
@@ -159,7 +159,7 @@ public class GridCoverageBuilder {
* Set of grid axes to reverse, as a bit mask. For any dimension <var>i</var>, the bit
* at {@code 1L << i} is set to 1 if the grid axis at that dimension should be flipped.
*
- * @see #flipAxis(int)
+ * @see #flipGridAxis(int)
*/
private long flippedAxes;
@@ -168,6 +168,7 @@ public class GridCoverageBuilder {
*
* @see #addImageProperty(String, Object)
*/
+ @SuppressWarnings("UseOfObsoleteCollectionType")
private Hashtable<String,Object> properties;
/**
@@ -208,7 +209,7 @@ public class GridCoverageBuilder {
* coordinates in the coverage domain (e.g. latitude values) are also increasing toward up.
* It often results in images flipped vertically, because popular image formats such as PNG
* use row indices increasing in the opposite direction (toward down).
- * This effect can be compensated by invoking <code>{@linkplain #flipAxis(int) flipAxis}(1)</code>.
+ * This effect can be compensated by invoking <code>{@linkplain #flipGridAxis(int) flipGridAxis}(1)</code>.
*
* <div class="note"><b>Design note:</b>
* {@code GridCoverageBuilder} does not flip the <var>y</var> axis by default because not all
@@ -230,11 +231,11 @@ public class GridCoverageBuilder {
* or {@code null} for removing previous domain setting.
* @return {@code this} for method invocation chaining.
*
- * @see #flipAxis(int)
+ * @see #flipGridAxis(int)
* @see GridGeometry#GridGeometry(GridExtent, Envelope)
*/
public GridCoverageBuilder setDomain(final Envelope domain) {
- return setDomain(domain == null ? null : new GridGeometry(null, domain, null));
+ return setDomain(domain == null ? null : new GridGeometry(null, domain, GridOrientation.HOMOTHETY));
}
/**
@@ -309,7 +310,7 @@ public class GridCoverageBuilder {
* must be equal to the number of sample dimensions.
*
* <p><b>Note:</b> row indices in an image are usually increasing down, while geographic coordinates
- * are usually increasing up. Consequently the <code>{@linkplain #flipAxis(int) flipAxis}(1)</code>
+ * are usually increasing up. Consequently the <code>{@linkplain #flipGridAxis(int) flipGridAxis}(1)</code>
* method may need to be invoked after this method.</p>
*
* @param data the rendered image to be wrapped in a {@code GridCoverage}. Can not be {@code null}.
@@ -333,7 +334,7 @@ public class GridCoverageBuilder {
* number of sample dimensions.
*
* <p><b>Note:</b> row indices in a raster are usually increasing down, while geographic coordinates
- * are usually increasing up. Consequently the <code>{@linkplain #flipAxis(int) flipAxis}(1)</code>
+ * are usually increasing up. Consequently the <code>{@linkplain #flipGridAxis(int) flipGridAxis}(1)</code>
* method may need to be invoked after this method.</p>
*
* @param data the raster to be wrapped in a {@code GridCoverage}. Can not be {@code null}.
@@ -379,7 +380,7 @@ public class GridCoverageBuilder {
/**
* Reverses axis direction in the specified grid dimension.
* For example if grid indices are (<var>column</var>, <var>row</var>),
- * then {@code flipAxis(1)} will reverse the direction of rows axis.
+ * then {@code flipGridAxis(1)} will reverse the direction of rows axis.
* Invoking this method a second time for the same dimension will cancel the flipping.
*
* <p>When building coverage with a {@linkplain #setDomain(Envelope) domain specified by an envelope}
@@ -392,8 +393,9 @@ public class GridCoverageBuilder {
* @return {@code this} for method invocation chaining.
*
* @see #setDomain(Envelope)
+ * @see GridOrientation#flipGridAxis(int)
*/
- public GridCoverageBuilder flipAxis(final int dimension) {
+ public GridCoverageBuilder flipGridAxis(final int dimension) {
ArgumentChecks.ensurePositive("dimension", dimension);
if (dimension >= Long.SIZE) {
throw new IllegalArgumentException(Errors.format(Errors.Keys.ExcessiveNumberOfDimensions_1, dimension + 1));
@@ -412,6 +414,7 @@ public class GridCoverageBuilder {
*
* @since 1.1
*/
+ @SuppressWarnings("UseOfObsoleteCollectionType")
public void addImageProperty(final String key, final Object value) {
ArgumentChecks.ensureNonNull("key", key);
ArgumentChecks.ensureNonNull("value", value);
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
index 727ea00..7652d5b 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
@@ -1375,11 +1375,11 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable
* axis direction except for those specified in the {@code flips} bitmask. The transform maps cell corners.
*
* @param env the target envelope. Despite this method name, the envelope CRS is ignored.
- * @param flips bitmask of target axes to flip (0 if none).
+ * @param flippedAxes bitmask of target axes to flip (0 if none).
* @param sourceDimensions source dimension for each target dimension, or {@code null} if dimensions are the same.
* @return an affine transform from this grid extent to the given envelope, expressed as a matrix.
*/
- final MatrixSIS cornerToCRS(final Envelope env, final long flips, final int[] sourceDimensions) {
+ final MatrixSIS cornerToCRS(final Envelope env, final long flippedAxes, final int[] sourceDimensions) {
final int srcDim = getDimension();
final int tgtDim = env.getDimension();
final MatrixSIS affine = Matrices.create(tgtDim + 1, srcDim + 1, ExtendedPrecisionMatrix.ZERO);
@@ -1388,7 +1388,7 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable
for (int j=0; j<tgtDim; j++) {
final int i = (sourceDimensions != null) ? sourceDimensions[j] : j;
if (i < srcDim) {
- final boolean flip = (flips & Numerics.bitmask(j)) != 0;
+ final boolean flip = (flippedAxes & Numerics.bitmask(j)) != 0;
offset.set(coordinates[i]);
scale.set(coordinates[i + srcDim]);
scale.subtract(offset);
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
index e02e8fa..6aa28df 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
@@ -46,7 +46,6 @@ import org.apache.sis.geometry.GeneralEnvelope;
import org.apache.sis.geometry.ImmutableEnvelope;
import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
import org.apache.sis.referencing.crs.AbstractCRS;
-import org.apache.sis.referencing.cs.AxesConvention;
import org.apache.sis.referencing.IdentifiedObjects;
import org.apache.sis.referencing.operation.matrix.Matrices;
import org.apache.sis.referencing.operation.matrix.MatrixSIS;
@@ -592,15 +591,15 @@ public class GridGeometry implements LenientComparable, Serializable {
*
* <h4>Dimension order</h4>
* The given envelope shall always declare dimensions in same order than the given extent.
- * This constructor may reorder CRS axes if {@code orientation} is {@link GridOrientation#DISPLAY},
- * but in such case this constructor will derive itself an envelope and CRS with the reordered axes.
+ * This constructor may reorder axes if {@code orientation} is (for example) {@link GridOrientation#DISPLAY},
+ * but in such case this constructor will derive itself an envelope and a CRS with reordered axes.
*
* @param extent the valid extent of grid coordinates, or {@code null} if unknown.
* @param envelope the envelope together with CRS of the "real world" coordinates, or {@code null} if unknown.
* @param orientation high-level description of desired characteristics of the {@code gridToCRS} transform.
- * Ignored (can be null) if {@code extent} or {@code envelope} is null.
+ * Ignored (can be null) if {@code envelope} is null.
* @throws NullPointerException if {@code extent} and {@code envelope} arguments are both null,
- * or if only the {@code orientation} argument is null.
+ * or if {@code envelope} is non-null but {@code orientation} is null.
*
* @see <a href="https://en.wikipedia.org/wiki/Axis-aligned_object">Axis-aligned object on Wikipedia</a>
*
@@ -616,27 +615,29 @@ public class GridGeometry implements LenientComparable, Serializable {
long flip = 0; // Bitmask specifying whether to reverse axis in each dimension.
ImmutableEnvelope target = null; // May have different axis order than the specified `envelope` CRS.
int[] sourceDimensions = null; // Indices in source envelope of axes colinear with the target envelope.
- final boolean reorderExtent = (orientation == GridOrientation.DISPLAY_GRID);
- if (envelope != null && (reorderExtent || orientation == GridOrientation.DISPLAY)) {
- final AbstractCRS sourceCRS = AbstractCRS.castOrCopy(envelope.getCoordinateReferenceSystem());
- if (sourceCRS != null) {
- final AbstractCRS targetCRS = sourceCRS.forConvention(AxesConvention.DISPLAY_ORIENTED);
- if (targetCRS != sourceCRS) {
- final CoordinateSystem sourceCS = sourceCRS.getCoordinateSystem();
- final CoordinateSystem targetCS = targetCRS.getCoordinateSystem();
- sourceDimensions = AxisDirections.indicesOfColinear(sourceCS, targetCS);
- if (sourceDimensions != null) {
- final double[] lowerCorner = new double[sourceDimensions.length];
- final double[] upperCorner = new double[sourceDimensions.length];
- for (int i=0; i < sourceDimensions.length; i++) {
- final int s = sourceDimensions[i];
- lowerCorner[i] = envelope.getMinimum(s);
- upperCorner[i] = envelope.getMaximum(s);
- if (sourceCS.getAxis(s).getDirection() != targetCS.getAxis(i).getDirection()) {
- flip |= Numerics.bitmask(i);
+ if (envelope != null) {
+ ArgumentChecks.ensureNonNull("orientation", orientation);
+ if (orientation.crsVariant != null) {
+ final AbstractCRS sourceCRS = AbstractCRS.castOrCopy(envelope.getCoordinateReferenceSystem());
+ if (sourceCRS != null) {
+ final AbstractCRS targetCRS = sourceCRS.forConvention(orientation.crsVariant);
+ if (targetCRS != sourceCRS) {
+ final CoordinateSystem sourceCS = sourceCRS.getCoordinateSystem();
+ final CoordinateSystem targetCS = targetCRS.getCoordinateSystem();
+ sourceDimensions = AxisDirections.indicesOfColinear(sourceCS, targetCS);
+ if (sourceDimensions != null) {
+ final double[] lowerCorner = new double[sourceDimensions.length];
+ final double[] upperCorner = new double[sourceDimensions.length];
+ for (int i=0; i < sourceDimensions.length; i++) {
+ final int s = sourceDimensions[i];
+ lowerCorner[i] = envelope.getMinimum(s);
+ upperCorner[i] = envelope.getMaximum(s);
+ if (sourceCS.getAxis(s).getDirection() != targetCS.getAxis(i).getDirection()) {
+ flip |= Numerics.bitmask(i);
+ }
}
+ target = new ImmutableEnvelope(lowerCorner, upperCorner, targetCRS);
}
- target = new ImmutableEnvelope(lowerCorner, upperCorner, targetCRS);
}
}
}
@@ -658,7 +659,7 @@ public class GridGeometry implements LenientComparable, Serializable {
} else {
this.envelope = target;
if (extent != null) {
- if (reorderExtent) {
+ if (orientation != null && orientation.canReorderGridAxis) {
if (!ArraysExt.isRange(0, sourceDimensions)) {
extent = extent.reorder(sourceDimensions);
}
@@ -671,8 +672,8 @@ public class GridGeometry implements LenientComparable, Serializable {
* that all scale factors are on diagonal, which allows simpler calculation than full
* matrix multiplication. Use double-double arithmetic everywhere.
*/
- ArgumentChecks.ensureNonNull("orientation", orientation);
- final MatrixSIS affine = extent.cornerToCRS(target, orientation.flip ^ flip, sourceDimensions);
+ @SuppressWarnings("null") // `!nilEnvelope` implies non-null `orientation`.
+ final MatrixSIS affine = extent.cornerToCRS(target, orientation.flippedAxes ^ flip, sourceDimensions);
cornerToCRS = MathTransforms.linear(affine);
final int srcDim = cornerToCRS.getSourceDimensions(); // Translation column in matrix.
final int tgtDim = cornerToCRS.getTargetDimensions(); // Number of matrix rows before last row.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridOrientation.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridOrientation.java
index 9f92f1b..5bf5ec3 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridOrientation.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridOrientation.java
@@ -16,17 +16,21 @@
*/
package org.apache.sis.coverage.grid;
+import java.util.Objects;
+import java.io.Serializable;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.datum.PixelInCell;
import org.apache.sis.referencing.cs.AxesConvention;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.resources.Errors;
/**
- * High-level description about how a grid is orientated relative to the CRS axes.
- * This is determined by the {@linkplain GridGeometry#getGridToCRS(PixelInCell) grid to CRS} transform.
- * For example conversion from grid coordinates to CRS coordinates may flip the <var>y</var> axis
+ * High-level description about how a grid is orientated relative to the CRS axes. The orientation of a grid
+ * is closely related to the {@linkplain GridGeometry#getGridToCRS(PixelInCell) grid to CRS} transform.
+ * For example the conversion from grid coordinates to CRS coordinates may flip the <var>y</var> axis
* (grid coordinates increasing toward down on screen), or may swap <var>x</var> and <var>y</var> axes, <i>etc.</i>
- * The possibilities are infinite; this enumeration covers only a few common types where the grid is
+ * The constants enumerated in this class cover only a few common cases where the grid is
* <a href="https://en.wikipedia.org/wiki/Axis-aligned_object">axis-aligned</a> with the CRS.
*
* @author Martin Desruisseaux (Geomatys)
@@ -37,7 +41,12 @@ import org.apache.sis.referencing.cs.AxesConvention;
* @since 1.1
* @module
*/
-public enum GridOrientation {
+public final class GridOrientation implements Serializable {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = -1354776950822418237L;
+
/**
* The {@code gridToCRS} transform applies only scales and translations (no axis flip or swap).
* Moving along the grid axis in dimension <var>i</var> causes a displacement along the CRS axis
@@ -59,11 +68,12 @@ public enum GridOrientation {
* <var>S<sub>y</sub></var> > 0 and
* <var>S<sub>z</sub></var> > 0.
*/
- HOMOTHETY(0),
+ public static final GridOrientation HOMOTHETY = new GridOrientation(0, null, false);
/**
* The {@code gridToCRS} transform applies scales and translations with a flip of the second axis (<var>y</var>).
- * This is the same kind of conversion than {@link #HOMOTHETY} except that the <var>S<sub>y</sub></var> term in
+ * This is equivalent to {@code HOMOTHETY.flipGridAxis(1)}; i.e.
+ * this is the same kind of conversion than {@link #HOMOTHETY} except that the <var>S<sub>y</sub></var> term in
* the matrix is replaced by −<var>S<sub>y</sub></var> and the <var>T<sub>y</sub></var> term has a different value.
* For example in the three-dimensional case, the {@code gridToCRS} transform is:
*
@@ -76,21 +86,25 @@ public enum GridOrientation {
* └ ┘
* }
*
+ * <h4>When to use</h4>
* {@code REFLECTION_Y} is commonly used when the grid is a {@link java.awt.image.RenderedImage}.
* By contrast, an {@link #HOMOTHETY} transform often results in <var>y</var> axis oriented toward up,
* instead of down as commonly expected with rendered images.
* This {@code REFLECTION_Y} value matches the common usage for grids backed by images.
+ *
+ * @see #flipGridAxis(int)
*/
- REFLECTION_Y(2),
+ public static final GridOrientation REFLECTION_Y = new GridOrientation(2, null, false);
/**
* CRS axes are reordered and oriented toward directions commonly used for displaying purpose.
+ * This is equivalent to {@code REFLECTION_Y.useVariantOfCRS(AxesConvention.DISPLAY_ORIENTED)}.
* {@link GridGeometry}s created with this orientation have properties computed as below:
*
* <ul>
* <li>The {@link GridExtent} specified by user (never modified).</li>
- * <li>An envelope initialized to user-specified envelope (potentially modified below).</li>
- * <li>A {@code gridToCRS} initialized to {@link #REFLECTION_Y} (potentially modified below).</li>
+ * <li>An envelope initialized to user-specified envelope (potentially modified as described below).</li>
+ * <li>A {@code gridToCRS} initialized to {@link #REFLECTION_Y} (potentially modified as described below).</li>
* <li>The {@linkplain AxesConvention#DISPLAY_ORIENTED display oriented} variant of the CRS specified by user.</li>
* <li>If above CRS variant is same as user-specified CRS, we are done. Otherwise:
* <ul>
@@ -115,36 +129,184 @@ public enum GridOrientation {
* └ ┘
* }
*
+ * <h4>When to use</h4>
* This orientation can be used for deriving a coordinate reference system with the
* <i>(<var>longitude</var>, <var>latitude</var>)</i> or <i>(<var>x</var>,<var>y</var>)</i> axis order,
* but without altering grid axes order.
*
+ * <h4>Alternative</h4>
+ * {@code DISPLAY.canReorderGridAxis(true)} is an alternative where grid axes get the same reordering than CRS axes.
+ * Consequently the {@link GridExtent} may be different then the specified extent
+ * but the {@code gridToCRS} transform always has the form shown in {@link #REFLECTION_Y}.
+ * This alternative can be used for deriving a coordinate reference system with the
+ * <i>(<var>longitude</var>, <var>latitude</var>)</i> or <i>(<var>x</var>,<var>y</var>)</i> axis order,
+ * and modify grid cell layout (i.e. replace the {@link GridExtent} instance)
+ * in way that allows {@link java.awt.image.RenderedImage} to appear with expected orientation.
+ *
+ * @see #useVariantOfCRS(AxesConvention)
* @see AxesConvention#DISPLAY_ORIENTED
*/
- DISPLAY(2),
+ public static final GridOrientation DISPLAY = new GridOrientation(2, AxesConvention.DISPLAY_ORIENTED, false);
/**
- * CRS and grid axes are reordered and oriented toward directions commonly used for displaying purpose.
- * This is similar to {@link #DISPLAY} except that {@link GridExtent} axes get the same reordering than CRS axes.
- * Consequently the {@code gridToCRS} transform always has the form shown in {@link #REFLECTION_Y}.
+ * Set of grid axes to reverse, as a bit mask. For any dimension <var>i</var>, the bit
+ * at {@code 1L << i} is set to 1 if the grid axis at that dimension should be flipped.
+ * This is the argument to give in calls to {@link GridExtent#cornerToCRS(Envelope, long, int[])}.
*
- * <p>This orientation can be used for deriving a coordinate reference system with the
- * <i>(<var>longitude</var>, <var>latitude</var>)</i> or <i>(<var>x</var>,<var>y</var>)</i> axis order,
- * and modify grid cell layout (i.e. replace the {@link GridExtent} instance)
- * in way that allows {@link java.awt.image.RenderedImage} to appear with expected orientation.</p>
+ * @see #flipGridAxis(int)
*/
- DISPLAY_GRID(2);
+ final long flippedAxes;
/**
- * Bitmask of axes to flip.
- * This is the argument to give in calls to {@link GridExtent#cornerToCRS(Envelope, long, int[])}.
+ * If the user-specified CRS should be substituted by a variant of that CRS, the variant to use.
+ * Otherwise {@code null}. If non-null, either the {@code gridToCRS} matrix may be non-diagonal
+ * or the {@link GridExtent} axes may be ordered, depending on {@link #canReorderGridAxis} value.
+ *
+ * @see #useVariantOfCRS(AxesConvention)
*/
- final int flip;
+ final AxesConvention crsVariant;
+
+ /**
+ * Whether {@link GridExtent} can be rewritten with a different axis order
+ * for matching the CRS axis order specified by {@link #crsVariant}.
+ * If {@code false}, then axis order changes will be handled in the {@code gridToCRS} transform instead.
+ *
+ * @see #canReorderGridAxis(boolean)
+ */
+ final boolean canReorderGridAxis;
/**
* Creates a new enumeration value.
*/
- private GridOrientation(final int flip) {
- this.flip = flip;
+ private GridOrientation(final long flippedAxes, final AxesConvention crsVariant, final boolean canReorderGridAxis) {
+ this.flippedAxes = flippedAxes;
+ this.crsVariant = crsVariant;
+ this.canReorderGridAxis = canReorderGridAxis;
+ }
+
+ /**
+ * Reverses axis direction in the specified grid dimension.
+ * For example if grid indices are (<var>column</var>, <var>row</var>),
+ * then {@code flipGridAxis(1)} will reverse the direction of rows axis.
+ * Invoking this method a second time for the same dimension will cancel the flipping.
+ *
+ * @param dimension index of the dimension in the grid on which to apply direction reversal.
+ * @return a grid orientation equals to this one except for the axis flip in specified dimension.
+ *
+ * @see #REFLECTION_Y
+ * @see GridCoverageBuilder#flipGridAxis(int)
+ */
+ public GridOrientation flipGridAxis(final int dimension) {
+ ArgumentChecks.ensurePositive("dimension", dimension);
+ if (dimension >= Long.SIZE) {
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.ExcessiveNumberOfDimensions_1, dimension + 1));
+ }
+ return new GridOrientation(flippedAxes ^ (1L << dimension), crsVariant, canReorderGridAxis);
+ }
+
+ /**
+ * Substitutes the user-specified CRS by a variant of it, for example with different axis order.
+ * If the CRS axis order changed as a result of this substitution, then:
+ *
+ * <ul>
+ * <li>Order of envelope coordinates are changed accordingly.</li>
+ * <li>CRS axis order change is mapped to the grid in one of the following ways:
+ * <ul>
+ * <li>If {@code canReorderGridAxis(true)} has been invoked, then the same change is applied
+ * on grid axis order. Consequently grid axes and CRS axes stay in the same order,
+ * but the resulting {@link GridExtent} may be different than the specified one.</li>
+ * <li>Otherwise {@link GridExtent} stay unchanged and axis order change is handled in the
+ * {@code gridToCRS} transform instead.</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * <h4>Limitations</h4>
+ * Current implementation accepts only axis order changes and direction changes.
+ * The units of measurement changes are not yet supported.
+ * Consequently {@link AxesConvention#NORMALIZED} is not accepted.
+ *
+ * @param variant the kind of substitution to apply on CRS, or {@code null} if none.
+ * @return a grid orientation equals to this one except that it uses the specified CRS variant.
+ *
+ * @see #DISPLAY
+ */
+ public GridOrientation useVariantOfCRS(final AxesConvention variant) {
+ if (variant == crsVariant) {
+ return this;
+ }
+ if (variant == AxesConvention.NORMALIZED) {
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.UnsupportedArgumentValue_1, variant));
+ }
+ return new GridOrientation(flippedAxes, variant, canReorderGridAxis);
+ }
+
+ /**
+ * Specifies whether a change of CRS axis order should be accompanied by an equivalent change of grid axis order.
+ * A value of {@code true} implies that user-specified {@link GridExtent} may be replaced by a different extent.
+ * If {@code false} (the default), then axis order changes will be handled in the {@code gridToCRS} transform
+ * instead.
+ *
+ * @param enabled whether changes of CRS axis order should be reflected by changes of grid axis order.
+ * @return a grid orientation equals to this one except that it has the specified flag.
+ */
+ public GridOrientation canReorderGridAxis(final boolean enabled) {
+ if (enabled == canReorderGridAxis) {
+ return this;
+ }
+ return new GridOrientation(flippedAxes, crsVariant, enabled);
+ }
+
+ /**
+ * Returns whether this object is equal to the given object.
+ *
+ * @param other the other object to compare with this object.
+ * @return whether the two objects are equal.
+ */
+ @Override
+ public boolean equals(final Object other) {
+ if (other instanceof GridOrientation) {
+ final GridOrientation that = (GridOrientation) other;
+ return flippedAxes == that.flippedAxes &&
+ crsVariant == that.crsVariant &&
+ canReorderGridAxis == that.canReorderGridAxis;
+ }
+ return false;
+ }
+
+ /**
+ * Returns a hash code value for this grid orientation.
+ */
+ @Override
+ public int hashCode() {
+ return Long.hashCode(flippedAxes) + Objects.hashCode(crsVariant) + Boolean.hashCode(canReorderGridAxis);
+ }
+
+ /**
+ * Returns a string representation of this grid orientation.
+ * This is for debugging purpose and may change in any future version.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder buffer = new StringBuilder(getClass().getSimpleName()).append('[');
+ String separator = "";
+ if (flippedAxes != 0) {
+ buffer.append("flip={");
+ long f = flippedAxes;
+ do {
+ final long i = Long.numberOfTrailingZeros(f);
+ buffer.append(separator).append(i);
+ f &= ~(1L << i);
+ separator = ", ";
+ } while (f != 0);
+ buffer.append('}');
+ }
+ if (crsVariant != null) {
+ buffer.append(separator).append("crs=").append(crsVariant);
+ if (canReorderGridAxis) {
+ buffer.append(" with grid axis reordering");
+ }
+ }
+ return buffer.append(']').toString();
}
}
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
index 74137f2..7c88794 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
@@ -156,7 +156,7 @@ public final strictfp class GridCoverageBuilderTest extends TestCase {
* Tests {@link GridCoverageBuilder#setValues(DataBuffer, Dimension)}.
*/
@Test
- public void createFromBufferTest() {
+ public void testCreateFromBuffer() {
final DataBuffer buffer = new DataBufferByte(new byte[] {1,2,3,4,5,6}, 6);
final GridCoverageBuilder builder = new GridCoverageBuilder();
assertSame(builder, builder.setValues(buffer, null));
@@ -172,10 +172,10 @@ public final strictfp class GridCoverageBuilderTest extends TestCase {
}
/**
- * Tests {@link GridCoverageBuilder#flipAxis(int)}.
+ * Tests {@link GridCoverageBuilder#flipGridAxis(int)}.
*/
@Test
- public void flippedAxisTest() {
+ public void testFlipGridAxis() {
final RenderedImage image = new BufferedImage(36, 18, BufferedImage.TYPE_INT_ARGB);
final GeneralEnvelope domain = new GeneralEnvelope(HardCodedCRS.WGS84);
domain.setRange(0, -180, +180);
@@ -201,7 +201,7 @@ public final strictfp class GridCoverageBuilderTest extends TestCase {
* This is the common orientation for images.
*/
{
- assertSame(builder, builder.flipAxis(1));
+ assertSame(builder, builder.flipGridAxis(1));
final GridCoverage coverage = builder.build();
final GridGeometry gg = coverage.getGridGeometry();
assertTrue(domain.equals(gg.getEnvelope(), STRICT, false));
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridDerivationTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridDerivationTest.java
index e32265d..45fb160 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridDerivationTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridDerivationTest.java
@@ -619,8 +619,8 @@ public final strictfp class GridDerivationTest extends TestCase {
Envelope2D domain = new Envelope2D(HardCodedCRS.WGS84, 10, 20, 110, 70);
Envelope2D request = new Envelope2D(HardCodedCRS.WGS84, -5, 25, 100, 90);
Envelope2D expected = new Envelope2D(HardCodedCRS.WGS84, 10, 25, 85, 65);
- GridGeometry grid1 = new GridGeometry(null, domain, null);
- GridGeometry grid2 = new GridGeometry(null, request, null);
+ GridGeometry grid1 = new GridGeometry(null, domain, GridOrientation.HOMOTHETY);
+ GridGeometry grid2 = new GridGeometry(null, request, GridOrientation.HOMOTHETY);
GridGeometry subgrid = grid1.derive().subgrid(grid2).build();
assertTrue(subgrid.isEnvelopeOnly());
assertEnvelopeEquals(expected, subgrid.getEnvelope(), STRICT);
@@ -630,7 +630,7 @@ public final strictfp class GridDerivationTest extends TestCase {
*/
request.setCoordinateReferenceSystem(HardCodedCRS.WGS84_φλ);
request.setRect(25, -5, 90, 100);
- grid2 = new GridGeometry(null, request, null);
+ grid2 = new GridGeometry(null, request, GridOrientation.HOMOTHETY);
subgrid = grid1.derive().subgrid(grid2).build();
assertSame(HardCodedCRS.WGS84, subgrid.getCoordinateReferenceSystem());
assertTrue(subgrid.isEnvelopeOnly());
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
index 68763d4..201d777 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
@@ -411,7 +411,7 @@ public final strictfp class GridGeometryTest extends TestCase {
* Same extent and envelope, but reordering extend dimensions
* instead than `gridToCRS` columns.
*/
- grid = new GridGeometry(extent, aoi, GridOrientation.DISPLAY_GRID);
+ grid = new GridGeometry(extent, aoi, GridOrientation.DISPLAY.canReorderGridAxis(true));
matrix = MathTransforms.getMatrix(grid.getGridToCRS(PixelInCell.CELL_CORNER));
assertMatrixEquals("cornerToCRS", new Matrix3(
0.5, 0, 50,
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridOrientationTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridOrientationTest.java
new file mode 100644
index 0000000..82eda57
--- /dev/null
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridOrientationTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.coverage.grid;
+
+import org.apache.sis.referencing.cs.AxesConvention;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests {@link GridOrientation}.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+public final strictfp class GridOrientationTest extends TestCase {
+ /**
+ * Tests {@link GridOrientation#useVariantOfCRS(AxesConvention)}. This test may fail if new enumeration values
+ * are added in {@link AxesConvention} without updating {@link GridOrientation#useVariantOfCRS(AxesConvention)}
+ * code accordingly. The main purpose of this test is to warn the developer if such situation happens.
+ */
+ @Test
+ public void testUseVariantOfCRS() {
+ final GridOrientation test = GridOrientation.DISPLAY;
+ for (final AxesConvention c : AxesConvention.values()) {
+ switch (c) {
+ case POSITIVE_RANGE: // Fallthrough
+ case RIGHT_HANDED: assertNotSame(test, test.useVariantOfCRS(c)); break;
+ case DISPLAY_ORIENTED: assertSame (test, test.useVariantOfCRS(c)); break;
+ default: {
+ try {
+ test.useVariantOfCRS(c);
+ fail("Should not accept " + c);
+ } catch (IllegalArgumentException e) {
+ final String message = e.getMessage();
+ assertTrue(message, message.contains(c.toString()));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Tests {@link GridOrientation#toString()}.
+ */
+ @Test
+ public void testToString() {
+ assertEquals("GridOrientation[]",
+ GridOrientation.HOMOTHETY.toString());
+ assertEquals("GridOrientation[flip={1}]",
+ GridOrientation.REFLECTION_Y.toString());
+ assertEquals("GridOrientation[flip={1}, crs=DISPLAY_ORIENTED]",
+ GridOrientation.DISPLAY.toString());
+ assertEquals("GridOrientation[flip={1}, crs=DISPLAY_ORIENTED with grid axis reordering]",
+ GridOrientation.DISPLAY.canReorderGridAxis(true).toString());
+ }
+}
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java
index 53a6bbb..ee67304 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java
@@ -440,7 +440,8 @@ public final strictfp class ResampledGridCoverageTest extends TestCase {
@Test
public void testSubGeographicArea() throws TransformException {
final GridCoverage2D source = createCoverage2D(); // Envelope2D(20, 15, 60, 62)
- GridGeometry gg = new GridGeometry(null, new Envelope2D(HardCodedCRS.WGS84, 18, 20, 17, 31), null);
+ final GridGeometry gg = new GridGeometry(null,
+ new Envelope2D(HardCodedCRS.WGS84, 18, 20, 17, 31), GridOrientation.HOMOTHETY);
final GridCoverage target = resample(source, gg);
final GridExtent sourceExtent = source.getGridGeometry().getExtent();
final GridExtent targetExtent = target.getGridGeometry().getExtent();
diff --git a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
index d757336..fa96385 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
@@ -96,6 +96,7 @@ import org.junit.runners.Suite;
org.apache.sis.coverage.SampleDimensionTest.class,
org.apache.sis.coverage.SampleRangeFormatTest.class,
org.apache.sis.coverage.grid.PixelTranslationTest.class,
+ org.apache.sis.coverage.grid.GridOrientationTest.class,
org.apache.sis.coverage.grid.GridExtentTest.class,
org.apache.sis.coverage.grid.GridGeometryTest.class,
org.apache.sis.coverage.grid.GridDerivationTest.class,