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 org.apache.commons.geometry.euclidean.threed.Vector3D;
20  import org.apache.commons.geometry.spherical.oned.AngularInterval;
21  import org.apache.commons.geometry.spherical.oned.Point1S;
22  import org.apache.commons.numbers.core.Precision;
23  
24  /** Class containing factory methods for constructing {@link GreatCircle} and {@link GreatCircleSubset} instances.
25   */
26  public final class GreatCircles {
27  
28      /** Utility class; no instantiation. */
29      private GreatCircles() {
30      }
31  
32      /** Create a great circle instance from its pole vector. An arbitrary u-axis is chosen.
33       * @param pole pole vector for the great circle
34       * @param precision precision context used to compare floating point values
35       * @return a great circle defined by the given pole vector
36       */
37      public static GreatCircle fromPole(final Vector3D pole, final Precision.DoubleEquivalence precision) {
38          final Vector3D.Unit u = pole.orthogonal();
39          final Vector3D.Unit v = pole.cross(u).normalize();
40          return new GreatCircle(pole.normalize(), u, v, precision);
41      }
42  
43      /** Create a great circle instance from its pole vector and a vector representing the u-axis
44       * in the equator plane. The u-axis vector defines the {@code 0pi} location for the embedded
45       * subspace.
46       * @param pole pole vector for the great circle
47       * @param u u-axis direction for the equator plane
48       * @param precision precision context used to compare floating point values
49       * @return a great circle defined by the given pole vector and u-axis direction
50       */
51      public static GreatCircle fromPoleAndU(final Vector3D pole, final Vector3D u,
52              final Precision.DoubleEquivalence precision) {
53  
54          final Vector3D.Unit unitPole = pole.normalize();
55          final Vector3D.Unit unitX = pole.orthogonal(u);
56          final Vector3D.Unit unitY = pole.cross(u).normalize();
57  
58          return new GreatCircle(unitPole, unitX, unitY, precision);
59      }
60  
61      /** Create a great circle instance from two points on the circle. The u-axis of the
62       * instance points to the location of the first point. The orientation of the circle
63       * is along the shortest path between the two points.
64       * @param a first point on the great circle
65       * @param b second point on the great circle
66       * @param precision precision context used to compare floating point values
67       * @return great circle instance containing the given points
68       * @throws IllegalArgumentException if either of the given points is NaN or infinite, or if the given points are
69       *      equal or antipodal as evaluated by the given precision context
70       */
71      public static GreatCircle fromPoints(final Point2S a, final Point2S b,
72              final Precision.DoubleEquivalence precision) {
73  
74          if (!a.isFinite() || !b.isFinite()) {
75              throw new IllegalArgumentException("Invalid points for great circle: " + a + ", " + b);
76          }
77  
78          String err = null;
79  
80          final double dist = a.distance(b);
81          if (precision.eqZero(dist)) {
82              err = "equal";
83          } else if (precision.eq(dist, Math.PI)) {
84              err = "antipodal";
85          }
86  
87          if (err != null) {
88              throw new IllegalArgumentException("Cannot create great circle from points " + a + " and " + b +
89                      ": points are " + err);
90          }
91  
92          final Vector3D.Unit u = a.getVector().normalize();
93          final Vector3D.Unit pole = u.cross(b.getVector()).normalize();
94          final Vector3D.Unit v = pole.cross(u).normalize();
95  
96          return new GreatCircle(pole, u, v, precision);
97      }
98  
99      /** Construct an arc along the shortest path between the given points. The underlying
100      * great circle is oriented in the direction from {@code start} to {@code end}.
101      * @param start start point for the interval
102      * @param end end point point for the interval
103      * @param precision precision context used to compare floating point numbers
104      * @return an arc representing the shortest path between the given points
105      * @throws IllegalArgumentException if either of the given points is NaN or infinite, or if the given
106      *      points are equal or antipodal as evaluated by the given precision context
107      * @see GreatCircles#fromPoints(Point2S, Point2S, org.apache.commons.numbers.core.Precision.DoubleEquivalence)
108      */
109     public static GreatArc arcFromPoints(final Point2S start, final Point2S end,
110             final Precision.DoubleEquivalence precision) {
111         final GreatCircle circle = GreatCircles.fromPoints(start, end, precision);
112 
113         final Point1S subspaceStart = circle.toSubspace(start);
114         final Point1S subspaceEnd = circle.toSubspace(end);
115         final AngularInterval.Convex interval = AngularInterval.Convex.of(subspaceStart, subspaceEnd, precision);
116 
117         return arcFromInterval(circle, interval);
118     }
119 
120     /** Construct an arc from a great circle and an angular interval.
121      * @param circle circle defining the arc
122      * @param interval interval representing the portion of the circle contained
123      *      in the arc
124      * @return an arc created from the given great circle and interval
125      */
126     public static GreatArc arcFromInterval(final GreatCircle circle, final AngularInterval.Convex interval) {
127         return new GreatArc(circle, interval);
128     }
129 
130     /** Validate that the actual great circle is equivalent to the expected great circle,
131      * throwing an exception if not.
132      * @param expected the expected great circle
133      * @param actual the actual great circle
134      * @throws IllegalArgumentException if the actual great circle is not equivalent to the
135      *      expected great circle
136      */
137     static void validateGreatCirclesEquivalent(final GreatCircle expected, final GreatCircle actual) {
138         if (!expected.eq(actual, expected.getPrecision())) {
139             throw new IllegalArgumentException("Arguments do not represent the same great circle. Expected " +
140                     expected + " but was " + actual + ".");
141         }
142     }
143 }