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 }