sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1714141 [3/5] - in /sis/branches/JDK6: ./ core/sis-build-helper/src/main/java/org/apache/sis/internal/book/ core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/ core/sis-feature/src/main/java/org/apache/sis/feature/ core/...
Date Thu, 12 Nov 2015 23:47:54 GMT
Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java?rev=1714141&r1=1714140&r2=1714141&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java [UTF-8] Thu Nov 12 23:47:52 2015
@@ -25,6 +25,7 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.operation.Matrix;
 import org.apache.sis.referencing.operation.matrix.Matrix2;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
+import org.apache.sis.referencing.operation.transform.ContextualParameters;
 import org.apache.sis.internal.referencing.provider.PolarStereographicA;
 import org.apache.sis.internal.referencing.provider.PolarStereographicB;
 import org.apache.sis.internal.referencing.provider.PolarStereographicC;
@@ -207,7 +208,7 @@ public class PolarStereographic extends
              *
              * In the spherical case, should give ρ == 2.
              */
-            ρ = verbatim(2 / sqrt(pow(1+excentricity, 1+excentricity) * pow(1-excentricity, 1-excentricity)));
+            ρ = verbatim(2 / sqrt(pow(1+eccentricity, 1+eccentricity) * pow(1-eccentricity, 1-eccentricity)));
             ρF = null;
         } else {
             /*
@@ -229,19 +230,19 @@ public class PolarStereographic extends
              */
             final double sinφ1 = sin(φ1);
             final double mF = initializer.scaleAtφ(sinφ1, cos(φ1));
-            ρ = verbatim(mF / expOfNorthing(φ1, excentricity*sinφ1));
+            ρ = verbatim(mF / expOfNorthing(φ1, eccentricity*sinφ1));
             ρF = (variant == C) ? verbatim(-mF) : null;
         }
         /*
          * At this point, all parameters have been processed. Now process to their
          * validation and the initialization of (de)normalize affine transforms.
          */
-        final MatrixSIS denormalize = context.getMatrix(false);
+        final MatrixSIS denormalize = context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
         denormalize.convertBefore(0, ρ, null);
         denormalize.convertBefore(1, ρ, ρF);
         if (isNorth) {
             final Number reverseSign = verbatim(-1);
-            final MatrixSIS normalize = context.getMatrix(true);
+            final MatrixSIS normalize = context.getMatrix(ContextualParameters.MatrixRole.NORMALIZATION);
             normalize  .convertAfter (1, reverseSign, null);
             denormalize.convertBefore(1, reverseSign, null);
         }
@@ -269,7 +270,7 @@ public class PolarStereographic extends
     @Override
     public MathTransform createMapProjection(final MathTransformFactory factory) throws FactoryException {
         PolarStereographic kernel = this;
-        if (excentricity == 0) {
+        if (eccentricity == 0) {
             kernel = new Spherical(this);
         }
         return context.completeTransform(factory, kernel);
@@ -304,7 +305,7 @@ public class PolarStereographic extends
          * The next step is to compute ρ = 2⋅a⋅k₀⋅t / …, but those steps are
          * applied by the denormalization matrix and shall not be done here.
          */
-        final double t = expOfNorthing(φ, excentricity*sinφ);
+        final double t = expOfNorthing(φ, eccentricity*sinφ);
         final double x = t * sinθ;
         final double y = t * cosθ;
         if (dstPts != null) {

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java?rev=1714141&r1=1714140&r2=1714141&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java [UTF-8] Thu Nov 12 23:47:52 2015
@@ -25,6 +25,7 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.operation.OperationMethod;
 import org.apache.sis.referencing.operation.matrix.Matrix2;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
+import org.apache.sis.referencing.operation.transform.ContextualParameters;
 import org.apache.sis.internal.referencing.provider.TransverseMercatorSouth;
 import org.apache.sis.internal.util.DoubleDouble;
 import org.apache.sis.parameter.Parameters;
@@ -72,7 +73,7 @@ public class TransverseMercator extends
 
     /**
      * Coefficients in the series expansion of the forward projection,
-     * depending only on {@linkplain #excentricity excentricity} value.
+     * depending only on {@linkplain #eccentricity eccentricity} value.
      * The series expansion is of the following form:
      *
      *     <blockquote>cf₂⋅f(2θ) + cf₄⋅f(4θ) + cf₆⋅f(6θ) + cf₈⋅f(8θ)</blockquote>
@@ -164,7 +165,7 @@ public class TransverseMercator extends
          * NOTE: the EPSG documentation makes special cases for φ₀ = 0 or ±π/2. This is not
          * needed here; we verified that the code below produces naturally the expected values.
          */
-        final double Q = asinh(tan(φ0)) - excentricity * atanh(excentricity * sin(φ0));
+        final double Q = asinh(tan(φ0)) - eccentricity * atanh(eccentricity * sin(φ0));
         final double β = atan(sinh(Q));
         final DoubleDouble M0 = new DoubleDouble();
         M0.value = cf8 * sin(8*β)
@@ -188,7 +189,7 @@ public class TransverseMercator extends
          *   - Multiply by the scale factor (done by the super-class constructor).
          *   - Add false easting and false northing (done by the super-class constructor).
          */
-        final MatrixSIS denormalize = context.getMatrix(false);
+        final MatrixSIS denormalize = context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
         denormalize.convertBefore(0, B, null);
         denormalize.convertBefore(1, B, M0);
         /*
@@ -216,7 +217,7 @@ public class TransverseMercator extends
          * 'n' value at serialization time.
          */
         final DoubleDouble t = new DoubleDouble(1, 0);
-        t.subtract(excentricitySquared, 0);
+        t.subtract(eccentricitySquared, 0);
         t.sqrt();
         t.ratio_1m_1p();
         computeCoefficients(t.doubleValue());
@@ -240,7 +241,7 @@ public class TransverseMercator extends
      *
      * As much as possible, b/a should be computed from the map projection parameters.
      * However if those parameters are not available anymore, then they can be computed
-     * from the excentricity as:
+     * from the eccentricity as:
      *
      *     <blockquote>b/a = √(1 - ℯ²)</blockquote>
      *
@@ -294,7 +295,7 @@ public class TransverseMercator extends
     @Override
     public MathTransform createMapProjection(final MathTransformFactory factory) throws FactoryException {
         TransverseMercator kernel = this;
-        if (excentricity == 0) {
+        if (eccentricity == 0) {
             kernel = new Spherical(this);
         }
         return context.completeTransform(factory, kernel);
@@ -316,8 +317,8 @@ public class TransverseMercator extends
         final double λ     = srcPts[srcOff  ];
         final double φ     = srcPts[srcOff+1];
         final double sinλ  = sin(λ);
-        final double ℯsinφ = sin(φ) * excentricity;
-        final double Q     = asinh(tan(φ)) - atanh(ℯsinφ) * excentricity;
+        final double ℯsinφ = sin(φ) * eccentricity;
+        final double Q     = asinh(tan(φ)) - atanh(ℯsinφ) * eccentricity;
         final double coshQ = cosh(Q);
         final double η0    = atanh(sinλ / coshQ);
         /*
@@ -418,7 +419,7 @@ public class TransverseMercator extends
         final double sqrt1_thQchη0 = sqrt(1 - (tanhQ * tanhQ) * (coshη0 * coshη0)); //-- Qη0
 
         //-- dQ_dλ = 0;
-        final double dQ_dφ  = 1 / cosφ - excentricitySquared * cosφ / (1 - ℯsinφ * ℯsinφ);
+        final double dQ_dφ  = 1 / cosφ - eccentricitySquared * cosφ / (1 - ℯsinφ * ℯsinφ);
 
         final double dη0_dλ =   cosλ * coshQ         / cosh2Q_sin2λ;
         final double dη0_dφ = - dQ_dφ * sinλ * sinhQ / cosh2Q_sin2λ;
@@ -542,10 +543,12 @@ public class TransverseMercator extends
         final double Q = asinh(tan(β));
         /*
          * Following usually converges in 4 iterations.
+         * The first iteration is unrolled.
          */
-        double Qp = Q, p = 0;
-        for (int i=0; i<MAXIMUM_ITERATIONS; i++) {
-            final double c = excentricity * atanh(excentricity * tanh(Qp));
+        double p = eccentricity * atanh(eccentricity * tanh(Q));
+        double Qp = Q + p;
+        for (int it=0; it<MAXIMUM_ITERATIONS; it++) {
+            final double c = eccentricity * atanh(eccentricity * tanh(Qp));
             Qp = Q + c;
             if (abs(c - p) <= ITERATION_TOLERANCE) {
                 dstPts[dstOff  ] = asin(tanh(η0) / cos(β));
@@ -558,6 +561,8 @@ public class TransverseMercator extends
     }
 
 
+
+
     /**
      * Provides the transform equations for the spherical case of the Transverse Mercator projection.
      *

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java?rev=1714141&r1=1714140&r2=1714141&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java [UTF-8] Thu Nov 12 23:47:52 2015
@@ -16,9 +16,12 @@
  */
 package org.apache.sis.referencing.operation.transform;
 
+import java.io.Serializable;
 import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.NoninvertibleTransformException;
 import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.internal.referencing.provider.Affine;
 import org.apache.sis.util.ComparisonMode;
@@ -37,13 +40,23 @@ import org.apache.sis.util.resources.Err
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.6
- * @version 0.6
+ * @version 0.7
  * @module
  */
-@SuppressWarnings("CloneInNonCloneableClass")
-abstract class AbstractLinearTransform extends AbstractMathTransform
-        implements LinearTransform, Matrix // Not Cloneable, despite the clone() method.
-{
+@SuppressWarnings("CloneInNonCloneableClass")   // Intentionally not Cloneable despite the clone() method.
+abstract class AbstractLinearTransform extends AbstractMathTransform implements LinearTransform, Matrix, Serializable {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -4649708313541868599L;
+
+    /**
+     * The inverse transform, or {@code null} if not yet created.
+     * This field is part of the serialization form in order to avoid rounding errors if a user
+     * asks for the inverse of the inverse (i.e. the original transform) after deserialization.
+     */
+    MathTransform inverse;
+
     /**
      * Constructs a transform.
      */
@@ -94,6 +107,29 @@ abstract class AbstractLinearTransform e
     }
 
     /**
+     * Creates the inverse transform of this object.
+     */
+    @Override
+    public synchronized MathTransform inverse() throws NoninvertibleTransformException {
+        if (inverse == null) {
+            /*
+             * Should never be the identity transform at this point (except during tests) because
+             * MathTransforms.linear(…) should never instantiate this class in the identity case.
+             * But we check anyway as a paranoiac safety.
+             */
+            if (isIdentity()) {
+                inverse = this;
+            } else {
+                inverse = MathTransforms.linear(Matrices.inverse(this));
+                if (inverse instanceof AbstractLinearTransform) {
+                    ((AbstractLinearTransform) inverse).inverse = this;
+                }
+            }
+        }
+        return inverse;
+    }
+
+    /**
      * Returns the parameter descriptors for this math transform.
      *
      * @return {@inheritDoc}

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java?rev=1714141&r1=1714140&r2=1714141&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] Thu Nov 12 23:47:52 2015
@@ -21,12 +21,14 @@ import java.util.Arrays;
 import java.io.Serializable;
 import javax.measure.unit.SI;
 import javax.measure.unit.NonSI;
+import org.opengis.util.FactoryException;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.MismatchedDimensionException;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.parameter.ParameterValueGroup;
 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.opengis.referencing.operation.NoninvertibleTransformException;
 import org.opengis.referencing.operation.OperationMethod;
@@ -79,7 +81,7 @@ import static org.apache.sis.util.Argume
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.7
  * @module
  *
  * @see DefaultMathTransformFactory
@@ -234,8 +236,8 @@ public abstract class AbstractMathTransf
      * @param expected  The expected dimension.
      * @param dimension The wrong dimension.
      */
-    static String mismatchedDimension(final String argument, final int expected, final int dimension) {
-        return Errors.format(Errors.Keys.MismatchedDimension_3, argument, expected, dimension);
+    static MismatchedDimensionException mismatchedDimension(final String argument, final int expected, final int dimension) {
+        return new MismatchedDimensionException(Errors.format(Errors.Keys.MismatchedDimension_3, argument, expected, dimension));
     }
 
     /**
@@ -341,8 +343,8 @@ public abstract class AbstractMathTransf
      * @param derivate {@code true} for computing the derivative, or {@code false} if not needed.
      * @return The matrix of the transform derivative at the given source position,
      *         or {@code null} if the {@code derivate} argument is {@code false}.
-     * @throws TransformException If the point can not be transformed or if a problem occurred while calculating the
-     *         derivative.
+     * @throws TransformException if the point can not be transformed or
+     *         if a problem occurred while calculating the derivative.
      *
      * @see #derivative(DirectPosition)
      * @see #transform(DirectPosition, DirectPosition)
@@ -752,7 +754,7 @@ public abstract class AbstractMathTransf
         final int dimSource = getSourceDimensions();
         final double[] coordinate = point.getCoordinate();
         if (coordinate.length != dimSource) {
-            throw new MismatchedDimensionException(mismatchedDimension("point", coordinate.length, dimSource));
+            throw mismatchedDimension("point", dimSource, coordinate.length);
         }
         final Matrix derivative = transform(coordinate, 0, null, 0, true);
         if (derivative == null) {
@@ -778,9 +780,9 @@ public abstract class AbstractMathTransf
     }
 
     /**
-     * Concatenates in an optimized way this math transform with the given one. A new math transform
-     * is created to perform the combined transformation. The {@code applyOtherFirst} value determines
-     * the transformation order as bellow:
+     * Concatenates in an optimized way this math transform with the given one.
+     * A new math transform is created to perform the combined transformation.
+     * The {@code applyOtherFirst} value determines the transformation order as bellow:
      *
      * <ul>
      *   <li>If {@code applyOtherFirst} is {@code true}, then transforming a point
@@ -802,9 +804,12 @@ public abstract class AbstractMathTransf
      * @param  other The math transform to apply.
      * @param  applyOtherFirst {@code true} if the transformation order is {@code other} followed by {@code this},
      *         or {@code false} if the transformation order is {@code this} followed by {@code other}.
+     * @param  factory The factory which is (indirectly) invoking this method, or {@code null} if none.
      * @return The combined math transform, or {@code null} if no optimized combined transform is available.
      */
-    MathTransform concatenate(final MathTransform other, final boolean applyOtherFirst) {
+    MathTransform concatenate(MathTransform other, boolean applyOtherFirst, MathTransformFactory factory)
+            throws FactoryException
+    {
         return null;
     }
 
@@ -901,6 +906,10 @@ public abstract class AbstractMathTransf
             if (mode.isIgnoringMetadata()) {
                 return true;
             }
+            /*
+             * We do not compare getParameters() because they usually duplicate the internal fields.
+             * Contextual parameters, on the other hand, typically contain new information.
+             */
             return Utilities.deepEquals(this.getContextualParameters(),
                                         that.getContextualParameters(), mode);
         }
@@ -921,7 +930,7 @@ public abstract class AbstractMathTransf
      * @param  transforms The full chain of concatenated transforms.
      * @param  index      The index of this transform in the {@code transforms} chain.
      * @param  inverse    Always {@code false}, except if we are formatting the inverse transform.
-     * @return Index of the last transform processed. Iteration should continue at that index + 1.
+     * @return Index of this transform in the {@code transforms} chain after processing.
      *
      * @see ConcatenatedTransform#getPseudoSteps()
      */
@@ -1087,14 +1096,19 @@ public abstract class AbstractMathTransf
          * but with the knowledge that this transform is an inverse transform.
          */
         @Override
-        final int beforeFormat(final List<Object> transforms, final int index, final boolean inverse) {
-            return AbstractMathTransform.this.beforeFormat(transforms, index, !inverse);
+        int beforeFormat(final List<Object> transforms, final int index, final boolean inverse) {
+            final ContextualParameters parameters = getContextualParameters();
+            if (parameters != null) {
+                return parameters.beforeFormat(transforms, index, inverse);
+            } else {
+                return AbstractMathTransform.this.beforeFormat(transforms, index, !inverse);
+            }
         }
 
         /**
          * Formats the inner part of a <cite>Well Known Text</cite> version 1 (WKT 1) element.
-         * If this inverse math transform has any parameter values, then this method format the
-         * WKT as in the {@linkplain AbstractMathTransform#formatWKT super-class method}.
+         * If this inverse math transform has any parameter values, then this method formats
+         * the WKT as in the {@linkplain AbstractMathTransform#formatWKT super-class method}.
          * Otherwise this method formats the math transform as an {@code "Inverse_MT"} entity.
          *
          * <div class="note"><b>Compatibility note:</b>
@@ -1111,6 +1125,7 @@ public abstract class AbstractMathTransf
                 WKTUtilities.appendParamMT(parameters, formatter);
                 return WKTKeywords.Param_MT;
             } else {
+                formatter.newLine();
                 formatter.append((FormattableObject) AbstractMathTransform.this);
                 return WKTKeywords.Inverse_MT;
             }

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform2D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform2D.java?rev=1714141&r1=1714140&r2=1714141&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform2D.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform2D.java [UTF-8] Thu Nov 12 23:47:52 2015
@@ -105,7 +105,7 @@ public abstract class AbstractMathTransf
     }
 
     /**
-     * Implementation of {@link #transform(DirectPosition, DirectPosition)} shared by the inverse transform.
+     * Implementation of {@link #transform(Point2D, Point2D)} shared by the inverse transform.
      */
     static Point2D transform(final AbstractMathTransform tr, final Point2D ptSrc, final Point2D ptDst) throws TransformException {
         final double[] ord = new double[] {ptSrc.getX(), ptSrc.getY()};

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java?rev=1714141&r1=1714140&r2=1714141&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java [UTF-8] Thu Nov 12 23:47:52 2015
@@ -19,6 +19,7 @@ package org.apache.sis.referencing.opera
 import java.util.List;
 import java.util.ArrayList;
 import java.io.Serializable;
+import org.opengis.util.FactoryException;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.MismatchedDimensionException;
 import org.opengis.parameter.ParameterValueGroup;
@@ -27,10 +28,12 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransform1D;
 import org.opengis.referencing.operation.MathTransform2D;
+import org.opengis.referencing.operation.MathTransformFactory;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.referencing.operation.NoninvertibleTransformException;
 import org.apache.sis.parameter.Parameterized;
 import org.apache.sis.referencing.operation.matrix.Matrices;
+import org.apache.sis.internal.referencing.provider.GeocentricAffine;
 import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.internal.system.Semaphores;
 import org.apache.sis.util.Classes;
@@ -53,7 +56,7 @@ import org.apache.sis.util.resources.Err
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.5
- * @version 0.5
+ * @version 0.7
  * @module
  *
  * @see org.opengis.referencing.operation.MathTransformFactory#createConcatenatedTransform(MathTransform, MathTransform)
@@ -144,18 +147,21 @@ class ConcatenatedTransform extends Abst
      *
      * @param tr1 The first math transform.
      * @param tr2 The second math transform.
-     * @return    The concatenated transform.
+     * @param factory The factory which is (indirectly) invoking this method, or {@code null} if none.
+     * @return The concatenated transform.
      *
      * @see MathTransforms#concatenate(MathTransform, MathTransform)
      */
-    public static MathTransform create(MathTransform tr1, MathTransform tr2) throws MismatchedDimensionException {
+    public static MathTransform create(MathTransform tr1, MathTransform tr2, final MathTransformFactory factory)
+            throws FactoryException, MismatchedDimensionException
+    {
         final int dim1 = tr1.getTargetDimensions();
         final int dim2 = tr2.getSourceDimensions();
         if (dim1 != dim2) {
             throw new MismatchedDimensionException(Errors.format(Errors.Keys.CanNotConcatenateTransforms_2, getName(tr1),
                     getName(tr2)) + ' ' + Errors.format(Errors.Keys.MismatchedDimension_2, dim1, dim2));
         }
-        MathTransform mt = createOptimized(tr1, tr2);
+        MathTransform mt = createOptimized(tr1, tr2, factory);
         if (mt != null) {
             return mt;
         }
@@ -188,9 +194,9 @@ class ConcatenatedTransform extends Abst
                 final ConcatenatedTransform ctr = (ConcatenatedTransform) candidate;
                 if (first) {
                     c1 = candidate = ctr.transform1;
-                    c2 = create(ctr.transform2, c2);
+                    c2 = create(ctr.transform2, c2, factory);
                 } else {
-                    c1 = create(c1, ctr.transform1);
+                    c1 = create(c1, ctr.transform1, factory);
                     c2 = candidate = ctr.transform2;
                 }
                 final int c = getStepCount(c1) + getStepCount(c2);
@@ -208,7 +214,7 @@ class ConcatenatedTransform extends Abst
          * Tries again the check for optimized cases (identity, etc.), because a
          * transform may have been simplified to identity as a result of the above.
          */
-        mt = createOptimized(tr1, tr2);
+        mt = createOptimized(tr1, tr2, factory);
         if (mt != null) {
             return mt;
         }
@@ -251,8 +257,12 @@ class ConcatenatedTransform extends Abst
      * Tries to returns an optimized concatenation, for example by merging two affine transforms
      * into a single one. If no optimized cases has been found, returns {@code null}. In the later
      * case, the caller will need to create a more heavy {@link ConcatenatedTransform} instance.
+     *
+     * @param factory The factory which is (indirectly) invoking this method, or {@code null} if none.
      */
-    private static MathTransform createOptimized(final MathTransform tr1, final MathTransform tr2) {
+    private static MathTransform createOptimized(final MathTransform tr1, final MathTransform tr2,
+            final MathTransformFactory factory) throws FactoryException
+    {
         /*
          * Trivial - but actually essential!! - check for the identity cases.
          */
@@ -268,7 +278,7 @@ class ConcatenatedTransform extends Abst
             if (matrix2 != null) {
                 final Matrix matrix = Matrices.multiply(matrix2, matrix1);
                 if (Matrices.isIdentity(matrix, IDENTITY_TOLERANCE)) {
-                    return MathTransforms.identity(matrix.getNumRow() - 1);
+                    return MathTransforms.identity(matrix.getNumRow() - 1);     // Returns a cached instance.
                 }
                 /*
                  * NOTE: It is quite tempting to "fix rounding errors" in the matrix before to create the transform.
@@ -277,7 +287,11 @@ class ConcatenatedTransform extends Abst
                  * Apache SIS performs matrix operations using double-double arithmetic in the hope to get exact
                  * results at the 'double' accuracy, which avoid the need for a tolerance threshold.
                  */
-                return MathTransforms.linear(matrix);
+                if (factory != null) {
+                    return factory.createAffineTransform(matrix);
+                } else {
+                    return MathTransforms.linear(matrix);
+                }
             }
             /*
              * If the second transform is a passthrough transform and all passthrough ordinates
@@ -287,9 +301,17 @@ class ConcatenatedTransform extends Abst
                 final PassThroughTransform candidate = (PassThroughTransform) tr2;
                 final Matrix sub = candidate.toSubMatrix(matrix1);
                 if (sub != null) {
-                    return PassThroughTransform.create(candidate.firstAffectedOrdinate,
-                            create(MathTransforms.linear(sub), candidate.subTransform),
-                            candidate.numTrailingOrdinates);
+                    if (factory != null) {
+                        return factory.createPassThroughTransform(
+                                candidate.firstAffectedOrdinate,
+                                factory.createConcatenatedTransform(factory.createAffineTransform(sub), candidate.subTransform),
+                                candidate.numTrailingOrdinates);
+                    } else {
+                        return PassThroughTransform.create(
+                                candidate.firstAffectedOrdinate,
+                                create(MathTransforms.linear(sub), candidate.subTransform, factory),
+                                candidate.numTrailingOrdinates);
+                    }
                 }
             }
         }
@@ -300,20 +322,20 @@ class ConcatenatedTransform extends Abst
         if (areInverse(tr1, tr2) || areInverse(tr2, tr1)) {
             assert tr1.getSourceDimensions() == tr2.getTargetDimensions();
             assert tr1.getTargetDimensions() == tr2.getSourceDimensions();
-            return MathTransforms.identity(tr1.getSourceDimensions());
+            return MathTransforms.identity(tr1.getSourceDimensions());      // Returns a cached instance.
         }
         /*
          * Gives a chance to AbstractMathTransform to returns an optimized object.
          * The main use case is Logarithmic vs Exponential transforms.
          */
         if (tr1 instanceof AbstractMathTransform) {
-            final MathTransform optimized = ((AbstractMathTransform) tr1).concatenate(tr2, false);
+            final MathTransform optimized = ((AbstractMathTransform) tr1).concatenate(tr2, false, factory);
             if (optimized != null) {
                 return optimized;
             }
         }
         if (tr2 instanceof AbstractMathTransform) {
-            final MathTransform optimized = ((AbstractMathTransform) tr2).concatenate(tr1, true);
+            final MathTransform optimized = ((AbstractMathTransform) tr2).concatenate(tr1, true, factory);
             if (optimized != null) {
                 return optimized;
             }
@@ -327,11 +349,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;
+                    }
                 }
             }
         }
@@ -454,6 +480,11 @@ class ConcatenatedTransform extends Abst
                 after = null;
             }
         }
+        /*
+         * Special case for datum shifts. Need to be done only after we processed
+         * 'beforeFormat(…)' for all objects and concatenated the affine transforms.
+         */
+        GeocentricAffine.asDatumShift(transforms);
         return transforms;
     }
 
@@ -837,11 +868,13 @@ class ConcatenatedTransform extends Abst
     @Override
     public synchronized MathTransform inverse() throws NoninvertibleTransformException {
         assert isValid();
-        if (inverse == null) {
-            inverse = create(transform2.inverse(), transform1.inverse());
+        if (inverse == null) try {
+            inverse = create(transform2.inverse(), transform1.inverse(), null);
             if (inverse instanceof ConcatenatedTransform) {
                 ((ConcatenatedTransform) inverse).inverse = this;
             }
+        } catch (FactoryException e) {
+            throw new NoninvertibleTransformException(Errors.format(Errors.Keys.NonInvertibleTransform), e);
         }
         return inverse;
     }

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java?rev=1714141&r1=1714140&r2=1714141&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java [UTF-8] Thu Nov 12 23:47:52 2015
@@ -23,8 +23,6 @@ import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.io.Serializable;
 import org.opengis.util.FactoryException;
-import org.opengis.referencing.operation.MathTransform;
-import org.opengis.referencing.operation.OperationMethod;
 import org.opengis.parameter.GeneralParameterValue;
 import org.opengis.parameter.GeneralParameterDescriptor;
 import org.opengis.parameter.ParameterValue;
@@ -32,7 +30,9 @@ import org.opengis.parameter.ParameterVa
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.parameter.ParameterNotFoundException;
+import org.opengis.referencing.operation.OperationMethod;
 import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransformFactory;
 import org.apache.sis.internal.referencing.ExtendedPrecisionMatrix;
 import org.apache.sis.internal.referencing.WKTUtilities;
@@ -110,7 +110,7 @@ import org.apache.sis.internal.jdk7.Obje
  *     and gives the linear parameters to the {@link #normalizeGeographicInputs normalizeGeographicInputs(…)} and
  *     {@link MatrixSIS#convertAfter MatrixSIS.convertAfter(…)} methods, which will create the matrices show above.
  *     The projection constructor is free to apply additional operations on the two affine transforms
- *     ({@linkplain #getMatrix(boolean) normalize / denormalize}) before or after the above-cited
+ *     ({@linkplain #getMatrix(MatrixRole) normalize / denormalize}) before or after the above-cited
  *     methods have been invoked.</li>
  *
  *   <li>After all parameter values have been set and the normalize / denormalize matrices defined,
@@ -128,7 +128,7 @@ import org.apache.sis.internal.jdk7.Obje
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.6
- * @version 0.6
+ * @version 0.7
  * @module
  *
  * @see org.apache.sis.referencing.operation.projection.NormalizedProjection
@@ -136,13 +136,57 @@ import org.apache.sis.internal.jdk7.Obje
  */
 public class ContextualParameters extends Parameters implements Serializable {
     /**
+     * Whether a matrix is used for <cite>normalization</cite> or <cite>denormalization</cite>
+     * before or after a non-linear operation.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @since   0.7
+     * @version 0.7
+     * @module
+     *
+     * @see ContextualParameters#getMatrix(MatrixRole)
+     */
+    public static enum MatrixRole {
+        /**
+         * Matrix for converting angular degrees to radians, or any other linear operations needed
+         * before to apply a non-linear operation. For example in a map projection, this matrix is
+         * typically (but not necessarily) as below:
+         *
+         * <center>{@include formulas.html#NormalizeGeographic}</center>
+         */
+        NORMALIZATION,
+
+        /**
+         * Inverse of the {@link #NORMALIZATION} matrix.
+         * For example in a map projection, this matrix is typically (but not necessarily) as below:
+         *
+         * <center>{@include formulas.html#DeormalizeGeographic}</center>
+         */
+        INVERSE_NORMALIZATION,
+
+        /**
+         * Matrix for scaling Cartesian coordinates to the size of the planet, or any other linear operations needed
+         * after execution of a non-linear operation. For example in a map projection, this matrix is typically
+         * (but not necessarily) as below:
+         *
+         * <center>{@include formulas.html#DenormalizeCartesian}</center>
+         */
+        DENORMALIZATION,
+
+        /**
+         * Inverse of the {@link #DENORMALIZATION} matrix.
+         */
+        INVERSE_DENORMALIZATION
+    }
+
+    /**
      * For cross-version compatibility.
      */
-    private static final long serialVersionUID = 4899134192407586472L;
+    private static final long serialVersionUID = 6769546741493459341L;
 
     /**
      * The parameters that represents the sequence of transforms as a whole. The parameter values may be used
-     * in the {@linkplain #getMatrix(boolean) (de)normalization} transforms, in the kernel, or both.
+     * in the {@linkplain #getMatrix(MatrixRole) (de)normalization} transforms, in the kernel, or both.
      *
      * @see #getDescriptor()
      */
@@ -151,11 +195,11 @@ public class ContextualParameters extend
     /**
      * The affine transform to be applied before (<cite>normalize</cite>) and after (<cite>denormalize</cite>)
      * the kernel operation. On {@code ContextualParameters} construction, those affines are initially identity
-     * transforms, to be modified in-place by callers of {@link #getMatrix(boolean)} or related methods.
+     * transforms, to be modified in-place by callers of {@link #getMatrix(MatrixRole)} or related methods.
      * After the {@link #completeTransform(MathTransformFactory, MathTransform)} method has been invoked,
      * those matrices are typically (but not necessarily) replaced by the {@link LinearTransform} instances itself.
      *
-     * @see #getMatrix(boolean)
+     * @see #getMatrix(MatrixRole)
      */
     private Matrix normalize, denormalize;
 
@@ -169,8 +213,16 @@ public class ContextualParameters extend
     private ParameterValue<?>[] values;
 
     /**
+     * If the inverse coordinate operation can be described by another {@code ContextualParameters} instance,
+     * a reference to that instance. Otherwise {@code null}.
+     */
+    private ContextualParameters inverse;
+
+    /**
      * {@code false} if this parameter group is modifiable, or {@code true} if it has been made unmodifiable
      * (frozen) by a call to {@link #completeTransform(MathTransformFactory, MathTransform)}.
+     *
+     * @see #freeze()
      */
     private boolean isFrozen;
 
@@ -183,7 +235,7 @@ public class ContextualParameters extend
      * <ul>
      *   <li>Set the relevant parameter values by calls to
      *     <code>{@linkplain #parameter(String) parameter(…)}.setValue(…)</code>.</li>
-     *   <li>Modify the element values in {@linkplain #getMatrix(boolean) normalization / denormalization}
+     *   <li>Modify the element values in {@linkplain #getMatrix(MatrixRole) normalization / denormalization}
      *     affine transforms, optionally by calls to the convenience methods in this class.</li>
      *   <li>Get the complete transforms chain with a call
      *     {@link #completeTransform(MathTransformFactory, MathTransform) completeTransform(…)}</li>
@@ -202,9 +254,38 @@ public class ContextualParameters extend
     }
 
     /**
+     * Equivalent to the public constructor, but avoid the need for an {@link OperationMethod} instance.
+     *
+     * @param descriptor The parameter descriptor.
+     * @param srcSize    Size of the normalization matrix: source dimensions + 1.
+     * @param tgtSize    Size of the denormalization matrix: target dimensions + 1.
+     */
+    ContextualParameters(final ParameterDescriptorGroup descriptor, final int srcSize, final int tgtSize) {
+        this.descriptor  = descriptor;
+        this.normalize   = Matrices.create(srcSize, srcSize, ExtendedPrecisionMatrix.IDENTITY);
+        this.denormalize = Matrices.create(tgtSize, tgtSize, ExtendedPrecisionMatrix.IDENTITY);
+        this.values      = new ParameterValue<?>[descriptor.descriptors().size()];
+    }
+
+    /**
+     * Creates a {@code ContextualParameters} for the inverse operation.
+     *
+     * @param  desc    Descriptor of the inverse operation.
+     * @param  forward The parameters created for the forward operation.
+     */
+    private ContextualParameters(final ParameterDescriptorGroup desc, final ContextualParameters forward) {
+        descriptor  = desc;
+        normalize   = forward.getMatrix(MatrixRole.INVERSE_DENORMALIZATION);
+        denormalize = forward.getMatrix(MatrixRole.INVERSE_NORMALIZATION);
+        values      = forward.values;
+        inverse     = forward;
+        isFrozen    = true;
+    }
+
+    /**
      * Creates a matrix for a linear step of the transforms chain.
      * It is important that the matrices created here are instances of {@link MatrixSIS}, in order
-     * to allow {@link #getMatrix(boolean)} to return the reference to the (de)normalize matrices.
+     * to allow {@link #getMatrix(MatrixRole)} to return the reference to the (de)normalize matrices.
      */
     private static MatrixSIS linear(final String name, final Integer size) {
         if (size == null) {
@@ -215,19 +296,36 @@ public class ContextualParameters extend
     }
 
     /**
+     * Creates a {@code ContextualParameters} for the inverse operation.
+     *
+     * @param  desc Descriptor of the inverse operation.
+     * @return Parameters for the inverse operation.
+     */
+    final synchronized ContextualParameters inverse(final ParameterDescriptorGroup desc) {
+        if (inverse == null) {
+            if (!isFrozen) {
+                freeze();
+            }
+            inverse = new ContextualParameters(desc, this);
+        }
+        assert inverse.descriptor == desc;
+        return inverse;
+    }
+
+    /**
      * Returns the parameters for the <cite>normalize</cite> → <cite>non-linear kernel</cite> →
      * <cite>denormalize</cite> sequence as a whole. This is the parameter descriptor of the
      * {@linkplain org.apache.sis.referencing.operation.DefaultOperationMethod operation method}
      * given to the constructor.
      *
      * <p>The values for those parameters is given by the {@link #values()} method. Those values may be used in
-     * the {@linkplain #getMatrix(boolean) normalization / denormalization} transforms, in the kernel, or both.</p>
+     * the {@linkplain #getMatrix(MatrixRole) normalization / denormalization} transforms, in the kernel, or both.</p>
      *
      * <div class="note"><b>Note:</b>
      * The definition of "kernel" is left to implementors.
      * In the particular case of Apache SIS implementation of map projections,
-     * kernels are subclasses of {@link org.apache.sis.referencing.operation.projection.NormalizedProjection}.
-     * </div>
+     * kernels are instances of {@link org.apache.sis.referencing.operation.projection.NormalizedProjection}.
+     * Other "kernels" in SIS are {@link EllipsoidToCentricTransform} and {@link MolodenskyTransform}.</div>
      *
      * @return The description of the parameters.
      */
@@ -237,17 +335,27 @@ public class ContextualParameters extend
     }
 
     /**
-     * Ensures that this instance is modifiable.
+     * Ensures that this {@code ContextualParameters} instance is modifiable.
      *
      * @throws IllegalStateException if this {@code ContextualParameter} has been made unmodifiable.
      */
     private void ensureModifiable() throws IllegalStateException {
+        assert Thread.holdsLock(this);
         if (isFrozen) {
             throw new IllegalStateException(Errors.format(Errors.Keys.UnmodifiableObject_1, getClass()));
         }
     }
 
     /**
+     * Returns the given matrix as an unmodifiable one if this {@code ContextualParameters} instance is unmodifiable.
+     * Note that if this instance is modifiable, then we <strong>must</strong> return a direct reference to the matrix,
+     * not a wrapper, because the caller may need to modify it.
+     */
+    private MatrixSIS toMatrixSIS(final Matrix m) {
+        return isFrozen ? Matrices.unmodifiable(m) : (MatrixSIS) m;
+    }
+
+    /**
      * Returns the affine transforms to be applied before or after the non-linear kernel operation.
      * Immediately after {@linkplain #ContextualParameters(OperationMethod) construction}, those matrices
      * are modifiable identity matrices. Callers can modify the matrix element values, typically by calls to
@@ -263,33 +371,68 @@ public class ContextualParameters extend
      * invoked, the matrices returned by this method are {@linkplain Matrices#unmodifiable(Matrix) unmodifiable}.
      *
      *
-     * <div class="section">Application to map projections</div>
-     * After {@link org.apache.sis.referencing.operation.projection.NormalizedProjection} construction, the matrices
+     * <div class="note"><b>Application to map projections:</b>
+     * after {@link org.apache.sis.referencing.operation.projection.NormalizedProjection} construction, the matrices
      * returned by {@code projection.getContextualParameters().getMatrix(…)} are initialized to the values shown below.
      * Note that some {@code NormalizedProjection} subclasses apply further modifications to those matrices.
      *
      * <table class="sis">
-     *   <caption>Initial matrix coefficients after construction</caption>
+     *   <caption>Initial matrix coefficients after {@code NormalizedProjection} construction</caption>
      *   <tr>
-     *     <th>{@code getMatrix(true)}</th>
-     *     <th class="sep">{@code getMatrix(false)}</th>
+     *     <th>{@code getMatrix(NORMALIZATION)}</th>
+     *     <th class="sep">{@code getMatrix(DENORMALIZATION)}</th>
      *   </tr><tr>
      *     <td>{@include formulas.html#NormalizeGeographic}</td>
      *     <td class="sep">{@include formulas.html#DenormalizeCartesian}</td>
      *   </tr>
      * </table>
+     * </div>
+     *
+     * @param  role {@code NORMALIZATION} for fetching the <cite>normalization</cite> transform to apply before the kernel,
+     *         {@code DENORMALIZATION} for the <cite>denormalization</cite> transform to apply after the kernel,
+     *         or {@code INVERSE_*} for the inverse of the above-cited matrices.
+     * @return The matrix for the requested normalization or denormalization affine transform.
+     *
+     * @since 0.7
+     */
+    public final MatrixSIS getMatrix(MatrixRole role) {
+        final Matrix fallback;
+        final ContextualParameters inverse;
+        synchronized (this) {
+            switch (role) {
+                default:                      throw new AssertionError(role);
+                case NORMALIZATION:           return toMatrixSIS(normalize);
+                case DENORMALIZATION:         return toMatrixSIS(denormalize);
+                case INVERSE_NORMALIZATION:   role = MatrixRole.DENORMALIZATION; fallback = normalize; break;
+                case INVERSE_DENORMALIZATION: role = MatrixRole.NORMALIZATION; fallback = denormalize; break;
+            }
+            inverse = this.inverse;     // Copy the reference while we are inside the synchronized block.
+        }
+        /*
+         * Following must be outside the synchronized block in order to avoid potential deadlock while invoking
+         * inverse.getMatrix(role). We do not cache the matrix here, but 'inverse' is likely to have cached it.
+         */
+        final Matrix m;
+        if (inverse != null) {
+            m = inverse.getMatrix(role);
+        } else try {
+            m = Matrices.inverse(fallback);
+        } catch (NoninvertibleMatrixException e) {
+            throw new IllegalStateException(Errors.format(Errors.Keys.CanNotCompute_1, role), e);
+        }
+        return Matrices.unmodifiable(m);
+    }
+
+    /**
+     * @deprecated Replaced by {@link #getMatrix(MatrixRole)}.
      *
      * @param  norm {@code true} for fetching the <cite>normalization</cite> transform to apply before the kernel,
      *         or {@code false} for the <cite>denormalization</cite> transform to apply after the kernel.
      * @return The matrix for the requested normalization ({@code true}) or denormalization ({@code false}) affine transform.
      */
+    @Deprecated
     public final MatrixSIS getMatrix(final boolean norm) {
-        final Matrix m = norm ? normalize : denormalize;
-        if (!isFrozen) {
-            return (MatrixSIS) m;       // Must be the same instance, not a copy.
-        } else {
-            return Matrices.unmodifiable(m);
-        }
+        return getMatrix(norm ? MatrixRole.NORMALIZATION : MatrixRole.DENORMALIZATION);
     }
 
     /**
@@ -307,7 +450,7 @@ public class ContextualParameters extend
      *         Callers can change that matrix directly if they want to apply additional normalization operations.
      * @throws IllegalStateException if this {@code ContextualParameter} has been made unmodifiable.
      */
-    public MatrixSIS normalizeGeographicInputs(final double λ0) {
+    public synchronized MatrixSIS normalizeGeographicInputs(final double λ0) {
         ensureModifiable();
         /*
          * In theory the check for (λ0 != 0) is useless. However Java has a notion of negative zero, and we want
@@ -341,7 +484,7 @@ public class ContextualParameters extend
      *         Callers can change that matrix directly if they want to apply additional denormalization operations.
      * @throws IllegalStateException if this {@code ContextualParameter} has been made unmodifiable.
      */
-    public MatrixSIS denormalizeGeographicOutputs(final double λ0) {
+    public synchronized MatrixSIS denormalizeGeographicOutputs(final double λ0) {
         ensureModifiable();
         final DoubleDouble toDegrees = DoubleDouble.createRadiansToDegrees();
         final MatrixSIS denormalize = (MatrixSIS) this.denormalize;  // Must be the same instance, not a copy.
@@ -353,7 +496,7 @@ public class ContextualParameters extend
     /**
      * Marks this {@code ContextualParameter} as unmodifiable and creates the
      * <cite>normalize</cite> → {@code kernel} → <cite>denormalize</cite> transforms chain.
-     * This method shall be invoked only after the {@linkplain #getMatrix(boolean) (de)normalization}
+     * This method shall be invoked only after the {@linkplain #getMatrix(MatrixRole) (de)normalization}
      * matrices have been set to their final values.
      *
      * <p>The transforms chain created by this method does not include any step for
@@ -372,51 +515,11 @@ public class ContextualParameters extend
      * @see org.apache.sis.referencing.operation.projection.NormalizedProjection#createMapProjection(MathTransformFactory)
      */
     @SuppressWarnings("AssignmentToForLoopParameter")
-    public MathTransform completeTransform(final MathTransformFactory factory, final MathTransform kernel)
+    public synchronized MathTransform completeTransform(final MathTransformFactory factory, final MathTransform kernel)
             throws FactoryException
     {
         if (!isFrozen) {
-            isFrozen = true;
-            /*
-             * Sort the parameter values in the same order than the parameter descriptor. This is not essential,
-             * but makes easier to read 'toString()' output by ensuring a consistent order for most projections.
-             * Some WKT parsers other than SIS may also require the parameter values to be listed in that specific
-             * order. We proceed by first copying all parameters in a temporary HashMap:
-             */
-            final Map<ParameterDescriptor<?>, ParameterValue<?>> parameters =
-                    new IdentityHashMap<ParameterDescriptor<?>, ParameterValue<?>>(values.length);
-            for (ParameterValue<?> p : values) {
-                if (p == null) {
-                    break;  // The first null value in the array indicates the end of sequence.
-                }
-                p = DefaultParameterValue.unmodifiable(p);
-                final ParameterDescriptor<?> desc = p.getDescriptor();
-                if (parameters.put(desc, p) != null) {
-                    // Should never happen unless ParameterValue.descriptor changed (contract violation).
-                    throw new IllegalStateException(Errors.format(Errors.Keys.ElementAlreadyPresent_1, desc.getName()));
-                }
-            }
-            /*
-             * Then, copy all HashMap values back to the 'values' array in the order they are declared in the
-             * descriptor. Implementation note: the iteration termination condition uses the values array, not
-             * the descriptors list, because the former is often shorter than the later. We should never reach
-             * the end of descriptors list before the end of values array because 'descriptors' contains all
-             * 'parameters' keys. This is verified by the 'assert' below.
-             */
-            values = new ParameterValue<?>[parameters.size()];
-            assert descriptor.descriptors().containsAll(parameters.keySet());
-            final Iterator<GeneralParameterDescriptor> it = descriptor.descriptors().iterator();
-            for (int i=0; i < values.length;) {
-                /*
-                 * No need to check for it.hasNext(), since a NoSuchElementException below would be a bug in
-                 * our algorithm (or a concurrent change in the 'descriptor.descriptors()' list, which would
-                 * be a contract violation). See above 'assert'.
-                 */
-                final ParameterValue<?> p = parameters.get(it.next());
-                if (p != null) {
-                    values[i++] = p;
-                }
-            }
+            freeze();
         }
         /*
          * Creates the ConcatenatedTransform, letting the factory returns the cached instance
@@ -431,6 +534,55 @@ public class ContextualParameters extend
     }
 
     /**
+     * Marks this contextual parameter as unmodifiable.
+     *
+     * @see #ensureModifiable()
+     */
+    private void freeze() {
+        isFrozen = true;
+        /*
+         * Sort the parameter values in the same order than the parameter descriptor. This is not essential,
+         * but makes easier to read 'toString()' output by ensuring a consistent order for most projections.
+         * Some WKT parsers other than SIS may also require the parameter values to be listed in that specific
+         * order. We proceed by first copying all parameters in a temporary HashMap:
+         */
+        final Map<ParameterDescriptor<?>, ParameterValue<?>> parameters =
+                new IdentityHashMap<ParameterDescriptor<?>, ParameterValue<?>>(values.length);
+        for (ParameterValue<?> p : values) {
+            if (p == null) {
+                break;  // The first null value in the array indicates the end of sequence.
+            }
+            p = DefaultParameterValue.unmodifiable(p);
+            final ParameterDescriptor<?> desc = p.getDescriptor();
+            if (parameters.put(desc, p) != null) {
+                // Should never happen unless ParameterValue.descriptor changed (contract violation).
+                throw new IllegalStateException(Errors.format(Errors.Keys.ElementAlreadyPresent_1, desc.getName()));
+            }
+        }
+        /*
+         * Then, copy all HashMap values back to the 'values' array in the order they are declared in the
+         * descriptor. Implementation note: the iteration termination condition uses the values array, not
+         * the descriptors list, because the former is often shorter than the later. We should never reach
+         * the end of descriptors list before the end of values array because 'descriptors' contains all
+         * 'parameters' keys. This is verified by the 'assert' below.
+         */
+        values = new ParameterValue<?>[parameters.size()];
+        assert descriptor.descriptors().containsAll(parameters.keySet());
+        final Iterator<GeneralParameterDescriptor> it = descriptor.descriptors().iterator();
+        for (int i=0; i < values.length;) {
+            /*
+             * No need to check for it.hasNext(), since a NoSuchElementException below would be a bug in
+             * our algorithm (or a concurrent change in the 'descriptor.descriptors()' list, which would
+             * be a contract violation). See above 'assert'.
+             */
+            final ParameterValue<?> p = parameters.get(it.next());
+            if (p != null) {
+                values[i++] = p;
+            }
+        }
+    }
+
+    /**
      * Returns the parameter value of the given name.
      * Before the call to {@link #completeTransform completeTransform(…)},
      * this method can be used for setting parameter values like below:
@@ -446,7 +598,7 @@ public class ContextualParameters extend
      * @throws ParameterNotFoundException if there is no parameter of the given name.
      */
     @Override
-    public ParameterValue<?> parameter(final String name) throws ParameterNotFoundException {
+    public synchronized ParameterValue<?> parameter(final String name) throws ParameterNotFoundException {
         final GeneralParameterDescriptor desc = descriptor.descriptor(name);
         if (!(desc instanceof ParameterDescriptor<?>)) {
             throw parameterNotFound(name);
@@ -493,7 +645,7 @@ public class ContextualParameters extend
      */
     @Override
     @SuppressWarnings("unchecked")
-    public List<GeneralParameterValue> values() {
+    public synchronized List<GeneralParameterValue> values() {
         int upper = values.length;
         while (upper != 0 && values[upper - 1] == null) {
             upper--;
@@ -537,7 +689,7 @@ public class ContextualParameters extend
      * @return A clone of this parameter value group.
      */
     @Override
-    public ContextualParameters clone() {
+    public synchronized ContextualParameters clone() {
         /*
          * Creates a new parameter array with enough room for adding new parameters.
          * Then replace each element by a modifiable clone.
@@ -552,6 +704,7 @@ public class ContextualParameters extend
         }
         /*
          * Now proceed to the clone of this ContextualParameters instance.
+         * We do not clone inverseFoo fields since they shall be null for modifiable instances.
          */
         final ContextualParameters clone = (ContextualParameters) super.clone();
         clone.values      = param;
@@ -565,7 +718,7 @@ public class ContextualParameters extend
      * implementation-dependent and may change in any future version.
      */
     @Override
-    public int hashCode() {
+    public synchronized int hashCode() {
         return (normalize.hashCode() + 31*denormalize.hashCode()) ^ Arrays.hashCode(values) ^ (int) serialVersionUID;
     }
 
@@ -576,7 +729,7 @@ public class ContextualParameters extend
      * @return {@code true} if the given object is equal to this one.
      */
     @Override
-    public boolean equals(final Object object) {
+    public synchronized boolean equals(final Object object) {
         if (object != null && object.getClass() == getClass()) {
             final ContextualParameters that = (ContextualParameters) object;
             return Objects.equals(descriptor,  that.descriptor)  &&
@@ -633,6 +786,7 @@ public class ContextualParameters extend
         @Override
         protected String formatTo(final Formatter formatter) {
             if (inverse) {
+                formatter.newLine();
                 formatter.append(new WKT(false));
                 return WKTKeywords.Inverse_MT;
             } else {
@@ -656,7 +810,7 @@ public class ContextualParameters extend
      * @param  transforms The full chain of concatenated transforms.
      * @param  index      The index of this transform in the {@code transforms} chain.
      * @param  inverse    Always {@code false}, except if we are formatting the inverse transform.
-     * @return Index of the last transform processed. Iteration should continue at that index + 1.
+     * @return Index of this transform in the {@code transforms} chain after processing.
      *
      * @see ConcatenatedTransform#getPseudoSteps()
      * @see AbstractMathTransform#beforeFormat(List, int, boolean)
@@ -691,17 +845,17 @@ public class ContextualParameters extend
          * in order to apply a change of axis order). We need to separate the "user-defined"
          * step from the "normalize" step.
          */
-        Matrix userDefined = inverse ? denormalize : normalize;
-        if (!inverse) try {
-            userDefined = Matrices.inverse(userDefined);
-        } catch (NoninvertibleMatrixException e) {
+        MatrixSIS userDefined;
+        try {
+            userDefined = getMatrix(inverse ? MatrixRole.DENORMALIZATION : MatrixRole.INVERSE_NORMALIZATION);
+        } catch (IllegalStateException e) {
             // Should never happen. But if it does, we abandon the attempt to change
             // the list elements and will format the objects in their "raw" format.
             unexpectedException(e);
             return index;
         }
         if (hasBefore) {
-            userDefined = Matrices.multiply(userDefined, before);
+            userDefined = userDefined.multiply(before);
         }
         /*
          * At this point "userDefined" is the affine transform to show to user instead of the
@@ -737,10 +891,9 @@ public class ContextualParameters extend
          * Note that if this operation fails, we will cancel everything we would have done
          * in this method (i.e. we do not touch the transforms list at all).
          */
-        userDefined = inverse ? normalize : denormalize;
-        if (!inverse) try {
-            userDefined = Matrices.inverse(userDefined);
-        } catch (NoninvertibleMatrixException e) {
+        try {
+            userDefined = getMatrix(inverse ? MatrixRole.NORMALIZATION : MatrixRole.INVERSE_DENORMALIZATION);
+        } catch (IllegalStateException e) {
             unexpectedException(e);
             return index;
         }
@@ -791,12 +944,11 @@ public class ContextualParameters extend
                 assert (old instanceof LinearTransform);
             }
         } else {
-            index++;
             if (hasAfter) {
-                final Object old = transforms.set(index, after);
+                final Object old = transforms.set(index + 1, after);
                 assert (old instanceof LinearTransform);
             } else {
-                transforms.add(index, after);
+                transforms.add(index + 1, after);
             }
         }
         return index;
@@ -811,7 +963,7 @@ public class ContextualParameters extend
      * because this error should occurs only in the context of WKT formatting of a concatenated
      * transform.</p>
      */
-    private static void unexpectedException(final NoninvertibleMatrixException e) {
-        Logging.unexpectedException(Logging.getLogger(Loggers.WKT), ConcatenatedTransform.class, "formatTo", e);
+    private static void unexpectedException(final IllegalStateException e) {
+        Logging.unexpectedException(Logging.getLogger(Loggers.WKT), ConcatenatedTransform.class, "formatTo", e.getCause());
     }
 }

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CopyTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CopyTransform.java?rev=1714141&r1=1714140&r2=1714141&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CopyTransform.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CopyTransform.java [UTF-8] Thu Nov 12 23:47:52 2015
@@ -17,7 +17,6 @@
 package org.apache.sis.referencing.operation.transform;
 
 import java.util.Arrays;
-import java.io.Serializable;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.MathTransform;
@@ -39,10 +38,10 @@ import org.apache.sis.referencing.operat
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.7
  * @module
  */
-final class CopyTransform extends AbstractLinearTransform implements Serializable {
+final class CopyTransform extends AbstractLinearTransform {
     /**
      * Serial number for inter-operability with different versions.
      */
@@ -61,11 +60,6 @@ final class CopyTransform extends Abstra
     private final int[] indices;
 
     /**
-     * The inverse transform. Will be created only when first needed.
-     */
-    private transient MathTransform inverse;
-
-    /**
      * Creates a new transform.
      *
      * @param srcDim The dimension of source coordinates.
@@ -85,9 +79,9 @@ final class CopyTransform extends Abstra
     static CopyTransform create(final Matrix matrix) {
         final int srcDim = matrix.getNumCol() - 1;
         final int dstDim = matrix.getNumRow() - 1;
-        for (int i=0; i<=srcDim; i++) {
+        for (int i=0; i <= srcDim; i++) {
             if (matrix.getElement(dstDim, i) != (i == srcDim ? 1 : 0)) {
-                // Not an affine transform.
+                // Not an affine transform (ignoring if square or not).
                 return null;
             }
         }
@@ -339,8 +333,9 @@ final class CopyTransform extends Abstra
     public synchronized MathTransform inverse() throws NoninvertibleTransformException {
         if (inverse == null) {
             /*
-             * Note: we do not perform the following optimization, because MathTransforms.linear(…)
-             *       should never instantiate this class in the identity case.
+             * Note: no need to perform the following check as this point because MathTransforms.linear(…)
+             *       should never instantiate this class in the identity case and because we perform an
+             *       equivalent check later anyway.
              *
              *       if (isIdentity()) {
              *           inverse = this;
@@ -361,7 +356,7 @@ final class CopyTransform extends Abstra
             for (int j=srcDim; --j>=0;) {
                 if (reverse[j] < 0) {
                     final MatrixSIS matrix = Matrices.createZero(srcDim + 1, dstDim + 1);
-                    for (j=0; j<srcDim; j++) { // NOSONAR: the outer loop will not continue.
+                    for (j=0; j<srcDim; j++) {      // Okay to reuse 'j' since the outer loop will not continue.
                         final int i = reverse[j];
                         if (i >= 0) {
                             matrix.setElement(j, i, 1);
@@ -371,14 +366,16 @@ final class CopyTransform extends Abstra
                     }
                     matrix.setElement(srcDim, dstDim, 1);
                     inverse = MathTransforms.linear(matrix);
-                    if (inverse instanceof ProjectiveTransform) {
-                        ((ProjectiveTransform) inverse).inverse = this;
+                    if (inverse instanceof AbstractLinearTransform) {
+                        ((AbstractLinearTransform) inverse).inverse = this;
                     }
                     return inverse;
                 }
             }
             /*
-             * At this point, we known that we can create the inverse transform.
+             * At this point, we know that we can create the inverse transform.
+             * If this transform is the identity transform (we should never happen,
+             * but we are paranoiac), then the old and new arrays would be equal.
              */
             CopyTransform copyInverse = this;
             if (!Arrays.equals(reverse, indices)) {

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java?rev=1714141&r1=1714140&r2=1714141&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] Thu Nov 12 23:47:52 2015
@@ -152,7 +152,7 @@ import org.apache.sis.internal.jdk8.JDK8
  *
  * @author  Martin Desruisseaux (Geomatys, IRD)
  * @since   0.6
- * @version 0.6
+ * @version 0.7
  * @module
  *
  * @see MathTransformProvider
@@ -560,7 +560,7 @@ public class DefaultMathTransformFactory
              * NormalizedProjection class (our base class for all map projection implementations)
              * to known that the ellipsoid definitive parameter is the inverse flattening factor
              * instead than the semi-major axis length. It makes a small difference in the accuracy
-             * of the excentricity parameter.
+             * of the eccentricity parameter.
              */
             if (isIvfDefinitive) try {
                 parameters.parameter(Constants.INVERSE_FLATTENING).setValue(ellipsoid.getInverseFlattening());
@@ -782,12 +782,15 @@ public class DefaultMathTransformFactory
             throws FactoryException
     {
         lastMethod.remove();
+        ArgumentChecks.ensureNonNull("tr1", tr1);
+        ArgumentChecks.ensureNonNull("tr2", tr2);
         final MathTransform tr;
         try {
-            tr = MathTransforms.concatenate(tr1, tr2);
+            tr = ConcatenatedTransform.create(tr1, tr2, this);
         } catch (IllegalArgumentException exception) {
             throw new FactoryException(exception);
         }
+        assert MathTransforms.isValid(MathTransforms.getSteps(tr)) : tr;
         return unique(tr);
     }
 

Copied: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java (from r1714140, sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java?p2=sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java&p1=sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java&r1=1714140&r2=1714141&rev=1714141&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java [UTF-8] Thu Nov 12 23:47:52 2015
@@ -738,6 +738,7 @@ next:   while (--numPts >= 0) {
          * Creates the inverse of the enclosing transform.
          */
         Inverse() {
+            EllipsoidToCentricTransform.this.super();
         }
 
         /**

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ExponentialTransform1D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ExponentialTransform1D.java?rev=1714141&r1=1714140&r2=1714141&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ExponentialTransform1D.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ExponentialTransform1D.java [UTF-8] Thu Nov 12 23:47:52 2015
@@ -19,6 +19,8 @@ package org.apache.sis.referencing.opera
 import java.io.Serializable;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransform1D;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.util.FactoryException;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.util.ComparisonMode;
 
@@ -219,11 +221,13 @@ final class ExponentialTransform1D exten
      * @param  applyOtherFirst {@code true} if the transformation order is {@code other}
      *         followed by {@code this}, or {@code false} if the transformation order is
      *         {@code this} followed by {@code other}.
-     * @return The combined math transform, or {@code null} if no optimized combined
-     *         transform is available.
+     * @param  factory The factory which is (indirectly) invoking this method, or {@code null} if none.
+     * @return The combined math transform, or {@code null} if no optimized combined transform is available.
      */
     @Override
-    final MathTransform concatenate(final MathTransform other, final boolean applyOtherFirst) {
+    final MathTransform concatenate(final MathTransform other, final boolean applyOtherFirst,
+            final MathTransformFactory factory) throws FactoryException
+    {
         if (other instanceof LinearTransform) {
             final LinearTransform1D linear = (LinearTransform1D) other;
             if (applyOtherFirst) {
@@ -240,7 +244,7 @@ final class ExponentialTransform1D exten
         } else if (other instanceof LogarithmicTransform1D) {
             return concatenateLog((LogarithmicTransform1D) other, applyOtherFirst);
         }
-        return super.concatenate(other, applyOtherFirst);
+        return super.concatenate(other, applyOtherFirst, factory);
     }
 
     /**

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/IdentityTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/IdentityTransform.java?rev=1714141&r1=1714140&r2=1714141&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/IdentityTransform.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/IdentityTransform.java [UTF-8] Thu Nov 12 23:47:52 2015
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.referencing.operation.transform;
 
-import java.io.Serializable;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.MathTransform;
@@ -37,7 +36,7 @@ import org.apache.sis.internal.referenci
  * @version 0.6
  * @module
  */
-final class IdentityTransform extends AbstractLinearTransform implements Serializable {
+final class IdentityTransform extends AbstractLinearTransform {
     /**
      * Serial number for inter-operability with different versions.
      */

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LogarithmicTransform1D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LogarithmicTransform1D.java?rev=1714141&r1=1714140&r2=1714141&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LogarithmicTransform1D.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LogarithmicTransform1D.java [UTF-8] Thu Nov 12 23:47:52 2015
@@ -19,6 +19,8 @@ package org.apache.sis.referencing.opera
 import java.io.Serializable;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransform1D;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.util.FactoryException;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.math.MathFunctions;
 import org.apache.sis.util.ArgumentChecks;
@@ -114,10 +116,13 @@ class LogarithmicTransform1D extends Abs
      * @param  other The math transform to apply.
      * @param  applyOtherFirst {@code true} if the transformation order is {@code other} followed by {@code this},
      *         or {@code false} if the transformation order is {@code this} followed by {@code other}.
+     * @param  factory The factory which is (indirectly) invoking this method, or {@code null} if none.
      * @return The combined math transform, or {@code null} if no optimized combined transform is available.
      */
     @Override
-    final MathTransform concatenate(final MathTransform other, final boolean applyOtherFirst) {
+    final MathTransform concatenate(final MathTransform other, final boolean applyOtherFirst,
+            final MathTransformFactory factory) throws FactoryException
+    {
         if (other instanceof LinearTransform1D) {
             final LinearTransform1D linear = (LinearTransform1D) other;
             if (applyOtherFirst) {
@@ -133,7 +138,7 @@ class LogarithmicTransform1D extends Abs
         } else if (other instanceof ExponentialTransform1D) {
             return ((ExponentialTransform1D) other).concatenateLog(this, !applyOtherFirst);
         }
-        return super.concatenate(other, applyOtherFirst);
+        return super.concatenate(other, applyOtherFirst, factory);
     }
 
     /**

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java?rev=1714141&r1=1714140&r2=1714141&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java [UTF-8] Thu Nov 12 23:47:52 2015
@@ -19,6 +19,7 @@ package org.apache.sis.referencing.opera
 import java.util.List;
 import java.util.Collections;
 import java.awt.geom.AffineTransform;
+import org.opengis.util.FactoryException;
 import org.opengis.geometry.MismatchedDimensionException;
 import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.MathTransform;
@@ -52,7 +53,7 @@ import static org.apache.sis.util.Argume
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.7
  * @module
  *
  * @see MathTransformFactory
@@ -143,7 +144,7 @@ public final class MathTransforms extend
         if (candidate != null) {
             return candidate;
         }
-        return new ProjectiveTransform(matrix);
+        return new ProjectiveTransform(matrix).optimize();
     }
 
     /**
@@ -185,7 +186,7 @@ public final class MathTransforms extend
             if (compound == null) {
                 compound = tr;
             } else {
-                compound = ConcatenatedTransform.create(compound, tr);
+                compound = concatenate(compound, tr);
             }
         }
         assert isValid(getSteps(compound)) : compound;
@@ -210,7 +211,12 @@ public final class MathTransforms extend
     {
         ensureNonNull("tr1", tr1);
         ensureNonNull("tr2", tr2);
-        final MathTransform tr = ConcatenatedTransform.create(tr1, tr2);
+        final MathTransform tr;
+        try {
+            tr = ConcatenatedTransform.create(tr1, tr2, null);
+        } catch (FactoryException e) {
+            throw new IllegalArgumentException(e);      // Should never happen actually.
+        }
         assert isValid(getSteps(tr)) : tr;
         return tr;
     }
@@ -310,7 +316,7 @@ public final class MathTransforms extend
      * (because their matrices should have been multiplied together).
      * This is used for assertion purposes only.
      */
-    private static boolean isValid(final List<MathTransform> steps) {
+    static boolean isValid(final List<MathTransform> steps) {
         boolean wasLinear = false;
         for (final MathTransform step : steps) {
             if (step instanceof LinearTransform) {

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PowerTransform1D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PowerTransform1D.java?rev=1714141&r1=1714140&r2=1714141&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PowerTransform1D.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PowerTransform1D.java [UTF-8] Thu Nov 12 23:47:52 2015
@@ -19,6 +19,8 @@ package org.apache.sis.referencing.opera
 import java.io.Serializable;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransform1D;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.util.FactoryException;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.util.ComparisonMode;
 
@@ -172,15 +174,18 @@ final class PowerTransform1D extends Abs
      * @param  other The math transform to apply.
      * @param  applyOtherFirst {@code true} if the transformation order is {@code other} followed by {@code this},
      *         or {@code false} if the transformation order is {@code this} followed by {@code other}.
+     * @param  factory The factory which is (indirectly) invoking this method, or {@code null} if none.
      * @return The combined math transform, or {@code null} if no optimized combined transform is available.
      */
     @Override
-    final MathTransform concatenate(final MathTransform other, final boolean applyOtherFirst) {
+    final MathTransform concatenate(final MathTransform other, final boolean applyOtherFirst,
+            final MathTransformFactory factory) throws FactoryException
+    {
         if (other instanceof PowerTransform1D) {
             return create(power + ((PowerTransform1D) other).power);
         }
         // TODO: more optimization could go here for logarithmic and exponential cases.
-        return super.concatenate(other, applyOtherFirst);
+        return super.concatenate(other, applyOtherFirst, factory);
     }
 
     /**



Mime
View raw message