sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1712369 - in /sis/branches/JDK8/core/sis-referencing/src: main/java/org/apache/sis/internal/referencing/provider/ main/java/org/apache/sis/referencing/operation/transform/ test/java/org/apache/sis/internal/referencing/provider/ test/java/o...
Date Tue, 03 Nov 2015 18:52:48 GMT
Author: desruisseaux
Date: Tue Nov  3 18:52:48 2015
New Revision: 1712369

URL: http://svn.apache.org/viewvc?rev=1712369&view=rev
Log:
Partial implementation of Molodensky transform.
This still a work in progress, but there is enough for running some tests.

Added:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MolodenskyTransformTest.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbridgedMolodensky.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.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/Molodensky.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidalToCartesianTransform.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/GeocentricTranslationTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbridgedMolodensky.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbridgedMolodensky.java?rev=1712369&r1=1712368&r2=1712369&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbridgedMolodensky.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbridgedMolodensky.java [UTF-8] Tue Nov  3 18:52:48 2015
@@ -60,7 +60,7 @@ public final class AbridgedMolodensky ex
     /**
      * The group of all parameters expected by this coordinate operation.
      */
-    private static final ParameterDescriptorGroup PARAMETERS;
+    public static final ParameterDescriptorGroup PARAMETERS;
     static {
         PARAMETERS = builder()
                 .addIdentifier("9605")

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java?rev=1712369&r1=1712368&r2=1712369&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java [UTF-8] Tue Nov  3 18:52:48 2015
@@ -71,21 +71,21 @@ public abstract class GeocentricAffine e
      * ({@linkplain BursaWolfParameters#tX tX}) parameter value. Valid values range
      * from negative to positive infinity. Units are {@linkplain SI#METRE metres}.
      */
-    static final ParameterDescriptor<Double> TX;
+    public static final ParameterDescriptor<Double> TX;
 
     /**
      * The operation parameter descriptor for the <cite>Y-axis translation</cite>
      * ({@linkplain BursaWolfParameters#tY tY}) parameter value. Valid values range
      * from negative to positive infinity. Units are {@linkplain SI#METRE metres}.
      */
-    static final ParameterDescriptor<Double> TY;
+    public static final ParameterDescriptor<Double> TY;
 
     /**
      * The operation parameter descriptor for the <cite>Z-axis translation</cite>
      * ({@linkplain BursaWolfParameters#tZ tZ}) parameter value. Valid values range
      * from negative to positive infinity. Units are {@linkplain SI#METRE metres}.
      */
-    static final ParameterDescriptor<Double> TZ;
+    public static final ParameterDescriptor<Double> TZ;
 
     /**
      * The operation parameter descriptor for the <cite>X-axis rotation</cite>

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=1712369&r1=1712368&r2=1712369&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] Tue Nov  3 18:52:48 2015
@@ -69,25 +69,25 @@ public abstract class GeocentricAffineBe
      * The operation parameter descriptor for the {@code "src_semi_major"} optional parameter value.
      * Valid values range from 0 to infinity. Units are {@linkplain SI#METRE metres}.
      */
-    static final ParameterDescriptor<Double> SRC_SEMI_MAJOR;
+    public static final ParameterDescriptor<Double> SRC_SEMI_MAJOR;
 
     /**
      * The operation parameter descriptor for the {@code "src_semi_minor"} optional parameter value.
      * Valid values range from 0 to infinity. Units are {@linkplain SI#METRE metres}.
      */
-    static final ParameterDescriptor<Double> SRC_SEMI_MINOR;
+    public static final ParameterDescriptor<Double> SRC_SEMI_MINOR;
 
     /**
      * The operation parameter descriptor for the {@code "src_semi_major"} optional parameter value.
      * Valid values range from 0 to infinity. Units are {@linkplain SI#METRE metres}.
      */
-    static final ParameterDescriptor<Double> TGT_SEMI_MAJOR;
+    public static final ParameterDescriptor<Double> TGT_SEMI_MAJOR;
 
     /**
      * The operation parameter descriptor for the {@code "src_semi_minor"} optional parameter value.
      * Valid values range from 0 to infinity. Units are {@linkplain SI#METRE metres}.
      */
-    static final ParameterDescriptor<Double> TGT_SEMI_MINOR;
+    public static final ParameterDescriptor<Double> TGT_SEMI_MINOR;
 
     static {
         final ParameterBuilder builder = builder().setCodeSpace(Citations.OGC, Constants.OGC).setRequired(false);

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java?rev=1712369&r1=1712368&r2=1712369&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java [UTF-8] Tue Nov  3 18:52:48 2015
@@ -67,7 +67,7 @@ public final class Molodensky extends Ge
      * in replacement of {@link #TGT_SEMI_MAJOR}.
      * Units are {@linkplain SI#METRE metres}.
      */
-    private static final ParameterDescriptor<Double> AXIS_LENGTH_DIFFERENCE;
+    public static final ParameterDescriptor<Double> AXIS_LENGTH_DIFFERENCE;
 
     /**
      * The operation parameter descriptor for the <cite>Flattening difference</cite> optional
@@ -75,12 +75,12 @@ public final class Molodensky extends Ge
      * replacement of {@link #TGT_SEMI_MINOR}.
      * Valid values range from -1 to +1, {@linkplain Unit#ONE dimensionless}.
      */
-    private static final ParameterDescriptor<Double> FLATTENING_DIFFERENCE;
+    public static final ParameterDescriptor<Double> FLATTENING_DIFFERENCE;
 
     /**
      * The group of all parameters expected by this coordinate operation.
      */
-    static final ParameterDescriptorGroup PARAMETERS;
+    public static final ParameterDescriptorGroup PARAMETERS;
     static {
         final ParameterBuilder builder = builder();
         AXIS_LENGTH_DIFFERENCE = builder.addName("Semi-major axis length difference").create(Double.NaN, SI.METRE);
@@ -197,20 +197,21 @@ public final class Molodensky extends Ge
          * in the geocentric domain does not have any impact on the input/output
          * geographic coordinates.
          */
-        final double a = values.doubleValue(SRC_SEMI_MAJOR);
-        final double b = values.doubleValue(SRC_SEMI_MINOR);
+        final double semiMajor = values.doubleValue(SRC_SEMI_MAJOR);
+        final double semiMinor = values.doubleValue(SRC_SEMI_MINOR);
         final double ta, tb;
         double d = values.doubleValue(AXIS_LENGTH_DIFFERENCE);
-        ta = Double.isNaN(d) ? values.doubleValue(TGT_SEMI_MAJOR) : a + d;
+        ta = Double.isNaN(d) ? values.doubleValue(TGT_SEMI_MAJOR) : semiMajor + d;
         d = values.doubleValue(FLATTENING_DIFFERENCE);
         if (Double.isNaN(d)) {
             tb = values.doubleValue(TGT_SEMI_MINOR);
         } else {
-            tb = ta*(b/a - d);
+            tb = ta*(semiMinor/semiMajor - d);
         }
-        final double dx = values.doubleValue(TX);
-        final double dy = values.doubleValue(TY);
-        final double dz = values.doubleValue(TZ);
+        final double tX = values.doubleValue(TX);
+        final double tY = values.doubleValue(TY);
+        final double tZ = values.doubleValue(TZ);
+//      ΔFlattening    =  (ta-tb)/ta - (semiMajor-semiMinor)/semiMajor;
         throw new UnsupportedOperationException("Not supported yet.");
     }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java?rev=1712369&r1=1712368&r2=1712369&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java [UTF-8] Tue Nov  3 18:52:48 2015
@@ -328,11 +328,15 @@ class ConcatenatedTransform extends Abst
      */
     private static String getName(final MathTransform transform) {
         if (transform instanceof AbstractMathTransform) {
-            ParameterValueGroup params = ((AbstractMathTransform) transform).getParameterValues();
-            if (params != null) {
-                String name = params.getDescriptor().getName().getCode();
-                if (name != null && !(name = name.trim()).isEmpty()) {
-                    return name;
+            ParameterValueGroup params;
+            params = ((AbstractMathTransform) transform).getContextualParameters();
+            if (params == null) {
+                params = ((AbstractMathTransform) transform).getParameterValues();
+                if (params != null) {
+                    String name = params.getDescriptor().getName().getCode();
+                    if (name != null && !(name = name.trim()).isEmpty()) {
+                        return name;
+                    }
                 }
             }
         }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java?rev=1712369&r1=1712368&r2=1712369&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java [UTF-8] Tue Nov  3 18:52:48 2015
@@ -491,6 +491,8 @@ public class ContextualParameters extend
      * @throws FactoryException if an error occurred while creating a math transform instance.
      *
      * @see org.apache.sis.referencing.operation.projection.NormalizedProjection#createMapProjection(MathTransformFactory)
+     * @see EllipsoidalToCartesianTransform#createGeodeticConversion(MathTransformFactory)
+     * @see MolodenskyTransform#createGeodeticTransformation(MathTransformFactory)
      */
     @SuppressWarnings("AssignmentToForLoopParameter")
     public synchronized MathTransform completeTransform(final MathTransformFactory factory, final MathTransform kernel)

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidalToCartesianTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidalToCartesianTransform.java?rev=1712369&r1=1712368&r2=1712369&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidalToCartesianTransform.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidalToCartesianTransform.java [UTF-8] Tue Nov  3 18:52:48 2015
@@ -213,18 +213,20 @@ public class EllipsoidalToCartesianTrans
      * @param withHeight {@code true} if geographic coordinates include an ellipsoidal height (i.e. are 3-D),
      *                   or {@code false} if they are only 2-D.
      */
-    public EllipsoidalToCartesianTransform(final double semiMajor, final double semiMinor, final Unit<Length> unit, final boolean withHeight) {
+    public EllipsoidalToCartesianTransform(final double semiMajor, final double semiMinor,
+            final Unit<Length> unit, final boolean withHeight)
+    {
         ArgumentChecks.ensureStrictlyPositive("semiMajor", semiMajor);
         ArgumentChecks.ensureStrictlyPositive("semiMinor", semiMinor);
         b = semiMinor / semiMajor;
         excentricitySquared = 1 - (b * b);
         useIterations = (excentricitySquared >= EXCENTRICITY_THRESHOLD * EXCENTRICITY_THRESHOLD);
         this.withHeight = withHeight;
-        context = new ContextualParameters(GeographicToGeocentric.PARAMETERS, withHeight ? 4 : 3, 4);
         /*
          * Copy parameters to the ContextualParameter. Those parameters are not used directly
          * by EllipsoidToCartesian, but we need to store them in case the user asks for them.
          */
+        context = new ContextualParameters(GeographicToGeocentric.PARAMETERS, withHeight ? 4 : 3, 4);
         context.getOrCreate(SEMI_MAJOR).setValue(semiMajor, unit);
         context.getOrCreate(SEMI_MINOR).setValue(semiMinor, unit);
         if (!withHeight) {
@@ -233,7 +235,7 @@ public class EllipsoidalToCartesianTrans
         /*
          * Prepare two affine transforms to be executed before and after this EllipsoidalToCartesianTransform:
          *
-         *   - A "normalization" transform for conversing degrees to radians and normalizing the height,
+         *   - A "normalization" transform for converting degrees to radians and normalizing the height,
          *   - A "denormalization" transform for scaling (X,Y,Z) to the semi-major axis length.
          */
         context.normalizeGeographicInputs(0);
@@ -322,7 +324,6 @@ public class EllipsoidalToCartesianTrans
      * @throws FactoryException if an error occurred while creating a transform.
      *
      * @see ContextualParameters#completeTransform(MathTransformFactory, MathTransform)
-     * @see org.apache.sis.referencing.operation.projection.NormalizedProjection#createMapProjection(MathTransformFactory)
      */
     public MathTransform createGeodeticConversion(final MathTransformFactory factory) throws FactoryException {
         return context.completeTransform(factory, this);
@@ -338,7 +339,7 @@ public class EllipsoidalToCartesianTrans
      *         <cite>normalize</cite> → {@code this} → <cite>denormalize</cite> transforms.
      */
     @Override
-    protected final ContextualParameters getContextualParameters() {
+    protected ContextualParameters getContextualParameters() {
         return context;
     }
 

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java?rev=1712369&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java [UTF-8] Tue Nov  3 18:52:48 2015
@@ -0,0 +1,549 @@
+/*
+ * 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.referencing.operation.transform;
+
+import java.util.Arrays;
+import java.io.Serializable;
+import javax.measure.unit.Unit;
+import javax.measure.quantity.Length;
+import org.opengis.util.FactoryException;
+import org.opengis.geometry.DirectPosition;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.internal.referencing.provider.Molodensky;
+import org.apache.sis.internal.referencing.provider.AbridgedMolodensky;
+import org.apache.sis.internal.referencing.provider.MapProjection;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.util.Debug;
+
+import static java.lang.Math.*;
+
+
+/**
+ * Two- or three-dimensional datum shift using the (potentially abridged) Molodensky transformation.
+ * The Molodensky transformation (EPSG:9604) and the abridged Molodensky transformation (EPSG:9605)
+ * transform geographic points from one geographic coordinate reference system to another (a datum shift).
+ * The Molodensky formulas are approximations of <cite>Geocentric translation (geographic domain)</cite>
+ * transformations (EPSG:1035 and 9603), but performed directly on geographic coordinates without
+ * Geographic/Geocentric conversions.
+ *
+ * <div class="section">Number of dimensions</div>
+ * {@code MolodenskyTransform}s works conceptually on three-dimensional coordinates, but the ellipsoidal height
+ * can be omitted resulting in two-dimensional coordinates. No dimension other than 2 or 3 are allowed.
+ * <ul>
+ *   <li>If the height is omitted from the input coordinates ({@code source3D} = {@code false}),
+ *       then the {@linkplain #getSourceDimensions() source dimensions} is 2 and the height is
+ *       assumed to be zero.</li>
+ *   <li>If the height is omitted from the output coordinates ({@code target3D} = {@code false}),
+ *       then the {@linkplain #getTargetDimensions() target dimensions} is 2 and the computed
+ *       height (typically non-zero even if the input height was zero) is lost.</li>
+ * </ul>
+ *
+ * <div class="section">Units of measurement</div>
+ * This {@code MolodenskyTransform} class expects ordinate values if the following order and units:
+ * <ol>
+ *   <li>longitudes in <strong>radians</strong> relative to the prime meridian (usually Greenwich),</li>
+ *   <li>latitudes in <strong>radians</strong>,</li>
+ *   <li>optionally heights above the ellipsoid, in same units than the ellipsoids axes.</li>
+ * </ol>
+ *
+ * For converting geographic coordinates in degrees, {@code MolodenskyTransform} instances
+ * need to be concatenated with the following affine transforms:
+ *
+ * <ul>
+ *   <li><cite>Normalization</cite> before {@code MolodenskyTransform}:<ul>
+ *     <li>Conversion of (λ,φ) from degrees to radians</li>
+ *   </ul></li>
+ *   <li><cite>Denormalization</cite> after {@code MolodenskyTransform}:<ul>
+ *     <li>Conversion of (λ,φ) from radians to degrees</li>
+ *   </ul></li>
+ * </ul>
+ *
+ * The full conversion chain including the above affine transforms
+ * can be created by {@link #createGeodeticTransformation(MathTransformFactory)}.
+ * Alternatively, the {@link #createGeodeticTransformation(Ellipsoid, Ellipsoid, boolean)}
+ * convenience method can also be used.
+ *
+ * @author  Rueben Schulz (UBC)
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @author  Rémi Maréchal (Geomatys)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ */
+public class MolodenskyTransform extends AbstractMathTransform implements Serializable {
+    /**
+     * Serial number for inter-operability with different versions.
+     */
+    private static final long serialVersionUID = 7206439437113286122L;
+
+    /**
+     * The value of 1/sin(1″) multiplied by the conversion factor from arc-seconds to radians (π/180)/(60⋅60).
+     * This is the final multiplication factor for Δλ and Δφ.
+     */
+    private static final double ANGULAR_SCALE = 1.00000000000391744;
+
+    /**
+     * A mask value for {@link #type}.
+     * <ul>
+     *   <li>If set, the target coordinates are three-dimensional.</li>
+     *   <li>If unset, the target coordinates are two-dimensional.</li>
+     * </ul>
+     */
+    private static final byte TARGET_DIMENSION_MASK = 1;
+
+    /**
+     * A mask value for {@link #type}.
+     * <ul>
+     *   <li>If set, the source coordinates are three-dimensional.</li>
+     *   <li>If unset, the source coordinates are two-dimensional.</li>
+     * </ul>
+     *
+     * This value <strong>must</strong> be equals to {@code TARGET_DIMENSION_MASK << 1}.
+     * This is required by the {@link #inverse()} method.
+     */
+    private static final byte SOURCE_DIMENSION_MASK = 2;
+
+    /**
+     * A mask value for {@link #type}. If set, then the Molodensky transform
+     * is the inverse of some previously existing Molodensky transform.
+     */
+    private static final byte INVERSE_MASK = 4;
+
+    /**
+     * A mask value for {@link #type}.
+     * <ul>
+     *   <li>If set, the transform uses the abridged formulas.</li>
+     *   <li>If unset, the transform uses the complete formulas.</li>
+     * </ul>
+     */
+    private static final byte ABRIDGED_MASK = 8;
+
+    /**
+     * Bitwise combination of the {@code *_MASK} constants.
+     */
+    private final byte type;
+
+    /**
+     * X,Y,Z shift in units of the semi-major axis.
+     */
+    private final double tX, tY, tZ;
+
+    /**
+     * Semi-major axis lengths of the source ellipsoid.
+     */
+    private final double a;
+
+    /**
+     * Difference in the semi-major axes of the target and source ellipsoids.
+     * {@code da = target a - source a}.
+     */
+    private final double da;
+
+    /**
+     * Difference between the flattening of the target and source ellipsoids, opportunistically modified with
+     * additional terms. The value depends on whether this Molodensky transform is abridged or not:
+     * <ul>
+     *   <li>For Molodensky, this field is set to (b⋅Δf).</li>
+     *   <li>For Abridged Molodensky, this field is set to (a⋅Δf) + (f⋅Δa).</li>
+     * </ul>
+     *
+     * where Δf = <var>target flattening</var> - <var>source flattening</var>.
+     */
+    private final double dfm;
+
+    /**
+     * The square of excentricity: ℯ² = (a²-b²)/a² where
+     * <var>a</var> is the <cite>semi-major</cite> axis length and
+     * <var>b</var> is the <cite>semi-minor</cite> axis length.
+     */
+    private final double excentricitySquared;
+
+    /**
+     * The parameters used for creating this conversion.
+     * They are used for formatting <cite>Well Known Text</cite> (WKT) and error messages.
+     *
+     * @see #getContextualParameters()
+     */
+    private final ContextualParameters context;
+
+    /**
+     * Creates a Molodensky transform from the specified parameters.
+     * Angular units of input coordinates and output coordinates are <strong>radians</strong>.
+     *
+     * @param isAbridged  {@code true} for the abridged formula, or {@code false} for the complete one.
+     * @param semiMajor   The source semi-major axis length in meters.
+     * @param semiMinor   The source semi-minor axis length in meters.
+     * @param source3D    {@code true} if the source coordinates have a height.
+     * @param ΔSemiMajor  Target semi-major axis length minus source semi-major axis length.
+     * @param ΔFlattening Target ellipsoid flattening minus source ellipsoid flattening.
+     * @param target3D    {@code true} if the target coordinates have a height.
+     * @param tX          The geocentric <var>X</var> translation in meters.
+     * @param tY          The geocentric <var>Y</var> translation in meters.
+     * @param tZ          The geocentric <var>Z</var> translation in meters.
+     * @param unit        The unit of {@code semiMajor}, {@code semiMinor}, {@code ΔSemiMajor},
+     *                    {@code tX}, {@code tY} and {@code tZ} arguments.
+     *
+     * @see #create(boolean, double, double, boolean, double, double, boolean, double, double, double)
+     */
+    protected MolodenskyTransform(final boolean isAbridged,
+            final double semiMajor, final double semiMinor, final boolean source3D,
+            final double ΔSemiMajor, final double ΔFlattening, final boolean target3D,
+            final double tX, final double tY, final double  tZ, final Unit<Length> unit)
+    {
+        byte type = isAbridged ? ABRIDGED_MASK : 0;
+        if (source3D) type |= SOURCE_DIMENSION_MASK;
+        if (target3D) type |= TARGET_DIMENSION_MASK;
+        this.type = type;
+        this.a    = semiMajor;
+        this.da   = ΔSemiMajor;
+        this.tX   = tX;
+        this.tY   = tY;
+        this.tZ   = tZ;
+
+        dfm = isAbridged ? (semiMajor*ΔFlattening) + (semiMajor-semiMinor)*(ΔSemiMajor/semiMajor)
+                         : (semiMinor*ΔFlattening);
+        excentricitySquared = 1 - (semiMinor*semiMinor) / (semiMajor*semiMajor);
+        /*
+         * Copy parameters to the ContextualParameter. Those parameters are not used directly
+         * by EllipsoidToCartesian, but we need to store them in case the user asks for them.
+         * When both EPSG and OGC parameters exist for equivalent information, we use EPSG ones.
+         */
+        context = new ContextualParameters(isAbridged ? AbridgedMolodensky.PARAMETERS : Molodensky.PARAMETERS,
+                                           source3D ? 4 : 3, target3D ? 4 : 3);
+        if (source3D == target3D) {
+            context.getOrCreate(Molodensky.DIMENSION).setValue(source3D ? 3 : 2);
+        }
+        context.getOrCreate(Molodensky.TX)                    .setValue(tX,          unit);
+        context.getOrCreate(Molodensky.TY)                    .setValue(tY,          unit);
+        context.getOrCreate(Molodensky.TZ)                    .setValue(tZ,          unit);
+        context.getOrCreate(Molodensky.AXIS_LENGTH_DIFFERENCE).setValue(ΔSemiMajor,  unit);
+        context.getOrCreate(Molodensky.FLATTENING_DIFFERENCE) .setValue(ΔFlattening, Unit.ONE);
+        context.getOrCreate(Molodensky.SRC_SEMI_MAJOR)        .setValue(semiMajor,   unit);
+        context.getOrCreate(Molodensky.SRC_SEMI_MINOR)        .setValue(semiMinor,   unit);
+        /*
+         * Prepare two affine transforms to be executed before and after this MolodenskyTransform:
+         *
+         *   - A "normalization" transform for converting degrees to radians,
+         *   - A "denormalization" transform for for converting radians to degrees.
+         */
+        context.normalizeGeographicInputs(0);
+        context.denormalizeGeographicOutputs(0);
+        /*
+         * In the particular case of abridged Molondensky, there is some more terms than we can
+         * delegate to the matrices. We can not do that for the non-abridged formulas however.
+         */
+        if (target3D && isAbridged) {
+            context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION).convertAfter(2, null, -da);
+        }
+    }
+
+    /**
+     * Returns the sequence of <cite>normalization</cite> → {@code this} → <cite>denormalization</cite>
+     * transforms as a whole. The transform works with input and output coordinates in the following units:
+     *
+     * <ol>
+     *   <li>longitudes in degrees relative to the prime meridian (usually Greenwich),</li>
+     *   <li>latitudes in degrees,</li>
+     *   <li>optionally heights above the ellipsoid, in the units given to the constructor (usually metres).</li>
+     * </ol>
+     *
+     * @param  factory The factory to use for creating the transform.
+     * @return The transformation between geographic coordinates.
+     * @throws FactoryException if an error occurred while creating a transform.
+     *
+     * @see ContextualParameters#completeTransform(MathTransformFactory, MathTransform)
+     */
+    public MathTransform createGeodeticTransformation(final MathTransformFactory factory) throws FactoryException {
+        return context.completeTransform(factory, this);
+    }
+
+    /**
+     * Returns the parameters used for creating the complete transformation. Those parameters describe a sequence
+     * of <cite>normalize</cite> → {@code this} → <cite>denormalize</cite> transforms, <strong>not</strong>
+     * including {@linkplain org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes axis swapping}.
+     * Those parameters are used for formatting <cite>Well Known Text</cite> (WKT) and error messages.
+     *
+     * @return The parameters values for the sequence of
+     *         <cite>normalize</cite> → {@code this} → <cite>denormalize</cite> transforms.
+     */
+    @Override
+    protected ContextualParameters getContextualParameters() {
+        return context;
+    }
+
+    /**
+     * Returns a copy of internal parameter values of this {@code MolodenskyTransform} transform.
+     * The returned group contains parameter values for the number of dimensions and the excentricity.
+     *
+     * <div class="note"><b>Note:</b>
+     * This method is mostly for {@linkplain org.apache.sis.io.wkt.Convention#INTERNAL debugging purposes}
+     * since the isolation of non-linear parameters in this class is highly implementation dependent.
+     * Most GIS applications will instead be interested in the {@linkplain #getContextualParameters()
+     * contextual parameters}.</div>
+     *
+     * @return A copy of the internal parameter values for this transform.
+     */
+    @Debug
+    @Override
+    public ParameterValueGroup getParameterValues() {
+        final Parameters pg = Parameters.castOrWrap(getParameterDescriptors().createValue());
+        final int dimension = getSourceDimensions();
+        if (dimension == getTargetDimensions()) {
+            pg.getOrCreate(Molodensky.DIMENSION).setValue(dimension);
+        }
+        pg.getOrCreate(MapProjection.EXCENTRICITY).setValue(sqrt(excentricitySquared));
+        // TODO: add other parameters
+        return pg;
+    }
+
+    /**
+     * Returns a description of the internal parameters of this {@code MolodenskyTransform} transform.
+     * The returned group contains parameter descriptors for the number of dimensions and the excentricity.
+     *
+     * @return A description of the internal parameters.
+     */
+    @Debug
+    @Override
+    public ParameterDescriptorGroup getParameterDescriptors() {
+        return null; // TODO
+    }
+
+    /**
+     * Returns {@code true} if this transform is the identity one.
+     * Molodensky transform is considered identity (minus rounding errors) if:
+     *
+     * <ul>
+     *   <li>the X,Y,Z shift are zero,</li>
+     *   <li>the source and target axis length are the same,</li>
+     *   <li>the input and output dimension are the same.</li>
+     * </ul>
+     *
+     * @return {@code true} if this transform is the identity transform.
+     */
+    @Override
+    public boolean isIdentity() {
+        return tX == 0 && tY == 0 && tZ == 0 && da == 0 && dfm == 0 && getSourceDimensions() == getTargetDimensions();
+    }
+
+    /**
+     * Returns {@code true} if this Molodensky transform uses abridged formulas instead than the complete ones.
+     * This is the value of the {@code isAbridged} boolean argument given to the constructor.
+     *
+     * @return {@code true} if this transform uses abridged formulas.
+     */
+    public boolean isAbridged() {
+        return (type & ABRIDGED_MASK) != 0;
+    }
+
+    /**
+     * Gets the dimension of input points.
+     *
+     * @return The input dimension, which is 2 or 3.
+     */
+    @Override
+    public final int getSourceDimensions() {
+        return (type & SOURCE_DIMENSION_MASK) != 0 ? 3 : 2;
+    }
+
+    /**
+     * Gets the dimension of output points.
+     *
+     * @return The output dimension, which is 2 or 3.
+     */
+    @Override
+    public final int getTargetDimensions() {
+        return (type & TARGET_DIMENSION_MASK) != 0 ? 3 : 2;
+    }
+
+    /**
+     * Computes the derivative at the given location.
+     * This method relaxes a little bit the {@code MathTransform} contract by accepting two- or three-dimensional
+     * points even if the number of dimensions does not match the {@link #getSourceDimensions()} or
+     * {@link #getTargetDimensions()} values.
+     *
+     * @param  point The coordinate point where to evaluate the derivative.
+     * @return The derivative at the specified point (never {@code null}).
+     * @throws TransformException if the derivative can not be evaluated at the specified point.
+     */
+    @Override
+    public Matrix derivative(final DirectPosition point) throws TransformException {
+        final int dim = point.getDimension();
+        final boolean withHeight;
+        final double h;
+        switch (dim) {
+            default: throw mismatchedDimension("point", getSourceDimensions(), dim);
+            case 3:  withHeight = true;  h = point.getOrdinate(2); break;
+            case 2:  withHeight = false; h = 0; break;
+        }
+        return transform(point.getOrdinate(0), point.getOrdinate(1), h, null, 0, true, withHeight, withHeight);
+    }
+
+    /**
+     * Transforms the (λ,φ) or (λ,φ,<var>h</var>) coordinates between two geographic CRS,
+     * and optionally returns the derivative at that location.
+     *
+     * @return {@inheritDoc}
+     * @throws TransformException if the point can not be transformed or
+     *         if a problem occurred while calculating the derivative.
+     */
+    @Override
+    public Matrix transform(final double[] srcPts, final int srcOff,
+                            final double[] dstPts, final int dstOff,
+                            final boolean derivate) throws TransformException
+    {
+        final boolean source3D = (type & SOURCE_DIMENSION_MASK) != 0;
+        final boolean target3D = (type & TARGET_DIMENSION_MASK) != 0;
+        return transform(srcPts[srcOff], srcPts[srcOff+1], source3D ? srcPts[srcOff+2] : 0,
+                         dstPts, dstOff, derivate, source3D, target3D);
+    }
+
+    /**
+     * Implementation of {@link #transform(double[], int, double[], int, boolean)} with possibility
+     * to override whether the source and target coordinates are two- or three-dimensional.
+     *
+     * @param λ        Longitude (radians).
+     * @param φ        Latitude (radians).
+     * @param h        Height above the ellipsoid in unit of semi-major axis.
+     * @param dstPts   The array into which the transformed coordinate is returned.
+     *                 May be {@code null} if only the derivative matrix is desired.
+     * @param dstOff   The offset to the location of the transformed point that is stored in the destination array.
+     * @param derivate {@code true} for computing the derivative, or {@code false} if not needed.
+     * @throws TransformException if a point can not be transformed.
+     */
+    private Matrix transform(final double λ, final double φ, final double h, final double[] dstPts, final int dstOff,
+                             final boolean derivate, final boolean source3D, final boolean target3D) throws TransformException
+    {
+        transform(new double[] {λ, φ, h}, 0, dstPts, dstOff, 1);    // Temporary patch.
+        return null;  // TODO
+    }
+
+    /**
+     * Transforms the (λ,φ) or (λ,φ,<var>h</var>) coordinates between two geographic CRS.
+     * This method performs the same transformation than {@link #transform(double[], int, double[], int, boolean)},
+     * but the formulas are repeated here for performance reasons.
+     *
+     * @throws TransformException if a point can not be transformed.
+     */
+    @Override
+    public void transform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) throws TransformException {
+        final boolean abridged = (type & ABRIDGED_MASK)         != 0;
+        final boolean source3D = (type & SOURCE_DIMENSION_MASK) != 0;
+        final boolean target3D = (type & TARGET_DIMENSION_MASK) != 0;
+        int srcDecrement = 0;
+        int dstDecrement = 0;
+        int offFinal     = 0;
+        double[] dstFinal  = null;
+        if (srcPts == dstPts) {
+            final int srcDim = source3D ? 3 : 2;
+            final int dstDim = target3D ? 3 : 2;
+            switch (IterationStrategy.suggest(srcOff, srcDim, dstOff, dstDim, numPts)) {
+                case ASCENDING: {
+                    break;
+                }
+                case DESCENDING: {
+                    srcOff += (numPts-1) * srcDim;
+                    dstOff += (numPts-1) * dstDim;
+                    srcDecrement = 2 * srcDim;
+                    dstDecrement = 2 * dstDim;
+                    break;
+                }
+                default: {  // BUFFER_SOURCE, but also a reasonable default for any case.
+                    final int upper = srcOff + numPts*srcDim;
+                    srcPts = Arrays.copyOfRange(srcPts, srcOff, upper);
+                    srcOff = 0;
+                    break;
+                }
+                case BUFFER_TARGET: {
+                    dstFinal = dstPts;
+                    dstPts = new double[numPts * dstDim];
+                    offFinal = dstOff;
+                    dstOff = 0;
+                    break;
+                }
+            }
+        }
+        while (--numPts >= 0) {
+            final double λ     = srcPts[srcOff++];
+            final double φ     = srcPts[srcOff++];
+            final double h     = source3D ? srcPts[srcOff++] : 0;
+            final double sinλ  = sin(λ);
+            final double cosλ  = cos(λ);
+            final double sinφ  = sin(φ);
+            final double cosφ  = cos(φ);
+            final double sin2φ = sinφ * sinφ;
+            final double rν2   = 1 - excentricitySquared * sin2φ;
+            final double rν    = sqrt(rν2);
+            final double ρ1    = (1 - excentricitySquared) / (rν * rν2);
+            final double tXY   = tY*sinλ + tX*cosλ;
+            /*
+             * Abridged Molodensky formulas from EPSG guidance note:
+             *
+             *     ν   = a / (1 - ℯ²⋅sin²φ)^(1/2)
+             *     ρ   = a⋅(1 – ℯ²) / (1 – ℯ²⋅sin²φ)^(3/2)
+             *     Δλ″ = (-tX⋅sinλ + tY⋅cosλ) / (ν⋅cosφ⋅sin1″)
+             *     Δφ″ = (-tX⋅sinφ⋅cosλ - tY⋅sinφ⋅sinλ + tZ⋅cosφ + [a⋅Δf + f⋅Δa]⋅sin(2φ)) / (ρ⋅sin1″)
+             *     Δh  = tX⋅cosφ⋅cosλ + tY⋅cosφ⋅sinλ + tZ⋅sinφ + (a⋅Δf + f⋅Δa)⋅sin²φ - Δa
+             *
+             * we set:
+             *
+             *    dfm     = (a⋅Δf + f⋅Δa) in abridged case (b⋅Δf in non-abridged case)
+             *    sin(2φ) = 2⋅sin(φ)⋅cos(φ)
+             */
+            double ν = a / rν;
+            double ρ = a * ρ1;
+            double t = dfm * 2;
+            if (!abridged) {
+                ν += h;
+                ρ += h;
+                t = da*excentricitySquared/rν + dfm*(1/rν + 1/(rν*rν2));
+            }
+            dstPts[dstOff++] = λ + ANGULAR_SCALE * (tY*cosλ - tX*sinλ) / (ν*cosφ);
+            dstPts[dstOff++] = φ + ANGULAR_SCALE * ((t*cosφ - tXY)*sinφ + tZ*cosφ) / ρ;
+            if (target3D) {
+                t = dfm * sin2φ;
+                if (!abridged) {
+                    t = t/rν - da*rν;
+                    // Note: in abridged Molodensky case, 'da' subtracted by the denormalization matrix.
+                }
+                dstPts[dstOff++] = h + tXY*cosφ + tZ*sinφ + t;
+            }
+            srcOff -= srcDecrement;
+            dstOff -= dstDecrement;
+        }
+        /*
+         * If the transformation result has been stored in a temporary
+         * array, copies the array content to its final location now.
+         */
+        if (dstFinal != null) {
+            System.arraycopy(dstPts, 0, dstFinal, offFinal, dstPts.length);
+        }
+    }
+
+    /*
+     * NOTE: we do not bother to override the methods expecting a 'float' array because those methods should
+     *       be rarely invoked. Since there is usually LinearTransforms before and after this transform, the
+     *       conversion between float and double will be handle by those LinearTransforms.   If nevertheless
+     *       this EllipsoidalToCartesianTransform is at the beginning or the end of a transformation chain,
+     *       the method inherited from the subclass will work (even if slightly slower).
+     */
+
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/GeocentricTranslationTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/GeocentricTranslationTest.java?rev=1712369&r1=1712368&r2=1712369&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/GeocentricTranslationTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/GeocentricTranslationTest.java [UTF-8] Tue Nov  3 18:52:48 2015
@@ -56,7 +56,8 @@ public final strictfp class GeocentricTr
      * Returns the sample point for a step in the example given by the EPSG guidance note.
      *
      * <blockquote><b>Source:</b>
-     * §2.4.3.5 <cite>Three-parameter geocentric translations</cite> in
+     * §2.4.3.5 <cite>Three-parameter geocentric translations</cite> and
+     * §2.4.4.2 <cite>Abridged Molodensky transformation</cite> in
      * IOGP Publication 373-7-2 – Geomatics Guidance Note number 7, part 2 – April 2015
      * </blockquote>
      *
@@ -69,7 +70,7 @@ public final strictfp class GeocentricTr
      *   <b>Step 4:</b> Target point in ED50:   53°48'36.565"N,  02'07"51.477"E,  28.02 metres.
      * </pre>
      *
-     * @param  step The step as a value from 1 to 4 inclusive.
+     * @param  step The step as a value from 1 to 5 inclusive.
      * @return The sample point at the given step.
      */
     public static double[] samplePoint(final int step) {
@@ -89,11 +90,16 @@ public final strictfp class GeocentricTr
                          140349.83,                 // Y: Toward 90° east
                         5124421.30                  // Z: Toward north pole
                     };
-            case 4: return new double[] {
+            case 4: return new double[] {           // Result according geocentric translation
                          2 + ( 7 + 51.477/60)/60,   // λ: Longitude
                         53 + (48 + 36.565/60)/60,   // φ: Latitude
                         28.02                       // h: Height
                     };
+            case 5: return new double[] {           // Result according abridged Molodensky
+                         2 + ( 7 + 51.477/60)/60,   // λ: Longitude
+                        53 + (48 + 36.563/60)/60,   // φ: Latitude
+                        28.091                      // h: Height
+                    };
             default: throw new AssertionError(step);
         }
     }
@@ -107,7 +113,8 @@ public final strictfp class GeocentricTr
     public static double precision(final int step) {
         switch (step) {
             case 1:
-            case 4: return 0.001 / 60 / 60 / 2;     // Half the precision of (λ,φ) values given by EPSG
+            case 4:
+            case 5: return 0.001 / 60 / 60 / 2;     // Half the precision of (λ,φ) values given by EPSG
             case 2: return 0.001 / 2;               // Half the precision for (X,Y,Z) values given by EPSG
             case 3: return 0.01 / 2;                // Half the precision for (X,Y,Z) values given by EPSG
             default: throw new AssertionError(step);

Added: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MolodenskyTransformTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MolodenskyTransformTest.java?rev=1712369&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MolodenskyTransformTest.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MolodenskyTransformTest.java [UTF-8] Tue Nov  3 18:52:48 2015
@@ -0,0 +1,137 @@
+/*
+ * 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.referencing.operation.transform;
+
+import javax.measure.unit.SI;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.datum.Ellipsoid;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.referencing.CommonCRS;
+
+import static java.lang.StrictMath.toRadians;
+
+// Test dependencies
+import org.apache.sis.internal.referencing.provider.GeocentricTranslationTest;
+import org.apache.sis.test.DependsOnMethod;
+import org.apache.sis.test.DependsOn;
+import org.junit.Test;
+
+import static org.apache.sis.test.Assert.*;
+
+
+/**
+ * Tests {@link MolodenskyTransform}.
+ *
+ * @author  Tara Athan
+ * @author  Martin Desruisseaux (Geomatys)
+ * @author  Rémi Maréchal (Geomatys)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ */
+@DependsOn({
+    CoordinateDomainTest.class,
+    ContextualParametersTest.class
+})
+public final strictfp class MolodenskyTransformTest extends MathTransformTestCase {
+    /**
+     * Creates a Molodensky transform for a datum shift from WGS84 to ED50.
+     * Tolerance thresholds are also initialized.
+     *
+     * @throws FactoryException if an error occurred while creating a transform step.
+     */
+    private void create(final boolean abridged) throws FactoryException {
+        final Ellipsoid source = CommonCRS.WGS84.ellipsoid();
+        final Ellipsoid target = CommonCRS.ED50.ellipsoid();
+        final double a = source.getSemiMajorAxis();
+        final double b = source.getSemiMinorAxis();
+        transform = new MolodenskyTransform(abridged, a, b, true, target.getSemiMajorAxis() - a,
+                1/target.getInverseFlattening() - 1/source.getInverseFlattening(), true, 84.87, 96.49, 116.95, SI.METRE)
+                .createGeodeticTransformation(DefaultFactories.forBuildin(MathTransformFactory.class));
+
+        final double delta = toRadians(100.0 / 60) / 1852;          // Approximatively 100 metres
+        derivativeDeltas = new double[] {delta, delta, 100};        // (Δλ, Δφ, Δh)
+        tolerance  = GeocentricTranslationTest.precision(1);        // Half the precision of target sample point
+        zTolerance = GeocentricTranslationTest.precision(3);        // Required precision for h
+        zDimension = new int[] {2};                                 // Dimension of h where to apply zTolerance
+        assertFalse(transform.isIdentity());
+    }
+
+    /**
+     * Tests using the sample point given by the EPSG guide.
+     *
+     * <ul>
+     *   <li>Source point in WGS84: 53°48'33.820"N, 02°07'46.380"E, 73.00 metres.</li>
+     *   <li>Target point in ED50:  53°48'36.565"N, 02'07"51.477"E, 28.02 metres.</li>
+     *   <li>Datum shift: dX = +84.87m, dY = +96.49m, dZ = +116.95m.</li>
+     * </ul>
+     *
+     * @throws FactoryException if an error occurred while creating a transform step.
+     * @throws TransformException if the transformation failed.
+     */
+    @Test
+    public void testAbridgedMolodensky() throws FactoryException, TransformException {
+        isInverseTransformSupported = false;
+        isDerivativeSupported = false;
+        create(true);
+        validate();
+        verifyTransform(GeocentricTranslationTest.samplePoint(1),
+                        GeocentricTranslationTest.samplePoint(5));
+    }
+
+    /**
+     * Tests using the same EPSG example than the one provided in {@link EllipsoidalToCartesianTransformTest}.
+     *
+     * <ul>
+     *   <li>Source point in WGS84: 53°48'33.820"N, 02°07'46.380"E, 73.00 metres.</li>
+     *   <li>Target point in ED50:  53°48'36.565"N, 02'07"51.477"E, 28.02 metres.</li>
+     *   <li>Datum shift: dX = +84.87m, dY = +96.49m, dZ = +116.95m.</li>
+     * </ul>
+     *
+     * @throws FactoryException if an error occurred while creating a transform step.
+     * @throws TransformException if the transformation failed.
+     */
+    @Test
+    @DependsOnMethod("testAbridgedMolodensky")
+    public void testMolodensky() throws FactoryException, TransformException {
+        isInverseTransformSupported = false;
+        isDerivativeSupported = false;
+        create(false);
+        validate();
+        verifyTransform(GeocentricTranslationTest.samplePoint(1),
+                        GeocentricTranslationTest.samplePoint(4));
+    }
+
+    /**
+     * Tests conversion of random points. The test is performed with the Molodensky transform,
+     * not the abridged one, because the errors caused by the abridged Molondeky method is too
+     * high for this test.
+     *
+     * @throws FactoryException if an error occurred while creating a transform step.
+     * @throws TransformException if a conversion failed.
+     */
+    @Test
+    @DependsOnMethod("testMolodensky")
+    public void testRandomPoints() throws FactoryException, TransformException {
+        isInverseTransformSupported = false;
+        isDerivativeSupported = false;
+        create(false);
+        verifyInDomain(CoordinateDomain.GEOGRAPHIC, 208129394);
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MolodenskyTransformTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MolodenskyTransformTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1712369&r1=1712368&r2=1712369&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] Tue Nov  3 18:52:48 2015
@@ -116,6 +116,7 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.operation.transform.MathTransformsTest.class,
     org.apache.sis.referencing.operation.transform.ContextualParametersTest.class,
     org.apache.sis.referencing.operation.transform.EllipsoidalToCartesianTransformTest.class,
+    org.apache.sis.referencing.operation.transform.MolodenskyTransformTest.class,
     org.apache.sis.referencing.operation.DefaultFormulaTest.class,
     org.apache.sis.referencing.operation.DefaultOperationMethodTest.class,
     org.apache.sis.referencing.operation.AbstractSingleOperationTest.class,



Mime
View raw message