sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Make a better effort to take the `gridToCRS` transform in account when determining a CRS for a GridExtent.
Date Wed, 15 Apr 2020 10:22:28 GMT
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 8bb0edb  Make a better effort to take the `gridToCRS` transform in account when determining
a CRS for a GridExtent.
8bb0edb is described below

commit 8bb0edb2cc89ff406f5a473c0d5ed410bd886597
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Wed Apr 15 12:21:12 2020 +0200

    Make a better effort to take the `gridToCRS` transform in account when determining a CRS
for a GridExtent.
---
 .../org/apache/sis/coverage/grid/GridExtent.java   |   8 +-
 .../apache/sis/coverage/grid/GridExtentCRS.java    | 136 +++++++++++++++------
 .../apache/sis/coverage/grid/GridExtentTest.java   |  39 +++++-
 3 files changed, 145 insertions(+), 38 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 a0806cf..a13b529 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
@@ -96,6 +96,8 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable
      * This map contains only the "positive" axis directions.
      *
      * @todo Verify if there is more directions to add as of ISO 19111:2018.
+     *
+     * @see #typeFromAxes(CoordinateReferenceSystem, int)
      */
     private static final Map<AxisDirection,DimensionNameType> AXIS_DIRECTIONS;
     static {
@@ -317,6 +319,7 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable
 
     /**
      * Infers the axis types from the given coordinate reference system.
+     * This method is the converse of {@link GridExtentCRS}.
      *
      * @param  crs        the coordinate reference system, or {@code null}.
      * @param  dimension  number of name type to infer. Shall not be greater than the CRS
dimension.
@@ -851,8 +854,9 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable
     public GeneralEnvelope toEnvelope(final MathTransform cornerToCRS) throws TransformException
{
         ArgumentChecks.ensureNonNull("cornerToCRS", cornerToCRS);
         final GeneralEnvelope envelope = toCRS(cornerToCRS, cornerToCRS, null);
-        if (cornerToCRS.isIdentity()) try {
-            envelope.setCoordinateReferenceSystem(GridExtentCRS.build(getDimension(), types,
null));
+        final Matrix gridToCRS = MathTransforms.getMatrix(cornerToCRS);
+        if (gridToCRS != null && Matrices.isAffine(gridToCRS)) try {
+            envelope.setCoordinateReferenceSystem(GridExtentCRS.build(gridToCRS, (types !=
null) ? types : DEFAULT_TYPES, null));
         } catch (FactoryException e) {
             throw new TransformException(e);
         }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtentCRS.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtentCRS.java
index 2e4d1c5..597e7b8 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtentCRS.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtentCRS.java
@@ -27,18 +27,24 @@ import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.opengis.referencing.crs.CRSFactory;
 import org.opengis.referencing.crs.EngineeringCRS;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.datum.EngineeringDatum;
+import org.opengis.referencing.operation.Matrix;
 import org.apache.sis.referencing.cs.AbstractCS;
 import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.internal.referencing.AxisDirections;
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.util.iso.Types;
 import org.apache.sis.measure.Units;
 import org.apache.sis.referencing.datum.DefaultEngineeringDatum;
+import org.apache.sis.util.Characters;
 
 
 /**
- * Builds the coordinate reference system of a {@link GridExtent}.
- * This is used only in the rare case where we need to represent an extent as an envelope.
+ * Builds the engineering coordinate reference system of a {@link GridExtent}.
+ * This is used only in the rare cases where we need to represent an extent as an envelope.
+ * This class converts {@link DimensionNameType} codes into axis names, abbreviations and
directions.
+ * It is the converse of {@link GridExtent#typeFromAxes(CoordinateReferenceSystem, int)}.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @version 1.1
@@ -74,53 +80,113 @@ final class GridExtentCRS {
     }
 
     /**
-     * Builds a coordinate reference system of the given number of dimensions for the given
axis types.
+     * Returns a default axis abbreviation for the given dimension.
      */
-    static EngineeringCRS build(final int dimension, final DimensionNameType[] types, final
Locale locale)
+    private static String abbreviation(final int dimension) {
+        final StringBuilder b = new StringBuilder(4).append('x').append(dimension);
+        for (int i=b.length(); --i >= 1;) {
+            b.setCharAt(i, Characters.toSuperScript(b.charAt(i)));
+        }
+        return b.toString();
+    }
+
+    /**
+     * Builds a coordinate reference system for the given axis types.
+     *
+     * @param  gridToCRS  matrix of the transform used for converting grid cell indices to
envelope coordinates.
+     *         It does not matter whether it maps pixel center or corner (translation coefficients
are ignored).
+     * @param  types   the value of {@link GridExtent#types} or a default value (shall not
be {@code null}).
+     * @param  locale  locale to use for axis names, or {@code null} for default.
+     * @return CRS for the grid, or {@code null}.
+     *
+     * @see GridExtent#typeFromAxes(CoordinateReferenceSystem, int)
+     */
+    static EngineeringCRS build(final Matrix gridToCRS, final DimensionNameType[] types,
final Locale locale)
             throws FactoryException
     {
+        final int tgtDim = gridToCRS.getNumRow() - 1;
+        final int srcDim = Math.min(gridToCRS.getNumCol() - 1, types.length);
+        final CoordinateSystemAxis[] axes = new CoordinateSystemAxis[tgtDim];
         final CSFactory csFactory = DefaultFactories.forBuildin(CSFactory.class);
-        final CoordinateSystemAxis[] axes = new CoordinateSystemAxis[dimension];
-        if (types != null) {
-skip:       for (int i=0; i<dimension; i++) {
-                final DimensionNameType type = types[i];
-                if (type != null) {
-                    final String abbreviation;
-                    final AxisDirection direction;
-                    if (type == DimensionNameType.ROW || type == DimensionNameType.LINE)
{
-                        abbreviation = "y"; direction = AxisDirection.ROW_POSITIVE;
-                    } else if (type == DimensionNameType.COLUMN || type == DimensionNameType.SAMPLE)
{
-                        abbreviation = "x"; direction = AxisDirection.COLUMN_POSITIVE;
-                    } else if (type == DimensionNameType.VERTICAL) {
-                        abbreviation = "z"; direction = AxisDirection.UP;
-                    } else if (type == DimensionNameType.TIME) {
-                        abbreviation = "t"; direction = AxisDirection.FUTURE;
-                    } else {
-                        abbreviation = "d" + dimension;
-                        direction = AxisDirection.OTHER;
+        for (int i=0; i<srcDim; i++) {
+            final DimensionNameType type = types[i];
+            if (type != null) {
+                /*
+                 * Try to locate the CRS dimension corresponding to grid dimension j.
+                 * We expect a one-to-one matching; if it is not the case, return null.
+                 * Current version does not accept scale factors, but we could revisit
+                 * in a future version if there is a need for it.
+                 */
+                int target = -1;
+                double scale = 0;
+                for (int j=0; j<tgtDim; j++) {
+                    final double m = gridToCRS.getElement(j, i);
+                    if (m != 0) {
+                        if (target >= 0 || axes[j] != null || Math.abs(m) != 1) {
+                            return null;
+                        }
+                        target = j;
+                        scale  = m;
                     }
-                    // Verify that no other axis has the same direction.
-                    for (int j=i; --j >= 0;) {
-                        final CoordinateSystemAxis previous = axes[j];
-                        if (previous != null && direction.equals(previous.getDirection()))
{
-                            continue skip;
+                }
+                if (target < 0) {
+                    return null;
+                }
+                /*
+                 * This hard-coded set of axis directions is the converse of
+                 * GridExtent.AXIS_DIRECTIONS map.
+                 */
+                String abbreviation;
+                AxisDirection direction;
+                if (type == DimensionNameType.COLUMN || type == DimensionNameType.SAMPLE)
{
+                    abbreviation = "x"; direction = AxisDirection.COLUMN_POSITIVE;
+                } else if (type == DimensionNameType.ROW || type == DimensionNameType.LINE)
{
+                    abbreviation = "y"; direction = AxisDirection.ROW_POSITIVE;
+                } else if (type == DimensionNameType.VERTICAL) {
+                    abbreviation = "z"; direction = AxisDirection.UP;
+                } else if (type == DimensionNameType.TIME) {
+                    abbreviation = "t"; direction = AxisDirection.FUTURE;
+                } else {
+                    abbreviation = abbreviation(target);
+                    direction = AxisDirection.OTHER;
+                }
+                /*
+                 * Verify that no other axis has the same direction and abbreviation. If
duplicated
+                 * values are found, keep only the first occurrence in grid axis order (may
not be
+                 * the CRS axis order).
+                 */
+                for (int k = tgtDim; --k >= 0;) {
+                    final CoordinateSystemAxis previous = axes[k];
+                    if (previous != null) {
+                        if (direction.equals(AxisDirections.absolute(previous.getDirection())))
{
+                            direction = AxisDirection.OTHER;
+                        }
+                        if (abbreviation.equals(previous.getAbbreviation())) {
+                            abbreviation = abbreviation(target);
                         }
                     }
-                    final String name = Types.toString(Types.getCodeTitle(type), locale);
-                    axes[i] = axis(csFactory, name, abbreviation, direction);
                 }
+                if (scale < 0) {
+                    direction = AxisDirections.opposite(direction);
+                }
+                final String name = Types.toString(Types.getCodeTitle(type), locale);
+                axes[target] = axis(csFactory, name, abbreviation, direction);
             }
         }
-        for (int i=0; i<dimension; i++) {
-            if (axes[i] == null) {
-                final String name = Vocabulary.getResources(locale).getString(Vocabulary.Keys.Dimension_1,
i);
-                final String abbreviation = "d" + dimension;
-                axes[i] = axis(csFactory, name, abbreviation, AxisDirection.OTHER);
+        /*
+         * Search for axes that have not been created in above loop.
+         * It happens when some axes have no associated `DimensionNameType` code.
+         */
+        for (int j=0; j<tgtDim; j++) {
+            if (axes[j] == null) {
+                final String name = Vocabulary.getResources(locale).getString(Vocabulary.Keys.Dimension_1,
j);
+                final String abbreviation = abbreviation(j);
+                axes[j] = axis(csFactory, name, abbreviation, AxisDirection.OTHER);
             }
         }
         final Map<String,?> properties = properties("Grid extent");
         final CoordinateSystem cs;
-        switch (dimension) {
+        switch (tgtDim) {
             case 2:  cs = csFactory.createAffineCS(properties, axes[0], axes[1]); break;
             case 3:  cs = csFactory.createAffineCS(properties, axes[0], axes[1], axes[2]);
break;
             default: cs = new AbstractCS(properties, axes); break;
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 88fd556..b0cb727 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
@@ -22,17 +22,22 @@ import org.opengis.geometry.Envelope;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.metadata.spatial.DimensionNameType;
 import org.opengis.coverage.PointOutsideCoverageException;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.referencing.cs.CoordinateSystem;
+import org.opengis.referencing.cs.AxisDirection;
 import org.apache.sis.geometry.AbstractEnvelope;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.geometry.GeneralDirectPosition;
 import org.apache.sis.coverage.SubspaceNotSpecifiedException;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.referencing.operation.matrix.Matrix3;
 import org.apache.sis.referencing.crs.HardCodedCRS;
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
-import static org.apache.sis.test.Assert.*;
+import static org.apache.sis.test.ReferencingAssert.*;
 
 
 /**
@@ -263,6 +268,38 @@ public final strictfp class GridExtentTest extends TestCase {
     }
 
     /**
+     * Tests {@link GridExtent#toEnvelope(MathTransform)}.
+     *
+     * @throws TransformException if an error occurred while transforming to an envelope.
+     */
+    @Test
+    public void testToEnvelope() throws TransformException {
+        final GridExtent extent = new GridExtent(new DimensionNameType[] {
+            DimensionNameType.ROW,
+            DimensionNameType.TIME,
+            DimensionNameType.COLUMN,
+            DimensionNameType.VERTICAL
+        }, new long[] {100, 5, 200, 40}, new long[] {500, 7, 800, 50}, false);
+        final GeneralEnvelope envelope = extent.toEnvelope(MathTransforms.linear(Matrices.create(5,
5, new double[] {
+            0,  0,  1,  0,  0,
+           -1,  0,  0,  0,  0,
+            0,  0,  0, -1,  0,
+            0,  1,  0,  0,  0,
+            0,  0,  0,  0,  1})));
+
+        assertEnvelopeEquals(new GeneralEnvelope(
+                new double[] {200, -500, -50, 5},
+                new double[] {800, -100, -40, 7}), envelope);
+
+        final CoordinateSystem cs = envelope.getCoordinateReferenceSystem().getCoordinateSystem();
+        assertAxisDirectionsEqual("toEnvelope", cs,
+                AxisDirection.COLUMN_POSITIVE,
+                AxisDirection.ROW_NEGATIVE,
+                AxisDirection.DOWN,
+                AxisDirection.FUTURE);
+    }
+
+    /**
      * Tests {@link GridExtent#toString()}.
      * Note that the string representation may change in any future SIS version.
      *


Mime
View raw message