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.intersection.duvenhage;
18  
19  import org.hipparchus.random.RandomGenerator;
20  import org.hipparchus.random.Well1024a;
21  import org.hipparchus.util.FastMath;
22  
23  import java.io.File;
24  import java.io.IOException;
25  import java.lang.reflect.Field;
26  
27  import org.junit.jupiter.api.Assertions;
28  import org.junit.jupiter.api.Test;
29  import org.junit.jupiter.api.io.TempDir;
30  
31  public class MinMaxTreeTileTest {
32  
33      @Test
34      public void testSizeTall()
35          throws SecurityException, NoSuchFieldException,
36                 IllegalArgumentException, IllegalAccessException {
37          MinMaxTreeTile tile = createTile(107, 19);
38          Assertions.assertEquals(9, tile.getLevels());
39  
40          Field startField = MinMaxTreeTile.class.getDeclaredField("start");
41          startField.setAccessible(true);
42          int[] start = (int[]) startField.get(tile);
43          Assertions.assertEquals(   0, start[ 0]);
44          Assertions.assertEquals(   7, start[ 1]);
45          Assertions.assertEquals(  21, start[ 2]);
46          Assertions.assertEquals(  49, start[ 3]);
47          Assertions.assertEquals(  91, start[ 4]);
48          Assertions.assertEquals( 172, start[ 5]);
49          Assertions.assertEquals( 307, start[ 6]);
50          Assertions.assertEquals( 577, start[ 7]);
51          Assertions.assertEquals(1117, start[ 8]);
52  
53          Assertions.assertTrue(tile.isColumnMerging(9));
54          Assertions.assertFalse(tile.isColumnMerging(8));
55          Assertions.assertTrue(tile.isColumnMerging(7));
56          Assertions.assertFalse(tile.isColumnMerging(6));
57          Assertions.assertTrue(tile.isColumnMerging(5));
58          Assertions.assertFalse(tile.isColumnMerging(4));
59          Assertions.assertTrue(tile.isColumnMerging(3));
60          Assertions.assertFalse(tile.isColumnMerging(2));
61          Assertions.assertTrue(tile.isColumnMerging(1));
62  
63          Field minTreeField = MinMaxTreeTile.class.getDeclaredField("minTree");
64          minTreeField.setAccessible(true);
65          Assertions.assertEquals(2187, ((double[]) minTreeField.get(tile)).length);
66          Field maxTreeField = MinMaxTreeTile.class.getDeclaredField("maxTree");
67          maxTreeField.setAccessible(true);
68          Assertions.assertEquals(2187, ((double[]) maxTreeField.get(tile)).length);
69  
70      }
71  
72      @Test
73      public void testSizeFat()
74          throws SecurityException, NoSuchFieldException,
75                 IllegalArgumentException, IllegalAccessException {
76          MinMaxTreeTile tile = createTile(4, 7);
77          Assertions.assertEquals(4, tile.getLevels());
78  
79          Field startField = MinMaxTreeTile.class.getDeclaredField("start");
80          startField.setAccessible(true);
81          int[] start = (int[]) startField.get(tile);
82          Assertions.assertEquals( 0, start[ 0]);
83          Assertions.assertEquals( 2, start[ 1]);
84          Assertions.assertEquals( 6, start[ 2]);
85          Assertions.assertEquals(14, start[ 3]);
86  
87          Assertions.assertTrue(tile.isColumnMerging(4));
88          Assertions.assertFalse(tile.isColumnMerging(3));
89          Assertions.assertTrue(tile.isColumnMerging(2));
90          Assertions.assertFalse(tile.isColumnMerging(1));
91  
92          Field minTreeField = MinMaxTreeTile.class.getDeclaredField("minTree");
93          minTreeField.setAccessible(true);
94          Assertions.assertEquals(30, ((double[]) minTreeField.get(tile)).length);
95          Field maxTreeField = MinMaxTreeTile.class.getDeclaredField("maxTree");
96          maxTreeField.setAccessible(true);
97          Assertions.assertEquals(30, ((double[]) maxTreeField.get(tile)).length);
98  
99      }
100 
101     @Test
102     public void testSinglePixel() {
103         Assertions.assertEquals(0, createTile(1, 1).getLevels());
104     }
105 
106     @Test
107     public void testMinMax() {
108         for (int nbRows = 1; nbRows < 25; nbRows++) {
109             for (int nbColumns = 1; nbColumns < 25; nbColumns++) {
110 
111                 MinMaxTreeTile tile = createTile(nbRows, nbColumns);
112 
113                 for (int level = 0; level < tile.getLevels(); level++) {
114                     for (int row = 0; row < nbRows; row++) {
115                         for (int column = 0; column < nbColumns; column++) {
116 
117                             // reference min and max
118                             int[] neighbors = neighbors(row, column, nbRows, nbColumns, tile.getLevels() - level);
119                             double min = Double.POSITIVE_INFINITY;
120                             double max = Double.NEGATIVE_INFINITY;
121                             for (int i = neighbors[0]; i < FastMath.min(neighbors[1] + 1, nbRows); ++i) {
122                                 for (int j = neighbors[2]; j < FastMath.min(neighbors[3] + 1, nbColumns); ++j) {
123                                     double cellValue = tile.getElevationAtIndices(i, j);
124                                     min = FastMath.min(min, cellValue);
125                                     max = FastMath.max(max, cellValue);
126                                 }
127                             }
128 
129                             Assertions.assertEquals(min, tile.getMinElevation(row, column, level), 1.0e-10 * min);
130                             Assertions.assertEquals(max, tile.getMaxElevation(row, column, level), 1.0e-10 * max);
131                         }
132                     }
133                 }
134             }
135         }
136     }
137 
138     @Test
139     public void testLocateMinMax() {
140         RandomGenerator random = new Well1024a(0xca9883209c6e740cl);
141         for (int nbRows = 1; nbRows < 25; nbRows++) {
142             for (int nbColumns = 1; nbColumns < 25; nbColumns++) {
143 
144                 MinMaxTreeTile tile = new MinMaxTreeTileFactory().createTile();
145                 tile.setGeometry(1.0, 2.0, 0.1, 0.2, nbRows, nbColumns);
146                 for (int i = 0; i < nbRows; ++i) {
147                     for (int j = 0; j < nbColumns; ++j) {
148                         final double e  = 1000.0 * random.nextDouble();
149                         tile.setElevation(i, j, e);
150                     }
151                 }
152                 tile.tileUpdateCompleted();
153 
154                 for (int i = 0; i < tile.getLatitudeRows(); ++i) {
155                     for (int j = 0; j < tile.getLongitudeColumns(); ++j) {
156                         for (int l = 0; l < tile.getLevels(); ++l) {
157                             int[] min = tile.locateMin(i, j, l);
158                             Assertions.assertEquals(tile.getMinElevation(i, j, l),
159                                                 tile.getElevationAtIndices(min[0], min[1]),
160                                                 1.0e-10);
161                             int[] max = tile.locateMax(i, j, l);
162                             Assertions.assertEquals(tile.getMaxElevation(i, j, l),
163                                                 tile.getElevationAtIndices(max[0], max[1]),
164                                                 1.0e-10);
165                         }
166                     }
167                 }
168             }
169         }
170     }
171 
172     @Test
173     public void testIssue189() {
174         MinMaxTreeTile tile = new MinMaxTreeTileFactory().createTile();
175         tile.setGeometry(1.0, 2.0, 0.1, 0.2, 2, 2);
176         tile.setElevation(0, 0, 1.0);
177         tile.setElevation(0, 1, 2.0);
178         tile.setElevation(1, 0, 3.0);
179         tile.setElevation(1, 1, 4.0);
180         tile.tileUpdateCompleted();
181         Assertions.assertEquals(1.0, tile.getMinElevation(0, 0, 0), 1.0e-10);
182         Assertions.assertEquals(3.0, tile.getMinElevation(1, 0, 0), 1.0e-10);
183         Assertions.assertEquals(4.0, tile.getMaxElevation(0, 0, 0), 1.0e-10);
184         Assertions.assertEquals(4.0, tile.getMaxElevation(1, 0, 0), 1.0e-10);
185     }
186 
187     @Test
188     public void testMergeLarge() {
189         MinMaxTreeTile tile = createTile(1201, 1201);
190         Assertions.assertEquals(21, tile.getLevels());
191         Assertions.assertEquals( 7, tile.getMergeLevel(703, 97, 765, 59));
192     }
193 
194     @Test
195     public void testMergeLevel() {
196         for (int nbRows = 1; nbRows < 20; nbRows++) {
197             for (int nbColumns = 1; nbColumns < 20; nbColumns++) {
198 
199                 MinMaxTreeTile tile = createTile(nbRows, nbColumns);
200 
201                 for (int i1 = 0; i1 < nbRows; i1++) {
202                     for (int j1 = 0; j1 < nbColumns; j1++) {
203                         for (int i2 = 0; i2 < nbRows; i2++) {
204                             for (int j2 = 0; j2 < nbColumns; j2++) {
205 
206                                 int level = tile.getMergeLevel(i1, j1, i2, j2);
207                                 if (level > 0) {
208                                     int[] neighbors1 = neighbors(i1, j1, nbRows, nbColumns, tile.getLevels() - level);
209                                     int[] neighbors2 = neighbors(i2, j2, nbRows, nbColumns, tile.getLevels() - level);
210                                     for (int k = 0; k < neighbors1.length; ++k) {
211                                         Assertions.assertTrue(neighbors1[0] == neighbors2[0] &&
212                                                           neighbors1[1] == neighbors2[1] &&
213                                                           neighbors1[2] == neighbors2[2] &&
214                                                           neighbors1[3] == neighbors2[3]);
215                                     }
216                                 }
217                                 if (level + 1 < tile.getLevels()) {
218                                     int[] neighbors1 = neighbors(i1, j1, nbRows, nbColumns, tile.getLevels() - (level + 1));
219                                     int[] neighbors2 = neighbors(i2, j2, nbRows, nbColumns, tile.getLevels() - (level + 1));
220                                     for (int k = 0; k < neighbors1.length; ++k) {
221                                         if ((neighbors1[0] == neighbors2[0] &&
222                                                 neighbors1[1] == neighbors2[1] &&
223                                                 neighbors1[2] == neighbors2[2] &&
224                                                 neighbors1[3] == neighbors2[3])) {
225                                             tile.getMergeLevel(i1, j1, i2, j2);
226                                         }
227                                         Assertions.assertFalse(neighbors1[0] == neighbors2[0] &&
228                                                            neighbors1[1] == neighbors2[1] &&
229                                                            neighbors1[2] == neighbors2[2] &&
230                                                            neighbors1[3] == neighbors2[3]);
231                                     }
232                                 }
233                             }
234                         }
235                     }
236                 }
237             }
238         }
239     }
240 
241     @Test
242     public void testSubTilesLimits() {
243         for (int nbRows = 1; nbRows < 25; nbRows++) {
244             for (int nbColumns = 1; nbColumns < 25; nbColumns++) {
245 
246                 MinMaxTreeTile tile = createTile(nbRows, nbColumns);
247 
248                 for (int level = 0; level < tile.getLevels(); ++level) {
249                     int[] neighbors = neighbors(0, 0, nbRows, nbColumns, tile.getLevels() - level);
250                     int subTileRows = neighbors[1] - neighbors[0];
251                     int subTileCols = neighbors[3] - neighbors[2];
252                     for (int i1 = 0; i1 < nbRows; ++i1) {
253                         for (int i2 = 0; i2 < nbRows; ++i2) {
254                             int[] crossings = tile.getCrossedBoundaryRows(i1, i2, level);
255                             int[] ref       = multiples(i1, i2, subTileRows);
256                             Assertions.assertArrayEquals(ref, crossings);
257                         }
258                     }
259                     for (int j1 = 0; j1 < nbColumns; ++j1) {
260                         for (int j2 = 0; j2 < nbColumns; ++j2) {
261                             int[] crossings = tile.getCrossedBoundaryColumns(j1, j2, level);
262                             int[] ref       = multiples(j1, j2, subTileCols);
263                             Assertions.assertArrayEquals(ref, crossings);
264                         }
265                     }
266                 }
267             }
268         }
269     }
270 
271     @Test
272     public void testForCoverage() throws IOException {
273         
274         org.orekit.rugged.errors.DumpManager.activate(File.createTempFile("junit", null, tempFolder));
275 
276         MinMaxTreeTile tile = createTile(1201, 1201);
277         tile.getMinElevation(100, 100, 0);
278         
279         org.orekit.rugged.errors.DumpManager.deactivate();
280     }
281     
282     private int[] neighbors(int row, int column, int nbRows, int nbColumns, int stages) {
283 
284         // poor man identification of neighbors cells merged together with specified cell
285         // this identification is intentionally independent of the MinMaxTreeTile class,
286         // for testing purposes
287         int rMin  = row;
288         int rN    = 1;
289         int rMask = -1;
290         int cMin  = column;
291         int cMask = -1;
292         int cN    = 1;
293 
294         boolean mergeColumns = true;
295         for (int i = 0; i < stages; ++i) {
296             if (mergeColumns) {
297                 cMask = cMask << 1;
298                 cMin  = cMin & cMask;
299                 cN    = cN * 2;
300             } else {
301                 rMask = rMask << 1;
302                 rMin  = rMin & rMask;
303                 rN    = rN * 2;
304             }
305             mergeColumns = !mergeColumns;
306         }
307 
308         return new int[] {
309             rMin, FastMath.min(rMin + rN, nbRows),
310             cMin, FastMath.min(cMin + cN, nbColumns)
311         };
312 
313     }
314 
315     private int[] multiples(int k1, int k2, int n) {
316 
317         // poor man identification of rows/columns crossings
318         // this identification is intentionally independent of the MinMaxTreeTile class,
319         // for testing purposes
320 
321         // intentionally dumb way of counting multiples of n
322         int kS = FastMath.min(k1, k2);
323         int kE = FastMath.max(k1, k2) + 1;
324         int count = 0;
325         for (int k = kS; k < kE; ++k) {
326             if (k % n == 0) {
327                 ++count;
328             }
329         }
330 
331         int[] multiples = new int[count];
332         int index = 0;
333         for (int k = kS; k < kE; ++k) {
334             if (k % n == 0) {
335                 multiples[index++] = k;
336             }
337         }
338 
339         if (k1 > k2) {
340             // revert the array
341             for (int i = 0; i < count / 2; ++i) {
342                 int tmp = multiples[i];
343                 multiples[i] = multiples[count - 1 - i];
344                 multiples[count - 1 - i] = tmp;
345             }
346         }
347 
348         return multiples;
349 
350     }
351 
352     private MinMaxTreeTile createTile(int nbRows, int nbColumns) {
353         MinMaxTreeTile tile = new MinMaxTreeTileFactory().createTile();
354         tile.setGeometry(1.0, 2.0, 0.1, 0.2, nbRows, nbColumns);
355         for (int i = 0; i < nbRows; ++i) {
356             for (int j = 0; j < nbColumns; ++j) {
357                 tile.setElevation(i, j, i + 0.01 * j);
358             }
359         }
360         tile.tileUpdateCompleted();
361         return tile;
362     }
363     
364     @TempDir
365     public File tempFolder;
366 
367 }