sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1822302 - in /sis/branches/JDK8/core/sis-referencing/src: main/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1D.java test/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1DTest.java
Date Fri, 26 Jan 2018 14:38:49 GMT
Author: desruisseaux
Date: Fri Jan 26 14:38:49 2018
New Revision: 1822302

URL: http://svn.apache.org/viewvc?rev=1822302&view=rev
Log:
Modification in the extrapolation method of LinearInterpolator1D:
keep the same slope than the extremum where extrapolation occurs.

Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1D.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1DTest.java

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1D.java?rev=1822302&r1=1822301&r2=1822302&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1D.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1D.java
[UTF-8] Fri Jan 26 14:38:49 2018
@@ -39,11 +39,26 @@ import org.apache.sis.util.resources.Err
  * <p>If desired values in decreasing order can be supported by inverting the sign
of all values,
  * then concatenating this transform with a transform that multiply all output values by
-1.</p>
  *
+ * <div class="section">Extrapolation</div>
+ * If an input value is outside the expected range of values, this class extrapolates using
the
+ * slope defined by the two first points if the requested value is before, or the slope defined
+ * by the two last points if the requested value is after.   In other words, extrapolations
are
+ * computed using only values at the extremum where extrapolation happen. This rule causes
less
+ * surprising behavior when computing a data cube envelope, which may need extrapolation
by 0.5
+ * pixel before the first value or after the last value.
+ *
+ * <div class="note"><b>Example:</b>
+ * if a vertical dimension is made of slices at y₀=5, y₁=10, y₂=100 and y₃=250 meters,
then linear
+ * interpolation at 0.5 is 7.5 meters and extrapolation at -0.5 is expected to give 2.5 meters.</div>
+ *
  * @author  Johann Sorel (Geomatys)
  * @author  Rémi Maréchal (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
- * @since   0.7
+ * @version 1.0
+ *
+ * @see MathTransforms#interpolate(double[], double[])
+ *
+ * @since 0.7
  * @module
  */
 final class LinearInterpolator1D extends AbstractMathTransform1D implements Serializable
{
@@ -54,15 +69,11 @@ final class LinearInterpolator1D extends
 
     /**
      * The sequence values specified at construction time.
+     * Must contain at least 2 values.
      */
     private final double[] values;
 
     /**
-     * The average function slope. Used only for extrapolations.
-     */
-    private final double slope;
-
-    /**
      * If the transform is invertible, the inverse. Otherwise {@code null}.
      * The transform is invertible only if values are in increasing order.
      */
@@ -72,15 +83,13 @@ final class LinearInterpolator1D extends
      * Creates a new transform which will interpolate in the given table of values.
      * The inputs are {0, 1, … , <var>N</var>} where <var>N</var>
is length of output values.
      *
-     * <p>This constructor assumes that the {@code values} array have already be clones,
-     * so it will not clone it again.</p>
+     * <p>This constructor assumes that the {@code values} array has already been cloned,
+     * so it will not clone it again. That array shall contain at least two values.</p>
      *
      * @param values  the <var>y</var> values in <var>y=f(x)</var>
where <var>x</var> = {0, 1, … , {@code values.length-1}}.
-     * @param slope   the value to use for extrapolation.
      */
-    private LinearInterpolator1D(final double[] values, final double slope) {
+    private LinearInterpolator1D(final double[] values) {
         this.values = values;                           // Cloning this array is caller's
responsibility.
-        this.slope  = slope;
         double last = values[0];
         for (int i=1; i<values.length; i++) {
             if (!(last <= (last = values[i]))) {        // Use '!' for catching NaN values.
@@ -93,14 +102,15 @@ final class LinearInterpolator1D extends
 
     /**
      * Creates a transform for the given values. This method returns an affine transform
instead than an
-     * interpolator if the given values form a series with a constant increment.
+     * interpolator if the given values form a series with a constant increment. The given
array shall
+     * contain at least two values.
      *
      * @param  values  a <strong>copy</strong> of the user-provided values. This
array may be modified.
      */
     private static MathTransform1D create(final double[] values) {
         final int n = values.length - 1;
         final double offset = values[0];
-        double slope = (values[n] - offset) / n;
+        final double slope = (values[n] - offset) / n;
         final double as = Math.abs(slope);
         /*
          * If the increment between values is constant (with a small tolerance factor),
@@ -122,12 +132,11 @@ final class LinearInterpolator1D extends
          */
         final boolean isReverted = (slope < 0);
         if (isReverted) {
-            slope = -slope;
             for (i=0; i <= n; i++) {
                 values[i] = -values[i];
             }
         }
-        MathTransform1D tr = new LinearInterpolator1D(values, slope);
+        MathTransform1D tr = new LinearInterpolator1D(values);
         if (isReverted) {
             tr = new ConcatenatedTransformDirect1D(tr, LinearTransform1D.NEGATE);
         }
@@ -223,17 +232,21 @@ final class LinearInterpolator1D extends
             final int n = values.length - 1;
             if (i < n) {
                 x -= i;
-                y = values[i] * (1-x) + values[i+1] * x;
-                d = values[i+1] - values[i];
+                final double y0 = values[i  ];
+                final double y1 = values[i+1];
+                y = y0 * (1-x) + y1 * x;
+                d = y1 - y0;
             } else {
                 // x is after the last available value.
-                y = (x - n) * slope + values[n];
-                d = slope;
+                final double y1 = values[n];
+                d = y1 - values[n-1];
+                y = (x - n) * d + y1;
             }
         } else {
             // x is before the first available value.
-            y = x * slope + values[0];
-            d = slope;
+            final double y0 = values[0];
+            d = values[1] - y0;
+            y = x * d + y0;
         }
         if (dstPts != null) {
             dstPts[dstOff] = y;
@@ -256,11 +269,13 @@ final class LinearInterpolator1D extends
                 return values[i] * (1-x) + values[i+1] * x;
             } else {
                 // x is after the last available value.
-                return (x - n) * slope + values[n];
+                final double y1 = values[n];
+                return (x - n) * (y1 - values[n-1]) + y1;
             }
         } else {
             // x is before the first available value.
-            return x * slope + values[0];
+            final double y0 = values[0];
+            return x * (values[1] - y0) + y0;
         }
     }
 
@@ -271,13 +286,8 @@ final class LinearInterpolator1D extends
      */
     @Override
     public double derivative(final double x) {
-        if (x >= 0) {
-            final int i = (int) x;
-            if (i < values.length - 1) {
-                return values[i+1] - values[i];
-            }
-        }
-        return slope;
+        final int i = Math.max(0, Math.min(values.length - 2, (int) x));
+        return values[i+1] - values[i];
     }
 
     /**
@@ -320,7 +330,8 @@ final class LinearInterpolator1D extends
             int i = Arrays.binarySearch(values, y);
             if (i >= 0) {
                 x = i;
-                d = (i >= 1 && i < values.length) ? (values[i] - values[i-1])
: slope;
+                i = Math.max(1, Math.min(values.length - 1, i));
+                d = values[i] - values[i-1];
             } else {
                 i = ~i;
                 if (i >= 1) {
@@ -330,11 +341,13 @@ final class LinearInterpolator1D extends
                     } else {
                         // y is after the last available value.
                         final int n = values.length - 1;
-                        x = (y - values[n]) / (d = slope) + n;
+                        final double y1 = values[n];
+                        x = (y - y1) / (d = y1 - values[n-1]) + n;
                     }
                 } else {
                     // y is before the first available value.
-                    x = (y - values[0]) / (d = slope);
+                    final double y0 = values[0];
+                    x = (y - y0) / (d = values[1] - y0);
                 }
             }
             if (dstPts != null) {
@@ -361,11 +374,13 @@ final class LinearInterpolator1D extends
                     } else {
                         // y is after the last available value.
                         final int n = values.length - 1;
-                        return (y - values[n]) / slope + n;
+                        final double y1 = values[n];
+                        return (y - y1) / (y1 - values[n-1]) + n;
                     }
                 } else {
                     // y is before the first available value.
-                    return (y - values[0]) / slope;
+                    final double y0 = values[0];
+                    return (y - y0) / (values[1] - y0);
                 }
             }
         }
@@ -380,13 +395,8 @@ final class LinearInterpolator1D extends
             if (i < 0) {
                 i = ~i;
             }
-            final double d;
-            if (i >= 1 && i < values.length) {
-                d = (values[i] - values[i-1]);
-            } else {
-                d = slope;
-            }
-            return 1 / d;
+            i = Math.max(1, Math.min(values.length - 1, i));
+            return 1 / (values[i] - values[i-1]);
         }
     }
 

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1DTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1DTest.java?rev=1822302&r1=1822301&r2=1822302&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1DTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/LinearInterpolator1DTest.java
[UTF-8] Fri Jan 26 14:38:49 2018
@@ -21,6 +21,7 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.operation.NoninvertibleTransformException;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.test.referencing.TransformTestCase;
+import org.apache.sis.test.DependsOnMethod;
 import org.junit.Test;
 
 import static org.opengis.test.Assert.*;
@@ -31,7 +32,7 @@ import static org.opengis.test.Assert.*;
  *
  * @author  Rémi Maréchal (Geomatys)
  * @author  Martin Desruisseaux (Geomatys).
- * @version 0.7
+ * @version 1.0
  * @since   0.7
  * @module
  */
@@ -238,4 +239,25 @@ public final strictfp class LinearInterp
          */
         verifyInDomain(new double[] {min}, new double[] {max}, new int[] {100}, new Random(randomSeed));
     }
+
+    /**
+     * Tests input values outside the expected range.
+     * A few values inside ranges are also tested as a safety.
+     *
+     * @throws TransformException if an error occurred while testing a value.
+     */
+    @Test
+    @DependsOnMethod("testIndicesToIncreasingValues")
+    public void testExtrapolations() throws TransformException {
+        values = new double[] {5, 10, 100, 250};
+        transform = LinearInterpolator1D.create(preimage, values);
+        derivativeDeltas = new double[] {0.1};
+        verifyTransform(new double[] {0,  1, 0.5, -0.5, -1, -2,   3, 3.5,   4,   5},    
   // Values to transform.
+                        new double[] {5, 10, 7.5,  2.5,  0, -5, 250, 325, 400, 550});   
   // Expected results.
+
+        verifyConsistency(0f, 1f, 0.5f, -0.5f, -1f, -2f, 3f, 3.5f, 4f, 5f);
+        verifyDerivative(0.25);     // Interpolation (verified by safety)
+        verifyDerivative(-8);       // Extrapolation
+        verifyDerivative( 8);
+    }
 }



Mime
View raw message