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: Fix a MismatchedDimensionException when extracting the horizontal component of a three-dimensional ProjectedCRS.
Date Sat, 07 Mar 2020 13:34:36 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 0280ffb  Fix a MismatchedDimensionException when extracting the horizontal component
of a three-dimensional ProjectedCRS.
0280ffb is described below

commit 0280ffbff82362f432f41ab8fd689678b07e1ed4
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sat Mar 7 14:21:49 2020 +0100

    Fix a MismatchedDimensionException when extracting the horizontal component of a three-dimensional
ProjectedCRS.
---
 .../sis/internal/referencing/AxisDirections.java   | 32 ++++++++++-
 .../main/java/org/apache/sis/referencing/CRS.java  |  2 +-
 .../referencing/EllipsoidalHeightSeparator.java    | 66 ++++++++++++++--------
 .../sis/referencing/crs/AbstractDerivedCRS.java    | 19 ++++++-
 .../apache/sis/referencing/crs/package-info.java   |  2 +-
 .../java/org/apache/sis/referencing/CRSTest.java   | 21 ++++++-
 .../apache/sis/referencing/crs/HardCodedCRS.java   |  2 +
 .../operation/HardCodedConversions.java            |  4 +-
 8 files changed, 115 insertions(+), 33 deletions(-)

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 545760c..dfbdeb4 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
@@ -43,7 +43,7 @@ import static org.apache.sis.util.CharSequences.*;
  * Utilities methods related to {@link AxisDirection}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   0.4
  * @module
  */
@@ -541,6 +541,36 @@ next:       for (int i=0; i <= limit; i++) {
     }
 
     /**
+     * Returns the indices in {@code cs} of axes colinear with the {@code subCS} axes.
+     * If many axes have the same direction (should not happen except for temporal axes),
+     * this method gives precedence to a sequence of consecutive indices.
+     *
+     * <p>This method is similar to {@link #indexOfColinear(CoordinateSystem, CoordinateSystem)}
except that it
+     * enumerates the indices instead than returning only the first index. If {@code indexOfColinear(…)}
can not
+     * find consecutive indices, then this method fallbacks on a sequence of indices regardless
their order.</p>
+     *
+     * @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.
+     *
+     * @since 1.1
+     */
+    public static int[] indicesOfColinear(final CoordinateSystem cs, final CoordinateSystem
subCS) {
+        final int[] indices = new int[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;
+                }
+            }
+        }
+        return indices;
+    }
+
+    /**
      * Returns whether the second axis is colinear with the first axis. This method returns
{@code true}
      * if the {@linkplain #absolute absolute} direction of the given directions are equal.
      * For example "down" is considered colinear with "up".
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
index ac57f3f..be29463 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
@@ -995,7 +995,7 @@ public final class CRS extends Static {
                     final int verticalDimension = Long.numberOfTrailingZeros((isVertical
? intersect : ~intersect) >>> previous);
                     final CoordinateSystemAxis verticalAxis = crs.getCoordinateSystem().getAxis(verticalDimension);
                     if (AxisDirections.isVertical(verticalAxis.getDirection())) try {
-                        addTo.add(new EllipsoidalHeightSeparator((GeodeticDatum) datum).separate((SingleCRS)
crs, isVertical));
+                        addTo.add(new EllipsoidalHeightSeparator((GeodeticDatum) datum, isVertical).separate((SingleCRS)
crs));
                         selected &= ~current;
                     } catch (IllegalArgumentException | ClassCastException e) {
                         throw new FactoryException(Resources.format(Resources.Keys.CanNotSeparateCRS_1,
crs.getName()));
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/EllipsoidalHeightSeparator.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/EllipsoidalHeightSeparator.java
index 562fada..3234d45 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/EllipsoidalHeightSeparator.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/EllipsoidalHeightSeparator.java
@@ -16,8 +16,6 @@
  */
 package org.apache.sis.referencing;
 
-import java.util.Map;
-import java.util.Collections;
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.cs.VerticalCS;
 import org.opengis.referencing.cs.CartesianCS;
@@ -26,6 +24,7 @@ import org.opengis.referencing.crs.CRSFactory;
 import org.opengis.referencing.crs.SingleCRS;
 import org.opengis.referencing.crs.VerticalCRS;
 import org.opengis.referencing.crs.ProjectedCRS;
+import org.opengis.referencing.crs.GeodeticCRS;
 import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
@@ -36,7 +35,11 @@ import org.apache.sis.internal.referencing.AxisDirections;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
 import org.apache.sis.referencing.cs.CoordinateSystems;
 import org.apache.sis.referencing.cs.AxisFilter;
+import org.apache.sis.referencing.operation.DefaultConversion;
 import org.apache.sis.util.Utilities;
+import org.apache.sis.util.resources.Errors;
+
+import static org.apache.sis.internal.referencing.ReferencingUtilities.getPropertiesForModifiedCRS;
 
 
 /**
@@ -44,7 +47,7 @@ import org.apache.sis.util.Utilities;
  * This is the converse of {@link org.apache.sis.internal.referencing.EllipsoidalHeightCombiner}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  *
  * @see org.apache.sis.internal.referencing.EllipsoidalHeightCombiner
  *
@@ -60,13 +63,17 @@ final class EllipsoidalHeightSeparator implements AxisFilter {
     /**
      * Whether to extract the vertical component ({@code true}) or the horizontal component
({@code false}).
      */
-    private boolean vertical;
+    private final boolean vertical;
 
     /**
      * Creates a new separator for a CRS having the given datum.
+     *
+     * @param  datum     the datum of the CRS to separate.
+     * @param  vertical  whether to extract the vertical component ({@code true}) or the
horizontal component ({@code false}).
      */
-    EllipsoidalHeightSeparator(final GeodeticDatum datum) {
-        this.datum = datum;
+    EllipsoidalHeightSeparator(final GeodeticDatum datum, final boolean vertical) {
+        this.datum    = datum;
+        this.vertical = vertical;
     }
 
     /**
@@ -85,34 +92,33 @@ final class EllipsoidalHeightSeparator implements AxisFilter {
     }
 
     /**
-     * Returns properties with the name of the given CRS.
-     */
-    private static Map<String,?> properties(final SingleCRS component) {
-        return Collections.singletonMap(SingleCRS.NAME_KEY, component.getName());
-    }
-
-    /**
      * Extracts the horizontal or vertical component of the coordinate reference system.
      *
-     * @param  crs       the coordinate reference system from which to extract the horizontal
or vertical component.
-     * @param  vertical  whether to extract the vertical component ({@code true}) or the
horizontal component ({@code false}).
+     * @param  crs  the coordinate reference system from which to extract the horizontal
or vertical component.
      * @return the requested component.
      * @throws IllegalArgumentException if the specified coordinate system can not be filtered.
      *         It may be because the coordinate system would contain an illegal number of
axes,
      *         or because an axis would have an unexpected direction or unexpected unit of
measurement.
      * @throws ClassCastException if a coordinate system is not of the expected type.
      */
-    SingleCRS separate(final SingleCRS crs, final boolean vertical) throws FactoryException
{
-        this.vertical = vertical;
+    SingleCRS separate(final SingleCRS crs) throws FactoryException {
         final CoordinateSystem cs = CoordinateSystems.replaceAxes(crs.getCoordinateSystem(),
this);
         if (vertical) {
             VerticalCRS component = CommonCRS.Vertical.ELLIPSOIDAL.crs();
             if (!Utilities.equalsIgnoreMetadata(component.getCoordinateSystem(), cs)) {
-                component = factory().createVerticalCRS(properties(component), component.getDatum(),
(VerticalCS) cs);
+                component = factory().createVerticalCRS(getPropertiesForModifiedCRS(component),
component.getDatum(), (VerticalCS) cs);
             }
             return component;
         }
-        if (crs instanceof GeographicCRS) {
+        /*
+         * Horizontal CRS requested. If geographic, try to use one of the pre-defined instances
if suitable.
+         * If no pre-defined instance match, create a new CRS.
+         */
+        if (crs instanceof GeodeticCRS) {
+            if (!(cs instanceof EllipsoidalCS)) {
+                throw new IllegalArgumentException(Errors.format(
+                        Errors.Keys.UnsupportedCoordinateSystem_1, IdentifiedObjects.getName(cs,
null)));
+            }
             final CommonCRS ref = CommonCRS.WGS84;
             if (Utilities.equalsIgnoreMetadata(ref.geographic().getCoordinateSystem(), cs))
{
                 final CommonCRS c = CommonCRS.forDatum(datum);
@@ -121,16 +127,30 @@ final class EllipsoidalHeightSeparator implements AxisFilter {
                 final CommonCRS c = CommonCRS.forDatum(datum);
                 if (c != null) return c.normalizedGeographic();
             }
-            return factory().createGeographicCRS(properties(crs), datum, (EllipsoidalCS)
cs);
+            return factory().createGeographicCRS(getPropertiesForModifiedCRS(crs), datum,
(EllipsoidalCS) cs);
         }
+        /*
+         * In the projected CRS case, in addition of reducing the number of dimensions in
the CartesianCS,
+         * we also need to reduce the number of dimensions in the base CRS and in the conversion.
+         */
         if (crs instanceof ProjectedCRS) {
             GeographicCRS baseCRS = ((ProjectedCRS) crs).getBaseCRS();
             if (ReferencingUtilities.getDimension(baseCRS) != 2) {
-                baseCRS = (GeographicCRS) separate(baseCRS, false);
+                baseCRS = (GeographicCRS) separate(baseCRS);
             }
             Conversion projection = ((ProjectedCRS) crs).getConversionFromBase();
-            return factory().createProjectedCRS(properties(crs), baseCRS, projection, (CartesianCS)
cs);
+            /*
+             * The conversion object of the given CRS has a base (source) CRS and a target
CRS that are not
+             * the ones of the new `ProjectedCRS` to create. In addition it has a `MathTransform`
expecting
+             * three-dimensional coordinates, while we need 2 dimensions. We can not use
that transform even
+             * after reducing its number of dimensions (with `TransformSeparator`) because
the `ProjectedCRS`
+             * constructor expects a normalized transform, while the `projection.getMathTransform()`
may not
+             * be normalized. So we are better to let constructor recreate the transform
from the parameters.
+             */
+            projection = new DefaultConversion(getPropertiesForModifiedCRS(projection),
+                                projection.getMethod(), null, projection.getParameterValues());
+            return factory().createProjectedCRS(getPropertiesForModifiedCRS(crs), baseCRS,
projection, (CartesianCS) cs);
         }
-        throw new IllegalArgumentException();
+        throw new IllegalArgumentException(Errors.format(Errors.Keys.UnsupportedType_1, crs.getClass()));
     }
 }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractDerivedCRS.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractDerivedCRS.java
index 3df352b..d8a1d2b 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractDerivedCRS.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractDerivedCRS.java
@@ -37,6 +37,7 @@ import org.opengis.geometry.MismatchedDimensionException;
 import org.apache.sis.referencing.operation.DefaultConversion;
 import org.apache.sis.internal.jaxb.referencing.CC_Conversion;
 import org.apache.sis.internal.referencing.ReferencingFactoryContainer;
+import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.internal.metadata.MetadataUtilities;
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.system.Semaphores;
@@ -53,7 +54,7 @@ import static org.apache.sis.util.Utilities.deepEquals;
  * (not by a {@linkplain org.apache.sis.referencing.datum.AbstractDatum datum}).
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.6
+ * @version 1.1
  *
  * @param <C>  the conversion type, either {@code Conversion} or {@code Projection}.
  *
@@ -107,8 +108,20 @@ abstract class AbstractDerivedCRS<C extends Conversion> extends
AbstractCRS impl
         ArgumentChecks.ensureNonNull("conversion", conversion);
         final MathTransform baseToDerived = conversion.getMathTransform();
         if (baseToDerived != null) {
-            ArgumentChecks.ensureDimensionMatches("baseCRS",   baseToDerived.getSourceDimensions(),
baseCRS);
-            ArgumentChecks.ensureDimensionMatches("derivedCS", baseToDerived.getTargetDimensions(),
derivedCS);
+check:      for (int i=0; ; i++) {
+                final CoordinateSystem cs;
+                final int actual;
+                switch (i) {
+                    case 0:  actual = baseToDerived.getSourceDimensions(); cs = baseCRS.getCoordinateSystem();
break;
+                    case 1:  actual = baseToDerived.getTargetDimensions(); cs = derivedCS;
break;
+                    default: break check;
+                }
+                final int expected = cs.getDimension();
+                if (actual != expected) {
+                    throw new MismatchedDimensionException(Resources.format(
+                            Resources.Keys.MismatchedTransformDimension_3, i, expected, actual));
+                }
+            }
         }
         conversionFromBase = createConversionFromBase(properties, baseCRS, conversion);
     }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/package-info.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/package-info.java
index 92333b8..8c7a310 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/package-info.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/package-info.java
@@ -73,7 +73,7 @@
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Cédric Briançon (Geomatys)
- * @version 0.7
+ * @version 1.1
  * @since   0.4
  * @module
  */
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java
index 7fa425f..a636454 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java
@@ -22,6 +22,7 @@ import java.util.Arrays;
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.NoSuchAuthorityCodeException;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.ProjectedCRS;
 import org.opengis.referencing.crs.GeodeticCRS;
 import org.opengis.referencing.crs.SingleCRS;
@@ -368,8 +369,24 @@ public final strictfp class CRSTest extends TestCase {
      */
     @Test
     public void testReduceGeographic3D() throws FactoryException {
-        assertSame(CommonCRS.Vertical.ELLIPSOIDAL.crs(),   CRS.reduce(HardCodedCRS.WGS84_3D,
2));
-        assertSame(CommonCRS.WGS84.normalizedGeographic(), CRS.reduce(HardCodedCRS.WGS84_3D,
0, 1));
+        final GeographicCRS crs = HardCodedCRS.WGS84_3D;
+        assertSame(CommonCRS.Vertical.ELLIPSOIDAL.crs(),   CRS.reduce(crs, 2));
+        assertSame(CommonCRS.WGS84.normalizedGeographic(), CRS.reduce(crs, 0, 1));
+    }
+
+    /**
+     * Tests {@link CRS#reduce(CoordinateReferenceSystem, int...)} with a three-dimensional
projected CRS
+     * to be reduced to a two-dimensional CRS.
+     *
+     * @throws FactoryException if an error occurred while creating a CRS.
+     *
+     * @since 1.1
+     */
+    @Test
+    public void testReduceProjected3D() throws FactoryException {
+        final ProjectedCRS crs = HardCodedConversions.mercator3D();
+        assertSame(CommonCRS.Vertical.ELLIPSOIDAL.crs(), CRS.reduce(crs, 2));
+        assertEqualsIgnoreMetadata(HardCodedConversions.mercator(), CRS.reduce(crs, 0, 1));
     }
 
     /**
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/HardCodedCRS.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/HardCodedCRS.java
index c145d9e..930bc9d 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/HardCodedCRS.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/HardCodedCRS.java
@@ -33,6 +33,8 @@ import static org.apache.sis.referencing.IdentifiedObjects.getProperties;
 
 /**
  * Collection of coordinate reference systems for testing purpose.
+ * This class defines geographic, vertical, temporal and engineering CRS, but no projected
CRS.
+ * For projected CRS, see {@link org.apache.sis.referencing.operation.HardCodedConversions}.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.1
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/HardCodedConversions.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/HardCodedConversions.java
index c011acf..469a1d1 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/HardCodedConversions.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/HardCodedConversions.java
@@ -31,7 +31,7 @@ import org.apache.sis.referencing.cs.HardCodedCS;
  * Collection of defining conversions for testing purpose.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.1
  * @since   0.8
  * @module
  */
@@ -78,7 +78,7 @@ public final strictfp class HardCodedConversions {
      * @return three-dimensional Mercator projection.
      */
     public static DefaultProjectedCRS mercator3D() {
-        return new DefaultProjectedCRS(name("Mercator 3D"),
+        return new DefaultProjectedCRS(name("Mercator (3D)"),
                 HardCodedCRS.WGS84_3D, HardCodedConversions.MERCATOR, HardCodedCS.PROJECTED_3D);
     }
 


Mime
View raw message