sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1679636 [3/5] - in /sis/trunk: ./ application/sis-console/src/main/java/org/apache/sis/console/ application/sis-console/src/test/java/org/apache/sis/console/ core/sis-feature/src/main/java/org/apache/sis/feature/ core/sis-metadata/src/main...
Date Fri, 15 May 2015 19:42:43 GMT
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] Fri May 15 19:42:41 2015
@@ -32,21 +32,23 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.metadata.Identifier;
 import org.apache.sis.parameter.Parameterized;
+import org.opengis.parameter.GeneralParameterValue;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.parameter.ParameterValueGroup;
 import org.apache.sis.io.wkt.Formatter;
 import org.apache.sis.io.wkt.FormattableObject;
 import org.apache.sis.util.iso.Types;
 import org.apache.sis.util.Classes;
-import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.collection.Containers;
 import org.apache.sis.util.UnsupportedImplementationException;
 import org.apache.sis.referencing.AbstractIdentifiedObject;
 import org.apache.sis.referencing.operation.transform.PassThroughTransform;
+import org.apache.sis.internal.referencing.ReferencingUtilities;
 import org.apache.sis.internal.referencing.OperationMethods;
 import org.apache.sis.internal.referencing.WKTUtilities;
+import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.internal.system.Semaphores;
 
@@ -74,7 +76,7 @@ import org.apache.sis.internal.jdk7.Obje
  * <div class="section">Instantiation</div>
  * This class is conceptually <cite>abstract</cite>, even if it is technically possible to instantiate it.
  * Typical applications should create instances of the most specific subclass prefixed by {@code Default} instead.
- * An exception to this rule may occur when it is not possible to identify the exact CRS type.
+ * An exception to this rule may occur when it is not possible to identify the exact operation type.
  *
  * <div class="section">Immutability and thread safety</div>
  * This base class is immutable and thus thread-safe if the property <em>values</em> (not necessarily the map itself)
@@ -162,40 +164,44 @@ public class AbstractCoordinateOperation
     private final MathTransform transform;
 
     /**
-     * Constructs a new object in which every attributes are set to a null value.
+     * Creates a new object in which every attributes are set to a null value.
      * <strong>This is not a valid object.</strong> This constructor is strictly
      * reserved to JAXB, which will assign values to the fields using reflexion.
      */
     AbstractCoordinateOperation() {
         super(org.apache.sis.internal.referencing.NilReferencingObject.INSTANCE);
-        sourceCRS = null;
-        targetCRS = null;
-        interpolationCRS = null;
-        operationVersion = null;
+        sourceCRS                   = null;
+        targetCRS                   = null;
+        interpolationCRS            = null;
+        operationVersion            = null;
         coordinateOperationAccuracy = null;
-        domainOfValidity = null;
-        scope = null;
-        transform = null;
+        domainOfValidity            = null;
+        scope                       = null;
+        transform                   = null;
     }
 
     /**
-     * Constructs a new coordinate operation with the same values than the specified defining conversion,
-     * together with the specified source and target CRS. This constructor is used by {@link DefaultConversion} only.
+     * Creates a new coordinate operation with the same values than the specified defining conversion,
+     * except for the source CRS, target CRS and the math transform which are set the given values.
+     *
+     * <p>This constructor is (indirectly) for {@link DefaultConversion} usage only,
+     * in order to create a "real" conversion from a defining conversion.</p>
      */
-    AbstractCoordinateOperation(final CoordinateOperation       definition,
+    AbstractCoordinateOperation(final CoordinateOperation definition,
                                 final CoordinateReferenceSystem sourceCRS,
-                                final CoordinateReferenceSystem targetCRS)
+                                final CoordinateReferenceSystem targetCRS,
+                                final MathTransform transform)
     {
         super(definition);
         this.sourceCRS                   = sourceCRS;
         this.targetCRS                   = targetCRS;
-        this.interpolationCRS            = null;
+        this.interpolationCRS            = getInterpolationCRS(definition);
         this.operationVersion            = definition.getOperationVersion();
         this.coordinateOperationAccuracy = definition.getCoordinateOperationAccuracy();
         this.domainOfValidity            = definition.getDomainOfValidity();
         this.scope                       = definition.getScope();
-        this.transform                   = definition.getMathTransform();
-        checkDimensions();
+        this.transform                   = transform;
+        checkDimensions(null);
     }
 
     /**
@@ -294,8 +300,6 @@ public class AbstractCoordinateOperation
                                        final MathTransform             transform)
     {
         super(properties);
-        ArgumentChecks.ensureNonNull("transform", transform);
-
         this.sourceCRS        = sourceCRS;
         this.targetCRS        = targetCRS;
         this.interpolationCRS = interpolationCRS;
@@ -307,30 +311,41 @@ public class AbstractCoordinateOperation
         if (value instanceof PositionalAccuracy[]) {
             coordinateOperationAccuracy = CollectionsExt.nonEmptySet((PositionalAccuracy[]) value);
         } else {
-            coordinateOperationAccuracy = (value == null) ? null :
-                    Collections.singleton((PositionalAccuracy) value);
+            coordinateOperationAccuracy = (value != null) ? Collections.singleton((PositionalAccuracy) value) : null;
         }
-        checkDimensions();
+        checkDimensions(properties);
     }
 
     /**
      * Ensures that {@link #sourceCRS}, {@link #targetCRS} and {@link #interpolationCRS} dimensions
      * are consistent with {@link #transform} input and output dimensions.
      */
-    private void checkDimensions() {
+    private void checkDimensions(final Map<String,?> properties) {
         if (transform != null) {
-            int sourceDim = transform.getSourceDimensions();
-            int targetDim = transform.getTargetDimensions();
-            if (interpolationCRS != null) {
-                final int dim = interpolationCRS.getCoordinateSystem().getDimension();
-                sourceDim -= dim;
-                targetDim -= dim;
-                if (sourceDim <= 0 || targetDim <= 0) {
-                    throw new IllegalArgumentException(Errors.format(Errors.Keys.MissingInterpolationOrdinates));
+            final int interpDim = ReferencingUtilities.getDimension(interpolationCRS);
+check:      for (int isTarget=0; ; isTarget++) {        // 0 == source check; 1 == target check.
+                final CoordinateReferenceSystem crs;    // Will determine the expected dimensions.
+                int actual;                             // The MathTransform number of dimensions.
+                switch (isTarget) {
+                    case 0: crs = sourceCRS; actual = transform.getSourceDimensions(); break;
+                    case 1: crs = targetCRS; actual = transform.getTargetDimensions(); break;
+                    default: break check;
+                }
+                int expected = ReferencingUtilities.getDimension(crs);
+                if (interpDim != 0) {
+                    if (actual == expected || actual < interpDim) {
+                        // This check is not strictly necessary as the next check below would catch the error,
+                        // but we provide here a hopefully more helpful error message for a common mistake.
+                        throw new IllegalArgumentException(Errors.getResources(properties)
+                                .getString(Errors.Keys.MissingInterpolationOrdinates));
+                    }
+                    expected += interpDim;
+                }
+                if (crs != null && actual != expected) {
+                    throw new IllegalArgumentException(Errors.getResources(properties).getString(
+                            Errors.Keys.MismatchedTransformDimension_3, isTarget, expected, actual));
                 }
             }
-            ArgumentChecks.ensureDimensionMatches("sourceCRS", sourceDim, sourceCRS);
-            ArgumentChecks.ensureDimensionMatches("targetCRS", targetDim, targetCRS);
         }
     }
 
@@ -447,7 +462,7 @@ public class AbstractCoordinateOperation
     /**
      * Returns the interpolation CRS of the given coordinate operation, or {@code null} if none.
      */
-    private static CoordinateReferenceSystem getInterpolationCRS(final CoordinateOperation operation) {
+    static CoordinateReferenceSystem getInterpolationCRS(final CoordinateOperation operation) {
         return (operation instanceof AbstractCoordinateOperation)
                ? ((AbstractCoordinateOperation) operation).getInterpolationCRS() : null;
     }
@@ -570,6 +585,8 @@ public class AbstractCoordinateOperation
     /**
      * Returns the operation method. This apply only to {@link AbstractSingleOperation} subclasses,
      * which will make this method public.
+     *
+     * @return The operation method, or {@code null} if none.
      */
     OperationMethod getMethod() {
         return null;
@@ -585,13 +602,13 @@ public class AbstractCoordinateOperation
         while (mt != null) {
             if (mt instanceof Parameterized) {
                 final ParameterDescriptorGroup param;
-                if (Semaphores.queryAndSet(Semaphores.PROJCS)) {
+                if (Semaphores.queryAndSet(Semaphores.ENCLOSED_IN_OPERATION)) {
                     throw new AssertionError(); // Should never happen.
                 }
                 try {
                     param = ((Parameterized) mt).getParameterDescriptors();
                 } finally {
-                    Semaphores.clear(Semaphores.PROJCS);
+                    Semaphores.clear(Semaphores.ENCLOSED_IN_OPERATION);
                 }
                 if (param != null) {
                     return param;
@@ -611,6 +628,7 @@ public class AbstractCoordinateOperation
      * Returns the parameter values. The default implementation infers the
      * parameter values from the {@linkplain #transform}, if possible.
      *
+     * @return The parameter values (never {@code null}).
      * @throws UnsupportedOperationException if the parameter values can not
      *         be determined for the current math transform implementation.
      */
@@ -619,13 +637,13 @@ public class AbstractCoordinateOperation
         while (mt != null) {
             if (mt instanceof Parameterized) {
                 final ParameterValueGroup param;
-                if (Semaphores.queryAndSet(Semaphores.PROJCS)) {
+                if (Semaphores.queryAndSet(Semaphores.ENCLOSED_IN_OPERATION)) {
                     throw new AssertionError(); // Should never happen.
                 }
                 try {
                     param = ((Parameterized) mt).getParameterValues();
                 } finally {
-                    Semaphores.clear(Semaphores.PROJCS);
+                    Semaphores.clear(Semaphores.ENCLOSED_IN_OPERATION);
                 }
                 if (param != null) {
                     return param;
@@ -721,27 +739,37 @@ public class AbstractCoordinateOperation
      *
      * @param  formatter The formatter to use.
      * @return {@code "CoordinateOperation"}.
+     *
+     * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#113">WKT 2 specification</a>
      */
     @Override
     protected String formatTo(final Formatter formatter) {
         super.formatTo(formatter);
-        append(formatter, getSourceCRS(), "SourceCRS");
-        append(formatter, getTargetCRS(), "TargetCRS");
+        formatter.newLine();
+        append(formatter, getSourceCRS(), WKTKeywords.SourceCRS);
+        append(formatter, getTargetCRS(), WKTKeywords.TargetCRS);
         formatter.append(DefaultOperationMethod.castOrCopy(getMethod()));
-        append(formatter, getInterpolationCRS(), "InterpolationCRS");
+        final ParameterValueGroup parameters = getParameterValues();
+        if (parameters != null) {
+            formatter.newLine();
+            for (final GeneralParameterValue param : parameters.values()) {
+                WKTUtilities.append(param, formatter);
+            }
+        }
+        append(formatter, getInterpolationCRS(), WKTKeywords.InterpolationCRS);
         final double accuracy = getLinearAccuracy();
         if (accuracy > 0) {
             formatter.append(new FormattableObject() {
                 @Override protected String formatTo(final Formatter formatter) {
                     formatter.append(accuracy);
-                    return "OperationAccuracy";
+                    return WKTKeywords.OperationAccuracy;
                 }
             });
         }
         if (formatter.getConvention().majorVersion() == 1) {
             formatter.setInvalidWKT(this, null);
         }
-        return "CoordinateOperation";
+        return WKTKeywords.CoordinateOperation;
     }
 
     /**
@@ -755,7 +783,9 @@ public class AbstractCoordinateOperation
         if (crs != null) {
             formatter.append(new FormattableObject() {
                 @Override protected String formatTo(final Formatter formatter) {
+                    formatter.indent(-1);
                     formatter.append(WKTUtilities.toFormattable(crs));
+                    formatter.indent(+1);
                     return type;
                 }
             });

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java [UTF-8] Fri May 15 19:42:41 2015
@@ -28,9 +28,12 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.parameter.Parameterized;
+import org.apache.sis.referencing.IdentifiedObjects;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.referencing.operation.transform.PassThroughTransform;
+import org.apache.sis.internal.referencing.ReferencingUtilities;
 import org.apache.sis.internal.referencing.OperationMethods;
+import org.apache.sis.internal.util.Constants;
 import org.apache.sis.util.collection.Containers;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.ArgumentChecks;
@@ -96,8 +99,9 @@ class AbstractSingleOperation extends Ab
                                    final MathTransform             transform)
     {
         super(properties, sourceCRS, targetCRS, interpolationCRS, transform);
-        ArgumentChecks.ensureNonNull("method", method);
-        checkDimensions(method, transform, properties);
+        ArgumentChecks.ensureNonNull("method",    method);
+        ArgumentChecks.ensureNonNull("transform", transform);
+        checkDimensions(method, ReferencingUtilities.getDimension(interpolationCRS), transform, properties);
         this.method = method;
         /*
          * Undocumented property, because SIS usually infers the parameters from the MathTransform.
@@ -109,18 +113,42 @@ class AbstractSingleOperation extends Ab
     }
 
     /**
-     * Constructs a new operation with the same values than the specified one, together with the
-     * specified source and target CRS. While the source operation can be an arbitrary one, it is
-     * typically a defining conversion.
+     * Creates a defining conversion from the given transform and/or parameters.
+     * See {@link DefaultConversion#DefaultConversion(Map, OperationMethod, MathTransform, ParameterValueGroup)}
+     * for more information.
+     */
+    AbstractSingleOperation(final Map<String,?>       properties,
+                            final OperationMethod     method,
+                            final MathTransform       transform,
+                            final ParameterValueGroup parameters)
+    {
+        super(properties, null, null, null, transform);
+        ArgumentChecks.ensureNonNull("method", method);
+        if (transform != null) {
+            checkDimensions(method, 0, transform, properties);
+        } else if (parameters == null) {
+            throw new IllegalArgumentException(Errors.getResources(properties)
+                    .getString(Errors.Keys.UnspecifiedParameterValues));
+        }
+        this.method = method;
+        this.parameters = (parameters != null) ? parameters.clone() : null;
+    }
+
+    /**
+     * Creates a new coordinate operation with the same values than the specified defining conversion,
+     * except for the source CRS, target CRS and the math transform which are set the given values.
+     *
+     * <p>This constructor is for {@link DefaultConversion} usage only,
+     * in order to create a "real" conversion from a defining conversion.</p>
      */
-    AbstractSingleOperation(final SingleOperation           definition,
+    AbstractSingleOperation(final SingleOperation definition,
                             final CoordinateReferenceSystem sourceCRS,
-                            final CoordinateReferenceSystem targetCRS)
+                            final CoordinateReferenceSystem targetCRS,
+                            final MathTransform transform)
     {
-        super(definition, sourceCRS, targetCRS);
+        super(definition, sourceCRS, targetCRS, transform);
         method = definition.getMethod();
-        parameters = (definition instanceof AbstractSingleOperation) ?
-                ((AbstractSingleOperation) definition).parameters : definition.getParameterValues();
+        parameters = getParameterValues(definition);
     }
 
     /**
@@ -135,8 +163,7 @@ class AbstractSingleOperation extends Ab
     protected AbstractSingleOperation(final SingleOperation operation) {
         super(operation);
         method = operation.getMethod();
-        parameters = (operation instanceof AbstractSingleOperation) ?
-                ((AbstractSingleOperation) operation).parameters : operation.getParameterValues();
+        parameters = getParameterValues(operation);
     }
 
     /**
@@ -161,18 +188,19 @@ class AbstractSingleOperation extends Ab
      * </ul>
      *
      * @param  method     The operation method to compare to the math transform.
+     * @param  interpDim  The number of interpolation dimension, or 0 if none.
      * @param  transform  The math transform to compare to the operation method.
      * @param  properties Properties of the caller object being constructed, used only for formatting error message.
      * @throws IllegalArgumentException if the number of dimensions are incompatible.
      */
-    static void checkDimensions(final OperationMethod method, MathTransform transform,
+    static void checkDimensions(final OperationMethod method, final int interpDim, MathTransform transform,
             final Map<String,?> properties) throws IllegalArgumentException
     {
         int actual = transform.getSourceDimensions();
         Integer expected = method.getSourceDimensions();
-        if (expected != null && actual > expected) {
+        if (expected != null && actual > expected + interpDim) {
             /*
-             * The given MathTransform use more dimensions than the OperationMethod.
+             * The given MathTransform uses more dimensions than the OperationMethod.
              * Try to locate one and only one sub-transform, ignoring axis swapping and scaling.
              */
             MathTransform subTransform = null;
@@ -193,19 +221,28 @@ class AbstractSingleOperation extends Ab
         }
         /*
          * Now verify if the MathTransform dimensions are equal to the OperationMethod ones,
-         * ignoring null java.lang.Integer instances.
+         * ignoring null java.lang.Integer instances.  We do not specify whether the method
+         * dimensions should include the interpolation dimensions or not, so we accept both.
          */
-        byte isTarget = 0; // false: wrong dimension is the source one.
-        if (expected == null || actual == expected) {
+        int isTarget = 0;   // 0 == false: the wrong dimension is the source one.
+        if (expected == null || (actual == expected) || (actual == expected + interpDim)) {
             actual = transform.getTargetDimensions();
             expected = method.getTargetDimensions();
-            if (expected == null || actual == expected) {
+            if (expected == null || (actual == expected) || (actual == expected + interpDim)) {
                 return;
             }
-            isTarget = 1; // true: wrong dimension is the target one.
+            isTarget = 1;   // 1 == true: the wrong dimension is the target one.
+        }
+        /*
+         * At least one dimension does not match.  In principle this is an error, but we make an exception for the
+         * "Affine parametric transformation" (EPSG:9624). The reason is that while OGC define that transformation
+         * as two-dimensional, it can easily be extended to any number of dimensions. Note that Apache SIS already
+         * has special handling for this operation (a TensorParameters dedicated class, etc.)
+         */
+        if (!IdentifiedObjects.isHeuristicMatchForName(method, Constants.AFFINE)) {
+            throw new IllegalArgumentException(Errors.getResources(properties).getString(
+                    Errors.Keys.MismatchedTransformDimension_3, isTarget, expected, actual));
         }
-        throw new IllegalArgumentException(Errors.getResources(properties).getString(
-                Errors.Keys.MismatchedTransformDimension_3, isTarget, expected, actual));
     }
 
     /**
@@ -235,9 +272,10 @@ class AbstractSingleOperation extends Ab
     }
 
     /**
-     * Returns the operation method.
+     * Returns a description of the operation method, including a list of expected parameter names.
+     * The returned object does not contains any parameter value.
      *
-     * @return The operation method.
+     * @return A description of the operation method.
      */
     @Override
     public OperationMethod getMethod() {
@@ -245,9 +283,15 @@ class AbstractSingleOperation extends Ab
     }
 
     /**
-     * Returns a description of the parameters. The default implementation tries to infer the
-     * description from the {@linkplain #getMathTransform() math transform} itself before to
-     * fallback on the {@linkplain DefaultOperationMethod#getParameters() method parameters}.
+     * Returns a description of the parameters. The default implementation performs the following choice:
+     *
+     * <ul>
+     *   <li>If parameter values were specified explicitely at construction time,
+     *       then the descriptor of those parameters is returned.</li>
+     *   <li>Otherwise if this method can infer the parameter descriptor from the
+     *       {@linkplain #getMathTransform() math transform}, then that descriptor is returned.</li>
+     *   <li>Otherwise fallback on the {@linkplain DefaultOperationMethod#getParameters() method parameters}.</li>
+     * </ul>
      *
      * <div class="note"><b>Note:</b>
      * the two parameter descriptions (from the {@code MathTransform} or from the {@code OperationMethod})
@@ -255,6 +299,9 @@ class AbstractSingleOperation extends Ab
      * values or units of measurement.</div>
      *
      * @return A description of the parameters.
+     *
+     * @see DefaultOperationMethod#getParameters()
+     * @see org.apache.sis.referencing.operation.transform.AbstractMathTransform#getParameterDescriptors()
      */
     @Override
     public ParameterDescriptorGroup getParameterDescriptors() {
@@ -262,12 +309,22 @@ class AbstractSingleOperation extends Ab
     }
 
     /**
-     * Returns the parameter values. The default implementation infers the parameter values from the
-     * {@linkplain #getMathTransform() math transform}, if possible.
+     * Returns the parameter values. The default implementation performs the following choice:
+     *
+     * <ul>
+     *   <li>If parameter values were specified explicitely at construction time, then a
+     *       {@linkplain org.apache.sis.parameter.DefaultParameterValueGroup#clone() clone}
+     *       of those parameters is returned.</li>
+     *   <li>Otherwise if this method can infer the parameter values from the
+     *       {@linkplain #getMathTransform() math transform}, then those parameters are returned.</li>
+     *   <li>Otherwise throw {@link org.apache.sis.util.UnsupportedImplementationException}.</li>
+     * </ul>
      *
      * @return The parameter values.
      * @throws UnsupportedOperationException if the parameter values can not be determined
      *         for the current math transform implementation.
+     *
+     * @see org.apache.sis.referencing.operation.transform.AbstractMathTransform#getParameterValues()
      */
     @Override
     public ParameterValueGroup getParameterValues() {
@@ -275,6 +332,16 @@ class AbstractSingleOperation extends Ab
     }
 
     /**
+     * Gets the parameter values of the given operation without computing and without cloning them (if possible).
+     * If the parameters are automatically inferred from the math transform, do not compute them and instead return
+     * {@code null} (in conformance with {@link #parameters} contract).
+     */
+    private static ParameterValueGroup getParameterValues(final SingleOperation operation) {
+        return (operation instanceof AbstractSingleOperation) ?
+                ((AbstractSingleOperation) operation).parameters : operation.getParameterValues();
+    }
+
+    /**
      * Compares this coordinate operation with the specified object for equality. If the {@code mode} argument
      * is {@link ComparisonMode#STRICT} or {@link ComparisonMode#BY_CONTRACT BY_CONTRACT}, then all available
      * properties are compared including the {@linkplain #getDomainOfValidity() domain of validity} and the

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] Fri May 15 19:42:41 2015
@@ -117,7 +117,7 @@ public class DefaultConcatenatedOperatio
                                          final MathTransformFactory factory)
             throws FactoryException
     {
-        this(properties, expand(operations, list, factory, true), list);
+        this(properties, expand(properties, operations, list, factory, true), list);
     }
 
     /**
@@ -148,10 +148,11 @@ public class DefaultConcatenatedOperatio
      * @return The concatenated math transform, or {@code null} if {@code wantTransform} was {@code false}.
      * @throws FactoryException if the factory can not concatenate the math transforms.
      */
-    private static MathTransform expand(final CoordinateOperation[] operations,
-                                        final List<SingleOperation> target,
-                                        final MathTransformFactory  factory,
-                                        final boolean wantTransform)
+    private static MathTransform expand(final Map<String,?> properties,
+            final CoordinateOperation[] operations,
+            final List<SingleOperation> target,
+            final MathTransformFactory  factory,
+            final boolean wantTransform)
             throws FactoryException
     {
         MathTransform transform = null;
@@ -164,9 +165,9 @@ public class DefaultConcatenatedOperatio
             } else if (op instanceof ConcatenatedOperation) {
                 final ConcatenatedOperation cop = (ConcatenatedOperation) op;
                 final List<SingleOperation> cops = cop.getOperations();
-                expand(cops.toArray(new CoordinateOperation[cops.size()]), target, factory, false);
+                expand(properties, cops.toArray(new CoordinateOperation[cops.size()]), target, factory, false);
             } else {
-                throw new IllegalArgumentException(Errors.format(
+                throw new IllegalArgumentException(Errors.getResources(properties).getString(
                         Errors.Keys.IllegalArgumentClass_2, "operations[" + i + ']', op.getClass()));
             }
             /*
@@ -180,8 +181,8 @@ public class DefaultConcatenatedOperatio
                         final int dim1 = previous.getCoordinateSystem().getDimension();
                         final int dim2 = next.getCoordinateSystem().getDimension();
                         if (dim1 != dim2) {
-                            throw new IllegalArgumentException(Errors.format(Errors.Keys.MismatchedDimension_3,
-                                    "operations[" + i + "].sourceCRS", dim1, dim2));
+                            throw new IllegalArgumentException(Errors.getResources(properties).getString(
+                                    Errors.Keys.MismatchedDimension_3, "operations[" + i + "].sourceCRS", dim1, dim2));
                         }
                     }
                 }
@@ -199,7 +200,7 @@ public class DefaultConcatenatedOperatio
             }
         }
         if (wantTransform && target.size() <= 1) {
-            throw new IllegalArgumentException(Errors.format(
+            throw new IllegalArgumentException(Errors.getResources(properties).getString(
                     Errors.Keys.TooFewOccurrences_2, 2, CoordinateOperation.class));
         }
         return transform;

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConicProjection.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConicProjection.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConicProjection.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConicProjection.java [UTF-8] Fri May 15 19:42:41 2015
@@ -17,8 +17,10 @@
 package org.apache.sis.referencing.operation;
 
 import javax.xml.bind.annotation.XmlTransient;
+import org.opengis.util.FactoryException;
 import org.opengis.referencing.operation.Conversion;
 import org.opengis.referencing.operation.ConicProjection;
+import org.opengis.referencing.operation.MathTransformFactory;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 
 
@@ -48,12 +50,14 @@ final class DefaultConicProjection exten
      * @param definition The defining conversion.
      * @param sourceCRS  The source CRS.
      * @param targetCRS  The target CRS.
+     * @param factory    The factory to use for creating a transform from the parameters or for performing axis changes.
      */
-    DefaultConicProjection(final Conversion                definition,
+    DefaultConicProjection(final Conversion definition,
                            final CoordinateReferenceSystem sourceCRS,
-                           final CoordinateReferenceSystem targetCRS)
+                           final CoordinateReferenceSystem targetCRS,
+                           final MathTransformFactory factory) throws FactoryException
     {
-        super(definition, sourceCRS, targetCRS);
+        super(definition, sourceCRS, targetCRS, factory);
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java [UTF-8] Fri May 15 19:42:41 2015
@@ -19,11 +19,27 @@ package org.apache.sis.referencing.opera
 import java.util.Map;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.XmlRootElement;
+import javax.measure.converter.ConversionException;
+import org.opengis.util.FactoryException;
+import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.referencing.operation.Conversion;
 import org.opengis.referencing.operation.OperationMethod;
 import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.crs.SingleCRS;
+import org.opengis.referencing.crs.GeneralDerivedCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.cs.CoordinateSystem;
+import org.opengis.referencing.datum.Datum;
+import org.apache.sis.referencing.cs.CoordinateSystems;
+import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
+import org.apache.sis.referencing.operation.matrix.Matrices;
+import org.apache.sis.internal.referencing.ReferencingUtilities;
+import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.Workaround;
+import org.apache.sis.util.Utilities;
 
 
 /**
@@ -33,9 +49,10 @@ import org.apache.sis.util.ArgumentCheck
  * The parameters describing coordinate conversions are defined rather than empirically derived.
  *
  * <p>This coordinate operation contains an {@linkplain DefaultOperationMethod operation method}, usually
- * with associated parameter values. In the SIS default implementation, the parameter values are inferred from the
- * {@linkplain #getMathTransform() math transform}. Subclasses may have to override the {@link #getParameterValues()}
- * method if they need to provide a different set of parameters.</p>
+ * with associated {@linkplain org.apache.sis.parameter.DefaultParameterValueGroup parameter values}.
+ * In the SIS implementation, the parameter values can be either inferred from the
+ * {@linkplain org.apache.sis.referencing.operation.transform.AbstractMathTransform math transform}
+ * or explicitely provided at construction time in a <cite>defining conversion</cite> (see below).</p>
  *
  * <div class="section">Defining conversions</div>
  * {@code OperationMethod} instances are generally created for a pair of existing {@linkplain #getSourceCRS() source}
@@ -44,14 +61,22 @@ import org.apache.sis.util.ArgumentCheck
  * {@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS projected CRS}.
  * Those <cite>defining conversions</cite> have no source and target CRS since those elements are provided by the
  * derived or projected CRS themselves. This class provides a {@linkplain #DefaultConversion(Map, OperationMethod,
- * MathTransform) constructor} for such defining conversions.
+ * MathTransform, ParameterValueGroup) constructor} for such defining conversions.
+ *
+ * <p>After the source and target CRS become known, we can invoke the {@link #specialize specialize(…)} method for
+ * {@linkplain DefaultMathTransformFactory#createBaseToDerived(CoordinateReferenceSystem, ParameterValueGroup,
+ * CoordinateSystem) creating a math transform from the parameters}, instantiate a new {@code Conversion} of a
+ * more specific type
+ * ({@link org.opengis.referencing.operation.ConicProjection},
+ *  {@link org.opengis.referencing.operation.CylindricalProjection} or
+ *  {@link org.opengis.referencing.operation.PlanarProjection}) if possible,
+ * and assign the source and target CRS to it.</p>
  *
  * <div class="section">Immutability and thread safety</div>
- * This base class is immutable and thus thread-safe if the property <em>values</em> (not necessarily the map itself)
- * given to the constructor are also immutable. Most SIS subclasses and related classes are immutable under similar
- * conditions. This means that unless otherwise noted in the javadoc, {@code CoordinateOperation} instances created
- * using only SIS factories and static constants can be shared by many objects and passed between threads without
- * synchronization.
+ * This class is immutable and thus thread-safe if the property <em>values</em> (not necessarily the map itself)
+ * given to the constructor are also immutable. This means that unless otherwise noted in the javadoc,
+ * {@code Conversion} instances created using only SIS factories and static constants can be shared
+ * by many objects and passed between threads without synchronization.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.6
@@ -109,10 +134,12 @@ public class DefaultConversion extends A
      *
      * @param properties The properties to be given to the identified object.
      * @param sourceCRS  The source CRS.
-     * @param targetCRS  The target CRS.
+     * @param targetCRS  The target CRS, which shall use a datum
+     *                   {@linkplain Utilities#equalsIgnoreMetadata equals (ignoring metadata)} to the source CRS datum.
      * @param interpolationCRS The CRS of additional coordinates needed for the operation, or {@code null} if none.
      * @param method     The coordinate operation method (mandatory in all cases).
      * @param transform  Transform from positions in the source CRS to positions in the target CRS.
+     * @throws MismatchedDatumException if the source and target CRS use different datum.
      */
     public DefaultConversion(final Map<String,?>             properties,
                              final CoordinateReferenceSystem sourceCRS,
@@ -124,27 +151,51 @@ public class DefaultConversion extends A
         super(properties, sourceCRS, targetCRS, interpolationCRS, method, transform);
         ArgumentChecks.ensureNonNull("sourceCRS", sourceCRS);
         ArgumentChecks.ensureNonNull("targetCRS", targetCRS);
+        ensureCompatibleDatum("targetCRS", sourceCRS, targetCRS);
     }
 
     /**
-     * Creates a defining conversion from the given transform.
-     * This conversion has no source and target CRS since those elements will be provided by the
-     * {@linkplain org.apache.sis.referencing.crs.DefaultDerivedCRS derived} or
-     * {@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS projected CRS}.
+     * Creates a defining conversion from the given transform and/or parameters.
+     * This conversion has no source and target CRS since those elements are usually unknown
+     * at <cite>defining conversion</cite> construction time.
+     * The source and target CRS will become known later, at the
+     * {@linkplain org.apache.sis.referencing.crs.DefaultDerivedCRS Derived CRS} or
+     * {@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS Projected CRS}
+     * construction time.
      *
-     * <p>The properties given in argument follow the same rules than for the
+     * <p>The {@code properties} map given in argument follows the same rules than for the
      * {@linkplain #DefaultConversion(Map, CoordinateReferenceSystem, CoordinateReferenceSystem,
      * CoordinateReferenceSystem, OperationMethod, MathTransform) above constructor}.</p>
      *
+     * <div class="section">Transform and parameters arguments</div>
+     * At least one of the {@code transform} or {@code parameters} argument must be non-null.
+     * If the caller supplies a {@code transform} argument, then it shall be a transform expecting
+     * {@linkplain org.apache.sis.referencing.cs.AxesConvention#NORMALIZED normalized} input coordinates
+     * and producing normalized output coordinates. See {@link org.apache.sis.referencing.cs.AxesConvention}
+     * for more information about what Apache SIS means by "normalized".
+     *
+     * <p>If the caller can not yet supply a {@code MathTransform}, then (s)he shall supply the parameter values needed
+     * for creating that transform, with the possible omission of {@code "semi_major"} and {@code "semi_minor"} values.
+     * The semi-major and semi-minor parameter values will be set automatically when the
+     * {@link #specialize specialize(…)} method will be invoked.</p>
+     *
+     * <p>If both the {@code transform} and {@code parameters} arguments are non-null, then the later should describes
+     * the parameters used for creating the transform. Those parameters will be stored for information purpose and can
+     * be given back by the {@link #getParameterValues()} method.</p>
+     *
      * @param properties The properties to be given to the identified object.
      * @param method     The operation method.
-     * @param transform  Transform from positions in the source CRS to positions in the target CRS.
+     * @param transform  Transform from positions in the source CRS to positions in the target CRS, or {@code null}.
+     * @param parameters The {@code transform} parameter values, or {@code null}.
+     *
+     * @see DefaultMathTransformFactory#createBaseToDerived(CoordinateSystem, MathTransform, CoordinateSystem)
      */
-    public DefaultConversion(final Map<String,?>   properties,
-                             final OperationMethod method,
-                             final MathTransform   transform)
+    public DefaultConversion(final Map<String,?>       properties,
+                             final OperationMethod     method,
+                             final MathTransform       transform,
+                             final ParameterValueGroup parameters)
     {
-        super(properties, null, null, null, method, transform);
+        super(properties, method, transform, parameters);
     }
 
     /**
@@ -155,12 +206,14 @@ public class DefaultConversion extends A
      * @param definition The defining conversion.
      * @param sourceCRS  The source CRS.
      * @param targetCRS  The target CRS.
+     * @param factory    The factory to use for creating a transform from the parameters or for performing axis changes.
      */
-    DefaultConversion(final Conversion                definition,
+    DefaultConversion(final Conversion definition,
                       final CoordinateReferenceSystem sourceCRS,
-                      final CoordinateReferenceSystem targetCRS)
+                      final CoordinateReferenceSystem targetCRS,
+                      final MathTransformFactory factory) throws FactoryException
     {
-        super(definition, sourceCRS, targetCRS);
+        super(definition, sourceCRS, targetCRS, createMathTransform(definition, sourceCRS, targetCRS, factory));
     }
 
     /**
@@ -224,8 +277,8 @@ public class DefaultConversion extends A
     /**
      * Returns a specialization of this conversion with a more specific type, source and target CRS.
      * This {@code specialize(…)} method is typically invoked on {@linkplain #DefaultConversion(Map,
-     * OperationMethod, MathTransform) defining conversion} instances, when more information become
-     * available about the conversion to create.
+     * OperationMethod, MathTransform, ParameterValueGroup) defining conversion} instances,
+     * when more information become available about the conversion to create.
      *
      * <p>The given {@code baseType} argument can be one of the following values:</p>
      * <ul>
@@ -244,17 +297,168 @@ public class DefaultConversion extends A
      * @param  baseType   The base GeoAPI interface to be implemented by the conversion to return.
      * @param  sourceCRS  The source CRS.
      * @param  targetCRS  The target CRS.
+     * @param  factory    The factory to use for creating a transform from the parameters or for performing axis changes.
      * @return The conversion of the given type between the given CRS.
      * @throws ClassCastException if a contradiction is found between the given {@code baseType},
      *         the defining {@linkplain DefaultConversion#getInterface() conversion type} and
      *         the {@linkplain DefaultOperationMethod#getOperationType() method operation type}.
+     * @throws MismatchedDatumException if the given CRS do not use the same datum than the source and target CRS
+     *         of this conversion.
+     * @throws FactoryException if the creation of a {@link MathTransform} from the {@linkplain #getParameterValues()
+     *         parameter values}, or a {@linkplain CoordinateSystems#swapAndScaleAxes change of axis order or units}
+     *         failed.
+     *
+     * @see DefaultMathTransformFactory#createBaseToDerived(CoordinateReferenceSystem, ParameterValueGroup, CoordinateSystem)
      */
     public <T extends Conversion> T specialize(final Class<T> baseType,
-            final CoordinateReferenceSystem sourceCRS, final CoordinateReferenceSystem targetCRS)
+            final CoordinateReferenceSystem sourceCRS, final CoordinateReferenceSystem targetCRS,
+            final MathTransformFactory factory) throws FactoryException
     {
         ArgumentChecks.ensureNonNull("baseType",  baseType);
         ArgumentChecks.ensureNonNull("sourceCRS", sourceCRS);
         ArgumentChecks.ensureNonNull("targetCRS", targetCRS);
-        return SubTypes.create(baseType, this, sourceCRS, targetCRS);
+        ArgumentChecks.ensureNonNull("factory",   factory);
+        /*
+         * Conceptual consistency check: verify that the new CRS use the same datum than the previous ones,
+         * since the purpose of this method is not to apply datum changes. Datum changes are the purpose of
+         * a dedicated kind of operations, namely Transformation.
+         */
+        ensureCompatibleDatum("sourceCRS", super.getSourceCRS(), sourceCRS);
+        if (!(targetCRS instanceof GeneralDerivedCRS)) {
+            ensureCompatibleDatum("targetCRS", super.getTargetCRS(), targetCRS);
+        } else {
+            /*
+             * Special case for derived and projected CRS: we can not check directly the datum of the target CRS
+             * of a derived CRS, because this method is invoked indirectly by SIS AbstractDerivedCRS constructor
+             * before its 'conversionFromBase' field is set. Since the Apache SIS implementations of derived CRS
+             * map the datum to getConversionFromBase().getSourceCRS().getDatum(), invoking targetCRS.getDatum()
+             * below may result in a NullPointerException. Instead we verify that 'this' conversion use the same
+             * datum for source and target CRS, since DerivedCRS and ProjectedCRS are expected to have the same
+             * datum than their source CRS.
+             */
+            if (super.getTargetCRS() != null) {
+                ensureCompatibleDatum("targetCRS", sourceCRS, super.getTargetCRS());
+            }
+        }
+        return SubTypes.create(baseType, this, sourceCRS, targetCRS, factory);
+    }
+
+    /**
+     * Ensures that the {@code actual} CRS uses a datum which is equals, ignoring metadata,
+     * to the datum of the {@code expected} CRS.
+     *
+     * @param param     The parameter name, used only in case of error.
+     * @param expected  The CRS containing the expected datum, or {@code null}.
+     * @param actual    The CRS for which to check the datum, or {@code null}.
+     * @throws MismatchedDatumException if the two CRS use different datum.
+     */
+    private static void ensureCompatibleDatum(final String param,
+            final CoordinateReferenceSystem expected,
+            final CoordinateReferenceSystem actual)
+    {
+        if ((expected instanceof SingleCRS) && (actual instanceof SingleCRS)) {
+            final Datum datum = ((SingleCRS) expected).getDatum();
+            if (datum != null && !Utilities.equalsIgnoreMetadata(datum, ((SingleCRS) actual).getDatum())) {
+                throw new MismatchedDatumException(Errors.format(
+                        Errors.Keys.IncompatibleDatum_2, datum.getName(), param));
+            }
+        }
+    }
+
+    /**
+     * Creates the math transform to be given to the sub-class constructor.
+     * This method is a workaround for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in constructors").
+     */
+    @Workaround(library="JDK", version="1.7")
+    private static MathTransform createMathTransform(
+            final Conversion definition,
+            final CoordinateReferenceSystem sourceCRS,
+            final CoordinateReferenceSystem targetCRS,
+            final MathTransformFactory factory) throws FactoryException
+    {
+        final int interpDim = ReferencingUtilities.getDimension(getInterpolationCRS(definition));
+        MathTransform mt = definition.getMathTransform();
+        if (mt == null) {
+            /*
+             * If the user did not specified explicitely a MathTransform, we will need to create it
+             * from the parameters. This case happen often when creating a ProjectedCRS, because the
+             * user often did not have all needed information when he created the defining conversion:
+             * the length of semi-major and semi-minor axes were often missing. But now we know those
+             * lengths thanks to the 'sourceCRS' argument given to this method. So we can complete the
+             * parameters. This is the job of MathTransformFactory.createBaseToDerived(…).
+             */
+            final ParameterValueGroup parameters = definition.getParameterValues();
+            if (parameters == null) {
+                throw new IllegalArgumentException(Errors.format(Errors.Keys.UnspecifiedParameterValues));
+            }
+            mt = factory.createBaseToDerived(sourceCRS, parameters, targetCRS.getCoordinateSystem());
+        } else {
+            /*
+             * If the user specified explicitely a MathTransform, we may still need to swap or scale axes.
+             * If this conversion is a defining conversion (which is usually the case when creating a new
+             * ProjectedCRS), then DefaultMathTransformFactory has a specialized createBaseToDerived(…)
+             * method for this job.
+             */
+            final CoordinateReferenceSystem mtSource = definition.getSourceCRS();
+            final CoordinateReferenceSystem mtTarget = definition.getTargetCRS();
+            if (mtSource == null && mtTarget == null && factory instanceof DefaultMathTransformFactory) {
+                mt = ((DefaultMathTransformFactory) factory).createBaseToDerived(
+                        sourceCRS.getCoordinateSystem(), mt,
+                        targetCRS.getCoordinateSystem());
+            } else {
+                /*
+                 * If we can not use our SIS factory implementation, or if this conversion is not a defining
+                 * conversion (i.e. if this is the conversion of an existing ProjectedCRS, in which case the
+                 * math transform may not be normalized), then we fallback on a simpler swapAndScaleAxes(…)
+                 * method defined in this class. This is needed for AbstractCRS.forConvention(AxisConvention).
+                 */
+                mt = swapAndScaleAxes(mt, sourceCRS, mtSource, interpDim, true,  factory);
+                mt = swapAndScaleAxes(mt, mtTarget, targetCRS, interpDim, false, factory);
+                return mt;  // Skip createPassThroughTransform(…) since it was handled by swapAndScaleAxes(…).
+            }
+        }
+        if (interpDim != 0) {
+            mt = factory.createPassThroughTransform(interpDim, mt, 0);
+        }
+        return mt;
+    }
+
+    /**
+     * Concatenates to the given transform the operation needed for swapping and scaling the axes.
+     * The two coordinate systems must implement the same GeoAPI coordinate system interface.
+     * For example if {@code sourceCRS} uses a {@code CartesianCS}, then {@code targetCRS} must use
+     * a {@code CartesianCS} too.
+     *
+     * @param transform The transform to which to concatenate axis changes.
+     * @param sourceCRS The first CRS of the pair for which to check for axes changes.
+     * @param targetCRS The second CRS of the pair for which to check for axes changes.
+     * @param interpDim The number of dimensions of the interpolation CRS, or 0 if none.
+     * @param isSource  {@code true} for pre-concatenating the changes, or {@code false} for post-concatenating.
+     * @param factory   The factory to use for performing axis changes.
+     */
+    private static MathTransform swapAndScaleAxes(MathTransform transform,
+            final CoordinateReferenceSystem sourceCRS,
+            final CoordinateReferenceSystem targetCRS,
+            final int interpDim, final boolean isSource,
+            final MathTransformFactory factory) throws FactoryException
+    {
+        if (sourceCRS != null && targetCRS != null && sourceCRS != targetCRS) try {
+            Matrix m = CoordinateSystems.swapAndScaleAxes(sourceCRS.getCoordinateSystem(),
+                                                          targetCRS.getCoordinateSystem());
+            if (!m.isIdentity()) {
+                if (interpDim != 0) {
+                    m = Matrices.createPassThrough(interpDim, m, 0);
+                }
+                final MathTransform s = factory.createAffineTransform(m);
+                transform = factory.createConcatenatedTransform(isSource ? s : transform,
+                                                                isSource ? transform : s);
+            }
+        } catch (ConversionException e) {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2,
+                    (isSource ? "sourceCRS" : "targetCRS"),
+                    (isSource ?  sourceCRS  :  targetCRS).getName()), e);
+        }
+        return transform;
     }
 }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCylindricalProjection.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCylindricalProjection.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCylindricalProjection.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCylindricalProjection.java [UTF-8] Fri May 15 19:42:41 2015
@@ -17,8 +17,10 @@
 package org.apache.sis.referencing.operation;
 
 import javax.xml.bind.annotation.XmlTransient;
+import org.opengis.util.FactoryException;
 import org.opengis.referencing.operation.Conversion;
 import org.opengis.referencing.operation.CylindricalProjection;
+import org.opengis.referencing.operation.MathTransformFactory;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 
 
@@ -48,12 +50,14 @@ final class DefaultCylindricalProjection
      * @param definition The defining conversion.
      * @param sourceCRS  The source CRS.
      * @param targetCRS  The target CRS.
+     * @param factory    The factory to use for creating a transform from the parameters or for performing axis changes.
      */
-    DefaultCylindricalProjection(final Conversion                definition,
+    DefaultCylindricalProjection(final Conversion definition,
                                  final CoordinateReferenceSystem sourceCRS,
-                                 final CoordinateReferenceSystem targetCRS)
+                                 final CoordinateReferenceSystem targetCRS,
+                                 final MathTransformFactory factory) throws FactoryException
     {
-        super(definition, sourceCRS, targetCRS);
+        super(definition, sourceCRS, targetCRS, factory);
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultFormula.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultFormula.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultFormula.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultFormula.java [UTF-8] Fri May 15 19:42:41 2015
@@ -20,6 +20,7 @@ import java.io.Serializable;
 import org.opengis.util.InternationalString;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.referencing.operation.Formula;
+import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.io.wkt.ElementKind;
 import org.apache.sis.io.wkt.FormattableObject;
 import org.apache.sis.io.wkt.Formatter;
@@ -191,6 +192,6 @@ public class DefaultFormula extends Form
             formatter.append(text.toString(formatter.getLocale()), ElementKind.REMARKS);
         }
         formatter.setInvalidWKT(Formula.class, null);
-        return "Formula";
+        return WKTKeywords.Formula;
     }
 }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java [UTF-8] Fri May 15 19:42:41 2015
@@ -33,6 +33,7 @@ import org.apache.sis.util.ComparisonMod
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.internal.util.Citations;
+import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.parameter.Parameterized;
 import org.apache.sis.referencing.NamedIdentifier;
 import org.apache.sis.referencing.IdentifiedObjects;
@@ -103,7 +104,8 @@ import org.apache.sis.internal.jdk7.Obje
  * @since   0.5
  * @module
  *
- * @see DefaultSingleOperation
+ * @see DefaultConversion
+ * @see DefaultTransformation
  * @see org.apache.sis.referencing.operation.transform.MathTransformProvider
  */
 public class DefaultOperationMethod extends AbstractIdentifiedObject implements OperationMethod {
@@ -550,6 +552,9 @@ public class DefaultOperationMethod exte
      * {@link #DefaultOperationMethod(MathTransform)} constructor has been unable to infer it.</div>
      *
      * @return The parameters, or {@code null} if unknown.
+     *
+     * @see DefaultConversion#getParameterDescriptors()
+     * @see DefaultConversion#getParameterValues()
      */
     @Override
     public ParameterDescriptorGroup getParameters() {
@@ -637,6 +642,8 @@ public class DefaultOperationMethod exte
      * Formats this operation as a <cite>Well Known Text</cite> {@code Method[…]} element.
      *
      * @return {@code "Method"} (WKT 2) or {@code "Projection"} (WKT 1).
+     *
+     * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#118">WKT 2 specification</a>
      */
     @Override
     protected String formatTo(final Formatter formatter) {
@@ -660,10 +667,10 @@ public class DefaultOperationMethod exte
              */
             final Class<? extends SingleOperation> type = getOperationType();
             if (Projection.class.isAssignableFrom(type) || type.isAssignableFrom(Projection.class)) {
-                return "Projection";
+                return WKTKeywords.Projection;
             }
             formatter.setInvalidWKT(this, null);
         }
-        return "Method";
+        return WKTKeywords.Method;
     }
 }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPlanarProjection.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPlanarProjection.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPlanarProjection.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPlanarProjection.java [UTF-8] Fri May 15 19:42:41 2015
@@ -17,8 +17,10 @@
 package org.apache.sis.referencing.operation;
 
 import javax.xml.bind.annotation.XmlTransient;
+import org.opengis.util.FactoryException;
 import org.opengis.referencing.operation.Conversion;
 import org.opengis.referencing.operation.PlanarProjection;
+import org.opengis.referencing.operation.MathTransformFactory;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 
 
@@ -48,12 +50,14 @@ final class DefaultPlanarProjection exte
      * @param definition The defining conversion.
      * @param sourceCRS  The source CRS.
      * @param targetCRS  The target CRS.
+     * @param factory    The factory to use for creating a transform from the parameters or for performing axis changes.
      */
-    DefaultPlanarProjection(final Conversion                definition,
+    DefaultPlanarProjection(final Conversion definition,
                             final CoordinateReferenceSystem sourceCRS,
-                            final CoordinateReferenceSystem targetCRS)
+                            final CoordinateReferenceSystem targetCRS,
+                            final MathTransformFactory factory) throws FactoryException
     {
-        super(definition, sourceCRS, targetCRS);
+        super(definition, sourceCRS, targetCRS, factory);
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultProjection.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultProjection.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultProjection.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultProjection.java [UTF-8] Fri May 15 19:42:41 2015
@@ -17,8 +17,10 @@
 package org.apache.sis.referencing.operation;
 
 import javax.xml.bind.annotation.XmlTransient;
+import org.opengis.util.FactoryException;
 import org.opengis.referencing.operation.Conversion;
 import org.opengis.referencing.operation.Projection;
+import org.opengis.referencing.operation.MathTransformFactory;
 import org.opengis.referencing.crs.ProjectedCRS;
 import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
@@ -63,12 +65,14 @@ class DefaultProjection extends DefaultC
      * @param definition The defining conversion.
      * @param sourceCRS  The source CRS.
      * @param targetCRS  The target CRS.
+     * @param factory    The factory to use for creating a transform from the parameters or for performing axis changes.
      */
-    DefaultProjection(final Conversion                definition,
+    DefaultProjection(final Conversion definition,
                       final CoordinateReferenceSystem sourceCRS,
-                      final CoordinateReferenceSystem targetCRS)
+                      final CoordinateReferenceSystem targetCRS,
+                      final MathTransformFactory factory) throws FactoryException
     {
-        super(definition, sourceCRS, targetCRS);
+        super(definition, sourceCRS, targetCRS, factory);
         ArgumentChecks.ensureCanCast("sourceCRS", GeographicCRS.class, sourceCRS);
         ArgumentChecks.ensureCanCast("targetCRS", ProjectedCRS .class, targetCRS);
     }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultTransformation.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultTransformation.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultTransformation.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultTransformation.java [UTF-8] Fri May 15 19:42:41 2015
@@ -39,11 +39,10 @@ import org.apache.sis.util.ArgumentCheck
  * method if they need to provide a different set of parameters.</p>
  *
  * <div class="section">Immutability and thread safety</div>
- * This base class is immutable and thus thread-safe if the property <em>values</em> (not necessarily the map itself)
- * given to the constructor are also immutable. Most SIS subclasses and related classes are immutable under similar
- * conditions. This means that unless otherwise noted in the javadoc, {@code CoordinateOperation} instances created
- * using only SIS factories and static constants can be shared by many objects and passed between threads without
- * synchronization.
+ * This class is immutable and thus thread-safe if the property <em>values</em> (not necessarily the map itself)
+ * given to the constructor are also immutable. This means that unless otherwise noted in the javadoc,
+ * {@code Transformation} instances created using only SIS factories and static constants can be shared
+ * by many objects and passed between threads without synchronization.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.6

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubTypes.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubTypes.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubTypes.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubTypes.java [UTF-8] Fri May 15 19:42:41 2015
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.referencing.operation;
 
+import org.opengis.util.FactoryException;
 import org.opengis.referencing.operation.*;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.referencing.AbstractIdentifiedObject;
@@ -30,7 +31,7 @@ import org.apache.sis.referencing.Abstra
  * <ul>
  *   <li>{@link AbstractCoordinateOperation#castOrCopy(CoordinateOperation)}</li>
  *   <li>{@link DefaultConversion#castOrCopy(Conversion)}</li>
- *   <li>{@link DefaultConversion#specialize(Class, CoordinateReferenceSystem, CoordinateReferenceSystem)}</li>
+ *   <li>{@link DefaultConversion#specialize(Class, CoordinateReferenceSystem, CoordinateReferenceSystem, MathTransformFactory)}</li>
  * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
@@ -119,13 +120,15 @@ final class SubTypes {
      * @param  definition The defining conversion.
      * @param  sourceCRS  The source CRS.
      * @param  targetCRS  The target CRS.
+     * @param  factory    The factory to use for creating a transform from the parameters or for performing axis changes.
      * @return The conversion of the given type between the given CRS.
      * @throws ClassCastException if a contradiction is found between the given {@code baseType},
      *         the defining {@linkplain DefaultConversion#getInterface() conversion type} and
      *         the {@linkplain DefaultOperationMethod#getOperationType() method operation type}.
      */
     static <T extends Conversion> T create(final Class<T> baseType, final Conversion definition,
-            final CoordinateReferenceSystem sourceCRS, final CoordinateReferenceSystem targetCRS)
+            final CoordinateReferenceSystem sourceCRS, final CoordinateReferenceSystem targetCRS,
+            final MathTransformFactory factory) throws FactoryException
     {
         Class<? extends T> type = baseType;
         if (definition instanceof AbstractIdentifiedObject) {
@@ -142,16 +145,22 @@ final class SubTypes {
             }
         }
         final Conversion conversion;
-        if (CylindricalProjection.class.isAssignableFrom(type)) {
-            conversion = new DefaultCylindricalProjection(definition, sourceCRS, targetCRS);
+        if (type.isInstance(definition)
+                && definition.getSourceCRS() == sourceCRS
+                && definition.getTargetCRS() == targetCRS
+                && definition.getMathTransform() != null)
+        {
+            conversion = definition;
+        } else if (CylindricalProjection.class.isAssignableFrom(type)) {
+            conversion = new DefaultCylindricalProjection(definition, sourceCRS, targetCRS, factory);
         } else if (ConicProjection.class.isAssignableFrom(type)) {
-            conversion = new DefaultConicProjection(definition, sourceCRS, targetCRS);
+            conversion = new DefaultConicProjection(definition, sourceCRS, targetCRS, factory);
         } else if (PlanarProjection.class.isAssignableFrom(type)) {
-            conversion = new DefaultPlanarProjection(definition, sourceCRS, targetCRS);
+            conversion = new DefaultPlanarProjection(definition, sourceCRS, targetCRS, factory);
         } else if (Projection.class.isAssignableFrom(type)) {
-            conversion = new DefaultProjection(definition, sourceCRS, targetCRS);
+            conversion = new DefaultProjection(definition, sourceCRS, targetCRS, factory);
         } else {
-            conversion = new DefaultConversion(definition, sourceCRS, targetCRS);
+            conversion = new DefaultConversion(definition, sourceCRS, targetCRS, factory);
         }
         return type.cast(conversion);
     }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java [UTF-8] Fri May 15 19:42:41 2015
@@ -25,8 +25,18 @@
  * sub-packages, but most users will not need to deal with them directly:</p>
  *
  * <ul>
- *   <li>{@link org.apache.sis.referencing.operation.projection} for map projections</li>
- *   <li>{@link org.apache.sis.referencing.operation.transform} for any transform other than map projections</li>
+ *   <li>{@link org.apache.sis.referencing.operation.projection} — map projections,</li>
+ *   <li>{@link org.apache.sis.referencing.operation.transform} — any transform other than map projections.</li>
+ * </ul>
+ *
+ * <div class="section">Apache SIS extensions</div>
+ * Some SIS implementations provide additional methods that are not part of OGC/ISO specifications:
+ *
+ * <ul>
+ *   <li>{@link org.apache.sis.referencing.operation.AbstractCoordinateOperation#getLinearAccuracy() AbstractCoordinateOperation.getLinearAccuracy()}
+ *     — tries to convert the accuracy to metres,</li>
+ *   <li>{@link org.apache.sis.referencing.operation.DefaultConversion#specialize DefaultConversion.specialize(…)}
+ *     — changes a <cite>defining conversion</cite> into a complete conversion.</li>
  * </ul>
  *
  * <div class="section">Apache SIS specific behavior</div>
@@ -70,6 +80,5 @@ import javax.xml.bind.annotation.adapter
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
 import org.apache.sis.xml.Namespaces;
 import org.apache.sis.internal.jaxb.gco.*;
-import org.apache.sis.internal.jaxb.referencing.*;
 import org.apache.sis.internal.jaxb.metadata.EX_Extent;
 import org.apache.sis.internal.jaxb.metadata.DQ_PositionalAccuracy;

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] Fri May 15 19:42:41 2015
@@ -30,13 +30,13 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.referencing.operation.NoninvertibleTransformException;
 import org.opengis.referencing.operation.OperationMethod;
-import org.opengis.referencing.operation.SingleOperation;
 import org.apache.sis.geometry.GeneralDirectPosition;
 import org.apache.sis.parameter.Parameterized;
 import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.io.wkt.Formatter;
 import org.apache.sis.io.wkt.FormattableObject;
 import org.apache.sis.internal.referencing.WKTUtilities;
+import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.util.Utilities;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.LenientComparable;
@@ -160,7 +160,7 @@ public abstract class AbstractMathTransf
      *
      * @return The parameter descriptors for this math transform, or {@code null} if unspecified.
      *
-     * @see OperationMethod#getParameters()
+     * @see org.apache.sis.referencing.operation.DefaultOperationMethod#getParameters()
      */
     @Override
     public ParameterDescriptorGroup getParameterDescriptors() {
@@ -185,7 +185,7 @@ public abstract class AbstractMathTransf
      *         of an ellipsoid of semi-major axis length of 1).
      *
      * @see #getContextualParameters()
-     * @see SingleOperation#getParameterValues()
+     * @see org.apache.sis.referencing.operation.DefaultConversion#getParameterValues()
      */
     @Override
     public ParameterValueGroup getParameterValues() {
@@ -949,7 +949,7 @@ public abstract class AbstractMathTransf
     @Override
     protected String formatTo(final Formatter formatter) {
         WKTUtilities.appendParamMT(getParameterValues(), formatter);
-        return "Param_MT";
+        return WKTKeywords.Param_MT;
     }
 
     /**
@@ -1110,10 +1110,10 @@ public abstract class AbstractMathTransf
             final ParameterValueGroup parameters = getParameterValues();
             if (parameters != null) {
                 WKTUtilities.appendParamMT(parameters, formatter);
-                return "Param_MT";
+                return WKTKeywords.Param_MT;
             } else {
                 formatter.append((FormattableObject) AbstractMathTransform.this);
-                return "Inverse_MT";
+                return WKTKeywords.Inverse_MT;
             }
         }
     }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java [UTF-8] Fri May 15 19:42:41 2015
@@ -31,6 +31,7 @@ import org.opengis.referencing.operation
 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.metadata.WKTKeywords;
 import org.apache.sis.internal.system.Semaphores;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.LenientComparable;
@@ -88,7 +89,7 @@ class ConcatenatedTransform extends Abst
      * But it is serialized in order to avoid rounding errors if the inverse
      * transform is serialized instead of the original one.
      */
-    private ConcatenatedTransform inverse;
+    private MathTransform inverse;
 
     /**
      * Constructs a concatenated transform. This constructor is for subclasses only.
@@ -217,7 +218,35 @@ class ConcatenatedTransform extends Abst
          * Can not avoid the creation of a ConcatenatedTransform object.
          * Check for the type to create (1D, 2D, general case...)
          */
-        return createConcatenatedTransform(tr1, tr2);
+        final int dimSource = tr1.getSourceDimensions();
+        final int dimTarget = tr2.getTargetDimensions();
+        if (dimSource == 1 && dimTarget == 1) {
+            /*
+             * Result needs to be a MathTransform1D.
+             */
+            if (tr1 instanceof MathTransform1D && tr2 instanceof MathTransform1D) {
+                return new ConcatenatedTransformDirect1D((MathTransform1D) tr1,
+                                                         (MathTransform1D) tr2);
+            } else {
+                return new ConcatenatedTransform1D(tr1, tr2);
+            }
+        } else if (dimSource == 2 && dimTarget == 2) {
+            /*
+             * Result needs to be a MathTransform2D.
+             */
+            if (tr1 instanceof MathTransform2D && tr2 instanceof MathTransform2D) {
+                return new ConcatenatedTransformDirect2D((MathTransform2D) tr1,
+                                                         (MathTransform2D) tr2);
+            } else {
+                return new ConcatenatedTransform2D(tr1, tr2);
+            }
+        } else if (dimSource == tr1.getTargetDimensions()   // dim1 = tr1.getTargetDimensions() and
+                && dimTarget == tr2.getSourceDimensions())  // dim2 = tr2.getSourceDimensions() may not be true anymore.
+        {
+            return new ConcatenatedTransformDirect(tr1, tr2);
+        } else {
+            return new ConcatenatedTransform(tr1, tr2);
+        }
     }
 
     /**
@@ -296,44 +325,6 @@ class ConcatenatedTransform extends Abst
     }
 
     /**
-     * Continue the construction started by the {@link #create(MathTransform, MathTransform)} method.
-     * The construction step is available separately for testing purpose (in a JUnit test), and for
-     * {@link #inverse()} implementation.
-     */
-    static ConcatenatedTransform createConcatenatedTransform(
-            final MathTransform tr1, final MathTransform tr2)
-    {
-        final int dimSource = tr1.getSourceDimensions();
-        final int dimTarget = tr2.getTargetDimensions();
-        /*
-         * Checks if the result need to be a MathTransform1D.
-         */
-        if (dimSource == 1 && dimTarget == 1) {
-            if (tr1 instanceof MathTransform1D && tr2 instanceof MathTransform1D) {
-                return new ConcatenatedTransformDirect1D((MathTransform1D) tr1,
-                                                         (MathTransform1D) tr2);
-            } else {
-                return new ConcatenatedTransform1D(tr1, tr2);
-            }
-        } else
-        /*
-         * Checks if the result need to be a MathTransform2D.
-         */
-        if (dimSource == 2 && dimTarget == 2) {
-            if (tr1 instanceof MathTransform2D && tr2 instanceof MathTransform2D) {
-                return new ConcatenatedTransformDirect2D((MathTransform2D) tr1,
-                                                         (MathTransform2D) tr2);
-            } else {
-                return new ConcatenatedTransform2D(tr1, tr2);
-            }
-        } else if (dimSource == tr1.getTargetDimensions() && tr2.getSourceDimensions() == dimTarget) {
-            return new ConcatenatedTransformDirect(tr1, tr2);
-        } else {
-            return new ConcatenatedTransform(tr1, tr2);
-        }
-    }
-
-    /**
      * Returns a name for the specified math transform.
      */
     private static String getName(final MathTransform transform) {
@@ -468,22 +459,20 @@ class ConcatenatedTransform extends Abst
      * is more than one remaining step, even if all other transform steps are not parameterizable,
      * would be a contract violation.</p>
      *
-     * <p>However in the special case where we are formatting {@code PROJCS} element, the above rule
-     * is slightly relaxed. More specifically we ignore affine transforms in order to accept axis
-     * swapping or unit conversions. This special case is internal to SIS implementation of WKT
-     * formatter and should be unknown to users.</p>
-     *
-     * <p>See {@link org.apache.sis.referencing.operation.DefaultSingleOperation#getParameterValues()}
-     * for the code where the above-cited special case is applied.</p>
+     * <p>However in the special case where we are getting the parameters of a {@code CoordinateOperation} instance
+     * through {@link org.apache.sis.referencing.operation.AbstractCoordinateOperation#getParameterValues()} method
+     * (often indirectly trough WKT formatting of a {@code "ProjectedCRS"} element), then the above rule is slightly
+     * relaxed: we ignore affine transforms in order to accept axis swapping or unit conversions. We do that in that
+     * particular case only because the coordinate systems given with the enclosing {@code CoordinateOperation} or
+     * {@code GeneralDerivedCRS} specify the axis swapping and unit conversions.
+     * This special case is internal to SIS implementation and should be unknown to users.</p>
      *
      * @return The parameterizable transform step, or {@code null} if none.
-     *
-     * @see org.apache.sis.referencing.operation.DefaultSingleOperation#simplify(MathTransform)
      */
     private Parameterized getParameterised() {
         Parameterized param = null;
         final List<Object> transforms = getPseudoSteps();
-        if (transforms.size() == 1 || Semaphores.query(Semaphores.PROJCS)) {
+        if (transforms.size() == 1 || Semaphores.query(Semaphores.ENCLOSED_IN_OPERATION)) {
             for (final Object candidate : transforms) {
                 /*
                  * Search for non-linear parameters only, ignoring affine transforms and the matrices
@@ -822,8 +811,10 @@ class ConcatenatedTransform extends Abst
     public synchronized MathTransform inverse() throws NoninvertibleTransformException {
         assert isValid();
         if (inverse == null) {
-            inverse = createConcatenatedTransform(transform2.inverse(), transform1.inverse());
-            inverse.inverse = this;
+            inverse = create(transform2.inverse(), transform1.inverse());
+            if (inverse instanceof ConcatenatedTransform) {
+                ((ConcatenatedTransform) inverse).inverse = this;
+            }
         }
         return inverse;
     }
@@ -914,10 +905,17 @@ class ConcatenatedTransform extends Abst
             formatter.newLine();
             if (step instanceof FormattableObject) {
                 formatter.append((FormattableObject) step); // May not implement MathTransform.
-            } else {
+            } else if (step instanceof MathTransform) {
                 formatter.append((MathTransform) step);
+            } else {
+                /*
+                 * Matrices may happen in a chain of pseudo-steps. For now wrap in a MathTransform.
+                 * We could define a Formatter.append(Matrix) method instead, but it is probably not
+                 * worth the cost.
+                 */
+                formatter.append(MathTransforms.linear((Matrix) step));
             }
         }
-        return "Concat_MT";
+        return WKTKeywords.Concat_MT;
     }
 }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java?rev=1679636&r1=1679635&r2=1679636&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java [UTF-8] Fri May 15 19:42:41 2015
@@ -36,6 +36,7 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.operation.MathTransformFactory;
 import org.apache.sis.internal.referencing.ExtendedPrecisionMatrix;
 import org.apache.sis.internal.referencing.WKTUtilities;
+import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.internal.util.DoubleDouble;
 import org.apache.sis.internal.util.UnmodifiableArrayList;
 import org.apache.sis.parameter.Parameters;
@@ -179,7 +180,7 @@ public class ContextualParameters extend
      *
      * <ul>
      *   <li>Set the relevant parameter values by calls to
-     *     <code>{@linkplain #parameter(ParameterDescriptor) parameter(…)}.setValue(…)</code>.</li>
+     *     <code>{@linkplain #parameter(String) parameter(…)}.setValue(…)</code>.</li>
      *   <li>Modify the element values in {@linkplain #getMatrix(boolean) normalization / denormalization}
      *     affine transforms, optionally by calls to the convenience methods in this class.</li>
      *   <li>Get the complete transforms chain with a call
@@ -629,10 +630,10 @@ public class ContextualParameters extend
         protected String formatTo(final Formatter formatter) {
             if (inverse) {
                 formatter.append(new WKT(false));
-                return "Inverse_MT";
+                return WKTKeywords.Inverse_MT;
             } else {
                 WKTUtilities.appendParamMT(ContextualParameters.this, formatter);
-                return "Param_MT";
+                return WKTKeywords.Param_MT;
             }
         }
     }



Mime
View raw message