sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1654425 - in /sis/branches/JDK7: ./ core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/ core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/ core/sis-referencing/src/main/java/org/apa...
Date Fri, 23 Jan 2015 22:50:08 GMT
Author: desruisseaux
Date: Fri Jan 23 22:50:08 2015
New Revision: 1654425

URL: http://svn.apache.org/r1654425
Log:
Merge from the JDK8 branch:
- partial port of Line class
- javadoc clarification

Added:
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/Line.java
      - copied unchanged from r1654423, sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Line.java
    sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/LineTest.java
      - copied unchanged from r1654423, sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/math/LineTest.java
Modified:
    sis/branches/JDK7/   (props changed)
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilderTest.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/CompoundDirectPositions.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/package-info.java
    sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/PlaneTest.java
    sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
    sis/branches/JDK7/src/main/javadoc/stylesheet.css

Propchange: sis/branches/JDK7/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Fri Jan 23 22:50:08 2015
@@ -1,4 +1,4 @@
 /sis/branches/Android:1430670-1480699
 /sis/branches/JDK6:1394913-1508480
-/sis/branches/JDK8:1584960-1654026
+/sis/branches/JDK8:1584960-1654423
 /sis/trunk:1394364-1508466,1519089-1519674

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java?rev=1654425&r1=1654424&r2=1654425&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java [UTF-8] Fri Jan 23 22:50:08 2015
@@ -18,19 +18,23 @@ package org.apache.sis.referencing.opera
 
 import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.MismatchedDimensionException;
+import org.apache.sis.math.Line;
 import org.apache.sis.math.Plane;
 import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
 import org.apache.sis.referencing.operation.transform.LinearTransform;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
-import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.Classes;
+import org.apache.sis.util.Debug;
 
 
 /**
  * Creates a linear (usually affine) transform which will map approximatively the given source points to
  * the given target points. The transform coefficients are determined using a <cite>least squares</cite>
- * estimation method.
+ * estimation method, with the assumption that source points are precise and all uncertainty is in the
+ * target points.
  *
  * <div class="note"><b>Implementation note:</b>
  * The quantity that current implementation tries to minimize is not strictly the squared Euclidian distance.
@@ -43,10 +47,14 @@ import org.apache.sis.util.resources.Err
  * @since   0.5
  * @version 0.5
  * @module
+ *
+ * @see LinearTransform
+ * @see Line
+ * @see Plane
  */
 public class LinearTransformBuilder {
     /**
-     * The arrays of source ordinate values, for example (x[], y[], z[]).
+     * The arrays of source ordinate values, for example (x[], y[]).
      * This is {@code null} if not yet specified.
      */
     private double[][] sources;
@@ -95,15 +103,19 @@ public class LinearTransformBuilder {
     /**
      * Sets the source points. The number of points shall be the same than the number of target points.
      *
-     * <p><b>Limitation:</b> in current implementation, the source points must be two-dimensional.
-     * But this restriction may be removed in a future SIS version.</p>
+     * <p><b>Limitation:</b> in current implementation, the source points must be one or two-dimensional.
+     * This restriction may be removed in a future SIS version.</p>
      *
-     * @param  points The source points.
+     * @param  points The source points, assumed precise.
      * @throws MismatchedDimensionException if at least one point does not have the expected number of dimensions.
      */
     public void setSourcePoints(final DirectPosition... points) throws MismatchedDimensionException {
         ArgumentChecks.ensureNonNull("points", points);
-        sources = toArrays(points, 2);
+        if (points.length != 0) {
+            sources = toArrays(points, points[0].getDimension() == 1 ? 1 : 2);
+        } else {
+            sources = null;
+        }
         correlation = null;
     }
 
@@ -112,7 +124,7 @@ public class LinearTransformBuilder {
      * Target points can have any number of dimensions (not necessarily 2), but all points shall have
      * the same number of dimensions.
      *
-     * @param  points The target points.
+     * @param  points The target points, assumed uncertain.
      * @throws MismatchedDimensionException if not all points have the same number of dimensions.
      */
     public void setTargetPoints(final DirectPosition... points) throws MismatchedDimensionException {
@@ -125,8 +137,14 @@ public class LinearTransformBuilder {
         correlation = null;
     }
 
+    /*
+     * No getters yet because we did not determined what they should return.
+     * Array? Collection? Map<source,target>?
+     */
+
     /**
-     * Creates a linear transform from the source and target points.
+     * Creates a linear transform approximation from the source points to the target points.
+     * This method assumes that source points are precise and all uncertainty is in the target points.
      *
      * @return The fitted linear transform.
      */
@@ -139,16 +157,64 @@ public class LinearTransformBuilder {
         }
         final int sourceDim = sources.length;
         final int targetDim = targets.length;
-        final MatrixSIS matrix = Matrices.createZero(targetDim + 1, sourceDim + 1);
-        final Plane plan = new Plane();
         correlation = new double[targetDim];
-        for (int j=0; j<targets.length; j++) {
-            correlation[j] = plan.fit(sources[0], sources[1], targets[j]);
-            matrix.setElement(j, 0, plan.cx);
-            matrix.setElement(j, 1, plan.cy);
-            matrix.setElement(j, 2, plan.c);
-        }
+        final MatrixSIS matrix = Matrices.createZero(targetDim + 1, sourceDim + 1);
         matrix.setElement(targetDim, sourceDim, 1);
+        switch (sourceDim) {
+            case 1: {
+                final Line line = new Line();
+                for (int j=0; j<targets.length; j++) {
+                    correlation[j] = line.fit(sources[0], targets[j]);
+                    matrix.setElement(j, 0, line.slope());
+                    matrix.setElement(j, 1, line.y0());
+                }
+                break;
+            }
+            case 2: {
+                final Plane plan = new Plane();
+                for (int j=0; j<targets.length; j++) {
+                    correlation[j] = plan.fit(sources[0], sources[1], targets[j]);
+                    matrix.setElement(j, 0, plan.slopeX());
+                    matrix.setElement(j, 1, plan.slopeY());
+                    matrix.setElement(j, 2, plan.z0());
+                }
+                break;
+            }
+            default: throw new AssertionError(sourceDim); // Should have been verified by setSourcePoints(…) method.
+        }
         return MathTransforms.linear(matrix);
     }
+
+    /**
+     * Returns the correlation coefficients of the last transform created by {@link #create()},
+     * or {@code null} if none. If non-null, the array length is equals to the number of target
+     * dimensions.
+     *
+     * @return Estimation of correlation coefficients for each target dimension, or {@code null}.
+     */
+    public double[] correlation() {
+        return (correlation != null) ? correlation.clone() : null;
+    }
+
+    /**
+     * Returns a string representation of this builder for debugging purpose.
+     *
+     * @return A string representation of this builder.
+     */
+    @Debug
+    @Override
+    public String toString() {
+        final StringBuilder buffer = new StringBuilder(Classes.getShortClassName(this)).append('[');
+        if (sources != null) {
+            buffer.append(sources[0].length).append(" points");
+            if (correlation != null) {
+                String separator = ", correlation is ";
+                for (final double c : correlation) {
+                    buffer.append(separator).append((float) c);
+                    separator = ", ";
+                }
+            }
+        }
+        return buffer.append(']').toString();
+    }
 }

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java?rev=1654425&r1=1654424&r2=1654425&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java [UTF-8] Fri Jan 23 22:50:08 2015
@@ -61,6 +61,10 @@ class GeneralMatrix extends MatrixSIS im
      * rounding errors anyway. So we put the threshold to some arbitrary mid-value, which may change in any future
      * SIS version according experience gained. As long as the value is smaller than 1, it still more accurate than
      * {@code double} arithmetic anyway.</p>
+     *
+     * <div class="note"><b>Note:</b>
+     * A similar constant exists in {@code org.apache.sis.math.Plane}.
+     * </div>
      */
     private static final double ZERO_THRESHOLD = 1E-14;
 

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform.java?rev=1654425&r1=1654424&r2=1654425&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform.java [UTF-8] Fri Jan 23 22:50:08 2015
@@ -64,6 +64,8 @@ import org.opengis.referencing.operation
  * @version 0.4
  * @module
  *
+ * @see org.apache.sis.referencing.operation.transform.MathTransforms#linear(Matrix)
+ * @see org.apache.sis.referencing.operation.builder.LinearTransformBuilder
  * @see java.awt.geom.AffineTransform
  * @see <a href="http://mathworld.wolfram.com/AffineTransformation.html">Affine transformation on MathWorld</a>
  */

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java?rev=1654425&r1=1654424&r2=1654425&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java [UTF-8] Fri Jan 23 22:50:08 2015
@@ -108,6 +108,7 @@ public final class MathTransforms extend
      * @param  matrix The matrix used to define the linear transform.
      * @return The linear (usually affine) transform.
      *
+     * @see #getMatrix(MathTransform)
      * @see org.opengis.referencing.operation.MathTransformFactory#createAffineTransform(Matrix)
      */
     public static LinearTransform linear(final Matrix matrix) {
@@ -296,6 +297,9 @@ public final class MathTransforms extend
      *
      * @param  transform The transform for which to get the matrix, or {@code null}.
      * @return The matrix of the given transform, or {@code null} if none.
+     *
+     * @see #linear(Matrix)
+     * @see LinearTransform#getMatrix()
      */
     public static Matrix getMatrix(final MathTransform transform) {
         if (transform instanceof LinearTransform) {

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilderTest.java?rev=1654425&r1=1654424&r2=1654425&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilderTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilderTest.java [UTF-8] Fri Jan 23 22:50:08 2015
@@ -16,8 +16,13 @@
  */
 package org.apache.sis.referencing.operation.builder;
 
+import java.util.Random;
+import java.awt.geom.AffineTransform;
 import org.opengis.referencing.operation.Matrix;
+import org.apache.sis.geometry.DirectPosition1D;
 import org.apache.sis.geometry.DirectPosition2D;
+import org.apache.sis.test.DependsOnMethod;
+import org.apache.sis.test.TestUtilities;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
@@ -34,10 +39,35 @@ import static org.junit.Assert.*;
  */
 public final strictfp class LinearTransformBuilderTest extends TestCase {
     /**
+     * Tolerance threshold for strict floating point comparisons. This is used when 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 {@code double} type since the implementation use double-double arithmetic.
+     */
+    private static final double STRICT = 0;
+
+    /**
      * Tests a very simple case where an exact answer is expected.
      */
     @Test
-    public void testExact() {
+    public void testMinimalist1D() {
+        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();
+        assertEquals("m₀₀",  2, m.getElement(0, 0), STRICT);
+        assertEquals("m₀₁", -1, m.getElement(0, 1), STRICT);
+        assertArrayEquals("correlation", new double[] {1}, builder.correlation(), STRICT);
+    }
+
+    /**
+     * Tests a very simple case where an exact answer is expected.
+     */
+    @Test
+    public void testMinimalist2D() {
         final LinearTransformBuilder builder = new LinearTransformBuilder();
         builder.setSourcePoints(
                 new DirectPosition2D(1, 1),
@@ -50,13 +80,159 @@ public final strictfp class LinearTransf
         final Matrix m = builder.create().getMatrix();
 
         // First row (x)
-        assertEquals( 2, m.getElement(0, 0), 0);
-        assertEquals( 0, m.getElement(0, 1), 1E-20);
-        assertEquals( 1, m.getElement(0, 2), 0);
+        assertEquals("m₀₀",  2, m.getElement(0, 0), STRICT);
+        assertEquals("m₀₁",  0, m.getElement(0, 1), STRICT);
+        assertEquals("m₀₂",  1, m.getElement(0, 2), STRICT);
 
         // Second row (y)
-        assertEquals( 0, m.getElement(1, 0), 0);
-        assertEquals( 3, m.getElement(1, 1), 0);
-        assertEquals(-1, m.getElement(1, 2), 0);
+        assertEquals("m₁₀",  0, m.getElement(1, 0), STRICT);
+        assertEquals("m₁₁",  3, m.getElement(1, 1), STRICT);
+        assertEquals("m₁₂", -1, m.getElement(1, 2), STRICT);
+
+        assertArrayEquals("correlation", new double[] {1, 1}, builder.correlation(), STRICT);
+    }
+
+    /**
+     * Tests with a random number of points with an exact solution expected.
+     */
+    @Test
+    @DependsOnMethod("testMinimalist1D")
+    public void testExact1D() {
+        final Random rd = TestUtilities.createRandomNumberGenerator(-6080923837183751016L);
+        for (int i=0; i<10; i++) {
+            test1D(rd, rd.nextInt(900) + 100, false, 1E-14, 1E-12);
+        }
+    }
+
+    /**
+     * Tests with a random number of points with an exact solution expected.
+     *
+     * <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>
+     */
+    @Test
+    @DependsOnMethod("testMinimalist2D")
+    public void testExact2D() {
+        final Random rd = TestUtilities.createRandomNumberGenerator(41632405806929L);
+        for (int i=0; i<10; i++) {
+            test2D(rd, rd.nextInt(900) + 100, false, 1E-14, 1E-12);
+        }
+    }
+
+    /**
+     * Tests with a random number of points and a random errors in target points.
+     */
+    @Test
+    @DependsOnMethod("testExact1D")
+    public void testNonExact1D() {
+        final Random rd = TestUtilities.createRandomNumberGenerator(8819436190826166876L);
+        for (int i=0; i<4; i++) {
+            test1D(rd, rd.nextInt(900) + 100, true, 0.02, 0.5);
+        }
+    }
+
+    /**
+     * Tests with a random number of points and a random errors in target points.
+     *
+     * <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, or that lot
+     * 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>
+     */
+    @Test
+    @DependsOnMethod("testExact2D")
+    public void testNonExact2D() {
+        final Random rd = TestUtilities.createRandomNumberGenerator(270575025643864L);
+        for (int i=0; i<4; i++) {
+            test2D(rd, rd.nextInt(900) + 100, true, 0.02, 0.5);
+        }
+    }
+
+    /**
+     * Implementation of {@link #testExact1D()} and {@link #testNonExact1D()}.
+     *
+     * @param rd        The random number generator to use.
+     * @param numPts    The number of points to generate.
+     * @param addErrors {@code true} for adding a random error in the target points.
+     * @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 scale  = rd.nextDouble() * 30 - 12;
+        final double offset = rd.nextDouble() * 10 - 4;
+        final DirectPosition1D[] sources = new DirectPosition1D[numPts];
+        final DirectPosition1D[] targets = new DirectPosition1D[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;
+        }
+        /*
+         * Create the fitted transform to test.
+         */
+        final LinearTransformBuilder builder = new LinearTransformBuilder();
+        builder.setSourcePoints(sources);
+        builder.setTargetPoints(targets);
+        final Matrix m = builder.create().getMatrix();
+        assertEquals("m₀₀", scale,  m.getElement(0, 0), scaleTolerance);
+        assertEquals("m₀₁", offset, m.getElement(0, 1), translationTolerance);
+        assertEquals("correlation", 1, Math.abs(builder.correlation()[0]), scaleTolerance);
+    }
+
+    /**
+     * Implementation of {@link #testExact2D()} and {@link #testNonExact2D()}.
+     *
+     * @param rd        The random number generator to use.
+     * @param numPts    The number of points to generate.
+     * @param addErrors {@code true} for adding a random error in the target points.
+     * @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)
+    {
+        /*
+         * Create an AffineTransform to use as the reference implementation.
+         */
+        final AffineTransform ref = AffineTransform.getRotateInstance(
+                rd.nextDouble() * (2 * Math.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];
+        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();
+            assertSame(tgt, ref.transform(src, tgt));
+            if (addErrors) {
+                tgt.x += rd.nextDouble() * 10 - 5;
+                tgt.y += rd.nextDouble() * 10 - 5;
+            }
+            sources[i] = src;
+            targets[i] = tgt;
+        }
+        /*
+         * Create the fitted transform to test.
+         */
+        final LinearTransformBuilder builder = new LinearTransformBuilder();
+        builder.setSourcePoints(sources);
+        builder.setTargetPoints(targets);
+        final Matrix m = builder.create().getMatrix();
+        /*
+         * Compare the coefficients with the reference implementation.
+         */
+        assertEquals("m₀₀", ref.getScaleX(),     m.getElement(0, 0), scaleTolerance);
+        assertEquals("m₀₁", ref.getShearX(),     m.getElement(0, 1), scaleTolerance);
+        assertEquals("m₀₂", ref.getTranslateX(), m.getElement(0, 2), translationTolerance);
+        assertEquals("m₁₀", ref.getShearY(),     m.getElement(1, 0), scaleTolerance);
+        assertEquals("m₁₁", ref.getScaleY(),     m.getElement(1, 1), scaleTolerance);
+        assertEquals("m₁₂", ref.getTranslateY(), m.getElement(1, 2), translationTolerance);
+        assertArrayEquals("correlation", new double[] {1, 1}, builder.correlation(), scaleTolerance);
     }
 }

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/CompoundDirectPositions.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/CompoundDirectPositions.java?rev=1654425&r1=1654424&r2=1654425&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/CompoundDirectPositions.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/CompoundDirectPositions.java [UTF-8] Fri Jan 23 22:50:08 2015
@@ -114,7 +114,7 @@ final class CompoundDirectPositions impl
      * @return Always {@code null}.
      */
     @Override
-    public final CoordinateReferenceSystem getCoordinateReferenceSystem() {
+    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
         return null;
     }
 
@@ -122,7 +122,7 @@ final class CompoundDirectPositions impl
      * Returns the number of dimensions.
      */
     @Override
-    public final int getDimension() {
+    public int getDimension() {
         return ordinates.length;
     }
 
@@ -138,7 +138,7 @@ final class CompoundDirectPositions impl
      * Not needed.
      */
     @Override
-    public final double[] getCoordinate() {
+    public double[] getCoordinate() {
         throw new UnsupportedOperationException();
     }
 

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java?rev=1654425&r1=1654424&r2=1654425&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java [UTF-8] Fri Jan 23 22:50:08 2015
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.math;
 
-import java.util.Arrays;
 import java.io.Serializable;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.MismatchedDimensionException;
@@ -24,13 +23,19 @@ import org.apache.sis.internal.util.Doub
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.util.resources.Errors;
 
+import static java.lang.Math.abs;
+import static java.lang.Math.sqrt;
+import static java.lang.Math.ulp;
+
 
 /**
  * Equation of a plane in a three-dimensional space (<var>x</var>,<var>y</var>,<var>z</var>).
- * The plane equation is expressed by {@link #c}, {@link #cx} and {@link #cy} coefficients as below:
+ * The plane equation is expressed by {@linkplain #slopeX() sx}, {@linkplain #slopeY() sy} and
+ * {@linkplain #z0() z₀} coefficients as below:
  *
  * <blockquote>
- *   <var>z</var>(<var>x</var>,<var>y</var>) = <var>c</var> + <var>cx</var>⋅<var>x</var> + <var>cy</var>⋅<var>y</var>
+ *   <var>{@linkplain #z(double, double) z}</var>(<var>x</var>,<var>y</var>) =
+ *   <var>sx</var>⋅<var>x</var> + <var>sy</var>⋅<var>y</var> + <var>z</var>₀
  * </blockquote>
  *
  * Those coefficients can be set directly, or computed by a linear regression of this plane
@@ -41,6 +46,9 @@ import org.apache.sis.util.resources.Err
  * @since   0.5 (derived from geotk-1.0)
  * @version 0.5
  * @module
+ *
+ * @see Line
+ * @see org.apache.sis.referencing.operation.builder.LinearTransformBuilder
  */
 public class Plane implements Cloneable, Serializable {
     /**
@@ -49,77 +57,167 @@ public class Plane implements Cloneable,
     private static final long serialVersionUID = 2956201711131316723L;
 
     /**
-     * The <var>c</var> coefficient for this plane. This coefficient appears in the plane equation
-     * <var><strong>c</strong></var>+<var>cx</var>⋅<var>x</var>+<var>cy</var>⋅<var>y</var>.
+     * Number of dimensions.
+     */
+    private static final int DIMENSION = 3;
+
+    /**
+     * Threshold value relative to 1 ULP of other terms in the  z = sx⋅x + sy⋅y + z₀  equation.
+     * A value of 1 would be theoretically sufficient since adding a value smaller to 1 ULP to
+     * a {@code double} has no effect. Nevertheless we use a smaller value as a safety because:
+     *
+     * <ul>
+     *   <li>We perform our checks using the points given by the user, but we don't know how
+     *       representative those points are.</li>
+     *   <li>We perform our checks using an approximation which may result in slightly different
+     *       decisions (false positives) than what we would got by a more robust check.</li>
+     * </ul>
+     *
+     * This arbitrary threshold value may change in any future SIS version according experience gained.
+     *
+     * <div class="note"><b>Note:</b>
+     * A similar constant exists in {@code org.apache.sis.referencing.operation.matrix.GeneralMatrix}.
+     * </div>
+     */
+    private static final double ZERO_THRESHOLD = 1E-14;
+
+    /**
+     * The slope along the <var>x</var> values. This coefficient appears in the plane equation
+     * <var><b><u>sx</u></b></var>⋅<var>x</var> + <var>sy</var>⋅<var>y</var> + <var>z</var>₀.
      */
-    public double c;
+    private double sx;
 
     /**
-     * The <var>cx</var> coefficient for this plane. This coefficient appears in the plane equation
-     * <var>c</var>+<var><strong>cx</strong></var>⋅<var>x</var>+<var>cy</var>⋅<var>y</var>.
+     * The slope along the <var>y</var> values. This coefficient appears in the plane equation
+     * <var>sx</var>⋅<var>x</var> + <var><b><u>sy</u></b></var>⋅<var>y</var> + <var>z</var>₀.
      */
-    public double cx;
+    private double sy;
 
     /**
-     * The <var>cy</var> coefficient for this plane. This coefficient appears in the place equation
-     * <var>c</var>+<var>cx</var>⋅<var>x</var>+<var><strong>cy</strong></var>⋅<var>y</var>.
+     * The <var>z</var> value at (<var>x</var>,<var>y</var>) = (0,0). This coefficient appears in the plane equation
+     * <var>sx</var>⋅<var>x</var> + <var>sy</var>⋅<var>y</var> + <b><u><var>z</var>₀</u></b>.
      */
-    public double cy;
+    private double z0;
 
     /**
-     * Construct a new plane. All coefficients are set to 0.
+     * Constructs a new plane with all coefficients initialized to {@link Double#NaN}.
      */
     public Plane() {
+        sx = sy = z0 = Double.NaN;
     }
 
     /**
-     * Computes the <var>z</var> value for the specified (<var>x</var>,<var>y</var>) point.
-     * The <var>z</var> value is computed using the following equation:
+     * Constructs a new plane initialized to the given coefficients.
      *
-     * <blockquote>z(x,y) = {@linkplain #c} + {@linkplain #cx}⋅x + {@linkplain #cy}⋅y</blockquote>
+     * @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).
      *
-     * @param x The <var>x</var> value.
-     * @param y The <var>y</var> value.
-     * @return  The <var>z</var> value.
+     * @see #setEquation(double, double, double)
      */
-    public final double z(final double x, final double y) {
-        return c + cx*x + cy*y;
+    public Plane(final double sx, final double sy, final double z0) {
+        this.sx = sx;
+        this.sy = sy;
+        this.z0 = z0;
     }
 
     /**
-     * Computes the <var>y</var> value for the specified (<var>x</var>,<var>z</var>) point.
-     * The <var>y</var> value is computed using the following equation:
+     * Returns the slope along the <var>x</var> values. This coefficient appears in the plane equation
+     * <var><b><u>sx</u></b></var>⋅<var>x</var> + <var>sy</var>⋅<var>y</var> + <var>z</var>₀.
      *
-     * <blockquote>y(x,z) = (z - ({@linkplain #c} + {@linkplain #cx}⋅x)) / {@linkplain #cy}</blockquote>
+     * @return The <var>sx</var> term.
+     */
+    public final double slopeX() {
+        return sx;
+    }
+
+    /**
+     * Returns the slope along the <var>y</var> values. This coefficient appears in the plane equation
+     * <var>sx</var>⋅<var>x</var> + <var><b><u>sy</u></b></var>⋅<var>y</var> + <var>z</var>₀.
      *
-     * @param x The <var>x</var> value.
-     * @param z The <var>y</var> value.
-     * @return  The <var>y</var> value.
+     * @return The <var>sy</var> term.
      */
-    public final double y(final double x, final double z) {
-        return (z - (c + cx*x)) / cy;
+    public final double slopeY() {
+        return sy;
+    }
+
+    /**
+     * Returns the <var>z</var> value at (<var>x</var>,<var>y</var>) = (0,0). This coefficient appears in the
+     * plane equation <var>sx</var>⋅<var>x</var> + <var>sy</var>⋅<var>y</var> + <b><var>z</var>₀</b>.
+     *
+     * @return The <var>z</var>₀ term.
+     *
+     * @see #z(double, double)
+     */
+    public final double z0() {
+        return z0;
     }
 
     /**
      * Computes the <var>x</var> value for the specified (<var>y</var>,<var>z</var>) point.
      * The <var>x</var> value is computed using the following equation:
      *
-     * <blockquote>x(y,z) = (z - ({@linkplain #c} + {@linkplain #cy}⋅y)) / {@linkplain #cx}</blockquote>
+     * <blockquote>x(y,z) = (z - ({@linkplain #z0() z₀} + {@linkplain #slopeY() sy}⋅y)) / {@linkplain #slopeX() sx}</blockquote>
      *
-     * @param y The <var>x</var> value.
-     * @param z The <var>y</var> value.
+     * @param y The <var>y</var> value where to compute <var>x</var>.
+     * @param z The <var>z</var> value where to compute <var>x</var>.
      * @return  The <var>x</var> value.
      */
     public final double x(final double y, final double z) {
-        return (z - (c + cy*y)) / cx;
+        return (z - (z0 + sy*y)) / sx;
+    }
+
+    /**
+     * Computes the <var>y</var> value for the specified (<var>x</var>,<var>z</var>) point.
+     * The <var>y</var> value is computed using the following equation:
+     *
+     * <blockquote>y(x,z) = (z - ({@linkplain #z0() z₀} + {@linkplain #slopeX() sx}⋅x)) / {@linkplain #slopeY() sy}</blockquote>
+     *
+     * @param x The <var>x</var> value where to compute <var>y</var>.
+     * @param z The <var>z</var> value where to compute <var>y</var>.
+     * @return  The <var>y</var> value.
+     */
+    public final double y(final double x, final double z) {
+        return (z - (z0 + sx*x)) / sy;
+    }
+
+    /**
+     * Computes the <var>z</var> value for the specified (<var>x</var>,<var>y</var>) point.
+     * The <var>z</var> value is computed using the following equation:
+     *
+     * <blockquote>z(x,y) = {@linkplain #z0() z₀} + {@linkplain #slopeX() sx}⋅x + {@linkplain #slopeY() sy}⋅y</blockquote>
+     *
+     * @param x The <var>x</var> value where to compute <var>z</var>.
+     * @param y The <var>y</var> value where to compute <var>z</var>.
+     * @return  The <var>z</var> value.
+     *
+     * @see #z0()
+     */
+    public final double z(final double x, final double y) {
+        return z0 + sx*x + sy*y;
+    }
+
+    /**
+     * Sets the equation of this plane to the given coefficients.
+     *
+     * @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).
+     */
+    public void setEquation(final double sx, final double sy, final double z0) {
+        this.sx = sx;
+        this.sy = sy;
+        this.z0 = z0;
     }
 
     /**
      * Computes the plane's coefficients from the given ordinate values.
-     * This method uses a linear regression in the least-square sense.
-     * {@link Double#NaN} values are ignored.
+     * 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>The result is undetermined if all points are colinear.</p>
+     * <p>{@link Double#NaN} values are ignored.
+     * The result is undetermined if all points are colinear.</p>
      *
      * @param  x vector of <var>x</var> coordinates.
      * @param  y vector of <var>y</var> coordinates.
@@ -128,31 +226,37 @@ 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) {
-        return fit(new CompoundDirectPositions(x, y, z));
+        // Do not invoke an overrideable method with our tricky iterable.
+        return fit(new CompoundDirectPositions(x, y, z), true, true, true);
     }
 
     /**
      * Computes the plane's coefficients from the given sequence of points.
-     * This method uses a linear regression in the least-square sense.
-     * 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.
+     * 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>The result is undetermined if all points are colinear.</p>
+     * <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>
      *
-     * @param  points The three dimensional points.
+     * @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 DirectPosition... points) throws MismatchedDimensionException {
-        return fit(Arrays.asList(points));
+    public double fit(final Iterable<? extends DirectPosition> points) {
+        return fit(points, true, true, true);
     }
 
     /**
      * Implementation of public {@code fit(…)} methods.
      * This method 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) {
+    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();
@@ -169,22 +273,22 @@ public class Plane implements Cloneable,
         final DoubleDouble zy     = new DoubleDouble();
         for (final DirectPosition p : points) {
             final int dimension = p.getDimension();
-            if (dimension != 3) {
+            if (dimension != DIMENSION) {
                 throw new MismatchedDimensionException(Errors.format(
-                        Errors.Keys.MismatchedDimension_3, "positions[" + i + ']', 3, dimension));
+                        Errors.Keys.MismatchedDimension_3, "points[" + i + ']', DIMENSION, dimension));
             }
             i++;
-            final double xi = p.getOrdinate(0); if (Double.isNaN(xi)) continue;
-            final double yi = p.getOrdinate(1); if (Double.isNaN(yi)) continue;
-            final double zi = p.getOrdinate(2); if (Double.isNaN(zi)) continue;
-            xx.setToProduct(xi, xi);
-            yy.setToProduct(yi, yi);
-            xy.setToProduct(xi, yi);
-            zx.setToProduct(zi, xi);
-            zy.setToProduct(zi, yi);
-            sum_x.add(xi);
-            sum_y.add(yi);
-            sum_z.add(zi);
+            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);
@@ -193,8 +297,8 @@ public class Plane implements Cloneable,
             n++;
         }
         /*
-         *    ( sum_zx - sum_z*sum_x )  =  cx*(sum_xx - sum_x*sum_x) + cy*(sum_xy - sum_x*sum_y)
-         *    ( sum_zy - sum_z*sum_y )  =  cx*(sum_xy - sum_x*sum_y) + cy*(sum_yy - sum_y*sum_y)
+         *    ( 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
@@ -208,26 +312,20 @@ public class Plane implements Cloneable,
         final DoubleDouble den = new DoubleDouble(xy); den.multiply(xy);
         den.subtract(tmp);
         /*
-         * cx = (zy*xy - zx*yy) / den
-         * cy = (zx*xy - zy*xx) / den
-         * c  = (sum_z - (cx*sum_x + cy*sum_y)) / n
+         * sx = (zy*xy - zx*yy) / den
+         * sy = (zx*xy - zy*xx) / den
+         * z₀ = (sum_z - (sx*sum_x + sy*sum_y)) / n
          */
-        final DoubleDouble cx = new DoubleDouble(zy); cx.multiply(xy); tmp.setFrom(zx); tmp.multiply(yy); cx.subtract(tmp); cx.divide(den);
-        final DoubleDouble cy = new DoubleDouble(zx); cy.multiply(xy); tmp.setFrom(zy); tmp.multiply(xx); cy.subtract(tmp); cy.divide(den);
-        final DoubleDouble c  = new DoubleDouble(cy);
-        c.multiply(sum_y);
-        tmp.setFrom(cx);
+        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(c);
-        c.setFrom(sum_z);
-        c.subtract(tmp);
-        c.divide(n, 0);
-        /*
-         * Done - store the result.
-         */
-        this.c  = c .value;
-        this.cx = cx.value;
-        this.cy = cy.value;
+        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
@@ -239,51 +337,57 @@ public class Plane implements Cloneable,
         final double mean_x = sum_x.value / n;
         final double mean_y = sum_y.value / n;
         final double mean_z = sum_z.value / n;
-        double sx=0, sy=0, pe=0;
+        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 xi = p.getOrdinate(2) - mean_z;
-            if (!Double.isNaN(xi)) {
-                final double yi = cx.value * (p.getOrdinate(0) - mean_x)
-                                + cy.value * (p.getOrdinate(1) - mean_y);
-                if (!Double.isNaN(yi)) {
-                    sx += xi * xi;
-                    sy += yi * yi;
-                    pe += xi * yi;
-                }
+            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;
             }
+            /*
+             * 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;
         }
-        return pe / Math.sqrt(sx * sy);
+        /*
+         * 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(detectZeroSx ? 0 : sx.value,
+                    detectZeroSy ? 0 : sy.value,
+                    detectZeroZ0 ? 0 : z0.value);
+        return sum_dsz / sqrt(sum_ds2 * sum_dz2);
     }
 
     /**
-     * Returns a string representation of this plane.
-     * The string will contains the plane's equation, as below:
+     * Returns a clone of this plane.
      *
-     * <blockquote>
-     *     <var>z</var>(<var>x</var>,<var>y</var>) = {@link #c} +
-     *     {@link #cx}⋅<var>x</var> + {@link #cy}⋅<var>y</var>
-     * </blockquote>
+     * @return A clone of this plane.
      */
     @Override
-    public String toString() {
-        final StringBuilder buffer = new StringBuilder("z(x,y)= ");
-        if (c == 0 && cx == 0 && cy == 0) {
-            buffer.append(0);
-        } else {
-            if (c != 0) {
-                buffer.append(c).append(" + ");
-            }
-            if (cx != 0) {
-                buffer.append(cx).append("⋅x");
-                if (cy != 0) {
-                    buffer.append(" + ");
-                }
-            }
-            if (cy != 0) {
-                buffer.append(cy).append("⋅y");
-            }
+    public Plane clone() {
+        try {
+            return (Plane) super.clone();
+        } catch (CloneNotSupportedException exception) {
+            throw new AssertionError(exception);
         }
-        return buffer.toString();
     }
 
     /**
@@ -296,9 +400,9 @@ public class Plane implements Cloneable,
     public boolean equals(final Object object) {
         if (object != null && getClass() == object.getClass()) {
             final Plane that = (Plane) object;
-            return Numerics.equals(this.c,  that.c ) &&
-                   Numerics.equals(this.cx, that.cx) &&
-                   Numerics.equals(this.cy, that.cy);
+            return Numerics.equals(this.z0,  that.z0 ) &&
+                   Numerics.equals(this.sx, that.sx) &&
+                   Numerics.equals(this.sy, that.sy);
         } else {
             return false;
         }
@@ -310,22 +414,32 @@ public class Plane implements Cloneable,
     @Override
     public int hashCode() {
         return Numerics.hashCode(serialVersionUID
-                     ^ (Double.doubleToLongBits(c )
-                + 31 * (Double.doubleToLongBits(cx)
-                + 31 * (Double.doubleToLongBits(cy)))));
+                     ^ (Double.doubleToLongBits(z0)
+                + 31 * (Double.doubleToLongBits(sx)
+                + 31 * (Double.doubleToLongBits(sy)))));
     }
 
     /**
-     * Returns a clone of this plane.
+     * Returns a string representation of this plane.
+     * The string will contains the plane's equation, as below:
      *
-     * @return A clone of this plane.
+     * <blockquote>
+     *     <var>z</var>(<var>x</var>,<var>y</var>) = {@linkplain #slopeX() sx}⋅<var>x</var>
+     *     + {@linkplain #slopeY() sy}⋅<var>y</var> + {@linkplain #z0() z₀}
+     * </blockquote>
      */
     @Override
-    public Plane clone() {
-        try {
-            return (Plane) super.clone();
-        } catch (CloneNotSupportedException exception) {
-            throw new AssertionError(exception);
+    public String toString() {
+        final StringBuilder buffer = new StringBuilder(60).append("z(x,y) = ");
+        String separator = "";
+        if (sx != 0) {
+            buffer.append(sx).append("⋅x");
+            separator = " + ";
+        }
+        if (sy != 0) {
+            buffer.append(separator).append(sy).append("⋅y");
+            separator = " + ";
         }
+        return buffer.append(separator).append(z0).toString();
     }
 }

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/package-info.java?rev=1654425&r1=1654424&r2=1654425&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/package-info.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/package-info.java [UTF-8] Fri Jan 23 22:50:08 2015
@@ -17,10 +17,24 @@
 
 /**
  * A set of mathematical objects and algebraic utilities.
+ * This package provides:
+ *
+ * <ul>
+ *   <li>Static functions in {@link org.apache.sis.math.MathFunctions} and {@link org.apache.sis.math.DecimalFunctions}.</li>
+ *   <li>{@link org.apache.sis.math.Statistics} accumulator, optionally with statistics on derivatives
+ *       and {@linkplain org.apache.sis.math.StatisticsFormat tabular formatting}.</li>
+ *   <li>Simple equations for {@link org.apache.sis.math.Line} and {@link org.apache.sis.math.Plane}
+ *       with capability to determine the coefficients from a set of coordinates.</li>
+ * </ul>
+ *
+ * <div class="note"><b>Note:</b>
+ * {@code Line} and {@code Plane} classes are not geometric objects since they are not bounded in space.
+ * For example the {@link java.awt.geom.Line2D} geometry have starting and ending points, while the
+ * {@code Line} class in this package extends to infinite.</div>
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-2.0)
- * @version 0.3
+ * @version 0.5
  * @module
  */
 package org.apache.sis.math;

Modified: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/PlaneTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/PlaneTest.java?rev=1654425&r1=1654424&r2=1654425&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/PlaneTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/PlaneTest.java [UTF-8] Fri Jan 23 22:50:08 2015
@@ -97,18 +97,15 @@ public final strictfp class PlaneTest ex
         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();
-        plan.cx =  2;
-        plan.cy =  3;
-        plan.c  = -4;
+        final Plane plan = 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);
         }
         final Plane fitted = assertFitEquals(6, x, y, z);
-        assertEquals("cx", plan.cx, fitted.cx, 0.01);
-        assertEquals("cy", plan.cy, fitted.cy, 0.01);
-        assertEquals("c",  plan.c,  fitted.c,  1);
+        assertEquals("sx", plan.slopeX(), fitted.slopeX(), 0.01);
+        assertEquals("sy", plan.slopeY(), fitted.slopeY(), 0.01);
+        assertEquals("z₀", plan.z0(),     fitted.z0(),     1);
     }
 
     /**
@@ -116,10 +113,7 @@ public final strictfp class PlaneTest ex
      */
     @Test
     public void testSerialization() {
-        final Plane local = new Plane();
-        local.c  =  3.7;
-        local.cx =  9.3;
-        local.cy = -1.8;
+        final Plane local = new Plane(3.7, 9.3, -1.8);
         assertNotSame(local, assertSerializedEquals(local));
     }
 }

Modified: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1654425&r1=1654424&r2=1654425&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] Fri Jan 23 22:50:08 2015
@@ -61,6 +61,7 @@ import org.junit.BeforeClass;
     org.apache.sis.math.StatisticsFormatTest.class,
     org.apache.sis.internal.util.UtilitiesTest.class,
     org.apache.sis.internal.util.DoubleDoubleTest.class,
+    org.apache.sis.math.LineTest.class,
     org.apache.sis.math.PlaneTest.class,
 
     // Collections.

Modified: sis/branches/JDK7/src/main/javadoc/stylesheet.css
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/src/main/javadoc/stylesheet.css?rev=1654425&r1=1654424&r2=1654425&view=diff
==============================================================================
--- sis/branches/JDK7/src/main/javadoc/stylesheet.css (original)
+++ sis/branches/JDK7/src/main/javadoc/stylesheet.css Fri Jan 23 22:50:08 2015
@@ -213,3 +213,11 @@ div.block pre, code, tt, dt code, table
   line-height: 1em;
   font-size:   1em;
 }
+
+/* Appareance of links in the "Description" column of class and package summaries.
+ * JDK style uses bold characters for the left column, which contains the class and
+ * package names. But we do not want those bold characters to apply to the descriptions.
+ */
+td.colLast a:link {
+  font-weight: normal;
+}



Mime
View raw message