sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1718283 - in /sis/branches/JDK8/core: sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ sis-referencing/src/test/java/org/apache/sis/re...
Date Mon, 07 Dec 2015 10:05:22 GMT
Author: desruisseaux
Date: Mon Dec  7 10:05:22 2015
New Revision: 1718283

URL: http://svn.apache.org/viewvc?rev=1718283&view=rev
Log:
Port the TransformSeparator class.
Fix a subtle bug in AbstractLinearTransform.equals(Object).

Added:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransformSeparator.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformSeparatorTest.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CopyTransform.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedTransform.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyFormula.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/GeocentricTranslationTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/Geographic3Dto2DTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java?rev=1718283&r1=1718282&r2=1718283&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java [UTF-8] Mon Dec  7 10:05:22 2015
@@ -24,6 +24,7 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.operation.NoninvertibleTransformException;
 import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.internal.referencing.provider.Affine;
+import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.resources.Errors;
 
@@ -55,7 +56,7 @@ abstract class AbstractLinearTransform e
      * This field is part of the serialization form in order to avoid rounding errors if a user
      * asks for the inverse of the inverse (i.e. the original transform) after deserialization.
      */
-    LinearTransform inverse;
+    volatile LinearTransform inverse;
 
     /**
      * Constructs a transform.
@@ -110,23 +111,31 @@ abstract class AbstractLinearTransform e
      * Creates the inverse transform of this object.
      */
     @Override
-    public synchronized LinearTransform inverse() throws NoninvertibleTransformException {
-        if (inverse == null) {
-            /*
-             * Should never be the identity transform at this point (except during tests) because
-             * MathTransforms.linear(…) should never instantiate this class in the identity case.
-             * But we check anyway as a paranoiac safety.
-             */
-            if (isIdentity()) {
-                inverse = this;
-            } else {
-                inverse = MathTransforms.linear(Matrices.inverse(this));
-                if (inverse instanceof AbstractLinearTransform) {
-                    ((AbstractLinearTransform) inverse).inverse = this;
+    @SuppressWarnings("DoubleCheckedLocking")  // Okay since 'inverse' is volatile.
+    public LinearTransform inverse() throws NoninvertibleTransformException {
+        LinearTransform inv = inverse;
+        if (inv == null) {
+            synchronized (this) {
+                inv = inverse;
+                if (inv == null) {
+                    /*
+                     * Should never be the identity transform at this point (except during tests) because
+                     * MathTransforms.linear(…) should never instantiate this class in the identity case.
+                     * But we check anyway as a paranoiac safety.
+                     */
+                    if (isIdentity()) {
+                        inv = this;
+                    } else {
+                        inv = MathTransforms.linear(Matrices.inverse(this));
+                        if (inv instanceof AbstractLinearTransform) {
+                            ((AbstractLinearTransform) inv).inverse = this;
+                        }
+                    }
+                    inverse = inv;
                 }
             }
         }
-        return inverse;
+        return inv;
     }
 
     /**
@@ -244,19 +253,77 @@ abstract class AbstractLinearTransform e
         if (object == this) { // Slight optimization
             return true;
         }
-        if (object != null) {
-            if (getClass() == object.getClass() && !mode.isApproximative()) {
-                return equalsSameClass(object);
+        if (object == null) {
+            return false;
+        }
+        final boolean isApproximative = mode.isApproximative();
+        if (!isApproximative && getClass() == object.getClass()) {
+            if (!equalsSameClass(object)) {
+                return false;
             }
-            if (mode != ComparisonMode.STRICT) {
-                if (object instanceof LinearTransform) {
-                    return Matrices.equals(this, ((LinearTransform) object).getMatrix(), mode);
-                } else if (object instanceof Matrix) {
-                    return Matrices.equals(this, (Matrix) object, mode);
-                }
+        } else if (mode == ComparisonMode.STRICT) {
+            return false;
+        } else {
+            final Matrix m;
+            if (object instanceof LinearTransform) {
+                m = ((LinearTransform) object).getMatrix();
+            } else if (object instanceof Matrix) {
+                m = (Matrix) object;
+            } else {
+                return false;
+            }
+            if (!Matrices.equals(this, m, mode)) {
+                return false;
+            }
+        }
+        /*
+         * At this point the transforms are considered equal. In theory we would not need to check
+         * the inverse transforms since if A and B are equal, then A⁻¹ and B⁻¹ should be equal too.
+         * However in Apache SIS this is not exactly true because computation of inverse transforms
+         * avoid NaN values in some circumstances. For example the inverse of a 2×3 matrix normally
+         * sets the "new" dimensions to NaN, but in the particular case where the transform is used
+         * for a "Geographic 2D to 3D" conversion it will rather set the new dimensions to zero. So
+         * A⁻¹ and B⁻¹ may differ in their "NaN versus 0" values even if A and B are equal.
+         *
+         * Opportunistically, the comparison of inverse transforms in approximative mode also ensures
+         * that we are below the tolerance threshold not only for this matrix, but for the inverse one
+         * as well.
+         */
+        if (object instanceof AbstractLinearTransform) {
+            /*
+             * If the 'inverse' matrix was not computed in any of the transforms being compared
+             * (i.e. if 'this.inverse' and 'object.inverse' are both null), then assume that the
+             * two transforms will compute their inverse in the same way. The intend is to avoid
+             * to trig the inverse transform computation.
+             *
+             * Note that this code requires the 'inverse' fields to be volatile
+             * (otherwise we would need to synchronize).
+             */
+            if (inverse == ((AbstractLinearTransform) object).inverse) {
+                return true;
+            }
+        }
+        /*
+         * Get the matrices of inverse transforms. In the following code 'null' is really the intended
+         * value for non-invertible matrices because the Matrices.equals(…) methods accept null values,
+         * so we are okay to ignore NoninvertibleTransformException in this particular case.
+         */
+        Matrix mt = null, mo = null;
+        try {
+            mt = inverse().getMatrix();
+        } catch (NoninvertibleTransformException e) {
+            // Leave 'mt' to null.
+        }
+        try {
+            if (object instanceof LinearTransform) {
+                mo = ((LinearTransform) object).inverse().getMatrix();
+            } else if (object instanceof Matrix) {
+                mo = Matrices.inverse((Matrix) object);
             }
+        } catch (NoninvertibleTransformException e) {
+            // Leave 'mo' to null.
         }
-        return false;
+        return Matrices.equals(mt, mo, isApproximative ? Numerics.COMPARISON_THRESHOLD : 0, isApproximative);
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CopyTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CopyTransform.java?rev=1718283&r1=1718282&r2=1718283&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CopyTransform.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CopyTransform.java [UTF-8] Mon Dec  7 10:05:22 2015
@@ -329,61 +329,69 @@ final class CopyTransform extends Abstra
      * Creates the inverse transform of this object.
      */
     @Override
-    public synchronized LinearTransform inverse() throws NoninvertibleTransformException {
-        if (inverse == null) {
-            /*
-             * Note: no need to perform the following check as this point because MathTransforms.linear(…)
-             *       should never instantiate this class in the identity case and because we perform an
-             *       equivalent check later anyway.
-             *
-             *       if (isIdentity()) {
-             *           inverse = this;
-             *       } else { ... }
-             */
-            final int srcDim = this.srcDim;
-            final int dstDim = indices.length;
-            final int[] reverse = new int[srcDim];
-            Arrays.fill(reverse, -1);
-            for (int i=dstDim; --i>=0;) {
-                reverse[indices[i]] = i;
-            }
-            /*
-             * Check if there is any unassigned dimension. In such case,
-             * delegates to the generic ProjectiveTransform with a matrix
-             * which set the missing values to NaN.
-             */
-            for (int j=srcDim; --j>=0;) {
-                if (reverse[j] < 0) {
-                    final MatrixSIS matrix = Matrices.createZero(srcDim + 1, dstDim + 1);
-                    for (j=0; j<srcDim; j++) {      // Okay to reuse 'j' since the outer loop will not continue.
-                        final int i = reverse[j];
-                        if (i >= 0) {
-                            matrix.setElement(j, i, 1);
-                        } else {
-                            matrix.setElement(j, dstDim, Double.NaN);
+    @SuppressWarnings("DoubleCheckedLocking")  // Okay since 'inverse' is volatile.
+    public LinearTransform inverse() throws NoninvertibleTransformException {
+        LinearTransform inv = inverse;
+        if (inv == null) {
+            synchronized (this) {
+                inv = inverse;
+                if (inv == null) {
+                    /*
+                     * Note: no need to perform the following check at this point because MathTransforms.linear(…)
+                     *       should never instantiate this class in the identity case and because we perform an
+                     *       equivalent check later anyway.
+                     *
+                     *       if (isIdentity()) {
+                     *           inverse = this;
+                     *       } else { ... }
+                     */
+                    final int srcDim = this.srcDim;
+                    final int dstDim = indices.length;
+                    final int[] reverse = new int[srcDim];
+                    Arrays.fill(reverse, -1);
+                    for (int i=dstDim; --i>=0;) {
+                        reverse[indices[i]] = i;
+                    }
+                    /*
+                     * Check if there is any unassigned dimension. In such case,
+                     * delegates to the generic ProjectiveTransform with a matrix
+                     * which set the missing values to NaN.
+                     */
+                    for (int j=srcDim; --j>=0;) {
+                        if (reverse[j] < 0) {
+                            final MatrixSIS matrix = Matrices.createZero(srcDim + 1, dstDim + 1);
+                            for (j=0; j<srcDim; j++) {      // Okay to reuse 'j' since the outer loop will not continue.
+                                final int i = reverse[j];
+                                if (i >= 0) {
+                                    matrix.setElement(j, i, 1);
+                                } else {
+                                    matrix.setElement(j, dstDim, Double.NaN);
+                                }
+                            }
+                            matrix.setElement(srcDim, dstDim, 1);
+                            inv = MathTransforms.linear(matrix);
+                            if (inv instanceof AbstractLinearTransform) {
+                                ((AbstractLinearTransform) inv).inverse = this;
+                            }
+                            inverse = inv;
+                            return inv;
                         }
                     }
-                    matrix.setElement(srcDim, dstDim, 1);
-                    inverse = MathTransforms.linear(matrix);
-                    if (inverse instanceof AbstractLinearTransform) {
-                        ((AbstractLinearTransform) inverse).inverse = this;
+                    /*
+                     * At this point, we know that we can create the inverse transform.
+                     * If this transform is the identity transform (we should never happen,
+                     * but we are paranoiac), then the old and new arrays would be equal.
+                     */
+                    CopyTransform copyInverse = this;
+                    if (!Arrays.equals(reverse, indices)) {
+                        copyInverse = new CopyTransform(indices.length, reverse);
+                        copyInverse.inverse = this;
                     }
-                    return inverse;
+                    inverse = inv = copyInverse;
                 }
             }
-            /*
-             * At this point, we know that we can create the inverse transform.
-             * If this transform is the identity transform (we should never happen,
-             * but we are paranoiac), then the old and new arrays would be equal.
-             */
-            CopyTransform copyInverse = this;
-            if (!Arrays.equals(reverse, indices)) {
-                copyInverse = new CopyTransform(indices.length, reverse);
-                copyInverse.inverse = this;
-            }
-            inverse = copyInverse;
         }
-        return inverse;
+        return inv;
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java?rev=1718283&r1=1718282&r2=1718283&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java [UTF-8] Mon Dec  7 10:05:22 2015
@@ -729,7 +729,9 @@ next:   while (--numPts >= 0) {
      */
     @Override
     protected int computeHashCode() {
-        return super.computeHashCode() + Numerics.hashCode(Double.doubleToLongBits(axisRatio));
+        int code = super.computeHashCode() + Numerics.hashCode(Double.doubleToLongBits(axisRatio));
+        if (withHeight) code += 37;
+        return code;
     }
 
     /**
@@ -746,6 +748,7 @@ next:   while (--numPts >= 0) {
         if (super.equals(object, mode)) {
             final EllipsoidToCentricTransform that = (EllipsoidToCentricTransform) object;
             return (withHeight == that.withHeight) && Numerics.equals(axisRatio, that.axisRatio);
+            // No need to compare the contextual parameters since this is done by super-class.
         }
         return false;
     }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedTransform.java?rev=1718283&r1=1718282&r2=1718283&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedTransform.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedTransform.java [UTF-8] Mon Dec  7 10:05:22 2015
@@ -435,6 +435,7 @@ public class InterpolatedTransform exten
             return true;
         }
         return super.equals(object, mode) && Objects.equals(grid, ((InterpolatedTransform) object).grid);
+        // No need to compare the contextual parameters since this is done by super-class.
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyFormula.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyFormula.java?rev=1718283&r1=1718282&r2=1718283&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyFormula.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyFormula.java [UTF-8] Mon Dec  7 10:05:22 2015
@@ -524,6 +524,7 @@ abstract class MolodenskyFormula extends
                 && Numerics.epsilonEqual(semiMajor,           that.semiMajor,           mode)
                 && Numerics.epsilonEqual(eccentricitySquared, that.eccentricitySquared, mode)
                 && Objects .equals(grid, that.grid);
+                // No need to compare the contextual parameters since this is done by super-class.
         }
         return false;
     }

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransformSeparator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransformSeparator.java?rev=1718283&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransformSeparator.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransformSeparator.java [UTF-8] Mon Dec  7 10:05:22 2015
@@ -0,0 +1,714 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation.transform;
+
+import java.util.Arrays;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.apache.sis.referencing.operation.matrix.Matrices;
+import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.ArraysExt;
+
+
+/**
+ * Extracts a sub-transform from a given {@code MathTransform} and source or target dimension indices.
+ * Given an arbitrary {@link MathTransform}, this class tries to return a new math transform that operates
+ * only on a given set of source or target dimensions.
+ *
+ * <div class="note"><b>Example:</b>
+ * if the supplied {@code transform} has (<var>x</var>,<var>y</var>,<var>z</var>) inputs
+ * and (<var>λ</var>,<var>φ</var>,<var>h</var>) outputs, then the following code:
+ *
+ * {@preformat java
+ *     TransformSeparator s = new TransformSeparator(theTransform);
+ *     s.addSourceDimensionRange(0, 2);
+ *     MathTransform mt = s.separate();
+ * }
+ *
+ * will return a transform with (<var>x</var>,<var>y</var>) inputs and (probably) (<var>λ</var>,<var>φ</var>) outputs.
+ * The output dimensions can be verified with a call to {@link #getTargetDimensions()}.</div>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ */
+public class TransformSeparator {
+    /**
+     * The transform to separate.
+     */
+    protected final MathTransform transform;
+
+    /**
+     * Indices of transform input dimensions to keep, or {@code null} if not yet defined.
+     * If non-null, the indices in the array must be sorted in strictly increasing order.
+     * This sequence can contain any integers in the range 0 inclusive to
+     * {@link MathTransform#getSourceDimensions()} exclusive.
+     *
+     * <p>Values in this array should never be modified. For adding, removing or editing indices,
+     * new arrays should be created and assigned to this field. This approach makes easier to keep snapshots
+     * of indices arrays at various stages during the process of separating a {@code MathTransform}.</p>
+     *
+     * @see #getSourceDimensions()
+     * @see #addSourceDimensions(int...)
+     * @see #addSourceDimensionRange(int, int)
+     */
+    protected int[] sourceDimensions;
+
+    /**
+     * Indices of transform output dimensions to keep, or {@code null} if not yet defined.
+     * If non-null, the indices in the array must be sorted in strictly increasing order.
+     * This sequence can contain any integers in the range 0 inclusive to
+     * {@link MathTransform#getTargetDimensions()} exclusive.
+     *
+     * <p>Values in this array should never be modified. For adding, removing or editing indices,
+     * new arrays should be created and assigned to this field. This approach makes easier to keep snapshots
+     * of indices arrays at various stages during the process of separating a {@code MathTransform}.</p>
+     *
+     * @see #getTargetDimensions()
+     * @see #addTargetDimensions(int...)
+     * @see #addTargetDimensionRange(int, int)
+     */
+    protected int[] targetDimensions;
+
+    /**
+     * The factory to use for creating new math transforms.
+     */
+    protected final MathTransformFactory factory;
+
+    /**
+     * Constructs a separator for the given transform.
+     *
+     * @param transform The transform to separate.
+     */
+    public TransformSeparator(final MathTransform transform) {
+        this(transform, DefaultFactories.forBuildin(MathTransformFactory.class));
+    }
+
+    /**
+     * Constructs a separator for the given transform and using the given factory.
+     *
+     * @param transform The transform to separate.
+     * @param factory The factory to use for creating new math transforms.
+     */
+    public TransformSeparator(final MathTransform transform, final MathTransformFactory factory) {
+        ArgumentChecks.ensureNonNull("transform", transform);
+        ArgumentChecks.ensureNonNull("factory", factory);
+        this.transform = transform;
+        this.factory   = factory;
+    }
+
+    /**
+     * Clears any {@linkplain #getSourceDimensions() source} and {@linkplain #getTargetDimensions() target dimension}
+     * settings. This method can be invoked when the same {@code MathTransform} needs to be separated in more than one
+     * part, for example an horizontal and a vertical component.
+     */
+    public void clear() {
+        sourceDimensions = null;
+        targetDimensions = null;
+    }
+
+    /**
+     * Inserts the specified {@code dimension} in the specified sequence at a position that preserve increasing order.
+     * This method does nothing if the given dimension already exists in the given array.
+     *
+     * <div class="note"><b>Note:</b>
+     * we do not provide public API for this method because we rather encourage bulk operations (adding many values
+     * at once), and because the public API does not allow to specify values in random order (for avoiding risk of
+     * confusion as some users could expect the separated transform to use the dimensions in the order he specified
+     * them).</div>
+     *
+     * @param sequence   The {@link #sourceDimensions} or {@link #targetDimensions} sequence to update.
+     * @param dimension  The value to add to the given sequence.
+     */
+    private static int[] insert(int[] sequence, int dimension) throws IllegalArgumentException {
+        if (sequence == null) {
+            return new int[] {dimension};
+        }
+        assert ArraysExt.isSorted(sequence, true);
+        int i = Arrays.binarySearch(sequence, dimension);
+        if (i < 0) {
+            i = ~i;   // Tild, not the minus sign.
+            sequence = ArraysExt.insert(sequence, i, 1);
+            sequence[i] = dimension;
+        }
+        assert Arrays.binarySearch(sequence, dimension) == i;
+        return sequence;
+    }
+
+    /**
+     * Adds the specified {@code dimensions} to the specified sequence.
+     * Values must be given in strictly increasing order.
+     *
+     * @param  sequence    The {@link #sourceDimensions} or {@link #targetDimensions} sequence to update.
+     * @param  dimensions  The user-supplied dimensions to add to the given sequence.
+     * @param  max         The maximal value allowed, exclusive.
+     * @throws IllegalArgumentException if a {@code dimensions} value does not meet the conditions.
+     */
+    private static int[] add(int[] sequence, final int[] dimensions, final int max) throws IllegalArgumentException {
+        int offset = 0;
+        int previous = -1;  // This initial value will ensure that we have no negative value.
+        if (sequence != null && (offset = sequence.length) != 0) {
+            previous = sequence[offset - 1];
+            sequence = Arrays.copyOf(sequence, offset + dimensions.length);
+            System.arraycopy(dimensions, 0, sequence, offset, dimensions.length);
+        } else {
+            sequence = dimensions.clone();
+        }
+        /*
+         * Ensure that the specified array contains strictly increasing non-negative values.
+         * We verify after the copy as a paranoiac safety against concurrent changes.
+         */
+        for (int i=offset; i<sequence.length; i++) {
+            final int value = sequence[i];
+            if (value <= previous || value >= max) {
+                throw new IllegalArgumentException(Errors.format(Errors.Keys.ValueOutOfRange_4,
+                        "dimensions[" + (i - offset) + ']', previous + 1, max - 1, value));
+            }
+            previous = value;
+        }
+        return sequence;
+    }
+
+    /**
+     * Adds the specified range to the specified sequence.
+     *
+     * @param  sequence  The {@link #sourceDimensions} or {@link #targetDimensions} sequence to update.
+     * @param  lower     The lower value of the range to add, inclusive.
+     * @param  upper     The upper value of the range to add, exclusive.
+     * @param  max       The maximal value allowed, exclusive.
+     * @throws IllegalArgumentException if the {@code lower} or {@code upper} value does not meet the conditions.
+     */
+    private static int[] add(int[] sequence, final int lower, final int upper, final int max) throws IllegalArgumentException {
+        if (lower < 0 || lower > upper) {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalRange_2, lower, upper));
+        }
+        int min    = 0;  // Inclusive
+        int offset = 0;
+        if (sequence != null && (offset = sequence.length) != 0) {
+            min = sequence[offset - 1] + 1;
+        }
+        final boolean isOutOfRange = (upper > max);
+        if (isOutOfRange || lower < min) {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.ValueOutOfRange_4,
+                    isOutOfRange ? "upper" : "lower", min, max-1, isOutOfRange ? upper : lower));
+        }
+        if (offset == 0) {
+            sequence = series(lower, upper);
+        } else {
+            sequence = Arrays.copyOf(sequence, (offset -= lower) + upper);
+            for (int i=lower; i<upper; i++) {
+                sequence[offset + i] = i;
+            }
+        }
+        assert containsAll(sequence, lower, upper);
+        return sequence;
+    }
+
+    /**
+     * Returns a series of increasing values starting at {@code lower}.
+     */
+    private static int[] series(final int lower, final int upper) throws IllegalArgumentException {
+        final int[] sequence = new int[upper - lower];
+        for (int i = 0; i < sequence.length; i++) {
+            sequence[i] = i + lower;
+        }
+        return sequence;
+    }
+
+    /**
+     * Adds input dimensions to keep in the separated transform.
+     * The given values are <em>source</em> dimension indices of the transform given to the constructor.
+     *
+     * <p>Constraints:</p>
+     * <ul>
+     *   <li>All numbers shall be in the range 0 inclusive to {@link MathTransform#getSourceDimensions()} exclusive.</li>
+     *   <li>The {@code dimensions} values shall be in strictly increasing order.</li>
+     *   <li>The {@code dimensions} values shall be greater than all values specified by all previous calls
+     *       of this method since construction or since the last call to {@link #clear()}.</li>
+     * </ul>
+     *
+     * @param  dimensions A sequence of source dimensions to keep, in strictly increasing order.
+     * @throws IllegalArgumentException if {@code dimensions} contains negative values
+     *         or if values are not in a strictly increasing order.
+     */
+    public void addSourceDimensions(final int... dimensions) throws IllegalArgumentException {
+        ArgumentChecks.ensureNonNull("dimensions", dimensions);
+        sourceDimensions = add(sourceDimensions, dimensions, transform.getSourceDimensions());
+    }
+
+    /**
+     * Adds a range of input dimensions to keep in the separated transform.
+     * The {@code lower} and {@code upper} values define a range of  <em>source</em> dimension indices
+     * of the transform given to the constructor.
+     *
+     * @param  lower The lower dimension, inclusive. Shall not be smaller than 0.
+     * @param  upper The upper dimension, exclusive. Shall be smaller than {@link MathTransform#getSourceDimensions()}.
+     * @throws IllegalArgumentException if {@code lower} or {@code upper} are out of bounds.
+     */
+    public void addSourceDimensionRange(final int lower, final int upper) throws IllegalArgumentException {
+        sourceDimensions = add(sourceDimensions, lower, upper, transform.getSourceDimensions());
+    }
+
+    /**
+     * Returns the input dimensions to keep or kept in the separated transform.
+     * This method performs the first applicable action in the following list:
+     *
+     * <ol>
+     *   <li><p>Source dimensions have been explicitly set by at least one call to {@link #addSourceDimensions(int...)}
+     *       or {@link #addSourceDimensionRange(int, int)} since construction or since last call to {@link #clear()}.
+     *       In such case, this method returns all specified source dimensions.</p></li>
+     *
+     *   <li><p>No source dimensions were set but {@link #separate()} has been invoked.
+     *       In such case, this method returns the sequence of source dimensions that {@code separate()} chooses to retain.
+     *       It is often, but not necessarily, all source dimensions of the transform given at construction time.</p></li>
+     *
+     *   <li><p>Otherwise an exception is thrown.</p></li>
+     * </ol>
+     *
+     * @return The input dimension as a sequence of strictly increasing values.
+     * @throws IllegalStateException if input dimensions have not been set and
+     *         {@link #separate()} has not yet been invoked.
+     */
+    public int[] getSourceDimensions() throws IllegalStateException {
+        if (sourceDimensions != null) {
+            return sourceDimensions.clone();
+        }
+        throw new IllegalStateException(Errors.format(Errors.Keys.UnspecifiedDimensions));
+    }
+
+    /**
+     * Adds output dimensions to keep in the separated transform.
+     * The given values are <em>target</em> dimension indices of the transform given to the constructor.
+     *
+     * <p>Constraints:</p>
+     * <ul>
+     *   <li>All numbers shall be in the range 0 inclusive to {@link MathTransform#getTargetDimensions()} exclusive.</li>
+     *   <li>The {@code dimensions} values shall be in strictly increasing order.</li>
+     *   <li>The {@code dimensions} values shall be greater than all values specified by all previous calls
+     *       of this method since construction or since the last call to {@link #clear()}.</li>
+     * </ul>
+     *
+     * @param  dimensions A sequence of target dimensions to keep, in strictly increasing order.
+     * @throws IllegalArgumentException if {@code dimensions} contains negative values
+     *         or if values are not in a strictly increasing order.
+     */
+    public void addTargetDimensions(final int... dimensions) throws IllegalArgumentException {
+        ArgumentChecks.ensureNonNull("dimensions", dimensions);
+        targetDimensions = add(targetDimensions, dimensions, transform.getTargetDimensions());
+    }
+
+    /**
+     * Adds a range of output dimensions to keep in the separated transform.
+     * The {@code lower} and {@code upper} values define a range of <em>target</em> dimension indices
+     * of the transform given to the constructor.
+     *
+     * @param  lower The lower dimension, inclusive. Shall not be smaller than 0.
+     * @param  upper The upper dimension, exclusive. Shall be smaller than {@link MathTransform#getTargetDimensions()}.
+     * @throws IllegalArgumentException if {@code lower} or {@code upper} are out of bounds.
+     */
+    public void addTargetDimensionRange(final int lower, final int upper) throws IllegalArgumentException {
+        targetDimensions = add(targetDimensions, lower, upper, transform.getTargetDimensions());
+    }
+
+    /**
+     * Returns the output dimensions to keep or kept in the separated transform.
+     * This method performs the first applicable action in the following list:
+     *
+     * <ol>
+     *   <li><p>Target dimensions have been explicitly set by at least one call to {@link #addTargetDimensions(int...)}
+     *       or {@link #addTargetDimensionRange(int, int)} since construction or since last call to {@link #clear()}.
+     *       In such case, this method returns all specified target dimensions.</p></li>
+     *
+     *   <li><p>No target dimensions were set but {@link #separate()} has been invoked.
+     *       In such case, the target dimensions are inferred automatically from the {@linkplain #getSourceDimensions()
+     *       source dimensions} and the transform.</p></li>
+     *
+     *   <li><p>Otherwise an exception is thrown.</p></li>
+     * </ol>
+     *
+     * @return The output dimension as a sequence of strictly increasing values.
+     * @throws IllegalStateException if output dimensions have not been set and
+     *         {@link #separate()} has not yet been invoked.
+     */
+    public int[] getTargetDimensions() throws IllegalStateException {
+        if (targetDimensions != null) {
+            return targetDimensions.clone();
+        }
+        throw new IllegalStateException(Errors.format(Errors.Keys.UnspecifiedDimensions));
+    }
+
+    /**
+     * Separates the math transform specified at construction time for given dimension indices.
+     * This method creates a math transform that use only the {@linkplain #addSourceDimensions(int...) specified
+     * source dimensions} and return only the {@linkplain #addTargetDimensions(int...) specified target dimensions}.
+     * If the source or target dimensions were not specified, then they will be inferred as below:
+     *
+     * <ul>
+     *   <li><p>If source dimensions were unspecified, then the returned transform will keep at least all source
+     *       dimensions needed for computing the specified target dimensions. In many cases the returned transform
+     *       unconditionally keep all source dimensions, but not necessarily. If all source dimensions need to be
+     *       kept, it is better to {@linkplain #addSourceDimensionRange(int, int) specify that explicitely}.</p></li>
+     *
+     *   <li><p>If target dimensions were unspecified, then the returned transform will expect only the specified
+     *       source dimensions as inputs, and the target dimensions will be inferred automatically.</p></li>
+     * </ul>
+     *
+     * The source and target dimensions actually used can be queried by calls to {@link #getSourceDimensions()}
+     * or {@link #getTargetDimensions()} after this {@code separate()} method.
+     *
+     * @return The separated math transform.
+     * @throws FactoryException if the transform can not be separated.
+     */
+    public MathTransform separate() throws FactoryException {
+        MathTransform tr = transform;
+        if (sourceDimensions == null || containsAll(sourceDimensions, 0, tr.getSourceDimensions())) {
+            if (targetDimensions != null && !containsAll(targetDimensions, 0, tr.getTargetDimensions())) {
+                tr = filterTargetDimensions(tr, targetDimensions);
+            }
+            if (sourceDimensions == null) {
+                sourceDimensions = series(0, transform.getSourceDimensions());
+            }
+            if (targetDimensions == null) {
+                targetDimensions = series(0, transform.getTargetDimensions());
+            }
+        } else {
+            /*
+             * At this point there is at least one source dimensions to take in account.
+             * Source dimensions are more difficult to process than target dimensions.
+             */
+            final int[] requested = targetDimensions;
+            tr = filterSourceDimensions(tr, sourceDimensions);  // May update targetDimensions.
+            assert ArraysExt.isSorted(targetDimensions, true) : "targetDimensions";
+            if (requested != null) {
+                final int[] inferred = targetDimensions;
+                targetDimensions = requested;
+                final int[] subDimensions = new int[requested.length];
+                for (int i=0; i<requested.length; i++) {
+                    final int r = requested[i];
+                    final int j = Arrays.binarySearch(inferred, r);
+                    if (j < 0) {
+                        /*
+                         * The user asked for some target dimensions that we can not keep, probably
+                         * because at least one of the requested target dimensions has a dependency to a
+                         * source dimension that does not appear in the list of source dimensions to keep.
+                         */
+                        throw new FactoryException(Errors.format(Errors.Keys.CanNotSeparateTargetDimension_1, r));
+                    }
+                    subDimensions[i] = j;
+                }
+                tr = filterTargetDimensions(tr, subDimensions);
+            }
+        }
+        /*
+         * We are done. But do a final verification on the number of dimensions.
+         */
+        int type     = 0;
+        int expected = sourceDimensions.length;
+        int actual   = tr.getSourceDimensions();
+        if (actual == expected) {
+            type     = 1;
+            expected = targetDimensions.length;
+            actual   = tr.getTargetDimensions();
+            if (actual == expected) {
+                return tr;
+            }
+        }
+        throw new FactoryException(Errors.format(Errors.Keys.MismatchedTransformDimension_3, type, expected, actual));
+    }
+
+    /**
+     * Creates a transform for the same mathematic than the given {@code step}
+     * but expecting only the given dimensions as inputs.
+     * This method is invoked by {@link #separate()} when user-specified source dimensions need to be taken in account.
+     * The given {@code step} and {@code dimensions} are typically the values of
+     * {@link #transform} and {@link #sourceDimensions} fields respectively, but not necessarily.
+     * In particular those arguments will differ when this method is invoked recursively for processing
+     * concatenated or {@linkplain PassThroughTransform#getSubTransform() sub-transforms}.
+     *
+     * <p>Subclasses can override this method if they need to handle some {@code MathTransform} implementations
+     * in a special way. However all implementations of this method shall obey to the following contract:</p>
+     * <ul>
+     *   <li>{@link #sourceDimensions} and {@link #targetDimensions} should not be assumed accurate
+     *       since they may be temporarily outdated or modified during recursive calls to this method.</li>
+     *   <li>{@link #sourceDimensions} should not be modified by this method.</li>
+     *   <li>{@link #targetDimensions} <strong>must</strong> be <em>overwritten</em> (not updated) by this method to the
+     *       sequence of all target dimensions of {@code step} that are also target dimensions of the returned transform.
+     *       The indices shall be in strictly increasing order from 0 inclusive to
+     *       {@code step.getTargetDimensions()} exclusive.</li>
+     * </ul>
+     *
+     * @param  step The transform for which to retain only a subset of the source dimensions.
+     * @param  dimensions Indices of the source dimensions of {@code step} to retain.
+     * @return A transform expecting only the given source dimensions.
+     * @throws FactoryException if the given transform is not separable.
+     */
+    @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
+    protected MathTransform filterSourceDimensions(final MathTransform step, final int[] dimensions) throws FactoryException {
+        if (dimensions.length == 0) {
+            return IdentityTransform.create(0);
+        }
+        final int numSrc = step.getSourceDimensions();
+        final int numTgt = step.getTargetDimensions();
+        final int lower  = dimensions[0];
+        final int upper  = dimensions[dimensions.length - 1] + 1;
+        if (lower == 0 && upper == numSrc && dimensions.length == numSrc) {
+            targetDimensions = series(0, numTgt);
+            return step;
+        }
+        if (step.isIdentity()) {
+            targetDimensions = dimensions;
+            return IdentityTransform.create(dimensions.length);
+        }
+        if (step instanceof ConcatenatedTransform) {
+            final ConcatenatedTransform ctr = (ConcatenatedTransform) step;
+            final MathTransform step1 = filterSourceDimensions(ctr.transform1, dimensions);
+            final MathTransform step2 = filterSourceDimensions(ctr.transform2, targetDimensions);
+            return factory.createConcatenatedTransform(step1, step2);
+            // Keep the 'targetDimensions' computed by the last step.
+        }
+        /*
+         * Special case for the passthrough transform: if at least one input dimension belong to the pass-
+         * through sub-transform, then invoke this method recursively for the sub-transform dimensions.
+         */
+        if (step instanceof PassThroughTransform) {
+            final PassThroughTransform passThrough = (PassThroughTransform) step;
+            final int numSubSrc = passThrough.subTransform.getSourceDimensions();
+            final int numNewDim = passThrough.subTransform.getTargetDimensions() - numSubSrc;
+            final int subLower  = passThrough.firstAffectedOrdinate;
+            final int subUpper  = subLower + numSubSrc;
+            int[] subDimensions = new int[dimensions.length];
+            targetDimensions    = null;
+            int n = 0;
+            for (int dim : dimensions) {
+                if (dim >= subLower) {
+                    if (dim < subUpper) {
+                        // Dimension n belong to the subtransform.
+                        subDimensions[n++] = dim - subLower;
+                        continue;
+                    }
+                    dim += numNewDim;
+                }
+                // Dimension n belong to heading or trailing dimensions.
+                // Passthrough, after adjustment for trailing dimensions.
+                targetDimensions = insert(targetDimensions, dim);
+            }
+            subDimensions = ArraysExt.resize(subDimensions, n);
+            /*
+             * If no source dimension belong to the sub-transform, then all source dimensions are heading or
+             * trailing dimensions. A passthrough transform without its sub-transform is an identity transform.
+             */
+            if (n == 0) {
+                return IdentityTransform.create(dimensions.length);
+            }
+            /*
+             * There is at least one dimension to separate in the sub-transform. Perform this separation and get
+             * the list of target dimensions. We need to offset the target dimensions by the amount of leading
+             * dimensions once the separation is done, in order to translate from the sub-transform's dimension
+             * numbering to the transform's numbering.
+             */
+            int[] target = targetDimensions;
+            final MathTransform subTransform = filterSourceDimensions(passThrough.subTransform, subDimensions);
+            for (final int dim : targetDimensions) {
+                target = insert(target, dim + subLower);
+            }
+            targetDimensions = target;
+            /*
+             * If all source dimensions not in the sub-transform are consecutive numbers, we can use our passthrough
+             * transform implementation. The "consecutive numbers" requirement (expressed in the 'if' statement below)
+             * is a consequence of a limitation in our current implementation: our current passthrough transform does
+             * not accept arbitrary index for modified ordinates.
+             */
+            if (containsAll(dimensions, lower, subLower) && containsAll(dimensions, subUpper, upper)) {
+                return factory.createPassThroughTransform(subLower - lower, subTransform, upper - subUpper);
+            }
+        }
+        /*
+         * If the transform is affine (or at least projective), express the transform as a matrix. Then, select
+         * target dimensions that depend only on specified source dimensions. If a target dimension depends on
+         * at least one discarded source dimension, then that output dimension will be discarded as well.
+         */
+        final Matrix matrix = MathTransforms.getMatrix(step);
+        if (matrix != null) {
+            targetDimensions = null;
+            int startOfRow = 0;
+            boolean isLastRowAccepted = false;
+            final int numFilteredColumns = (dimensions.length + 1);
+            double[] elements = new double[(numTgt + 1) * numFilteredColumns];
+reduce:     for (int j=0; j <= numTgt; j++) {
+                /*
+                 * For each target dimension (i.e. a matrix row), find the matrix elements (excluding translation
+                 * terms in the last column) for each source dimension to be kept. If a dependancy to at least one
+                 * discarded input dimension is found, then the whole output dimension is discarded.
+                 */
+                int filteredColumn = 0;
+                for (int i=0; i<numSrc; i++) {
+                    final double element = matrix.getElement(j,i);
+                    if (filteredColumn < dimensions.length && dimensions[filteredColumn] == i) {
+                        elements[startOfRow + filteredColumn++] = element;
+                    } else if (element != 0) {
+                        // Output dimension 'j' depends on one of discarded input dimension 'i'.
+                        // The whole row will be discarded.
+                        continue reduce;
+                    }
+                }
+                elements[startOfRow + filteredColumn++] = matrix.getElement(j, numSrc);  // Copy the translation term.
+                assert filteredColumn == numFilteredColumns : filteredColumn;            // We should have used all values in the 'dimensions' array.
+                startOfRow += numFilteredColumns;
+                if (j == numTgt) {
+                    // In an affine transform, the last row is usually [0 0 0 … 1].
+                    // This is not a real dimension, but nevertheless mandatory.
+                    isLastRowAccepted = true;
+                } else {
+                    targetDimensions = insert(targetDimensions, j);
+                }
+            }
+            if (isLastRowAccepted) {
+                elements = ArraysExt.resize(elements, startOfRow);
+                return factory.createAffineTransform(Matrices.create(startOfRow / numFilteredColumns, numFilteredColumns, elements));
+            }
+            /*
+             * In an affine transform, the last row is not supposed to have dependency to any source dimension.
+             * But if we reach this point, our matrix has such dependencies.
+             */
+        }
+        throw new FactoryException(Errors.format(Errors.Keys.NotAnAffineTransform));
+    }
+
+    /**
+     * Creates a transform for the same mathematic than the given {@code step}
+     * but producing only the given dimensions as outputs.
+     * This method is invoked by {@link #separate()} when user-specified target dimensions need to be taken in account.
+     * The given {@code step} and {@code dimensions} are typically the values of
+     * {@link #transform} and {@link #targetDimensions} fields respectively, but not necessarily.
+     *
+     * <p>Subclasses can override this method if they need to handle some {@code MathTransform} implementations
+     * in a special way. However all implementations of this method shall obey to the following contract:</p>
+     * <ul>
+     *   <li>{@link #sourceDimensions} and {@link #targetDimensions} should not be assumed accurate.</li>
+     *   <li>{@link #sourceDimensions} should not be modified by this method.</li>
+     *   <li>{@link #targetDimensions} should not be modified by this method.</li>
+     * </ul>
+     *
+     * The number and nature of inputs stay unchanged. For example if the supplied {@code transform}
+     * has (<var>longitude</var>, <var>latitude</var>, <var>height</var>) outputs, then a filtered
+     * transform may keep only the (<var>longitude</var>, <var>latitude</var>) part for the same inputs.
+     * In most cases, the filtered transform is non-invertible since it loose informations.
+     *
+     * @param  step The transform for which to retain only a subset of the target dimensions.
+     * @param  dimensions Indices of the target dimensions of {@code step} to retain.
+     * @return A transform producing only the given target dimensions.
+     * @throws FactoryException if the given transform is not separable.
+     */
+    protected MathTransform filterTargetDimensions(MathTransform step, final int[] dimensions) throws FactoryException {
+        final int numSrc = step.getSourceDimensions();
+              int numTgt = step.getTargetDimensions();
+        final int lower  = dimensions[0];
+        final int upper  = dimensions[dimensions.length - 1];
+        if (lower == 0 && upper == numTgt && dimensions.length == numTgt) {
+            return step;
+        }
+        /*
+         * If the transform is an instance of passthrough transform but no dimension from its sub-transform
+         * is requested, then ignore the sub-transform (i.e. treat the whole transform as identity, except
+         * for the number of target dimension which may be different from the number of input dimension).
+         */
+        int removeAt = 0;
+        int numRemoved = 0;
+        if (step instanceof PassThroughTransform) {
+            final PassThroughTransform passThrough = (PassThroughTransform) step;
+            final int subLower  = passThrough.firstAffectedOrdinate;
+            final int numSubTgt = passThrough.subTransform.getTargetDimensions();
+            if (!containsAny(dimensions, subLower, subLower + numSubTgt)) {
+                step = IdentityTransform.create(numTgt = numSrc);
+                removeAt = subLower;
+                numRemoved = numSubTgt - passThrough.subTransform.getSourceDimensions();
+            }
+        }
+        /*                                                  ┌  ┐     ┌          ┐ ┌ ┐
+         * Create the matrix to be used as a filter         │x'│     │1  0  0  0│ │x│
+         * and concatenate it to the transform. The         │z'│  =  │0  0  1  0│ │y│
+         * matrix will contain 1 only in the target         │1 │     │0  0  0  1│ │z│
+         * dimensions to keep, as in this example:          └  ┘     └          ┘ │1│
+         *                                                                        └ ┘
+         */
+        final Matrix matrix = Matrices.createZero(dimensions.length + 1, numTgt + 1);
+        for (int j=0; j<dimensions.length; j++) {
+            int i = dimensions[j];
+            if (i >= removeAt) {
+                i -= numRemoved;
+            }
+            matrix.setElement(j, i, 1);
+        }
+        matrix.setElement(dimensions.length, numTgt, 1);
+        return factory.createConcatenatedTransform(step, factory.createAffineTransform(matrix));
+    }
+
+    /**
+     * Returns {@code true} if the given sequence contains all index in the range {@code lower} inclusive
+     * to {@code upper} exclusive.
+     *
+     * @param  sequence The {@link #sourceDimensions} or {@link #targetDimensions} sequence to test.
+     * @param  lower    The lower value, inclusive.
+     * @param  upper    The upper value, exclusive.
+     * @return {@code true} if the full range was found in the sequence.
+     */
+    private static boolean containsAll(final int[] sequence, final int lower, int upper) {
+        if (lower == upper) {
+            return true;
+        }
+        if (sequence != null) {
+            assert ArraysExt.isSorted(sequence, true);
+            int index = Arrays.binarySearch(sequence, lower);
+            if (index >= 0) {
+                index += --upper - lower;
+                if (index >= 0 && index < sequence.length) {
+                    return sequence[index] == upper;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns {@code true} if the given sequence contains any value in the given range.
+     *
+     * @param  sequence The {@link #sourceDimensions} or {@link #targetDimensions} sequence to test.
+     * @param  lower    The lower value, inclusive.
+     * @param  upper    The upper value, exclusive.
+     * @return {@code true} if the sequence contains at least one value in the given range.
+     */
+    private static boolean containsAny(final int[] sequence, final int lower, final int upper) {
+        if (upper == lower) {
+            return true;
+        }
+        if (sequence != null) {
+            assert ArraysExt.isSorted(sequence, true);
+            int index = Arrays.binarySearch(sequence, lower);
+            if (index >= 0) {
+                return true;
+            }
+            index = ~index;    // Tild, not minus sign.
+            return (index < sequence.length) && (sequence[index] < upper);
+        }
+        return false;
+    }
+}

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

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

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/GeocentricTranslationTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/GeocentricTranslationTest.java?rev=1718283&r1=1718282&r2=1718283&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/GeocentricTranslationTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/GeocentricTranslationTest.java [UTF-8] Mon Dec  7 10:05:22 2015
@@ -40,7 +40,7 @@ import org.apache.sis.test.DependsOn;
 import org.junit.Test;
 
 import static java.lang.StrictMath.toRadians;
-import static org.junit.Assert.*;
+import static org.opengis.test.Assert.*;
 
 
 /**
@@ -195,8 +195,12 @@ public final strictfp class GeocentricTr
         final Parameters values = Parameters.castOrWrap(factory.getDefaultParameters("Geocentric translations (geog2D domain)"));
         setTranslation(values);
         setEllipsoids(values, CommonCRS.WGS84.ellipsoid(), CommonCRS.ED50.ellipsoid());
-        return Geographic3Dto2DTest.createDatumShiftForGeographic2D(factory,
-                new GeocentricTranslation().createMathTransform(factory, values), values);
+        final MathTransform gt = new GeocentricTranslation().createMathTransform(factory, values);
+        assertFalse("isIdentity", gt.isIdentity());
+        assertEquals("sourceDimensions", 3, gt.getSourceDimensions());
+        assertEquals("targetDimensions", 3, gt.getTargetDimensions());
+        assertInstanceOf("Geocentric translation", LinearTransform.class, gt);
+        return Geographic3Dto2DTest.createDatumShiftForGeographic2D(factory, gt, values);
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/Geographic3Dto2DTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/Geographic3Dto2DTest.java?rev=1718283&r1=1718282&r2=1718283&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/Geographic3Dto2DTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/Geographic3Dto2DTest.java [UTF-8] Mon Dec  7 10:05:22 2015
@@ -83,6 +83,8 @@ public final strictfp class Geographic3D
     static MathTransform createDatumShiftForGeographic2D(final MathTransformFactory factory,
             final MathTransform affine, final Parameters pv) throws FactoryException
     {
+        assertEquals("sourceDimensions", 3, affine.getSourceDimensions());
+        assertEquals("targetDimensions", 3, affine.getTargetDimensions());
         /*
          * Create a "Geographic to Geocentric" conversion with ellipsoid axis length units converted to metres
          * (the unit implied by SRC_SEMI_MAJOR) because it is the unit of Bursa-Wolf parameters that we created above.
@@ -91,12 +93,19 @@ public final strictfp class Geographic3D
         step.getOrCreate(MapProjection.SEMI_MAJOR).setValue(pv.doubleValue(GeocentricAffineBetweenGeographic.SRC_SEMI_MAJOR));
         step.getOrCreate(MapProjection.SEMI_MINOR).setValue(pv.doubleValue(GeocentricAffineBetweenGeographic.SRC_SEMI_MINOR));
         MathTransform toGeocentric = factory.createParameterizedTransform(step);
+        assertEquals("sourceDimensions", 3, toGeocentric.getSourceDimensions());
+        assertEquals("targetDimensions", 3, toGeocentric.getTargetDimensions());
+
         final MathTransform reduce = factory.createParameterizedTransform(factory.getDefaultParameters("Geographic3D to 2D conversion"));
+        assertEquals("sourceDimensions", 3, reduce.getSourceDimensions());
+        assertEquals("targetDimensions", 2, reduce.getTargetDimensions());
         try {
             toGeocentric = factory.createConcatenatedTransform(reduce.inverse(), toGeocentric);
         } catch (NoninvertibleTransformException e) {
             throw new FactoryException(e);
         }
+        assertEquals("sourceDimensions", 2, toGeocentric.getSourceDimensions());
+        assertEquals("targetDimensions", 3, toGeocentric.getTargetDimensions());
         /*
          * Create a "Geocentric to Geographic" conversion with ellipsoid axis length units converted to metres
          * because this is the unit of the Geocentric CRS used above.
@@ -105,7 +114,12 @@ public final strictfp class Geographic3D
         step.getOrCreate(MapProjection.SEMI_MAJOR).setValue(pv.doubleValue(GeocentricAffineBetweenGeographic.TGT_SEMI_MAJOR));
         step.getOrCreate(MapProjection.SEMI_MINOR).setValue(pv.doubleValue(GeocentricAffineBetweenGeographic.TGT_SEMI_MINOR));
         MathTransform toGeographic = factory.createParameterizedTransform(step);
+        assertEquals("sourceDimensions", 3, toGeographic.getSourceDimensions());
+        assertEquals("targetDimensions", 3, toGeographic.getTargetDimensions());
+
         toGeographic = factory.createConcatenatedTransform(toGeographic, reduce);
+        assertEquals("sourceDimensions", 3, toGeographic.getSourceDimensions());
+        assertEquals("targetDimensions", 2, toGeographic.getTargetDimensions());
         /*
          * The  Geocentric → Affine → Geographic  chain.
          */

Added: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformSeparatorTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformSeparatorTest.java?rev=1718283&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformSeparatorTest.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformSeparatorTest.java [UTF-8] Mon Dec  7 10:05:22 2015
@@ -0,0 +1,286 @@
+/*
+ * 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.Iterator;
+import javax.measure.unit.SI;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.referencing.datum.HardCodedDatum;
+import org.apache.sis.referencing.operation.matrix.Matrices;
+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.opengis.test.Assert.*;
+
+
+/**
+ * Tests {@link TransformSeparator}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ */
+@DependsOn({
+    PassThroughTransformTest.class,
+    ConcatenatedTransformTest.class
+})
+public final strictfp class TransformSeparatorTest extends TestCase {
+    /**
+     * Verifies the argument checks performed by the {@code add} methods.
+     */
+    @Test
+    public void testArgumentChecks() {
+        final TransformSeparator s = new TransformSeparator(MathTransforms.identity(8));
+        try {
+            s.getSourceDimensions();
+            fail("Shall not return unspecified dimensions.");
+        } catch (IllegalStateException e) {
+            // This is the expected exception.
+        }
+        try {
+            s.getTargetDimensions();
+            fail("Shall not return unspecified dimensions.");
+        } catch (IllegalStateException e) {
+            // This is the expected exception.
+        }
+        s.addSourceDimensionRange(1, 4);
+        s.addTargetDimensions(0, 3, 4);
+        assertArrayEquals("sourceDimensions", new int[] {1, 2, 3}, s.getSourceDimensions());
+        assertArrayEquals("targetDimensions", new int[] {0, 3, 4}, s.getTargetDimensions());
+        try {
+            s.addSourceDimensions(3, 4, 5);
+            fail("Shall not accept non-increasing value.");
+        } catch (IllegalArgumentException e) {
+            // This is the expected exception.
+            assertTrue(e.getMessage().contains("dimensions[0]"));
+        }
+        try {
+            s.addTargetDimensionRange(3, 5);
+            fail("Shall not accept non-increasing value.");
+        } catch (IllegalArgumentException e) {
+            // This is the expected exception.
+            assertTrue(e.getMessage().contains("lower"));
+        }
+        s.addSourceDimensions(4, 6);
+        s.addTargetDimensionRange(6, 8);
+        assertArrayEquals("sourceDimensions", new int[] {1, 2, 3, 4, 6}, s.getSourceDimensions());
+        assertArrayEquals("targetDimensions", new int[] {0, 3, 4, 6, 7}, s.getTargetDimensions());
+        try {
+            s.addSourceDimensions(8);
+            fail("Shall not accept value out of range.");
+        } catch (IllegalArgumentException e) {
+            // This is the expected exception.
+            assertTrue(e.getMessage().contains("dimensions[0]"));
+        }
+        try {
+            s.addTargetDimensions(3, 8);
+            fail("Shall not accept value out of range.");
+        } catch (IllegalArgumentException e) {
+            // This is the expected exception.
+            assertTrue(e.getMessage().contains("dimensions[0]"));
+        }
+    }
+
+    /**
+     * Tests separation of a linear transform.
+     *
+     * @throws FactoryException if an error occurred while creating a new transform.
+     */
+    @Test
+    public void testLinearTransform() throws FactoryException {
+        Matrix matrix = Matrices.create(4, 4, new double[] {
+            2, 0, 0, 7,     // Some random values.
+            0, 5, 0, 6,
+            1, 0, 3, 8,
+            0, 0, 0, 1
+        });
+        final TransformSeparator s = new TransformSeparator(MathTransforms.linear(matrix));
+        /*
+         * Trivial case: no dimension specified, we should get the transform unchanged.
+         */
+        assertSame("transform", s.transform, s.separate());
+        assertArrayEquals("sourceDimensions", new int[] {0, 1, 2}, s.getSourceDimensions());
+        assertArrayEquals("targetDimensions", new int[] {0, 1, 2}, s.getTargetDimensions());
+        /*
+         * Filter only target dimensions. This is the easiest non-trivial case since we just
+         * need to drop some rows. There is no analysis to perform on the matrix values.
+         */
+        matrix = Matrices.create(3, 4, new double[] {
+            2, 0, 0, 7,
+            1, 0, 3, 8,
+            0, 0, 0, 1
+        });
+        s.clear();
+        s.addTargetDimensions(0, 2);
+        assertMatrixEquals("transform", matrix, ((LinearTransform) s.separate()).getMatrix(), STRICT);
+        assertArrayEquals("sourceDimensions", new int[] {0, 1, 2}, s.getSourceDimensions());
+        assertArrayEquals("targetDimensions", new int[] {0, 2},    s.getTargetDimensions());
+        /*
+         * Filter only source dimensions. Do not specify any target dimensions for now.
+         * TransformSeparator needs to examine the matrix values and drop all target dimensions
+         * that depend on an excluded source dimensions.
+         */
+        matrix = Matrices.create(2, 3, new double[] {
+            5, 0, 6,
+            0, 0, 1
+        });
+        s.clear();
+        s.addSourceDimensions(1, 2);
+        assertMatrixEquals("transform", matrix, ((LinearTransform) s.separate()).getMatrix(), STRICT);
+        assertArrayEquals("sourceDimensions", new int[] {1, 2}, s.getSourceDimensions());
+        assertArrayEquals("targetDimensions", new int[] {1},    s.getTargetDimensions());
+        /*
+         * Filter both source and target dimensions. Source dimensions 0 and 2 allow the target dimensions 0 and 2
+         * (target dimension 1 is discarded because it depends on source dimension 1).  Then the target dimensions
+         * are filtered for retaining only dimension 0.
+         */
+        matrix = Matrices.create(2, 3, new double[] {
+            2, 0, 7,
+            0, 0, 1
+        });
+        s.clear();
+        s.addSourceDimensions(0, 2);
+        s.addTargetDimensions(0);
+        assertMatrixEquals("transform", matrix, ((LinearTransform) s.separate()).getMatrix(), STRICT);
+        assertArrayEquals("sourceDimensions", new int[] {0, 2}, s.getSourceDimensions());
+        assertArrayEquals("targetDimensions", new int[] {0},    s.getTargetDimensions());
+        /*
+         * Try again, but with the addition of a target dimension that TransformSeparator can not keep.
+         * It shall cause an exception to be thrown.
+         */
+        s.addTargetDimensions(1);
+        try {
+            s.separate();
+            fail("Should not have been able to separate that transform.");
+        } catch (FactoryException e) {
+            // This is the expected exception.
+            assertNotNull(e.getMessage());
+        }
+    }
+
+    /**
+     * Tests separation of a concatenated transform.
+     *
+     * @throws FactoryException if an error occurred while creating a new transform.
+     */
+    @Test
+    @DependsOnMethod("testLinearTransform")
+    public void testConcatenatedTransform() throws FactoryException {
+        final MathTransformFactory factory = DefaultFactories.forBuildin(MathTransformFactory.class);
+        final TransformSeparator s = new TransformSeparator(EllipsoidToCentricTransform.createGeodeticConversion(
+                factory, HardCodedDatum.WGS84.getEllipsoid(), false), factory);
+
+        s.addSourceDimensions(0, 1);
+        s.addTargetDimensions(0, 1);
+        final Iterator<MathTransform> it = MathTransforms.getSteps(s.separate()).iterator();
+        assertInstanceOf("normalize",   LinearTransform.class,             it.next());
+        assertInstanceOf("transform",   EllipsoidToCentricTransform.class, it.next());
+        assertInstanceOf("denormalize", LinearTransform.class,             it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * Tests separation of a pass through transform.
+     *
+     * @throws FactoryException if an error occurred while creating a new transform.
+     */
+    @Test
+    @DependsOnMethod("testLinearTransform")
+    public void testPassThroughTransform() throws FactoryException {
+        final MathTransform nonLinear = new EllipsoidToCentricTransform(6378137, 6356752.314245179,
+                SI.METRE, false, EllipsoidToCentricTransform.TargetType.CARTESIAN);
+        final TransformSeparator s = new TransformSeparator(PassThroughTransform.create(2, nonLinear, 3));
+        /*
+         * Trivial case: no dimension specified, we should get the transform unchanged.
+         */
+        assertSame("transform", s.transform, s.separate());
+        assertArrayEquals("sourceDimensions", new int[] {0, 1, 2, 3, 4, 5, 6},    s.getSourceDimensions());
+        assertArrayEquals("targetDimensions", new int[] {0, 1, 2, 3, 4, 5, 6, 7}, s.getTargetDimensions());
+        /*
+         * Filter only target dimensions. If the requested indices overlap the pass-through transform,
+         * TransformSeparator will just concatenate a matrix after the transform for dropping dimensions.
+         */
+        Matrix matrix = Matrices.create(4, 9, new double[] {
+            0, 1, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 1, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 1, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 1
+        });
+        s.clear();
+        s.addTargetDimensions(1, 2, 7);
+        MathTransform r = s.separate();
+        assertArrayEquals("sourceDimensions", new int[] {0, 1, 2, 3, 4, 5, 6}, s.getSourceDimensions());
+        assertArrayEquals("targetDimensions", new int[] {1, 2, 7}, s.getTargetDimensions());
+        assertInstanceOf ("separate()", ConcatenatedTransform.class, r);
+        assertSame(s.transform, ((ConcatenatedTransform) r).transform1);
+        assertMatrixEquals("separate().transform2", matrix,
+                ((LinearTransform) (((ConcatenatedTransform) r).transform2)).getMatrix(), STRICT);
+        /*
+         * Filter only target dimensions, but with indices that are all outside the pass-through transform.
+         * TransformSeparator should be able to give us a simple affine transform.
+         */
+        matrix = Matrices.create(4, 8, new double[] {
+            0, 1, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 1, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 1, 0,
+            0, 0, 0, 0, 0, 0, 0, 1
+        });
+        s.clear();
+        s.addTargetDimensions(1, 5, 7);
+        r = s.separate();
+        assertArrayEquals ("sourceDimensions", new int[] {0, 1, 2, 3, 4, 5, 6}, s.getSourceDimensions());
+        assertArrayEquals ("targetDimensions", new int[] {1, 5, 7}, s.getTargetDimensions());
+        assertInstanceOf  ("separate()", LinearTransform.class, r);
+        assertMatrixEquals("separate().transform2", matrix, ((LinearTransform) r).getMatrix(), STRICT);
+        /*
+         * Filter source dimensions. If we ask only for dimensions not in the pass-through transform,
+         * then TransformSeparator should return an affine transform.
+         */
+        matrix = Matrices.create(3, 3, new double[] {
+            1, 0, 0,
+            0, 1, 0,
+            0, 0, 1
+        });
+        s.clear();
+        s.addSourceDimensions(0, 6);
+        r = s.separate();
+        assertArrayEquals ("sourceDimensions", new int[] {0, 6}, s.getSourceDimensions());
+        assertArrayEquals ("targetDimensions", new int[] {0, 7}, s.getTargetDimensions());
+        assertInstanceOf  ("separate()", LinearTransform.class, r);
+        assertMatrixEquals("separate().transform2", matrix, ((LinearTransform) r).getMatrix(), STRICT);
+        /*
+         * Filter source dimensions, now with overlapping in the pass-through transform.
+         * TransformSeparator is expected to create a new PassThroughTransform.
+         */
+        s.clear();
+        s.addSourceDimensions(1, 2, 3, 4, 5);
+        r = s.separate();
+        assertArrayEquals("sourceDimensions", new int[] {1, 2, 3, 4, 5}, s.getSourceDimensions());
+        assertArrayEquals("targetDimensions", new int[] {1, 2, 3, 4, 5, 6}, s.getTargetDimensions());
+        assertInstanceOf ("separate()", PassThroughTransform.class, r);
+        assertSame  ("subTransform",  nonLinear, ((PassThroughTransform) r).subTransform);
+        assertEquals("firstAffectedOrdinate", 1, ((PassThroughTransform) r).firstAffectedOrdinate);
+        assertEquals("numTrailingOrdinates",  2, ((PassThroughTransform) r).numTrailingOrdinates);
+    }
+}

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

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

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1718283&r1=1718282&r2=1718283&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] Mon Dec  7 10:05:22 2015
@@ -113,6 +113,7 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.operation.transform.CopyTransformTest.class,
     org.apache.sis.referencing.operation.transform.PassThroughTransformTest.class,
     org.apache.sis.referencing.operation.transform.ConcatenatedTransformTest.class,
+    org.apache.sis.referencing.operation.transform.TransformSeparatorTest.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,

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1718283&r1=1718282&r2=1718283&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] Mon Dec  7 10:05:22 2015
@@ -151,6 +151,11 @@ public final class Errors extends Indexe
         public static final short CanNotRepresentInFormat_2 = 10;
 
         /**
+         * Target dimension {0} depends on excluded source dimensions.
+         */
+        public static final short CanNotSeparateTargetDimension_1 = 175;
+
+        /**
          * Can not set a value for property “{0}”.
          */
         public static final short CanNotSetPropertyValue_1 = 11;
@@ -479,11 +484,6 @@ public final class Errors extends Indexe
         public static final short InfiniteArgumentValue_1 = 51;
 
         /**
-         * Inseparable transform.
-         */
-        public static final short InseparableTransform = 175;
-
-        /**
          * Argument ‘{0}’ shall contain at least {1} elements. A number of {2} is insufficient.
          */
         public static final short InsufficientArgumentSize_3 = 53;
@@ -1041,6 +1041,11 @@ public final class Errors extends Indexe
         public static final short UnspecifiedCRS = 173;
 
         /**
+         * Dimensions have not been specified.
+         */
+        public static final short UnspecifiedDimensions = 204;
+
+        /**
          * No format is specified for objects of class ‘{0}’.
          */
         public static final short UnspecifiedFormatForClass_1 = 126;

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1718283&r1=1718282&r2=1718283&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] Mon Dec  7 10:05:22 2015
@@ -41,6 +41,7 @@ CanNotOpen_1                      = Can
 CanNotParseFile_2                 = Can not parse \u201c{1}\u201d as a file in the {0} format.
 CanNotRead_1                      = Can not read \u201c{0}\u201d.
 CanNotRepresentInFormat_2         = Can not represent \u201c{1}\u201d in the {0} format.
+CanNotSeparateTargetDimension_1   = Target dimension {0} depends on excluded source dimensions.
 CanNotSetPropertyValue_1          = Can not set a value for property \u201c{0}\u201d.
 CanNotTransformEnvelope           = Can not transform envelope.
 CanNotTransformEnvelopeToGeodetic = Can not transform envelope to a geodetic CRS.
@@ -106,7 +107,6 @@ IdentifierAlreadyBound_1          = Iden
 IndexOutOfBounds_1                = Index {0} is out of bounds.
 IndicesOutOfBounds_2              = Indices ({0}, {1}) are out of bounds.
 InfiniteArgumentValue_1           = Argument \u2018{0}\u2019 can not take an infinite value.
-InseparableTransform              = Inseparable transform.
 InsufficientArgumentSize_3        = Argument \u2018{0}\u2019 shall contain at least {1} elements. A number of {2} is insufficient.
 KeyCollision_1                    = A different value is already associated to the \u201c{0}\u201d key.
 LatitudesAreOpposite_2            = Latitudes {0} and {1} are opposite.
@@ -219,6 +219,7 @@ UnparsableStringForClass_3        = Text
 UnparsableStringInElement_2       = Can not parse \u201c{1}\u201d in element \u201c{0}\u201d.
 UnresolvedFeatureName_1           = Feature named \u201c{0}\u201d has not yet been resolved.
 UnspecifiedCRS                    = Coordinate reference system has not been specified.
+UnspecifiedDimensions             = Dimensions have not been specified.
 UnspecifiedFormatForClass_1       = No format is specified for objects of class \u2018{0}\u2019.
 UnspecifiedParameterValues        = Parameter values have not been specified.
 UnsupportedImplementation_1       = Can not handle instances of \u2018{0}\u2019 because arbitrary implementations are not yet supported.

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1718283&r1=1718282&r2=1718283&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] Mon Dec  7 10:05:22 2015
@@ -38,6 +38,7 @@ CanNotOpen_1                      = Ne p
 CanNotParseFile_2                 = Ne peut pas lire \u00ab\u202f{1}\u202f\u00bb comme un fichier au format {0}.
 CanNotRead_1                      = Ne peut pas lire \u00ab\u202f{0}\u202f\u00bb.
 CanNotRepresentInFormat_2         = Ne peut pas repr\u00e9senter \u00ab\u202f{1}\u202f\u00bb dans le format {0}.
+CanNotSeparateTargetDimension_1   = La dimension de destination {0} d\u00e9pend de dimensions sources qui ont \u00e9t\u00e9 exclues.
 CanNotSetPropertyValue_1          = Ne peut pas d\u00e9finir une valeur pour la propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb.
 CanNotTransformEnvelope           = Ne peut pas transformer l\u2019enveloppe.
 CanNotTransformEnvelopeToGeodetic = Ne peut pas transformer l\u2019enveloppe vers un syst\u00e8me g\u00e9od\u00e9sique.
@@ -103,7 +104,6 @@ IdentifierAlreadyBound_1          = L\u2
 IndexOutOfBounds_1                = L\u2019index {0} est en dehors des limites permises.
 IndicesOutOfBounds_2              = Les index ({0}, {1}) sont en dehors des limites permises.
 InfiniteArgumentValue_1           = L\u2019argument \u2018{0}\u2019 ne peut pas prendre une valeur infinie.
-InseparableTransform              = La transformation n\u2019est pas s\u00e9parable.
 InsufficientArgumentSize_3        = L\u2019argument \u2018{0}\u2019 doit contenir au moins {1} \u00e9l\u00e9ments. Un nombre de {2} est insuffisant.
 KeyCollision_1                    = Une valeur diff\u00e9rente est d\u00e9j\u00e0 associ\u00e9e \u00e0 la cl\u00e9 \u00ab\u202f{0}\u202f\u00bb.
 LatitudesAreOpposite_2            = Les latitudes {0} et {1} sont oppos\u00e9es.
@@ -211,6 +211,7 @@ UnmodifiableGeometry              = Cett
 UnmodifiableMetadata              = Cette m\u00e9ta-donn\u00e9e n\u2019est pas modifiable.
 UnmodifiableObject_1              = Cette instance de \u2018{0}\u2019 n\u2019est pas modifiable.
 UnspecifiedCRS                    = Le syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es n\u2019a pas \u00e9t\u00e9 sp\u00e9cifi\u00e9.
+UnspecifiedDimensions             = Les dimensions n\u2019ont pas \u00e9t\u00e9 sp\u00e9cifi\u00e9es.
 UnspecifiedFormatForClass_1       = Aucun format n\u2019est sp\u00e9cifi\u00e9 pour les objets de classe \u2018{0}\u2019.
 UnspecifiedParameterValues        = Les valeurs des param\u00e8tres n\u2019ont pas \u00e9t\u00e9 sp\u00e9cifi\u00e9es.
 UnparsableStringForClass_2        = Le texte \u00ab\u202f{1}\u202f\u00bb n\u2019est pas reconnu comme un objet de type \u2018{0}\u2019.




Mime
View raw message