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 dceffce01b58f1901345ca9ca6bb112726b3c02a
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Thu Sep 24 16:44:23 2020 +0200
Add a `DISPLAY` enumeration value in `GridOrientation`.
---
.../org/apache/sis/coverage/grid/GridExtent.java | 2 +-
.../org/apache/sis/coverage/grid/GridGeometry.java | 55 +++++++++++++++++++---
.../apache/sis/coverage/grid/GridOrientation.java | 29 ++++++++++--
.../apache/sis/coverage/grid/GridExtentTest.java | 10 ++++
.../apache/sis/coverage/grid/GridGeometryTest.java | 35 ++++++++++++++
.../sis/internal/referencing/AxisDirections.java | 24 +++++-----
.../internal/referencing/AxisDirectionsTest.java | 38 ++++++++++++++-
7 files changed, 170 insertions(+), 23 deletions(-)
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 6b8f174..51177bc 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
@@ -1367,7 +1367,7 @@ public class GridExtent implements GridEnvelope, LenientComparable,
Serializable
scale.set(coordinates[i + srcDim]);
scale.subtract(offset);
scale.add(1); // == getSize(j) but without
overflow.
- scale.inverseDivideGuessError(env.getSpan(i)); // == (envelope span) / (grid
size).
+ scale.inverseDivideGuessError(env.getSpan(j)); // == (envelope span) / (grid
size).
if (flip) scale.negate();
if (!offset.isZero()) { // Use `if` for keeping the
value if scale is NaN.
offset.multiply(scale);
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 5ae3434..d5c52e1 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
@@ -45,6 +45,8 @@ import org.apache.sis.geometry.Envelopes;
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;
@@ -52,6 +54,7 @@ import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.referencing.operation.transform.PassThroughTransform;
import org.apache.sis.internal.referencing.DirectPositionView;
import org.apache.sis.internal.referencing.TemporalAccessor;
+import org.apache.sis.internal.referencing.AxisDirections;
import org.apache.sis.internal.metadata.ReferencingServices;
import org.apache.sis.internal.system.Modules;
import org.apache.sis.internal.feature.Resources;
@@ -586,6 +589,11 @@ public class GridGeometry implements LenientComparable, Serializable
{
* For grid geometries describing preexisting data, it is safer and more flexible to
use one of
* the constructors expecting a {@link MathTransform} argument.</p>
*
+ * <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.
+ *
* @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.
@@ -599,19 +607,53 @@ public class GridGeometry implements LenientComparable, Serializable
{
this.extent = extent;
nonLinears = 0;
/*
+ * Potentially change axis order and orientation according the given `GridOrientation`
(which may be null).
+ * Current code assumes that units of measurement are unchanged, which should be
true for CRSs built using
+ * AxisConvention.DISPLAY_ORIENTED.
+ */
+ 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.
+ if (envelope != null && 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);
+ }
+ }
+ target = new ImmutableEnvelope(lowerCorner, upperCorner, targetCRS);
+ }
+ }
+ }
+ }
+ if (target == null) {
+ target = ImmutableEnvelope.castOrCopy(envelope);
+ }
+ /*
* If the envelope contains no useful information, then the grid extent is mandatory.
* We do that for forcing grid geometries to contain at least one information.
*/
boolean nilEnvelope = true;
- final ImmutableEnvelope env = ImmutableEnvelope.castOrCopy(envelope);
- if (env == null || ((nilEnvelope = env.isAllNaN()) && env.getCoordinateReferenceSystem()
== null)) {
- if (env != null && nilEnvelope) {
+ if (target == null || ((nilEnvelope = target.isAllNaN()) && target.getCoordinateReferenceSystem()
== null)) {
+ if (target != null && nilEnvelope) {
throw new NullArgumentException(Errors.format(Errors.Keys.UnspecifiedCRS));
}
ArgumentChecks.ensureNonNull("extent", extent);
this.envelope = null;
} else {
- this.envelope = env;
+ this.envelope = target;
if (extent != null && !nilEnvelope) {
/*
* If we have both the extent and an envelope with at least one non-NaN coordinates,
@@ -620,13 +662,14 @@ public class GridGeometry implements LenientComparable, Serializable
{
* matrix multiplication. Use double-double arithmetic everywhere.
*/
ArgumentChecks.ensureNonNull("orientation", orientation);
- final MatrixSIS affine = extent.cornerToCRS(env, orientation.flip, null);
+ final MatrixSIS affine = extent.cornerToCRS(target, orientation.flip ^ 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.
resolution = new double[tgtDim];
for (int j=0; j<tgtDim; j++) {
- final DoubleDouble scale = (DoubleDouble) affine.getNumber(j, j);
+ final int i = (sourceDimensions != null) ? sourceDimensions[j] : j;
+ final DoubleDouble scale = (DoubleDouble) affine.getNumber(j, i);
final DoubleDouble offset = (DoubleDouble) affine.getNumber(j, srcDim);
resolution[j] = Math.abs(scale.doubleValue());
scale.multiply(0.5);
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 cff41ae..3d10a4d 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
@@ -18,6 +18,7 @@ package org.apache.sis.coverage.grid;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.datum.PixelInCell;
+import org.apache.sis.referencing.cs.AxesConvention;
/**
@@ -29,7 +30,10 @@ import org.opengis.referencing.datum.PixelInCell;
*
* @author Martin Desruisseaux (Geomatys)
* @version 1.1
- * @since 1.1
+ *
+ * @see GridGeometry#GridGeometry(GridExtent, Envelope, GridOrientation)
+ *
+ * @since 1.1
* @module
*/
public enum GridOrientation {
@@ -66,11 +70,28 @@ public enum GridOrientation {
* instead of down as commonly expected with rendered images.
* This {@code REFLECTION_Y} value matches the common usage for grids backed by images.</p>
*/
- REFLECTION_Y(2);
+ REFLECTION_Y(2),
- /*
- * TODO: add DISPLAY. The difference compared to REFLECTION_Y is that it may change CRS
axes.
+ /**
+ * CRS axes are reordered and oriented toward directions commonly used for displaying
purpose.
+ * 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.
+ * Grid axes order is unchanged.
+ * An example of {@code gridToCRS} transform obtained by this orientation is as below
+ * (the exact matrix depends on the CRS axes):
+ *
+ * {@preformat math
+ * ┌ ┐
+ * │ 0 Sx 0 Tx │
+ * │ −Sy 0 0 Ty │
+ * │ 0 0 Sz Tz │
+ * │ 0 0 0 1 │
+ * └ ┘
+ * }
+ *
+ * @see AxesConvention#DISPLAY_ORIENTED
*/
+ DISPLAY(2);
/**
* Bitmask of axes to flip.
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
index 1a67174..b1cc369 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
@@ -295,6 +295,16 @@ public final strictfp class GridExtentTest extends TestCase {
0.5, 0, 50,
0, -2, 20,
0, 0, 1), extent.cornerToCRS(aoi, 2, null), STRICT);
+ /*
+ * Swap axis order. The {1,0} indices apply to grid dimensions, not to CRS dimensions.
+ * Verification: x = 0.375 × −25 + 49.375 = 40 (the minimum value declared
in envelope).
+ * y = 2.667 × −20 + 43.333 ≈ −10 (idem).
+ */
+ assertMatrixEquals("cornerToCRS", new Matrix3(
+ 0, 0.375, 49.375,
+ 2.6666666666666667, 0, 43.333333333333333,
+ 0, 0, 1),
+ extent.cornerToCRS(aoi, 0, new int[] {1,0}), 1E-15);
}
/**
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 42acf36..07ebd7e 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
@@ -369,6 +369,41 @@ public final strictfp class GridGeometryTest extends TestCase {
assertArrayEquals("resolution", new double[] {0.5, 2}, grid.getResolution(false),
STRICT);
assertTrue("isConversionLinear", grid.isConversionLinear(0, 1));
verifyGridToCRS(grid);
+ /*
+ * The use of `DISPLAY` mode in this particular case should be equivalent ro `REFLECTION_Y`.
+ */
+ assertEquals(grid, new GridGeometry(extent, aoi, GridOrientation.DISPLAY));
+ }
+
+ /**
+ * Tests the construction from a geospatial envelope and an extent with reordering of
axes
+ * for matching display convention.
+ *
+ * @see GridExtentTest#testCornerToCRS()
+ */
+ @Test
+ public void testFromExtentAndDisplayEnvelope() {
+ final GeneralEnvelope aoi = new GeneralEnvelope(HardCodedCRS.WGS84_φλ);
+ aoi.setRange(1, 40, 55);
+ aoi.setRange(0, -10, 70);
+ final GridExtent extent = new GridExtent(null,
+ new long[] {-25, -20},
+ new long[] { 15, 10}, false);
+ /*
+ * Same case than the one tested by `testFromExtentAndEnvelope()`,
+ * but with axis order swapped.
+ */
+ GridGeometry grid = new GridGeometry(extent, aoi, GridOrientation.DISPLAY);
+ Matrix matrix = MathTransforms.getMatrix(grid.getGridToCRS(PixelInCell.CELL_CORNER));
+ assertMatrixEquals("cornerToCRS", new Matrix3(
+ 0, 0.5, 50,
+ -2, 0, 20,
+ 0, 0, 1), matrix, STRICT);
+
+ // Verify other computed properties.
+ assertArrayEquals("resolution", new double[] {0.5, 2}, grid.getResolution(false),
STRICT);
+ assertTrue("isConversionLinear", grid.isConversionLinear(0, 1));
+ verifyGridToCRS(grid);
}
/**
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java
index dfbdeb4..9f512dc 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java
@@ -29,6 +29,7 @@ import org.apache.sis.internal.metadata.NameToIdentifier;
import org.apache.sis.util.ComparisonMode;
import org.apache.sis.util.Characters;
import org.apache.sis.util.Utilities;
+import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.Static;
import org.apache.sis.util.iso.Types;
import org.apache.sis.measure.Units;
@@ -492,10 +493,10 @@ public final class AxisDirections extends Static {
* <li>Otherwise if a sequence of {@code cs} axes have similar names than the
{@code subCS} axes (as determined
* by {@linkplain org.apache.sis.referencing.IdentifiedObjects#isHeuristicMatchForName
heuristic match},
* that sequence has precedence.</li>
- * <li>Otherwise the index of the first sequence if returned, regardless axis
names.</li>
+ * <li>Otherwise the index of the first sequence is returned, regardless axis
names.</li>
* </ol>
*
- * Note that colinear axes are normally not allowed, except if the case of {@link org.opengis.referencing.crs.TemporalCRS}
+ * Note that colinear axes are normally not allowed, except in the case of {@link org.opengis.referencing.crs.TemporalCRS}
* when one time axis is the runtime (the date where a numerical model has been executed)
and the other time axis is the
* forecast time (the date at which a prevision is made).
*
@@ -551,20 +552,21 @@ next: for (int i=0; i <= limit; i++) {
*
* @param cs the coordinate system which contains all axes, or {@code null}.
* @param subCS the coordinate system to search into {@code cs}.
- * @return the sequence of axes colinear with {@code subCS} axes, or {@code null} if
none.
+ * @return indices in {@code cs} of axes colinear with {@code subCS} axes in the order
they appear in {@code subCS},
+ * or {@code null} if at least one {@code subCS} axis can not be mapped to a
{@code cs} axis.
*
* @since 1.1
*/
public static int[] indicesOfColinear(final CoordinateSystem cs, final CoordinateSystem
subCS) {
- final int[] indices = new int[subCS.getDimension()];
+ final int dim = subCS.getDimension();
final int index = indexOfColinear(cs, subCS); // More robust than fallback
below.
- for (int i=0; i<indices.length; i++) {
- if (index >= 0) {
- indices[i] = index + i;
- } else {
- if ((indices[i] = indexOfColinear(cs, subCS.getAxis(i).getDirection())) <
0) {
- return null;
- }
+ if (index >= 0) {
+ return ArraysExt.range(index, index + dim);
+ }
+ final int[] indices = new int[dim];
+ for (int i=0; i<dim; i++) {
+ if ((indices[i] = indexOfColinear(cs, subCS.getAxis(i).getDirection())) <
0) {
+ return null;
}
}
return indices;
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/AxisDirectionsTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/AxisDirectionsTest.java
index c711d32..5df9414 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/AxisDirectionsTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/AxisDirectionsTest.java
@@ -43,7 +43,7 @@ import static org.apache.sis.internal.referencing.AxisDirections.COUNTER_CLOCKWI
* {@code sis-referencing} module because those tests use {@link HardCodedAxes} constants.</p>
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
* @since 0.4
* @module
*/
@@ -421,6 +421,42 @@ public final strictfp class AxisDirectionsTest extends TestCase {
}
/**
+ * Tests {@link AxisDirections#indicesOfColinear(CoordinateSystem, CoordinateSystem)}.
+ *
+ * @since 1.1
+ */
+ @Test
+ public void testIndicesOfColinear() {
+ assertArrayEquals(new int[] {0, 1}, AxisDirections.indicesOfColinear(
+ HardCodedCS.GEODETIC_3D,
+ HardCodedCS.GEODETIC_2D));
+
+ assertArrayEquals(new int[] {1, 0}, AxisDirections.indicesOfColinear(
+ HardCodedCS.GEODETIC_φλ,
+ HardCodedCS.GEODETIC_2D));
+
+ assertArrayEquals(new int[] {1, 0}, AxisDirections.indicesOfColinear(
+ HardCodedCS.GEODETIC_3D,
+ HardCodedCS.GEODETIC_φλ));
+
+ assertArrayEquals(new int[] {2}, AxisDirections.indicesOfColinear(
+ HardCodedCS.GEODETIC_3D,
+ HardCodedCS.ELLIPSOIDAL_HEIGHT));
+
+ assertArrayEquals(new int[] {2}, AxisDirections.indicesOfColinear(
+ HardCodedCS.GEODETIC_3D,
+ HardCodedCS.DEPTH));
+
+ assertArrayEquals(null, AxisDirections.indicesOfColinear(
+ HardCodedCS.GEODETIC_3D,
+ HardCodedCS.DAYS));
+
+ assertArrayEquals(null, AxisDirections.indicesOfColinear(
+ HardCodedCS.GEODETIC_2D,
+ HardCodedCS.GEODETIC_3D));
+ }
+
+ /**
* Tests {@link AxisDirections#suggestAbbreviation(String, AxisDirection, Unit)}.
*
* @since 0.6
|