1   /* Copyright 2002-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.models.earth.tessellation;
18  
19  import org.hipparchus.geometry.euclidean.threed.Vector3D;
20  import org.hipparchus.geometry.partitioning.Region;
21  import org.hipparchus.geometry.partitioning.Region.Location;
22  import org.hipparchus.geometry.partitioning.RegionFactory;
23  import org.hipparchus.geometry.spherical.twod.Circle;
24  import org.hipparchus.geometry.spherical.twod.S2Point;
25  import org.hipparchus.geometry.spherical.twod.Sphere2D;
26  import org.hipparchus.geometry.spherical.twod.SphericalPolygonsSet;
27  import org.hipparchus.geometry.spherical.twod.SubCircle;
28  import org.hipparchus.util.FastMath;
29  import org.junit.jupiter.api.Assertions;
30  import org.junit.jupiter.api.BeforeEach;
31  import org.junit.jupiter.api.Test;
32  import org.orekit.Utils;
33  import org.orekit.bodies.GeodeticPoint;
34  import org.orekit.bodies.OneAxisEllipsoid;
35  import org.orekit.errors.OrekitException;
36  import org.orekit.errors.OrekitMessages;
37  import org.orekit.frames.Frame;
38  import org.orekit.frames.FramesFactory;
39  import org.orekit.orbits.CircularOrbit;
40  import org.orekit.orbits.Orbit;
41  import org.orekit.orbits.PositionAngleType;
42  import org.orekit.time.AbsoluteDate;
43  import org.orekit.time.TimeScalesFactory;
44  import org.orekit.utils.Constants;
45  import org.orekit.utils.IERSConventions;
46  
47  import java.util.List;
48  
49  public class EllipsoidTessellatorTest {
50  
51      @Test
52      public void testTilesAlongDescendingTrackWithoutTruncation() {
53          final EllipsoidTessellator tessellator =
54                  new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, false), 16);
55          final List<List<Tile>> tiles = tessellator.tessellate(buildFrance(),
56                                                                50000.0, 150000.0, 5000.0, 5000.0,
57                                                                false, false);
58          Assertions.assertEquals(2,   tiles.size());
59          Assertions.assertEquals(108, FastMath.max(tiles.get(0).size(), tiles.get(1).size()));
60          Assertions.assertEquals(4,   FastMath.min(tiles.get(0).size(), tiles.get(1).size()));
61  
62      }
63  
64      @Test
65      public void testTilesAlongDescendingTrackWithTruncation() {
66          final EllipsoidTessellator tessellator =
67                  new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, false), 16);
68          final List<List<Tile>> tiles = tessellator.tessellate(buildFrance(),
69                                                                50000.0, 150000.0, 5000.0, 5000.0,
70                                                                true, true);
71          Assertions.assertEquals(2,   tiles.size());
72          Assertions.assertEquals(108, FastMath.max(tiles.get(0).size(), tiles.get(1).size()));
73          Assertions.assertEquals(4,   FastMath.min(tiles.get(0).size(), tiles.get(1).size()));
74  
75      }
76  
77      @Test
78      public void testSampleAlongDescendingTrack() {
79          final EllipsoidTessellator tessellator =
80                  new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, false), 4);
81          final List<List<GeodeticPoint>> samples = tessellator.sample(buildFrance(), 25000.0, 50000.0);
82          Assertions.assertEquals(2,   samples.size());
83          Assertions.assertEquals(455, FastMath.max(samples.get(0).size(), samples.get(1).size()));
84          Assertions.assertEquals(9,   FastMath.min(samples.get(0).size(), samples.get(1).size()));
85      }
86  
87      @Test
88      public void testTilesAlongAscendingTrack() {
89          final EllipsoidTessellator tessellator =
90                  new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, true), 4);
91          final List<List<Tile>> tiles = tessellator.tessellate(buildFrance(),
92                                                                50000.0, 140000.0, 5000.0, 5000.0,
93                                                                false, false);
94          Assertions.assertEquals(2,   tiles.size());
95          Assertions.assertEquals(121, FastMath.max(tiles.get(0).size(), tiles.get(1).size()));
96          Assertions.assertEquals(6,   FastMath.min(tiles.get(0).size(), tiles.get(1).size()));
97      }
98  
99      @Test
100     public void testSampleAlongAscendingTrack() {
101         final EllipsoidTessellator tessellator =
102                 new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, true), 4);
103         final List<List<GeodeticPoint>> samples = tessellator.sample(buildFrance(),
104                                                               25000.0, 50000.0);
105         Assertions.assertEquals(2,   samples.size());
106         Assertions.assertEquals(454, FastMath.max(samples.get(0).size(), samples.get(1).size()));
107         Assertions.assertEquals(10,  FastMath.min(samples.get(0).size(), samples.get(1).size()));
108     }
109 
110     @Test
111     public void testTilesConstantAzimuth() {
112         final EllipsoidTessellator tessellator =
113                 new EllipsoidTessellator(ellipsoid, new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(120)), 4);
114         final List<List<Tile>> tiles = tessellator.tessellate(buildFrance(),
115                                                               50000.0, 150000.0, -5000.0, -5000.0,
116                                                               false, false);
117         Assertions.assertEquals(2,  tiles.size());
118         Assertions.assertEquals(86, FastMath.max(tiles.get(0).size(), tiles.get(1).size()));
119         Assertions.assertEquals(4,  FastMath.min(tiles.get(0).size(), tiles.get(1).size()));
120         checkTilesDontOverlap(tiles);
121     }
122 
123     @Test
124     public void testSampleConstantAzimuth() {
125         final EllipsoidTessellator tessellator =
126                 new EllipsoidTessellator(ellipsoid, new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(120)), 4);
127         final List<List<GeodeticPoint>> samples = tessellator.sample(buildFrance(), 25000.0, 50000.0);
128         Assertions.assertEquals(2,   samples.size());
129         Assertions.assertEquals(452, FastMath.max(samples.get(0).size(), samples.get(1).size()));
130         Assertions.assertEquals(9,   FastMath.min(samples.get(0).size(), samples.get(1).size()));
131     }
132 
133     @Test
134     public void testTilesIslandJoining() {
135         final EllipsoidTessellator tessellator =
136                 new EllipsoidTessellator(ellipsoid, new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(120.0)), 4);
137         final List<List<Tile>> tiles = tessellator.tessellate(buildFrance(),
138                                                               150000.0, 250000.0, -5000.0, -5000.0,
139                                                               false, false);
140         Assertions.assertEquals(1,  tiles.size());
141         Assertions.assertEquals(30, tiles.get(0).size());
142         checkTilesDontOverlap(tiles);
143     }
144 
145     @Test
146     public void testTilesSmallZoneWithoutTruncation() {
147 
148         TileAiming aiming = new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(193.7));
149         EllipsoidTessellator tessellator =
150                 new EllipsoidTessellator(ellipsoid, aiming, 16);
151 
152         SphericalPolygonsSet small = buildSimpleZone(1.0e-10, new double[][] {
153             { 43.6543, 1.4268 }, { 43.6120, 1.4179 }, { 43.6016, 1.3994 }, { 43.5682, 1.4159 },
154             { 43.5707, 1.4358 }, { 43.5573, 1.4941 }, { 43.6041, 1.4866 }
155         });
156 
157         final List<List<Tile>> tiles = tessellator.tessellate(small, 50000.0, 150000.0, 0, 0,
158                                                               false, false);
159         Assertions.assertEquals(1, tiles.size());
160         Assertions.assertEquals(1, tiles.get(0).size());
161         Tile t = tiles.get(0).get(0);
162 
163         // without truncation, the tile must match width and length specification
164         // (the remaining error is due to Cartesian distance and non-developable ellipsoid)
165         Assertions.assertEquals(150000.0,
166                             Vector3D.distance(ellipsoid.transform(t.getVertices()[0]),
167                                               ellipsoid.transform(t.getVertices()[1])),
168                             140.0);
169         Assertions.assertEquals( 50000.0,
170                             Vector3D.distance(ellipsoid.transform(t.getVertices()[1]),
171                                              ellipsoid.transform(t.getVertices()[2])),
172                             0.4);
173         Assertions.assertEquals(150000.0,
174                             Vector3D.distance(ellipsoid.transform(t.getVertices()[2]),
175                                              ellipsoid.transform(t.getVertices()[3])),
176                             140.0);
177         Assertions.assertEquals( 50000.0,
178                             Vector3D.distance(ellipsoid.transform(t.getVertices()[3]),
179                                              ellipsoid.transform(t.getVertices()[0])),
180                             0.4);
181 
182     }
183 
184     @Test
185     public void testTilesSmallZoneWithTruncation() {
186 
187         TileAiming aiming = new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(193.7));
188         EllipsoidTessellator tessellator =
189                 new EllipsoidTessellator(ellipsoid, aiming, 16);
190 
191         SphericalPolygonsSet small = buildSimpleZone(1.0e-10, new double[][] {
192             { 43.6543, 1.4268 }, { 43.6120, 1.4179 }, { 43.6016, 1.3994 }, { 43.5682, 1.4159 },
193             { 43.5707, 1.4358 }, { 43.5573, 1.4941 }, { 43.6041, 1.4866 }
194         });
195 
196         final List<List<Tile>> tiles = tessellator.tessellate(small, 50000.0, 150000.0, 0, 0,
197                                                               true, true);
198 
199         Assertions.assertEquals(1, tiles.size());
200         Assertions.assertEquals(1, tiles.get(0).size());
201         Tile t = tiles.get(0).get(0);
202 
203         // with truncation, the tile is a fraction of the width and length specification
204         Assertions.assertEquals(2.0 / 16.0 * 150000.0,
205                                 Vector3D.distance(ellipsoid.transform(t.getVertices()[0]),
206                                                   ellipsoid.transform(t.getVertices()[1])),
207                                 10.0);
208         Assertions.assertEquals(4.0 / 16.0 * 50000.0,
209                                 Vector3D.distance(ellipsoid.transform(t.getVertices()[1]),
210                                                   ellipsoid.transform(t.getVertices()[2])),
211                                 0.01);
212         Assertions.assertEquals(2.0 / 16.0 * 150000.0,
213                                 Vector3D.distance(ellipsoid.transform(t.getVertices()[2]),
214                                                   ellipsoid.transform(t.getVertices()[3])),
215                                 10.0);
216         Assertions.assertEquals(4.0 / 16.0 * 50000.0,
217                                 Vector3D.distance(ellipsoid.transform(t.getVertices()[3]),
218                                                   ellipsoid.transform(t.getVertices()[0])),
219                                 0.01);
220     }
221 
222     @Test
223     public void testStairedTruncatedTiles() {
224 
225         TileAiming aiming = new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(170.0));
226         EllipsoidTessellator tessellator =
227                 new EllipsoidTessellator(ellipsoid, aiming, 16);
228 
229         SphericalPolygonsSet small = buildSimpleZone(1.0e-10, new double[][] {
230             { 45.335,  0.457 },
231             { 45.342,  0.469 },
232             { 45.371,  0.424 }
233         });
234 
235         final double maxWidth  = 800.0;
236         final double maxLength = 10000.0;
237         final List<List<Tile>> tiles = tessellator.tessellate(small, maxWidth, maxLength, 0, 0,
238                                                               false, true);
239         Assertions.assertEquals(1, tiles.size());
240         Assertions.assertEquals(4, tiles.get(0).size());
241         for (final Tile tile : tiles.get(0)) {
242             Vector3D v0 = ellipsoid.transform(tile.getVertices()[0]);
243             Vector3D v1 = ellipsoid.transform(tile.getVertices()[1]);
244             Vector3D v2 = ellipsoid.transform(tile.getVertices()[2]);
245             Vector3D v3 = ellipsoid.transform(tile.getVertices()[3]);
246             Assertions.assertTrue(Vector3D.distance(v0, v1) < (6.0002 / 16.0) * maxLength);
247             Assertions.assertTrue(Vector3D.distance(v2, v3) < (6.0002 / 16.0) * maxLength);
248             Assertions.assertEquals(maxWidth, Vector3D.distance(v1, v2), 1.0e-3);
249             Assertions.assertEquals(maxWidth, Vector3D.distance(v3, v0), 1.0e-3);
250         }
251 
252     }
253 
254     @Test
255     public void testTooThinRemainingRegion() {
256         TileAiming aiming = new ConstantAzimuthAiming(ellipsoid, -0.2185);
257         EllipsoidTessellator tessellator =
258                 new EllipsoidTessellator(ellipsoid, aiming, 16);
259 
260         SphericalPolygonsSet small = buildSimpleZone(1.0e-10, new double[][]{
261             { 32.7342, -16.9407 }, { 32.7415, -16.9422 }, { 32.7481, -16.9463 }, { 32.7531, -16.9528 },
262             { 32.7561, -16.9608 }, { 32.7567, -16.9696 }, { 32.7549, -16.9781 }, { 32.7508, -16.9855 },
263             { 32.7450, -16.9909 }, { 32.7379, -16.9937 }, { 32.7305, -16.9937 }, { 32.7235, -16.9909 },
264             { 32.7177, -16.9855 }, { 32.7136, -16.9781 }, { 32.7118, -16.9696 }, { 32.7124, -16.9608 },
265             { 32.7154, -16.9528 }, { 32.7204, -16.9463 }, { 32.7269, -16.9422 }
266         });
267 
268         final double maxWidth  = 40000.0;
269         final double maxLength = 40000.0;
270         final List<List<Tile>> tiles = tessellator.tessellate(small, maxWidth, maxLength, 0, 0,
271                                                               false, true);
272         Assertions.assertEquals(1, tiles.size());
273         Assertions.assertEquals(1, tiles.get(0).size());
274         for (final Tile tile : tiles.get(0)) {
275             Vector3D v0 = ellipsoid.transform(tile.getVertices()[0]);
276             Vector3D v1 = ellipsoid.transform(tile.getVertices()[1]);
277             Vector3D v2 = ellipsoid.transform(tile.getVertices()[2]);
278             Vector3D v3 = ellipsoid.transform(tile.getVertices()[3]);
279             Assertions.assertTrue(Vector3D.distance(v0, v1) < 0.3 * maxLength);
280             Assertions.assertEquals(maxWidth, Vector3D.distance(v1, v2), 0.1);
281             Assertions.assertTrue(Vector3D.distance(v2, v3) < 0.3 * maxLength);
282             Assertions.assertEquals(maxWidth, Vector3D.distance(v3, v0), 0.1);
283         }
284 
285     }
286 
287     @Test
288     public void testNormalZoneTolerance() {
289         doTestVariableTolerance(1.0e-10);
290     }
291 
292     @Test
293     public void testLargeZoneTolerance() {
294         // this used to trigger an exception in EllipsoidTessellator.recurseMeetInside (Orekit)
295         doTestVariableTolerance(1.0e-6);
296     }
297 
298     @Test
299     public void testHugeZoneTolerance() {
300         // this used to trigger an exception in Characterization.characterize (Apache Commons Math)
301         // this was due to issue MATH-1266, solved in Apache Commons Math 3.6
302         doTestVariableTolerance(1.0e-4);
303     }
304 
305     private void doTestVariableTolerance(final double tolerance) {
306         final ConstantAzimuthAiming aiming = new ConstantAzimuthAiming(ellipsoid,
307                                                                        FastMath.toRadians(-168.178485));
308         EllipsoidTessellator tessellator =
309                 new EllipsoidTessellator(ellipsoid, aiming, 16);
310 
311         SphericalPolygonsSet small = buildSimpleZone(tolerance, new double[][]{
312             { -0.01048739, 0.01598931 }, { -0.00789627, 0.01555693 }, { -0.00558595, 0.01430664 },
313             { -0.00380677, 0.01237394 }, { -0.00275154, 0.00996826 }, { -0.00253461, 0.00735029 },
314             { -0.00317949, 0.00480374 }, { -0.00461629, 0.00260455 }, { -0.00668931, 0.00099105 },
315             { -0.00917392, 0.00013808 }, { -0.01180086, 0.00013808 }, { -0.01428546, 0.00099105 },
316             { -0.01635849, 0.00260455 }, { -0.01779529, 0.00480374 }, { -0.01844016, 0.00735029 },
317             { -0.01822323, 0.00996826 }, { -0.01716800, 0.01237394 }, { -0.01538882, 0.01430664 },
318             { -0.01307850, 0.01555693 }
319         });
320 
321         final double maxWidth  = 40000.0;
322         final double maxLength = 40000.0;
323         final List<List<Tile>> tiles =
324                 tessellator.tessellate(small, maxWidth, maxLength, 0, 0, false, true);
325         Assertions.assertEquals(1, tiles.size());
326         Assertions.assertEquals(1, tiles.get(0).size());
327 
328     }
329 
330     private void checkTilesDontOverlap(final List<List<Tile>> tiles) {
331         for (final List<Tile> list : tiles) {
332             for (final Tile tile : list) {
333                 final SphericalPolygonsSet quadrilateral =
334                         new SphericalPolygonsSet(1.0e-10,
335                                                  toS2Point(tile.getVertices()[0]),
336                                                  toS2Point(tile.getVertices()[1]),
337                                                  toS2Point(tile.getVertices()[2]),
338                                                  toS2Point(tile.getVertices()[3]));
339                 for (final List<Tile> otherList : tiles) {
340                     for (final Tile otherTile : otherList) {
341                         if (otherTile != tile) {
342                             for (final GeodeticPoint vertex : otherTile.getVertices()) {
343                                 Assertions.assertEquals(Location.OUTSIDE, quadrilateral.checkPoint(toS2Point(vertex)),"tiles overlap at: " + vertex);
344                             }
345                         }
346                     }
347                 }
348             }
349         }
350     }
351 
352     @Test
353     public void testSampleAroundPoleConstantAzimuth() {
354         SphericalPolygonsSet aoi = new SphericalPolygonsSet(1.e-9,
355                                                             new S2Point(FastMath.toRadians(-120.0), FastMath.toRadians(5.0)),
356                                                             new S2Point(FastMath.toRadians(   0.0), FastMath.toRadians(5.0)),
357                                                             new S2Point(FastMath.toRadians( 120.0), FastMath.toRadians(5.0)));
358         doTestSampleAroundPole(aoi, new ConstantAzimuthAiming(ellipsoid, 0.0), -1);
359     }
360 
361     @Test
362     public void testSampleAroundPoleDivertedSingularity() {
363         SphericalPolygonsSet aoi = new SphericalPolygonsSet(1.e-9,
364                                                             new S2Point(FastMath.toRadians(-120.0), FastMath.toRadians(5.0)),
365                                                             new S2Point(FastMath.toRadians(   0.0), FastMath.toRadians(5.0)),
366                                                             new S2Point(FastMath.toRadians( 120.0), FastMath.toRadians(5.0)));
367         doTestSampleAroundPole(aoi, new DivertedSingularityAiming(aoi), 993);
368     }
369 
370     @Test
371     public void testIssue1388A() {
372         doTestIssue1388(true);
373     }
374 
375     @Test
376     public void testIssue1388B() {
377         doTestIssue1388(false);
378     }
379 
380     private void doTestIssue1388(final boolean order) {
381         final double[][] coordinates1 = new double[][] {
382             { 18.52684751402596,  -76.97880893719434 },
383             { 18.451108862175584, -76.99484778988442 },
384             { 18.375369256045143, -77.01087679277504 },
385             { 18.299628701801268, -77.02689599675749 },
386             { 18.223887203723567, -77.04290545732495 },
387             { 18.148144771769385, -77.05890521550272 },
388             { 18.072401410187016, -77.0748953260324  },
389             { 17.99665712514784,  -77.09087584010511 },
390             { 17.92091192468999,  -77.10684680260262 },
391             { 17.84516581113023,  -77.12280827309398 },
392             { 17.769418792522664, -77.13876029654094 },
393             { 17.747659863099422, -77.14334087084347 },
394             { 17.67571798336192,  -77.15846791369165 },
395             { 17.624293265977183, -77.16928381433733 },
396             { 17.5485398681768,   -77.18520934447962 },
397             { 17.526779103104783, -77.1897823275402  },
398             { 17.49650619905315,  -77.0342031192472  },
399             { 17.588661518962343, -77.01473903648854 },
400             { 17.728574326965138, -76.98517769352242 },
401             { 17.80416324015021,  -76.96919708557023 },
402             { 17.87969526622326,  -76.95321858415689 },
403             { 17.955280973332677, -76.93721874766547 },
404             { 18.030855567607098, -76.92121123297645 },
405             { 18.106414929680927, -76.90519686376611 },
406             { 18.182031502555215, -76.88916022728444 },
407             { 18.257597934434987, -76.87312403715188 },
408             { 18.3331742522667,   -76.85707550881591 },
409             { 18.408750874895002, -76.84101662269072 },
410             { 18.57249082100609,  -76.80620195239239 },
411             { 18.602585205425896, -76.96276018365842 }
412         };
413 
414         final double[][] coordinates2 = new double[][] {
415             { 18.338614038907608, -78.37885677406668 },
416             { 18.195574802144037, -78.24425107003432 },
417             { 18.20775293886321,  -78.0711865934217  },
418             { 18.07679345301507,  -77.95901517339438 },
419             { 18.006705181057598, -77.85325354879791 },
420             { 17.857293838883137, -77.73787723105598 },
421             { 17.854243316622103, -77.57442744758828 },
422             { 17.875595873376014, -77.38213358468467 },
423             { 17.72607423578937,  -77.23470828979222 },
424             { 17.71386286451302,  -77.12253686976543 },
425             { 17.790170276013725, -77.14817605148616 },
426             { 17.869495404611797, -77.14497115377101 },
427             { 17.854243309397717, -76.9302429967729  },
428             { 17.954882874700132, -76.84371075846688 },
429             { 17.94268718313505,  -76.6898756681441  },
430             { 17.869495397388064, -76.54886016868198 },
431             { 17.863394719203555, -76.35015651034861 },
432             { 17.93049065091843,  -76.23478019260665 },
433             { 18.155989976776553, -76.32451732862788 },
434             { 18.22601854027039,  -76.63218750927341 },
435             { 18.33861403170316,  -76.85653034932697 },
436             { 18.405527980074993, -76.97831646249921 },
437             { 18.4541763474828,   -77.28598664314421 },
438             { 18.496732365966466, -77.705828243816   },
439             { 18.451136227912485, -78.00708862903122 },
440             { 18.405527980074993, -78.25707065080552 }
441         };
442 
443         final double[][] expectedIn = new double[][] {
444                 { 18.408, -77.003 },
445                 { 18.338, -76.857 },
446                 { 17.869, -77.117 },
447                 { 17.857, -76.959 },
448                 { 17.761, -77.139 },
449                 { 17.715, -77.125 }
450         };
451 
452         final double[][] expectedOut = new double[][] {
453                 { 17.794, -77.145 },
454                 { 17.736, -76.981 },
455                 { 17.715, -77.138 },
456                 { 18.153, -77.059 },
457                 { 18.232, -76.877 },
458                 { 18.373, -76.917 },
459                 { 17.871, -77.261 } // this is the point that was wrongly inside the intersection
460         };
461 
462         SphericalPolygonsSet shape1 = buildSimpleZone(1e-10, coordinates1);
463         SphericalPolygonsSet shape2 = buildSimpleZone(1e-10, coordinates2);
464         Region<Sphere2D, S2Point, Circle, SubCircle> intersection =
465             order ?
466             new RegionFactory<Sphere2D, S2Point, Circle, SubCircle>().intersection(shape1, shape2) :
467             new RegionFactory<Sphere2D, S2Point, Circle, SubCircle>().intersection(shape2, shape1);
468 
469         for (final double[] doubles : expectedIn) {
470             Assertions.assertEquals(Location.INSIDE,
471                                     intersection.checkPoint(new S2Point(FastMath.toRadians(doubles[1]),
472                                                                         FastMath.toRadians(90.0 - doubles[0]))));
473         }
474 
475         for (final double[] doubles : expectedOut) {
476             Assertions.assertEquals(Location.OUTSIDE,
477                                     intersection.checkPoint(new S2Point(FastMath.toRadians(doubles[1]),
478                                                                         FastMath.toRadians(90.0 - doubles[0]))));
479         }
480 
481     }
482 
483     private void doTestSampleAroundPole(final SphericalPolygonsSet aoi, final TileAiming aiming,
484                                         final int expectedNodes) {
485         EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, aiming, 1);
486         try {
487             List<List<GeodeticPoint>> sampledZone = tessellator.sample(aoi, 20000.0, 20000.0);
488             if (expectedNodes < 0) {
489                 Assertions.fail("an exception should have been thrown");
490             } else {
491                 Assertions.assertEquals(1,             sampledZone.size());
492                 Assertions.assertEquals(expectedNodes, sampledZone.get(0).size());
493             }
494         } catch (OrekitException oe) {
495             Assertions.assertEquals(OrekitMessages.CANNOT_COMPUTE_AIMING_AT_SINGULAR_POINT, oe.getSpecifier());
496         }
497 
498     }
499 
500     private S2Point toS2Point(final GeodeticPoint point) {
501         return new S2Point(point.getLongitude(), 0.5 * FastMath.PI - point.getLatitude());
502     }
503 
504     @BeforeEach
505     public void setUp() {
506         Utils.setDataRoot("regular-data");
507         // the following orbital parameters have been computed using
508         // Orekit tutorial about phasing, using the following configuration:
509         //
510         //  orbit.date                          = 2012-01-01T00:00:00.000
511         //  phasing.orbits.number               = 143
512         //  phasing.days.number                 =  10
513         //  sun.synchronous.reference.latitude  = 0
514         //  sun.synchronous.reference.ascending = false
515         //  sun.synchronous.mean.solar.time     = 10:30:00
516         //  gravity.field.degree                = 12
517         //  gravity.field.order                 = 12
518         AbsoluteDate date = new AbsoluteDate("2012-01-01T00:00:00.000", TimeScalesFactory.getUTC());
519         Frame eme2000 = FramesFactory.getEME2000();
520         orbit = new CircularOrbit(7173352.811913891,
521                                   -4.029194321683225E-4, 0.0013530362644647786,
522                                   FastMath.toRadians(98.63218182243709),
523                                   FastMath.toRadians(77.55565567747836),
524                                   FastMath.PI, PositionAngleType.TRUE,
525                                   eme2000, date, Constants.EIGEN5C_EARTH_MU);
526         ellipsoid =
527                 new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
528                                      Constants.WGS84_EARTH_FLATTENING,
529                                      FramesFactory.getITRF(IERSConventions.IERS_2010, true));
530     }
531 
532     private SphericalPolygonsSet buildFrance() {
533 
534         final SphericalPolygonsSet continental = buildSimpleZone(1.0e-10, new double[][] {
535             { 51.14850,  2.51357 }, { 50.94660,  1.63900 }, { 50.12717,  1.33876 }, { 49.34737, -0.98946 },
536             { 49.77634, -1.93349 }, { 48.64442, -1.61651 }, { 48.90169, -3.29581 }, { 48.68416, -4.59234 },
537             { 47.95495, -4.49155 }, { 47.57032, -2.96327 }, { 46.01491, -1.19379 }, { 44.02261, -1.38422 },
538             { 43.42280, -1.90135 }, { 43.03401, -1.50277 }, { 42.34338,  1.82679 }, { 42.47301,  2.98599 },
539             { 43.07520,  3.10041 }, { 43.39965,  4.55696 }, { 43.12889,  6.52924 }, { 43.69384,  7.43518 },
540             { 44.12790,  7.54959 }, { 45.02851,  6.74995 }, { 45.33309,  7.09665 }, { 46.42967,  6.50009 },
541             { 46.27298,  6.02260 }, { 46.72577,  6.03738 }, { 47.62058,  7.46675 }, { 49.01778,  8.09927 },
542             { 49.20195,  6.65822 }, { 49.44266,  5.89775 }, { 49.98537,  4.79922 }
543           });
544 
545         final SphericalPolygonsSet corsica =
546                 EllipsoidTessellator.buildSimpleZone(1.0e-10,
547                                                      new GeodeticPoint(FastMath.toRadians(42.15249),
548                                                                        FastMath.toRadians(9.56001),
549                                                                        0.0),
550                                                      new GeodeticPoint(FastMath.toRadians(43.00998),
551                                                                        FastMath.toRadians(9.39000),
552                                                                        0.0),
553                                                      new GeodeticPoint(FastMath.toRadians(42.62812),
554                                                                        FastMath.toRadians(8.74600),
555                                                                        0.0),
556                                                      new GeodeticPoint(FastMath.toRadians(42.25651),
557                                                                        FastMath.toRadians(8.54421),
558                                                                        0.0),
559                                                      new GeodeticPoint(FastMath.toRadians(41.58361),
560                                                                        FastMath.toRadians(8.77572),
561                                                                        0.0),
562                                                      new GeodeticPoint(FastMath.toRadians(41.38000),
563                                                                        FastMath.toRadians(9.22975),
564                                                                        0.0));
565 
566           return (SphericalPolygonsSet) new RegionFactory<Sphere2D, S2Point, Circle, SubCircle>().
567                  union(continental, corsica);
568 
569     }
570 
571     private SphericalPolygonsSet buildSimpleZone(double tolerance, double[][] points) {
572         for (int i = 0; i < points.length; ++i) {
573             points[i][0] = FastMath.toRadians(points[i][0]);
574             points[i][1] = FastMath.toRadians(points[i][1]);
575         }
576         return EllipsoidTessellator.buildSimpleZone(tolerance, points);
577     }
578 
579     private Orbit orbit;
580     private OneAxisEllipsoid ellipsoid;
581 
582 }