sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1787578 - in /sis/branches/JDK8/core: sis-referencing/src/main/java/org/apache/sis/internal/referencing/ sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ sis-referencing/src/main/java/org/apache/sis/referencing/d...
Date Sat, 18 Mar 2017 17:21:46 GMT
Author: desruisseaux
Date: Sat Mar 18 17:21:45 2017
New Revision: 1787578

URL: http://svn.apache.org/viewvc?rev=1787578&view=rev
Log:
Initial draft of LocalizationGridBuilder, using DatumShiftGrid is the backing mechanism for applying residuals.

Added:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/ResidualGrid.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/TransformBuilder.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilderTest.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ExtendedPrecisionMatrix.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridCompressed.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedTransform.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilderTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Line.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ExtendedPrecisionMatrix.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ExtendedPrecisionMatrix.java?rev=1787578&r1=1787577&r2=1787578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ExtendedPrecisionMatrix.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ExtendedPrecisionMatrix.java [UTF-8] Sat Mar 18 17:21:45 2017
@@ -25,12 +25,19 @@ import org.opengis.referencing.operation
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.8
  * @module
  */
 public interface ExtendedPrecisionMatrix extends Matrix {
     /**
      * A sentinel value for {@link org.apache.sis.referencing.operation.matrix.Matrices#create(int, int, Number[])}
+     * meaning that we request an extended precision matrix with all elements initialized to zero.
+     * This is a non-public feature because we try to hide our extended-precision mechanism from the users.
+     */
+    Number[] ZERO = new Number[0];
+
+    /**
+     * A sentinel value for {@link org.apache.sis.referencing.operation.matrix.Matrices#create(int, int, Number[])}
      * meaning that we request an extended precision matrix initialized to the identity (or diagonal) matrix.
      * This is a non-public feature because we try to hide our extended-precision mechanism from the users.
      */

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridCompressed.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridCompressed.java?rev=1787578&r1=1787577&r2=1787578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridCompressed.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridCompressed.java [UTF-8] Sat Mar 18 17:21:45 2017
@@ -27,9 +27,9 @@ import org.apache.sis.math.DecimalFuncti
  * increase the precision in the common case where the shifts are specified with no more than
  * 5 digits in base 10 in ASCII files.
  *
- * @param <C> Dimension of the coordinate unit (usually {@link javax.measure.quantity.Angle}).
- * @param <T> Dimension of the translation unit (usually {@link javax.measure.quantity.Angle}
- *            or {@link javax.measure.quantity.Length}).
+ * @param  <C>  dimension of the coordinate unit (usually {@link javax.measure.quantity.Angle}).
+ * @param  <T>  dimension of the translation unit (usually {@link javax.measure.quantity.Angle}
+ *              or {@link javax.measure.quantity.Length}).
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.7
@@ -123,7 +123,7 @@ final class DatumShiftGridCompressed<C e
      * Returns a new grid with the same geometry than this grid but different data arrays.
      */
     @Override
-    final DatumShiftGridFile<C,T> setData(final Object[] other) {
+    protected final DatumShiftGridFile<C,T> setData(final Object[] other) {
         return new DatumShiftGridCompressed<>(this, averages, (short[][]) other, scale);
     }
 
@@ -140,11 +140,13 @@ final class DatumShiftGridCompressed<C e
     }
 
     /**
-     * Returns direct references (not cloned) to the data arrays.
+     * Returns direct references (not cloned) to the data arrays. This method is for cache management,
+     * {@link #equals(Object)} and {@link #hashCode()} implementations only and should not be invoked
+     * in other context.
      */
     @Override
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
-    final Object[] getData() {
+    protected final Object[] getData() {
         return data;
     }
 

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java?rev=1787578&r1=1787577&r2=1787578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java [UTF-8] Sat Mar 18 17:21:45 2017
@@ -24,28 +24,35 @@ import javax.measure.Quantity;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.parameter.GeneralParameterDescriptor;
+import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.NoninvertibleTransformException;
 import org.apache.sis.math.DecimalFunctions;
 import org.apache.sis.util.collection.Cache;
 import org.apache.sis.util.Debug;
 import org.apache.sis.parameter.Parameters;
 import org.apache.sis.referencing.datum.DatumShiftGrid;
+import org.apache.sis.referencing.operation.transform.LinearTransform;
 import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
+import org.apache.sis.internal.util.Utilities;
 
 
 /**
  * A datum shift grid loaded from a file.
  * The filename is usually a parameter defined in the EPSG database.
+ * This class should not be in public API because it requires implementation to expose internal mechanic:
  *
- * <p>This class is in internal package (not public API) because it makes the following assumptions:</p>
  * <ul>
- *   <li>Values <var>x₀</var>, <var>y₀</var>, <var>Δx</var> and <var>Δy</var>
- *       given to the constructor are in degrees and needs to be converted to radians.</li>
- *   <li>Single floating-point precision ({@code float)} is sufficient.</li>
- *   <li>Values were defined in base 10, usually in ASCII files. This assumption has an impact on conversions
- *       from {@code float} to {@code double} performed by the {@link #getCellValue(int, int, int)} method.</li>
+ *   <li>Subclasses need to give an access to their internal data (not a copy) through the {@link #getData()}
+ *       and {@link #setData(Object[])} methods. We use that for managing the cache, reducing memory usage by
+ *       sharing data and for {@link #equals(Object)} and {@link #hashCode()} implementations.</li>
+ *   <li>{@link #descriptor}, {@link #gridToTarget()} and {@link #setFileParameters(Parameters)} are convenience
+ *       members for {@link org.apache.sis.referencing.operation.transform.InterpolatedTransform} constructor.
+ *       What they do are closely related to how {@code InterpolatedTransform} works, and trying to document that
+ *       in a public API would probably be too distracting for the users.</li>
  * </ul>
  *
+ * The main concrete subclass is {@link DatumShiftGridFile.Float}.
+ *
  * @param  <C>  dimension of the coordinate unit (usually {@link javax.measure.quantity.Angle}).
  * @param  <T>  dimension of the translation unit (usually {@link javax.measure.quantity.Angle}
  *              or {@link javax.measure.quantity.Length}).
@@ -54,6 +61,8 @@ import org.apache.sis.internal.referenci
  * @since   0.7
  * @version 0.8
  * @module
+ *
+ * @see org.apache.sis.referencing.operation.transform.InterpolatedTransform
  */
 public abstract class DatumShiftGridFile<C extends Quantity<C>, T extends Quantity<T>> extends DatumShiftGrid<C,T> {
     /**
@@ -91,7 +100,7 @@ public abstract class DatumShiftGridFile
     /**
      * Number of grid cells along the <var>x</var> axis.
      */
-    final int nx;
+    protected final int nx;
 
     /**
      * The best translation accuracy that we can expect from this file.
@@ -101,7 +110,37 @@ public abstract class DatumShiftGridFile
      *
      * @see #getCellPrecision()
      */
-    double accuracy;
+    protected double accuracy;
+
+    /**
+     * Creates a new datum shift grid for the given grid geometry.
+     * The actual offset values need to be provided by subclasses.
+     *
+     * @param  coordinateUnit    the unit of measurement of input values, before conversion to grid indices by {@code coordinateToGrid}.
+     * @param  translationUnit   the unit of measurement of output values.
+     * @param  isCellValueRatio  {@code true} if results of {@link #interpolateInCell interpolateInCell(…)} are divided by grid cell size.
+     * @param  coordinateToGrid  conversion from the "real world" coordinates to grid indices including fractional parts.
+     * @param  nx                number of cells along the <var>x</var> axis in the grid.
+     * @param  ny                number of cells along the <var>y</var> axis in the grid.
+     * @param  descriptor        the parameter descriptor of the provider that created this grid.
+     * @param  files             the file(s) from which the grid has been loaded.
+     *
+     * @since 0.8
+     */
+    protected DatumShiftGridFile(final Unit<C> coordinateUnit,
+                                 final Unit<T> translationUnit,
+                                 final boolean isCellValueRatio,
+                                 final LinearTransform coordinateToGrid,
+                                 final int nx, final int ny,
+                                 final ParameterDescriptorGroup descriptor,
+                                 final Path... files)
+    {
+        super(coordinateUnit, coordinateToGrid, new int[] {nx, ny}, isCellValueRatio, translationUnit);
+        this.descriptor = descriptor;
+        this.files      = files;
+        this.nx         = nx;
+        this.accuracy   = Double.NaN;
+    }
 
     /**
      * Creates a new datum shift grid for the given grid geometry.
@@ -123,15 +162,8 @@ public abstract class DatumShiftGridFile
                        final ParameterDescriptorGroup descriptor,
                        final Path... files) throws NoninvertibleTransformException
     {
-        super(coordinateUnit, new AffineTransform2D(Δx, 0, 0, Δy, x0, y0).inverse(),
-                new int[] {nx, ny}, isCellValueRatio, translationUnit);
-        this.descriptor = descriptor;
-        this.files      = files;
-        this.nx         = nx;
-        this.accuracy   = Double.NaN;
-        if (files.length == 0) {
-            throw new IllegalArgumentException();
-        }
+        this(coordinateUnit, translationUnit, isCellValueRatio,
+                new AffineTransform2D(Δx, 0, 0, Δy, x0, y0).inverse(), nx, ny, descriptor, files);
     }
 
     /**
@@ -139,7 +171,7 @@ public abstract class DatumShiftGridFile
      *
      * @param  other  the other datum shift grid from which to copy the grid geometry.
      */
-    DatumShiftGridFile(final DatumShiftGridFile<C,T> other) {
+    protected DatumShiftGridFile(final DatumShiftGridFile<C,T> other) {
         super(other);
         descriptor = other.descriptor;
         files      = other.files;
@@ -148,21 +180,29 @@ public abstract class DatumShiftGridFile
     }
 
     /**
-     * Suggests a precision for the translation values in this grid.
-     * The default implementation returns a value smaller than the accuracy.
-     *
-     * @return a precision for the translation values in this grid.
+     * Returns {@code this} casted to the given type, after verification that those types are valid.
+     * This method is invoked after {@link NADCON}, {@link NTv2} or other providers got an existing
+     * {@code DatumShiftGridFile} instance from the {@link #CACHE}.
      */
-    @Override
-    public double getCellPrecision() {
-        return accuracy / 10;   // Division by 10 is arbitrary.
+    @SuppressWarnings("unchecked")
+    final <NC extends Quantity<NC>, NT extends Quantity<NT>> DatumShiftGridFile<NC,NT> castTo(
+            final Class<NC> coordinateType, final Class<NT> translationType)
+    {
+        super.getCoordinateUnit() .asType(coordinateType);
+        super.getTranslationUnit().asType(translationType);
+        return (DatumShiftGridFile<NC,NT>) this;
     }
 
     /**
      * If a grid exists in the cache for the same data, returns a new grid sharing the same data arrays.
      * Otherwise returns {@code this}.
+     *
+     * @return a grid using the same data than this grid, or {@code this}.
+     *
+     * @see #getData()
+     * @see #setData(Object[])
      */
-    final DatumShiftGridFile<C,T> useSharedData() {
+    protected final DatumShiftGridFile<C,T> useSharedData() {
         final Object[] data = getData();
         for (final DatumShiftGridFile<?,?> grid : CACHE.values()) {
             final Object[] other = grid.getData();
@@ -179,13 +219,44 @@ public abstract class DatumShiftGridFile
      * the same data than an existing grid. The typical use case is when a filename is different but still
      * reference the same grid (e.g. symbolic link, lower case versus upper case in a case-insensitive file
      * system).
+     *
+     * @param  other  data from another {@code DatumShiftGridFile} that we can share.
+     * @return a new {@code DatumShiftGridFile} using the given data reference.
      */
-    abstract DatumShiftGridFile<C,T> setData(Object[] other);
+    protected abstract DatumShiftGridFile<C,T> setData(Object[] other);
 
     /**
-     * Returns the data for each shift dimensions.
+     * Returns the data for each shift dimensions. This method is for cache management, {@link #equals(Object)}
+     * and {@link #hashCode()} implementations only and should not be invoked in other context.
+     *
+     * @return a direct (not cloned) reference to the internal data array.
      */
-    abstract Object[] getData();
+    protected abstract Object[] getData();
+
+    /**
+     * Suggests a precision for the translation values in this grid.
+     * The default implementation returns a value smaller than the accuracy.
+     *
+     * @return a precision for the translation values in this grid.
+     */
+    @Override
+    public double getCellPrecision() {
+        return accuracy / 10;   // Division by 10 is arbitrary.
+    }
+
+    /**
+     * Returns the transform from grid coordinates to "real world" coordinates after the datum shift has been applied,
+     * or {@code null} for the default. This is usually the inverse of the transform from "real world" coordinates to
+     * grid coordinates before datum shift, since NADCON and NTv2 transformations have source and target coordinates
+     * in the same coordinate system (with axis units in degrees). But this method may be overridden by subclasses that
+     * use {@code DatumShiftGridFile} for other kind of transformations.
+     *
+     * @return the transformation from grid coordinates to "real world" coordinates after datum shift,
+     *         or {@code null} for the default (namely the inverse of the "source to grid" transformation).
+     */
+    public Matrix gridToTarget() {
+        return null;
+    }
 
     /**
      * Sets all parameters for a value of type {@link Path} to the values given to th constructor.
@@ -193,31 +264,21 @@ public abstract class DatumShiftGridFile
      * @param  parameters  the parameter group where to set the values.
      */
     public final void setFileParameters(final Parameters parameters) {
-        int i = 0;  // The 'files' array should always contains at least one element.
-        for (final GeneralParameterDescriptor gd : descriptor.descriptors()) {
-            if (gd instanceof ParameterDescriptor<?>) {
-                final ParameterDescriptor<?> d = (ParameterDescriptor<?>) gd;
-                if (Path.class.isAssignableFrom(d.getValueClass())) {
-                    parameters.getOrCreate(d).setValue(files[i]);
-                    if (++i == files.length) break;
+        if (files.length != 0) {
+            int i = 0;
+            for (final GeneralParameterDescriptor gd : descriptor.descriptors()) {
+                if (gd instanceof ParameterDescriptor<?>) {
+                    final ParameterDescriptor<?> d = (ParameterDescriptor<?>) gd;
+                    if (Path.class.isAssignableFrom(d.getValueClass())) {
+                        parameters.getOrCreate(d).setValue(files[i]);
+                        if (++i == files.length) break;
+                    }
                 }
             }
         }
     }
 
     /**
-     * Returns {@code this} casted to the given type, after verification that those types are valid.
-     */
-    @SuppressWarnings("unchecked")
-    final <NC extends Quantity<NC>, NT extends Quantity<NT>> DatumShiftGridFile<NC,NT> castTo(
-            final Class<NC> coordinateType, final Class<NT> translationType)
-    {
-        super.getCoordinateUnit() .asType(coordinateType);
-        super.getTranslationUnit().asType(translationType);
-        return (DatumShiftGridFile<NC,NT>) this;
-    }
-
-    /**
      * Returns {@code true} if the given object is a grid containing the same data than this grid.
      *
      * @param  other  the other object to compare with this datum shift grid.
@@ -254,7 +315,7 @@ public abstract class DatumShiftGridFile
     @Debug
     @Override
     public String toString() {
-        return "DatumShiftGrid[\"" + files[0].getFileName() + "\"]";
+        return Utilities.toString(getClass(), "file", (files.length != 0) ? files[0] : null);
     }
 
 
@@ -262,6 +323,14 @@ public abstract class DatumShiftGridFile
 
     /**
      * An implementation of {@link DatumShiftGridFile} which stores the offset values in {@code float[]} arrays.
+     * This class is in internal package (not public API) because it makes the following assumptions:
+     * <ul>
+     *   <li>Values <var>x₀</var>, <var>y₀</var>, <var>Δx</var> and <var>Δy</var>
+     *       given to the constructor are in degrees and needs to be converted to radians.</li>
+     *   <li>Single floating-point precision ({@code float)} is sufficient.</li>
+     *   <li>Values were defined in base 10, usually in ASCII files. This assumption has an impact on conversions
+     *       from {@code float} to {@code double} performed by the {@link #getCellValue(int, int, int)} method.</li>
+     * </ul>
      *
      * @author  Martin Desruisseaux (Geomatys)
      * @since   0.7
@@ -313,16 +382,18 @@ public abstract class DatumShiftGridFile
          * Returns a new grid with the same geometry than this grid but different data arrays.
          */
         @Override
-        final DatumShiftGridFile<C,T> setData(final Object[] other) {
+        protected final DatumShiftGridFile<C,T> setData(final Object[] other) {
             return new Float<>(this, (float[][]) other);
         }
 
         /**
-         * Returns direct references (not cloned) to the data arrays.
+         * Returns direct references (not cloned) to the data arrays. This method is for cache management,
+         * {@link #equals(Object)} and {@link #hashCode()} implementations only and should not be invoked
+         * in other context.
          */
         @Override
         @SuppressWarnings("ReturnOfCollectionOrArrayField")
-        final Object[] getData() {
+        protected final Object[] getData() {
             return offsets;
         }
 

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java?rev=1787578&r1=1787577&r2=1787578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java [UTF-8] Sat Mar 18 17:21:45 2017
@@ -233,9 +233,9 @@ public abstract class DatumShiftGrid<C e
     }
 
     /**
-     * Computes the conversion factors needed by {@link #interpolateAtNormalized(double, double, double[])}.
-     * This method takes only the 2 first dimensions. If a conversion factor can not be computed, then it is
-     * set to NaN.
+     * Computes the conversion factors needed by {@link #interpolateInCell(double, double, double[])}.
+     * This method takes only the 2 first dimensions. If a conversion factor can not be computed,
+     * then it is set to NaN.
      */
     @SuppressWarnings("fallthrough")
     private void computeConversionFactors() {
@@ -308,7 +308,7 @@ public abstract class DatumShiftGrid<C e
      * validity will still be accepted, but the extrapolated results may be very wrong.
      *
      * <p>The unit of measurement for the coordinate values in the returned envelope is
-     * given by {@link #getCoordinateUnit()}. The complete CRS is undefined.</p>
+     * given by {@link #getCoordinateUnit()}. The envelope CRS is undefined.</p>
      *
      * @return the domain covered by this grid.
      * @throws TransformException if an error occurred while computing the envelope.
@@ -327,6 +327,7 @@ public abstract class DatumShiftGrid<C e
      *
      * @return the unit of measurement of input values before conversion to grid indices.
      *
+     * @see #getTranslationUnit()
      * @see org.apache.sis.referencing.operation.AbstractCoordinateOperation#getInterpolationCRS()
      */
     public Unit<C> getCoordinateUnit() {
@@ -339,7 +340,7 @@ public abstract class DatumShiftGrid<C e
      * given by {@link #getCoordinateUnit()}.
      * The output points are grid indices with integer values in the center of grid cells.
      *
-     * <p>This transform is usually two-dimensional and linear, in which case conversions from (<var>x</var>,<var>y</var>)
+     * <p>This transform is usually two-dimensional, in which case conversions from (<var>x</var>,<var>y</var>)
      * coordinates to ({@code gridX}, {@code gridY}) indices can be done with the following formulas:</p>
      * <ul>
      *   <li><var>gridX</var> = (<var>x</var> - <var>x₀</var>) / <var>Δx</var></li>
@@ -416,6 +417,7 @@ public abstract class DatumShiftGrid<C e
      *
      * @return the unit of measurement of output values interpolated by {@code interpolateAt(…)}.
      *
+     * @see #getCoordinateUnit()
      * @see #interpolateAt
      */
     public Unit<T> getTranslationUnit() {
@@ -461,7 +463,7 @@ public abstract class DatumShiftGrid<C e
     /**
      * Interpolates the translation to apply for the given two-dimensional grid indices. The result is stored in
      * the given {@code vector} array, which shall have a length of at least {@link #getTranslationDimensions()}.
-     * The output unit of measurement is the same than the one documented in {@link #getCellValue}.
+     * The output unit of measurement is the same than the one documented in {@link #getCellValue(int, int, int)}.
      *
      * <div class="section">Default implementation</div>
      * The default implementation performs the following steps for each dimension <var>dim</var>,

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java?rev=1787578&r1=1787577&r2=1787578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java [UTF-8] Sat Mar 18 17:21:45 2017
@@ -19,9 +19,12 @@ package org.apache.sis.referencing.opera
 import java.util.Map;
 import java.util.Arrays;
 import java.io.IOException;
+import org.opengis.util.FactoryException;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.MismatchedDimensionException;
 import org.opengis.geometry.coordinate.Position;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.MathTransformFactory;
 import org.apache.sis.io.TableAppender;
 import org.apache.sis.math.Line;
 import org.apache.sis.math.Plane;
@@ -29,7 +32,7 @@ import org.apache.sis.math.Vector;
 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.internal.referencing.ExtendedPrecisionMatrix;
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.ArgumentChecks;
@@ -54,11 +57,12 @@ import org.apache.sis.util.Debug;
  * @version 0.8
  * @module
  *
+ * @see LocalizationGridBuilder
  * @see LinearTransform
  * @see Line
  * @see Plane
  */
-public class LinearTransformBuilder {
+public class LinearTransformBuilder extends TransformBuilder {
     /**
      * Number of grid columns and rows, or {@code null} if the coordinates are not distributed on a regular grid.
      * If the grid size is known, then the {@link #sources} coordinates do not need to be specified.
@@ -94,7 +98,7 @@ public class LinearTransformBuilder {
      * The product of all {@link #gridSize} values, or 0 if none if {@link #gridSize} is null.
      * If non-zero, then this is the length of {@link #targets} arrays to create.
      */
-    private final int gridLength;
+    final int gridLength;
 
     /**
      * Number of valid positions in the {@link #sources} or {@link #targets} arrays.
@@ -167,6 +171,14 @@ public class LinearTransformBuilder {
     }
 
     /**
+     * Returns the grid size for the given dimension. It is caller's responsibility to ensure that
+     * this method is invoked only on instances created by {@link #LinearTransformBuilder(int...)}.
+     */
+    final int gridSize(final int srcDim) {
+        return gridSize[srcDim];
+    }
+
+    /**
      * Allocates memory for a builder created for source positions distributed on a grid.
      * All target values need to be initialized to NaN because we can not rely on {@link #numPoints}.
      *
@@ -286,6 +298,44 @@ search: for (int j=0; j<numPoints; j++)
     }
 
     /**
+     * Returns the error message to be given to {@link IllegalStateException} when there is no data.
+     */
+    private static String noData() {
+        return Errors.format(Errors.Keys.MissingValueForProperty_1, "sourceToTarget");
+    }
+
+    /**
+     * Returns the number of dimensions in source positions.
+     *
+     * @return the dimension of source points.
+     * @throws IllegalStateException if the number of source dimensions is not yet known.
+     *
+     * @see LinearTransform#getSourceDimensions()
+     *
+     * @since 0.8
+     */
+    public int getSourceDimensions() {
+        if (gridSize != null) return gridSize.length;
+        if (sources  != null) return sources.length;
+        throw new IllegalStateException(noData());
+    }
+
+    /**
+     * Returns the number of dimensions in target positions.
+     *
+     * @return the dimension of target points.
+     * @throws IllegalStateException if the number of target dimensions is not yet known.
+     *
+     * @see LinearTransform#getTargetDimensions()
+     *
+     * @since 0.8
+     */
+    public int getTargetDimensions() {
+        if (targets != null) return targets.length;
+        throw new IllegalStateException(noData());
+    }
+
+    /**
      * Returns the direct position of the given position, or {@code null} if none.
      */
     private static DirectPosition position(final Position p) {
@@ -486,6 +536,19 @@ search: for (int j=0; j<numPoints; j++)
     }
 
     /**
+     * More straightforward version of {@link #getControlPoint(int[])} for the case where this
+     * {@code LinearTransformBuilder} is known to have been built for grid source coordinates.
+     * This method is for {@link LocalizationGridBuilder#create()} internal usage.
+     */
+    final void getControlPoint2D(final int[] source, final double[] target) {
+        assert gridSize != null;
+        final int index = flatIndex(source);
+        for (int i=0; i<target.length; i++) {
+            target[i] = targets[i][index];
+        }
+    }
+
+    /**
      * Sets the source points, overwriting any previous setting. The number of source points will need to be the same
      * than the number of {@linkplain #setTargetPoints target points} when the {@link #create()} method will be invoked.
      * In current Apache SIS implementation, the source points must be one or two-dimensional.
@@ -564,56 +627,93 @@ search: for (int j=0; j<numPoints; j++)
      * This method assumes that source positions are precise and that all uncertainty is in the target positions.
      *
      * @return the fitted linear transform.
-     * @throws IllegalStateException if the source or target points have not be specified
-     *         or if those two sets do not have the same number of points.
+     *
+     * @deprecated Replaced by {@link #create(MathTransformFactory)}.
      */
+    @Deprecated
     public LinearTransform create() {
+        try {
+            return create(null);
+        } catch (FactoryException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Creates a linear transform approximation from the source positions to the target positions.
+     * This method assumes that source positions are precise and that all uncertainty is in the target positions.
+     *
+     * @param  factory  the factory to use for creating the transform, or {@code null} for the default factory.
+     *                  The {@link MathTransformFactory#createAffineTransform(Matrix)} method of that factory
+     *                  shall return {@link LinearTransform} instances.
+     * @return the fitted linear transform.
+     * @throws FactoryException if the transform can not be created,
+     *         for example because the source or target points have not be specified.
+     *
+     * @since 0.8
+     */
+    @Override
+    @SuppressWarnings("serial")
+    public LinearTransform create(final MathTransformFactory factory) throws FactoryException {
         if (transform == null) {
             processPendings();
             final double[][] sources = this.sources;                    // Protect from changes.
             final double[][] targets = this.targets;
             if (targets == null) {
-                throw new IllegalStateException(Errors.format(Errors.Keys.MissingValueForProperty_1, "sourceToTarget"));
+                throw new FactoryException(noData());
             }
             final int sourceDim = (sources != null) ? sources.length : gridSize.length;
             final int targetDim = targets.length;
             correlation = new double[targetDim];
-            final MatrixSIS matrix = Matrices.createZero(targetDim + 1, sourceDim + 1);
+            final MatrixSIS matrix = Matrices.create(targetDim + 1, sourceDim + 1,  ExtendedPrecisionMatrix.ZERO);
             matrix.setElement(targetDim, sourceDim, 1);
             for (int j=0; j < targetDim; j++) {
                 final double c;
                 switch (sourceDim) {
                     case 1: {
-                        final Line line = new Line();
+                        final int row = j;
+                        final Line line = new Line() {
+                            @Override public void setEquation(final Number slope, final Number y0) {
+                                super.setEquation(slope, y0);
+                                matrix.setNumber(row, 0, slope);    // Preserve the extended precision (double-double).
+                                matrix.setNumber(row, 1, y0);
+                            }
+                        };
                         if (sources != null) {
                             c = line.fit(vector(sources[0]), vector(targets[j]));
                         } else {
                             c = line.fit(Vector.createSequence(0, 1, gridSize[0]),
                                          Vector.create(targets[j], false));
                         }
-                        matrix.setElement(j, 0, line.slope());
-                        matrix.setElement(j, 1, line.y0());
                         break;
                     }
                     case 2: {
-                        final Plane plan = new Plane();
+                        final int row = j;
+                        final Plane plan = new Plane() {
+                            @Override public void setEquation(final Number sx, final Number sy, final Number z0) {
+                                super.setEquation(sx, sy, z0);
+                                matrix.setNumber(row, 0, sx);       // Preserve the extended precision (double-double).
+                                matrix.setNumber(row, 1, sy);
+                                matrix.setNumber(row, 2, z0);
+                            }
+                        };
                         if (sources != null) {
                             c = plan.fit(vector(sources[0]), vector(sources[1]), vector(targets[j]));
-                        } else {
+                        } else try {
                             c = plan.fit(gridSize[0], gridSize[1], Vector.create(targets[j], false));
+                        } catch (IllegalArgumentException e) {
+                            // This may happen if the z vector still contain some "NaN" values.
+                            throw new FactoryException(noData(), e);
                         }
-                        matrix.setElement(j, 0, plan.slopeX());
-                        matrix.setElement(j, 1, plan.slopeY());
-                        matrix.setElement(j, 2, plan.z0());
                         break;
                     }
                     default: {
-                        throw new UnsupportedOperationException(Errors.format(Errors.Keys.ExcessiveNumberOfDimensions_1, sourceDim));
+                        throw new FactoryException(Errors.format(Errors.Keys.ExcessiveNumberOfDimensions_1, sourceDim));
                     }
                 }
                 correlation[j] = c;
             }
-            transform = MathTransforms.linear(matrix);
+            transform = (LinearTransform) nonNull(factory).createAffineTransform(matrix);
         }
         return transform;
     }

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java?rev=1787578&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java [UTF-8] Sat Mar 18 17:21:45 2017
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation.builder;
+
+import org.opengis.util.FactoryException;
+import org.opengis.geometry.DirectPosition;
+import org.opengis.geometry.MismatchedDimensionException;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.referencing.operation.transform.InterpolatedTransform;
+import org.apache.sis.referencing.operation.transform.LinearTransform;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.referencing.datum.DatumShiftGrid;
+import org.apache.sis.geometry.DirectPosition2D;
+import org.apache.sis.util.ArgumentChecks;
+
+
+/**
+ * Creates an "almost linear" transform mapping the given source points to the given target points.
+ * The transform is backed by a <cite>grid of localization</cite>, a two-dimensional array of coordinate points.
+ * Grid size is {@code width} × {@code height} and input coordinates are (<var>i</var>,<var>j</var>) index in the grid,
+ * where <var>i</var> must be in the [0…{@code width}-1] range and <var>j</var> in the [0…{@code height}-1] range inclusive.
+ * Output coordinates are the values stored in the grid of localization at the specified index.
+ * After a {@code LocalizationGridBuilder} instance has been fully populated (i.e. real world coordinates have been
+ * specified for all grid cells), a transformation from grid coordinates to "real world" coordinates can be obtained
+ * with the {@link #create()} method. If this transform is close enough to an affine transform,
+ * then an instance of {@link LinearTransform} is returned.
+ * Otherwise, a transform backed by the localization grid is returned.
+ *
+ * <p>This builder performs two steps:</p>
+ * <ol>
+ *   <li>Compute a linear approximation of the transformation using {@link LinearTransformBuilder}.</li>
+ *   <li>Compute {@link DatumShiftGrid} with the residuals.</li>
+ *   <li>Create a {@link InterpolatedTransform} with the above shift grid.</li>
+ * </ol>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ *
+ * @see InterpolatedTransform
+ * @see LinearTransform
+ * @see DatumShiftGrid
+ */
+public class LocalizationGridBuilder extends TransformBuilder {
+    /**
+     * The transform for the linear part.
+     */
+    private final LinearTransformBuilder linear;
+
+    /**
+     * A temporary array for two-dimensional source coordinates.
+     * Used for reducing object allocations.
+     */
+    private final int[] tmp;
+
+    /**
+     * Conversions from source real-world coordinates to grid indices before interpolation.
+     */
+    private LinearTransform sourceToGrid;
+
+    /**
+     * Creates a new, initially empty, builder.
+     *
+     * @param width   the number of columns in the grid of target positions.
+     * @param height  the number of rows in the grid of target positions.
+     */
+    public LocalizationGridBuilder(final int width, final int height) {
+        linear = new LinearTransformBuilder(width, height);
+        tmp    = new int[2];
+        sourceToGrid = MathTransforms.identity(2);
+    }
+
+    /**
+     * Defines relationship between "real-world" source coordinates and grid coordinates.
+     * The given transform is usually two-dimensional, in which case conversions from (<var>x</var>,<var>y</var>)
+     * source coordinates to ({@code gridX}, {@code gridY}) indices can be done with the following formulas:
+     * <ul>
+     *   <li><var>gridX</var> = (<var>x</var> - <var>x₀</var>) / <var>Δx</var></li>
+     *   <li><var>gridY</var> = (<var>y</var> - <var>y₀</var>) / <var>Δy</var></li>
+     * </ul>
+     *
+     * where:
+     * <ul>
+     *   <li>(<var>x₀</var>, <var>y₀</var>) is the coordinate of the center of the cell at grid index (0,0).</li>
+     *   <li><var>Δx</var> and <var>Δy</var> are the distances between two cells on the <var>x</var> and <var>y</var>
+     *       axes respectively, in the unit of measurement given by {@link #getCoordinateUnit()}.</li>
+     * </ul>
+     *
+     * The {@code coordinateToGrid} transform for the above formulas can be represented by the following matrix:
+     *
+     * {@preformat math
+     *   ┌                      ┐
+     *   │ 1/Δx      0   -x₀/Δx │
+     *   │    0   1/Δy   -y₀/Δy │
+     *   │    0      0        1 │
+     *   └                      ┘
+     * }
+     *
+     * If this method is never invoked, then the default conversion is identity.
+     *
+     * @param sourceToGrid  conversion from the "real world" source coordinates to grid indices including fractional parts.
+     *
+     * @see DatumShiftGrid#getCoordinateToGrid()
+     */
+    public void setSourceToGrid(final LinearTransform sourceToGrid) {
+        ArgumentChecks.ensureNonNull("sourceToGrid", sourceToGrid);
+        this.sourceToGrid = sourceToGrid;
+    }
+
+    /**
+     * Sets a single matching control point pair. Source position is assumed precise and target position is assumed uncertain.
+     * If the given source position was already associated with another target position, then the old target position is discarded.
+     *
+     * @param  gridX   the column index in the grid where to store the given target position.
+     * @param  gridY   the row index in the grid where to store the given target position.
+     * @param  target  the target coordinates, assumed uncertain.
+     * @throws IllegalArgumentException if the {@code x} or {@code y} ordinate value is out of grid range.
+     * @throws MismatchedDimensionException if the target position does not have the expected number of dimensions.
+     */
+    public void setControlPoint(final int gridX, final int gridY, final double... target) {
+        tmp[0] = gridX;
+        tmp[1] = gridY;
+        linear.setControlPoint(tmp, target);
+    }
+
+    /**
+     * Returns a single target coordinate for the given source coordinate, or {@code null} if none.
+     *
+     * @param  gridX  the column index in the grid where to read the target position.
+     * @param  gridY  the row index in the grid where to read the target position.
+     * @return the target coordinates associated to the given source, or {@code null} if none.
+     * @throws IllegalArgumentException if the {@code x} or {@code y} ordinate value is out of grid range.
+     */
+    public double[] getControlPoint(final int gridX, final int gridY) {
+        tmp[0] = gridX;
+        tmp[1] = gridY;
+        return linear.getControlPoint(tmp);
+    }
+
+    /**
+     * Returns whether the result of last call to {@link LinearTransformBuilder#create()} can be considered
+     * a good fit. If {@code true}, then {@link #create()} will return the linear transform directly.
+     */
+    private boolean isLinear() {
+        for (final double c : linear.correlation()) {
+            if (c < 0.99) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Creates a transform from the source points to the target points.
+     * This method assumes that source points are precise and all uncertainty is in the target points.
+     * If this transform is close enough to an affine transform, then an instance of {@link LinearTransform} is returned.
+     *
+     * @param  factory  the factory to use for creating the transform, or {@code null} for the default factory.
+     *                  The {@link MathTransformFactory#createAffineTransform(Matrix)} method of that factory
+     *                  shall return {@link LinearTransform} instances.
+     * @return the transform from source to target points.
+     * @throws FactoryException if the transform can not be created,
+     *         for example because the target points have not be specified.
+     */
+    @Override
+    public MathTransform create(final MathTransformFactory factory) throws FactoryException {
+        final LinearTransform gridToCoord = linear.create(factory);
+        if (isLinear()) {
+            return gridToCoord;
+        }
+        final int      width  = linear.gridSize(0);
+        final int      height = linear.gridSize(1);
+        final int      tgtDim = gridToCoord.getTargetDimensions();
+        final double[] data   = new double[tgtDim * linear.gridLength];
+        final double[] point  = new double[tgtDim];
+        try {
+            final DirectPosition2D src = new DirectPosition2D();
+            DirectPosition tgt = null;
+            for (int k=0,y=0; y<height; y++) {
+                src.y  = y;
+                tmp[1] = y;
+                for (int x=0; x<width; x++) {
+                    src.x  = x;
+                    tmp[0] = x;
+                    linear.getControlPoint2D(tmp, point);               // Expected position.
+                    tgt = gridToCoord.transform(src, tgt);              // Interpolated position.
+                    for (int i=0; i<tgtDim; i++) {
+                        data[k++] = point[i] - tgt.getOrdinate(i);      // Residual.
+                    }
+                }
+            }
+            /*
+             * At this point, we computed the residual of all coordinate values.
+             * Now we need to express those residuals in grid units instead than
+             * "real world" unit, because InterpolatedTransform works that way.
+             */
+            final LinearTransform coordToGrid = gridToCoord.inverse();
+            if (tgtDim == 2) {
+                coordToGrid.deltaTransform(data, 0, data, 0, linear.gridLength);
+            } else {
+                throw new UnsupportedOperationException();          // TODO: use a fallback.
+            }
+        } catch (TransformException e) {
+            throw new FactoryException(e);                          // Should never happen.
+        }
+        return InterpolatedTransform.createGeodeticTransformation(nonNull(factory),
+                new ResidualGrid(sourceToGrid, gridToCoord, width, height, tgtDim, data));
+    }
+}

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

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

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/ResidualGrid.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/ResidualGrid.java?rev=1787578&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/ResidualGrid.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/ResidualGrid.java [UTF-8] Sat Mar 18 17:21:45 2017
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation.builder;
+
+import javax.measure.quantity.Dimensionless;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.apache.sis.referencing.operation.transform.LinearTransform;
+import org.apache.sis.internal.referencing.provider.DatumShiftGridFile;
+import org.apache.sis.parameter.ParameterBuilder;
+import org.apache.sis.measure.Units;
+import org.opengis.referencing.operation.Matrix;
+
+
+/**
+ * The residuals after an affine approximation has been created for a set of matching control point pairs.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+final class ResidualGrid extends DatumShiftGridFile<Dimensionless,Dimensionless> {
+    /**
+     * The parameter descriptors for the "Localization grid" operation.
+     */
+    private static final ParameterDescriptorGroup PARAMETERS;
+    static {
+        final ParameterBuilder builder = new ParameterBuilder();
+        PARAMETERS = builder.addName("Localization grid").createGroup();
+    }
+
+    /**
+     * The residual data, as translations to apply on the result of affine transform.
+     * In this flat array, index of target dimension varies fastest, then column index, then row index.
+     */
+    private final double[] offsets;
+
+    /**
+     * Number of dimension of target coordinates.
+     */
+    private final int numDim;
+
+    /**
+     * Conversion from grid coordinates to the final "real world" coordinates.
+     *
+     * @see #gridToTarget()
+     */
+    private final LinearTransform gridToTarget;
+
+    /**
+     * Creates a new residual grid.
+     *
+     * @param sourceToGrid  conversion from the "real world" source coordinates to grid indices including fractional parts.
+     * @param gridToTarget  conversion from grid coordinates to the final "real world" coordinates.
+     * @param numDim        number of dimension of target coordinates.
+     * @param residuals     the residual data, as translations to apply on the result of affine transform.
+     */
+    ResidualGrid(final LinearTransform sourceToGrid, final LinearTransform gridToTarget,
+            final int nx, final int ny, final int numDim, final double[] residuals)
+    {
+        super(Units.UNITY, Units.UNITY, true, sourceToGrid, nx, ny, PARAMETERS);
+        this.gridToTarget = gridToTarget;
+        this.numDim       = numDim;
+        this.offsets      = residuals;
+        this.accuracy     = 0.01;           // TODO
+    }
+
+    /**
+     * Creates a new datum shift grid with the same grid geometry than the given grid
+     * but a reference to a different data array.
+     */
+    private ResidualGrid(final ResidualGrid other, final double[] data) {
+        super(other);
+        gridToTarget = other.gridToTarget;
+        numDim       = other.numDim;
+        accuracy     = other.accuracy;
+        offsets      = data;
+    }
+
+    /**
+     * Returns a new grid with the same geometry than this grid but different data array.
+     */
+    @Override
+    protected DatumShiftGridFile<Dimensionless, Dimensionless> setData(final Object[] other) {
+        return new ResidualGrid(this, (double[]) other[0]);
+    }
+
+    /**
+     * Returns reference to the data array. This method is for cache management, {@link #equals(Object)}
+     * and {@link #hashCode()} implementations only and should not be invoked in other context.
+     */
+    @Override
+    protected Object[] getData() {
+        return new Object[] {offsets};
+    }
+
+    /**
+     * Returns the transform from grid coordinates to "real world" coordinates after the datum shift has been applied.
+     */
+    @Override
+    public Matrix gridToTarget() {
+        return gridToTarget.getMatrix();
+    }
+
+    /**
+     * Returns the number of dimensions of the translation vectors interpolated by this shift grid.
+     */
+    @Override
+    public int getTranslationDimensions() {
+        return numDim;
+    }
+
+    /**
+     * Returns the cell value at the given dimension and grid index.
+     */
+    @Override
+    public double getCellValue(int dim, int gridX, int gridY) {
+        return offsets[(gridX + gridY*nx) * numDim + dim];
+    }
+
+    /**
+     * Returns {@code true} if the given object is a grid containing the same data than this grid.
+     */
+    @Override
+    public boolean equals(final Object other) {
+        if (super.equals(other)) {
+            // Offset array has been compared by the parent class.
+            return numDim == ((ResidualGrid) other).numDim;
+        }
+        return false;
+    }
+}

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

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

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/TransformBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/TransformBuilder.java?rev=1787578&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/TransformBuilder.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/TransformBuilder.java [UTF-8] Sat Mar 18 17:21:45 2017
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation.builder;
+
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.util.FactoryException;
+import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
+
+
+/**
+ * Creates a transform which will map approximatively the given source positions to the given target positions.
+ * The transform may be a linear approximation the minimize the errors in a <cite>least square</cite> sense,
+ * or a more accurate transform using a localization grid.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+public abstract class TransformBuilder {
+    /**
+     * For subclass constructors.
+     */
+    protected TransformBuilder() {
+    }
+
+    /**
+     * Creates a transform from the source points to the target points.
+     *
+     * @param  factory  the factory to use for creating the transform, or {@code null} for the default factory.
+     * @return the transform from source to target points.
+     * @throws FactoryException if the transform can not be created,
+     *         for example because the target points have not be specified.
+     */
+    public abstract MathTransform create(final MathTransformFactory factory) throws FactoryException;
+
+    /**
+     * Returns the given factory if non-null, or the default factory otherwise.
+     */
+    static MathTransformFactory nonNull(MathTransformFactory factory) {
+        if (factory == null) {
+            factory = DefaultFactories.forBuildin(MathTransformFactory.class, DefaultMathTransformFactory.class);
+        }
+        return factory;
+    }
+}

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

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

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java?rev=1787578&r1=1787577&r2=1787578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] Sat Mar 18 17:21:45 2017
@@ -68,7 +68,7 @@ import org.apache.sis.internal.referenci
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.4
- * @version 0.7
+ * @version 0.8
  * @module
  *
  * @see org.apache.sis.parameter.TensorParameters
@@ -203,8 +203,12 @@ public final class Matrices extends Stat
      */
     public static MatrixSIS create(final int numRow, final int numCol, final Number[] elements) {
         ArgumentChecks.ensureNonNull("elements", elements);
-        if (elements == ExtendedPrecisionMatrix.IDENTITY) { // Intentionally undocumented features.
-            return GeneralMatrix.createExtendedPrecision(numRow, numCol, true);
+        /*
+         * Below is an intantionally undocumented feature. We use those sentinel values as a way to create
+         * matrices with extended precision without exposing our double-double arithmetic in public API.
+         */
+        if (elements == ExtendedPrecisionMatrix.IDENTITY || elements == ExtendedPrecisionMatrix.ZERO) {
+            return GeneralMatrix.createExtendedPrecision(numRow, numCol, elements == ExtendedPrecisionMatrix.IDENTITY);
         }
         final GeneralMatrix matrix = GeneralMatrix.createExtendedPrecision(numRow, numCol, false);
         if (matrix.setElements(elements)) {

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedTransform.java?rev=1787578&r1=1787577&r2=1787578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedTransform.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedTransform.java [UTF-8] Sat Mar 18 17:21:45 2017
@@ -59,7 +59,7 @@ import org.apache.sis.internal.referenci
  * If the grid indices are non-integer values, then the translations are interpolated using a bilinear interpolation.
  * If the grid indices are outside the grid domain ([0 … <var>width</var>-2] × [0 … <var>height</var>-2]
  * where <var>width</var> and <var>height</var> are the number of columns and rows in the grid),
- * then the translations are extrapolated. The translation is then added to the input coordinates.</p>
+ * then the translations are extrapolated. The translation is finally added to the input coordinates.</p>
  *
  * <p>The input and output coordinates can have any number of dimensions, provided that they are the same
  * than the number of {@linkplain DatumShiftGrid#getTranslationDimensions() translation dimensions}.
@@ -72,6 +72,9 @@ import org.apache.sis.internal.referenci
  * @since   0.7
  * @version 0.8
  * @module
+ *
+ * @see DatumShiftGrid
+ * @see org.apache.sis.referencing.operation.builder.LocalizationGridBuilder
  */
 public class InterpolatedTransform extends DatumShiftTransform {
     /**
@@ -148,7 +151,7 @@ public class InterpolatedTransform exten
             ((DatumShiftGridFile<?,?>) grid).setFileParameters(context);
         }
         /*
-         * Set the normalization matrix to the conversion from grid coordinates (e.g. seconds of angle)
+         * Set the normalization matrix to the conversion from source coordinates (e.g. seconds of angle)
          * to grid indices. This will allow us to invoke DatumShiftGrid.interpolateAtCell(x, y, vector)
          * directly in the transform(…) methods.
          */
@@ -177,9 +180,20 @@ public class InterpolatedTransform exten
             }
         }
         /*
-         * Denormalization is the inverse of all above conversions.
+         * Denormalization is the inverse of all above conversions in the usual case (NADCON and NTv2) where the
+         * source coordinate system is the same than the target coordinate system, for example with axis unit in
+         * degrees. However we also use this InterpolatedTransform implementation for other operation, like the
+         * one created by LocalizationGridBuilder. Those later operations may require a different denormalization
+         * matrix.
          */
-        context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION).setMatrix(normalize.inverse());
+        Matrix denormalize = null;
+        if (grid instanceof DatumShiftGridFile<?,?>) {
+            denormalize = ((DatumShiftGridFile<?,?>) grid).gridToTarget();
+        }
+        if (denormalize == null) {
+            denormalize = normalize.inverse();                      // Normal NACDON and NTv2 case.
+        }
+        context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION).setMatrix(denormalize);
         inverse = createInverse();
     }
 

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilderTest.java?rev=1787578&r1=1787577&r2=1787578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilderTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilderTest.java [UTF-8] Sat Mar 18 17:21:45 2017
@@ -20,6 +20,7 @@ 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;
@@ -42,9 +43,11 @@ import static org.junit.Assert.*;
 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();
         final Map<DirectPosition1D,DirectPosition1D> pos = new HashMap<>(4);
         assertNull(pos.put(new DirectPosition1D(1), new DirectPosition1D(1)));
@@ -55,7 +58,7 @@ public final strictfp class LinearTransf
         assertArrayEquals(new double[] {3}, builder.getControlPoint(new int[] {2}), STRICT);
         assertNull(                         builder.getControlPoint(new int[] {3}));
 
-        final Matrix m = builder.create().getMatrix();
+        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);
@@ -66,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),
@@ -84,7 +89,7 @@ public final strictfp class LinearTransf
         assertArrayEquals(new double[] {5, 5}, builder.getControlPoint(new int[] {2, 2}), STRICT);
         assertNull(                            builder.getControlPoint(new int[] {2, 1}));
 
-        final Matrix m = builder.create().getMatrix();
+        final Matrix m = builder.create(null).getMatrix();
 
         // First row (x)
         assertEquals("m₀₀",  2, m.getElement(0, 0), STRICT);
@@ -102,10 +107,12 @@ 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() {
+    public void testExplicitSource2D() throws FactoryException {
         testSetAllPoints(new LinearTransformBuilder());
         testSetEachPoint(new LinearTransformBuilder());
     }
@@ -114,11 +121,15 @@ public final strictfp class LinearTransf
      * Same test than {@link #testExplicitSource2D()}, but using the
      * {@link LinearTransformBuilder#LinearTransformBuilder(int...)} constructor.
      *
+     * @throws FactoryException if the transform can not be created.
+     *
+     * @see LocalizationGridBuilderTest#testSixPoints()
+     *
      * @since 0.8
      */
     @Test
     @DependsOnMethod("testExplicitSource2D")
-    public void testImplicitSource2D() {
+    public void testImplicitSource2D() throws FactoryException {
         testSetAllPoints(new LinearTransformBuilder(2, 3));
         testSetEachPoint(new LinearTransformBuilder(2, 3));
     }
@@ -127,7 +138,7 @@ public final strictfp class LinearTransf
      * Execution of {@link #testExplicitSource2D()} and {@link #testImplicitSource2D()}
      * where all control points are specified by a map.
      */
-    private void testSetAllPoints(final LinearTransformBuilder builder) {
+    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)));
@@ -143,7 +154,7 @@ public final strictfp class LinearTransf
      * Execution of {@link #testExplicitSource2D()} and {@link #testImplicitSource2D()}
      * where all control points are specified one-by-one.
      */
-    private void testSetEachPoint(final LinearTransformBuilder builder) {
+    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});
@@ -156,8 +167,8 @@ public final strictfp class LinearTransf
     /**
      * Verifies the transform created by {@link #testExplicitSource2D()} and {@link #testImplicitSource2D()}.
      */
-    private void verify(final LinearTransformBuilder builder) {
-        final Matrix m = builder.create().getMatrix();
+    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);
@@ -165,7 +176,9 @@ public final strictfp class LinearTransf
         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);
@@ -178,10 +191,12 @@ public final strictfp class LinearTransf
 
     /**
      * 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);
@@ -194,10 +209,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);
@@ -206,10 +223,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);
@@ -224,10 +243,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);
@@ -243,7 +264,7 @@ 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;
@@ -261,7 +282,7 @@ public final strictfp class LinearTransf
          */
         final LinearTransformBuilder builder = new LinearTransformBuilder();
         builder.setControlPoints(pos);
-        final Matrix m = builder.create().getMatrix();
+        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);
@@ -276,7 +297,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.
@@ -301,7 +322,7 @@ public final strictfp class LinearTransf
          */
         final LinearTransformBuilder builder = new LinearTransformBuilder();
         builder.setControlPoints(pos);
-        final Matrix m = builder.create().getMatrix();
+        final Matrix m = builder.create(null).getMatrix();
         /*
          * Compare the coefficients with the reference implementation.
          */

Added: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilderTest.java?rev=1787578&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilderTest.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilderTest.java [UTF-8] Sat Mar 18 17:21:45 2017
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation.builder;
+
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.test.referencing.TransformTestCase;
+import org.apache.sis.test.DependsOn;
+import org.junit.Test;
+
+
+/**
+ * Tests {@link LocalizationGridBuilder}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+@DependsOn(LinearTransformBuilderTest.class)
+public final strictfp class LocalizationGridBuilderTest extends TransformTestCase {
+    /**
+     * Tests a small grid of 3 rows and 2 columns.
+     * This tests use the same point than {@link LinearTransformBuilderTest#testImplicitSource2D()}
+     *
+     * @throws FactoryException if an error occurred while computing the localization grid.
+     * @throws TransformException if an error occurred while testing a transformation.
+     *
+     * @see LinearTransformBuilderTest#testImplicitSource2D()
+     */
+    @Test
+    public void testSixPoints() throws FactoryException, TransformException {
+        final LocalizationGridBuilder builder = new LocalizationGridBuilder(2, 3);
+        builder.setControlPoint(0, 0, 3, 9);
+        builder.setControlPoint(0, 1, 4, 7);
+        builder.setControlPoint(0, 2, 6, 6);
+        builder.setControlPoint(1, 0, 4, 8);
+        builder.setControlPoint(1, 1, 5, 4);
+        builder.setControlPoint(1, 2, 8, 2);
+
+        transform = builder.create(null);
+        tolerance = 1;                          // TODO: temporary high value while we debug.
+        verifyTransform(new double[] {0, 0}, new double[] {3, 9});
+    }
+}

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

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

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1787578&r1=1787577&r2=1787578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] Sat Mar 18 17:21:45 2017
@@ -225,6 +225,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/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Line.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Line.java?rev=1787578&r1=1787577&r2=1787578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Line.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Line.java [UTF-8] Sat Mar 18 17:21:45 2017
@@ -195,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.
      *
@@ -344,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/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java?rev=1787578&r1=1787577&r2=1787578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java [UTF-8] Sat Mar 18 17:21:45 2017
@@ -213,6 +213,22 @@ 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>.
@@ -258,8 +274,9 @@ public class Plane implements Cloneable,
 
     /**
      * Computes the plane's coefficients from values distributed on a regular grid. Invoking this method
-     * is equivalent 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:
+     * 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">
@@ -286,13 +303,14 @@ public class Plane implements Cloneable,
      *
      * 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.
+     * 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.
+     * @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
      */
@@ -307,7 +325,7 @@ public class Plane implements Cloneable,
         final Fit r = new Fit(nx, ny, z);
         r.resolve();
         final double p = r.correlation(nx, length, z, null);
-        setEquation(r.sx.value, r.sy.value, r.z0.value);
+        setEquation(r.sx, r.sy, r.z0);
         return p;
     }
 
@@ -331,7 +349,7 @@ public class Plane implements Cloneable,
          * 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.value, r.sy.value, r.z0.value);
+        setEquation(r.sx, r.sy, r.z0);
         return p;
     }
 



Mime
View raw message