sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Added three convenience methods: - MatrixSIS.translate(double[]) - MathTransforms.getMatrix(MathTransform, DirectPosition) - GridExtent.append(DimentionNameType, long, long, boolean)
Date Fri, 23 Nov 2018 20:43:20 GMT
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 027eaaa  Added three convenience methods: - MatrixSIS.translate(double[]) - MathTransforms.getMatrix(MathTransform,
DirectPosition) - GridExtent.append(DimentionNameType, long, long, boolean)
027eaaa is described below

commit 027eaaa5871c53bf6d2526cba5bcfcb9d5a80592
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Fri Nov 23 21:42:17 2018 +0100

    Added three convenience methods:
    - MatrixSIS.translate(double[])
    - MathTransforms.getMatrix(MathTransform, DirectPosition)
    - GridExtent.append(DimentionNameType, long, long, boolean)
---
 .../org/apache/sis/coverage/grid/GridExtent.java   | 130 +++++++++++++--------
 .../apache/sis/coverage/grid/GridExtentTest.java   |  41 ++++++-
 .../referencing/operation/matrix/MatrixSIS.java    |  51 +++++++-
 .../operation/transform/LinearInterpolator1D.java  |   2 +-
 .../operation/transform/MathTransforms.java        |  54 +++++++++
 .../operation/matrix/GeneralMatrixTest.java        |  15 ++-
 .../referencing/operation/matrix/Matrix3Test.java  |  13 ++-
 .../operation/matrix/MatrixTestCase.java           |  60 +++++++---
 .../operation/transform/MathTransformsTest.java    |  39 ++++++-
 .../java/org/apache/sis/math/SequenceVector.java   |   2 +
 .../test/java/org/apache/sis/math/VectorTest.java  |   2 +-
 .../java/org/apache/sis/internal/netcdf/Grid.java  |   2 +-
 12 files changed, 337 insertions(+), 74 deletions(-)

diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
index 055b499..b14edd8 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
@@ -122,11 +122,11 @@ public class GridExtent implements Serializable {
     private final DimensionNameType[] types;
 
     /**
-     * Minimum and maximum grid ordinates. The first half contains minimum ordinates (inclusive),
-     * while the last half contains maximum ordinates (<strong>inclusive</strong>).
Note that the
+     * Minimum and maximum grid coordinates. The first half contains minimum coordinates
(inclusive),
+     * while the last half contains maximum coordinates (<strong>inclusive</strong>).
Note that the
      * later is the opposite of Java2D usage but conform to ISO specification.
      */
-    private final long[] ordinates;
+    private final long[] coordinates;
 
     /**
      * Creates a new array of coordinates with the given number of dimensions.
@@ -142,17 +142,17 @@ public class GridExtent implements Serializable {
     }
 
     /**
-     * Checks if ordinate values in the low part are less than or
-     * equal to the corresponding ordinate value in the high part.
+     * Checks if coordinate values in the low part are less than or
+     * equal to the corresponding coordinate value in the high part.
      *
      * @throws IllegalArgumentException if a coordinate value in the low part is
      *         greater than the corresponding coordinate value in the high part.
      */
-    private static void checkCoherence(final long[] ordinates) throws IllegalArgumentException
{
-        final int dimension = ordinates.length >>> 1;
+    private static void checkCoherence(final long[] coordinates) throws IllegalArgumentException
{
+        final int dimension = coordinates.length >>> 1;
         for (int i=0; i<dimension; i++) {
-            final long lower = ordinates[i];
-            final long upper = ordinates[i + dimension];
+            final long lower = coordinates[i];
+            final long upper = coordinates[i + dimension];
             if (lower > upper) {
                 throw new IllegalArgumentException(Resources.format(
                         Resources.Keys.IllegalGridEnvelope_3, i, lower, upper));
@@ -208,7 +208,7 @@ public class GridExtent implements Serializable {
      * @param axisTypes  the axis types, or {@code null} if unspecified.
      */
     private GridExtent(final int dimension, final DimensionNameType[] axisTypes) {
-        ordinates = allocate(dimension);
+        coordinates = allocate(dimension);
         types = validateAxisTypes(axisTypes);
     }
 
@@ -223,9 +223,9 @@ public class GridExtent implements Serializable {
     public GridExtent(final long width, final long height) {
         ArgumentChecks.ensureStrictlyPositive("width",  width);
         ArgumentChecks.ensureStrictlyPositive("height", height);
-        ordinates = new long[4];
-        ordinates[2] = width  - 1;
-        ordinates[3] = height - 1;
+        coordinates = new long[4];
+        coordinates[2] = width  - 1;
+        coordinates[3] = height - 1;
         types = DEFAULT_TYPES;
     }
 
@@ -247,12 +247,12 @@ public class GridExtent implements Serializable {
      * The {@code axisTypes} array shall not contain duplicated elements,
      * but may contain {@code null} elements if the type of some axes are unknown.</p>
      *
-     * @param  axisTypes  the type of each grid axis, or {@code null} if unspecified.
-     * @param  low    the valid minimum grid coordinate (always inclusive), or {@code null}
for all zeros.
-     * @param  high   the valid maximum grid coordinate, inclusive or exclusive depending
on the next argument.
+     * @param  axisTypes       the type of each grid axis, or {@code null} if unspecified.
+     * @param  low             the valid minimum grid coordinates (always inclusive), or
{@code null} for all zeros.
+     * @param  high            the valid maximum grid coordinates, inclusive or exclusive
depending on the next argument.
      * @param  isHighIncluded  {@code true} if the {@code high} values are inclusive (as
in ISO 19123 specification),
-     *         or {@code false} if they are exclusive (as in Java2D usage).
-     *         This argument does not apply to {@code low} values, which are always inclusive.
+     *                         or {@code false} if they are exclusive (as in Java2D usage).
+     *                         This argument does not apply to {@code low} values, which
are always inclusive.
      * @throws IllegalArgumentException if a coordinate value in the low part is
      *         greater than the corresponding coordinate value in the high part.
      *
@@ -268,17 +268,17 @@ public class GridExtent implements Serializable {
         if (axisTypes != null && axisTypes.length != dimension) {
             throw new IllegalArgumentException(Errors.format(Errors.Keys.MismatchedArrayLengths));
         }
-        ordinates = allocate(dimension);
+        coordinates = allocate(dimension);
         if (low != null) {
-            System.arraycopy(low, 0, ordinates, 0, dimension);
+            System.arraycopy(low, 0, coordinates, 0, dimension);
         }
-        System.arraycopy(high, 0, ordinates, dimension, dimension);
+        System.arraycopy(high, 0, coordinates, dimension, dimension);
         if (!isHighIncluded) {
-            for (int i=dimension; i < ordinates.length; i++) {
-                ordinates[i] = Math.decrementExact(ordinates[i]);
+            for (int i=dimension; i < coordinates.length; i++) {
+                coordinates[i] = Math.decrementExact(coordinates[i]);
             }
         }
-        checkCoherence(ordinates);
+        checkCoherence(coordinates);
         types = validateAxisTypes(axisTypes);
     }
 
@@ -297,7 +297,7 @@ public class GridExtent implements Serializable {
      */
     GridExtent(final AbstractEnvelope envelope) {
         final int dimension = envelope.getDimension();
-        ordinates = allocate(dimension);
+        coordinates = allocate(dimension);
         for (int i=0; i<dimension; i++) {
             final double min = envelope.getLower(i);
             final double max = envelope.getUpper(i);
@@ -328,15 +328,15 @@ public class GridExtent implements Serializable {
                         }
                     }
                 }
-                ordinates[i] = lower;
-                ordinates[i + dimension] = upper;
+                coordinates[i] = lower;
+                coordinates[i + dimension] = upper;
             } else {
                 throw new IllegalArgumentException(Resources.format(
                         Resources.Keys.IllegalGridEnvelope_3, i, min, max));
             }
         }
         /*
-         * At this point we finished to compute ordinate values.
+         * At this point we finished to compute coordinate values.
          * Now try to infer dimension types from the CRS axes.
          * This is only for information purpose.
          */
@@ -369,12 +369,12 @@ public class GridExtent implements Serializable {
     protected GridExtent(final GridEnvelope extent) {
         ArgumentChecks.ensureNonNull("extent", extent);
         final int dimension = extent.getDimension();
-        ordinates = allocate(dimension);
+        coordinates = allocate(dimension);
         for (int i=0; i<dimension; i++) {
-            ordinates[i] = extent.getLow(i);
-            ordinates[i + dimension] = extent.getHigh(i);
+            coordinates[i] = extent.getLow(i);
+            coordinates[i + dimension] = extent.getHigh(i);
         }
-        checkCoherence(ordinates);
+        checkCoherence(coordinates);
         types = (extent instanceof GridExtent) ? ((GridExtent) extent).types : null;
     }
 
@@ -400,7 +400,7 @@ public class GridExtent implements Serializable {
      * @return the number of dimensions.
      */
     public final int getDimension() {
-        return ordinates.length >>> 1;
+        return coordinates.length >>> 1;
     }
 
     /**
@@ -413,7 +413,7 @@ public class GridExtent implements Serializable {
      *       before to become public API, in order to use the interface in return type.
      */
     GridCoordinatesView getLow() {
-        return new GridCoordinatesView(ordinates, 0);
+        return new GridCoordinatesView(coordinates, 0);
     }
 
     /**
@@ -426,7 +426,7 @@ public class GridExtent implements Serializable {
      *       before to become public API, in order to use the interface in return type.
      */
     GridCoordinatesView getHigh() {
-        return new GridCoordinatesView(ordinates, getDimension());
+        return new GridCoordinatesView(coordinates, getDimension());
     }
 
     /**
@@ -442,7 +442,7 @@ public class GridExtent implements Serializable {
      */
     public long getLow(final int index) {
         ArgumentChecks.ensureValidIndex(getDimension(), index);
-        return ordinates[index];
+        return coordinates[index];
     }
 
     /**
@@ -459,7 +459,7 @@ public class GridExtent implements Serializable {
     public long getHigh(final int index) {
         final int dimension = getDimension();
         ArgumentChecks.ensureValidIndex(dimension, index);
-        return ordinates[index + dimension];
+        return coordinates[index + dimension];
     }
 
     /**
@@ -478,7 +478,7 @@ public class GridExtent implements Serializable {
     public long getSize(final int index) {
         final int dimension = getDimension();
         ArgumentChecks.ensureValidIndex(dimension, index);
-        return Math.incrementExact(Math.subtractExact(ordinates[dimension + index], ordinates[index]));
+        return Math.incrementExact(Math.subtractExact(coordinates[dimension + index], coordinates[index]));
     }
 
     /**
@@ -493,7 +493,7 @@ public class GridExtent implements Serializable {
         final int dimension = getDimension();
         final GeneralDirectPosition center = new GeneralDirectPosition(dimension);
         for (int i=0; i<dimension; i++) {
-            center.setOrdinate(i, ((double) ordinates[i] + (double) ordinates[i + dimension]
+ 1.0) * 0.5);
+            center.setOrdinate(i, ((double) coordinates[i] + (double) coordinates[i + dimension]
+ 1.0) * 0.5);
         }
         return center;
     }
@@ -540,7 +540,7 @@ public class GridExtent implements Serializable {
         final int dimension = getDimension();
         GeneralEnvelope envelope = new GeneralEnvelope(dimension);
         for (int i=0; i<dimension; i++) {
-            envelope.setRange(i, ordinates[i], ordinates[i + dimension] + 1.0);
+            envelope.setRange(i, coordinates[i], coordinates[i + dimension] + 1.0);
         }
         envelope = Envelopes.transform(cornerToCRS, envelope);
         if (envelope.isEmpty()) try {
@@ -553,7 +553,7 @@ public class GridExtent implements Serializable {
             final boolean isCenter = (gridToCRS != cornerToCRS);
             TransformSeparator separator = null;
             for (int srcDim=0; srcDim < dimension; srcDim++) {
-                if (ordinates[srcDim + dimension] == 0 && ordinates[srcDim] == 0)
{
+                if (coordinates[srcDim + dimension] == 0 && coordinates[srcDim] ==
0) {
                     /*
                      * At this point we found a grid dimension with [0 … 0] range. Only
this specific range is processed because
                      * it is assumed associated to NaN scale factors in the 'gridToCRS' matrix,
since the resolution is computed
@@ -604,6 +604,42 @@ public class GridExtent implements Serializable {
     }
 
     /**
+     * Returns a new grid envelope with the specified dimension added after this grid envelope
dimensions.
+     *
+     * @param  axisType        the type of the grid axis to add, or {@code null} if unspecified.
+     * @param  low             the valid minimum grid coordinate (always inclusive).
+     * @param  high            the valid maximum grid coordinate, inclusive or exclusive
depending on the next argument.
+     * @param  isHighIncluded  {@code true} if the {@code high} value is inclusive (as in
ISO 19123 specification),
+     *                         or {@code false} if it is exclusive (as in Java2D usage).
+     *                         This argument does not apply to {@code low} value, which is
always inclusive.
+     * @return a new grid envelope with the specified dimension added.
+     * @throws IllegalArgumentException if the low coordinate value is greater than the high
coordinate value.
+     */
+    public GridExtent append(final DimensionNameType axisType, final long low, long high,
final boolean isHighIncluded) {
+        if (!isHighIncluded) {
+            high = Math.decrementExact(high);
+        }
+        final int dimension = getDimension();
+        final int newDim    = dimension + 1;
+        DimensionNameType[] axisTypes = null;
+        if (types != null || axisType != null) {
+            if (types != null) {
+                axisTypes = Arrays.copyOf(types, newDim);
+            } else {
+                axisTypes = new DimensionNameType[newDim];
+            }
+            axisTypes[dimension] = axisType;
+        }
+        final GridExtent ex = new GridExtent(newDim, axisTypes);
+        System.arraycopy(coordinates, 0,         ex.coordinates, 0,      dimension);
+        System.arraycopy(coordinates, dimension, ex.coordinates, newDim, dimension);
+        ex.coordinates[dimension]          = low;
+        ex.coordinates[dimension + newDim] = high;
+        checkCoherence(ex.coordinates);
+        return ex;
+    }
+
+    /**
      * Returns a new grid envelope that encompass only some dimensions of this grid envelope.
      * This method copies this grid envelope into a new grid envelope, beginning at dimension
      * {@code lower} and extending to dimension {@code upper-1} inclusive. Thus the dimension
@@ -626,8 +662,8 @@ public class GridExtent implements Serializable {
             axisTypes = Arrays.copyOfRange(axisTypes, lower, upper);
         }
         final GridExtent sub = new GridExtent(newDim, axisTypes);
-        System.arraycopy(ordinates, lower,           sub.ordinates, 0,      newDim);
-        System.arraycopy(ordinates, lower+dimension, sub.ordinates, newDim, newDim);
+        System.arraycopy(coordinates, lower,           sub.coordinates, 0,      newDim);
+        System.arraycopy(coordinates, lower+dimension, sub.coordinates, newDim, newDim);
         return sub;
     }
 
@@ -639,7 +675,7 @@ public class GridExtent implements Serializable {
      */
     @Override
     public int hashCode() {
-        return Arrays.hashCode(ordinates) + Arrays.hashCode(types) ^ (int) serialVersionUID;
+        return Arrays.hashCode(coordinates) + Arrays.hashCode(types) ^ (int) serialVersionUID;
     }
 
     /**
@@ -652,7 +688,7 @@ public class GridExtent implements Serializable {
     public boolean equals(final Object object) {
         if (object != null && object.getClass() == GridExtent.class) {
             final GridExtent other = (GridExtent) object;
-            return Arrays.equals(ordinates, other.ordinates) && Arrays.equals(types,
other.types);
+            return Arrays.equals(coordinates, other.coordinates) && Arrays.equals(types,
other.types);
         }
         return false;
     }
@@ -683,8 +719,8 @@ public class GridExtent implements Serializable {
             if ((types == null) || (name = Types.getCodeTitle(types[i])) == null) {
                 name = vocabulary.getString(Vocabulary.Keys.Dimension_1, i);
             }
-            final long lower = ordinates[i];
-            final long upper = ordinates[i + dimension];
+            final long lower = coordinates[i];
+            final long upper = coordinates[i + dimension];
             table.setCellAlignment(TableAppender.ALIGN_LEFT);
             if (tree) {
                 branch(table, i < dimension - 1);
diff --git a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
index 38f83c7..a1a55f3 100644
--- a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
+++ b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
@@ -36,6 +36,14 @@ import static org.junit.Assert.*;
  */
 public final strictfp class GridExtentTest extends TestCase {
     /**
+     * Verifies the low and high values in the specified dimension of the given extent
+     */
+    private static void assertExtentEquals(final GridExtent extent, final int dimension,
final int low, final int high) {
+        assertEquals("low",  low,  extent.getLow (dimension));
+        assertEquals("high", high, extent.getHigh(dimension));
+    }
+
+    /**
      * Tests the {@link GridExtent#GridExtent(AbstractEnvelope)} constructor.
      */
     @Test
@@ -71,10 +79,35 @@ public final strictfp class GridExtentTest extends TestCase {
     }
 
     /**
-     * Verifies the low and high values in the specified dimension of the given extent
+     * Tests {@link GridExtent#append(DimensionNameType, long, long, boolean)}.
      */
-    private static void assertExtentEquals(final GridExtent extent, final int dimension,
final int low, final int high) {
-        assertEquals("low",  low,  extent.getLow (dimension));
-        assertEquals("high", high, extent.getHigh(dimension));
+    @Test
+    public void testAppend() {
+        GridExtent extent = new GridExtent(new DimensionNameType[] {DimensionNameType.COLUMN,
DimensionNameType.ROW},
+                                           new long[] {100, 200}, new long[] {500, 800},
true);
+        extent = extent.append(DimensionNameType.TIME, 40, 50, false);
+        assertEquals("dimension", 3, extent.getDimension());
+        assertExtentEquals(extent, 0, 100, 500);
+        assertExtentEquals(extent, 1, 200, 800);
+        assertExtentEquals(extent, 2,  40,  49);
+        assertEquals(DimensionNameType.COLUMN, extent.getAxisType(0).get());
+        assertEquals(DimensionNameType.ROW,    extent.getAxisType(1).get());
+        assertEquals(DimensionNameType.TIME,   extent.getAxisType(2).get());
+    }
+
+    /**
+     * Tests {@link GridExtent#subExtent(int, int)}.
+     */
+    @Test
+    public void testSubExtent() {
+        GridExtent extent = new GridExtent(
+                new DimensionNameType[] {DimensionNameType.COLUMN, DimensionNameType.ROW,
DimensionNameType.TIME},
+                new long[] {100, 200, 40}, new long[] {500, 800, 50}, false);
+        extent = extent.subExtent(0, 2);
+        assertEquals("dimension", 2, extent.getDimension());
+        assertExtentEquals(extent, 0, 100, 499);
+        assertExtentEquals(extent, 1, 200, 799);
+        assertEquals(DimensionNameType.COLUMN, extent.getAxisType(0).get());
+        assertEquals(DimensionNameType.ROW,    extent.getAxisType(1).get());
     }
 }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java
index 32000e5..d2f26f4 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java
@@ -55,7 +55,7 @@ import org.apache.sis.util.resources.Errors;
  * </ul>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
+ * @version 1.0
  *
  * @see Matrices
  *
@@ -579,7 +579,7 @@ public abstract class MatrixSIS implements Matrix, LenientComparable,
Cloneable,
     public double[] multiply(final double[] vector) {
         final int numCol = getNumCol();
         if (vector.length != numCol) {
-            throw new MismatchedMatrixSizeException(Errors.format(Errors.Keys.UnexpectedArrayLength_2,
 numCol, vector.length));
+            throw new MismatchedMatrixSizeException(Errors.format(Errors.Keys.UnexpectedArrayLength_2,
numCol, vector.length));
         }
         final double[] target = new double[getNumRow()];
         final DoubleDouble ele = new DoubleDouble();
@@ -597,6 +597,53 @@ public abstract class MatrixSIS implements Matrix, LenientComparable,
Cloneable,
     }
 
     /**
+     * Multiplies this matrix by a translation matrix. Invoking this method is equivalent
to invoking
+     * <code>{@linkplain #multiply(Matrix) multiply}(T)</code> where <var>T</var>
is a matrix like
+     * below (size varies):
+     *
+     * {@preformat math
+     *        ┌                    ┐
+     *        │ 1  0  0  vector[0] │
+     *    T = │ 0  1  0  vector[1] │
+     *        │ 0  0  1  vector[2] │
+     *        │ 0  0  0  vector[3] │
+     *        └                    ┘
+     * }
+     *
+     * The length of the given vector must be equals to the number of columns in this matrix.
+     * The last vector element is 1 for an affine transform, but other values are allowed.
+     * This matrix will be modified in-place.
+     *
+     * <p>If this matrix is used for coordinate conversions, then converting a position
with
+     * the resulting matrix is equivalent to first translating the point by the given vector,
+     * then applying the conversion represented by the original matrix.</p>
+     *
+     * @param  vector  a vector representing a translation to be applied before this matrix.
+     *
+     * @since 1.0
+     *
+     * @see AffineTransform#translate(double, double)
+     */
+    public void translate(final double[] vector) {
+        final int numCol = getNumCol();
+        if (vector.length != numCol) {
+            throw new MismatchedMatrixSizeException(Errors.format(Errors.Keys.UnexpectedArrayLength_2,
numCol, vector.length));
+        }
+        final int numRow = getNumRow();
+        final DoubleDouble s = new DoubleDouble();
+        final DoubleDouble t = new DoubleDouble();
+        for (int j=0; j<numRow; j++) {
+            s.clear();
+            for (int i=0; i<numCol; i++) {
+                get(j, i, t);
+                t.multiply(vector[i]);
+                s.add(t);
+            }
+            set(j, numCol-1, s);
+        }
+    }
+
+    /**
      * Returns the value of <var>U</var> which solves {@code this} × <var>U</var>
= {@code matrix}.
      * This is equivalent to first computing the inverse of {@code this}, then multiplying
the result
      * by the given matrix.
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1D.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1D.java
index ae8421c..2e310b4 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1D.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1D.java
@@ -124,7 +124,7 @@ final class LinearInterpolator1D extends AbstractMathTransform1D implements
Seri
                 return LinearTransform1D.create(slope, offset);
             }
             value = values[i];
-        } while (Numerics.epsilonEqual(value, offset + slope*i,
+        } while (Numerics.epsilonEqual(value, offset + slope*i,     // TODO: use Math.fma
with JDK9.
                         Math.max(Math.abs(value), as) * Numerics.COMPARISON_THRESHOLD));
         /*
          * If the values are in decreasing order, reverse their sign so we get increasing
order.
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
index 5fb3123..f7660fe 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
@@ -22,6 +22,7 @@ import java.util.Collections;
 import java.awt.geom.AffineTransform;
 import org.opengis.util.FactoryException;
 import org.opengis.geometry.Envelope;
+import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.MismatchedDimensionException;
 import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.MathTransform;
@@ -33,8 +34,10 @@ import org.apache.sis.internal.referencing.DirectPositionView;
 import org.apache.sis.internal.referencing.ExtendedPrecisionMatrix;
 import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
 import org.apache.sis.referencing.operation.matrix.AffineTransforms2D;
+import org.apache.sis.referencing.operation.matrix.MatrixSIS;
 import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.Static;
 
 
@@ -502,6 +505,57 @@ public final class MathTransforms extends Static {
     }
 
     /**
+     * Returns the coefficients of an affine transform in the vicinity of the given position.
+     * If the given transform is linear, then this method produces a result identical to
{@link #getMatrix(MathTransform)}.
+     * Otherwise the returned matrix can be used for {@linkplain #linear(Matrix) building
a linear transform} which can be
+     * used as an approximation of the given transform for short distances around the given
position.
+     *
+     * @param  transform  the transform to approximate by an affine transform, or {@code
null}.
+     * @param  position   position around which to get the coefficient of an affine transform
approximation.
+     * @return the matrix of the given transform around the given position, or {@code null}
if the given transform was null.
+     * @throws TransformException if an error occurred while transforming the given position
or computing the derivative at
+     *         that position.
+     *
+     * @since 1.0
+     */
+    public static Matrix getMatrix(final MathTransform transform, final DirectPosition position)
throws TransformException {
+        if (transform == null) {
+            return null;
+        }
+        final int srcDim = transform.getSourceDimensions();
+        ArgumentChecks.ensureDimensionMatches("position", srcDim, position);
+        final Matrix affine = getMatrix(transform);
+        if (affine != null) {
+            return affine;
+        }
+        final int tgtDim = transform.getTargetDimensions();
+        double[] pts = new double[Math.max(srcDim + 1, tgtDim)];
+        for (int i=0; i<srcDim; i++) {
+            pts[i] = position.getOrdinate(i);
+        }
+        final Matrix d = derivativeAndTransform(transform, pts, 0, pts, 0);
+        final MatrixSIS a = Matrices.createZero(tgtDim + 1, srcDim + 1);
+        for (int j=0; j<tgtDim; j++) {
+            for (int i=0; i<srcDim; i++) {
+                a.setElement(j, i, d.getElement(j, i));
+            }
+            a.setElement(j, srcDim, pts[j]);
+            pts[j] = -position.getOrdinate(j);                  // To be used by a.translate(pts)
later.
+        }
+        a.setElement(tgtDim, srcDim, 1);
+        /*
+         * At this point, the translation column in the matrix is set as if the coordinate
system origin
+         * was at the given position. We want to keep the original coordinate system origin.
We do that
+         * be applying a translation in the opposite direction before the affine transform.
Translation
+         * terms were opportunistically set in the previous loop.
+         */
+        pts = ArraysExt.resize(pts, srcDim + 1);
+        pts[srcDim] = 1;
+        a.translate(pts);
+        return a;
+    }
+
+    /**
      * A buckle method for calculating derivative and coordinate transformation in a single
step.
      * The transform result is stored in the given destination array, and the derivative
matrix
      * is returned. Invoking this method is equivalent to the following code, except that
it may
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/GeneralMatrixTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/GeneralMatrixTest.java
index 7a62467..bb4d88f 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/GeneralMatrixTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/GeneralMatrixTest.java
@@ -27,7 +27,7 @@ import static org.junit.Assert.*;
  * This class inherits all tests defined in {@link MatrixTestCase}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.4
  * @module
  */
@@ -129,4 +129,17 @@ public final strictfp class GeneralMatrixTest extends MatrixTestCase
{
         testMultiplyVector(new GeneralMatrix(3, 3, true, 1));    // Double precision
         testMultiplyVector(new GeneralMatrix(3, 3, true, 2));    // Double-double precision
     }
+
+    /**
+     * Tests {@link MatrixSIS#translate(double[])}
+     * using {@link java.awt.geom.AffineTransform} as a reference implementation.
+     *
+     * @since 1.0
+     */
+    @Test
+    public void testTranslateVector() {
+        testTranslateVector(new GeneralMatrix(3, 3, true, 1));    // Double precision
+//      testTranslateVector(new GeneralMatrix(3, 3, true, 2));    // Double-double precision
+        // TODO: revisit commented-out test after using Math.fma with JDK9.
+    }
 }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java
index cf21b57..cb5b336 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java
@@ -28,7 +28,7 @@ import static org.apache.sis.referencing.operation.matrix.Matrix3.SIZE;
  * This class inherits all tests defined in {@link MatrixTestCase}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.4
  * @module
  */
@@ -133,4 +133,15 @@ public final strictfp class Matrix3Test extends MatrixTestCase {
     public void testMultiplyVector() {
         testMultiplyVector(new Matrix3());
     }
+
+    /**
+     * Tests {@link MatrixSIS#translate(double[])}
+     * using {@link java.awt.geom.AffineTransform} as a reference implementation.
+     *
+     * @since 1.0
+     */
+    @Test
+    public void testTranslateVector() {
+        testTranslateVector(new Matrix3());
+    }
 }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java
index 5ca2f0a..d2a270c 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java
@@ -45,7 +45,7 @@ import static org.apache.sis.test.Assert.*;
  * <p>This class uses <a href="http://math.nist.gov/javanumerics/jama">JAMA</a>
as the reference implementation.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.4
  * @module
  */
@@ -497,12 +497,50 @@ public abstract strictfp class MatrixTestCase extends TestCase {
     }
 
     /**
-     * Tests {@link MatrixSIS#multiply(double[])} using {@link AffineTransform}
-     * as a reference implementation. This test can be run only with matrices of size 3×3.
-     * Consequently it is sub-classes responsibility to add a {@code testMultiplyVector()}
-     * method which invoke this method.
+     * Tests {@link MatrixSIS#translate(double[])} using {@link AffineTransform} as a reference
implementation.
+     * This test can be run only with matrices of size 3×3. Consequently it is sub-classes
responsibility to add
+     * a {@code testTranslateVector()} method which invoke this method.
      *
-     * @param  matrix  the matrix of size 3×3 to test.
+     * @param  matrix  an initially empty matrix of size 3×3 to test.
+     *
+     * @since 1.0
+     */
+    final void testTranslateVector(final MatrixSIS matrix) {
+        initialize(-1691066807752485433L);
+        final AffineTransform at = new AffineTransform();
+        final double vector[] = new double[] {0, 0, 1};
+        for (int n=0; n<NUMBER_OF_REPETITIONS; n++) {
+            if ((n % 10) == 0) {
+                setRandomValues(at, matrix);
+            }
+            at.translate(vector[0] = random.nextDouble() * 50 - 25,
+                         vector[1] = random.nextDouble() * 50 - 25);
+            matrix.translate(vector);
+            assertMatrixEquals("translate", AffineTransforms2D.toMatrix(at), matrix, TOLERANCE);
+        }
+    }
+
+    /**
+     * Sets random values in the given affine transform and a copy of those values in the
given matrix.
+     */
+    private void setRandomValues(final AffineTransform at, final MatrixSIS matrix) {
+        at.setToRotation(random.nextDouble() * StrictMath.PI);
+        at.scale(nextNonZeroRandom(), nextNonZeroRandom());
+        at.translate(random.nextDouble() * 100 - 50,
+                     random.nextDouble() * 100 - 50);
+        matrix.setElements(new double[] {
+            at.getScaleX(), at.getShearX(), at.getTranslateX(),
+            at.getShearY(), at.getScaleY(), at.getTranslateY(),
+                         0,              0,                  1
+        });
+    }
+
+    /**
+     * Tests {@link MatrixSIS#multiply(double[])} using {@link AffineTransform} as a reference
implementation.
+     * This test can be run only with matrices of size 3×3. Consequently it is sub-classes
responsibility to add
+     * a {@code testMultiplyVector()} method which invoke this method.
+     *
+     * @param  matrix  an initially empty matrix of size 3×3 to test.
      *
      * @since 0.8
      */
@@ -512,15 +550,7 @@ public abstract strictfp class MatrixTestCase extends TestCase {
         final double vector[] = new double[3];
         for (int n=0; n<NUMBER_OF_REPETITIONS; n++) {
             if ((n % 10) == 0) {
-                at.setToRotation(random.nextDouble() * StrictMath.PI);
-                at.scale(nextNonZeroRandom(), nextNonZeroRandom());
-                at.translate(random.nextDouble() * 100 - 50,
-                             random.nextDouble() * 100 - 50);
-                matrix.setElements(new double[] {
-                    at.getScaleX(), at.getShearX(), at.getTranslateX(),
-                    at.getShearY(), at.getScaleY(), at.getTranslateY(),
-                                 0,              0,                  1
-                });
+                setRandomValues(at, matrix);
             }
             vector[0] = random.nextDouble() * 50 - 25;
             vector[1] = random.nextDouble() * 50 - 25;
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformsTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformsTest.java
index 9a3c6cf..6866535 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformsTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformsTest.java
@@ -17,11 +17,15 @@
 package org.apache.sis.referencing.operation.transform;
 
 import java.util.List;
+import org.opengis.geometry.DirectPosition;
+import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.referencing.operation.matrix.Matrix2;
 import org.apache.sis.referencing.operation.matrix.Matrix3;
 import org.apache.sis.referencing.operation.matrix.Matrix4;
+import org.apache.sis.geometry.GeneralDirectPosition;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
@@ -32,7 +36,7 @@ import static org.opengis.test.Assert.*;
  * Tests {@link MathTransforms}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.6
+ * @version 1.0
  * @since   0.5
  * @module
  */
@@ -120,4 +124,37 @@ public final strictfp class MathTransformsTest extends TestCase {
             0,  0,  0,  0,  0,  0,  1
         }), MathTransforms.getMatrix(r), STRICT);
     }
+
+    /**
+     * Tests {@link MathTransforms#getMatrix(MathTransform, DirectPosition)}.
+     *
+     * @throws TransformException if an error occurred while computing the derivative.
+     */
+    @Test
+    public void testGetMatrix() throws TransformException {
+        // Prepare a transform with a non-linear component.
+        MathTransform tr = MathTransforms.interpolate(null, new double[] {2, 6, 14, 15});
+        tr = MathTransforms.passThrough(1, tr, 1);
+        tr = MathTransforms.concatenate(tr, MathTransforms.linear(new Matrix4(
+            5,  0,  0,  9,
+            0,  1,  0,  0,
+            0,  0,  2, -7,
+            0,  0,  0,  1)));
+
+        // In the following position, only 1.5 matter because only dimension 1 is non-linear.
+        final DirectPosition pos = new GeneralDirectPosition(3, 1.5, 6);
+        final Matrix affine = MathTransforms.getMatrix(tr, pos);
+        assertMatrixEquals("Affine approximation", new Matrix4(
+            5,  0,  0,  9,
+            0,  8,  0, -2,
+            0,  0,  2, -7,
+            0,  0,  0,  1), affine, STRICT);
+        /*
+         * Transformation using above approximation shall produce the same result than the
original
+         * transform if we do the comparison at the position where the approximation has
been computed.
+         */
+        DirectPosition expected = tr.transform(pos, null);
+        DirectPosition actual = MathTransforms.linear(affine).transform(pos, null);
+        assertEquals(expected, actual);
+    }
 }
diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java b/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java
index 8d25e48..9613a77 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java
@@ -184,6 +184,7 @@ abstract class SequenceVector extends Vector implements Serializable {
         @Override public double doubleValue(final int index) {
             ArgumentChecks.ensureValidIndex(length, index);
             return first + increment*index;
+            // TODO: use Math.fma with JDK9.
         }
 
         /** Computes the value at the given index. */
@@ -209,6 +210,7 @@ abstract class SequenceVector extends Vector implements Serializable {
         /** Computes the minimal and maximal values in this vector. */
         @SuppressWarnings({"unchecked","rawtypes"})
         @Override public NumberRange<?> range() {
+            // TODO: use Math.fma with JDK9.
             double min = first;
             double max = first + increment * (length - 1);
             if (max < min) {
diff --git a/core/sis-utility/src/test/java/org/apache/sis/math/VectorTest.java b/core/sis-utility/src/test/java/org/apache/sis/math/VectorTest.java
index ab022fd..590b977 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/math/VectorTest.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/math/VectorTest.java
@@ -252,7 +252,7 @@ public final strictfp class VectorTest extends TestCase {
     }
 
     /**
-     * Tests {@link Vector#repetitions()}.
+     * Tests {@link Vector#repetitions(int...)}.
      */
     @Test
     public void testRepetitions() {
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
index 48ac268..129fa29 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
@@ -234,7 +234,7 @@ public abstract class Grid extends NamedElement {
                 }
             }
         }
-        return new GridExtent(names, new long[high.length], high, false);
+        return new GridExtent(names, null, high, false);
     }
 
     /**


Mime
View raw message