View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF 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.apache.commons.geometry.spherical.twod;
18  
19  import java.util.regex.Pattern;
20  
21  import org.apache.commons.geometry.core.GeometryTestUtils;
22  import org.apache.commons.geometry.core.partitioning.HyperplaneLocation;
23  import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
24  import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
25  import org.apache.commons.geometry.euclidean.threed.Vector3D;
26  import org.apache.commons.geometry.spherical.SphericalTestUtils;
27  import org.apache.commons.geometry.spherical.oned.AngularInterval;
28  import org.apache.commons.geometry.spherical.oned.Point1S;
29  import org.apache.commons.numbers.angle.PlaneAngleRadians;
30  import org.junit.Assert;
31  import org.junit.Test;
32  
33  public class GreatCircleTest {
34  
35      private static final double TEST_EPS = 1e-10;
36  
37      private static final DoublePrecisionContext TEST_PRECISION =
38              new EpsilonDoublePrecisionContext(TEST_EPS);
39  
40      private static final Vector3D.Unit X = Vector3D.Unit.PLUS_X;
41      private static final Vector3D.Unit Y = Vector3D.Unit.PLUS_Y;
42      private static final Vector3D.Unit Z = Vector3D.Unit.PLUS_Z;
43  
44      @Test
45      public void testFromPole() {
46          // act/assert
47          checkGreatCircle(GreatCircles.fromPole(X, TEST_PRECISION), X, Z);
48          checkGreatCircle(GreatCircles.fromPole(Y, TEST_PRECISION), Y, Z.negate());
49          checkGreatCircle(GreatCircles.fromPole(Z, TEST_PRECISION), Z, Y);
50      }
51  
52      @Test
53      public void testFromPoleAndXAxis() {
54          // act/assert
55          checkGreatCircle(GreatCircles.fromPoleAndU(X, Y, TEST_PRECISION), X, Y);
56          checkGreatCircle(GreatCircles.fromPoleAndU(X, Z, TEST_PRECISION), X, Z);
57          checkGreatCircle(GreatCircles.fromPoleAndU(Y, Z, TEST_PRECISION), Y, Z);
58      }
59  
60      @Test
61      public void testFromPoints() {
62          // act/assert
63          checkGreatCircle(GreatCircles.fromPoints(
64                      Point2S.of(0, PlaneAngleRadians.PI_OVER_TWO),
65                      Point2S.of(PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI_OVER_TWO),
66                      TEST_PRECISION),
67                  Z, X);
68  
69          checkGreatCircle(GreatCircles.fromPoints(
70                  Point2S.of(0, PlaneAngleRadians.PI_OVER_TWO),
71                  Point2S.of(-0.1 * PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO),
72                  TEST_PRECISION),
73              Z.negate(), X);
74  
75          checkGreatCircle(GreatCircles.fromPoints(
76                  Point2S.of(0, PlaneAngleRadians.PI_OVER_TWO),
77                  Point2S.of(1.5 * PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO),
78                  TEST_PRECISION),
79              Z.negate(), X);
80  
81          checkGreatCircle(GreatCircles.fromPoints(
82                  Point2S.of(0, 0),
83                  Point2S.of(0, PlaneAngleRadians.PI_OVER_TWO),
84                  TEST_PRECISION),
85              Y, Z);
86      }
87  
88      @Test
89      public void testFromPoints_invalidPoints() {
90          // arrange
91          final Point2S p1 = Point2S.of(0, PlaneAngleRadians.PI_OVER_TWO);
92          final Point2S p2 = Point2S.of(PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO);
93  
94          // act/assert
95          GeometryTestUtils.assertThrows(() -> {
96              GreatCircles.fromPoints(p1, p1, TEST_PRECISION);
97          }, IllegalArgumentException.class, Pattern.compile("^.*points are equal$"));
98  
99          GeometryTestUtils.assertThrows(() -> {
100             GreatCircles.fromPoints(p1, Point2S.of(1e-12, PlaneAngleRadians.PI_OVER_TWO), TEST_PRECISION);
101         }, IllegalArgumentException.class, Pattern.compile("^.*points are equal$"));
102 
103         GeometryTestUtils.assertThrows(() -> {
104             GreatCircles.fromPoints(
105                     Point2S.from(Vector3D.Unit.PLUS_X),
106                     Point2S.from(Vector3D.Unit.MINUS_X),
107                     TEST_PRECISION);
108         }, IllegalArgumentException.class, Pattern.compile("^.*points are antipodal$"));
109 
110         GeometryTestUtils.assertThrows(() -> {
111             GreatCircles.fromPoints(p1, Point2S.NaN, TEST_PRECISION);
112         }, IllegalArgumentException.class);
113 
114         GeometryTestUtils.assertThrows(() -> {
115             GreatCircles.fromPoints(Point2S.NaN, p2, TEST_PRECISION);
116         }, IllegalArgumentException.class);
117 
118         GeometryTestUtils.assertThrows(() -> {
119             GreatCircles.fromPoints(p1, Point2S.of(Double.POSITIVE_INFINITY, PlaneAngleRadians.PI_OVER_TWO), TEST_PRECISION);
120         }, IllegalArgumentException.class);
121 
122         GeometryTestUtils.assertThrows(() -> {
123             GreatCircles.fromPoints(Point2S.of(Double.POSITIVE_INFINITY, PlaneAngleRadians.PI_OVER_TWO), p2, TEST_PRECISION);
124         }, IllegalArgumentException.class);
125     }
126 
127     @Test
128     public void testOffset_point() {
129         // --- arrange
130         final GreatCircle circle = GreatCircles.fromPoleAndU(
131                 Vector3D.Unit.MINUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
132 
133         // --- act/assert
134 
135         // on circle
136         for (double polar = -PlaneAngleRadians.PI_OVER_TWO; polar <= PlaneAngleRadians.PI_OVER_TWO; polar += 0.1) {
137             Assert.assertEquals(0, circle.offset(Point2S.of(PlaneAngleRadians.PI_OVER_TWO, polar)), TEST_EPS);
138             Assert.assertEquals(0, circle.offset(Point2S.of(-PlaneAngleRadians.PI_OVER_TWO, polar)), TEST_EPS);
139         }
140 
141         // +1/-1
142         Assert.assertEquals(-1, circle.offset(Point2S.of(PlaneAngleRadians.PI_OVER_TWO + 1, PlaneAngleRadians.PI_OVER_TWO)), TEST_EPS);
143         Assert.assertEquals(1, circle.offset(Point2S.of(-PlaneAngleRadians.PI_OVER_TWO + 1, PlaneAngleRadians.PI_OVER_TWO)), TEST_EPS);
144 
145         // poles
146         Assert.assertEquals(-PlaneAngleRadians.PI_OVER_TWO, circle.offset(Point2S.of(PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO)), TEST_EPS);
147         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, circle.offset(Point2S.of(0.0, PlaneAngleRadians.PI_OVER_TWO)), TEST_EPS);
148     }
149 
150     @Test
151     public void testOffset_vector() {
152         // --- arrange
153         final GreatCircle circle = GreatCircles.fromPoleAndU(
154                 Vector3D.Unit.MINUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
155 
156         // --- act/assert
157 
158         // on circle
159         Assert.assertEquals(0, circle.offset(Vector3D.of(0, 1, 0)), TEST_EPS);
160         Assert.assertEquals(0, circle.offset(Vector3D.of(0, 0, 1)), TEST_EPS);
161         Assert.assertEquals(0, circle.offset(Vector3D.of(0, -1, 0)), TEST_EPS);
162         Assert.assertEquals(0, circle.offset(Vector3D.of(0, 0, -1)), TEST_EPS);
163 
164         // +1/-1
165         Assert.assertEquals(-0.25 * PlaneAngleRadians.PI, circle.offset(Vector3D.of(-1, 1, 0)), TEST_EPS);
166         Assert.assertEquals(-0.25 * PlaneAngleRadians.PI, circle.offset(Vector3D.of(-1, 0, 1)), TEST_EPS);
167         Assert.assertEquals(-0.25 * PlaneAngleRadians.PI, circle.offset(Vector3D.of(-1, -1, 0)), TEST_EPS);
168         Assert.assertEquals(-0.25 * PlaneAngleRadians.PI, circle.offset(Vector3D.of(-1, 0, -1)), TEST_EPS);
169 
170         Assert.assertEquals(0.25 * PlaneAngleRadians.PI, circle.offset(Vector3D.of(1, 1, 0)), TEST_EPS);
171         Assert.assertEquals(0.25 * PlaneAngleRadians.PI, circle.offset(Vector3D.of(1, 0, 1)), TEST_EPS);
172         Assert.assertEquals(0.25 * PlaneAngleRadians.PI, circle.offset(Vector3D.of(1, -1, 0)), TEST_EPS);
173         Assert.assertEquals(0.25 * PlaneAngleRadians.PI, circle.offset(Vector3D.of(1, 0, -1)), TEST_EPS);
174 
175         // poles
176         Assert.assertEquals(-PlaneAngleRadians.PI_OVER_TWO, circle.offset(Vector3D.Unit.MINUS_X), TEST_EPS);
177         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, circle.offset(Vector3D.Unit.PLUS_X), TEST_EPS);
178     }
179 
180     @Test
181     public void testAzimuth_point() {
182         // --- arrange
183         final GreatCircle circle = GreatCircles.fromPoleAndU(
184                 Vector3D.Unit.MINUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
185 
186         // --- act/assert
187 
188         // on circle
189         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, circle.azimuth(Point2S.from(Vector3D.of(0, 1, 0))), TEST_EPS);
190         Assert.assertEquals(0.0, circle.azimuth(Point2S.from(Vector3D.of(0, 0, 1))), TEST_EPS);
191         Assert.assertEquals(1.5 * PlaneAngleRadians.PI, circle.azimuth(Point2S.from(Vector3D.of(0, -1, 0))), TEST_EPS);
192         Assert.assertEquals(PlaneAngleRadians.PI, circle.azimuth(Point2S.from(Vector3D.of(0, 0, -1))), TEST_EPS);
193 
194         // +1/-1
195         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, circle.azimuth(Point2S.from(Vector3D.of(-1, 1, 0))), TEST_EPS);
196         Assert.assertEquals(0.0, circle.azimuth(Point2S.from(Vector3D.of(-1, 0, 1))), TEST_EPS);
197         Assert.assertEquals(1.5 * PlaneAngleRadians.PI, circle.azimuth(Point2S.from(Vector3D.of(-1, -1, 0))), TEST_EPS);
198         Assert.assertEquals(PlaneAngleRadians.PI, circle.azimuth(Point2S.from(Vector3D.of(-1, 0, -1))), TEST_EPS);
199 
200         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, circle.azimuth(Point2S.from(Vector3D.of(1, 1, 0))), TEST_EPS);
201         Assert.assertEquals(0.0, circle.azimuth(Point2S.from(Vector3D.of(1, 0, 1))), TEST_EPS);
202         Assert.assertEquals(1.5 * PlaneAngleRadians.PI, circle.azimuth(Point2S.from(Vector3D.of(1, -1, 0))), TEST_EPS);
203         Assert.assertEquals(PlaneAngleRadians.PI, circle.azimuth(Point2S.from(Vector3D.of(1, 0, -1))), TEST_EPS);
204 
205         // poles
206         Assert.assertEquals(0, circle.azimuth(Point2S.from(Vector3D.Unit.MINUS_X)), TEST_EPS);
207         Assert.assertEquals(0, circle.azimuth(Point2S.from(Vector3D.Unit.PLUS_X)), TEST_EPS);
208     }
209 
210     @Test
211     public void testAzimuth_vector() {
212         // --- arrange
213         final GreatCircle circle = GreatCircles.fromPoleAndU(
214                 Vector3D.Unit.MINUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
215 
216         // --- act/assert
217 
218         // on circle
219         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, circle.azimuth(Vector3D.of(0, 1, 0)), TEST_EPS);
220         Assert.assertEquals(0.0, circle.azimuth(Vector3D.of(0, 0, 1)), TEST_EPS);
221         Assert.assertEquals(1.5 * PlaneAngleRadians.PI, circle.azimuth(Vector3D.of(0, -1, 0)), TEST_EPS);
222         Assert.assertEquals(PlaneAngleRadians.PI, circle.azimuth(Vector3D.of(0, 0, -1)), TEST_EPS);
223 
224         // +1/-1
225         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, circle.azimuth(Vector3D.of(-1, 1, 0)), TEST_EPS);
226         Assert.assertEquals(0.0, circle.azimuth(Vector3D.of(-1, 0, 1)), TEST_EPS);
227         Assert.assertEquals(1.5 * PlaneAngleRadians.PI, circle.azimuth(Vector3D.of(-1, -1, 0)), TEST_EPS);
228         Assert.assertEquals(PlaneAngleRadians.PI, circle.azimuth(Vector3D.of(-1, 0, -1)), TEST_EPS);
229 
230         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, circle.azimuth(Vector3D.of(1, 1, 0)), TEST_EPS);
231         Assert.assertEquals(0.0, circle.azimuth(Vector3D.of(1, 0, 1)), TEST_EPS);
232         Assert.assertEquals(1.5 * PlaneAngleRadians.PI, circle.azimuth(Vector3D.of(1, -1, 0)), TEST_EPS);
233         Assert.assertEquals(PlaneAngleRadians.PI, circle.azimuth(Vector3D.of(1, 0, -1)), TEST_EPS);
234 
235         // poles
236         Assert.assertEquals(0, circle.azimuth(Vector3D.Unit.MINUS_X), TEST_EPS);
237         Assert.assertEquals(0, circle.azimuth(Vector3D.Unit.PLUS_X), TEST_EPS);
238     }
239 
240     @Test
241     public void testVectorAt() {
242         // arrange
243         final GreatCircle circle = GreatCircles.fromPoleAndU(
244                 Vector3D.Unit.MINUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
245 
246         // act/assert
247         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.PLUS_Z, circle.vectorAt(0.0), TEST_EPS);
248         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.PLUS_Y, circle.vectorAt(PlaneAngleRadians.PI_OVER_TWO), TEST_EPS);
249         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.MINUS_Z, circle.vectorAt(PlaneAngleRadians.PI), TEST_EPS);
250         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.MINUS_Y, circle.vectorAt(-PlaneAngleRadians.PI_OVER_TWO), TEST_EPS);
251         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.PLUS_Z, circle.vectorAt(PlaneAngleRadians.TWO_PI), TEST_EPS);
252     }
253 
254     @Test
255     public void testProject() {
256         // arrange
257         final GreatCircle circle = GreatCircles.fromPoleAndU(
258                 Vector3D.Unit.MINUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
259 
260         // act/assert
261         SphericalTestUtils.assertPointsEqual(Point2S.of(PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI_OVER_TWO),
262                 circle.project(Point2S.of(PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI_OVER_TWO)), TEST_EPS);
263         SphericalTestUtils.assertPointsEqual(Point2S.of(PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI_OVER_TWO),
264                 circle.project(Point2S.of(PlaneAngleRadians.PI_OVER_TWO + 1, PlaneAngleRadians.PI_OVER_TWO)), TEST_EPS);
265         SphericalTestUtils.assertPointsEqual(Point2S.of(PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI_OVER_TWO),
266                 circle.project(Point2S.of(PlaneAngleRadians.PI_OVER_TWO - 1, PlaneAngleRadians.PI_OVER_TWO)), TEST_EPS);
267 
268         SphericalTestUtils.assertPointsEqual(Point2S.of(-PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI_OVER_TWO),
269                 circle.project(Point2S.of(-PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI_OVER_TWO)), TEST_EPS);
270         SphericalTestUtils.assertPointsEqual(Point2S.of(-PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI_OVER_TWO),
271                 circle.project(Point2S.of(-PlaneAngleRadians.PI_OVER_TWO + 1, PlaneAngleRadians.PI_OVER_TWO)), TEST_EPS);
272         SphericalTestUtils.assertPointsEqual(Point2S.of(-PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI_OVER_TWO),
273                 circle.project(Point2S.of(-PlaneAngleRadians.PI_OVER_TWO - 1, PlaneAngleRadians.PI_OVER_TWO)), TEST_EPS);
274     }
275 
276     @Test
277     public void testProject_poles() {
278         // arrange
279         final GreatCircle minusXCircle = GreatCircles.fromPoleAndU(
280                 Vector3D.Unit.MINUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
281         final GreatCircle plusZCircle = GreatCircles.fromPoleAndU(
282                 Vector3D.Unit.PLUS_Z, Vector3D.Unit.MINUS_Y, TEST_PRECISION);
283 
284         // act
285         SphericalTestUtils.assertPointsEqual(Point2S.of(0.0, 0.0),
286                 minusXCircle.project(Point2S.from(Vector3D.Unit.MINUS_X)), TEST_EPS);
287         SphericalTestUtils.assertPointsEqual(Point2S.of(0.0, 0.0),
288                 minusXCircle.project(Point2S.from(Vector3D.Unit.PLUS_X)), TEST_EPS);
289 
290         SphericalTestUtils.assertPointsEqual(Point2S.of(1.5 * PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO),
291                 plusZCircle.project(Point2S.from(Vector3D.Unit.PLUS_Z)), TEST_EPS);
292         SphericalTestUtils.assertPointsEqual(Point2S.of(1.5 * PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO),
293                 plusZCircle.project(Point2S.from(Vector3D.Unit.MINUS_Z)), TEST_EPS);
294     }
295 
296     @Test
297     public void testReverse() {
298         // arrange
299         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
300 
301         // act
302         final GreatCircle reverse = circle.reverse();
303 
304         // assert
305         checkGreatCircle(reverse, Vector3D.Unit.MINUS_Z, Vector3D.Unit.PLUS_X);
306     }
307 
308     @Test
309     public void testTransform_rotateAroundPole() {
310         // arrange
311         final GreatCircle circle = GreatCircles.fromPoints(
312                 Point2S.of(0, PlaneAngleRadians.PI_OVER_TWO),
313                 Point2S.of(1, PlaneAngleRadians.PI_OVER_TWO),
314                 TEST_PRECISION);
315 
316         final Transform2S t = Transform2S.createRotation(circle.getPolePoint(), 0.25 * PlaneAngleRadians.PI);
317 
318         // act
319         final GreatCircle result = circle.transform(t);
320 
321         // assert
322         Assert.assertNotSame(circle, result);
323         checkGreatCircle(result, Vector3D.Unit.PLUS_Z, Vector3D.Unit.from(1, 1, 0));
324     }
325 
326     @Test
327     public void testTransform_rotateAroundNonPole() {
328         // arrange
329         final GreatCircle circle = GreatCircles.fromPoints(
330                 Point2S.of(0, PlaneAngleRadians.PI_OVER_TWO),
331                 Point2S.of(1, PlaneAngleRadians.PI_OVER_TWO),
332                 TEST_PRECISION);
333 
334         final Transform2S t = Transform2S.createRotation(Point2S.of(0, PlaneAngleRadians.PI_OVER_TWO), PlaneAngleRadians.PI_OVER_TWO);
335 
336         // act
337         final GreatCircle result = circle.transform(t);
338 
339         // assert
340         Assert.assertNotSame(circle, result);
341         checkGreatCircle(result, Vector3D.Unit.MINUS_Y, Vector3D.Unit.PLUS_X);
342     }
343 
344     @Test
345     public void testTransform_piMinusAzimuth() {
346         // arrange
347         final GreatCircle circle = GreatCircles.fromPoints(
348                 Point2S.of(0, PlaneAngleRadians.PI_OVER_TWO),
349                 Point2S.of(1, PlaneAngleRadians.PI_OVER_TWO),
350                 TEST_PRECISION);
351 
352         final Transform2S t = Transform2S.createReflection(Point2S.PLUS_J)
353                 .rotate(Point2S.PLUS_K, PlaneAngleRadians.PI);
354 
355         // act
356         final GreatCircle result = circle.transform(t);
357 
358         // assert
359         Assert.assertNotSame(circle, result);
360         checkGreatCircle(result, Vector3D.Unit.MINUS_Z, Vector3D.Unit.MINUS_X);
361     }
362 
363     @Test
364     public void testSimilarOrientation() {
365         // arrange
366         final GreatCircle a = GreatCircles.fromPole(Vector3D.Unit.PLUS_Z, TEST_PRECISION);
367         final GreatCircle b = GreatCircles.fromPole(Vector3D.Unit.PLUS_X, TEST_PRECISION);
368         final GreatCircle c = GreatCircles.fromPole(Vector3D.Unit.MINUS_Z, TEST_PRECISION);
369         final GreatCircle d = GreatCircles.fromPole(Vector3D.Unit.from(1, 1, -1), TEST_PRECISION);
370         final GreatCircle e = GreatCircles.fromPole(Vector3D.Unit.from(1, 1, 1), TEST_PRECISION);
371 
372         // act/assert
373         Assert.assertTrue(a.similarOrientation(a));
374 
375         Assert.assertFalse(a.similarOrientation(b));
376         Assert.assertFalse(a.similarOrientation(c));
377         Assert.assertFalse(a.similarOrientation(d));
378 
379         Assert.assertTrue(a.similarOrientation(e));
380     }
381 
382     @Test
383     public void testSpan() {
384         // arrange
385         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
386 
387         // act
388         final GreatArc span = circle.span();
389 
390         // assert
391         Assert.assertSame(circle, span.getCircle());
392         Assert.assertTrue(span.getInterval().isFull());
393 
394         Assert.assertNull(span.getStartPoint());
395         Assert.assertNull(span.getEndPoint());
396     }
397 
398     @Test
399     public void testArc_points_2s() {
400         // arrange
401         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
402 
403         // act/assert
404         checkArc(circle.arc(Point2S.of(1, PlaneAngleRadians.PI_OVER_TWO), Point2S.of(0, 1)),
405                 Point2S.of(PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI_OVER_TWO), Point2S.of(0, 0));
406 
407         Assert.assertTrue(circle.arc(Point2S.PLUS_I, Point2S.PLUS_I).isFull());
408     }
409 
410     @Test
411     public void testArc_points_1s() {
412         // arrange
413         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
414 
415         // act/assert
416         checkArc(circle.arc(Point1S.of(PlaneAngleRadians.PI), Point1S.of(1.5 * PlaneAngleRadians.PI)),
417                 Point2S.of(0, PlaneAngleRadians.PI), Point2S.of(PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI_OVER_TWO));
418 
419         Assert.assertTrue(circle.arc(Point1S.of(1), Point1S.of(1)).isFull());
420     }
421 
422     @Test
423     public void testArc_azimuths() {
424         // arrange
425         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
426 
427         // act/assert
428         checkArc(circle.arc(PlaneAngleRadians.PI, 1.5 * PlaneAngleRadians.PI),
429                 Point2S.of(0, PlaneAngleRadians.PI), Point2S.of(PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI_OVER_TWO));
430 
431         Assert.assertTrue(circle.arc(1, 1).isFull());
432     }
433 
434     @Test
435     public void testArc_interval() {
436         // arrange
437         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
438         final AngularInterval.Convex interval = AngularInterval.Convex.of(1, 2, TEST_PRECISION);
439 
440         // act
441         final GreatArc arc = circle.arc(interval);
442 
443         // assert
444         Assert.assertSame(circle, arc.getCircle());
445         Assert.assertSame(interval, arc.getInterval());
446     }
447 
448     @Test
449     public void testIntersection_parallel() {
450         // arrange
451         final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-3);
452 
453         final GreatCircle a = GreatCircles.fromPole(Vector3D.Unit.PLUS_X, precision);
454         final GreatCircle b = GreatCircles.fromPole(Vector3D.Unit.PLUS_X, precision);
455         final GreatCircle c = GreatCircles.fromPole(Vector3D.Unit.of(1, 1e-4, 1e-4), precision);
456         final GreatCircle d = GreatCircles.fromPole(Vector3D.Unit.MINUS_X, precision);
457         final GreatCircle e = GreatCircles.fromPole(Vector3D.Unit.of(-1, 1e-4, 1e-4), precision);
458 
459         // act/assert
460         Assert.assertNull(a.intersection(b));
461         Assert.assertNull(a.intersection(c));
462         Assert.assertNull(a.intersection(d));
463         Assert.assertNull(a.intersection(e));
464     }
465 
466     @Test
467     public void testIntersection() {
468         // arrange
469         final GreatCircle a = GreatCircles.fromPole(Vector3D.Unit.PLUS_X, TEST_PRECISION);
470         final GreatCircle b = GreatCircles.fromPole(Vector3D.Unit.PLUS_Y, TEST_PRECISION);
471         final GreatCircle c = GreatCircles.fromPole(Vector3D.Unit.PLUS_Z, TEST_PRECISION);
472 
473         // act/assert
474         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.PLUS_Z,
475                 a.intersection(b).getVector(), TEST_EPS);
476         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.MINUS_Z,
477                 b.intersection(a).getVector(), TEST_EPS);
478 
479         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.PLUS_X,
480                 b.intersection(c).getVector(), TEST_EPS);
481         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.MINUS_X,
482                 c.intersection(b).getVector(), TEST_EPS);
483     }
484 
485     @Test
486     public void testAngle_withoutReferencePoint() {
487      // arrange
488         final GreatCircle a = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION);
489         final GreatCircle b = GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_I, TEST_PRECISION);
490         final GreatCircle c = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_K, TEST_PRECISION);
491         final GreatCircle d = GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_K, TEST_PRECISION);
492         final GreatCircle e = GreatCircles.fromPoleAndU(
493                 Vector3D.Unit.of(1, 0, 1),
494                 Vector3D.Unit.PLUS_Y,
495                 TEST_PRECISION);
496 
497         final GreatCircle f = GreatCircles.fromPoleAndU(
498                 Vector3D.Unit.of(1, 0, -1),
499                 Vector3D.Unit.PLUS_Y,
500                 TEST_PRECISION);
501 
502         // act/assert
503         Assert.assertEquals(0, a.angle(a), TEST_EPS);
504         Assert.assertEquals(PlaneAngleRadians.PI, a.angle(b), TEST_EPS);
505 
506         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, a.angle(c), TEST_EPS);
507         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, c.angle(a), TEST_EPS);
508 
509         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, a.angle(d), TEST_EPS);
510         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, d.angle(a), TEST_EPS);
511 
512         Assert.assertEquals(0.25 * PlaneAngleRadians.PI, a.angle(e), TEST_EPS);
513         Assert.assertEquals(0.25 * PlaneAngleRadians.PI, e.angle(a), TEST_EPS);
514 
515         Assert.assertEquals(0.75 * PlaneAngleRadians.PI, a.angle(f), TEST_EPS);
516         Assert.assertEquals(0.75 * PlaneAngleRadians.PI, f.angle(a), TEST_EPS);
517     }
518 
519     @Test
520     public void testAngle_withReferencePoint() {
521         // arrange
522         final GreatCircle a = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION);
523         final GreatCircle b = GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_I, TEST_PRECISION);
524         final GreatCircle c = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_K, TEST_PRECISION);
525         final GreatCircle d = GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_K, TEST_PRECISION);
526         final GreatCircle e = GreatCircles.fromPoleAndU(
527                 Vector3D.Unit.of(1, 0, 1),
528                 Vector3D.Unit.PLUS_Y,
529                 TEST_PRECISION);
530 
531         final GreatCircle f = GreatCircles.fromPoleAndU(
532                 Vector3D.Unit.of(1, 0, -1),
533                 Vector3D.Unit.PLUS_Y,
534                 TEST_PRECISION);
535 
536         // act/assert
537         Assert.assertEquals(0, a.angle(a, Point2S.PLUS_J), TEST_EPS);
538         Assert.assertEquals(0, a.angle(a, Point2S.MINUS_J), TEST_EPS);
539 
540         Assert.assertEquals(-PlaneAngleRadians.PI, a.angle(b, Point2S.PLUS_J), TEST_EPS);
541         Assert.assertEquals(-PlaneAngleRadians.PI, a.angle(b, Point2S.MINUS_J), TEST_EPS);
542 
543         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, a.angle(c, Point2S.PLUS_I), TEST_EPS);
544         Assert.assertEquals(-PlaneAngleRadians.PI_OVER_TWO, a.angle(c, Point2S.MINUS_I), TEST_EPS);
545 
546         Assert.assertEquals(-PlaneAngleRadians.PI_OVER_TWO, c.angle(a, Point2S.PLUS_I), TEST_EPS);
547         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, c.angle(a, Point2S.MINUS_I), TEST_EPS);
548 
549         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, a.angle(d, Point2S.PLUS_J), TEST_EPS);
550         Assert.assertEquals(-PlaneAngleRadians.PI_OVER_TWO, a.angle(d, Point2S.MINUS_J), TEST_EPS);
551 
552         Assert.assertEquals(-PlaneAngleRadians.PI_OVER_TWO, d.angle(a, Point2S.PLUS_J), TEST_EPS);
553         Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, d.angle(a, Point2S.MINUS_J), TEST_EPS);
554 
555         Assert.assertEquals(0.25 * PlaneAngleRadians.PI, a.angle(e, Point2S.PLUS_J), TEST_EPS);
556         Assert.assertEquals(-0.25 * PlaneAngleRadians.PI, a.angle(e, Point2S.MINUS_J), TEST_EPS);
557 
558         Assert.assertEquals(-0.25 * PlaneAngleRadians.PI, e.angle(a, Point2S.PLUS_J), TEST_EPS);
559         Assert.assertEquals(0.25 * PlaneAngleRadians.PI, e.angle(a, Point2S.MINUS_J), TEST_EPS);
560 
561         Assert.assertEquals(0.75 * PlaneAngleRadians.PI, a.angle(f, Point2S.PLUS_J), TEST_EPS);
562         Assert.assertEquals(-0.75 * PlaneAngleRadians.PI, a.angle(f, Point2S.MINUS_J), TEST_EPS);
563 
564         Assert.assertEquals(-0.75 * PlaneAngleRadians.PI, f.angle(a, Point2S.PLUS_J), TEST_EPS);
565         Assert.assertEquals(0.75 * PlaneAngleRadians.PI, f.angle(a, Point2S.MINUS_J), TEST_EPS);
566     }
567 
568     @Test
569     public void testAngle_withReferencePoint_pointEquidistanceFromIntersections() {
570         // arrange
571         final GreatCircle a = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION);
572         final GreatCircle b = GreatCircles.fromPoleAndU(
573                 Vector3D.Unit.of(1, 0, 1),
574                 Vector3D.Unit.PLUS_Y,
575                 TEST_PRECISION);
576 
577         // act/assert
578         Assert.assertEquals(-0.25 * PlaneAngleRadians.PI, a.angle(b, Point2S.PLUS_I), TEST_EPS);
579         Assert.assertEquals(-0.25 * PlaneAngleRadians.PI, a.angle(b, Point2S.MINUS_I), TEST_EPS);
580     }
581 
582     @Test
583     public void testToSubspace() {
584         // arrange
585         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Y, Vector3D.Unit.MINUS_Z, TEST_PRECISION);
586 
587         // act/assert
588         SphericalTestUtils.assertPointsEqual(Point1S.ZERO,
589                 circle.toSubspace(Point2S.from(Vector3D.Unit.MINUS_Z)), TEST_EPS);
590 
591         SphericalTestUtils.assertPointsEqual(Point1S.of(0.25 * PlaneAngleRadians.PI),
592                 circle.toSubspace(Point2S.from(Vector3D.of(-1, -1, -1))), TEST_EPS);
593         SphericalTestUtils.assertPointsEqual(Point1S.of(0.75 * PlaneAngleRadians.PI),
594                 circle.toSubspace(Point2S.from(Vector3D.of(-1, 1, 1))), TEST_EPS);
595         SphericalTestUtils.assertPointsEqual(Point1S.of(1.25 * PlaneAngleRadians.PI),
596                 circle.toSubspace(Point2S.from(Vector3D.of(1, -1, 1))), TEST_EPS);
597         SphericalTestUtils.assertPointsEqual(Point1S.of(1.75 * PlaneAngleRadians.PI),
598                 circle.toSubspace(Point2S.from(Vector3D.of(1, 1, -1))), TEST_EPS);
599 
600         SphericalTestUtils.assertPointsEqual(Point1S.ZERO,
601                 circle.toSubspace(Point2S.from(Vector3D.Unit.PLUS_Y)), TEST_EPS);
602         SphericalTestUtils.assertPointsEqual(Point1S.ZERO,
603                 circle.toSubspace(Point2S.from(Vector3D.Unit.MINUS_Y)), TEST_EPS);
604     }
605 
606     @Test
607     public void testToSpace() {
608         // arrange
609         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Y, Vector3D.Unit.MINUS_Z, TEST_PRECISION);
610 
611         // act/assert
612         SphericalTestUtils.assertPointsEqual(Point2S.from(Vector3D.Unit.MINUS_Z),
613                 circle.toSpace(Point1S.ZERO), TEST_EPS);
614 
615         SphericalTestUtils.assertPointsEqual(Point2S.from(Vector3D.of(-1, 0, -1)),
616                 circle.toSpace(Point1S.of(0.25 * PlaneAngleRadians.PI)), TEST_EPS);
617         SphericalTestUtils.assertPointsEqual(Point2S.from(Vector3D.of(-1, 0, 1)),
618                 circle.toSpace(Point1S.of(0.75 * PlaneAngleRadians.PI)), TEST_EPS);
619         SphericalTestUtils.assertPointsEqual(Point2S.from(Vector3D.of(1, 0, 1)),
620                 circle.toSpace(Point1S.of(1.25 * PlaneAngleRadians.PI)), TEST_EPS);
621         SphericalTestUtils.assertPointsEqual(Point2S.from(Vector3D.of(1, 0, -1)),
622                 circle.toSpace(Point1S.of(1.75 * PlaneAngleRadians.PI)), TEST_EPS);
623     }
624 
625     @Test
626     public void testEq() {
627         // arrange
628         final double eps = 1e-3;
629         final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(eps);
630 
631         final GreatCircle a = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, precision);
632 
633         final GreatCircle b = GreatCircles.fromPoleAndU(Vector3D.Unit.MINUS_Z, Vector3D.Unit.PLUS_X, precision);
634         final GreatCircle c = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.MINUS_X, precision);
635         final GreatCircle d = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
636 
637         final GreatCircle e = GreatCircles.fromPoleAndU(Vector3D.of(1e-6, 0, 1), Vector3D.Unit.PLUS_X, precision);
638         final GreatCircle f = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.of(1, 1e-6, 0), precision);
639         final GreatCircle g = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X,
640                 new EpsilonDoublePrecisionContext(eps));
641 
642         // act/assert
643         Assert.assertTrue(a.eq(a, precision));
644 
645         Assert.assertFalse(a.eq(b, precision));
646         Assert.assertFalse(a.eq(c, precision));
647 
648         Assert.assertTrue(a.eq(d, precision));
649         Assert.assertTrue(a.eq(e, precision));
650         Assert.assertTrue(e.eq(a, precision));
651 
652         Assert.assertTrue(a.eq(f, precision));
653         Assert.assertTrue(f.eq(a, precision));
654 
655         Assert.assertTrue(g.eq(e, precision));
656         Assert.assertTrue(e.eq(g, precision));
657     }
658 
659     @Test
660     public void testHashCode() {
661         // arrange
662         final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-3);
663 
664         final GreatCircle a = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
665 
666         final GreatCircle b = GreatCircles.fromPoleAndU(Vector3D.of(0, 1, 1), Vector3D.Unit.PLUS_X, TEST_PRECISION);
667         final GreatCircle c = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.MINUS_X, TEST_PRECISION);
668         final GreatCircle d = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, precision);
669 
670         final GreatCircle e = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
671 
672         // act
673         final int hash = a.hashCode();
674 
675         // act/assert
676         Assert.assertEquals(hash, a.hashCode());
677 
678         Assert.assertNotEquals(hash, b.hashCode());
679         Assert.assertNotEquals(hash, c.hashCode());
680         Assert.assertNotEquals(hash, d.hashCode());
681 
682         Assert.assertEquals(hash, e.hashCode());
683     }
684 
685     @Test
686     public void testEquals() {
687         // arrange
688         final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-3);
689 
690         final GreatCircle a = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
691 
692         final GreatCircle b = GreatCircles.fromPoleAndU(Vector3D.Unit.MINUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
693         final GreatCircle c = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.MINUS_X, TEST_PRECISION);
694         final GreatCircle d = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, precision);
695 
696         final GreatCircle e = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
697 
698         // act/assert
699         Assert.assertEquals(a, a);
700 
701         Assert.assertFalse(a.equals(null));
702         Assert.assertFalse(a.equals(new Object()));
703 
704         Assert.assertNotEquals(a, b);
705         Assert.assertNotEquals(a, c);
706         Assert.assertNotEquals(a, d);
707 
708         Assert.assertEquals(a, e);
709         Assert.assertEquals(e, a);
710     }
711 
712     @Test
713     public void testToString() {
714         // arrange
715         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
716 
717         // act
718         final String str = circle.toString();
719 
720         // assert
721         GeometryTestUtils.assertContains("GreatCircle[", str);
722         GeometryTestUtils.assertContains("pole= (0.0, 0.0, 1.0)", str);
723         GeometryTestUtils.assertContains("u= (1.0, 0.0, 0.0)", str);
724         GeometryTestUtils.assertContains("v= (0.0, 1.0, 0.0)", str);
725     }
726 
727     private static void checkGreatCircle(final GreatCircle circle, final Vector3D pole, final Vector3D u) {
728         SphericalTestUtils.assertVectorsEqual(pole, circle.getPole(), TEST_EPS);
729         SphericalTestUtils.assertVectorsEqual(pole, circle.getW(), TEST_EPS);
730         SphericalTestUtils.assertVectorsEqual(u, circle.getU(), TEST_EPS);
731         SphericalTestUtils.assertVectorsEqual(pole.cross(u), circle.getV(), TEST_EPS);
732 
733         final Point2S plusPolePt = Point2S.from(circle.getPole());
734         final Point2S minusPolePt = Point2S.from(circle.getPole().negate());
735         final Point2S origin = Point2S.from(circle.getU());
736 
737         SphericalTestUtils.assertPointsEqual(plusPolePt, circle.getPolePoint(), TEST_EPS);
738 
739         Assert.assertFalse(circle.contains(plusPolePt));
740         Assert.assertFalse(circle.contains(minusPolePt));
741         Assert.assertTrue(circle.contains(origin));
742 
743         Assert.assertEquals(HyperplaneLocation.MINUS, circle.classify(plusPolePt));
744         Assert.assertEquals(HyperplaneLocation.PLUS, circle.classify(minusPolePt));
745         Assert.assertEquals(HyperplaneLocation.ON, circle.classify(origin));
746     }
747 
748     private static void checkArc(final GreatArc arc, final Point2S start, final Point2S end) {
749         SphericalTestUtils.assertPointsEq(start, arc.getStartPoint(), TEST_EPS);
750         SphericalTestUtils.assertPointsEq(end, arc.getEndPoint(), TEST_EPS);
751     }
752 }