1   /* Copyright 2002-2022 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.Location;
21  import org.hipparchus.geometry.partitioning.RegionFactory;
22  import org.hipparchus.geometry.spherical.twod.S2Point;
23  import org.hipparchus.geometry.spherical.twod.Sphere2D;
24  import org.hipparchus.geometry.spherical.twod.SphericalPolygonsSet;
25  import org.hipparchus.util.FastMath;
26  import org.junit.jupiter.api.Assertions;
27  import org.junit.jupiter.api.BeforeEach;
28  import org.junit.jupiter.api.Test;
29  import org.orekit.Utils;
30  import org.orekit.bodies.GeodeticPoint;
31  import org.orekit.bodies.OneAxisEllipsoid;
32  import org.orekit.errors.OrekitException;
33  import org.orekit.errors.OrekitMessages;
34  import org.orekit.frames.Frame;
35  import org.orekit.frames.FramesFactory;
36  import org.orekit.orbits.CircularOrbit;
37  import org.orekit.orbits.Orbit;
38  import org.orekit.orbits.PositionAngle;
39  import org.orekit.time.AbsoluteDate;
40  import org.orekit.time.TimeScalesFactory;
41  import org.orekit.utils.Constants;
42  import org.orekit.utils.IERSConventions;
43  
44  import java.io.IOException;
45  import java.util.List;
46  
47  public class EllipsoidTessellatorTest {
48  
49      @Test
50      public void testTilesAlongDescendingTrackWithoutTruncation() {
51          final EllipsoidTessellator tessellator =
52                  new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, false), 16);
53          final List<List<Tile>> tiles = tessellator.tessellate(buildFrance(),
54                                                                50000.0, 150000.0, 5000.0, 5000.0,
55                                                                false, false);
56          Assertions.assertEquals(2,   tiles.size());
57          Assertions.assertEquals(109, FastMath.max(tiles.get(0).size(), tiles.get(1).size()));
58          Assertions.assertEquals(4,   FastMath.min(tiles.get(0).size(), tiles.get(1).size()));
59  
60      }
61  
62      @Test
63      public void testTilesAlongDescendingTrackWithTruncation() {
64          final EllipsoidTessellator tessellator =
65                  new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, false), 16);
66          final List<List<Tile>> tiles = tessellator.tessellate(buildFrance(),
67                                                                50000.0, 150000.0, 5000.0, 5000.0,
68                                                                true, true);
69          Assertions.assertEquals(2,   tiles.size());
70          Assertions.assertEquals(108, FastMath.max(tiles.get(0).size(), tiles.get(1).size()));
71          Assertions.assertEquals(4,   FastMath.min(tiles.get(0).size(), tiles.get(1).size()));
72  
73      }
74  
75      @Test
76      public void testSampleAlongDescendingTrack() {
77          final EllipsoidTessellator tessellator =
78                  new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, false), 4);
79          final List<List<GeodeticPoint>> samples = tessellator.sample(buildFrance(), 25000.0, 50000.0);
80          Assertions.assertEquals(2,   samples.size());
81          Assertions.assertEquals(452, FastMath.max(samples.get(0).size(), samples.get(1).size()));
82          Assertions.assertEquals(9,   FastMath.min(samples.get(0).size(), samples.get(1).size()));
83      }
84  
85      @Test
86      public void testTilesAlongAscendingTrack() {
87          final EllipsoidTessellator tessellator =
88                  new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, true), 4);
89          final List<List<Tile>> tiles = tessellator.tessellate(buildFrance(),
90                                                                50000.0, 150000.0, 5000.0, 5000.0,
91                                                                false, false);
92          Assertions.assertEquals(2,   tiles.size());
93          Assertions.assertEquals(112, FastMath.max(tiles.get(0).size(), tiles.get(1).size()));
94          Assertions.assertEquals(6,   FastMath.min(tiles.get(0).size(), tiles.get(1).size()));
95      }
96  
97      @Test
98      public void testSampleAlongAscendingTrack() {
99          final EllipsoidTessellator tessellator =
100                 new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, true), 4);
101         final List<List<GeodeticPoint>> samples = tessellator.sample(buildFrance(),
102                                                               25000.0, 50000.0);
103         Assertions.assertEquals(2,   samples.size());
104         Assertions.assertEquals(452, FastMath.max(samples.get(0).size(), samples.get(1).size()));
105         Assertions.assertEquals(10,  FastMath.min(samples.get(0).size(), samples.get(1).size()));
106     }
107 
108     @Test
109     public void testTilesConstantAzimuth() {
110         final EllipsoidTessellator tessellator =
111                 new EllipsoidTessellator(ellipsoid, new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(120)), 4);
112         final List<List<Tile>> tiles = tessellator.tessellate(buildFrance(),
113                                                               50000.0, 150000.0, -5000.0, -5000.0,
114                                                               false, false);
115         Assertions.assertEquals(2,  tiles.size());
116         Assertions.assertEquals(86, FastMath.max(tiles.get(0).size(), tiles.get(1).size()));
117         Assertions.assertEquals(4,  FastMath.min(tiles.get(0).size(), tiles.get(1).size()));
118         checkTilesDontOverlap(tiles);
119     }
120 
121     @Test
122     public void testSampleConstantAzimuth() {
123         final EllipsoidTessellator tessellator =
124                 new EllipsoidTessellator(ellipsoid, new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(120)), 4);
125         final List<List<GeodeticPoint>> samples = tessellator.sample(buildFrance(), 25000.0, 50000.0);
126         Assertions.assertEquals(2,   samples.size());
127         Assertions.assertEquals(455, FastMath.max(samples.get(0).size(), samples.get(1).size()));
128         Assertions.assertEquals(9,   FastMath.min(samples.get(0).size(), samples.get(1).size()));
129     }
130 
131     @Test
132     public void testTilesIslandJoining() {
133         final EllipsoidTessellator tessellator =
134                 new EllipsoidTessellator(ellipsoid, new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(120.0)), 4);
135         final List<List<Tile>> tiles = tessellator.tessellate(buildFrance(),
136                                                               150000.0, 250000.0, -5000.0, -5000.0,
137                                                               false, false);
138         Assertions.assertEquals(1,  tiles.size());
139         Assertions.assertEquals(28, tiles.get(0).size());
140         checkTilesDontOverlap(tiles);
141     }
142 
143     @Test
144     public void testTilesSmallZoneWithoutTruncation() throws IOException {
145 
146         TileAiming aiming = new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(193.7));
147         EllipsoidTessellator tessellator =
148                 new EllipsoidTessellator(ellipsoid, aiming, 16);
149 
150         SphericalPolygonsSet small = buildSimpleZone(1.0e-10, new double[][] {
151             { 43.6543, 1.4268 }, { 43.6120, 1.4179 }, { 43.6016, 1.3994 }, { 43.5682, 1.4159 },
152             { 43.5707, 1.4358 }, { 43.5573, 1.4941 }, { 43.6041, 1.4866 }
153         });
154 
155         final List<List<Tile>> tiles = tessellator.tessellate(small, 50000.0, 150000.0, 0, 0,
156                                                               false, false);
157         Assertions.assertEquals(1, tiles.size());
158         Assertions.assertEquals(1, tiles.get(0).size());
159         Tile t = tiles.get(0).get(0);
160 
161         // without truncation, the tile must match width and length specification
162         // (the remaining error is due to Cartesian distance and non-developable ellipsoid)
163         Assertions.assertEquals(150000.0,
164                             Vector3D.distance(ellipsoid.transform(t.getVertices()[0]),
165                                               ellipsoid.transform(t.getVertices()[1])),
166                             140.0);
167         Assertions.assertEquals( 50000.0,
168                             Vector3D.distance(ellipsoid.transform(t.getVertices()[1]),
169                                              ellipsoid.transform(t.getVertices()[2])),
170                             0.4);
171         Assertions.assertEquals(150000.0,
172                             Vector3D.distance(ellipsoid.transform(t.getVertices()[2]),
173                                              ellipsoid.transform(t.getVertices()[3])),
174                             140.0);
175         Assertions.assertEquals( 50000.0,
176                             Vector3D.distance(ellipsoid.transform(t.getVertices()[3]),
177                                              ellipsoid.transform(t.getVertices()[0])),
178                             0.4);
179 
180     }
181 
182     @Test
183     public void testTilesSmallZoneWithTruncation() throws IOException {
184 
185         TileAiming aiming = new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(193.7));
186         EllipsoidTessellator tessellator =
187                 new EllipsoidTessellator(ellipsoid, aiming, 16);
188 
189         SphericalPolygonsSet small = buildSimpleZone(1.0e-10, new double[][] {
190             { 43.6543, 1.4268 }, { 43.6120, 1.4179 }, { 43.6016, 1.3994 }, { 43.5682, 1.4159 },
191             { 43.5707, 1.4358 }, { 43.5573, 1.4941 }, { 43.6041, 1.4866 }
192         });
193 
194         final List<List<Tile>> tiles = tessellator.tessellate(small, 50000.0, 150000.0, 0, 0,
195                                                               true, true);
196         Assertions.assertEquals(1, tiles.size());
197         Assertions.assertEquals(1, tiles.get(0).size());
198         Tile t = tiles.get(0).get(0);
199 
200         // with truncation, the tile is a fraction of the width and length specification
201         Assertions.assertEquals(3.0 / 16.0 * 150000.0,
202                             Vector3D.distance(ellipsoid.transform(t.getVertices()[0]),
203                                               ellipsoid.transform(t.getVertices()[1])),
204                             10.0);
205         Assertions.assertEquals(4.0 / 16.0 * 50000.0,
206                             Vector3D.distance(ellipsoid.transform(t.getVertices()[1]),
207                                              ellipsoid.transform(t.getVertices()[2])),
208                             0.01);
209         Assertions.assertEquals(3.0 / 16.0 * 150000.0,
210                            Vector3D.distance(ellipsoid.transform(t.getVertices()[2]),
211                                              ellipsoid.transform(t.getVertices()[3])),
212                            10.0);
213         Assertions.assertEquals(4.0 / 16.0 * 50000.0,
214                             Vector3D.distance(ellipsoid.transform(t.getVertices()[3]),
215                                              ellipsoid.transform(t.getVertices()[0])),
216                             0.01);
217     }
218 
219     @Test
220     public void testStairedTruncatedTiles() {
221 
222         TileAiming aiming = new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(170.0));
223         EllipsoidTessellator tessellator =
224                 new EllipsoidTessellator(ellipsoid, aiming, 16);
225 
226         SphericalPolygonsSet small = buildSimpleZone(1.0e-10, new double[][] {
227             { 45.335,  0.457 },
228             { 45.342,  0.469 },
229             { 45.371,  0.424 }
230         });
231 
232         final double maxWidth  = 800.0;
233         final double maxLength = 10000.0;
234         final List<List<Tile>> tiles = tessellator.tessellate(small, maxWidth, maxLength, 0, 0,
235                                                               false, true);
236         Assertions.assertEquals(1, tiles.size());
237         Assertions.assertEquals(4, tiles.get(0).size());
238         for (final Tile tile : tiles.get(0)) {
239             Vector3D v0 = ellipsoid.transform(tile.getVertices()[0]);
240             Vector3D v1 = ellipsoid.transform(tile.getVertices()[1]);
241             Vector3D v2 = ellipsoid.transform(tile.getVertices()[2]);
242             Vector3D v3 = ellipsoid.transform(tile.getVertices()[3]);
243             Assertions.assertTrue(Vector3D.distance(v0, v1) < (6.0002 / 16.0) * maxLength);
244             Assertions.assertTrue(Vector3D.distance(v2, v3) < (6.0002 / 16.0) * maxLength);
245             Assertions.assertEquals(maxWidth, Vector3D.distance(v1, v2), 1.0e-3);
246             Assertions.assertEquals(maxWidth, Vector3D.distance(v3, v0), 1.0e-3);
247         }
248 
249     }
250 
251     @Test
252     public void testTooThinRemainingRegion() {
253         TileAiming aiming = new ConstantAzimuthAiming(ellipsoid, -0.2185);
254         EllipsoidTessellator tessellator =
255                 new EllipsoidTessellator(ellipsoid, aiming, 16);
256 
257         SphericalPolygonsSet small = buildSimpleZone(1.0e-10, new double[][]{
258             { 32.7342, -16.9407 }, { 32.7415, -16.9422 }, { 32.7481, -16.9463 }, { 32.7531, -16.9528 },
259             { 32.7561, -16.9608 }, { 32.7567, -16.9696 }, { 32.7549, -16.9781 }, { 32.7508, -16.9855 },
260             { 32.7450, -16.9909 }, { 32.7379, -16.9937 }, { 32.7305, -16.9937 }, { 32.7235, -16.9909 },
261             { 32.7177, -16.9855 }, { 32.7136, -16.9781 }, { 32.7118, -16.9696 }, { 32.7124, -16.9608 },
262             { 32.7154, -16.9528 }, { 32.7204, -16.9463 }, { 32.7269, -16.9422 }
263         });
264 
265         final double maxWidth  = 40000.0;
266         final double maxLength = 40000.0;
267         final List<List<Tile>> tiles = tessellator.tessellate(small, maxWidth, maxLength, 0, 0,
268                                                               false, true);
269         Assertions.assertEquals(1, tiles.size());
270         Assertions.assertEquals(1, tiles.get(0).size());
271         for (final Tile tile : tiles.get(0)) {
272             Vector3D v0 = ellipsoid.transform(tile.getVertices()[0]);
273             Vector3D v1 = ellipsoid.transform(tile.getVertices()[1]);
274             Vector3D v2 = ellipsoid.transform(tile.getVertices()[2]);
275             Vector3D v3 = ellipsoid.transform(tile.getVertices()[3]);
276             Assertions.assertTrue(Vector3D.distance(v0, v1) < 0.3 * maxLength);
277             Assertions.assertEquals(maxWidth, Vector3D.distance(v1, v2), 0.1);
278             Assertions.assertTrue(Vector3D.distance(v2, v3) < 0.3 * maxLength);
279             Assertions.assertEquals(maxWidth, Vector3D.distance(v3, v0), 0.1);
280         }
281 
282     }
283 
284     @Test
285     public void testNormalZoneTolerance() {
286         doTestVariableTolerance(1.0e-10);
287     }
288 
289     @Test
290     public void testLargeZoneTolerance() {
291         // this used to trigger an exception in EllipsoidTessellator.recurseMeetInside (Orekit)
292         doTestVariableTolerance(1.0e-6);
293     }
294 
295     @Test
296     public void testHugeZoneTolerance() {
297         // this used to trigger an exception in Characterization.characterize (Apache Commons Math)
298         // this was due to issue MATH-1266, solved in Apache Commons Math 3.6
299         doTestVariableTolerance(1.0e-4);
300     }
301 
302     private void doTestVariableTolerance(final double tolerance) {
303         final ConstantAzimuthAiming aiming = new ConstantAzimuthAiming(ellipsoid,
304                                                                        FastMath.toRadians(-168.178485));
305         EllipsoidTessellator tessellator =
306                 new EllipsoidTessellator(ellipsoid, aiming, 16);
307 
308         SphericalPolygonsSet small = buildSimpleZone(tolerance, new double[][]{
309             { -0.01048739, 0.01598931 }, { -0.00789627, 0.01555693 }, { -0.00558595, 0.01430664 },
310             { -0.00380677, 0.01237394 }, { -0.00275154, 0.00996826 }, { -0.00253461, 0.00735029 },
311             { -0.00317949, 0.00480374 }, { -0.00461629, 0.00260455 }, { -0.00668931, 0.00099105 },
312             { -0.00917392, 0.00013808 }, { -0.01180086, 0.00013808 }, { -0.01428546, 0.00099105 },
313             { -0.01635849, 0.00260455 }, { -0.01779529, 0.00480374 }, { -0.01844016, 0.00735029 },
314             { -0.01822323, 0.00996826 }, { -0.01716800, 0.01237394 }, { -0.01538882, 0.01430664 },
315             { -0.01307850, 0.01555693 }
316         });
317 
318         final double maxWidth  = 40000.0;
319         final double maxLength = 40000.0;
320         final List<List<Tile>> tiles =
321                 tessellator.tessellate(small, maxWidth, maxLength, 0, 0, false, true);
322         Assertions.assertEquals(1, tiles.size());
323         Assertions.assertEquals(1, tiles.get(0).size());
324 
325     }
326 
327     private void checkTilesDontOverlap(final List<List<Tile>> tiles) {
328         for (final List<Tile> list : tiles) {
329             for (final Tile tile : list) {
330                 final SphericalPolygonsSet quadrilateral =
331                         new SphericalPolygonsSet(1.0e-10,
332                                                  toS2Point(tile.getVertices()[0]),
333                                                  toS2Point(tile.getVertices()[1]),
334                                                  toS2Point(tile.getVertices()[2]),
335                                                  toS2Point(tile.getVertices()[3]));
336                 for (final List<Tile> otherList : tiles) {
337                     for (final Tile otherTile : otherList) {
338                         if (otherTile != tile) {
339                             for (final GeodeticPoint vertex : otherTile.getVertices()) {
340                                 Assertions.assertEquals(Location.OUTSIDE, quadrilateral.checkPoint(toS2Point(vertex)),"tiles overlap at: " + vertex);
341                             }
342                         }
343                     }
344                 }
345             }
346         }
347     }
348 
349     @Test
350     public void testSampleAroundPoleConstantAzimuth() {
351         SphericalPolygonsSet aoi = new SphericalPolygonsSet(1.e-9, new S2Point[] {
352             new S2Point(FastMath.toRadians(-120.0), FastMath.toRadians(5.0)),
353             new S2Point(FastMath.toRadians(   0.0), FastMath.toRadians(5.0)),
354             new S2Point(FastMath.toRadians( 120.0), FastMath.toRadians(5.0))
355         });
356         doTestSampleAroundPole(aoi, new ConstantAzimuthAiming(ellipsoid, 0.0), -1);
357     }
358 
359     @Test
360     public void testSampleAroundPoleDivertedSingularity() {
361         SphericalPolygonsSet aoi = new SphericalPolygonsSet(1.e-9, new S2Point[] {
362             new S2Point(FastMath.toRadians(-120.0), FastMath.toRadians(5.0)),
363             new S2Point(FastMath.toRadians(   0.0), FastMath.toRadians(5.0)),
364             new S2Point(FastMath.toRadians( 120.0), FastMath.toRadians(5.0))
365         });
366         doTestSampleAroundPole(aoi, new DivertedSingularityAiming(aoi), 993);
367     }
368 
369     private void doTestSampleAroundPole(final SphericalPolygonsSet aoi, final TileAiming aiming, final int expectedNodes) {
370         EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, aiming, 1);
371         try {
372             List<List<GeodeticPoint>> sampledZone = tessellator.sample(aoi, 20000.0, 20000.0);
373             if (expectedNodes < 0) {
374                 Assertions.fail("an exception should have been thrown");
375             } else {
376                 Assertions.assertEquals(1,             sampledZone.size());
377                 Assertions.assertEquals(expectedNodes, sampledZone.get(0).size());
378             }
379         } catch (OrekitException oe) {
380             Assertions.assertEquals(OrekitMessages.CANNOT_COMPUTE_AIMING_AT_SINGULAR_POINT, oe.getSpecifier());
381         }
382 
383     }
384 
385     private S2Point toS2Point(final GeodeticPoint point) {
386         return new S2Point(point.getLongitude(), 0.5 * FastMath.PI - point.getLatitude());
387     }
388 
389     @BeforeEach
390     public void setUp() {
391         Utils.setDataRoot("regular-data");
392         // the following orbital parameters have been computed using
393         // Orekit tutorial about phasing, using the following configuration:
394         //
395         //  orbit.date                          = 2012-01-01T00:00:00.000
396         //  phasing.orbits.number               = 143
397         //  phasing.days.number                 =  10
398         //  sun.synchronous.reference.latitude  = 0
399         //  sun.synchronous.reference.ascending = false
400         //  sun.synchronous.mean.solar.time     = 10:30:00
401         //  gravity.field.degree                = 12
402         //  gravity.field.order                 = 12
403         AbsoluteDate date = new AbsoluteDate("2012-01-01T00:00:00.000", TimeScalesFactory.getUTC());
404         Frame eme2000 = FramesFactory.getEME2000();
405         orbit = new CircularOrbit(7173352.811913891,
406                                   -4.029194321683225E-4, 0.0013530362644647786,
407                                   FastMath.toRadians(98.63218182243709),
408                                   FastMath.toRadians(77.55565567747836),
409                                   FastMath.PI, PositionAngle.TRUE,
410                                   eme2000, date, Constants.EIGEN5C_EARTH_MU);
411         ellipsoid =
412                 new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
413                                      Constants.WGS84_EARTH_FLATTENING,
414                                      FramesFactory.getITRF(IERSConventions.IERS_2010, true));
415     }
416 
417     private SphericalPolygonsSet buildFrance() {
418 
419         final SphericalPolygonsSet continental = buildSimpleZone(1.0e-10, new double[][] {
420             { 51.14850,  2.51357 }, { 50.94660,  1.63900 }, { 50.12717,  1.33876 }, { 49.34737, -0.98946 },
421             { 49.77634, -1.93349 }, { 48.64442, -1.61651 }, { 48.90169, -3.29581 }, { 48.68416, -4.59234 },
422             { 47.95495, -4.49155 }, { 47.57032, -2.96327 }, { 46.01491, -1.19379 }, { 44.02261, -1.38422 },
423             { 43.42280, -1.90135 }, { 43.03401, -1.50277 }, { 42.34338,  1.82679 }, { 42.47301,  2.98599 },
424             { 43.07520,  3.10041 }, { 43.39965,  4.55696 }, { 43.12889,  6.52924 }, { 43.69384,  7.43518 },
425             { 44.12790,  7.54959 }, { 45.02851,  6.74995 }, { 45.33309,  7.09665 }, { 46.42967,  6.50009 },
426             { 46.27298,  6.02260 }, { 46.72577,  6.03738 }, { 47.62058,  7.46675 }, { 49.01778,  8.09927 },
427             { 49.20195,  6.65822 }, { 49.44266,  5.89775 }, { 49.98537,  4.79922 }
428           });
429 
430         final SphericalPolygonsSet corsica =
431                 EllipsoidTessellator.buildSimpleZone(1.0e-10,
432                                                      new GeodeticPoint(FastMath.toRadians(42.15249),
433                                                                        FastMath.toRadians(9.56001),
434                                                                        0.0),
435                                                      new GeodeticPoint(FastMath.toRadians(43.00998),
436                                                                        FastMath.toRadians(9.39000),
437                                                                        0.0),
438                                                      new GeodeticPoint(FastMath.toRadians(42.62812),
439                                                                        FastMath.toRadians(8.74600),
440                                                                        0.0),
441                                                      new GeodeticPoint(FastMath.toRadians(42.25651),
442                                                                        FastMath.toRadians(8.54421),
443                                                                        0.0),
444                                                      new GeodeticPoint(FastMath.toRadians(41.58361),
445                                                                        FastMath.toRadians(8.77572),
446                                                                        0.0),
447                                                      new GeodeticPoint(FastMath.toRadians(41.38000),
448                                                                        FastMath.toRadians(9.22975),
449                                                                        0.0));
450 
451           return (SphericalPolygonsSet) new RegionFactory<Sphere2D>().union(continental, corsica);
452 
453     }
454 
455     private SphericalPolygonsSet buildSimpleZone(double tolerance, double[][] points) {
456         for (int i = 0; i < points.length; ++i) {
457             points[i][0] = FastMath.toRadians(points[i][0]);
458             points[i][1] = FastMath.toRadians(points[i][1]);
459         }
460         return EllipsoidTessellator.buildSimpleZone(tolerance, points);
461     }
462 
463     private Orbit orbit;
464     private OneAxisEllipsoid ellipsoid;
465 
466 }