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