You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2015/03/27 18:04:02 UTC

svn commit: r1669621 - in /sis/branches/JDK8/core/sis-referencing/src: main/java/org/apache/sis/referencing/operation/matrix/ main/java/org/apache/sis/referencing/operation/projection/ main/java/org/apache/sis/referencing/operation/transform/ test/java...

Author: desruisseaux
Date: Fri Mar 27 17:04:01 2015
New Revision: 1669621

URL: http://svn.apache.org/r1669621
Log:
Referencing: completed ContextualParameters implementation, added documentation and tests.

Added:
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ContextualParametersTest.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/UnmodifiableMatrix.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/package-info.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/formulas.html
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorGroupTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java?rev=1669621&r1=1669620&r2=1669621&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] Fri Mar 27 17:04:01 2015
@@ -57,9 +57,13 @@ import java.util.Objects;
  *       {@link #isAffine   isAffine},
  *       {@link #isIdentity isIdentity},
  *       {@link #equals(Matrix, Matrix, double, boolean) equals},
- *       {@link #equals(Matrix, Matrix, ComparisonMode)  equals},
  *       {@link #toString(Matrix) toString}.
  *   </li>
+ *   <li>Miscellaneous:
+ *       {@link #multiply     multiply},
+ *       {@link #inverse      inverse},
+ *       {@link #unmodifiable unmodifiable},
+ *   </li>
  * </ul>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java?rev=1669621&r1=1669620&r2=1669621&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java [UTF-8] Fri Mar 27 17:04:01 2015
@@ -32,13 +32,24 @@ import org.apache.sis.util.resources.Err
  * A {@link Matrix} able to perform some operations of interest to Spatial Information Systems (SIS).
  * This class completes the GeoAPI {@link Matrix} interface with some operations used by {@code sis-referencing}.
  * It is not a {@code MatrixSIS} goal to provide all possible Matrix operations, as there is too many of them.
- * This class focuses only on:
+ * This class focuses on:
  *
  * <ul>
- *   <li>basic operations needed for <cite>referencing by coordinates</cite>:
- *       {@link #transpose()}, {@link #inverse()}, {@link #multiply(Matrix)};</li>
- *   <li>some operations more specific to referencing by coordinates:
- *       {@link #isAffine()}, {@link #normalizeColumns()}, {@link #concatenate(int, Number, Number)}.</li>
+ *   <li>Only the basic matrix operations needed for <cite>referencing by coordinates</cite>:
+ *     <ul>
+ *       <li>{@link #isIdentity()}</li>
+ *       <li>{@link #multiply(Matrix)}</li>
+ *       <li>{@link #inverse()}</li>
+ *       <li>{@link #transpose()}</li>
+ *     </ul>
+ *   </li><li>Other operations which are not general-purpose matrix operations,
+ *     but are needed in the context of referencing by coordinates:
+ *     <ul>
+ *       <li>{@link #isAffine()}</li>
+ *       <li>{@link #normalizeColumns()}</li>
+ *       <li>{@link #concatenate(int, Number, Number)}</li>
+ *     </ul>
+ *   </li>
  * </ul>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/UnmodifiableMatrix.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/UnmodifiableMatrix.java?rev=1669621&r1=1669620&r2=1669621&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/UnmodifiableMatrix.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/UnmodifiableMatrix.java [UTF-8] Fri Mar 27 17:04:01 2015
@@ -127,6 +127,6 @@ final class UnmodifiableMatrix extends M
      */
     @Override
     public MatrixSIS clone() {
-        return MatrixSIS.castOrCopy(matrix.clone());
+        return castOrCopy(matrix.clone());
     }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java?rev=1669621&r1=1669620&r2=1669621&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java [UTF-8] Fri Mar 27 17:04:01 2015
@@ -60,13 +60,19 @@ import java.util.Objects;
  *   affine transform.</li>
  * </ul>
  *
- * The normalize and denormalize steps are represented below by the matrices on the left and right sides respectively.
- * The matrices below show only the basic parameters common to all projections. Some projections will put more elements
- * in those matrices.
+ * The normalize and denormalize steps are represented below by the matrices immediately on the left and right
+ * sides of {@code NormalizedProjection} respectively. The first matrix on the left side is for
+ * {@linkplain org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes swapping axes}
+ * from (<var>latitude</var>, <var>longitude</var>) to (<var>longitude</var>, <var>latitude</var>) order,
+ * and is shown here for completeness.
+ * The matrices below show only the basic parameters common to most projections.
+ * Some projections will put more elements in those matrices.
  *
  * <center>
  *   <table class="compact" style="td {vertical-align: middle}" summary="Decomposition of a map projection">
  *     <tr>
+ *       <td>{@include ../transform/formulas.html#SwapAxes}</td>
+ *       <td>→</td>
  *       <td>{@include ../transform/formulas.html#NormalizeGeographic}</td>
  *       <td>→</td>
  *       <td>{@code NormalizedProjection}</td>
@@ -176,10 +182,12 @@ public abstract class NormalizedProjecti
     }
 
     /**
-     * Returns the parameters used for creating the complete map projection. Those parameters describe a sequence
-     * of <cite>normalize</cite> → {@code this} → <cite>denormalize</cite> transforms. They are used for formatting
-     * <cite>Well Known Text</cite> (WKT) and error messages. Subclasses shall not use the values defined in the
-     * returned object for computation purpose, except at construction time.
+     * Returns the parameters used for creating the complete map projection. Those parameters describe a sequence of
+     * <cite>normalize</cite> → {@code this} → <cite>denormalize</cite> transforms, <strong>not</strong> including
+     * {@linkplain org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes axis swapping}.
+     * Those parameters are used for formatting <cite>Well Known Text</cite> (WKT) and error messages.
+     * Subclasses shall not use the values defined in the returned object for computation purpose,
+     * except at construction time.
      *
      * @return The parameters values for the sequence of <cite>normalize</cite> → {@code this} → <cite>denormalize</cite>
      *         transforms, or {@code null} if unspecified.

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/package-info.java?rev=1669621&r1=1669620&r2=1669621&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/package-info.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/package-info.java [UTF-8] Fri Mar 27 17:04:01 2015
@@ -115,7 +115,9 @@
  *
  *
  * <div class="section">Projection on unit ellipse</div>
- * A map projection in this package is actually the concatenation of the following transforms, in that order:
+ * A map projection in this package is actually the concatenation of the following transforms, in that order
+ * (ignoring {@linkplain org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes axis order changes}
+ * and conversions from/to units other then degrees and metres, which are not the purpose of this package):
  *
  * <ul>
  *   <li>A {@linkplain org.apache.sis.referencing.operation.transform.ContextualParameters#normalizeGeographicInputs normalization} affine transform</li>

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java?rev=1669621&r1=1669620&r2=1669621&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] Fri Mar 27 17:04:01 2015
@@ -211,6 +211,8 @@ public abstract class AbstractMathTransf
      *         Callers should not modify the returned parameters, since modifications (if allowed)
      *         will generally not be reflected back in this {@code MathTransform}.
      *
+     * @see org.apache.sis.referencing.operation.projection.NormalizedProjection#getContextualParameters()
+     *
      * @since 0.6
      */
     protected ContextualParameters getContextualParameters() {

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java?rev=1669621&r1=1669620&r2=1669621&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java [UTF-8] Fri Mar 27 17:04:01 2015
@@ -17,9 +17,8 @@
 package org.apache.sis.referencing.operation.transform;
 
 import java.util.List;
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Objects;
-import java.util.Collections;
 import java.io.Serializable;
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.operation.MathTransform;
@@ -69,7 +68,9 @@ import static org.apache.sis.util.Argume
  * This object is used mostly for Apache SIS implementation of map projections, where the non-linear kernel is a
  * {@linkplain org.apache.sis.referencing.operation.projection.NormalizedProjection normalized projection}.
  * The {@linkplain #completeTransform(MathTransformFactory, MathTransform) complete map projection} is a chain
- * of 3 transforms a below:
+ * of 3 transforms as shown below. Note that this chain does not include the step for
+ * {@linkplain org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes swapping the axis order}.
+ * Axes swapping and conversions from units other than degrees is performed outside {@code ContextualParameters}.
  *
  * <center>
  *   <table class="compact" style="td {vertical-align: middle}" summary="Decomposition of a map projection">
@@ -107,7 +108,10 @@ import static org.apache.sis.util.Argume
  *   <li>After all parameter values have been set and the normalize / denormalize matrices defined,
  *     the {@link #completeTransform(MathTransformFactory, MathTransform) completeTransform(…)} method
  *     will mark this {@code ContextualParameters} object as unmodifiable and create the complete chain
- *     of transforms, from (λ,φ) in angular degrees to (x,y) in metres.</li>
+ *     of transforms, from (λ,φ) in angular degrees to (x,y) in metres. Note that conversions to other
+ *     units and {@linkplain org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes changes
+ *     in axis order} are not the purpose of this transforms chain; they are separated steps.
+ *   </li>
  * </ol>
  *
  * <div class="section">Serialization</div>
@@ -148,10 +152,19 @@ public class ContextualParameters extend
     private Matrix normalize, denormalize;
 
     /**
-     * The list of parameter values. This list is modifiable after construction, but is replaced by an
-     * unmodifiable list after {@link #completeTransform(MathTransformFactory, MathTransform)} has been invoked.
+     * The parameter values. Null elements in this array are empty slots available for adding new parameter values.
+     * The array length is the maximum number of parameters allowed, which is determined by the {@link #descriptor}.
+     *
+     * <p>This array is modifiable after construction, but is considered unmodifiable after
+     * {@link #completeTransform(MathTransformFactory, MathTransform)} has been invoked.</p>
+     */
+    private ParameterValue<?>[] values;
+
+    /**
+     * {@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)}.
      */
-    private List<GeneralParameterValue> values;
+    private boolean isFrozen;
 
     /**
      * Creates a new group of parameters for the given non-linear coordinate operation method.
@@ -161,13 +174,15 @@ 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(ParameterDescriptor) 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
      *     {@link #completeTransform(MathTransformFactory, MathTransform) completeTransform(…)}</li>
      * </ul>
      *
+     * See class javadoc for more information.
+     *
      * @param method The non-linear operation method for which to define the parameter values.
      */
     public ContextualParameters(final OperationMethod method) {
@@ -175,15 +190,7 @@ public class ContextualParameters extend
         descriptor  = method.getParameters();
         normalize   = linear("sourceDimensions", method.getSourceDimensions());
         denormalize = linear("targetDimensions", method.getTargetDimensions());
-        values      = new ArrayList<>(descriptor.descriptors().size());  // See isModifiable() below.
-    }
-
-    /**
-     * Returns {@code true} if this parameter group is modifiable, or {@code false} if it has been made
-     * unmodifiable by a call to {@link #completeTransform(MathTransformFactory, MathTransform)}.
-     */
-    private boolean isModifiable() {
-        return values.getClass() == ArrayList.class;        // '==' is faster than 'instanceof'.
+        values      = new ParameterValue<?>[descriptor.descriptors().size()];
     }
 
     /**
@@ -195,7 +202,8 @@ public class ContextualParameters extend
         if (size == null) {
             throw new IllegalArgumentException(Errors.format(Errors.Keys.MissingValueForProperty_1, name));
         }
-        return Matrices.create(size, size, ExtendedPrecisionMatrix.IDENTITY);
+        final int n = size + 1;
+        return Matrices.create(n, n, ExtendedPrecisionMatrix.IDENTITY);
     }
 
     /**
@@ -221,12 +229,12 @@ public class ContextualParameters extend
     }
 
     /**
-     * Returns the affine transforms to be applied before or after the kernel operation.
+     * Returns the affine transforms to be applied before or after the non-linear kernel operation.
      *
      * <p>Immediately after {@linkplain #ContextualParameters(OperationMethod) construction}, those matrices
      * are modifiable identity matrices. Callers can modify the matrix element values, typically by calls to
-     * the {@link MatrixSIS#concatenate(int, Number, Number)} method. Alternatively, the following methods
-     * can be invoked for applying some frequently used configurations:
+     * the {@link MatrixSIS#concatenate(int, Number, Number) MatrixSIS.concatenate(…)} method. Alternatively,
+     * the following methods can be invoked for applying some frequently used configurations:
      *
      * <ul>
      *   <li>{@link #normalizeGeographicInputs(double)}</li>
@@ -243,7 +251,7 @@ public class ContextualParameters extend
      */
     public final MatrixSIS getMatrix(final boolean norm) {
         final Matrix m = norm ? normalize : denormalize;
-        if (isModifiable()) {
+        if (!isFrozen) {
             return (MatrixSIS) m;       // Must be the same instance, not a copy.
         } else {
             return Matrices.unmodifiable(m);
@@ -256,7 +264,7 @@ public class ContextualParameters extend
      * @throws IllegalStateException if this {@code ContextualParameter} has been made unmodifiable.
      */
     private void ensureModifiable() throws IllegalStateException {
-        if (!isModifiable()) {
+        if (isFrozen) {
             throw new IllegalStateException(Errors.format(Errors.Keys.UnmodifiableObject_1, getClass()));
         }
     }
@@ -266,8 +274,8 @@ public class ContextualParameters extend
      * The normalization can optionally subtract the given λ₀ value (in degrees) from the longitude.
      *
      * <p>Invoking this method is equivalent to {@linkplain java.awt.geom.AffineTransform#concatenate concatenating}
-     * the normalization matrix by the following matrix. This will have the effect of applying the conversion described
-     * above before any other operation:</p>
+     * the normalization matrix with the following matrix. This will have the effect of applying the conversion
+     * described above before any other operation:</p>
      *
      * <center>{@include formulas.html#NormalizeGeographic}</center>
      *
@@ -299,8 +307,9 @@ public class ContextualParameters extend
      * in the two first dimensions from radians to degrees. After this conversion, the denormalization
      * can optionally add the given λ₀ value (in degrees) to the longitude.
      *
-     * <p>Invoking this method is equivalent to convert coordinates using the following
-     * affine transform after the non-linear kernel:</p>
+     * <p>Invoking this method is equivalent to {@linkplain java.awt.geom.AffineTransform#concatenate concatenating}
+     * the denormalization matrix with the following matrix. This will have the effect of applying the conversion
+     * described above after the non-linear kernel operation:</p>
      *
      * <center>{@include formulas.html#DeormalizeGeographic}</center>
      *
@@ -323,8 +332,9 @@ public class ContextualParameters extend
      * in the two first dimensions.
      * In map projection, the translation terms are <cite>false easting</cite> (FE) and <cite>false northing</cite> (FN).
      *
-     * <p>Invoking this method is equivalent to convert coordinates using the following
-     * affine transform after the non-linear kernel:</p>
+     * <p>Invoking this method is equivalent to {@linkplain java.awt.geom.AffineTransform#concatenate concatenating}
+     * the denormalization matrix with the following matrix. This will have the effect of applying the conversion
+     * described above after the non-linear kernel operation:</p>
      *
      * <center>{@include formulas.html#DenormalizeCartesian}</center>
      *
@@ -350,6 +360,12 @@ public class ContextualParameters extend
      * This method shall be invoked only after the {@linkplain #getMatrix(boolean) (de)normalization}
      * matrices have been set to their final values.
      *
+     * <p>The transforms chain created by this method does not include any step for
+     * {@linkplain org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes changing axis order}
+     * or for converting to units other than degrees or metres. Such steps, if desired, should be defined
+     * outside {@code ContextualParameters}. Efficient concatenation of those steps will happen "under
+     * the hood".</p>
+     *
      * @param  factory The factory to use for creating math transform instances.
      * @param  kernel The (usually non-linear) kernel.
      * @return The concatenation of <cite>normalize</cite> → <cite>the given kernel</cite> → <cite>denormalize</cite>
@@ -359,12 +375,16 @@ public class ContextualParameters extend
     public MathTransform completeTransform(final MathTransformFactory factory, final MathTransform kernel)
             throws FactoryException
     {
-        if (isModifiable()) {
-            final GeneralParameterValue[] p = values.toArray(new GeneralParameterValue[values.size()]);
-            for (int i=0; i<p.length; i++) {
-                p[i] = DefaultParameterValue.unmodifiable((ParameterValue<?>) p[i]);
+        if (!isFrozen) {
+            isFrozen = true;
+            for (int i=0; i < values.length; i++) {
+                final ParameterValue<?> p = values[i];
+                if (p == null) {
+                    values = Arrays.copyOf(values, i);  // Trim extra values.
+                    break;
+                }
+                values[i] = DefaultParameterValue.unmodifiable(p);
             }
-            values = UnmodifiableArrayList.wrap(p);
         }
         /*
          * Creates the ConcatenatedTransform, letting the factory returns the cached instance
@@ -380,6 +400,8 @@ public class ContextualParameters extend
 
     /**
      * Returns the parameter value of the given name.
+     * This method can be invoked either before or after the call to {@link #completeTransform completeTransform(…)},
+     * but the returned parameter will be modifiable only before that call.
      *
      * @param  name The name of the parameter to search.
      * @return The parameter value for the given name.
@@ -387,16 +409,37 @@ public class ContextualParameters extend
      */
     @Override
     public ParameterValue<?> parameter(final String name) throws ParameterNotFoundException {
-        final GeneralParameterDescriptor p = descriptor.descriptor(name);
-        if (p instanceof ParameterDescriptor<?>) {
-            return parameter((ParameterDescriptor<?>) p);
-        } else {
+        final GeneralParameterDescriptor desc = descriptor.descriptor(name);
+        if (!(desc instanceof ParameterDescriptor<?>)) {
             throw parameterNotFound(name);
         }
-    }
-
-    public ParameterValue<?> parameter(final ParameterDescriptor<?> param) throws ParameterNotFoundException {
-        throw new UnsupportedOperationException("Not supported yet."); // TODO
+        /*
+         * Search for existing parameter instance. This implementation does not scale,
+         * but should be okay since the amount of parameters is typically very small
+         * (rarely more than 6 parameters in map projections).
+         */
+        for (int i=0; i < values.length; i++) {
+            ParameterValue<?> p = values[i];
+            if (p == null) {
+                /*
+                 * No existing parameter instance. Create a new one if this ContextualParameter
+                 * is still modifiable.
+                 */
+                ensureModifiable();
+                p = ((ParameterDescriptor<?>) desc).createValue();
+                values[i] = p;
+            } else if (p.getDescriptor() != desc) {  // Identity comparison should be okay here.
+                continue;
+            }
+            return p;   // Found or created a parameter.
+        }
+        ensureModifiable();
+        /*
+         * Should never reach this point. If it happen anyway, this means that the descriptor now accepts
+         * more parameters than what it declared at ContextualParameteres construction time, or that some
+         * ParameterDescriptor instances changed.
+         */
+        throw new IllegalStateException(Errors.format(Errors.Keys.UnexpectedChange_1, descriptor.getName()));
     }
 
     /**
@@ -406,19 +449,17 @@ public class ContextualParameters extend
      * @return All parameter values.
      */
     @Override
+    @SuppressWarnings("unchecked")
     public List<GeneralParameterValue> values() {
-        if (isModifiable()) {
-            return Collections.unmodifiableList(values);
-        } else {
-            // Already unmodifiable, no need to protect against changes.
-            assert (values instanceof UnmodifiableArrayList<?>);
-            return values;
+        int upper = values.length;
+        while (upper != 0 && values[upper - 1] == null) {
+            upper--;
         }
+        return UnmodifiableArrayList.wrap(values, 0, upper);
     }
 
     /**
      * Unsupported operation, since {@code ContextualParameters} groups do not contain sub-groups.
-     * This limitation may be revisited in future SIS version.
      *
      * @param name Ignored.
      * @return Never returned.
@@ -430,7 +471,6 @@ public class ContextualParameters extend
 
     /**
      * Unsupported operation, since {@code ContextualParameters} groups do not contain sub-groups.
-     * This limitation may be revisited in future SIS version.
      *
      * @param name Ignored.
      * @return Never returned.
@@ -449,19 +489,35 @@ public class ContextualParameters extend
     }
 
     /**
-     * Returns a clone of this parameter value group.
+     * Returns a modifiable clone of this parameter value group.
      *
      * @return A clone of this parameter value group.
      */
     @Override
     public ContextualParameters clone() {
+        /*
+         * Creates a new parameter array with enough room for adding new parameters.
+         * Then replace each element by a modifiable clone.
+         */
+        final ParameterValue<?>[] param = Arrays.copyOf(values, descriptor.descriptors().size());
+        for (int i=0; i<param.length; i++) {
+            final ParameterValue<?> p = param[i];
+            if (p == null) {
+                break;
+            }
+            param[i] = param[i].clone();
+        }
+        /*
+         * Now proceed to the clone of this ContextualParameters instance.
+         */
         final ContextualParameters clone;
         try {
             clone = (ContextualParameters) super.clone();
         } catch (CloneNotSupportedException e) {
-            throw new AssertionError(e);
+            throw new AssertionError(e);    // Should never happen since we are cloneable.
         }
-        clone.  normalize =   normalize.clone();
+        clone.values      = param;
+        clone.normalize   = normalize.clone();
         clone.denormalize = denormalize.clone();
         return clone;
     }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/formulas.html
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/formulas.html?rev=1669621&r1=1669620&r2=1669621&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/formulas.html [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/formulas.html [UTF-8] Fri Mar 27 17:04:01 2015
@@ -117,6 +117,32 @@
 
 
 
+    <h2>SwapAxes</h2>
+    <math display="block" alttext="MathML capable browser required">
+      <mfenced open="[" close="]">
+        <mtable>
+          <mtr>
+            <mtd><mn>0</mn></mtd>
+            <mtd><mn>1</mn></mtd>
+            <mtd><mn>0</mn></mtd>
+          </mtr>
+          <mtr>
+            <mtd><mn>1</mn></mtd>
+            <mtd><mn>0</mn></mtd>
+            <mtd><mn>0</mn></mtd>
+          </mtr>
+          <mtr>
+            <mtd><mn>0</mn></mtd>
+            <mtd><mn>0</mn></mtd>
+            <mtd><mn>1</mn></mtd>
+          </mtr>
+        </mtable>
+      </mfenced>
+    </math>
+
+
+
+
     <h2>NormalizeGeographic</h2>
     <math display="block" alttext="MathML capable browser required">
       <mfenced open="[" close="]">

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorGroupTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorGroupTest.java?rev=1669621&r1=1669620&r2=1669621&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorGroupTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorGroupTest.java [UTF-8] Fri Mar 27 17:04:01 2015
@@ -57,7 +57,7 @@ public final strictfp class DefaultParam
      * The very last parameter has a maximum number of occurrence of 2, which is illegal
      * according ISO 19111 but nevertheless supported by Apache SIS.
      */
-    static final DefaultParameterDescriptorGroup M1_M1_O1_O2;
+    public static final DefaultParameterDescriptorGroup M1_M1_O1_O2;
     static {
         final Class<Integer> type = Integer.class;
         final Map<String,Object> properties = new HashMap<>(4);

Added: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ContextualParametersTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ContextualParametersTest.java?rev=1669621&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ContextualParametersTest.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ContextualParametersTest.java [UTF-8] Fri Mar 27 17:04:01 2015
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation.transform;
+
+import java.util.Collections;
+import org.opengis.util.FactoryException;
+import org.opengis.parameter.ParameterValue;
+import org.opengis.parameter.ParameterNotFoundException;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.apache.sis.referencing.operation.DefaultOperationMethod;
+import org.apache.sis.parameter.DefaultParameterDescriptorGroupTest;
+import org.apache.sis.test.DependsOnMethod;
+import org.apache.sis.test.DependsOn;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.apache.sis.test.MetadataAssert.*;
+
+
+/**
+ * Tests {@link ContextualParameters}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.6
+ * @version 0.6
+ * @module
+ */
+@DependsOn(DefaultParameterDescriptorGroupTest.class)
+public final strictfp class ContextualParametersTest extends TestCase {
+    /**
+     * Creates an instance to use for testing purpose.
+     */
+    private static ContextualParameters create(final int srcDim, final int dstDim) {
+        return new ContextualParameters(new DefaultOperationMethod(
+                Collections.singletonMap(DefaultOperationMethod.NAME_KEY, "Test method"),
+                srcDim, dstDim, DefaultParameterDescriptorGroupTest.M1_M1_O1_O2));
+    }
+
+    /**
+     * Tests {@link ContextualParameters#parameter(String)} and {@link ContextualParameters#values()}.
+     */
+    @Test
+    public void testParameters() {
+        final ContextualParameters p = create(1, 1);
+        assertTrue("values().isEmpty()",       p.values().isEmpty());
+        assertTrue("normalize.isIdentity()",   p.getMatrix(true).isIdentity());
+        assertTrue("denormalize.isIdentity()", p.getMatrix(false).isIdentity());
+
+        final ParameterValue<?> p1 = p.parameter("Mandatory 1");
+        final ParameterValue<?> p2 = p.parameter("Mandatory 2");
+        try {
+            p.parameter("Mandatory 3");
+            fail("Shall not find a non-existent parameter.");
+        } catch (ParameterNotFoundException e) {
+            // This is the expected exception.
+            final String message = e.getMessage();
+            assertTrue(message, message.contains("Mandatory 3"));
+        }
+
+        assertNotSame(p1, p2);
+        assertSame(p1, p.parameter("Mandatory 1"));
+        assertSame(p2, p.parameter("Mandatory 2"));
+        assertEquals("values().size()", 2, p.values().size());
+        assertArrayEquals("values.toArray()", new ParameterValue<?>[] {p1, p2}, p.values().toArray());
+    }
+
+    /**
+     * Tests Well Known Text (WKT) formatting.
+     */
+    @DependsOnMethod("testParameters")
+    public void testWKT() {
+        final ContextualParameters p = create(1, 1);
+        p.parameter("Mandatory 1").setValue(4);
+        p.parameter("Mandatory 2").setValue(5);
+        assertWktEquals(
+                "Param_MT[“Test group”,\n" +
+                "  Parameter[“Mandatory 1”, 4],\n" +
+                "  Parameter[“Mandatory 2”, 5]]", p);
+    }
+
+    /**
+     * Tests {@link ContextualParameters#completeTransform(MathTransformFactory, MathTransform)}
+     * with identity normalization / denormalization transform. The complete transform should be
+     * equals to the kernel (often the same instance, but not necessarily because of caching).
+     *
+     * @throws FactoryException Should never happen.
+     */
+    @Test
+    public void testSameTransform() throws FactoryException {
+        final ContextualParameters p = create(1, 1);
+        final MathTransform kernel = MathTransforms.linear(3, 4);
+        assertEquals(kernel, p.completeTransform(DefaultMathTransformFactoryTest.FACTORY, kernel));
+        try {
+            p.parameter("Mandatory 1");
+            fail("Shall not be allowed to modify an immutable instance.");
+        } catch (IllegalStateException e) {
+            // This is the expected exception.
+            final String message = e.getMessage();
+            assertTrue(message, message.contains("ContextualParameters"));
+        }
+    }
+}

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

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

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java?rev=1669621&r1=1669620&r2=1669621&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java [UTF-8] Fri Mar 27 17:04:01 2015
@@ -27,8 +27,6 @@ import org.apache.sis.internal.util.Cons
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
 import org.junit.Test;
 
 import static org.opengis.test.Assert.*;
@@ -52,23 +50,7 @@ public final strictfp class DefaultMathT
     /**
      * The factory being tested.
      */
-    private static DefaultMathTransformFactory factory;
-
-    /**
-     * Creates the factory to be tested.
-     */
-    @BeforeClass
-    public static void createFactory() {
-        factory = new DefaultMathTransformFactory();
-    }
-
-    /**
-     * Releases the factory used for the tests.
-     */
-    @AfterClass
-    public static void releaseFactory() {
-        factory = null;
-    }
+    static final DefaultMathTransformFactory FACTORY = new DefaultMathTransformFactory();
 
     /**
      * Tests the {@link DefaultMathTransformFactory#getOperationMethod(String)} method.
@@ -78,11 +60,11 @@ public final strictfp class DefaultMathT
     @Test
     public void testGetOperationMethod() throws NoSuchIdentifierException {
         // A conversion which is not a projection.
-        OperationMethod method = factory.getOperationMethod(Constants.AFFINE);
+        OperationMethod method = FACTORY.getOperationMethod(Constants.AFFINE);
         assertInstanceOf("Affine", Affine.class, method);
 
         // Same than above, using EPSG code.
-        assertSame("EPSG:9624", method, factory.getOperationMethod("EPSG:9624"));
+        assertSame("EPSG:9624", method, FACTORY.getOperationMethod("EPSG:9624"));
     }
 
     /**
@@ -92,7 +74,7 @@ public final strictfp class DefaultMathT
     @DependsOnMethod("testGetOperationMethod")
     public void testNonExistentCode() {
         try {
-            factory.getOperationMethod("EPXX:9624");
+            FACTORY.getOperationMethod("EPXX:9624");
             fail("Expected NoSuchIdentifierException");
         } catch (NoSuchIdentifierException e) {
             final String message = e.getLocalizedMessage();
@@ -108,16 +90,16 @@ public final strictfp class DefaultMathT
     @Test
     @DependsOnMethod("testGetOperationMethod")
     public void testGetAvailableMethods() throws NoSuchIdentifierException {
-        final Set<OperationMethod> transforms  = factory.getAvailableMethods(SingleOperation.class);
-        final Set<OperationMethod> conversions = factory.getAvailableMethods(Conversion.class);
-        final Set<OperationMethod> projections = factory.getAvailableMethods(Projection.class);
+        final Set<OperationMethod> transforms  = FACTORY.getAvailableMethods(SingleOperation.class);
+        final Set<OperationMethod> conversions = FACTORY.getAvailableMethods(Conversion.class);
+        final Set<OperationMethod> projections = FACTORY.getAvailableMethods(Projection.class);
         /*
          * Following tests should not cause loading of more classes than needed.
          */
         assertFalse(transforms .isEmpty());
         assertFalse(conversions.isEmpty());
         assertTrue (projections.isEmpty());
-        assertTrue (conversions.contains(factory.getOperationMethod(Constants.AFFINE)));
+        assertTrue (conversions.contains(FACTORY.getOperationMethod(Constants.AFFINE)));
         /*
          * Following tests will force instantiation of all remaining OperationMethod.
          */

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1669621&r1=1669620&r2=1669621&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] Fri Mar 27 17:04:01 2015
@@ -85,6 +85,7 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.operation.transform.ConcatenatedTransformTest.class,
     org.apache.sis.referencing.operation.transform.TransferFunctionTest.class,
     org.apache.sis.referencing.operation.transform.MathTransformsTest.class,
+    org.apache.sis.referencing.operation.transform.ContextualParametersTest.class,
 
     // Test map projections.
     org.apache.sis.referencing.operation.DefaultFormulaTest.class,