From bfa8d8a7af76b550586c1fb95489a98b76d1500c Mon Sep 17 00:00:00 2001
From: Luc Maisonobe <luc@orekit.org>
Date: Sat, 11 Dec 2021 10:38:32 +0100
Subject: [PATCH] Fixed longitude normalization issues.

Fixes #388
---
 src/changes/changes.xml                       |   3 +
 .../org/orekit/rugged/raster/SimpleTile.java  |  33 +++--
 .../java/org/orekit/rugged/raster/Tile.java   |   5 +-
 .../orekit/rugged/raster/SimpleTileTest.java  | 136 +++++++++++-------
 4 files changed, 107 insertions(+), 70 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index fbc9ab78..af17c43f 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -21,6 +21,9 @@
   </properties>
   <body>
     <release version="3.0" date="TBD" description="TBD">
+      <action dev="luc" type="update" issue="388">
+        Fixed longitude normalization issue with tiles.
+      </action>
       <action dev="luc" type="update" issue="387">
         Updated dependencies to Orekit 11.0.1 (and Hipparchus 2.0).
       </action>
diff --git a/src/main/java/org/orekit/rugged/raster/SimpleTile.java b/src/main/java/org/orekit/rugged/raster/SimpleTile.java
index 07e8dade..053c9993 100644
--- a/src/main/java/org/orekit/rugged/raster/SimpleTile.java
+++ b/src/main/java/org/orekit/rugged/raster/SimpleTile.java
@@ -16,12 +16,11 @@
  */
 package org.orekit.rugged.raster;
 
+import java.util.Arrays;
+
 import org.hipparchus.geometry.euclidean.threed.Vector3D;
 import org.hipparchus.util.FastMath;
 import org.hipparchus.util.Precision;
-import java.util.Arrays;
-
-import org.orekit.bodies.GeodeticPoint;
 import org.orekit.rugged.errors.DumpManager;
 import org.orekit.rugged.errors.RuggedException;
 import org.orekit.rugged.errors.RuggedMessages;
@@ -302,7 +301,7 @@ public class SimpleTile implements Tile {
 
     /** {@inheritDoc} */
     @Override
-    public NormalizedGeodeticPoint cellIntersection(final GeodeticPoint p, final Vector3D los,
+    public NormalizedGeodeticPoint cellIntersection(final NormalizedGeodeticPoint p, final Vector3D los,
                                                     final int latitudeIndex, final int longitudeIndex) {
 
         // ensure neighboring cells to not fall out of tile
@@ -317,10 +316,16 @@ public class SimpleTile implements Tile {
         final double z10 = getElevationAtIndices(iLat,     jLong + 1);
         final double z11 = getElevationAtIndices(iLat + 1, jLong + 1);
 
+        // normalize back to tile coordinates
+        final NormalizedGeodeticPoint tileP = new NormalizedGeodeticPoint(p.getLatitude(),
+                                                                          p.getLongitude(),
+                                                                          p.getAltitude(),
+                                                                          x00);
+
         // line-of-sight coordinates at close points
-        final double dxA = (p.getLongitude() - x00) / longitudeStep;
-        final double dyA = (p.getLatitude()  - y00) / latitudeStep;
-        final double dzA = p.getAltitude();
+        final double dxA = (tileP.getLongitude() - x00) / longitudeStep;
+        final double dyA = (tileP.getLatitude()  - y00) / latitudeStep;
+        final double dzA = tileP.getAltitude();
         final double dxB = dxA + los.getX() / longitudeStep;
         final double dyB = dyA + los.getY() / latitudeStep;
         final double dzB = dzA + los.getZ();
@@ -368,8 +373,8 @@ public class SimpleTile implements Tile {
 
         }
 
-        final NormalizedGeodeticPoint p1 = interpolate(t1, p, dxA, dyA, los, x00);
-        final NormalizedGeodeticPoint p2 = interpolate(t2, p, dxA, dyA, los, x00);
+        final NormalizedGeodeticPoint p1 = interpolate(t1, tileP, dxA, dyA, los, x00);
+        final NormalizedGeodeticPoint p2 = interpolate(t2, tileP, dxA, dyA, los, x00);
 
         // select the first point along line-of-sight
         if (p1 == null) {
@@ -384,7 +389,7 @@ public class SimpleTile implements Tile {
 
     /** Interpolate point along a line.
      * @param t abscissa along the line
-     * @param p start point
+     * @param tileP start point, normalized to tile area
      * @param dxP relative coordinate of the start point with respect to current cell
      * @param dyP relative coordinate of the start point with respect to current cell
      * @param los direction of the line-of-sight, in geodetic space
@@ -392,7 +397,7 @@ public class SimpleTile implements Tile {
      * be normalized between lc-Ï€ and lc+Ï€
      * @return interpolated point along the line
      */
-    private NormalizedGeodeticPoint interpolate(final double t, final GeodeticPoint p,
+    private NormalizedGeodeticPoint interpolate(final double t, final NormalizedGeodeticPoint tileP,
                                                 final double dxP, final double dyP,
                                                 final Vector3D los, final double centralLongitude) {
 
@@ -403,9 +408,9 @@ public class SimpleTile implements Tile {
         final double dx = dxP + t * los.getX() / longitudeStep;
         final double dy = dyP + t * los.getY() / latitudeStep;
         if (dx >= -TOLERANCE && dx <= 1 + TOLERANCE && dy >= -TOLERANCE && dy <= 1 + TOLERANCE) {
-            return new NormalizedGeodeticPoint(p.getLatitude()  + t * los.getY(),
-                                               p.getLongitude() + t * los.getX(),
-                                               p.getAltitude()  + t * los.getZ(),
+            return new NormalizedGeodeticPoint(tileP.getLatitude()  + t * los.getY(),
+                                               tileP.getLongitude() + t * los.getX(),
+                                               tileP.getAltitude()  + t * los.getZ(),
                                                centralLongitude);
         } else {
             return null;
diff --git a/src/main/java/org/orekit/rugged/raster/Tile.java b/src/main/java/org/orekit/rugged/raster/Tile.java
index ce66a126..ccdb66dc 100644
--- a/src/main/java/org/orekit/rugged/raster/Tile.java
+++ b/src/main/java/org/orekit/rugged/raster/Tile.java
@@ -17,7 +17,6 @@
 package org.orekit.rugged.raster;
 
 import org.hipparchus.geometry.euclidean.threed.Vector3D;
-import org.orekit.bodies.GeodeticPoint;
 import org.orekit.rugged.utils.NormalizedGeodeticPoint;
 
 /** Interface representing a raster tile.
@@ -265,8 +264,8 @@ public interface Tile extends UpdatableTile {
      * @return point corresponding to line-of-sight crossing the Digital Elevation Model surface
      * if it lies within the cell, null otherwise
      */
-    NormalizedGeodeticPoint cellIntersection(GeodeticPoint p, Vector3D los,
-                                              int latitudeIndex, int longitudeIndex);
+    NormalizedGeodeticPoint cellIntersection(NormalizedGeodeticPoint p, Vector3D los,
+                                             int latitudeIndex, int longitudeIndex);
 
     /** Check if a tile covers a ground point.
      * @param latitude ground point latitude
diff --git a/src/test/java/org/orekit/rugged/raster/SimpleTileTest.java b/src/test/java/org/orekit/rugged/raster/SimpleTileTest.java
index a5b219ed..64fcb92d 100644
--- a/src/test/java/org/orekit/rugged/raster/SimpleTileTest.java
+++ b/src/test/java/org/orekit/rugged/raster/SimpleTileTest.java
@@ -18,12 +18,14 @@ package org.orekit.rugged.raster;
 
 import org.hipparchus.geometry.euclidean.threed.Vector3D;
 import org.hipparchus.util.FastMath;
+import org.hipparchus.util.MathUtils;
 import org.junit.Assert;
 import org.junit.Test;
 import org.orekit.bodies.GeodeticPoint;
 import org.orekit.rugged.errors.RuggedException;
 import org.orekit.rugged.errors.RuggedMessages;
 import org.orekit.rugged.raster.Tile.Location;
+import org.orekit.rugged.utils.NormalizedGeodeticPoint;
 
 public class SimpleTileTest {
 
@@ -216,16 +218,44 @@ public class SimpleTileTest {
         tile.setElevation(21, 14, 162.0);
         tile.setElevation(21, 15,  95.0);
         tile.tileUpdateCompleted();
-        GeodeticPoint gpA = new GeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.1 * tile.getLatitudeStep(),
-                                              tile.getLongitudeAtIndex(14) + 0.2 * tile.getLongitudeStep(),
-                                              300.0);
-        GeodeticPoint gpB = new GeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.7 * tile.getLatitudeStep(),
-                                              tile.getLongitudeAtIndex(14) + 0.9 * tile.getLongitudeStep(),
-                                              10.0);
-        GeodeticPoint gpIAB = tile.cellIntersection(gpA, los(gpA, gpB), 20, 14);
+        NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.1 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(14) + 0.2 * tile.getLongitudeStep(),
+                                                                  300.0, tile.getLongitudeAtIndex(14));
+        NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.7 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(14) + 0.9 * tile.getLongitudeStep(),
+                                                                  10.0, tile.getLongitudeAtIndex(14));
+        NormalizedGeodeticPoint gpIAB = tile.cellIntersection(gpA, los(gpA, gpB), 20, 14);
         checkInLine(gpA, gpB, gpIAB);
         checkOnTile(tile, gpIAB);
-        GeodeticPoint gpIBA = tile.cellIntersection(gpB, los(gpB, gpA), 20, 14);
+        NormalizedGeodeticPoint gpIBA = tile.cellIntersection(gpB, los(gpB, gpA), 20, 14);
+        checkInLine(gpA, gpB, gpIBA);
+        checkOnTile(tile, gpIBA);
+
+        Assert.assertEquals(gpIAB.getLatitude(),  gpIBA.getLatitude(),  1.0e-10);
+        Assert.assertEquals(gpIAB.getLongitude(), gpIBA.getLongitude(), 1.0e-10);
+        Assert.assertEquals(gpIAB.getAltitude(),  gpIBA.getAltitude(),  1.0e-10);
+
+    }
+
+    @Test
+    public void testCellIntersection2PiWrapping() {
+        SimpleTile tile = new SimpleTileFactory().createTile();
+        tile.setGeometry(0.0, 0.0, 0.025, 0.025, 50, 50);
+        tile.setElevation(20, 14,  91.0);
+        tile.setElevation(20, 15, 210.0);
+        tile.setElevation(21, 14, 162.0);
+        tile.setElevation(21, 15,  95.0);
+        tile.tileUpdateCompleted();
+        NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.1 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(14) + 0.2 * tile.getLongitudeStep(),
+                                                                  300.0, +4 * FastMath.PI);
+        NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.7 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(14) + 0.9 * tile.getLongitudeStep(),
+                                                                  10.0, +4 * FastMath.PI);
+        NormalizedGeodeticPoint gpIAB = tile.cellIntersection(gpA, los(gpA, gpB), 20, 14);
+        checkInLine(gpA, gpB, gpIAB);
+        checkOnTile(tile, gpIAB);
+        NormalizedGeodeticPoint gpIBA = tile.cellIntersection(gpB, los(gpB, gpA), 20, 14);
         checkInLine(gpA, gpB, gpIBA);
         checkOnTile(tile, gpIBA);
 
@@ -244,19 +274,19 @@ public class SimpleTileTest {
         tile.setElevation(21, 14, 162.0);
         tile.setElevation(21, 15,  95.0);
         tile.tileUpdateCompleted();
-        GeodeticPoint gpA = new GeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.1 * tile.getLatitudeStep(),
-                                              tile.getLongitudeAtIndex(14) + 0.2 * tile.getLongitudeStep(),
-                                              120.0);
-        GeodeticPoint gpB = new GeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.7 * tile.getLatitudeStep(),
-                                              tile.getLongitudeAtIndex(14) + 0.9 * tile.getLongitudeStep(),
-                                              130.0);
+        NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.1 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(14) + 0.2 * tile.getLongitudeStep(),
+                                                                  120.0, tile.getLongitudeAtIndex(14));
+        NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.7 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(14) + 0.9 * tile.getLongitudeStep(),
+                                                                  130.0, tile.getLongitudeAtIndex(14));
 
         // the line from gpA to gpB should traverse the DEM twice within the tile
         // we use the points in the two different orders to retrieve both solutions
-        GeodeticPoint gpIAB = tile.cellIntersection(gpA, los(gpA, gpB), 20, 14);
+        NormalizedGeodeticPoint gpIAB = tile.cellIntersection(gpA, los(gpA, gpB), 20, 14);
         checkInLine(gpA, gpB, gpIAB);
         checkOnTile(tile, gpIAB);
-        GeodeticPoint gpIBA = tile.cellIntersection(gpB, los(gpB, gpA), 20, 14);
+        NormalizedGeodeticPoint gpIBA = tile.cellIntersection(gpB, los(gpB, gpA), 20, 14);
         checkInLine(gpA, gpB, gpIBA);
         checkOnTile(tile, gpIBA);
 
@@ -275,12 +305,12 @@ public class SimpleTileTest {
         tile.setElevation(21, 14, 162.0);
         tile.setElevation(21, 15,  95.0);
         tile.tileUpdateCompleted();
-        GeodeticPoint gpA = new GeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.1 * tile.getLatitudeStep(),
-                                              tile.getLongitudeAtIndex(14) + 0.2 * tile.getLongitudeStep(),
-                                              180.0);
-        GeodeticPoint gpB = new GeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.7 * tile.getLatitudeStep(),
-                                              tile.getLongitudeAtIndex(14) + 0.9 * tile.getLongitudeStep(),
-                                              190.0);
+        NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.1 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(14) + 0.2 * tile.getLongitudeStep(),
+                                                                  180.0, tile.getLongitudeAtIndex(14));
+        NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.7 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(14) + 0.9 * tile.getLongitudeStep(),
+                                                                  190.0, tile.getLongitudeAtIndex(14));
 
         Assert.assertNull(tile.cellIntersection(gpA, los(gpA, gpB), 20, 14));
 
@@ -295,17 +325,17 @@ public class SimpleTileTest {
         tile.setElevation(1, 0,  40.0);
         tile.setElevation(1, 1,  40.0);
         tile.tileUpdateCompleted();
-        GeodeticPoint gpA = new GeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.25 * tile.getLatitudeStep(),
-                                              tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
-                                              50.0);
-        GeodeticPoint gpB = new GeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.75 * tile.getLatitudeStep(),
-                                              tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
-                                              20.0);
-
-        GeodeticPoint gpIAB = tile.cellIntersection(gpA, los(gpA, gpB), 0, 0);
+        NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.25 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
+                                                                  50.0, tile.getLongitudeAtIndex(0));
+        NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.75 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
+                                                                  20.0, tile.getLongitudeAtIndex(0));
+
+        NormalizedGeodeticPoint gpIAB = tile.cellIntersection(gpA, los(gpA, gpB), 0, 0);
         checkInLine(gpA, gpB, gpIAB);
         checkOnTile(tile, gpIAB);
-        GeodeticPoint gpIBA = tile.cellIntersection(gpB, los(gpB, gpA), 0, 0);
+        NormalizedGeodeticPoint gpIBA = tile.cellIntersection(gpB, los(gpB, gpA), 0, 0);
         checkInLine(gpA, gpB, gpIBA);
         checkOnTile(tile, gpIBA);
 
@@ -324,12 +354,12 @@ public class SimpleTileTest {
         tile.setElevation(1, 0,  40.0);
         tile.setElevation(1, 1,  40.0);
         tile.tileUpdateCompleted();
-        GeodeticPoint gpA = new GeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.25 * tile.getLatitudeStep(),
-                                              tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
-                                              45.0);
-        GeodeticPoint gpB = new GeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.75 * tile.getLatitudeStep(),
-                                              tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
-                                              55.0);
+        NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.25 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
+                                                                  45.0, tile.getLongitudeAtIndex(0));
+        NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.75 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
+                                                                  55.0, tile.getLongitudeAtIndex(0));
 
        Assert.assertNull(tile.cellIntersection(gpA, los(gpA, gpB), 0, 0));
 
@@ -344,12 +374,12 @@ public class SimpleTileTest {
         tile.setElevation(1, 0,  40.0);
         tile.setElevation(1, 1,  40.0);
         tile.tileUpdateCompleted();
-        GeodeticPoint gpA = new GeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.25 * tile.getLatitudeStep(),
-                                              tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
-                                              45.0);
-        GeodeticPoint gpB = new GeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.75 * tile.getLatitudeStep(),
-                                              tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
-                                              50.0);
+        NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.25 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
+                                                                  45.0, tile.getLongitudeAtIndex(0));
+        NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.75 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
+                                                                  50.0, tile.getLongitudeAtIndex(0));
 
         Assert.assertNull(tile.cellIntersection(gpA, los(gpA, gpB), 0, 0));
 
@@ -364,17 +394,17 @@ public class SimpleTileTest {
         tile.setElevation(1, 0,  40.0);
         tile.setElevation(1, 1,  40.0);
         tile.tileUpdateCompleted();
-        GeodeticPoint gpA = new GeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.25 * tile.getLatitudeStep(),
-                                              tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
-                                              32.5);
-        GeodeticPoint gpB = new GeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.75 * tile.getLatitudeStep(),
-                                              tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
-                                              37.5);
-
-        GeodeticPoint gpIAB = tile.cellIntersection(gpA, los(gpA, gpB), 0, 0);
+        NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.25 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
+                                                                  32.5, tile.getLongitudeAtIndex(0));
+        NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.75 * tile.getLatitudeStep(),
+                                                                  tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
+                                                                  37.5, tile.getLongitudeAtIndex(0));
+
+        NormalizedGeodeticPoint gpIAB = tile.cellIntersection(gpA, los(gpA, gpB), 0, 0);
         checkInLine(gpA, gpB, gpIAB);
         checkOnTile(tile, gpIAB);
-        GeodeticPoint gpIBA = tile.cellIntersection(gpB, los(gpB, gpA), 0, 0);
+        NormalizedGeodeticPoint gpIBA = tile.cellIntersection(gpB, los(gpB, gpA), 0, 0);
         checkInLine(gpA, gpB, gpIBA);
         checkOnTile(tile, gpIBA);
 
@@ -432,7 +462,7 @@ public class SimpleTileTest {
                             1.0e-10);
 
         Assert.assertEquals(gpI.getLongitude(),
-                            gpA.getLongitude() * (1 - t) + gpB.getLongitude() * t,
+                            MathUtils.normalizeAngle(gpA.getLongitude() * (1 - t) + gpB.getLongitude() * t, gpI.getLongitude()),
                             1.0e-10);
 
     }
-- 
GitLab