1   /* Copyright 2013-2025 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.rugged.raster;
18  
19  import org.hipparchus.geometry.euclidean.threed.Vector3D;
20  import org.hipparchus.util.FastMath;
21  import org.hipparchus.util.MathUtils;
22  import org.junit.jupiter.api.Assertions;
23  import org.junit.jupiter.api.Test;
24  import org.orekit.bodies.GeodeticPoint;
25  import org.orekit.rugged.errors.RuggedException;
26  import org.orekit.rugged.errors.RuggedMessages;
27  import org.orekit.rugged.raster.Tile.Location;
28  import org.orekit.rugged.utils.NormalizedGeodeticPoint;
29  
30  public class SimpleTileTest {
31  
32      @Test
33      public void testNotConfigured() {
34          SimpleTile tile = new SimpleTileFactory().createTile();
35          Assertions.assertEquals(0, tile.getMinimumLatitude(), 1.0e-10);
36          Assertions.assertEquals(0, tile.getMinimumLongitude(), 1.0e-10);
37          Assertions.assertEquals(0, tile.getLatitudeStep(), 1.0e-10);
38          Assertions.assertEquals(0, tile.getLongitudeStep(), 1.0e-10);
39          Assertions.assertEquals(0, tile.getLatitudeRows());
40          Assertions.assertEquals(0, tile.getLongitudeColumns());
41          Assertions.assertEquals(0, tile.getMinElevation(), 1.0e-10);
42          Assertions.assertEquals(0, tile.getMaxElevation(), 1.0e-10);
43      }
44  
45      @Test
46      public void testEmpty() {
47          SimpleTile tile = new SimpleTileFactory().createTile();
48          try {
49              tile.setGeometry(1.0, 2.0, 0.1, 0.2, 0, 200);
50              Assertions.fail("an exception should have been thrown");
51          } catch (RuggedException re) {
52              Assertions.assertEquals(RuggedMessages.EMPTY_TILE, re.getSpecifier());
53              Assertions.assertEquals(  0, ((Integer) re.getParts()[0]).intValue());
54              Assertions.assertEquals(200, ((Integer) re.getParts()[1]).intValue());
55          }
56          try {
57              tile.setGeometry(1.0, 2.0, 0.1, 0.2, 100, 0);
58              Assertions.fail("an exception should have been thrown");
59          } catch (RuggedException re) {
60              Assertions.assertEquals(RuggedMessages.EMPTY_TILE, re.getSpecifier());
61              Assertions.assertEquals(100, ((Integer) re.getParts()[0]).intValue());
62              Assertions.assertEquals(  0, ((Integer) re.getParts()[1]).intValue());
63          }
64      }
65  
66      @Test
67      public void testUpdate() {
68  
69          SimpleTile tile = new SimpleTileFactory().createTile();
70          tile.setGeometry(1.0, 2.0, 0.1, 0.2, 100, 200);
71          for (int i = 0; i < tile.getLatitudeRows(); ++i) {
72              for (int j = 0; j < tile.getLongitudeColumns(); ++j) {
73                  tile.setElevation(i, j, 1000 * i + j);
74              }
75          }
76          tile.tileUpdateCompleted();
77  
78          Assertions.assertEquals(1.0, tile.getMinimumLatitude(), 1.0e-10);
79          Assertions.assertEquals(2.0, tile.getMinimumLongitude(), 1.0e-10);
80          Assertions.assertEquals(0.1, tile.getLatitudeStep(), 1.0e-10);
81          Assertions.assertEquals(0.2, tile.getLongitudeStep(), 1.0e-10);
82          Assertions.assertEquals(100, tile.getLatitudeRows());
83          Assertions.assertEquals(200, tile.getLongitudeColumns());
84          Assertions.assertEquals(0.0, tile.getMinElevation(), 1.0e-10);
85          Assertions.assertEquals(99199.0, tile.getMaxElevation(), 1.0e-10);
86  
87          Assertions.assertEquals(Location.SOUTH_WEST, tile.getLocation( 0.0,  1.0));
88          Assertions.assertEquals(Location.WEST,       tile.getLocation( 6.0,  1.0));
89          Assertions.assertEquals(Location.NORTH_WEST, tile.getLocation(12.0,  1.0));
90          Assertions.assertEquals(Location.SOUTH,      tile.getLocation( 0.0, 22.0));
91          Assertions.assertEquals(Location.HAS_INTERPOLATION_NEIGHBORS,    tile.getLocation( 6.0, 22.0));
92          Assertions.assertEquals(Location.NORTH,      tile.getLocation(12.0, 22.0));
93          Assertions.assertEquals(Location.SOUTH_EAST, tile.getLocation( 0.0, 43.0));
94          Assertions.assertEquals(Location.EAST,       tile.getLocation( 6.0, 43.0));
95          Assertions.assertEquals(Location.NORTH_EAST, tile.getLocation(12.0, 43.0));
96          for (int i = 0; i < tile.getLatitudeRows(); ++i) {
97              for (int j = 0; j < tile.getLongitudeColumns(); ++j) {
98                  Assertions.assertEquals(1000 * i + j, tile.getElevationAtIndices(i, j), 1.0e-10);
99              }
100         }
101 
102     }
103     
104     @Test
105     public void testZipper() {
106 
107         SimpleTile tile0 = new SimpleTileFactory().createTile();
108         tile0.setGeometry(1.0, 2.0, 0.1, 0.2, 100, 200);
109         for (int i = 0; i < tile0.getLatitudeRows(); ++i) {
110             for (int j = 0; j < tile0.getLongitudeColumns(); ++j) {
111                 tile0.setElevation(i, j, 1000 * i + j);
112             }
113         }
114         tile0.tileUpdateCompleted();
115 
116         Assertions.assertEquals(Location.SOUTH_WEST, tile0.getLocation( 0.0,  1.0));
117         Assertions.assertEquals(Location.WEST,       tile0.getLocation( 6.0,  1.0));
118         Assertions.assertEquals(Location.NORTH_WEST, tile0.getLocation(12.0,  1.0));
119         Assertions.assertEquals(Location.SOUTH,      tile0.getLocation( 0.0, 22.0));
120         Assertions.assertEquals(Location.HAS_INTERPOLATION_NEIGHBORS,    tile0.getLocation( 6.0, 22.0));
121         Assertions.assertEquals(Location.NORTH,      tile0.getLocation(12.0, 22.0));
122         Assertions.assertEquals(Location.SOUTH_EAST, tile0.getLocation( 0.0, 43.0));
123         Assertions.assertEquals(Location.EAST,       tile0.getLocation( 6.0, 43.0));
124         Assertions.assertEquals(Location.NORTH_EAST, tile0.getLocation(12.0, 43.0));
125     }
126 
127     @Test
128     public void testOutOfBoundsIndices() {
129 
130         SimpleTile tile = new SimpleTileFactory().createTile();
131         tile.setGeometry(1.0, 2.0, 0.1, 0.2, 100, 200);
132         tile.setElevation(50, 100, 1000.0);
133         tile.tileUpdateCompleted();
134         checkOutOfBound( -1, 100, tile);
135         checkOutOfBound(100, 100, tile);
136         checkOutOfBound( 50,  -1, tile);
137         checkOutOfBound( 50, 200, tile);
138     }
139 
140     @Test
141     public void testIndexShift() {
142 
143         SimpleTile tile = new SimpleTileFactory().createTile();
144         tile.setGeometry(1.0, 2.0, 0.1, 0.2, 100, 200);
145         tile.setElevation(50, 100, 1000.0);
146         tile.tileUpdateCompleted();
147 
148         // indices correspond to cells centers
149         double latCenterColumn50 = tile.getLatitudeAtIndex(50);
150         double latCenterColumn51 = tile.getLatitudeAtIndex(51);
151         double lonCenterRow23    = tile.getLongitudeAtIndex(23);
152         double lonCenterRow24    = tile.getLongitudeAtIndex(24);
153 
154         // getLatitudeIndex shift indices 1/2 cell, so that
155         // the specified latitude is always between index and index+1
156         // so despite latWestColumn51 is very close to column 51 center,
157         // getLatitudeIndex should return 50
158         double latWestColumn51   = 0.001 * latCenterColumn50 + 0.999 * latCenterColumn51;
159         int retrievedLatIndex = tile.getFloorLatitudeIndex(latWestColumn51);
160         Assertions.assertEquals(50, retrievedLatIndex);
161         Assertions.assertTrue(tile.getLatitudeAtIndex(retrievedLatIndex) < latWestColumn51);
162         Assertions.assertTrue(latWestColumn51 < tile.getLatitudeAtIndex(retrievedLatIndex + 1));
163 
164         // getLongitudeIndex shift indices 1/2 cell, so that
165         // the specified longitude is always between index and index+1
166         // so despite lonSouthRow24 is very close to row 24 center,
167         // getLongitudeIndex should return 23
168         double lonSouthRow24     = 0.001 * lonCenterRow23    + 0.999 * lonCenterRow24;
169         int retrievedLonIndex = tile.getFloorLongitudeIndex(lonSouthRow24);
170         Assertions.assertEquals(23, retrievedLonIndex);
171         Assertions.assertTrue(tile.getLongitudeAtIndex(retrievedLonIndex) < lonSouthRow24);
172         Assertions.assertTrue(lonSouthRow24 < tile.getLongitudeAtIndex(retrievedLonIndex + 1));
173 
174     }
175 
176     private void checkOutOfBound(int i, int j, Tile tile) {
177         try {
178             tile.setElevation(i, j, 1000.0);
179         } catch (RuggedException re) {
180             Assertions.assertEquals(RuggedMessages.OUT_OF_TILE_INDICES, re.getSpecifier());
181             Assertions.assertEquals(i,                              ((Integer) re.getParts()[0]).intValue());
182             Assertions.assertEquals(j,                              ((Integer) re.getParts()[1]).intValue());
183             Assertions.assertEquals(tile.getLatitudeRows() - 1,     ((Integer) re.getParts()[2]).intValue());
184             Assertions.assertEquals(tile.getLongitudeColumns() - 1, ((Integer) re.getParts()[3]).intValue());
185         }
186     }
187 
188     @Test
189     public void testInterpolation() {
190         SimpleTile tile = new SimpleTileFactory().createTile();
191         tile.setGeometry(0.0, 0.0, 1.0, 1.0, 50, 50);
192         tile.setElevation(20, 14,  91.0);
193         tile.setElevation(20, 15, 210.0);
194         tile.setElevation(21, 14, 162.0);
195         tile.setElevation(21, 15,  95.0);
196         tile.tileUpdateCompleted();
197         Assertions.assertEquals(150.5, tile.interpolateElevation(20.0, 14.5), 1.0e-10);
198         Assertions.assertEquals(128.5, tile.interpolateElevation(FastMath.nextDown(21.0), 14.5), 1.0e-10);
199         Assertions.assertTrue(Double.isNaN(tile.interpolateElevation(FastMath.nextUp(21.0), 14.5)));
200         Assertions.assertEquals(146.1, tile.interpolateElevation(20.2, 14.5), 1.0e-10);
201     }
202 
203     @Test
204     public void testInterpolationWithinTolerance() {
205         SimpleTile tile = new SimpleTileFactory().createTile();
206         tile.setGeometry(0.0, 0.0, 1.0, 1.0, 2, 2);
207         tile.setElevation(0, 0,  91.0);
208         tile.setElevation(0, 1, 210.0);
209         tile.setElevation(1, 0, 162.0);
210         tile.setElevation(1, 1,  95.0);
211         tile.tileUpdateCompleted();
212         // the following points are 1/16 cell out of tile
213         Assertions.assertEquals(151.875, tile.interpolateElevation(-0.0625,  0.5),    1.0e-10);
214         Assertions.assertEquals(127.125, tile.interpolateElevation( 1.0625,  0.5),    1.0e-10);
215         Assertions.assertEquals(124.875, tile.interpolateElevation( 0.5,    -0.0625), 1.0e-10);
216         Assertions.assertEquals(154.125, tile.interpolateElevation( 0.5,     1.0625), 1.0e-10);
217     }
218 
219     @Test
220     public void testInterpolationOutOfTolerance() {
221         SimpleTile tile = new SimpleTileFactory().createTile();
222         tile.setGeometry(0.0, 0.0, 1.0, 1.0, 2, 2);
223         tile.setElevation(0, 0,  91.0);
224         tile.setElevation(0, 1, 210.0);
225         tile.setElevation(1, 0, 162.0);
226         tile.setElevation(1, 1,  95.0);
227         tile.tileUpdateCompleted();
228         // the following points are 3/16 cell out of tile
229         checkOutOfBound(-0.1875,  0.5,    tile);
230         checkOutOfBound( 1.1875,  0.5,    tile);
231         checkOutOfBound( 0.5,    -0.1875, tile);
232         checkOutOfBound( 0.5,     1.1875, tile);
233     }
234 
235     @Test
236     public void testCellIntersection() {
237         SimpleTile tile = new SimpleTileFactory().createTile();
238         tile.setGeometry(0.0, 0.0, 0.025, 0.025, 50, 50);
239         tile.setElevation(20, 14,  91.0);
240         tile.setElevation(20, 15, 210.0);
241         tile.setElevation(21, 14, 162.0);
242         tile.setElevation(21, 15,  95.0);
243         tile.tileUpdateCompleted();
244         NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.1 * tile.getLatitudeStep(),
245                                                                   tile.getLongitudeAtIndex(14) + 0.2 * tile.getLongitudeStep(),
246                                                                   300.0, tile.getLongitudeAtIndex(14));
247         NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.7 * tile.getLatitudeStep(),
248                                                                   tile.getLongitudeAtIndex(14) + 0.9 * tile.getLongitudeStep(),
249                                                                   10.0, tile.getLongitudeAtIndex(14));
250         NormalizedGeodeticPoint gpIAB = tile.cellIntersection(gpA, los(gpA, gpB), 20, 14);
251         checkInLine(gpA, gpB, gpIAB);
252         checkOnTile(tile, gpIAB);
253         NormalizedGeodeticPoint gpIBA = tile.cellIntersection(gpB, los(gpB, gpA), 20, 14);
254         checkInLine(gpA, gpB, gpIBA);
255         checkOnTile(tile, gpIBA);
256 
257         Assertions.assertEquals(gpIAB.getLatitude(),  gpIBA.getLatitude(),  1.0e-10);
258         Assertions.assertEquals(gpIAB.getLongitude(), gpIBA.getLongitude(), 1.0e-10);
259         Assertions.assertEquals(gpIAB.getAltitude(),  gpIBA.getAltitude(),  1.0e-10);
260 
261     }
262 
263     @Test
264     public void testCellIntersection2PiWrapping() {
265         SimpleTile tile = new SimpleTileFactory().createTile();
266         tile.setGeometry(0.0, 0.0, 0.025, 0.025, 50, 50);
267         tile.setElevation(20, 14,  91.0);
268         tile.setElevation(20, 15, 210.0);
269         tile.setElevation(21, 14, 162.0);
270         tile.setElevation(21, 15,  95.0);
271         tile.tileUpdateCompleted();
272         NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.1 * tile.getLatitudeStep(),
273                                                                   tile.getLongitudeAtIndex(14) + 0.2 * tile.getLongitudeStep(),
274                                                                   300.0, +4 * FastMath.PI);
275         NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.7 * tile.getLatitudeStep(),
276                                                                   tile.getLongitudeAtIndex(14) + 0.9 * tile.getLongitudeStep(),
277                                                                   10.0, +4 * FastMath.PI);
278         NormalizedGeodeticPoint gpIAB = tile.cellIntersection(gpA, los(gpA, gpB), 20, 14);
279         checkInLine(gpA, gpB, gpIAB);
280         checkOnTile(tile, gpIAB);
281         NormalizedGeodeticPoint gpIBA = tile.cellIntersection(gpB, los(gpB, gpA), 20, 14);
282         checkInLine(gpA, gpB, gpIBA);
283         checkOnTile(tile, gpIBA);
284 
285         Assertions.assertEquals(gpIAB.getLatitude(),  gpIBA.getLatitude(),  1.0e-10);
286         Assertions.assertEquals(gpIAB.getLongitude(), gpIBA.getLongitude(), 1.0e-10);
287         Assertions.assertEquals(gpIAB.getAltitude(),  gpIBA.getAltitude(),  1.0e-10);
288 
289     }
290 
291     @Test
292     public void testCellIntersection2Solutions() {
293         SimpleTile tile = new SimpleTileFactory().createTile();
294         tile.setGeometry(0.0, 0.0, 0.025, 0.025, 50, 50);
295         tile.setElevation(20, 14,  91.0);
296         tile.setElevation(20, 15, 210.0);
297         tile.setElevation(21, 14, 162.0);
298         tile.setElevation(21, 15,  95.0);
299         tile.tileUpdateCompleted();
300         NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.1 * tile.getLatitudeStep(),
301                                                                   tile.getLongitudeAtIndex(14) + 0.2 * tile.getLongitudeStep(),
302                                                                   120.0, tile.getLongitudeAtIndex(14));
303         NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.7 * tile.getLatitudeStep(),
304                                                                   tile.getLongitudeAtIndex(14) + 0.9 * tile.getLongitudeStep(),
305                                                                   130.0, tile.getLongitudeAtIndex(14));
306 
307         // the line from gpA to gpB should traverse the DEM twice within the tile
308         // we use the points in the two different orders to retrieve both solutions
309         NormalizedGeodeticPoint gpIAB = tile.cellIntersection(gpA, los(gpA, gpB), 20, 14);
310         checkInLine(gpA, gpB, gpIAB);
311         checkOnTile(tile, gpIAB);
312         NormalizedGeodeticPoint gpIBA = tile.cellIntersection(gpB, los(gpB, gpA), 20, 14);
313         checkInLine(gpA, gpB, gpIBA);
314         checkOnTile(tile, gpIBA);
315 
316         // the two solutions are different
317         Assertions.assertEquals(120.231, gpIAB.getAltitude(), 1.0e-3);
318         Assertions.assertEquals(130.081, gpIBA.getAltitude(), 1.0e-3);
319 
320     }
321 
322     @Test
323     public void testCellIntersectionNoSolutions() {
324         SimpleTile tile = new SimpleTileFactory().createTile();
325         tile.setGeometry(0.0, 0.0, 0.025, 0.025, 50, 50);
326         tile.setElevation(20, 14,  91.0);
327         tile.setElevation(20, 15, 210.0);
328         tile.setElevation(21, 14, 162.0);
329         tile.setElevation(21, 15,  95.0);
330         tile.tileUpdateCompleted();
331         NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.1 * tile.getLatitudeStep(),
332                                                                   tile.getLongitudeAtIndex(14) + 0.2 * tile.getLongitudeStep(),
333                                                                   180.0, tile.getLongitudeAtIndex(14));
334         NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(20)  + 0.7 * tile.getLatitudeStep(),
335                                                                   tile.getLongitudeAtIndex(14) + 0.9 * tile.getLongitudeStep(),
336                                                                   190.0, tile.getLongitudeAtIndex(14));
337 
338         Assertions.assertNull(tile.cellIntersection(gpA, los(gpA, gpB), 20, 14));
339 
340     }
341 
342     @Test
343     public void testCellIntersectionLinearOnly() {
344         SimpleTile tile = new SimpleTileFactory().createTile();
345         tile.setGeometry(0.0, 0.0, 0.025, 0.025, 50, 50);
346         tile.setElevation(0, 0,  30.0);
347         tile.setElevation(0, 1,  30.0);
348         tile.setElevation(1, 0,  40.0);
349         tile.setElevation(1, 1,  40.0);
350         tile.tileUpdateCompleted();
351         NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.25 * tile.getLatitudeStep(),
352                                                                   tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
353                                                                   50.0, tile.getLongitudeAtIndex(0));
354         NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.75 * tile.getLatitudeStep(),
355                                                                   tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
356                                                                   20.0, tile.getLongitudeAtIndex(0));
357 
358         NormalizedGeodeticPoint gpIAB = tile.cellIntersection(gpA, los(gpA, gpB), 0, 0);
359         checkInLine(gpA, gpB, gpIAB);
360         checkOnTile(tile, gpIAB);
361         NormalizedGeodeticPoint gpIBA = tile.cellIntersection(gpB, los(gpB, gpA), 0, 0);
362         checkInLine(gpA, gpB, gpIBA);
363         checkOnTile(tile, gpIBA);
364 
365         Assertions.assertEquals(gpIAB.getLatitude(),  gpIBA.getLatitude(),  1.0e-10);
366         Assertions.assertEquals(gpIAB.getLongitude(), gpIBA.getLongitude(), 1.0e-10);
367         Assertions.assertEquals(gpIAB.getAltitude(),  gpIBA.getAltitude(),  1.0e-10);
368 
369     }
370 
371     @Test
372     public void testCellIntersectionLinearIntersectionOutside() {
373         SimpleTile tile = new SimpleTileFactory().createTile();
374         tile.setGeometry(0.0, 0.0, 0.025, 0.025, 50, 50);
375         tile.setElevation(0, 0,  30.0);
376         tile.setElevation(0, 1,  30.0);
377         tile.setElevation(1, 0,  40.0);
378         tile.setElevation(1, 1,  40.0);
379         tile.tileUpdateCompleted();
380         NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.25 * tile.getLatitudeStep(),
381                                                                   tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
382                                                                   45.0, tile.getLongitudeAtIndex(0));
383         NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.75 * tile.getLatitudeStep(),
384                                                                   tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
385                                                                   55.0, tile.getLongitudeAtIndex(0));
386 
387        Assertions.assertNull(tile.cellIntersection(gpA, los(gpA, gpB), 0, 0));
388 
389     }
390 
391     @Test
392     public void testCellIntersectionLinearNoIntersection() {
393         SimpleTile tile = new SimpleTileFactory().createTile();
394         tile.setGeometry(0.0, 0.0, 0.025, 0.025, 50, 50);
395         tile.setElevation(0, 0,  30.0);
396         tile.setElevation(0, 1,  30.0);
397         tile.setElevation(1, 0,  40.0);
398         tile.setElevation(1, 1,  40.0);
399         tile.tileUpdateCompleted();
400         NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.25 * tile.getLatitudeStep(),
401                                                                   tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
402                                                                   45.0, tile.getLongitudeAtIndex(0));
403         NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.75 * tile.getLatitudeStep(),
404                                                                   tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
405                                                                   50.0, tile.getLongitudeAtIndex(0));
406 
407         Assertions.assertNull(tile.cellIntersection(gpA, los(gpA, gpB), 0, 0));
408 
409     }
410 
411     @Test
412     public void testCellIntersectionConstant0() {
413         SimpleTile tile = new SimpleTileFactory().createTile();
414         tile.setGeometry(0.0, 0.0, 0.025, 0.025, 50, 50);
415         tile.setElevation(0, 0,  30.0);
416         tile.setElevation(0, 1,  30.0);
417         tile.setElevation(1, 0,  40.0);
418         tile.setElevation(1, 1,  40.0);
419         tile.tileUpdateCompleted();
420         NormalizedGeodeticPoint gpA = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.25 * tile.getLatitudeStep(),
421                                                                   tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
422                                                                   32.5, tile.getLongitudeAtIndex(0));
423         NormalizedGeodeticPoint gpB = new NormalizedGeodeticPoint(tile.getLatitudeAtIndex(0)  + 0.75 * tile.getLatitudeStep(),
424                                                                   tile.getLongitudeAtIndex(0) + 0.50 * tile.getLongitudeStep(),
425                                                                   37.5, tile.getLongitudeAtIndex(0));
426 
427         NormalizedGeodeticPoint gpIAB = tile.cellIntersection(gpA, los(gpA, gpB), 0, 0);
428         checkInLine(gpA, gpB, gpIAB);
429         checkOnTile(tile, gpIAB);
430         NormalizedGeodeticPoint gpIBA = tile.cellIntersection(gpB, los(gpB, gpA), 0, 0);
431         checkInLine(gpA, gpB, gpIBA);
432         checkOnTile(tile, gpIBA);
433 
434         Assertions.assertEquals(gpIAB.getLatitude(),  gpA.getLatitude(),  1.0e-10);
435         Assertions.assertEquals(gpIAB.getLongitude(), gpA.getLongitude(), 1.0e-10);
436         Assertions.assertEquals(gpIAB.getAltitude(),  gpA.getAltitude(),  1.0e-10);
437         Assertions.assertEquals(gpIBA.getLatitude(),  gpB.getLatitude(),  1.0e-10);
438         Assertions.assertEquals(gpIBA.getLongitude(), gpB.getLongitude(), 1.0e-10);
439         Assertions.assertEquals(gpIBA.getAltitude(),  gpB.getAltitude(),  1.0e-10);
440 
441     }
442 
443     private Vector3D los(GeodeticPoint gpA, GeodeticPoint gpB) {
444         // this is a crude conversion into geodetic space
445         // intended *only* for the purposes of these tests
446         // it considers the geodetic space *is* perfectly Cartesian
447         // in the East, North, Zenith frame
448         return new Vector3D(gpB.getLongitude() - gpA.getLongitude(),
449                             gpB.getLatitude()  - gpA.getLatitude(),
450                             gpB.getAltitude()  - gpA.getAltitude());
451     }
452 
453     private void checkOutOfBound(double latitude, double longitude, Tile tile) {
454         try {
455             tile.interpolateElevation(latitude, longitude);
456         } catch (RuggedException re) {
457             Assertions.assertEquals(RuggedMessages.OUT_OF_TILE_ANGLES, re.getSpecifier());
458             Assertions.assertEquals(FastMath.toDegrees(latitude),
459                                 ((Double) re.getParts()[0]).doubleValue(),
460                                 1.0e-10);
461             Assertions.assertEquals(FastMath.toDegrees(longitude),
462                                 ((Double) re.getParts()[1]).doubleValue(),
463                                 1.0e-10);
464             Assertions.assertEquals(FastMath.toDegrees(tile.getMinimumLatitude()),
465                                 ((Double) re.getParts()[2]).doubleValue(),
466                                 1.0e-10);
467             Assertions.assertEquals(FastMath.toDegrees(tile.getMaximumLatitude()),
468                                 ((Double) re.getParts()[3]).doubleValue(),
469                                 1.0e-10);
470             Assertions.assertEquals(FastMath.toDegrees(tile.getMinimumLongitude()),
471                                 ((Double) re.getParts()[4]).doubleValue(),
472                                 1.0e-10);
473             Assertions.assertEquals(FastMath.toDegrees(tile.getMaximumLongitude()),
474                                 ((Double) re.getParts()[5]).doubleValue(),
475                                 1.0e-10);
476         }
477     }
478 
479     private void checkInLine(GeodeticPoint gpA, GeodeticPoint gpB, GeodeticPoint gpI) {
480 
481         double t = (gpI.getAltitude() - gpA.getAltitude()) / (gpB.getAltitude() - gpA.getAltitude());
482 
483         Assertions.assertEquals(gpI.getLatitude(),
484                             gpA.getLatitude() * (1 - t) + gpB.getLatitude() * t,
485                             1.0e-10);
486 
487         Assertions.assertEquals(gpI.getLongitude(),
488                             MathUtils.normalizeAngle(gpA.getLongitude() * (1 - t) + gpB.getLongitude() * t, gpI.getLongitude()),
489                             1.0e-10);
490 
491     }
492 
493     private void checkOnTile(Tile tile, GeodeticPoint gpI) {
494         Assertions.assertEquals(gpI.getAltitude(),
495                             tile.interpolateElevation(gpI.getLatitude(), gpI.getLongitude()),
496                             1.0e-10);
497     }
498 }