sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1788983 [2/2] - in /sis/trunk: ./ core/sis-feature/src/main/java/org/apache/sis/feature/ core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/ core/sis-referencing/src/main/java/org/apache/sis/internal/ref...
Date Mon, 27 Mar 2017 16:38:01 GMT
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilderTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilderTest.java?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilderTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilderTest.java [UTF-8] Mon Mar 27 16:38:00 2017
@@ -16,8 +16,11 @@
  */
 package org.apache.sis.referencing.operation.builder;
 
+import java.util.Map;
+import java.util.HashMap;
 import java.util.Random;
 import java.awt.geom.AffineTransform;
+import org.opengis.util.FactoryException;
 import org.opengis.referencing.operation.Matrix;
 import org.apache.sis.geometry.DirectPosition1D;
 import org.apache.sis.geometry.DirectPosition2D;
@@ -34,23 +37,28 @@ import static org.junit.Assert.*;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.5
+ * @version 0.8
  * @module
  */
 public final strictfp class LinearTransformBuilderTest extends TestCase {
     /**
      * Tests a very simple case where an exact answer is expected.
+     *
+     * @throws FactoryException if the transform can not be created.
      */
     @Test
-    public void testMinimalist1D() {
+    public void testMinimalist1D() throws FactoryException {
         final LinearTransformBuilder builder = new LinearTransformBuilder();
-        builder.setSourcePoints(
-                new DirectPosition1D(1),
-                new DirectPosition1D(2));
-        builder.setTargetPoints(
-                new DirectPosition1D(1),
-                new DirectPosition1D(3));
-        final Matrix m = builder.create().getMatrix();
+        final Map<DirectPosition1D,DirectPosition1D> pos = new HashMap<>(4);
+        assertNull(pos.put(new DirectPosition1D(1), new DirectPosition1D(1)));
+        assertNull(pos.put(new DirectPosition1D(2), new DirectPosition1D(3)));
+        builder.setControlPoints(pos);
+
+        assertArrayEquals(new double[] {1}, builder.getControlPoint(new int[] {1}), STRICT);
+        assertArrayEquals(new double[] {3}, builder.getControlPoint(new int[] {2}), STRICT);
+        assertNull(                         builder.getControlPoint(new int[] {3}));
+
+        final Matrix m = builder.create(null).getMatrix();
         assertEquals("m₀₀",  2, m.getElement(0, 0), STRICT);
         assertEquals("m₀₁", -1, m.getElement(0, 1), STRICT);
         assertArrayEquals("correlation", new double[] {1}, builder.correlation(), STRICT);
@@ -61,9 +69,11 @@ public final strictfp class LinearTransf
      * Tolerance threshold is set to zero because the math transform has been built from exactly 3 points,
      * in which case we expect an exact solution without rounding errors at the scale of the {@code double}
      * type. This is possible because SIS implementation uses double-double arithmetic.
+     *
+     * @throws FactoryException if the transform can not be created.
      */
     @Test
-    public void testMinimalist2D() {
+    public void testMinimalist2D() throws FactoryException {
         final LinearTransformBuilder builder = new LinearTransformBuilder();
         builder.setSourcePoints(
                 new DirectPosition2D(1, 1),
@@ -73,7 +83,13 @@ public final strictfp class LinearTransf
                 new DirectPosition2D(3, 2),
                 new DirectPosition2D(3, 5),
                 new DirectPosition2D(5, 5));
-        final Matrix m = builder.create().getMatrix();
+
+        assertArrayEquals(new double[] {3, 2}, builder.getControlPoint(new int[] {1, 1}), STRICT);
+        assertArrayEquals(new double[] {3, 5}, builder.getControlPoint(new int[] {1, 2}), STRICT);
+        assertArrayEquals(new double[] {5, 5}, builder.getControlPoint(new int[] {2, 2}), STRICT);
+        assertNull(                            builder.getControlPoint(new int[] {2, 1}));
+
+        final Matrix m = builder.create(null).getMatrix();
 
         // First row (x)
         assertEquals("m₀₀",  2, m.getElement(0, 0), STRICT);
@@ -89,11 +105,96 @@ public final strictfp class LinearTransf
     }
 
     /**
+     * Tests a two-dimensional case where sources coordinates are explicitely given.
+     *
+     * @throws FactoryException if the transform can not be created.
+     *
+     * @since 0.8
+     */
+    @Test
+    public void testExplicitSource2D() throws FactoryException {
+        testSetAllPoints(new LinearTransformBuilder());
+        testSetEachPoint(new LinearTransformBuilder());
+    }
+
+    /**
+     * Same test than {@link #testExplicitSource2D()}, but using the
+     * {@link LinearTransformBuilder#LinearTransformBuilder(int...)} constructor.
+     *
+     * @throws FactoryException if the transform can not be created.
+     *
+     * @since 0.8
+     */
+    @Test
+    @DependsOnMethod("testExplicitSource2D")
+    public void testImplicitSource2D() throws FactoryException {
+        testSetAllPoints(new LinearTransformBuilder(2, 3));
+        testSetEachPoint(new LinearTransformBuilder(2, 3));
+    }
+
+    /**
+     * Execution of {@link #testExplicitSource2D()} and {@link #testImplicitSource2D()}
+     * where all control points are specified by a map.
+     */
+    private void testSetAllPoints(final LinearTransformBuilder builder) throws FactoryException {
+        final Map<DirectPosition2D,DirectPosition2D> pos = new HashMap<>(8);
+        assertNull(pos.put(new DirectPosition2D(0, 0), new DirectPosition2D(3, 9)));
+        assertNull(pos.put(new DirectPosition2D(0, 1), new DirectPosition2D(4, 7)));
+        assertNull(pos.put(new DirectPosition2D(0, 2), new DirectPosition2D(6, 6)));
+        assertNull(pos.put(new DirectPosition2D(1, 0), new DirectPosition2D(4, 8)));
+        assertNull(pos.put(new DirectPosition2D(1, 1), new DirectPosition2D(5, 4)));
+        assertNull(pos.put(new DirectPosition2D(1, 2), new DirectPosition2D(8, 2)));
+        builder.setControlPoints(pos);
+        verify(builder);
+    }
+
+    /**
+     * Execution of {@link #testExplicitSource2D()} and {@link #testImplicitSource2D()}
+     * where all control points are specified one-by-one.
+     */
+    private void testSetEachPoint(final LinearTransformBuilder builder) throws FactoryException {
+        builder.setControlPoint(new int[] {0, 0}, new double[] {3, 9});
+        builder.setControlPoint(new int[] {0, 1}, new double[] {4, 7});
+        builder.setControlPoint(new int[] {0, 2}, new double[] {6, 6});
+        builder.setControlPoint(new int[] {1, 0}, new double[] {4, 8});
+        builder.setControlPoint(new int[] {1, 1}, new double[] {5, 4});
+        builder.setControlPoint(new int[] {1, 2}, new double[] {8, 2});
+        verify(builder);
+    }
+
+    /**
+     * Verifies the transform created by {@link #testExplicitSource2D()} and {@link #testImplicitSource2D()}.
+     */
+    private void verify(final LinearTransformBuilder builder) throws FactoryException {
+        final Matrix m = builder.create(null).getMatrix();
+
+        assertArrayEquals(new double[] {3, 9}, builder.getControlPoint(new int[] {0, 0}), STRICT);
+        assertArrayEquals(new double[] {4, 7}, builder.getControlPoint(new int[] {0, 1}), STRICT);
+        assertArrayEquals(new double[] {6, 6}, builder.getControlPoint(new int[] {0, 2}), STRICT);
+        assertArrayEquals(new double[] {4, 8}, builder.getControlPoint(new int[] {1, 0}), STRICT);
+        assertArrayEquals(new double[] {5, 4}, builder.getControlPoint(new int[] {1, 1}), STRICT);
+        assertArrayEquals(new double[] {8, 2}, builder.getControlPoint(new int[] {1, 2}), STRICT);
+        /*
+         * Expect STRICT results because Apache SIS uses double-double arithmetic.
+         */
+        assertEquals("m₀₀",  16 / 12d, m.getElement(0, 0), STRICT);        // First row (x)
+        assertEquals("m₀₁",  21 / 12d, m.getElement(0, 1), STRICT);
+        assertEquals("m₀₂",  31 / 12d, m.getElement(0, 2), STRICT);
+        assertEquals("m₁₀", -32 / 12d, m.getElement(1, 0), STRICT);        // Second row (y)
+        assertEquals("m₁₁", -27 / 12d, m.getElement(1, 1), STRICT);
+        assertEquals("m₁₂", 115 / 12d, m.getElement(1, 2), STRICT);
+
+        assertArrayEquals("correlation", new double[] {0.9656, 0.9536}, builder.correlation(), 0.0001);
+    }
+
+    /**
      * Tests with a random number of points with an exact solution expected.
+     *
+     * @throws FactoryException if the transform can not be created.
      */
     @Test
     @DependsOnMethod("testMinimalist1D")
-    public void testExact1D() {
+    public void testExact1D() throws FactoryException {
         final Random rd = TestUtilities.createRandomNumberGenerator(-6080923837183751016L);
         for (int i=0; i<10; i++) {
             test1D(rd, rd.nextInt(900) + 100, false, 1E-14, 1E-12);
@@ -106,10 +207,12 @@ public final strictfp class LinearTransf
      * <p><b>Note:</b> this test can pass with a random seed most of the time. But we fix the seed anyway
      * because there is always a small probability that truly random points are all colinear, in which case
      * the test would fail. Even if the probability is low, we do not take the risk of random build failures.</p>
+     *
+     * @throws FactoryException if the transform can not be created.
      */
     @Test
     @DependsOnMethod("testMinimalist2D")
-    public void testExact2D() {
+    public void testExact2D() throws FactoryException {
         final Random rd = TestUtilities.createRandomNumberGenerator(41632405806929L);
         for (int i=0; i<10; i++) {
             test2D(rd, rd.nextInt(900) + 100, false, 1E-14, 1E-12);
@@ -118,10 +221,12 @@ public final strictfp class LinearTransf
 
     /**
      * Tests with a random number of points and a random errors in target points.
+     *
+     * @throws FactoryException if the transform can not be created.
      */
     @Test
     @DependsOnMethod("testExact1D")
-    public void testNonExact1D() {
+    public void testNonExact1D() throws FactoryException {
         final Random rd = TestUtilities.createRandomNumberGenerator(8819436190826166876L);
         for (int i=0; i<4; i++) {
             test1D(rd, rd.nextInt(900) + 100, true, 0.02, 0.5);
@@ -136,10 +241,12 @@ public final strictfp class LinearTransf
      * of errors are in the same directions (thus introducing a larger bias than expected), in which case
      * the test would fail. Even if the probability is low, we do not take the risk of such random build
      * failures.</p>
+     *
+     * @throws FactoryException if the transform can not be created.
      */
     @Test
     @DependsOnMethod("testExact2D")
-    public void testNonExact2D() {
+    public void testNonExact2D() throws FactoryException {
         final Random rd = TestUtilities.createRandomNumberGenerator(270575025643864L);
         for (int i=0; i<4; i++) {
             test2D(rd, rd.nextInt(900) + 100, true, 0.02, 0.5);
@@ -155,28 +262,25 @@ public final strictfp class LinearTransf
      * @param  scaleTolerance  tolerance threshold for floating point comparisons.
      */
     private static void test1D(final Random rd, final int numPts, final boolean addErrors,
-            final double scaleTolerance, final double translationTolerance)
+            final double scaleTolerance, final double translationTolerance) throws FactoryException
     {
         final double scale  = rd.nextDouble() * 30 - 12;
         final double offset = rd.nextDouble() * 10 - 4;
-        final DirectPosition1D[] sources = new DirectPosition1D[numPts];
-        final DirectPosition1D[] targets = new DirectPosition1D[numPts];
+        final Map<DirectPosition1D,DirectPosition1D> pos = new HashMap<>(numPts);
         for (int i=0; i<numPts; i++) {
             final DirectPosition1D src = new DirectPosition1D(rd.nextDouble() * 100 - 50);
             final DirectPosition1D tgt = new DirectPosition1D(src.ordinate * scale + offset);
             if (addErrors) {
                 tgt.ordinate += rd.nextDouble() * 10 - 5;
             }
-            sources[i] = src;
-            targets[i] = tgt;
+            assertNull(pos.put(src, tgt));
         }
         /*
          * Create the fitted transform to test.
          */
         final LinearTransformBuilder builder = new LinearTransformBuilder();
-        builder.setSourcePoints(sources);
-        builder.setTargetPoints(targets);
-        final Matrix m = builder.create().getMatrix();
+        builder.setControlPoints(pos);
+        final Matrix m = builder.create(null).getMatrix();
         assertEquals("m₀₀", scale,  m.getElement(0, 0), scaleTolerance);
         assertEquals("m₀₁", offset, m.getElement(0, 1), translationTolerance);
         assertEquals("correlation", 1, StrictMath.abs(builder.correlation()[0]), scaleTolerance);
@@ -191,7 +295,7 @@ public final strictfp class LinearTransf
      * @param  scaleTolerance  tolerance threshold for floating point comparisons.
      */
     private static void test2D(final Random rd, final int numPts, final boolean addErrors,
-            final double scaleTolerance, final double translationTolerance)
+            final double scaleTolerance, final double translationTolerance) throws FactoryException
     {
         /*
          * Create an AffineTransform to use as the reference implementation.
@@ -200,8 +304,7 @@ public final strictfp class LinearTransf
                 rd.nextDouble() * (2 * StrictMath.PI),  // Rotation angle
                 rd.nextDouble() * 30 - 12,              // Center X
                 rd.nextDouble() * 10 - 8);              // Center Y
-        final DirectPosition2D[] sources = new DirectPosition2D[numPts];
-        final DirectPosition2D[] targets = new DirectPosition2D[numPts];
+        final Map<DirectPosition2D,DirectPosition2D> pos = new HashMap<>(numPts);
         for (int i=0; i<numPts; i++) {
             final DirectPosition2D src = new DirectPosition2D(rd.nextDouble() * 100 - 50, rd.nextDouble() * 200 - 75);
             final DirectPosition2D tgt = new DirectPosition2D();
@@ -210,16 +313,14 @@ public final strictfp class LinearTransf
                 tgt.x += rd.nextDouble() * 10 - 5;
                 tgt.y += rd.nextDouble() * 10 - 5;
             }
-            sources[i] = src;
-            targets[i] = tgt;
+            assertNull(pos.put(src, tgt));
         }
         /*
          * Create the fitted transform to test.
          */
         final LinearTransformBuilder builder = new LinearTransformBuilder();
-        builder.setSourcePoints(sources);
-        builder.setTargetPoints(targets);
-        final Matrix m = builder.create().getMatrix();
+        builder.setControlPoints(pos);
+        final Matrix m = builder.create(null).getMatrix();
         /*
          * Compare the coefficients with the reference implementation.
          */

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/GeneralMatrixTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/GeneralMatrixTest.java?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/GeneralMatrixTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/GeneralMatrixTest.java [UTF-8] Mon Mar 27 16:38:00 2017
@@ -28,7 +28,7 @@ import static org.junit.Assert.*;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.4
- * @version 0.6
+ * @version 0.8
  * @module
  */
 public final strictfp class GeneralMatrixTest extends MatrixTestCase {
@@ -117,4 +117,15 @@ public final strictfp class GeneralMatri
         testConvertAfter(new GeneralMatrix(3, 3, true, 1));    // Double precision
         testConvertAfter(new GeneralMatrix(3, 3, true, 2));    // Double-double precision
     }
+
+    /**
+     * Tests {@link MatrixSIS#multiply(double[])} using {@link AffineTranform} as a reference implementation.
+     *
+     * @since 0.8
+     */
+    @Test
+    public void testMultiplyVector() {
+        testMultiplyVector(new GeneralMatrix(3, 3, true, 1));    // Double precision
+        testMultiplyVector(new GeneralMatrix(3, 3, true, 2));    // Double-double precision
+    }
 }

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java [UTF-8] Mon Mar 27 16:38:00 2017
@@ -29,7 +29,7 @@ import static org.apache.sis.referencing
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.4
- * @version 0.6
+ * @version 0.8
  * @module
  */
 @DependsOn(SolverTest.class)
@@ -122,4 +122,14 @@ public final strictfp class Matrix3Test
     public void testConvertAfter() {
         testConvertAfter(new Matrix3());
     }
+
+    /**
+     * Tests {@link MatrixSIS#multiply(double[])} using {@link AffineTranform} as a reference implementation.
+     *
+     * @since 0.8
+     */
+    @Test
+    public void testMultiplyVector() {
+        testMultiplyVector(new Matrix3());
+    }
 }

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java [UTF-8] Mon Mar 27 16:38:00 2017
@@ -46,7 +46,7 @@ import static org.apache.sis.test.Assert
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.4
- * @version 0.6
+ * @version 0.8
  * @module
  */
 public abstract strictfp class MatrixTestCase extends TestCase {
@@ -269,7 +269,7 @@ public abstract strictfp class MatrixTes
          * End of initialization - now perform the actual test.
          */
         assertEqualsJAMA(reference, matrix, STRICT);
-        for (int k=0; k<50; k++) {
+        for (int k=0; k<NUMBER_OF_REPETITIONS; k++) {
             final int    j = random.nextInt(numRow);
             final int    i = random.nextInt(numCol);
             final double e = random.nextDouble() * 100;
@@ -410,7 +410,7 @@ public abstract strictfp class MatrixTes
             matrix.setElement(0, 1, at.getShearX());
             matrix.setElement(1, 0, at.getShearY());
         }
-        for (int i=0; i<100; i++) {
+        for (int i=0; i<NUMBER_OF_REPETITIONS; i++) {
             /*
              * 1) For the first  30 iterations, test the result of applying only a scale.
              * 2) For the next   30 iterations, test the result of applying only a translation.
@@ -476,7 +476,7 @@ public abstract strictfp class MatrixTes
         final AffineTransform at = AffineTransform.getShearInstance(nextNonZeroRandom(), nextNonZeroRandom());
         matrix.setElement(0, 1, at.getShearX());
         matrix.setElement(1, 0, at.getShearY());
-        for (int i=0; i<30; i++) {
+        for (int i=0; i<NUMBER_OF_REPETITIONS; i++) {
             final Number scale  = nextNonZeroRandom();
             final Number offset = nextNonZeroRandom();
             final int tgtDim = (i & 1);
@@ -496,6 +496,41 @@ public abstract strictfp class MatrixTes
         }
     }
 
+    /**
+     * Tests {@link MatrixSIS#multiply(double[])} using {@link AffineTranform} 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  the matrix of size 3×3 to test.
+     *
+     * @since 0.8
+     */
+    final void testMultiplyVector(final MatrixSIS matrix) {
+        initialize(8433903323905121506L);
+        final AffineTransform at = new AffineTransform();
+        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
+                });
+            }
+            vector[0] = random.nextDouble() * 50 - 25;
+            vector[1] = random.nextDouble() * 50 - 25;
+            vector[2] = 1;
+            final double[] result = matrix.multiply(vector);        // The result to verify.
+            at.transform(vector, 0, vector, 0, 1);                  // The expected result.
+            assertEquals("x", vector[0], result[0], TOLERANCE);
+            assertEquals("y", vector[1], result[1], TOLERANCE);
+        }
+    }
+
     /**
      * Tests {@link MatrixSIS#multiply(Matrix)}.
      */

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] Mon Mar 27 16:38:00 2017
@@ -208,6 +208,7 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.operation.CoordinateOperationFinderTest.class,
     org.apache.sis.referencing.operation.DefaultCoordinateOperationFactoryTest.class,
     org.apache.sis.referencing.operation.builder.LinearTransformBuilderTest.class,
+    org.apache.sis.referencing.operation.builder.LocalizationGridBuilderTest.class,
 
     // Geometry
     org.apache.sis.geometry.AbstractDirectPositionTest.class,

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java [UTF-8] Mon Mar 27 16:38:00 2017
@@ -213,7 +213,7 @@ abstract class ArrayVector<E extends Num
     /**
      * A vector backed by an array of type {@code double[]}.
      */
-    private static final class Doubles extends ArrayVector<Double> {
+    static final class Doubles extends ArrayVector<Double> {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = -2900375382498345812L;
 

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/CompoundDirectPositions.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/CompoundDirectPositions.java?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/CompoundDirectPositions.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/CompoundDirectPositions.java [UTF-8] Mon Mar 27 16:38:00 2017
@@ -35,14 +35,14 @@ import org.apache.sis.util.resources.Err
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.5
+ * @version 0.8
  * @module
  */
 final class CompoundDirectPositions implements DirectPosition, Iterable<DirectPosition>, Iterator<DirectPosition> {
     /**
      * The arrays of ordinate values, for example (x[], y[], z[]).
      */
-    private final double[][] ordinates;
+    private final Vector[] ordinates;
 
     /**
      * Length of all ordinate values minus one.
@@ -57,15 +57,16 @@ final class CompoundDirectPositions impl
     /**
      * Wraps the given array of ordinate values.
      */
-    CompoundDirectPositions(final double[]... ordinates) {
+    CompoundDirectPositions(final Vector... ordinates) {
         this.ordinates = ordinates;
-        final int length = ordinates[0].length;
+        final int length = ordinates[0].size();
         for (int i=1; i<ordinates.length; i++) {
-            if (ordinates[i].length != length) {
+            if (ordinates[i].size() != length) {
                 throw new IllegalArgumentException(Errors.format(Errors.Keys.MismatchedArrayLengths));
             }
         }
-        last = length - 1;
+        last  = length - 1;
+        index = length;
     }
 
     /**
@@ -75,6 +76,9 @@ final class CompoundDirectPositions impl
      */
     @Override
     public Iterator<DirectPosition> iterator() {
+        if (hasNext()) {
+            throw new UnsupportedOperationException(Errors.format(Errors.Keys.CanIterateOnlyOnce));
+        }
         index = -1;
         return this;
     }
@@ -131,7 +135,7 @@ final class CompoundDirectPositions impl
      */
     @Override
     public double getOrdinate(final int dimension) {
-        return ordinates[dimension][index];
+        return ordinates[dimension].doubleValue(index);
     }
 
     /**

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Line.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Line.java?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Line.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Line.java [UTF-8] Mon Mar 27 16:38:00 2017
@@ -22,6 +22,7 @@ import org.opengis.geometry.MismatchedDi
 import org.apache.sis.internal.util.DoubleDouble;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.ArgumentChecks;
 
 import static java.lang.Double.*;
 
@@ -42,7 +43,7 @@ import static java.lang.Double.*;
  *
  * @author  Martin Desruisseaux (MPO, IRD)
  * @since   0.5
- * @version 0.5
+ * @version 0.8
  * @module
  *
  * @see Plane
@@ -194,6 +195,21 @@ public class Line implements Cloneable,
     }
 
     /**
+     * Sets this line from values of arbitrary {@code Number} type. This method is invoked by algorithms that
+     * may produce other kind of numbers (for example with different precision) than the usual {@code double}
+     * primitive type. The default implementation delegates to {@link #setEquation(double, double)}, but
+     * subclasses can override this method if they want to process other kind of numbers in a special way.
+     *
+     * @param  slope  the slope.
+     * @param  y0     the <var>y</var> value at <var>x</var> = 0.
+     *
+     * @since 0.8
+     */
+    public void setEquation(final Number slope, final Number y0) {
+        setEquation(slope.doubleValue(), y0.doubleValue());
+    }
+
+    /**
      * Sets a line through the specified points.
      * The line will continue toward infinity after the points.
      *
@@ -219,8 +235,10 @@ public class Line implements Cloneable,
     /**
      * Given a set of data points <var>x</var>[0 … <var>n</var>-1], <var>y</var>[0 … <var>n</var>-1],
      * fits them to a straight line <var>y</var> = <var>slope</var>⋅<var>x</var> + <var>y₀</var> in a
-     * least-squares senses. This method assume that the <var>x</var> values are precise and all uncertainty
-     * is in <var>y</var>.
+     * least-squares senses.
+     * This method assumes that the <var>x</var> values are precise and all uncertainty is in <var>y</var>.
+     *
+     * <p>The default implementation delegates to {@link #fit(Vector, Vector)}.</p>
      *
      * @param  x  vector of <var>x</var> values (independent variable).
      * @param  y  vector of <var>y</var> values (dependent variable).
@@ -229,30 +247,46 @@ public class Line implements Cloneable,
      * @throws IllegalArgumentException if <var>x</var> and <var>y</var> do not have the same length.
      */
     public double fit(final double[] x, final double[] y) {
-        // Do not invoke an overrideable method with our tricky iterable.
-        return doFit(new CompoundDirectPositions(x, y));
+        ArgumentChecks.ensureNonNull("x", x);
+        ArgumentChecks.ensureNonNull("y", y);
+        return fit(new ArrayVector.Doubles(x), new ArrayVector.Doubles(y));
     }
 
     /**
-     * Given a sequence of points, fits them to a straight line <var>y</var> = <var>slope</var>⋅<var>x</var> +
-     * <var>y₀</var> in a least-squares senses. This method assume that the <var>x</var> values are precise and
-     * all uncertainty is in <var>y</var>.
+     * Given a set of data points <var>x</var>[0 … <var>n</var>-1], <var>y</var>[0 … <var>n</var>-1],
+     * fits them to a straight line <var>y</var> = <var>slope</var>⋅<var>x</var> + <var>y₀</var> in a
+     * least-squares senses.
+     * This method assumes that the <var>x</var> values are precise and all uncertainty is in <var>y</var>.
      *
-     * <p>Points shall be two dimensional with ordinate values in the (<var>x</var>,<var>y</var>) order.
-     * {@link Double#NaN} ordinate values are ignored.</p>
+     * <p>The default implementation delegates to {@link #fit(Iterable)}.</p>
      *
-     * @param  points  the two-dimensional points.
+     * @param  x  vector of <var>x</var> values (independent variable).
+     * @param  y  vector of <var>y</var> values (dependent variable).
      * @return estimation of the correlation coefficient. The closer this coefficient is to +1 or -1, the better the fit.
-     * @throws MismatchedDimensionException if a point is not two-dimensional.
+     *
+     * @throws IllegalArgumentException if <var>x</var> and <var>y</var> do not have the same length.
+     *
+     * @since 0.8
      */
-    public double fit(final Iterable<? extends DirectPosition> points) {
-        return doFit(points);
+    public double fit(final Vector x, final Vector y) {
+        ArgumentChecks.ensureNonNull("x", x);
+        ArgumentChecks.ensureNonNull("y", y);
+        return fit(new CompoundDirectPositions(x, y));
     }
 
     /**
-     * Implementation of public {@code fit(…)} methods.
+     * Given a sequence of points, fits them to a straight line
+     * <var>y</var> = <var>slope</var>⋅<var>x</var> + <var>y₀</var> in a least-squares senses.
+     * Points shall be two dimensional with ordinate values in the (<var>x</var>,<var>y</var>) order.
+     * This method assumes that the <var>x</var> values are precise and all uncertainty is in <var>y</var>.
+     * {@link Double#NaN} ordinate values are ignored.
+     *
+     * @param  points  the two-dimensional points.
+     * @return estimation of the correlation coefficient. The closer this coefficient is to +1 or -1, the better the fit.
+     * @throws MismatchedDimensionException if a point is not two-dimensional.
      */
-    private double doFit(final Iterable<? extends DirectPosition> points) {
+    public double fit(final Iterable<? extends DirectPosition> points) {
+        ArgumentChecks.ensureNonNull("points", points);
         int i = 0, n = 0;
         final DoubleDouble mean_x = new DoubleDouble();
         final DoubleDouble mean_y = new DoubleDouble();
@@ -325,7 +359,7 @@ public class Line implements Cloneable,
         b.multiply(a);
         b.negate();
         b.add(mean_y);          // y₀ = mean_y - mean_x * slope
-        setEquation(a.value, b.value);
+        setEquation(a, b);
         /*
          * Compute the correlation coefficient:
          * mean_xy / sqrt(mean_x2 * (mean_y2 - mean_y * mean_y))

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java [UTF-8] Mon Mar 27 16:38:00 2017
@@ -16,17 +16,22 @@
  */
 package org.apache.sis.math;
 
+import java.util.Iterator;
 import java.io.Serializable;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.MismatchedDimensionException;
 import org.apache.sis.internal.util.DoubleDouble;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.ArgumentChecks;
 
 import static java.lang.Math.abs;
 import static java.lang.Math.sqrt;
 import static java.lang.Math.ulp;
 
+// Branch-dependent imports
+import org.apache.sis.internal.jdk8.JDK8;
+
 
 /**
  * Equation of a plane in a three-dimensional space (<var>x</var>,<var>y</var>,<var>z</var>).
@@ -44,7 +49,7 @@ import static java.lang.Math.ulp;
  * @author  Martin Desruisseaux (MPO, IRD)
  * @author  Howard Freeland (MPO, for algorithmic inspiration)
  * @since   0.5
- * @version 0.5
+ * @version 0.8
  * @module
  *
  * @see Line
@@ -211,13 +216,28 @@ public class Plane implements Cloneable,
     }
 
     /**
+     * Sets this plane from values of arbitrary {@code Number} type. This method is invoked by algorithms that
+     * may produce other kind of numbers (for example with different precision) than the usual {@code double}
+     * primitive type. The default implementation delegates to {@link #setEquation(double, double, double)},
+     * but subclasses can override this method if they want to process other kind of numbers in a special way.
+     *
+     * @param sx  the slope along the <var>x</var> values.
+     * @param sy  the slope along the <var>y</var> values.
+     * @param z0  the <var>z</var> value at (<var>x</var>,<var>y</var>) = (0,0).
+     *
+     * @since 0.8
+     */
+    public void setEquation(final Number sx, final Number sy, final Number z0) {
+        setEquation(sx.doubleValue(), sy.doubleValue(), z0.doubleValue());
+    }
+
+    /**
      * Computes the plane's coefficients from the given ordinate values.
-     * This method uses a linear regression in the least-square sense,
-     * with the assumption that the (<var>x</var>,<var>y</var>) values are precise
-     * and all uncertainty is in <var>z</var>.
+     * This method uses a linear regression in the least-square sense, with the assumption that
+     * the (<var>x</var>,<var>y</var>) values are precise and all uncertainty is in <var>z</var>.
+     * {@link Double#NaN} values are ignored. The result is undetermined if all points are colinear.
      *
-     * <p>{@link Double#NaN} values are ignored.
-     * The result is undetermined if all points are colinear.</p>
+     * <p>The default implementation delegates to {@link #fit(Vector, Vector, Vector)}.</p>
      *
      * @param  x  vector of <var>x</var> coordinates.
      * @param  y  vector of <var>y</var> coordinates.
@@ -226,154 +246,313 @@ public class Plane implements Cloneable,
      * @throws IllegalArgumentException if <var>x</var>, <var>y</var> and <var>z</var> do not have the same length.
      */
     public double fit(final double[] x, final double[] y, final double[] z) {
-        // Do not invoke an overrideable method with our tricky iterable.
-        return fit(new CompoundDirectPositions(x, y, z), true, true, true);
+        ArgumentChecks.ensureNonNull("x", x);
+        ArgumentChecks.ensureNonNull("y", y);
+        ArgumentChecks.ensureNonNull("z", z);
+        return fit(new ArrayVector.Doubles(x), new ArrayVector.Doubles(y), new ArrayVector.Doubles(z));
+    }
+
+    /**
+     * Computes the plane's coefficients from the given ordinate values.
+     * This method uses a linear regression in the least-square sense, with the assumption that
+     * the (<var>x</var>,<var>y</var>) values are precise and all uncertainty is in <var>z</var>.
+     * {@link Double#NaN} values are ignored. The result is undetermined if all points are colinear.
+     *
+     * <p>The default implementation delegates to {@link #fit(Iterable)}.</p>
+     *
+     * @param  x  vector of <var>x</var> coordinates.
+     * @param  y  vector of <var>y</var> coordinates.
+     * @param  z  vector of <var>z</var> values.
+     * @return an estimation of the Pearson correlation coefficient.
+     * @throws IllegalArgumentException if <var>x</var>, <var>y</var> and <var>z</var> do not have the same length.
+     *
+     * @since 0.8
+     */
+    public double fit(final Vector x, final Vector y, final Vector z) {
+        ArgumentChecks.ensureNonNull("x", x);
+        ArgumentChecks.ensureNonNull("y", y);
+        ArgumentChecks.ensureNonNull("z", z);
+        return fit(new CompoundDirectPositions(x, y, z));
+    }
+
+    /**
+     * Computes the plane's coefficients from values distributed on a regular grid. Invoking this method
+     * is equivalent (except for NaN handling) to invoking {@link #fit(Vector, Vector, Vector)} where all
+     * vectors have a length of {@code nx} × {@code ny} and the <var>x</var> and <var>y</var> vectors have
+     * the following content:
+     *
+     * <blockquote>
+     * <table class="compact" summary="x and y vector content">
+     *   <tr>
+     *     <th><var>x</var> vector</th>
+     *     <th><var>y</var> vector</th>
+     *   </tr><tr>
+     *     <td>
+     *       0 1 2 3 4 5 … n<sub>x</sub>-1<br>
+     *       0 1 2 3 4 5 … n<sub>x</sub>-1<br>
+     *       0 1 2 3 4 5 … n<sub>x</sub>-1<br>
+     *       …<br>
+     *       0 1 2 3 4 5 … n<sub>x</sub>-1<br>
+     *     </td><td>
+     *       0 0 0 0 0 0 … 0<br>
+     *       1 1 1 1 1 1 … 1<br>
+     *       2 2 2 2 2 2 … 2<br>
+     *       …<br>
+     *       n<sub>y</sub>-1 n<sub>y</sub>-1 n<sub>y</sub>-1 … n<sub>y</sub>-1<br>
+     *     </td>
+     *   </tr>
+     * </table>
+     * </blockquote>
+     *
+     * This method uses a linear regression in the least-square sense, with the assumption that
+     * the (<var>x</var>,<var>y</var>) values are precise and all uncertainty is in <var>z</var>.
+     * The result is undetermined if all points are colinear.
+     *
+     * @param  nx  number of columns.
+     * @param  ny  number of rows.
+     * @param  z   values of a matrix of {@code nx} columns by {@code ny} rows organized in a row-major fashion.
+     * @return an estimation of the Pearson correlation coefficient.
+     * @throws IllegalArgumentException if <var>z</var> does not have the expected length or if a <var>z</var>
+     *         value is {@link Double#NaN}.
+     *
+     * @since 0.8
+     */
+    public double fit(final int nx, final int ny, final Vector z) {
+        ArgumentChecks.ensureStrictlyPositive("nx", nx);
+        ArgumentChecks.ensureStrictlyPositive("ny", ny);
+        ArgumentChecks.ensureNonNull("z", z);
+        final int length = JDK8.multiplyExact(nx, ny);
+        if (z.size() != length) {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.UnexpectedArrayLength_2, length, z.size()));
+        }
+        final Fit r = new Fit(nx, ny, z);
+        r.resolve();
+        final double p = r.correlation(nx, length, z, null);
+        setEquation(r.sx, r.sy, r.z0);
+        return p;
     }
 
     /**
      * Computes the plane's coefficients from the given sequence of points.
-     * This method uses a linear regression in the least-square sense,
-     * with the assumption that the (<var>x</var>,<var>y</var>) values are precise
-     * and all uncertainty is in <var>z</var>.
-     *
-     * <p>Points shall be three dimensional with ordinate values in the (<var>x</var>,<var>y</var>,<var>z</var>) order.
-     * {@link Double#NaN} ordinate values are ignored.
-     * The result is undetermined if all points are colinear.</p>
+     * This method uses a linear regression in the least-square sense, with the assumption that
+     * the (<var>x</var>,<var>y</var>) values are precise and all uncertainty is in <var>z</var>.
+     * Points shall be three dimensional with ordinate values in the (<var>x</var>,<var>y</var>,<var>z</var>) order.
+     * {@link Double#NaN} values are ignored. The result is undetermined if all points are colinear.
      *
      * @param  points  the three-dimensional points.
      * @return an estimation of the Pearson correlation coefficient.
      * @throws MismatchedDimensionException if a point is not three-dimensional.
      */
     public double fit(final Iterable<? extends DirectPosition> points) {
-        return fit(points, true, true, true);
+        ArgumentChecks.ensureNonNull("points", points);
+        final Fit r = new Fit(points);
+        r.resolve();
+        final double p = r.correlation(0, 0, null, points.iterator());
+        /*
+         * Store the result only when we are done, so we have a "all or nothing" behavior.
+         * We invoke the setEquation(sx, sy, z₀) method in case the user override it.
+         */
+        setEquation(r.sx, r.sy, r.z0);
+        return p;
     }
 
     /**
-     * Implementation of public {@code fit(…)} methods.
-     * This method needs to iterate over the points two times:
+     * Computes the plane coefficients. This class needs to iterate over the points two times:
      * one for computing the coefficients, and one for the computing the Pearson coefficient.
      * The second pass can also opportunistically checks if some small coefficients can be replaced by zero.
      */
-    private double fit(final Iterable<? extends DirectPosition> points,
-            boolean detectZeroSx, boolean detectZeroSy, boolean detectZeroZ0)
-    {
-        int i = 0, n = 0;
-        final DoubleDouble sum_x  = new DoubleDouble();
-        final DoubleDouble sum_y  = new DoubleDouble();
-        final DoubleDouble sum_z  = new DoubleDouble();
-        final DoubleDouble sum_xx = new DoubleDouble();
-        final DoubleDouble sum_yy = new DoubleDouble();
-        final DoubleDouble sum_xy = new DoubleDouble();
-        final DoubleDouble sum_zx = new DoubleDouble();
-        final DoubleDouble sum_zy = new DoubleDouble();
-        final DoubleDouble xx     = new DoubleDouble();
-        final DoubleDouble yy     = new DoubleDouble();
-        final DoubleDouble xy     = new DoubleDouble();
-        final DoubleDouble zx     = new DoubleDouble();
-        final DoubleDouble zy     = new DoubleDouble();
-        for (final DirectPosition p : points) {
-            final int dimension = p.getDimension();
-            if (dimension != DIMENSION) {
-                throw new MismatchedDimensionException(Errors.format(
-                        Errors.Keys.MismatchedDimension_3, "points[" + i + ']', DIMENSION, dimension));
+    private static final class Fit {
+        private final DoubleDouble sum_x  = new DoubleDouble();
+        private final DoubleDouble sum_y  = new DoubleDouble();
+        private final DoubleDouble sum_z  = new DoubleDouble();
+        private final DoubleDouble sum_xx = new DoubleDouble();
+        private final DoubleDouble sum_yy = new DoubleDouble();
+        private final DoubleDouble sum_xy = new DoubleDouble();
+        private final DoubleDouble sum_zx = new DoubleDouble();
+        private final DoubleDouble sum_zy = new DoubleDouble();
+        private final DoubleDouble xx     = new DoubleDouble();
+        private final DoubleDouble yy     = new DoubleDouble();
+        private final DoubleDouble xy     = new DoubleDouble();
+        private final DoubleDouble zx     = new DoubleDouble();
+        private final DoubleDouble zy     = new DoubleDouble();
+        private final int n;
+
+        /** Solution of the plane equation. */ DoubleDouble sx, sy, z0;
+
+        /**
+         * Computes the values of all {@code sum_*} fields from randomly distributed points.
+         * Value of all other fields are undetermined..
+         */
+        Fit(final Iterable<? extends DirectPosition> points) {
+            int i = 0, n = 0;
+            for (final DirectPosition p : points) {
+                final int dimension = p.getDimension();
+                if (dimension != DIMENSION) {
+                    throw new MismatchedDimensionException(Errors.format(
+                            Errors.Keys.MismatchedDimension_3, "points[" + i + ']', DIMENSION, dimension));
+                }
+                i++;
+                final double x = p.getOrdinate(0); if (Double.isNaN(x)) continue;
+                final double y = p.getOrdinate(1); if (Double.isNaN(y)) continue;
+                final double z = p.getOrdinate(2); if (Double.isNaN(z)) continue;
+                xx.setToProduct(x, x);
+                yy.setToProduct(y, y);
+                xy.setToProduct(x, y);
+                zx.setToProduct(z, x);
+                zy.setToProduct(z, y);
+                sum_x .add(x );
+                sum_y .add(y );
+                sum_z .add(z );
+                sum_xx.add(xx);
+                sum_yy.add(yy);
+                sum_xy.add(xy);
+                sum_zx.add(zx);
+                sum_zy.add(zy);
+                n++;
             }
-            i++;
-            final double x = p.getOrdinate(0); if (Double.isNaN(x)) continue;
-            final double y = p.getOrdinate(1); if (Double.isNaN(y)) continue;
-            final double z = p.getOrdinate(2); if (Double.isNaN(z)) continue;
-            xx.setToProduct(x, x);
-            yy.setToProduct(y, y);
-            xy.setToProduct(x, y);
-            zx.setToProduct(z, x);
-            zy.setToProduct(z, y);
-            sum_x.add(x);
-            sum_y.add(y);
-            sum_z.add(z);
-            sum_xx.add(xx);
-            sum_yy.add(yy);
-            sum_xy.add(xy);
-            sum_zx.add(zx);
-            sum_zy.add(zy);
-            n++;
+            this.n = n;
         }
-        /*
-         *    ( sum_zx - sum_z*sum_x )  =  sx*(sum_xx - sum_x*sum_x) + sy*(sum_xy - sum_x*sum_y)
-         *    ( sum_zy - sum_z*sum_y )  =  sx*(sum_xy - sum_x*sum_y) + sy*(sum_yy - sum_y*sum_y)
-         */
-        zx.setFrom(sum_x); zx.divide(-n, 0); zx.multiply(sum_z); zx.add(sum_zx);    // zx = sum_zx - sum_z*sum_x/n
-        zy.setFrom(sum_y); zy.divide(-n, 0); zy.multiply(sum_z); zy.add(sum_zy);    // zy = sum_zy - sum_z*sum_y/n
-        xx.setFrom(sum_x); xx.divide(-n, 0); xx.multiply(sum_x); xx.add(sum_xx);    // xx = sum_xx - sum_x*sum_x/n
-        xy.setFrom(sum_y); xy.divide(-n, 0); xy.multiply(sum_x); xy.add(sum_xy);    // xy = sum_xy - sum_x*sum_y/n
-        yy.setFrom(sum_y); yy.divide(-n, 0); yy.multiply(sum_y); yy.add(sum_yy);    // yy = sum_yy - sum_y*sum_y/n
-        /*
-         * den = (xy*xy - xx*yy)
-         */
-        final DoubleDouble tmp = new DoubleDouble(xx); tmp.multiply(yy);
-        final DoubleDouble den = new DoubleDouble(xy); den.multiply(xy);
-        den.subtract(tmp);
-        /*
-         * sx = (zy*xy - zx*yy) / den
-         * sy = (zx*xy - zy*xx) / den
-         * z₀ = (sum_z - (sx*sum_x + sy*sum_y)) / n
-         */
-        final DoubleDouble sx = new DoubleDouble(zy); sx.multiply(xy); tmp.setFrom(zx); tmp.multiply(yy); sx.subtract(tmp); sx.divide(den);
-        final DoubleDouble sy = new DoubleDouble(zx); sy.multiply(xy); tmp.setFrom(zy); tmp.multiply(xx); sy.subtract(tmp); sy.divide(den);
-        final DoubleDouble z0 = new DoubleDouble(sy);
-        z0.multiply(sum_y);
-        tmp.setFrom(sx);
-        tmp.multiply(sum_x);
-        tmp.add(z0);
-        z0.setFrom(sum_z);
-        z0.subtract(tmp);
-        z0.divide(n, 0);
-        /*
-         * At this point, the model is computed. Now computes an estimation of the Pearson
-         * correlation coefficient. Note that both the z array and the z computed from the
-         * model have the same average, called sum_z below (the name is not true anymore).
-         *
-         * We do not use double-double arithmetic here since the Pearson coefficient is
-         * for information purpose (quality estimation).
+
+        /**
+         * Computes the values of all {@code sum_*} fields from the <var>z</var> values on a regular grid.
+         * Value of all other fields are undetermined..
          */
-        final double mean_x = sum_x.value / n;
-        final double mean_y = sum_y.value / n;
-        final double mean_z = sum_z.value / n;
-        final double offset = abs((sx.value * mean_x + sy.value * mean_y) + z0.value); // Offsetted z₀ - see comment before usage.
-        double sum_ds2 = 0, sum_dz2 = 0, sum_dsz = 0;
-        for (final DirectPosition p : points) {
-            final double x = (p.getOrdinate(0) - mean_x) * sx.value;
-            final double y = (p.getOrdinate(1) - mean_y) * sy.value;
-            final double z = (p.getOrdinate(2) - mean_z);
-            final double s = x + y;
-            if (!Double.isNaN(s) && !Double.isNaN(z)) {
-                sum_ds2 += s * s;
-                sum_dz2 += z * z;
-                sum_dsz += s * z;
-            }
+        Fit(final int nx, final int ny, final Vector vz) {
             /*
-             * Algorithm for detecting if a coefficient should be zero:
-             * If for every points given by the user, adding (sx⋅x) in (sx⋅x + sy⋅y + z₀) does not make any difference
-             * because (sx⋅x) is smaller than 1 ULP of (sy⋅y + z₀), then it is not worth adding it and  sx  can be set
-             * to zero. The same rational applies to (sy⋅y) and z₀.
-             *
-             * Since we work with differences from the means, the  z = sx⋅x + sy⋅y + z₀  equation can be rewritten as:
+             * Computes the sum of x, y and z values. Computes also the sum of x², y², x⋅y, z⋅x and z⋅y values.
+             * When possible, we will avoid to compute the sum inside the loop and use the following identities
+             * instead:
              *
-             *     Δz = sx⋅Δx + sy⋅Δy + (sx⋅mx + sy⋅my + z₀ - mz)    where the term between (…) is close to zero.
+             *           1 + 2 + 3 ... + n    =    n⋅(n+1)/2              (arithmetic series)
+             *        1² + 2² + 3² ... + n²   =    n⋅(n+0.5)⋅(n+1)/3
              *
-             * The check for (sx⋅Δx) and (sy⋅Δy) below ignore the (…) term since it is close to zero.
-             * The check for  z₀  is derived from an equation without the  -mz  term.
+             * Note that for exclusive upper bound, we need to replace n by n-1 in above formulas.
              */
-            if (detectZeroSx && abs(x) >= ulp(y * ZERO_THRESHOLD)) detectZeroSx = false;
-            if (detectZeroSy && abs(y) >= ulp(x * ZERO_THRESHOLD)) detectZeroSy = false;
-            if (detectZeroZ0 && offset >= ulp(s * ZERO_THRESHOLD)) detectZeroZ0 = false;
+            int n = 0;
+            for (int y=0; y<ny; y++) {
+                for (int x=0; x<nx; x++) {
+                    final double z = vz.doubleValue(n);
+                    if (Double.isNaN(z)) {
+                        throw new IllegalArgumentException(Errors.format(Errors.Keys.NotANumber_1, "z[" + n + ']'));
+                    }
+                    zx.setToProduct(z, x);
+                    zy.setToProduct(z, y);
+                    sum_z .add(z );
+                    sum_zx.add(zx);
+                    sum_zy.add(zy);
+                    n++;
+                }
+            }
+            sum_x .value = n/2d;  sum_x .multiply(nx-1,   0);                     // Division by 2 is exact.
+            sum_y .value = n/2d;  sum_y .multiply(ny-1,   0);
+            sum_xx.value = n;     sum_xx.multiply(nx-0.5, 0); sum_xx.multiply(nx-1, 0); sum_xx.divide(3, 0);
+            sum_yy.value = n;     sum_yy.multiply(ny-0.5, 0); sum_yy.multiply(ny-1, 0); sum_yy.divide(3, 0);
+            sum_xy.value = n/4d;  sum_xy.multiply(ny-1,   0); sum_xy.multiply(nx-1, 0);
+            this.n = n;
         }
-        /*
-         * Store the result only when we are done, so we have a "all or nothing" behavior.
-         * We invoke the setEquation(sx, sy, z₀) method in case the user override it.
+
+        /**
+         * Computes the {@link #sx}, {@link #sy} and {@link #z0} values using the sums computed by the constructor.
          */
-        setEquation(detectZeroSx ? 0 : sx.value,
-                    detectZeroSy ? 0 : sy.value,
-                    detectZeroZ0 ? 0 : z0.value);
-        return Math.min(sum_dsz / sqrt(sum_ds2 * sum_dz2), 1);
+        private void resolve() {
+            /*
+             *    ( sum_zx - sum_z⋅sum_x )  =  sx⋅(sum_xx - sum_x⋅sum_x) + sy⋅(sum_xy - sum_x⋅sum_y)
+             *    ( sum_zy - sum_z⋅sum_y )  =  sx⋅(sum_xy - sum_x⋅sum_y) + sy⋅(sum_yy - sum_y⋅sum_y)
+             */
+            zx.setFrom(sum_x); zx.divide(-n, 0); zx.multiply(sum_z); zx.add(sum_zx);    // zx = sum_zx - sum_z⋅sum_x/n
+            zy.setFrom(sum_y); zy.divide(-n, 0); zy.multiply(sum_z); zy.add(sum_zy);    // zy = sum_zy - sum_z⋅sum_y/n
+            xx.setFrom(sum_x); xx.divide(-n, 0); xx.multiply(sum_x); xx.add(sum_xx);    // xx = sum_xx - sum_x⋅sum_x/n
+            xy.setFrom(sum_y); xy.divide(-n, 0); xy.multiply(sum_x); xy.add(sum_xy);    // xy = sum_xy - sum_x⋅sum_y/n
+            yy.setFrom(sum_y); yy.divide(-n, 0); yy.multiply(sum_y); yy.add(sum_yy);    // yy = sum_yy - sum_y⋅sum_y/n
+            /*
+             * den = (xy⋅xy - xx⋅yy)
+             */
+            final DoubleDouble tmp = new DoubleDouble(xx); tmp.multiply(yy);
+            final DoubleDouble den = new DoubleDouble(xy); den.multiply(xy);
+            den.subtract(tmp);
+            /*
+             * sx = (zy⋅xy - zx⋅yy) / den
+             * sy = (zx⋅xy - zy⋅xx) / den
+             * z₀ = (sum_z - (sx⋅sum_x + sy⋅sum_y)) / n
+             */
+            sx = new DoubleDouble(zy); sx.multiply(xy); tmp.setFrom(zx); tmp.multiply(yy); sx.subtract(tmp); sx.divide(den);
+            sy = new DoubleDouble(zx); sy.multiply(xy); tmp.setFrom(zy); tmp.multiply(xx); sy.subtract(tmp); sy.divide(den);
+            z0 = new DoubleDouble(sy);
+            z0.multiply(sum_y);
+            tmp.setFrom(sx);
+            tmp.multiply(sum_x);
+            tmp.add(z0);
+            z0.setFrom(sum_z);
+            z0.subtract(tmp);
+            z0.divide(n, 0);
+        }
+
+        /**
+         * Computes an estimation of the Pearson correlation coefficient. We do not use double-double arithmetic
+         * here since the Pearson coefficient is for information purpose (quality estimation).
+         *
+         * <p>Only one of ({@code nx}, {@code length}, {@code z}) tuple or {@code points} argument should be non-null.</p>
+         */
+        double correlation(final int nx, final int length, final Vector vz,
+                           final Iterator<? extends DirectPosition> points)
+        {
+            boolean detectZeroSx = true;
+            boolean detectZeroSy = true;
+            boolean detectZeroZ0 = true;
+            final double sx     = this.sx.value;
+            final double sy     = this.sy.value;
+            final double z0     = this.z0.value;
+            final double mean_x = sum_x.value / n;
+            final double mean_y = sum_y.value / n;
+            final double mean_z = sum_z.value / n;
+            final double offset = abs((sx * mean_x + sy * mean_y) + z0);    // Offsetted z₀ - see comment before usage.
+            int index = 0;
+            double sum_ds2 = 0, sum_dz2 = 0, sum_dsz = 0;
+            for (;;) {
+                double x, y, z;
+                if (vz != null) {
+                    if (index >= length) break;
+                    x = index % nx;
+                    y = index / nx;
+                    z = vz.doubleValue(index++);
+                } else {
+                    if (!points.hasNext()) break;
+                    final DirectPosition p = points.next();
+                    x = p.getOrdinate(0);
+                    y = p.getOrdinate(1);
+                    z = p.getOrdinate(2);
+                }
+                x = (x - mean_x) * sx;
+                y = (y - mean_y) * sy;
+                z = (z - mean_z);
+                final double s = x + y;
+                if (!Double.isNaN(s) && !Double.isNaN(z)) {
+                    sum_ds2 += s * s;
+                    sum_dz2 += z * z;
+                    sum_dsz += s * z;
+                }
+                /*
+                 * Algorithm for detecting if a coefficient should be zero:
+                 * If for every points given by the user, adding (sx⋅x) in (sx⋅x + sy⋅y + z₀) does not make any difference
+                 * because (sx⋅x) is smaller than 1 ULP of (sy⋅y + z₀), then it is not worth adding it and  sx  can be set
+                 * to zero. The same rational applies to (sy⋅y) and z₀.
+                 *
+                 * Since we work with differences from the means, the  z = sx⋅x + sy⋅y + z₀  equation can be rewritten as:
+                 *
+                 *     Δz = sx⋅Δx + sy⋅Δy + (sx⋅mx + sy⋅my + z₀ - mz)    where the term between (…) is close to zero.
+                 *
+                 * The check for (sx⋅Δx) and (sy⋅Δy) below ignore the (…) term since it is close to zero.
+                 * The check for  z₀  is derived from an equation without the  -mz  term.
+                 */
+                if (detectZeroSx && abs(x) >= ulp(y * ZERO_THRESHOLD)) detectZeroSx = false;
+                if (detectZeroSy && abs(y) >= ulp(x * ZERO_THRESHOLD)) detectZeroSy = false;
+                if (detectZeroZ0 && offset >= ulp(s * ZERO_THRESHOLD)) detectZeroZ0 = false;
+            }
+            if (detectZeroSx) this.sx.clear();
+            if (detectZeroSy) this.sy.clear();
+            if (detectZeroZ0) this.z0.clear();
+            return Math.min(sum_dsz / sqrt(sum_ds2 * sum_dz2), 1);
+        }
     }
 
     /**

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java [UTF-8] Mon Mar 27 16:38:00 2017
@@ -54,12 +54,11 @@ import static java.lang.Math.*;
  * }
  * </div>
  *
- * Like most {@link java.text.Format} implementations, this class is not thread-safe.
- * Each thread should use its own {@code StatisticsFormat} instance or apply synchronization.
- *
- * <div class="warning"><b>Limitation:</b>
- * the current implementation can only format statistics — parsing is not yet implemented.
- * </div>
+ * <p><b>Limitations:</b></p>
+ * <ul>
+ *   <li>The current implementation can only format features — parsing is not yet implemented.</li>
+ *   <li>{@code StatisticsFormat}, like most {@code java.text.Format} subclasses, is not thread-safe.</li>
+ * </ul>
  *
  * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
  * @since   0.3

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] Mon Mar 27 16:38:00 2017
@@ -70,6 +70,11 @@ public final class Errors extends Indexe
         public static final short AmbiguousName_3 = 1;
 
         /**
+         * This object can iterate only once.
+         */
+        public static final short CanIterateOnlyOnce = 172;
+
+        /**
          * No element can be added to this set because properties ‘{0}’ and ‘{1}’ are mutually
          * exclusive.
          */
@@ -640,6 +645,11 @@ public final class Errors extends Indexe
         public static final short NotAUnicodeIdentifier_1 = 112;
 
         /**
+         * {0} is not an integer value.
+         */
+        public static final short NotAnInteger_1 = 171;
+
+        /**
          * Argument ‘{0}’ shall not be null.
          */
         public static final short NullArgument_1 = 113;

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] Mon Mar 27 16:38:00 2017
@@ -25,6 +25,7 @@
 # can reorder the parameters as they want.
 #
 AmbiguousName_3                   = Name \u201c{2}\u201d is ambiguous because it can be understood as either \u201c{0}\u201d or \u201c{1}\u201d.
+CanIterateOnlyOnce                = This object can iterate only once.
 CanNotAddToExclusiveSet_2         = No element can be added to this set because properties \u2018{0}\u2019 and \u2018{1}\u2019 are mutually exclusive.
 CanNotAssign_2                    = Can not assign \u201c{1}\u201d to \u201c{0}\u201d.
 CanNotAssignUnitToDimension_2     = Can not assign units \u201c{1}\u201d to dimension \u201c{0}\u201d.
@@ -133,6 +134,7 @@ NonTemporalUnit_1                 = \u20
 NonSystemUnit_1                   = \u201c{0}\u201d is not a fundamental or derived unit.
 NonRatioUnit_1                    = The scale of measurement for \u201c{0}\u201d unit is not a ratio scale.
 NotABackwardReference_1           = No element for the \u201c{0}\u201d identifier, or the identifier is a forward reference.
+NotAnInteger_1                    = {0} is not an integer value.
 NotAKeyValuePair_1                = \u201c{0}\u201d is not a key-value pair.
 NotANumber_1                      = Argument \u2018{0}\u2019 shall not be NaN (Not-a-Number).
 NotAPrimitiveWrapper_1            = Class \u2018{0}\u2019 is not a primitive type wrapper.

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] Mon Mar 27 16:38:00 2017
@@ -22,6 +22,7 @@
 #   U+00A0 NO-BREAK SPACE         before  :
 #
 AmbiguousName_3                   = Le nom \u00ab\u202f{2}\u202f\u00bb est ambigu\u00eb car il peut \u00eatre interpr\u00e9t\u00e9 comme \u00ab\u202f{0}\u202f\u00bb ou \u00ab\u202f{1}\u202f\u00bb.
+CanIterateOnlyOnce                = Cet objet ne peut it\u00e9rer qu\u2019une seule fois.
 CanNotAddToExclusiveSet_2         = Aucun \u00e9l\u00e9ment ne peut \u00eatre ajout\u00e9 \u00e0 cet ensemble car les propri\u00e9t\u00e9s \u2018{0}\u2019 et \u2018{1}\u2019 sont mutuellement exclusives.
 CanNotAssign_2                    = Ne peut pas assigner \u00ab\u202f{1}\u202f\u00bb \u00e0 \u00ab\u202f{0}\u202f\u00bb.
 CanNotAssignUnitToDimension_2     = Ne peut pas assigner les unit\u00e9s \u00ab\u202f{1}\u202f\u00bb \u00e0 la dimension \u00ab\u202f{0}\u202f\u00bb.
@@ -130,6 +131,7 @@ NonTemporalUnit_1                 = \u00
 NonSystemUnit_1                   = \u00ab\u202f{0}\u202f\u00bb n\u2019est pas une unit\u00e9 fondamentale ou d\u00e9riv\u00e9e.
 NonRatioUnit_1                    = L\u2019\u00e9chelle de mesure de l\u2019unit\u00e9 \u00ab\u202f{0}\u202f\u00bb n\u2019est pas une \u00e9chelle de rapports.
 NotABackwardReference_1           = Il n\u2019y a pas d\u2019\u00e9l\u00e9ment pour l\u2019identifiant \u201c{0}\u201d, ou l\u2019identifiant est une r\u00e9f\u00e9rence vers l\u2019avant.
+NotAnInteger_1                    = {0} n\u2019est pas un nombre entier.
 NotAKeyValuePair_1                = \u00ab\u202f{0}\u202f\u00bb n\u2019est pas une paire cl\u00e9-valeur.
 NotANumber_1                      = L\u2019argument \u2018{0}\u2019 ne doit pas \u00eatre NaN (Not-a-Number).
 NotAPrimitiveWrapper_1            = La classe \u2018{0}\u2019 n\u2019est pas un adaptateur d\u2019un type primitif.

Modified: sis/trunk/core/sis-utility/src/test/java/org/apache/sis/math/PlaneTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/test/java/org/apache/sis/math/PlaneTest.java?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/test/java/org/apache/sis/math/PlaneTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/test/java/org/apache/sis/math/PlaneTest.java [UTF-8] Mon Mar 27 16:38:00 2017
@@ -31,12 +31,18 @@ import static org.apache.sis.test.Assert
  *
  * @author  Martin Desruisseaux (MPO, IRD)
  * @since   0.5
- * @version 0.5
+ * @version 0.8
  * @module
  */
 @DependsOn(org.apache.sis.internal.util.DoubleDoubleTest.class)
 public final strictfp class PlaneTest extends TestCase {
     /**
+     * The Pearson coefficient computed by the last call to
+     * {@link #assertFitEquals(double, double[], double[], double[])}.
+     */
+    private double pearson;
+
+    /**
      * Invokes {@link Plane#fit(DirectPosition[])} with the given arrays,
      * and compares the fitted values against the original values.
      *
@@ -44,9 +50,9 @@ public final strictfp class PlaneTest ex
      *
      * @param  tolerance  the maximal difference allowed between the fitted and the original values.
      */
-    private static Plane assertFitEquals(final double tolerance, final double[] x, final double[] y, final double[] z) {
+    private Plane assertFitEquals(final double tolerance, final double[] x, final double[] y, final double[] z) {
         final Plane plan = new Plane();
-        final double pearson = plan.fit(x, y, z);
+        pearson = plan.fit(x, y, z);
         assertEquals("Pearson correlation coefficient", 1, pearson, 0.01);
         for (int i = 0; i < z.length; i++) {
             assertEquals("z(x,y)", z[i], plan.z(x[i], y[i]), tolerance);
@@ -59,11 +65,11 @@ public final strictfp class PlaneTest ex
      *
      * @param  rd      the random number generator to use.
      * @param  length  the desired array length.
-     * @param  offset  the minimal value allowed in the returned array.
      * @param  scale   the difference between the minimal and maximal allowed values.
+     * @param  offset  the minimal value allowed in the returned array.
      * @return the array of random values.
      */
-    private static double[] random(final Random rd, final int length, final double offset, final double scale) {
+    private static double[] random(final Random rd, final int length, final double scale, final double offset) {
         final double[] x = new double[length];
         for (int i=0; i<length; i++) {
             x[i] = offset + scale * rd.nextDouble();
@@ -77,10 +83,9 @@ public final strictfp class PlaneTest ex
      */
     @Test
     public void testFit3Points() {
-        // We fix the random seed in order to avoid colinear points.
-        final Random rd = new Random(4762038814364076443L);
+        final Random rd = new Random(4762038814364076443L);     // Fix the random seed for avoiding colinear points.
         for (int n=0; n<10; n++) {
-            assertFitEquals(1E-9, // We expect close to exact matches.
+            assertFitEquals(1E-9,                               // We expect close to exact matches.
                     random(rd, 3, 100, -25),
                     random(rd, 3,  80, -20),
                     random(rd, 3, 150, -40));
@@ -96,16 +101,67 @@ public final strictfp class PlaneTest ex
         final Random  rd = new Random(7241997054993322309L);
         final double x[] = random(rd, 4000, 100, -25);
         final double y[] = random(rd, 4000,  80, -20);
-        final double z[] = random(rd, 4000, 150, -40);
-        final Plane plan = new Plane(2, 3, -4);
+        final double z[] = random(rd, 4000,  10,  -5);
+        final Plane reference = new Plane(2, 3, -4);
         for (int i=0; i<z.length; i++) {
-            // Compute points with random displacements above or below a known plane.
-            z[i] = plan.z(x[i], y[i]) + (10 * rd.nextDouble() - 5);
+            z[i] += reference.z(x[i], y[i]);    // Compute points with random displacements above or below a known plane.
         }
+        /*
+         * For a random seed fixed to 7241997054993322309, the coefficients are:
+         *
+         *   - reference:     z(x,y) = 2.0⋅x + 3.0⋅y + -4.0
+         *   - fitted:        z(x,y) = 2.001595888896693⋅x + 3.0021028196088055⋅y + -4.105960575835259
+         */
         final Plane fitted = assertFitEquals(6, x, y, z);
-        assertEquals("sx", plan.slopeX(), fitted.slopeX(), 0.01);
-        assertEquals("sy", plan.slopeY(), fitted.slopeY(), 0.01);
-        assertEquals("z₀", plan.z0(),     fitted.z0(),     1);
+        assertEquals("sx", reference.slopeX(), fitted.slopeX(), 0.002);
+        assertEquals("sy", reference.slopeY(), fitted.slopeY(), 0.003);
+        assertEquals("z₀", reference.z0(),     fitted.z0(),     0.2);
+    }
+
+    /**
+     * Verifies that {@link Plane#fit(int, int, Vector)} produces the same result than
+     * {@link Plane#fit(double[], double[], double[])}.
+     *
+     * @since 0.8
+     */
+    @Test
+    @DependsOnMethod("testFitManyPoints")
+    public void testFitGrid() {
+        final int nx = 20;
+        final int ny = 30;
+        final Random  rd = new Random(1224444984079270867L);
+        final double z[] = random(rd, nx*ny, 12, -6);
+        final double x[] = new double[z.length];
+        final double y[] = new double[z.length];
+        final Plane reference = new Plane(-46, 17, 35);
+        for (int i=0; i<z.length; i++) {
+            x[i] = i % nx;
+            y[i] = i / nx;
+            z[i] += reference.z(x[i], y[i]);
+        }
+        /*
+         * Opportunistically verify the result of fit(double[], double[], double[]), but it is
+         * not the purpose of this test (it should have been verified by testFitManyPoints()).
+         * For a random seed fixed to 7241997054993322309, the coefficients are:
+         *
+         *   - reference:     z(x,y) = -46.0⋅x + 17.0⋅y + 35.0
+         *   - fitted:        z(x,y) = -45.96034442769177⋅x + 16.981792790278828⋅y + 34.901440258861314
+         */
+        final Plane fitted = assertFitEquals(7, x, y, z);
+        assertEquals("sx", reference.slopeX(), fitted.slopeX(), 0.05);
+        assertEquals("sy", reference.slopeY(), fitted.slopeY(), 0.02);
+        assertEquals("z₀", reference.z0(),     fitted.z0(),     0.1);
+        final double ep = pearson;
+        /*
+         * Verify that the optimized code path for a grid produces the exact same result than the
+         * generic code path.
+         */
+        final Plane gf = new Plane();
+        gf.fit(nx, ny, Vector.create(z, false));
+        assertEquals("sx", fitted.slopeX(), gf.slopeX(), STRICT);
+        assertEquals("sy", fitted.slopeY(), gf.slopeY(), STRICT);
+        assertEquals("z₀", fitted.z0(),     gf.z0(),     STRICT);
+        assertEquals("Pearson", ep,         pearson,     STRICT);
     }
 
     /**

Modified: sis/trunk/ide-project/NetBeans/nbproject/project.properties
URL: http://svn.apache.org/viewvc/sis/trunk/ide-project/NetBeans/nbproject/project.properties?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/ide-project/NetBeans/nbproject/project.properties [ISO-8859-1] (original)
+++ sis/trunk/ide-project/NetBeans/nbproject/project.properties [ISO-8859-1] Mon Mar 27 16:38:00 2017
@@ -90,15 +90,17 @@ jdom1.version        = 1.0
 jdom2.version        = 2.0.4
 jee.version          = 7.0
 osgi.version         = 6.0.0
-netcdf.version       = 4.6.6
-joda-time.version    = 2.2
-httpclient.version   = 4.3.6
+netcdf.version       = 4.6.8
+joda-time.version    = 2.8.1
+httpclient.version   = 4.5.1
+httpcore.version     = 4.4.4
+cm-logging.version   = 1.2
 slf4j.version        = 1.7.7
 junit.version        = 4.12
 hamcrest.version     = 1.3
 jaxb-ns-mapper       = 2.2.4
 hsqldb.version       = 2.3.4
-postgresql.version   = 9.4.1212.jre7
+postgresql.version   = 42.0.0.jre7
 
 #
 # Classpaths for compilation, execution and tests.
@@ -142,6 +144,8 @@ run.test.classpath=\
     ${maven.repository}/edu/ucar/udunits/${netcdf.version}/udunits-${netcdf.version}.jar:\
     ${maven.repository}/edu/ucar/httpservices/${netcdf.version}/httpservices-${netcdf.version}.jar:\
     ${maven.repository}/joda-time/joda-time/${joda-time.version}/joda-time-${joda-time.version}.jar:\
+    ${maven.repository}/commons-logging/commons-logging/${cm-logging.version}/commons-logging-${cm-logging.version}.jar:\
+    ${maven.repository}/org/apache/httpcomponents/httpcore/${httpcore.version}/httpcore-${httpcore.version}.jar:\
     ${maven.repository}/org/apache/httpcomponents/httpclient/${httpclient.version}/httpclient-${httpclient.version}.jar:\
     ${maven.repository}/org/slf4j/slf4j-api/${slf4j.version}/slf4j-api-${slf4j.version}.jar:\
     ${maven.repository}/org/slf4j/slf4j-jdk14/${slf4j.version}/slf4j-jdk14-${slf4j.version}.jar

Modified: sis/trunk/pom.xml
URL: http://svn.apache.org/viewvc/sis/trunk/pom.xml?rev=1788983&r1=1788982&r2=1788983&view=diff
==============================================================================
--- sis/trunk/pom.xml (original)
+++ sis/trunk/pom.xml Mon Mar 27 16:38:00 2017
@@ -429,7 +429,7 @@ Apache SIS is a free software, Java lang
       <dependency>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-compress</artifactId>
-        <version>1.12</version>
+        <version>1.13</version>
       </dependency>
 
       <!-- Databases -->
@@ -442,7 +442,7 @@ Apache SIS is a free software, Java lang
       <dependency>
         <groupId>org.postgresql</groupId>
         <artifactId>postgresql</artifactId>
-        <version>9.4.1212.jre7</version>
+        <version>42.0.0.jre7</version>
         <scope>test</scope>
       </dependency>
 
@@ -488,7 +488,7 @@ Apache SIS is a free software, Java lang
          The last properties in this list depend on the Apache SIS branch.
        =================================================================== -->
   <properties>
-    <netcdf.version>4.6.6</netcdf.version>
+    <netcdf.version>4.6.8</netcdf.version>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <website.encoding>UTF-8</website.encoding>
     <website.locale>en</website.locale>



Mime
View raw message