From 62a4698c3a68854169991d48f45bd3cec522aa22 Mon Sep 17 00:00:00 2001
From: Luc Maisonobe <luc@orekit.org>
Date: Wed, 11 Feb 2015 15:53:04 +0100
Subject: [PATCH] Fixed computation of min/max when some cells are Double.NaN.

---
 .../java/org/orekit/rugged/api/Rugged.java    |   7 +
 .../duvenhage/MinMaxTreeTile.java             | 177 +++++-------------
 .../org/orekit/rugged/raster/SimpleTile.java  |  13 +-
 .../org/orekit/rugged/utils/MaxSelector.java  |  74 ++++++++
 .../org/orekit/rugged/utils/MinSelector.java  |  74 ++++++++
 .../org/orekit/rugged/utils/Selector.java     |  43 +++++
 .../orekit/rugged/errors/DumpManagerTest.java |   5 +
 .../rugged/errors/DumpReplayerTest.java       |   7 +-
 .../orekit/rugged/raster/SimpleTileTest.java  |   3 +-
 .../resources/replay/replay-direct-loc-01.txt |  38 ++--
 10 files changed, 284 insertions(+), 157 deletions(-)
 create mode 100644 src/main/java/org/orekit/rugged/utils/MaxSelector.java
 create mode 100644 src/main/java/org/orekit/rugged/utils/MinSelector.java
 create mode 100644 src/main/java/org/orekit/rugged/utils/Selector.java

diff --git a/src/main/java/org/orekit/rugged/api/Rugged.java b/src/main/java/org/orekit/rugged/api/Rugged.java
index 0e23ae86..0f17428c 100644
--- a/src/main/java/org/orekit/rugged/api/Rugged.java
+++ b/src/main/java/org/orekit/rugged/api/Rugged.java
@@ -174,6 +174,13 @@ public class Rugged {
         return scToBody.isInRange(date);
     }
 
+    /** Get the observed body ellipsoid.
+     * @return observed body ellipsoid
+     */
+    public ExtendedEllipsoid getEllipsoid() {
+        return ellipsoid;
+    }
+
     /** Direct location of a sensor line.
      * @param sensorName name of the line sensor
      * @param lineNumber number of the line to localize on ground
diff --git a/src/main/java/org/orekit/rugged/intersection/duvenhage/MinMaxTreeTile.java b/src/main/java/org/orekit/rugged/intersection/duvenhage/MinMaxTreeTile.java
index a8fd2e10..2c7bbfb6 100644
--- a/src/main/java/org/orekit/rugged/intersection/duvenhage/MinMaxTreeTile.java
+++ b/src/main/java/org/orekit/rugged/intersection/duvenhage/MinMaxTreeTile.java
@@ -16,10 +16,12 @@
  */
 package org.orekit.rugged.intersection.duvenhage;
 
-import org.apache.commons.math3.analysis.BivariateFunction;
 import org.apache.commons.math3.util.FastMath;
 import org.orekit.rugged.errors.DumpManager;
 import org.orekit.rugged.raster.SimpleTile;
+import org.orekit.rugged.utils.MaxSelector;
+import org.orekit.rugged.utils.MinSelector;
+import org.orekit.rugged.utils.Selector;
 
 /** Simple implementation of a {@link org.orekit.rugged.raster.Tile}
  * with a min/max kd tree.
@@ -112,41 +114,11 @@ public class MinMaxTreeTile extends SimpleTile {
 
             final double[] preprocessed = new double[raw.length];
 
-            // we don't use org.apache.commons.math3.analysis.function.Min
-            // because we want to ignore NaN values instead of spreading them
-            final BivariateFunction min = new BivariateFunction() {
-                /** {@inheritDoc} */
-                @Override
-                public double value(final double x, final double y) {
-                    if (Double.isNaN(x)) {
-                        return y;
-                    } else if (Double.isNaN(y)) {
-                        return x;
-                    } else {
-                        return x <= y ? x : y;
-                    }
-                }
-            };
-            preprocess(preprocessed, raw, nbRows, nbCols, min);
-            applyRecursively(minTree, start.length - 1, nbRows, nbCols, min, preprocessed, 0);
-
-            // we don't use org.apache.commons.math3.analysis.function.Max
-            // because we want to ignore NaN values instead of spreading them
-            final BivariateFunction max = new BivariateFunction() {
-                /** {@inheritDoc} */
-                @Override
-                public double value(final double x, final double y) {
-                    if (Double.isNaN(x)) {
-                        return y;
-                    } else if (Double.isNaN(y)) {
-                        return x;
-                    } else {
-                        return x <= y ? y : x;
-                    }
-                }
-            };
-            preprocess(preprocessed, raw, nbRows, nbCols, max);
-            applyRecursively(maxTree, start.length - 1, nbRows, nbCols, max, preprocessed, 0);
+            preprocess(preprocessed, raw, nbRows, nbCols, MinSelector.getInstance());
+            applyRecursively(minTree, start.length - 1, nbRows, nbCols, MinSelector.getInstance(), preprocessed, 0);
+
+            preprocess(preprocessed, raw, nbRows, nbCols, MaxSelector.getInstance());
+            applyRecursively(maxTree, start.length - 1, nbRows, nbCols, MaxSelector.getInstance(), preprocessed, 0);
 
         }
 
@@ -284,77 +256,7 @@ public class MinMaxTreeTile extends SimpleTile {
      * @return row/column indices of the cell at which min elevation is reached
      */
     public int[] locateMin(final int i, final int j, final int level) {
-
-        final int k  = start.length - level;
-        int rowShift = k / 2;
-        int colShift = (k + 1) / 2;
-        int levelI   = i >> rowShift;
-        int levelJ   = j >> colShift;
-        int levelR   = 1 + ((getLatitudeRows()     - 1) >> rowShift);
-        int levelC   = 1 + ((getLongitudeColumns() - 1) >> colShift);
-
-        // track the cell ancestors from merged tree at specified level up to tree at level 1
-        for (int l = level + 1; l < start.length; ++l) {
-
-            if (isColumnMerging(l)) {
-
-                --colShift;
-                levelC = 1 + ((getLongitudeColumns() - 1) >> colShift);
-                levelJ = levelJ << 1;
-
-                if (levelJ + 1 < levelC) {
-                    // the cell results from a regular merging of two columns
-                    if (minTree[start[l] + levelI * levelC + levelJ] >
-                        minTree[start[l] + levelI * levelC + levelJ + 1]) {
-                        levelJ++;
-                    }
-                }
-
-            } else {
-
-                --rowShift;
-                levelR = 1 + ((getLatitudeRows() - 1) >> rowShift);
-                levelI = levelI << 1;
-
-                if (levelI + 1 < levelR) {
-                    // the cell results from a regular merging of two rows
-                    if (minTree[start[l] + levelI       * levelC + levelJ] >
-                        minTree[start[l] + (levelI + 1) * levelC + levelJ]) {
-                        levelI++;
-                    }
-                }
-
-            }
-
-        }
-
-        // we are now at first merge level, which always results from a column merge
-        // or pre-processed data, which themselves result from merging four cells
-        // used in interpolation
-        // this imply the ancestor of min/max at (n, m) is one of
-        // (2n, m), (2n+1, m), (2n+2, m), (2n, m+1), (2n+1, m+1), (2n+2, m+1)
-        int minI = levelI;
-        int minJ = 2 * levelJ;
-        double minElevation = Double.POSITIVE_INFINITY;
-        for (int n = 2 * levelJ; n < 2 * levelJ + 3; ++n) {
-            if (n < getLongitudeColumns()) {
-                for (int m = levelI; m < levelI + 2; ++m) {
-                    if (m < getLatitudeRows()) {
-                        final double elevation = raw[m * getLongitudeColumns() + n];
-                        if (elevation < minElevation) {
-                            minI         = m;
-                            minJ         = n;
-                            minElevation = elevation;
-                        }
-                    }
-                }
-            }
-        }
-
-        return new int[] {
-            minI, minJ
-        };
-
+        return locateMinMax(i, j, level, MinSelector.getInstance(), minTree);
     }
 
     /** Locate the cell at which max elevation is reached for a specified level.
@@ -371,6 +273,19 @@ public class MinMaxTreeTile extends SimpleTile {
      * @return row/column indices of the cell at which min elevation is reached
      */
     public int[] locateMax(final int i, final int j, final int level) {
+        return locateMinMax(i, j, level, MaxSelector.getInstance(), maxTree);
+    }
+
+    /** Locate the cell at which min/max elevation is reached for a specified level.
+     * @param i row index of the cell
+     * @param j column index of the cell
+     * @param level tree level of the sub-tile considered
+     * @param selector min/max selector to use
+     * @param tree min/max tree to use
+     * @return row/column indices of the cell at which min/max elevation is reached
+     */
+    private int[] locateMinMax(final int i, final int j, final int level,
+                               final Selector selector, final double[] tree) {
 
         final int k  = start.length - level;
         int rowShift = k / 2;
@@ -391,8 +306,8 @@ public class MinMaxTreeTile extends SimpleTile {
 
                 if (levelJ + 1 < levelC) {
                     // the cell results from a regular merging of two columns
-                    if (maxTree[start[l] + levelI * levelC + levelJ] <
-                            maxTree[start[l] + levelI * levelC + levelJ + 1]) {
+                    if (selector.selectFirst(tree[start[l] + levelI * levelC + levelJ + 1],
+                                             tree[start[l] + levelI * levelC + levelJ])) {
                         levelJ++;
                     }
                 }
@@ -405,8 +320,8 @@ public class MinMaxTreeTile extends SimpleTile {
 
                 if (levelI + 1 < levelR) {
                     // the cell results from a regular merging of two rows
-                    if (maxTree[start[l] + levelI       * levelC + levelJ] <
-                            maxTree[start[l] + (levelI + 1) * levelC + levelJ]) {
+                    if (selector.selectFirst(tree[start[l] + (levelI + 1) * levelC + levelJ],
+                                             tree[start[l] + levelI       * levelC + levelJ])) {
                         levelI++;
                     }
                 }
@@ -420,18 +335,18 @@ public class MinMaxTreeTile extends SimpleTile {
         // used in interpolation
         // this imply the ancestor of min/max at (n, m) is one of
         // (2n, m), (2n+1, m), (2n+2, m), (2n, m+1), (2n+1, m+1), (2n+2, m+1)
-        int maxI = levelI;
-        int maxJ = 2 * levelJ;
-        double maxElevation = Double.NEGATIVE_INFINITY;
+        int selectedI = levelI;
+        int selectedJ = 2 * levelJ;
+        double selectedElevation = Double.NaN;
         for (int n = 2 * levelJ; n < 2 * levelJ + 3; ++n) {
             if (n < getLongitudeColumns()) {
                 for (int m = levelI; m < levelI + 2; ++m) {
                     if (m < getLatitudeRows()) {
                         final double elevation = raw[m * getLongitudeColumns() + n];
-                        if (elevation > maxElevation) {
-                            maxI         = m;
-                            maxJ         = n;
-                            maxElevation = elevation;
+                        if (selector.selectFirst(elevation, selectedElevation)) {
+                            selectedI         = m;
+                            selectedJ         = n;
+                            selectedElevation = elevation;
                         }
                     }
                 }
@@ -439,7 +354,7 @@ public class MinMaxTreeTile extends SimpleTile {
         }
 
         return new int[] {
-            maxI, maxJ
+            selectedI, selectedJ
         };
 
     }
@@ -638,11 +553,11 @@ public class MinMaxTreeTile extends SimpleTile {
      * @param elevations raw elevations te preprocess
      * @param nbRows number of rows
      * @param nbCols number of columns
-     * @param f function to apply
+     * @param selector selector to use
      */
     private void preprocess(final double[] preprocessed, final double[] elevations,
                             final int nbRows, final int nbCols,
-                            final BivariateFunction f) {
+                            final Selector selector) {
 
         int k = 0;
 
@@ -650,20 +565,20 @@ public class MinMaxTreeTile extends SimpleTile {
 
             // regular elements with both a column at right and a row below
             for (int j = 0; j < nbCols - 1; ++j) {
-                preprocessed[k] = f.value(f.value(elevations[k],          elevations[k + 1]),
-                                          f.value(elevations[k + nbCols], elevations[k + nbCols + 1]));
+                preprocessed[k] = selector.select(selector.select(elevations[k],          elevations[k + 1]),
+                                                  selector.select(elevations[k + nbCols], elevations[k + nbCols + 1]));
                 k++;
             }
 
             // last column elements, lacking a right column
-            preprocessed[k] = f.value(elevations[k], elevations[k + nbCols]);
+            preprocessed[k] = selector.select(elevations[k], elevations[k + nbCols]);
             k++;
 
         }
 
         // last row elements, lacking a below row
         for (int j = 0; j < nbCols - 1; ++j) {
-            preprocessed[k] = f.value(elevations[k], elevations[k + 1]);
+            preprocessed[k] = selector.select(elevations[k], elevations[k + 1]);
             k++;
         }
 
@@ -677,13 +592,13 @@ public class MinMaxTreeTile extends SimpleTile {
      * @param level current level
      * @param levelRows number of rows at current level
      * @param levelColumns number of columns at current level
-     * @param f function to apply
+     * @param selector to apply
      * @param base base array from which function arguments are drawn
      * @param first index of the first element to consider in base array
      */
     private void applyRecursively(final double[] tree,
                                   final int level, final int levelRows, final int levelColumns,
-                                  final BivariateFunction f,
+                                  final Selector selector,
                                   final double[] base, final int first) {
 
         if (isColumnMerging(level + 1)) {
@@ -698,7 +613,7 @@ public class MinMaxTreeTile extends SimpleTile {
 
                 // regular pairs
                 for (int j = 0; j < jEnd; ++j) {
-                    tree[iTree++] = f.value(base[iBase], base[iBase + 1]);
+                    tree[iTree++] = selector.select(base[iBase], base[iBase + 1]);
                     iBase += 2;
                 }
 
@@ -711,7 +626,7 @@ public class MinMaxTreeTile extends SimpleTile {
             }
 
             if (level > 0) {
-                applyRecursively(tree, level - 1, levelRows, nextColumns, f, tree, start[level]);
+                applyRecursively(tree, level - 1, levelRows, nextColumns, selector, tree, start[level]);
             }
 
         } else {
@@ -727,7 +642,7 @@ public class MinMaxTreeTile extends SimpleTile {
             for (int i = 0; i < iEnd; ++i) {
 
                 for (int j = 0; j < levelColumns; ++j) {
-                    tree[iTree++] = f.value(base[iBase], base[iBase + levelColumns]);
+                    tree[iTree++] = selector.select(base[iBase], base[iBase + levelColumns]);
                     iBase++;
                 }
                 iBase += levelColumns;
@@ -740,7 +655,7 @@ public class MinMaxTreeTile extends SimpleTile {
             }
 
             if (level > 0) {
-                applyRecursively(tree, level - 1, nextRows, levelColumns, f, tree, start[level]);
+                applyRecursively(tree, level - 1, nextRows, levelColumns, selector, tree, start[level]);
             }
 
         }
diff --git a/src/main/java/org/orekit/rugged/raster/SimpleTile.java b/src/main/java/org/orekit/rugged/raster/SimpleTile.java
index 2fe4d842..5cc3c0a1 100644
--- a/src/main/java/org/orekit/rugged/raster/SimpleTile.java
+++ b/src/main/java/org/orekit/rugged/raster/SimpleTile.java
@@ -16,6 +16,8 @@
  */
 package org.orekit.rugged.raster;
 
+import java.util.Arrays;
+
 import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
 import org.apache.commons.math3.util.FastMath;
 import org.apache.commons.math3.util.Precision;
@@ -23,6 +25,8 @@ import org.orekit.bodies.GeodeticPoint;
 import org.orekit.rugged.errors.DumpManager;
 import org.orekit.rugged.errors.RuggedException;
 import org.orekit.rugged.errors.RuggedMessages;
+import org.orekit.rugged.utils.MaxSelector;
+import org.orekit.rugged.utils.MinSelector;
 import org.orekit.rugged.utils.NormalizedGeodeticPoint;
 
 
@@ -105,6 +109,7 @@ public class SimpleTile implements Tile {
             throw new RuggedException(RuggedMessages.EMPTY_TILE, newLatitudeRows, newLongitudeColumns);
         }
         this.elevations = new double[newLatitudeRows * newLongitudeColumns];
+        Arrays.fill(elevations, Double.NaN);
 
     }
 
@@ -224,20 +229,20 @@ public class SimpleTile implements Tile {
 
     /** {@inheritDoc} */
     @Override
-    public void setElevation(final int latitudeIndex, final int longitudeIndex,
-                             final double elevation) throws RuggedException {
+    public void setElevation(final int latitudeIndex, final int longitudeIndex, final double elevation)
+        throws RuggedException {
         if (latitudeIndex  < 0 || latitudeIndex  > (latitudeRows - 1) ||
             longitudeIndex < 0 || longitudeIndex > (longitudeColumns - 1)) {
             throw new RuggedException(RuggedMessages.OUT_OF_TILE_INDICES,
                                       latitudeIndex, longitudeIndex,
                                       latitudeRows - 1, longitudeColumns - 1);
         }
-        if (elevation < minElevation) {
+        if (MinSelector.getInstance().selectFirst(elevation, minElevation)) {
             minElevation               = elevation;
             minElevationLatitudeIndex  = latitudeIndex;
             minElevationLongitudeIndex = longitudeIndex;
         }
-        if (elevation > maxElevation) {
+        if (MaxSelector.getInstance().selectFirst(elevation, maxElevation)) {
             maxElevation               = elevation;
             maxElevationLatitudeIndex  = latitudeIndex;
             maxElevationLongitudeIndex = longitudeIndex;
diff --git a/src/main/java/org/orekit/rugged/utils/MaxSelector.java b/src/main/java/org/orekit/rugged/utils/MaxSelector.java
new file mode 100644
index 00000000..44c706b2
--- /dev/null
+++ b/src/main/java/org/orekit/rugged/utils/MaxSelector.java
@@ -0,0 +1,74 @@
+/* Copyright 2013-2015 CS Systèmes d'Information
+ * Licensed to CS Systèmes d'Information (CS) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS 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.orekit.rugged.utils;
+
+/** Selector for max value.
+ * <p>
+ * This selector considers {@code Double.NaN} values correspond
+ * to non-initialized data that should be ignored rather than
+ * selected.
+ * </p>
+ * @see MinSelector
+ * @author Luc Maisonobe
+ */
+public class MaxSelector extends Selector {
+
+    /** Private constructor for singleton.
+     */
+    private MaxSelector() {
+    }
+
+    /** Get the unique instance.
+     * @return unique instance of the min selector.
+     */
+    public static MaxSelector getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    /** Check if first value should be selected.
+     * @param v1 first value
+     * @param v2 second value
+     * @return true if v1 is higher than v2, or if v2 is {@code Double.NaN}
+     */
+    @Override
+    public boolean selectFirst(final double v1, final double v2) {
+        return v1 > v2 || Double.isNaN(v2);
+    }
+
+    /** Holder for the min selector singleton.
+     * <p>
+     * We use the Initialization On Demand Holder Idiom to store
+     * the singletons, as it is both thread-safe, efficient (no
+     * synchronization) and works with all versions of java.
+     * </p>
+     */
+    private static class LazyHolder {
+
+        /** Unique instance. */
+        private static final MaxSelector INSTANCE = new MaxSelector();
+
+        /** Private constructor.
+         * <p>This class is a utility class, it should neither have a public
+         * nor a default constructor. This private constructor prevents
+         * the compiler from generating one automatically.</p>
+         */
+        private LazyHolder() {
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/orekit/rugged/utils/MinSelector.java b/src/main/java/org/orekit/rugged/utils/MinSelector.java
new file mode 100644
index 00000000..7a32ee80
--- /dev/null
+++ b/src/main/java/org/orekit/rugged/utils/MinSelector.java
@@ -0,0 +1,74 @@
+/* Copyright 2013-2015 CS Systèmes d'Information
+ * Licensed to CS Systèmes d'Information (CS) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS 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.orekit.rugged.utils;
+
+/** Selector for min value.
+ * <p>
+ * This selector considers {@code Double.NaN} values correspond
+ * to non-initialized data that should be ignored rather than
+ * selected.
+ * </p>
+ * @see MaxSelector
+ * @author Luc Maisonobe
+ */
+public class MinSelector extends Selector {
+
+    /** Private constructor for singleton.
+     */
+    private MinSelector() {
+    }
+
+    /** Get the unique instance.
+     * @return unique instance of the min selector.
+     */
+    public static MinSelector getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    /** Check if first value should be selected.
+     * @param v1 first value
+     * @param v2 second value
+     * @return true if v1 is lower than v2, or if v2 is {@code Double.NaN}
+     */
+    @Override
+    public boolean selectFirst(final double v1, final double v2) {
+        return v1 < v2 || Double.isNaN(v2);
+    }
+
+    /** Holder for the min selector singleton.
+     * <p>
+     * We use the Initialization On Demand Holder Idiom to store
+     * the singletons, as it is both thread-safe, efficient (no
+     * synchronization) and works with all versions of java.
+     * </p>
+     */
+    private static class LazyHolder {
+
+        /** Unique instance. */
+        private static final MinSelector INSTANCE = new MinSelector();
+
+        /** Private constructor.
+         * <p>This class is a utility class, it should neither have a public
+         * nor a default constructor. This private constructor prevents
+         * the compiler from generating one automatically.</p>
+         */
+        private LazyHolder() {
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/orekit/rugged/utils/Selector.java b/src/main/java/org/orekit/rugged/utils/Selector.java
new file mode 100644
index 00000000..060a8801
--- /dev/null
+++ b/src/main/java/org/orekit/rugged/utils/Selector.java
@@ -0,0 +1,43 @@
+/* Copyright 2013-2015 CS Systèmes d'Information
+ * Licensed to CS Systèmes d'Information (CS) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS 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.orekit.rugged.utils;
+
+
+/** Class for selecting one value among two.
+ * @see MinSelector
+ * @see MaxSelector
+ * @author Luc Maisonobe
+ */
+public abstract class Selector {
+
+    /** Check if first value should be selected.
+     * @param v1 first value
+     * @param v2 second value
+     * @return true if v1 should be selected
+     */
+    public abstract boolean selectFirst(double v1, double v2);
+
+    /** Select a value.
+     * @param v1 first value
+     * @param v2 second value
+     * @return selected value
+     */
+    public double select(final double v1, final double v2) {
+        return selectFirst(v1, v2) ? v1 : v2;
+    }
+
+}
diff --git a/src/test/java/org/orekit/rugged/errors/DumpManagerTest.java b/src/test/java/org/orekit/rugged/errors/DumpManagerTest.java
index f073cbe3..77eda7bd 100644
--- a/src/test/java/org/orekit/rugged/errors/DumpManagerTest.java
+++ b/src/test/java/org/orekit/rugged/errors/DumpManagerTest.java
@@ -21,6 +21,7 @@ import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
+import java.io.PrintStream;
 import java.net.URISyntaxException;
 
 import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
@@ -77,6 +78,8 @@ public class DumpManagerTest {
         int countDirectLoc       = 0;
         int countDirectLocResult = 0;
         BufferedReader br = new BufferedReader(new FileReader(dump));
+        PrintStream out = new PrintStream("/home/luc/sources/eclipse/rugged/src/test/resources/replay/replay-direct-loc-02.txt",
+                                          "UTF-8");
         for (String line = br.readLine(); line != null; line = br.readLine()) {
             String trimmed = line.trim();
             if (trimmed.length() > 0 && !trimmed.startsWith("#")){
@@ -100,8 +103,10 @@ public class DumpManagerTest {
                    Assert.fail(line);
                 }
             }
+            out.println(line);
         }
         br.close();
+        out.close();
         Assert.assertEquals(1,   countAlgorithm);
         Assert.assertEquals(1,   countEllipsoid);
         Assert.assertEquals(1,   countSpan);
diff --git a/src/test/java/org/orekit/rugged/errors/DumpReplayerTest.java b/src/test/java/org/orekit/rugged/errors/DumpReplayerTest.java
index 2ab2bf16..c628d180 100644
--- a/src/test/java/org/orekit/rugged/errors/DumpReplayerTest.java
+++ b/src/test/java/org/orekit/rugged/errors/DumpReplayerTest.java
@@ -21,6 +21,7 @@ import java.io.File;
 import java.io.IOException;
 import java.net.URISyntaxException;
 
+import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
 import org.junit.Assert;
 import org.junit.Test;
 import org.orekit.bodies.GeodeticPoint;
@@ -47,9 +48,9 @@ public class DumpReplayerTest {
         for (final DumpReplayer.Result result : results) {
             GeodeticPoint expectedGP = (GeodeticPoint) result.getExpected();
             GeodeticPoint replayedGP = (GeodeticPoint) result.getReplayed();
-            Assert.assertEquals(expectedGP.getLatitude(),  replayedGP.getLatitude(),  1.0e-12);
-            Assert.assertEquals(expectedGP.getLongitude(), replayedGP.getLongitude(), 1.0e-12);
-            Assert.assertEquals(expectedGP.getAltitude(),  replayedGP.getAltitude(),  1.0e-6);
+            double distance = Vector3D.distance(rugged.getEllipsoid().transform(expectedGP),
+                                                rugged.getEllipsoid().transform(replayedGP));
+            Assert.assertEquals(0.0, distance, 3.0e-9);
         }
 
     }
diff --git a/src/test/java/org/orekit/rugged/raster/SimpleTileTest.java b/src/test/java/org/orekit/rugged/raster/SimpleTileTest.java
index 0e2ba0a6..3e33de62 100644
--- a/src/test/java/org/orekit/rugged/raster/SimpleTileTest.java
+++ b/src/test/java/org/orekit/rugged/raster/SimpleTileTest.java
@@ -173,7 +173,8 @@ public class SimpleTileTest {
         tile.setElevation(21, 15,  95.0);
         tile.tileUpdateCompleted();
         Assert.assertEquals(150.5, tile.interpolateElevation(20.0, 14.5), 1.0e-10);
-        Assert.assertEquals(128.5, tile.interpolateElevation(21.0, 14.5), 1.0e-10);
+        Assert.assertEquals(128.5, tile.interpolateElevation(FastMath.nextDown(21.0), 14.5), 1.0e-10);
+        Assert.assertTrue(Double.isNaN(tile.interpolateElevation(FastMath.nextUp(21.0), 14.5)));
         Assert.assertEquals(146.1, tile.interpolateElevation(20.2, 14.5), 1.0e-10);
     }
 
diff --git a/src/test/resources/replay/replay-direct-loc-01.txt b/src/test/resources/replay/replay-direct-loc-01.txt
index 610b0274..605137c0 100644
--- a/src/test/resources/replay/replay-direct-loc-01.txt
+++ b/src/test/resources/replay/replay-direct-loc-01.txt
@@ -1,8 +1,8 @@
-# Rugged library dump file, created on 2015-02-10T16:00:15Z
+# Rugged library dump file, created on 2015-02-11T14:08:32Z
 # all units are SI units (m, m/s, rad ...)
 direct location: date 2012-01-01T12:30:00.00000000000000Z position  1.500000000000000e+00  0.000000000000000e+00 -2.000000000000000e-01 los  0.000000000000000e+00 -7.547095802227720e-01  6.560590289905073e-01 lightTime true aberration true
 span: minDate 2012-01-01T12:29:59.85000000000000Z maxDate 2012-01-01T12:30:00.15000000000000Z tStep  1.000000000000000e-03 tolerance  5.000000000000000e+00 inertialFrame EME2000
-transform: index 150 body r -8.085963389171905e-01 -3.465415132416125e-04  4.896468952533137e-04 -5.883634938068593e-01 Ω -8.740475534355122e-08  1.215132763920864e-09 -7.292109805268457e-05 ΩDot -1.642299174832679e-16  8.973031065665686e-17  1.983408395824453e-19 spacecraft p  1.384771423708159e+04  3.157872644483112e+03 -7.179504513218164e+06 v -3.193269831348565e+01 -8.025700179158079e+00  8.276060585645734e+00 a -9.306388141791047e-01 -8.320023410534990e+00  1.352798021525103e-03 r -6.828948932066574e-01  4.142451147005848e-01 -3.878489669799004e-01  4.600312256701621e-01 Ω -1.009835959093257e-03  1.982938126604663e-04  1.645927204535755e-04 ΩDot -3.647403055483546e-07  2.008714378022283e-07 -1.257148591486377e-06
+transform: index 150 body r -8.085963389171905e-01 -3.465415132416124e-04  4.896468952533136e-04 -5.883634938068593e-01 Ω -8.740475534355121e-08  1.215132763920863e-09 -7.292109805268457e-05 ΩDot -1.642299174832473e-16  8.973031065833714e-17  1.983408395826415e-19 spacecraft p  1.384771423708159e+04  3.157872644483112e+03 -7.179504513218164e+06 v -3.193269831348565e+01 -8.025700179158079e+00  8.276060585645734e+00 a -9.306388141791047e-01 -8.320023410534990e+00  1.352798021525103e-03 r -6.828948932066574e-01  4.142451147005848e-01 -3.878489669799004e-01  4.600312256701621e-01 Ω -1.009835959093257e-03  1.982938126604663e-04  1.645927204535755e-04 ΩDot -3.647403055483546e-07  2.008714378022283e-07 -1.257148591486377e-06
 ellipsoid: ae  6.378137000000000e+06 f  3.352810664747481e-03 frame ITRF_CIO_CONV_2010_SIMPLE_EOP
 algorithm: DUVENHAGE
 DEM tile: t0 latMin -4.014257279586958e-01 latStep  6.817692390602850e-05 latRows 257 lonMin  2.495820830351891e+00 lonStep  6.817692390602850e-05 lonCols 257
@@ -40,16 +40,26 @@ DEM cell: t0 latIndex 98 lonIndex 49 elevation -2.653137720265925e+01
 DEM cell: t0 latIndex 97 lonIndex 48 elevation -9.460844691182658e+01
 DEM cell: t0 latIndex 98 lonIndex 48 elevation -6.303622859747302e+01
 direct location result: latitude -3.947611273580563e-01 longitude  2.499133846389992e+00 elevation -4.295871413137783e+01
+direct location: date 2012-01-01T12:30:00.00000000000000Z position  1.500000000000000e+00  0.000000000000000e+00 -2.000000000000000e-01 los  0.000000000000000e+00 -7.595223858294946e-01  6.504811645419663e-01 lightTime true aberration true
 DEM cell: t0 latIndex 91 lonIndex 0 elevation -4.644285910379659e+02
 DEM cell: t0 latIndex 92 lonIndex 0 elevation -6.361521230433466e+02
 DEM cell: t0 latIndex 91 lonIndex 1 elevation -6.695324221432916e+02
 DEM cell: t0 latIndex 92 lonIndex 1 elevation -6.941696826528058e+02
-DEM cell: t0 latIndex 91 lonIndex 2 elevation -7.590003401646543e+02
-DEM cell: t0 latIndex 90 lonIndex 3 elevation -8.207436833089408e+02
+DEM cell: t0 latIndex 96 lonIndex 12 elevation -6.229043689225860e+02
+DEM cell: t0 latIndex 97 lonIndex 12 elevation -6.074403513292080e+02
+DEM cell: t0 latIndex 96 lonIndex 13 elevation -6.849894309179065e+02
+DEM cell: t0 latIndex 97 lonIndex 13 elevation -6.129357249231432e+02
 DEM cell: t0 latIndex 90 lonIndex 4 elevation -8.534523757445398e+02
-DEM cell: t0 latIndex 90 lonIndex 2 elevation -7.924361301694835e+02
+DEM cell: t0 latIndex 91 lonIndex 4 elevation -8.996784383751103e+02
+DEM cell: t0 latIndex 90 lonIndex 5 elevation -9.026655203899021e+02
+DEM cell: t0 latIndex 91 lonIndex 5 elevation -9.279387777828925e+02
+DEM cell: t0 latIndex 92 lonIndex 2 elevation -7.517293327072680e+02
+DEM cell: t0 latIndex 93 lonIndex 2 elevation -7.766413818620708e+02
+DEM cell: t0 latIndex 92 lonIndex 3 elevation -8.594398740278656e+02
+DEM cell: t0 latIndex 93 lonIndex 3 elevation -8.590835217917245e+02
 DEM cell: t0 latIndex 90 lonIndex 1 elevation -7.251222469503653e+02
-direct location: date 2012-01-01T12:30:00.00000000000000Z position  1.500000000000000e+00  0.000000000000000e+00 -2.000000000000000e-01 los  0.000000000000000e+00 -7.595223858294946e-01  6.504811645419663e-01 lightTime true aberration true
+DEM cell: t0 latIndex 90 lonIndex 2 elevation -7.924361301694835e+02
+DEM cell: t0 latIndex 91 lonIndex 2 elevation -7.590003401646543e+02
 DEM cell: t0 latIndex 90 lonIndex 0 elevation -5.786400093316511e+02
 direct location result: latitude -3.952555711362365e-01 longitude  2.495847653590435e+00 elevation -5.905034290161145e+02
 direct location: date 2012-01-01T12:30:00.00000000000000Z position  1.500000000000000e+00  0.000000000000000e+00 -2.000000000000000e-01 los  0.000000000000000e+00 -7.596364750290255e-01  6.503479267326657e-01 lightTime true aberration true
@@ -60,23 +70,15 @@ DEM cell: t1 latIndex 90 lonIndex 255 elevation -6.028136533065612e+02
 DEM cell: t1 latIndex 90 lonIndex 256 elevation -5.786400093316511e+02
 DEM cell: t1 latIndex 91 lonIndex 255 elevation -6.221990476940956e+02
 DEM cell: t1 latIndex 91 lonIndex 256 elevation -4.644285910379659e+02
-DEM cell: t1 latIndex 88 lonIndex 252 elevation  0.000000000000000e+00
-DEM cell: t1 latIndex 89 lonIndex 252 elevation -4.877794667497003e+02
-DEM cell: t1 latIndex 88 lonIndex 253 elevation  0.000000000000000e+00
-DEM cell: t1 latIndex 89 lonIndex 253 elevation -5.757446064041551e+02
-DEM cell: t1 latIndex 91 lonIndex 252 elevation  0.000000000000000e+00
-DEM cell: t1 latIndex 92 lonIndex 252 elevation  0.000000000000000e+00
-DEM cell: t1 latIndex 91 lonIndex 253 elevation -6.152228016740617e+02
-DEM cell: t1 latIndex 92 lonIndex 253 elevation  0.000000000000000e+00
 DEM cell: t1 latIndex 92 lonIndex 256 elevation -6.361521230433466e+02
 DEM cell: t1 latIndex 91 lonIndex 257 elevation -6.361521230433466e+02
 DEM cell: t1 latIndex 92 lonIndex 257 elevation -5.119072938899205e+02
 direct location result: latitude -3.952672865845636e-01 longitude  2.495769458919830e+00 elevation -5.923807760050810e+02
 direct location: date 2012-01-01T12:30:00.00000000000000Z position  1.500000000000000e+00  0.000000000000000e+00 -2.000000000000000e-01 los  0.000000000000000e+00 -7.597505408555608e-01  6.502146689130315e-01 lightTime true aberration true
-DEM cell: t1 latIndex 88 lonIndex 248 elevation  0.000000000000000e+00
-DEM cell: t1 latIndex 89 lonIndex 248 elevation -2.769462712597335e+02
-DEM cell: t1 latIndex 88 lonIndex 249 elevation  0.000000000000000e+00
-DEM cell: t1 latIndex 89 lonIndex 249 elevation -3.314207112316006e+02
+DEM cell: t1 latIndex 92 lonIndex 248 elevation -1.470524675650724e+02
+DEM cell: t1 latIndex 93 lonIndex 248 elevation -1.448837787295385e+02
+DEM cell: t1 latIndex 92 lonIndex 249 elevation -2.503632800423777e+02
+DEM cell: t1 latIndex 93 lonIndex 249 elevation -2.077565123004881e+02
 DEM cell: t1 latIndex 90 lonIndex 254 elevation -5.859504161342276e+02
 DEM cell: t1 latIndex 91 lonIndex 254 elevation -6.458840770174961e+02
 direct location result: latitude -3.952790935038382e-01 longitude  2.495690638626864e+00 elevation -5.963149908542051e+02
-- 
GitLab