sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1813279 - in /sis/branches/JDK8/core/sis-referencing/src: main/java/org/apache/sis/internal/referencing/j2d/ main/java/org/apache/sis/referencing/operation/transform/ test/java/org/apache/sis/referencing/operation/transform/ test/java/org/...
Date Wed, 25 Oct 2017 13:08:10 GMT
Author: desruisseaux
Date: Wed Oct 25 13:08:10 2017
New Revision: 1813279

URL: http://svn.apache.org/viewvc?rev=1813279&view=rev
Log:
Add (for performance reason) a specialization for Molodensky transform in the particular case
where the transform is two-dimensional, abridged and without translation terms.
This is a first step before a bug fix about ellipsoid change when there is no Bursa-Wolf parameters.

Added:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbridgedMolodenskyTransform2D.java
      - copied, changed from r1813195, sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/AbridgedMolodenskyTransformTest.java
  (with props)
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AffineMatrix.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform2D.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformTestCase.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/j2d/AffineMatrix.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AffineMatrix.java?rev=1813279&r1=1813278&r2=1813279&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AffineMatrix.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AffineMatrix.java
[UTF-8] Wed Oct 25 13:08:10 2017
@@ -31,7 +31,7 @@ import org.apache.sis.util.ArgumentCheck
  * used in double-double arithmetic.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.5
+ * @version 0.8
  * @since   0.5
  * @module
  */
@@ -181,6 +181,26 @@ final class AffineMatrix implements Exte
     }
 
     /**
+     * Compares this matrix with the given object for equality, including error terms (if
any).
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj instanceof AffineMatrix) {
+            final AffineMatrix other = (AffineMatrix) obj;
+            return transform.equals(other.transform) && Arrays.equals(errors, other.errors);
+        }
+        return false;
+    }
+
+    /**
+     * Returns a hash code value for this matrix.
+     */
+    @Override
+    public int hashCode() {
+        return (transform.hashCode() * 31 + Arrays.hashCode(errors)) ^ (int) serialVersionUID;
+    }
+
+    /**
      * Returns a string representation of this matrix.
      *
      * @return a string representation of this matrix.

Copied: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbridgedMolodenskyTransform2D.java
(from r1813195, 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/AbridgedMolodenskyTransform2D.java?p2=sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbridgedMolodenskyTransform2D.java&p1=sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java&r1=1813195&r2=1813279&rev=1813279&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbridgedMolodenskyTransform2D.java
[UTF-8] Wed Oct 25 13:08:10 2017
@@ -16,389 +16,97 @@
  */
 package org.apache.sis.referencing.operation.transform;
 
-import java.util.Arrays;
-import javax.measure.Unit;
-import org.opengis.util.FactoryException;
-import org.opengis.parameter.ParameterDescriptorGroup;
+import java.io.IOException;
+import java.io.ObjectInputStream;
 import org.opengis.referencing.datum.Ellipsoid;
-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.parameter.Parameters;
-import org.apache.sis.measure.Units;
-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.
+ * Two-dimensional abridged Molodensky transform with all translation terms fixed to zero.
+ * This implementation performs only a change of ellipsoid. It provides nothing new compared
+ * to {@link MolodenskyFormula}, except performance.
+ *
+ * <p><b>Note:</b> this transform is yet more abridged than standard "abridged
Molondensky" transform since
+ * it sets all translation terms to zero. A better class name could be "Very abridged Molodensky
transform".
+ * For the usual abridged Molondensky with non-zero translation terms, use the parent class.</p>
  *
- * <p>{@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.</p>
- * <ul>
- *   <li>If the height is omitted from the input coordinates ({@code isSource3D} =
{@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 isTarget3D} =
{@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>
- *
- * The transform expect ordinate values if the following order:
- * <ol>
- *   <li>longitudes (λ) relative to the prime meridian (usually Greenwich),</li>
- *   <li>latitudes (φ),</li>
- *   <li>optionally heights above the ellipsoid (h).</li>
- * </ol>
- *
- * The units of measurements depend on how the {@code MathTransform} has been created:
- * <ul>
- *   <li>{@code MolodenskyTransform} instances created directly by the constructor
work with angular values in radians.
- *       That constructor is reserved for subclasses only.</li>
- *   <li>Transforms created by the {@link #createGeodeticTransformation createGeodeticTransformation(…)}
static method
- *       work with angular values in degrees and heights in the same units than the <strong>source</strong>
ellipsoid
- *       axes (usually metres).</li>
- * </ul>
- *
- * <div class="section">Comparison of Molodensky and geocentric translation</div>
- * Compared to the <cite>"Geocentric translation (geographic domain)"</cite>
method,
- * the Molodensky method has errors usually within a few centimetres.
- * The Abridged Molodensky method has more noticeable errors, of a few tenths of centimetres.
- *
- * <p>Another difference between Molodensky and geocentric translation methods is their
behavior when crossing the
- * anti-meridian. If a datum shift causes a longitude to cross the anti-meridian (e.g. 179.999°
become 180.001°),
- * the Molodensky method will keep 180.001° as-is while the geocentric translation method
will wrap the longitude
- * to -179.999°. Such wrap-around behavior may or may not be desired, depending on the applications.</p>
- *
- * @author  Rueben Schulz (UBC)
- * @author  Martin Desruisseaux (IRD, Geomatys)
- * @author  Rémi Maréchal (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
  * @version 0.8
- * @since   0.7
+ * @since   0.8
  * @module
  */
-public class MolodenskyTransform extends MolodenskyFormula {
+final class AbridgedMolodenskyTransform2D extends MolodenskyTransform2D {
     /**
      * Serial number for inter-operability with different versions.
      */
-    private static final long serialVersionUID = 7206439437113286122L;
+    private static final long serialVersionUID = -7732503837408555590L;
 
     /**
-     * Internal parameter descriptor, used only for debugging purpose.
-     * Created only when first needed.
-     *
-     * @see #getParameterDescriptors()
+     * A combination of {@link #ANGULAR_SCALE} with other fields in an expression that become
constants
+     * because of the simplification applied in this {@code AbridgedMolodenskyTransform2D}.
      */
-    @Debug
-    private static ParameterDescriptorGroup DESCRIPTOR;
+    private transient double scale;
 
     /**
-     * The inverse of this Molodensky transform.
-     *
-     * @see #inverse()
+     * Constructs a 2D transform.
      */
-    private final MolodenskyTransform inverse;
-
-    /**
-     * Creates a Molodensky transform from the specified parameters.
-     * This {@code MolodenskyTransform} class expects ordinate values in 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 source
ellipsoid 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>
-     *
-     * After {@code MolodenskyTransform} construction,
-     * the full conversion chain including the above affine transforms can be created by
-     * <code>{@linkplain #getContextualParameters()}.{@linkplain ContextualParameters#completeTransform
-     * completeTransform}(factory, this)}</code>.
-     *
-     * @param source       the source ellipsoid.
-     * @param isSource3D   {@code true} if the source coordinates have a height.
-     * @param target       the target ellipsoid.
-     * @param isTarget3D   {@code true} if the target coordinates have a height.
-     * @param tX           the geocentric <var>X</var> translation in same units
than the source ellipsoid axes.
-     * @param tY           the geocentric <var>Y</var> translation in same units
than the source ellipsoid axes.
-     * @param tZ           the geocentric <var>Z</var> translation in same units
than the source ellipsoid axes.
-     * @param isAbridged   {@code true} for the abridged formula, or {@code false} for the
complete one.
-     *
-     * @see #createGeodeticTransformation(MathTransformFactory, Ellipsoid, boolean, Ellipsoid,
boolean, double, double, double, boolean)
-     */
-    protected MolodenskyTransform(final Ellipsoid source, final boolean isSource3D,
-                                  final Ellipsoid target, final boolean isTarget3D,
-                                  final double tX, final double tY, final double tZ,
-                                  final boolean isAbridged)
-    {
-        super(source, isSource3D, target, isTarget3D, tX, tY, tZ, null, isAbridged,
-                isAbridged ? AbridgedMolodensky.PARAMETERS : Molodensky.PARAMETERS);
-        if (!isSource3D && !isTarget3D) {
-            inverse = new MolodenskyTransform2D(this, source, target);
-        } else {
-            inverse = new MolodenskyTransform(this, source, target);
-        }
+    AbridgedMolodenskyTransform2D(final Ellipsoid source, final Ellipsoid target) {
+        super(source, target, 0, 0, 0, true);
+        computeTransientFields();
     }
 
     /**
-     * Constructs the inverse of a Molodensky transform.
+     * Constructs the inverse of a 2D transform.
      *
      * @param inverse  the transform for which to create the inverse.
      * @param source   the source ellipsoid of the given {@code inverse} transform.
      * @param target   the target ellipsoid of the given {@code inverse} transform.
      */
-    MolodenskyTransform(final MolodenskyTransform inverse, final Ellipsoid source, final
Ellipsoid target) {
-        super(inverse, source, target, inverse.context.getDescriptor());
-        this.inverse = inverse;
+    AbridgedMolodenskyTransform2D(final MolodenskyTransform inverse, final Ellipsoid source,
final Ellipsoid target) {
+        super(inverse, source, target);
+        computeTransientFields();
     }
 
     /**
-     * Invoked by constructor and by {@link #getParameterValues()} for setting all parameters
other than axis lengths.
+     * Invoked on deserialization for restoring the {@link #scale} field.
      *
-     * @param  pg         where to set the parameters.
-     * @param  semiMinor  ignored.
-     * @param  unit       the unit of measurement to declare.
-     * @param  Δf         the flattening difference to set, or NaN if this method should
fetch that value itself.
+     * @param  in  the input stream from which to deserialize a math transform.
+     * @throws IOException if an I/O error occurred while reading or if the stream contains
invalid data.
+     * @throws ClassNotFoundException if the class serialized on the stream is not on the
classpath.
      */
-    @Override
-    final void completeParameters(final Parameters pg, final double semiMinor, final Unit<?>
unit, double Δf) {
-        if (Double.isNaN(Δf)) {
-            Δf = context.doubleValue(Molodensky.FLATTENING_DIFFERENCE);
-        }
-        super.completeParameters(pg, semiMinor, unit, Δf);
-        pg.getOrCreate(Molodensky.TX)                    .setValue(tX, unit);
-        pg.getOrCreate(Molodensky.TY)                    .setValue(tY, unit);
-        pg.getOrCreate(Molodensky.TZ)                    .setValue(tZ, unit);
-        pg.getOrCreate(Molodensky.AXIS_LENGTH_DIFFERENCE).setValue(Δa, unit);
-        pg.getOrCreate(Molodensky.FLATTENING_DIFFERENCE) .setValue(Δf, Units.UNITY);
-        if (pg != context) {
-            pg.parameter("abridged").setValue(isAbridged);  // Only in internal parameters.
-        }
+    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException
{
+        in.defaultReadObject();
+        computeTransientFields();
     }
 
     /**
-     * Creates a transformation between two from geographic CRS. This factory method combines
the
-     * {@code MolodenskyTransform} instance with the steps needed for converting values between
-     * degrees to radians. The transform works with input and output coordinates in the following
units:
-     *
-     * <ol>
-     *   <li>longitudes in <strong>degrees</strong> relative to the prime
meridian (usually Greenwich),</li>
-     *   <li>latitudes in <strong>degrees</strong>,</li>
-     *   <li>optionally heights above the ellipsoid, in same units than the source
ellipsoids axes.</li>
-     * </ol>
-     *
-     * @param  factory      the factory to use for creating the transform.
-     * @param  source       the source ellipsoid.
-     * @param  isSource3D   {@code true} if the source coordinates have a height.
-     * @param  target       the target ellipsoid.
-     * @param  isTarget3D   {@code true} if the target coordinates have a height.
-     * @param  tX           the geocentric <var>X</var> translation in same units
than the source ellipsoid axes.
-     * @param  tY           the geocentric <var>Y</var> translation in same units
than the source ellipsoid axes.
-     * @param  tZ           the geocentric <var>Z</var> translation in same units
than the source ellipsoid axes.
-     * @param  isAbridged   {@code true} for the abridged formula, or {@code false} for the
complete one.
-     * @return the transformation between geographic coordinates in degrees.
-     * @throws FactoryException if an error occurred while creating a transform.
+     * Computes the {@link #scale} field from existing fields.
      */
-    public static MathTransform createGeodeticTransformation(final MathTransformFactory factory,
-            final Ellipsoid source, final boolean isSource3D,
-            final Ellipsoid target, final boolean isTarget3D,
-            final double tX, final double tY, final double tZ,
-            final boolean isAbridged) throws FactoryException
-    {
-        final MolodenskyTransform tr;
-        if (!isSource3D && !isTarget3D) {
-            tr = new MolodenskyTransform2D(source, target, tX, tY, tZ, isAbridged);
-        } else {
-            tr = new MolodenskyTransform(source, isSource3D, target, isTarget3D, tX, tY,
tZ, isAbridged);
-        }
-        tr.inverse.context.completeTransform(factory, null);
-        return tr.context.completeTransform(factory, tr);
+    private void computeTransientFields() {
+        scale = 2 * ANGULAR_SCALE * Δfmod / (semiMajor * (1 - eccentricitySquared));
     }
 
     /**
-     * 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>difference between semi-major axis lengths (Δa) is zero,</li>
-     *   <li>difference between flattening factors (Δf) is zero,</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 && Δa == 0 &&
Δfmod == 0 && getSourceDimensions() == getTargetDimensions();
-    }
-
-    /**
-     * 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
-    {
-        return transform(srcPts[srcOff], srcPts[srcOff+1], isSource3D ? srcPts[srcOff+2]
: 0,
-                         dstPts, dstOff, tX, tY, tZ, null, derivate);
-    }
-
-    /**
-     * 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.
+     * Transforms the (λ,φ) coordinates between two geographic CRS. This method performs
the same transformation
+     * than {@link MolodenskyTransform}, but the formulas are repeated and simplified here
for performance reasons.
+     * In addition of using abridged Molodensky formulas, this method assumes that {@link
#tX}, {@link #tY} and
+     * {@link #tZ} fields are zero.
      *
      * @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 {
-        int srcInc = 0;
-        int dstInc = 0;
-        int offFinal = 0;
-        double[] dstFinal  = null;
-        if (srcPts == dstPts) {
-            final int srcDim = isSource3D ? 3 : 2;
-            final int dstDim = isTarget3D ? 3 : 2;
-            switch (IterationStrategy.suggest(srcOff, srcDim, dstOff, dstDim, numPts)) {
-                case ASCENDING: {
-                    break;
-                }
-                case DESCENDING: {
-                    srcOff += (numPts-1) * srcDim;
-                    dstOff += (numPts-1) * dstDim;
-                    srcInc = -2 * srcDim;
-                    dstInc = -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;
-                }
-            }
-        }
-        /*
-         * The code in the following loop is basically a copy-and-paste of the code in the
-         * MolodenskyFormula.transform(λ, φ, h, …) method, but without derivative matrix
-         * computation and without support for interpolation of (tX,tY,tZ) values in a grid.
-         */
+        System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
         while (--numPts >= 0) {
-            final double λ     = srcPts[srcOff++];
-            final double φ     = srcPts[srcOff++];
-            final double h     = isSource3D ? srcPts[srcOff++] : 0;
-            final double sinλ  = sin(λ);
-            final double cosλ  = cos(λ);
-            final double sinφ  = sin(φ);
-            final double cosφ  = cos(φ);
-            final double sin2φ = sinφ * sinφ;
-                  double ρden  = 1 - eccentricitySquared * sin2φ;               // Denominator
of ρ (completed later)
-            final double νden  = sqrt(ρden);                                    // Denominator
of ν
-            double ρ = semiMajor * (1 - eccentricitySquared) / (ρden *= νden);  // (also
complete calculation of ρden)
-            double ν = semiMajor / νden;                                        // Other
notation: Rm = ρ and Rn = ν
-            double t = Δfmod * 2;                                               // A term
in the calculation of Δφ
-            if (!isAbridged) {
-                ρ += h;
-                ν += h;
-                t = t*(0.5/νden + 0.5/ρden)                 // = Δf⋅[ν⋅(b/a) + ρ⋅(a/b)]
    (without the +h in ν and ρ)
-                        + Δa*eccentricitySquared/νden;      // = Δa⋅[ℯ²⋅ν/a]
-            }
-            final double spcλ = tY*sinλ + tX*cosλ;
-            dstPts[dstOff++] = λ + ANGULAR_SCALE * (tY*cosλ - tX*sinλ) / (ν*cosφ);
-            dstPts[dstOff++] = φ + ANGULAR_SCALE * ((t*cosφ - spcλ)*sinφ + tZ*cosφ)
/ ρ;
-            if (isTarget3D) {
-                t = Δfmod * sin2φ;                          // A term in the calculation
of Δh
-                double d = Δa;
-                if (!isAbridged) {
-                    t /= νden;                              // = Δf⋅(b/a)⋅ν⋅sin²φ
-                    d *= νden;                              // = Δa⋅(a/ν)
-                }
-                dstPts[dstOff++] = h + spcλ*cosφ + tZ*sinφ + t - d;
-            }
-            srcOff += srcInc;
-            dstOff += dstInc;
-        }
-        /*
-         * 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 handled by those LinearTransforms.
 If nevertheless
-     *       this MolodenskyTransform is at the beginning or the end of a transformation
chain,  the methods
-     *       inherited from the subclass will work (but may be slightly slower).
-     */
-
-    /**
-     * Returns the inverse of this Molodensky transform. The source ellipsoid of the returned
transform will
-     * be the target ellipsoid of this transform, and conversely.
-     *
-     * @return a Molodensky transform from the target ellipsoid to the source ellipsoid of
this transform.
-     */
-    @Override
-    public MathTransform inverse() {
-        return inverse;
-    }
-
-    /**
-     * 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 eccentricity.
-     *
-     * <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 description of the internal parameters.
-     */
-    @Debug
-    @Override
-    public ParameterDescriptorGroup getParameterDescriptors() {
-        synchronized (MolodenskyTransform.class) {
-            if (DESCRIPTOR == null) {
-                DESCRIPTOR = Molodensky.internal();
-            }
-            return DESCRIPTOR;
+            final double φ    = dstPts[++dstOff];
+            final double sinφ = sin(φ);
+            final double cosφ = cos(φ);
+            double ρ = 1 - eccentricitySquared * (sinφ * sinφ);
+            ρ *= sqrt(ρ);
+            dstPts[dstOff++] = φ + scale * (cosφ*sinφ) * ρ;
         }
     }
 }

Modified: 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=1813279&r1=1813278&r2=1813279&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java
[UTF-8] Wed Oct 25 13:08:10 2017
@@ -153,7 +153,11 @@ public class MolodenskyTransform extends
         super(source, isSource3D, target, isTarget3D, tX, tY, tZ, null, isAbridged,
                 isAbridged ? AbridgedMolodensky.PARAMETERS : Molodensky.PARAMETERS);
         if (!isSource3D && !isTarget3D) {
-            inverse = new MolodenskyTransform2D(this, source, target);
+            if (isAbridged && tX == 0 && tY == 0 && tZ == 0) {
+                inverse = new AbridgedMolodenskyTransform2D(this, source, target);
+            } else {
+                inverse = new MolodenskyTransform2D(this, source, target);
+            }
         } else {
             inverse = new MolodenskyTransform(this, source, target);
         }
@@ -226,7 +230,11 @@ public class MolodenskyTransform extends
     {
         final MolodenskyTransform tr;
         if (!isSource3D && !isTarget3D) {
-            tr = new MolodenskyTransform2D(source, target, tX, tY, tZ, isAbridged);
+            if (isAbridged && tX == 0 && tY == 0 && tZ == 0) {
+                tr = new AbridgedMolodenskyTransform2D(source, target);
+            } else {
+                tr = new MolodenskyTransform2D(source, target, tX, tY, tZ, isAbridged);
+            }
         } else {
             tr = new MolodenskyTransform(source, isSource3D, target, isTarget3D, tX, tY,
tZ, isAbridged);
         }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform2D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform2D.java?rev=1813279&r1=1813278&r2=1813279&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform2D.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform2D.java
[UTF-8] Wed Oct 25 13:08:10 2017
@@ -33,7 +33,7 @@ import org.opengis.referencing.operation
  * @since   0.7
  * @module
  */
-final class MolodenskyTransform2D extends MolodenskyTransform implements MathTransform2D
{
+class MolodenskyTransform2D extends MolodenskyTransform implements MathTransform2D {
     /**
      * Serial number for compatibility with different versions.
      */
@@ -52,9 +52,9 @@ final class MolodenskyTransform2D extend
     /**
      * Constructs the inverse of a 2D transform.
      *
-     * @param inverse The transform for which to create the inverse.
-     * @param source  The source ellipsoid of the given {@code inverse} transform.
-     * @param target  The target ellipsoid of the given {@code inverse} transform.
+     * @param inverse  the transform for which to create the inverse.
+     * @param source   the source ellipsoid of the given {@code inverse} transform.
+     * @param target   the target ellipsoid of the given {@code inverse} transform.
      */
     MolodenskyTransform2D(final MolodenskyTransform inverse, final Ellipsoid source, final
Ellipsoid target) {
         super(inverse, source, target);

Added: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/AbridgedMolodenskyTransformTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/AbridgedMolodenskyTransformTest.java?rev=1813279&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/AbridgedMolodenskyTransformTest.java
(added)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/AbridgedMolodenskyTransformTest.java
[UTF-8] Wed Oct 25 13:08:10 2017
@@ -0,0 +1,145 @@
+/*
+ * 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 org.opengis.test.CalculationType;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.referencing.datum.HardCodedDatum;
+import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.internal.referencing.Formulas;
+import org.apache.sis.test.DependsOnMethod;
+import org.apache.sis.test.DependsOn;
+import org.junit.Test;
+
+import static org.apache.sis.test.Assert.*;
+import static java.lang.StrictMath.toRadians;
+
+
+/**
+ * Tests {@link AbridgedMolodenskyTransform2D}. This class takes {@link MolodenskyTransform}
+ * as a reference implementation and verifies that we get consistent results. We rely on
the
+ * fact that {@link MolodenskyTransform#transform(double[], int, double[], int, boolean)}
is
+ * not overridden.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 0.8
+ * @since   0.8
+ * @module
+ */
+@DependsOn(MolodenskyTransformTest.class)
+public final strictfp class AbridgedMolodenskyTransformTest extends MathTransformTestCase
{
+    /**
+     * Creates a new test case.
+     */
+    public AbridgedMolodenskyTransformTest() {
+        final double delta = toRadians(100.0 / 60) / 1852;      // Approximatively 100 metres
+        derivativeDeltas   = new double[] {delta, delta};       // (Δλ, Δφ)
+        λDimension         = new int[] {0};                     // Dimension for which to
ignore ±360° differences.
+        tolerance          = Formulas.ANGULAR_TOLERANCE;        // Tolerance for longitude
and latitude in degrees
+    }
+
+    /**
+     * Compares the abridged Molodensky transform with a non-abridged one.
+     * This test works on angular values in radians, without concatenated transforms.
+     *
+     * @throws FactoryException if an error occurred while creating a transform step.
+     * @throws TransformException if a transformation failed.
+     *
+     * @see MolodenskyTransformTest#compareWithGeocentricTranslation()
+     */
+    @Test
+    public void compareWithReferenceImplementation() throws FactoryException, TransformException
{
+        final AbstractMathTransform transform = new AbridgedMolodenskyTransform2D(
+                HardCodedDatum.WGS84 .getEllipsoid(),
+                HardCodedDatum.SPHERE.getEllipsoid());
+        this.transform = transform;
+        tolerance = toRadians(tolerance);
+        final double[] sources  = generateRandomCoordinates(CoordinateDomain.GEOGRAPHIC_RADIANS,
0f);
+        final double[] targets  = new double[2];
+        final double[] expected = new double[2];
+        for (int i=0; i<sources.length; i+=2) {
+            transform.transform(sources, i, targets,  0, 1);        // Use the overridden
method.
+            transform.transform(sources, i, expected, 0, false);    // Use the MolodenskyTransform
method.
+            assertCoordinateEquals("transform", expected, targets, 0, CalculationType.DIRECT_TRANSFORM);
+        }
+    }
+
+    /**
+     * Creates an abridged Molodensky transform working on geographic coordinates in degrees.
+     * It should be an {@link AbridgedMolodenskyTransform2D} instance concatenated with affine
transforms.
+     */
+    private static MathTransform create() throws FactoryException {
+        final MathTransform tr = MolodenskyTransform.createGeodeticTransformation(
+                        DefaultFactories.forBuildin(MathTransformFactory.class),
+                        HardCodedDatum.WGS84 .getEllipsoid(), false,
+                        HardCodedDatum.SPHERE.getEllipsoid(), false, 0, 0, 0, true);
+        int n = 0;
+        for (final MathTransform step : MathTransforms.getSteps(tr)) {
+            if (!(step instanceof LinearTransform)) {
+                assertInstanceOf("kernel", AbridgedMolodenskyTransform2D.class, step);
+                n++;
+            }
+        }
+        assertEquals("Number of Molodensky transform instance", 1, n);
+        return tr;
+    }
+
+    /**
+     * Verifies consistency of all transform methods. This method tests the full operation
chain,
+     * including the conversions from degrees to radians.
+     *
+     * @throws FactoryException if an error occurred while creating a transform step.
+     * @throws TransformException if a transformation failed.
+     */
+    @Test
+    @DependsOnMethod("compareWithReferenceImplementation")
+    public void testConsistency() throws FactoryException, TransformException {
+        transform = create();
+        validate();
+        isDerivativeSupported = false;
+        isInverseTransformSupported = false;
+        verifyInDomain(CoordinateDomain.GEOGRAPHIC, -1941624852762631518L);
+        /*
+         * Calculation of "expected" transform derivative by finite difference
+         * seems too approximative for the default accuracy. (Actually, we are
+         * not completely sure that there is no bug in derivative formula...)
+         */
+        tolerance *= 10;
+        isDerivativeSupported = true;
+        verifyInDomain(CoordinateDomain.GEOGRAPHIC, 4350796528249596132L);
+    }
+
+    /**
+     * Tests a deserialized instance. The intend is to verify that the transient fields
+     * are correctly recomputed.
+     *
+     * @throws FactoryException if an error occurred while creating a transform step.
+     * @throws TransformException if a transformation failed.
+     */
+    @Test
+    @DependsOnMethod("testConsistency")
+    public void testSerialization() throws FactoryException, TransformException {
+        transform = assertSerializedEquals(create());
+        validate();
+        isDerivativeSupported = false;
+        isInverseTransformSupported = false;
+        verifyInDomain(CoordinateDomain.GEOGRAPHIC, 3534897149662911157L);
+    }
+}

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

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

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java?rev=1813279&r1=1813278&r2=1813279&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java
[UTF-8] Wed Oct 25 13:08:10 2017
@@ -310,6 +310,7 @@ public abstract strictfp class MathTrans
 
     /**
      * Generates random numbers that can be used for the current transform.
+     * The number of dimensions is given by {@code transform.getSourceDimensions()}.
      *
      * @param  domain   the domain of the numbers to be generated.
      * @param  propNaN  approximative percentage of NaN values as a fraction between 0 and
1, or 0 if none.

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=1813279&r1=1813278&r2=1813279&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] Wed Oct 25 13:08:10 2017
@@ -125,6 +125,7 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.operation.transform.ContextualParametersTest.class,
     org.apache.sis.referencing.operation.transform.EllipsoidToCentricTransformTest.class,
     org.apache.sis.referencing.operation.transform.MolodenskyTransformTest.class,
+    org.apache.sis.referencing.operation.transform.AbridgedMolodenskyTransformTest.class,
     org.apache.sis.referencing.operation.transform.SphericalToCartesianTest.class,
     org.apache.sis.referencing.operation.transform.CartesianToSphericalTest.class,
     org.apache.sis.referencing.operation.transform.PolarToCartesianTest.class,



Mime
View raw message