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 2015/11/20 22:38:16 UTC

svn commit: r1715426 - in /sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis: internal/referencing/ internal/referencing/provider/ referencing/ referencing/operation/transform/

Author: desruisseaux
Date: Fri Nov 20 21:38:15 2015
New Revision: 1715426

URL: http://svn.apache.org/viewvc?rev=1715426&view=rev
Log:
More rigorous checks in situations where an ellipsoidal coordinate system (EllipsoidalCS) is involved:

  - Checks for GeodeticCRS + EllipsoidalCS are more reliable than checks for GeographicCRS.
  - AbstractProvider.getNumEllipsoids() method shall differentiate the cases where a single
    ellipsoid is available in the source CRS, or in the target CRS.

Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractProvider.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffineBetweenGeographic.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricToGeographic.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyFormula.java

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java?rev=1715426&r1=1715425&r2=1715426&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java [UTF-8] Fri Nov 20 21:38:15 2015
@@ -163,20 +163,34 @@ public final class ReferencingUtilities
      * the two first dimensions use an instance of {@link GeographicCRS}. Otherwise (i.e. if the
      * two first dimensions are not geographic), returns {@code null}.
      *
+     * <p>This method excludes geocentric CRS on intend. Some callers needs this exclusion as a way to identify
+     * which CRS in a Geographic/Geocentric conversion is the geographic one. An other point of view is to said
+     * that if this method returns a non-null value, then the coordinates are expected to be either two-dimensional
+     * or three-dimensional with an ellipsoidal height.</p>
+     *
      * @param  crs The coordinate reference system for which to get the ellipsoid.
      * @return The ellipsoid in the given CRS, or {@code null} if none.
      *
      * @since 0.6
      */
     public static Ellipsoid getEllipsoidOfGeographicCRS(CoordinateReferenceSystem crs) {
-        while (!(crs instanceof GeographicCRS)) {
+        while (!(crs instanceof GeodeticCRS)) {
             if (crs instanceof CompoundCRS) {
                 crs = ((CompoundCRS) crs).getComponents().get(0);
             } else {
                 return null;
             }
         }
-        return ((GeographicCRS) crs).getDatum().getEllipsoid();
+        /*
+         * In order to determine if the CRS is geographic, checking the CoordinateSystem type is more reliable
+         * then checking if the CRS implements the GeographicCRS interface.  This is because the GeographicCRS
+         * interface is GeoAPI-specific, so a CRS may be OGC-compliant without implementing that interface.
+         */
+        if (crs.getCoordinateSystem() instanceof EllipsoidalCS) {
+            return ((GeodeticCRS) crs).getDatum().getEllipsoid();
+        } else {
+            return null;    // Geocentric CRS.
+        }
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractProvider.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractProvider.java?rev=1715426&r1=1715425&r2=1715426&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractProvider.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractProvider.java [UTF-8] Fri Nov 20 21:38:15 2015
@@ -187,11 +187,11 @@ public abstract class AbstractProvider e
     }
 
     /**
-     * Returns the number of ellipsoids (0, 1 or 2) concerned by this operation. This method is invoked by
-     * {@link org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory} for determining
-     * if this operation has {@code "semi_major"}, {@code "semi_minor"}, {@code "src_semi_major"} or
-     * {@code "src_semi_minor"} parameters that may need to be filled with values inferred from the
-     * source or target {@link org.apache.sis.referencing.datum.DefaultGeodeticDatum}.
+     * Flags whether the source and/or target ellipsoid are concerned by this operation. This method is invoked by
+     * {@link org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory} for determining if this
+     * operation has {@code "semi_major"}, {@code "semi_minor"}, {@code "src_semi_major"}, {@code "src_semi_minor"}
+     * parameters that may need to be filled with values inferred from the source or target
+     * {@link org.apache.sis.referencing.datum.DefaultGeodeticDatum}.
      * Meaning of return values:
      *
      * <ul>
@@ -199,7 +199,9 @@ public abstract class AbstractProvider e
      *       There is no parameter that need to be completed.</li>
      *   <li>1 if this operation has {@code "semi_major"} and {@code "semi_minor"} parameters that need
      *       to be set to the axis lengths of the source ellipsoid.</li>
-     *   <li>2 if this operation has {@code "src_semi_major"}, {@code "src_semi_minor"}, {@code "tgt_semi_major"}
+     *   <li>2 if this operation has {@code "semi_major"} and {@code "semi_minor"} parameters that need
+     *       to be set to the axis lengths of the target ellipsoid.</li>
+     *   <li>3 if this operation has {@code "src_semi_major"}, {@code "src_semi_minor"}, {@code "tgt_semi_major"}
      *       and {@code "tgt_semi_minor"} parameters that need to be set to the axis lengths of the source and
      *       target ellipsoids.</li>
      * </ul>
@@ -207,9 +209,9 @@ public abstract class AbstractProvider e
      * This method is just a hint. If the information is not provided, {@code DefaultMathTransformFactory}
      * will try to infer it from the type of user-specified source and target CRS.
      *
-     * @return 0, 1 or 2.
+     * @return 0, 1, 2 or 3.
      */
-    public int getNumEllipsoids() {
+    public int getEllipsoidsMask() {
         return 0;
     }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java?rev=1715426&r1=1715425&r2=1715426&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java [UTF-8] Fri Nov 20 21:38:15 2015
@@ -218,7 +218,7 @@ public final class Equirectangular exten
      * @return 1, meaning that the operation requires a source ellipsoid.
      */
     @Override
-    public final int getNumEllipsoids() {
+    public final int getEllipsoidsMask() {
         return 1;
     }
 

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java?rev=1715426&r1=1715425&r2=1715426&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java [UTF-8] Fri Nov 20 21:38:15 2015
@@ -186,11 +186,11 @@ public final class FranceGeocentricInter
      * {@code "src_semi_major"}, {@code "src_semi_minor"} , {@code "tgt_semi_major"} and
      * {@code "tgt_semi_minor"} parameters.
      *
-     * @return 2, meaning that the operation requires source and target ellipsoids.
+     * @return 3, meaning that the operation requires source and target ellipsoids.
      */
     @Override
-    public int getNumEllipsoids() {
-        return 2;
+    public int getEllipsoidsMask() {
+        return 3;
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffineBetweenGeographic.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffineBetweenGeographic.java?rev=1715426&r1=1715425&r2=1715426&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffineBetweenGeographic.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffineBetweenGeographic.java [UTF-8] Fri Nov 20 21:38:15 2015
@@ -113,11 +113,11 @@ public abstract class GeocentricAffineBe
      * {@code "src_semi_major"}, {@code "src_semi_minor"} , {@code "tgt_semi_major"} and
      * {@code "tgt_semi_minor"} parameters.
      *
-     * @return 2, meaning that the operation requires source and target ellipsoids.
+     * @return 3, meaning that the operation requires source and target ellipsoids.
      */
     @Override
-    public final int getNumEllipsoids() {
-        return 2;
+    public final int getEllipsoidsMask() {
+        return 3;
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricToGeographic.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricToGeographic.java?rev=1715426&r1=1715425&r2=1715426&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricToGeographic.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricToGeographic.java [UTF-8] Fri Nov 20 21:38:15 2015
@@ -77,6 +77,17 @@ public final class GeocentricToGeographi
     }
 
     /**
+     * Notifies {@code DefaultMathTransformFactory} that Geographic/geocentric conversions
+     * require values for the {@code "semi_major"} and {@code "semi_minor"} parameters.
+     *
+     * @return 2, meaning that the operation requires a target ellipsoid.
+     */
+    @Override
+    public int getEllipsoidsMask() {
+        return 2;
+    }
+
+    /**
      * Creates a transform from the specified group of parameter values.
      *
      * @param  factory The factory to use for creating the transform.

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java?rev=1715426&r1=1715425&r2=1715426&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java [UTF-8] Fri Nov 20 21:38:15 2015
@@ -90,7 +90,7 @@ public final class GeographicToGeocentri
      * @return 1, meaning that the operation requires a source ellipsoid.
      */
     @Override
-    public final int getNumEllipsoids() {
+    public int getEllipsoidsMask() {
         return 1;
     }
 

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java?rev=1715426&r1=1715425&r2=1715426&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java [UTF-8] Fri Nov 20 21:38:15 2015
@@ -210,7 +210,7 @@ public abstract class MapProjection exte
      * @return 1, meaning that the operation requires a source ellipsoid.
      */
     @Override
-    public final int getNumEllipsoids() {
+    public final int getEllipsoidsMask() {
         return 1;
     }
 

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java?rev=1715426&r1=1715425&r2=1715426&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java [UTF-8] Fri Nov 20 21:38:15 2015
@@ -260,7 +260,7 @@ public final class CRS extends Static {
      * CRS as horizontal if it is two-dimensional and comply with one of the following conditions:
      *
      * <ul>
-     *   <li>is an instance of {@link GeographicCRS}, or</li>
+     *   <li>is an instance of {@link GeographicCRS} (or an equivalent {@link GeodeticCRS}), or</li>
      *   <li>is an instance of {@link ProjectedCRS}, or</li>
      *   <li>is an instance of {@link EngineeringCRS} (following
      *     <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#111">ISO 19162 §16.1</a>
@@ -282,8 +282,17 @@ public final class CRS extends Static {
      * @category information
      */
     public static boolean isHorizontalCRS(final CoordinateReferenceSystem crs) {
-        if (crs instanceof GeographicCRS || crs instanceof ProjectedCRS || crs instanceof EngineeringCRS) {
-            return crs.getCoordinateSystem().getDimension() == 2;
+        /*
+         * In order to determine if the CRS is geographic, checking the CoordinateSystem type is more reliable
+         * then checking if the CRS implements the GeographicCRS interface.  This is because the GeographicCRS
+         * interface is GeoAPI-specific, so a CRS may be OGC-compliant without implementing that interface.
+         */
+        final boolean isGeodetic = (crs instanceof GeodeticCRS);
+        if (isGeodetic || crs instanceof ProjectedCRS || crs instanceof EngineeringCRS) {
+            final CoordinateSystem cs = crs.getCoordinateSystem();
+            if (cs.getDimension() == 2) {
+                return !isGeodetic || (cs instanceof EllipsoidalCS);
+            }
         }
         return false;
     }
@@ -307,21 +316,24 @@ public final class CRS extends Static {
         if (isHorizontalCRS(crs)) {
             return (SingleCRS) crs;
         }
-        if (crs instanceof GeographicCRS) {
-            EllipsoidalCS cs = ((GeographicCRS) crs).getCoordinateSystem();
-            final int i = AxisDirections.indexOfColinear(cs, AxisDirection.UP);
-            if (i >= 0) {
-                final CoordinateSystemAxis xAxis = cs.getAxis(i > 0 ? 0 : 1);
-                final CoordinateSystemAxis yAxis = cs.getAxis(i > 1 ? 1 : 2);
-                cs = CommonCRS.DEFAULT.geographic().getCoordinateSystem();
-                if (!Utilities.equalsIgnoreMetadata(cs.getAxis(0), xAxis) ||
-                    !Utilities.equalsIgnoreMetadata(cs.getAxis(1), yAxis))
-                {
-                    // We can not reuse the name of the existing CS, because it typically
-                    // contains text about axes including the axis that we just dropped.
-                    cs = new DefaultEllipsoidalCS(singletonMap(EllipsoidalCS.NAME_KEY, "Ellipsoidal 2D"), xAxis, yAxis);
+        if (crs instanceof GeodeticCRS) {
+            CoordinateSystem cs = crs.getCoordinateSystem();
+            if (cs instanceof EllipsoidalCS) {  // See comment in isHorizontalCRS(…) method.
+                final int i = AxisDirections.indexOfColinear(cs, AxisDirection.UP);
+                if (i >= 0) {
+                    final CoordinateSystemAxis xAxis = cs.getAxis(i > 0 ? 0 : 1);
+                    final CoordinateSystemAxis yAxis = cs.getAxis(i > 1 ? 1 : 2);
+                    cs = CommonCRS.DEFAULT.geographic().getCoordinateSystem();
+                    if (!Utilities.equalsIgnoreMetadata(cs.getAxis(0), xAxis) ||
+                        !Utilities.equalsIgnoreMetadata(cs.getAxis(1), yAxis))
+                    {
+                        // We can not reuse the name of the existing CS, because it typically
+                        // contains text about axes including the axis that we just dropped.
+                        cs = new DefaultEllipsoidalCS(singletonMap(EllipsoidalCS.NAME_KEY, "Ellipsoidal 2D"), xAxis, yAxis);
+                    }
+                    return new DefaultGeographicCRS(IdentifiedObjects.getProperties(crs),
+                            ((GeodeticCRS) crs).getDatum(), (EllipsoidalCS) cs);
                 }
-                return new DefaultGeographicCRS(IdentifiedObjects.getProperties(crs), ((GeographicCRS) crs).getDatum(), cs);
             }
         }
         if (crs instanceof CompoundCRS) {
@@ -384,17 +396,19 @@ public final class CRS extends Static {
                 }
             } while ((a = !a) == allowCreateEllipsoidal);
         }
-        if (allowCreateEllipsoidal && crs instanceof GeographicCRS) {
+        if (allowCreateEllipsoidal && crs instanceof GeodeticCRS) {
             final CoordinateSystem cs = crs.getCoordinateSystem();
-            final int i = AxisDirections.indexOfColinear(cs, AxisDirection.UP);
-            if (i >= 0) {
-                final CoordinateSystemAxis axis = cs.getAxis(i);
-                VerticalCRS c = CommonCRS.Vertical.ELLIPSOIDAL.crs();
-                if (!c.getCoordinateSystem().getAxis(0).equals(axis)) {
-                    final Map<String,?> properties = IdentifiedObjects.getProperties(c);
-                    c = new DefaultVerticalCRS(properties, c.getDatum(), new DefaultVerticalCS(properties, axis));
+            if (cs instanceof EllipsoidalCS) {  // See comment in isHorizontalCRS(…) method.
+                final int i = AxisDirections.indexOfColinear(cs, AxisDirection.UP);
+                if (i >= 0) {
+                    final CoordinateSystemAxis axis = cs.getAxis(i);
+                    VerticalCRS c = CommonCRS.Vertical.ELLIPSOIDAL.crs();
+                    if (!c.getCoordinateSystem().getAxis(0).equals(axis)) {
+                        final Map<String,?> properties = IdentifiedObjects.getProperties(c);
+                        c = new DefaultVerticalCRS(properties, c.getDatum(), new DefaultVerticalCS(properties, axis));
+                    }
+                    return c;
                 }
-                return c;
             }
         }
         return null;

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java?rev=1715426&r1=1715425&r2=1715426&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] Fri Nov 20 21:38:15 2015
@@ -17,7 +17,6 @@
 package org.apache.sis.referencing.operation.transform;
 
 import java.util.IdentityHashMap;
-import java.util.Locale;
 import java.util.Map;
 import java.util.ServiceLoader;
 import java.util.Set;
@@ -499,12 +498,14 @@ public class DefaultMathTransformFactory
 
         /**
          * Sets the source ellipsoid and coordinate system to values inferred from the given CRS.
+         * The source ellipsoid will be non-null only if the given CRS is geographic (not geocentric).
          *
          * @param crs The source coordinate reference system (can be {@code null}).
          */
         public void setSource(final CoordinateReferenceSystem crs) {
             sourceCS = (crs != null) ? crs.getCoordinateSystem() : null;
             sourceEllipsoid = ReferencingUtilities.getEllipsoidOfGeographicCRS(crs);
+            // Ellipsoid is intentionally null for GeocentricCRS.
         }
 
         /**
@@ -520,12 +521,14 @@ public class DefaultMathTransformFactory
 
         /**
          * Sets the target ellipsoid and coordinate system to values inferred from the given CRS.
+         * The target ellipsoid will be non-null only if the given CRS is geographic (not geocentric).
          *
          * @param crs The target coordinate reference system (can be {@code null}).
          */
         public void setTarget(final CoordinateReferenceSystem crs) {
             targetCS = (crs != null) ? crs.getCoordinateSystem() : null;
             targetEllipsoid = ReferencingUtilities.getEllipsoidOfGeographicCRS(crs);
+            // Ellipsoid is intentionally null for GeocentricCRS.
         }
 
         /**
@@ -620,9 +623,15 @@ public class DefaultMathTransformFactory
          *         informative exception.
          */
         private static RuntimeException setEllipsoid(final ParameterValueGroup parameters, final Ellipsoid ellipsoid,
-                final String semiMajor, final String semiMinor, final boolean inverseFlattening)
+                final String semiMajor, final String semiMinor, final boolean inverseFlattening, RuntimeException failure)
         {
-            RuntimeException failure = null;
+            /*
+             * Note: we could also consider to set the "dim" parameter here based on the number of dimensions
+             * of the coordinate system. But except for the Molodensky operation, this would be SIS-specific.
+             * A more portable way is to concatenate a "Geographic 3D to 2D" operation after the transform if
+             * we see that the dimensions do not match. It also avoid attempt to set a "dim" parameter on map
+             * projections, which is not allowed.
+             */
             if (ellipsoid != null) {
                 ParameterValue<?> mismatchedParam = null;
                 double mismatchedValue = 0;
@@ -653,11 +662,15 @@ public class DefaultMathTransformFactory
                      * can do something about that. If it can not, createParameterizedTransform(…) is
                      * the right place to throw the exception.
                      */
-                    failure = e;
+                    if (failure == null) {
+                        failure = e;
+                    } else {
+                        failure.addSuppressed(e);
+                    }
                 }
                 final boolean isIvfDefinitive;
                 if (mismatchedParam != null) {
-                    final LogRecord record = Messages.getResources((Locale) null).getLogRecord(Level.WARNING,
+                    final LogRecord record = Messages.getResources(null).getLogRecord(Level.WARNING,
                             Messages.Keys.MismatchedEllipsoidAxisLength_3, ellipsoid.getName().getCode(),
                             mismatchedParam.getDescriptor().getName().getCode(), mismatchedValue);
                     record.setLoggerName(Loggers.COORDINATE_OPERATION);
@@ -700,32 +713,33 @@ public class DefaultMathTransformFactory
          *         informative exception.
          */
         final RuntimeException setEllipsoids(final OperationMethod method, final ParameterValueGroup parameters) {
-            final int n;
+            int n;
             if (method instanceof AbstractProvider) {
-                n = ((AbstractProvider) method).getNumEllipsoids();
+                n = ((AbstractProvider) method).getEllipsoidsMask();
             } else {
                 // Fallback used only when the information is not available in
-                // a more reliable way from AbstractProvider.getNumEllipsoids().
-                n = (sourceEllipsoid == null) ? 0 : (targetEllipsoid == null) ? 1 : 2;
+                // a more reliable way from AbstractProvider.getEllipsoidsMask().
+                n = 0;
+                if (sourceEllipsoid != null) n  = 1;
+                if (targetEllipsoid != null) n |= 2;
             }
             switch (n) {
-                case 0: {
-                    return null;
-                }
-                case 1: {
-                    return setEllipsoid(parameters, getSourceEllipsoid(), Constants.SEMI_MAJOR, Constants.SEMI_MINOR, true);
-                }
-                case 2: {
-                    final RuntimeException f1, f2;
-                    f1 = setEllipsoid(parameters, getSourceEllipsoid(), "src_semi_major", "src_semi_minor", false);
-                    f2 = setEllipsoid(parameters, getTargetEllipsoid(), "tgt_semi_major", "tgt_semi_minor", false);
-                    if (f2 != null) {
-                        if (f1 == null) {
-                            return f2;
+                case 0: return null;
+                case 1: return setEllipsoid(parameters, getSourceEllipsoid(), Constants.SEMI_MAJOR, Constants.SEMI_MINOR, true, null);
+                case 2: return setEllipsoid(parameters, getTargetEllipsoid(), Constants.SEMI_MAJOR, Constants.SEMI_MINOR, true, null);
+                case 3: {
+                    RuntimeException failure = null;
+                    if (sourceCS != null) try {
+                        final ParameterValue<?> p = parameters.parameter("dim");
+                        if (p.getValue() == null) {
+                            p.setValue(sourceCS.getDimension());
                         }
-                        f1.addSuppressed(f2);
+                    } catch (IllegalArgumentException | IllegalStateException e) {
+                        failure = e;
                     }
-                    return f1;
+                    failure = setEllipsoid(parameters, getSourceEllipsoid(), "src_semi_major", "src_semi_minor", false, failure);
+                    failure = setEllipsoid(parameters, getTargetEllipsoid(), "tgt_semi_major", "tgt_semi_minor", false, failure);
+                    return failure;
                 }
                 default: throw new AssertionError(n);
             }
@@ -1091,16 +1105,16 @@ public class DefaultMathTransformFactory
     }
 
     /**
-     * Creates a math transform object from a XML string. The default implementation
-     * always throws an exception, since this constructor is not yet implemented.
+     * There is no XML format for math transforms.
      *
      * @param  xml Math transform encoded in XML format.
      * @throws FactoryException if the object creation failed.
      */
     @Override
+    @Deprecated
     public MathTransform createFromXML(String xml) throws FactoryException {
         lastMethod.remove();
-        throw new FactoryException("Not yet implemented.");
+        throw new FactoryException(Errors.format(Errors.Keys.UnsupportedOperation_1, "createFromXML"));
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyFormula.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyFormula.java?rev=1715426&r1=1715425&r2=1715426&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyFormula.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyFormula.java [UTF-8] Fri Nov 20 21:38:15 2015
@@ -260,10 +260,13 @@ abstract class MolodenskyFormula extends
      * @param Δf         The flattening difference to set, or NaN if this method should fetch that value itself.
      */
     void completeParameters(final Parameters pg, final double semiMinor, final Unit<?> unit, final double Δf) {
-        final int dim = getSourceDimensions();
-        if (dim == getTargetDimensions()) {
-            pg.getOrCreate(Molodensky.DIMENSION).setValue(dim);
-        }
+        /*
+         * Unconditionally set the "dim" parameters to the number of source dimensions (do not check for consistency
+         * with the number of target dimensions) because source dimensions determine the value of ellipsoidal heights,
+         * which may change the horizontal numerical values. By contrast, the number of target dimensions does not have
+         * any impact on numerical values (it can just causes a drop of the third value).
+         */
+        pg.getOrCreate(Molodensky.DIMENSION).setValue(getSourceDimensions());
     }
 
     /**