001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.geometry.spherical.twod;
018
019import org.apache.commons.geometry.euclidean.threed.Vector3D;
020import org.apache.commons.geometry.spherical.oned.AngularInterval;
021import org.apache.commons.geometry.spherical.oned.Point1S;
022import org.apache.commons.numbers.core.Precision;
023
024/** Class containing factory methods for constructing {@link GreatCircle} and {@link GreatCircleSubset} instances.
025 */
026public final class GreatCircles {
027
028    /** Utility class; no instantiation. */
029    private GreatCircles() {
030    }
031
032    /** Create a great circle instance from its pole vector. An arbitrary u-axis is chosen.
033     * @param pole pole vector for the great circle
034     * @param precision precision context used to compare floating point values
035     * @return a great circle defined by the given pole vector
036     */
037    public static GreatCircle fromPole(final Vector3D pole, final Precision.DoubleEquivalence precision) {
038        final Vector3D.Unit u = pole.orthogonal();
039        final Vector3D.Unit v = pole.cross(u).normalize();
040        return new GreatCircle(pole.normalize(), u, v, precision);
041    }
042
043    /** Create a great circle instance from its pole vector and a vector representing the u-axis
044     * in the equator plane. The u-axis vector defines the {@code 0pi} location for the embedded
045     * subspace.
046     * @param pole pole vector for the great circle
047     * @param u u-axis direction for the equator plane
048     * @param precision precision context used to compare floating point values
049     * @return a great circle defined by the given pole vector and u-axis direction
050     */
051    public static GreatCircle fromPoleAndU(final Vector3D pole, final Vector3D u,
052            final Precision.DoubleEquivalence precision) {
053
054        final Vector3D.Unit unitPole = pole.normalize();
055        final Vector3D.Unit unitX = pole.orthogonal(u);
056        final Vector3D.Unit unitY = pole.cross(u).normalize();
057
058        return new GreatCircle(unitPole, unitX, unitY, precision);
059    }
060
061    /** Create a great circle instance from two points on the circle. The u-axis of the
062     * instance points to the location of the first point. The orientation of the circle
063     * is along the shortest path between the two points.
064     * @param a first point on the great circle
065     * @param b second point on the great circle
066     * @param precision precision context used to compare floating point values
067     * @return great circle instance containing the given points
068     * @throws IllegalArgumentException if either of the given points is NaN or infinite, or if the given points are
069     *      equal or antipodal as evaluated by the given precision context
070     */
071    public static GreatCircle fromPoints(final Point2S a, final Point2S b,
072            final Precision.DoubleEquivalence precision) {
073
074        if (!a.isFinite() || !b.isFinite()) {
075            throw new IllegalArgumentException("Invalid points for great circle: " + a + ", " + b);
076        }
077
078        String err = null;
079
080        final double dist = a.distance(b);
081        if (precision.eqZero(dist)) {
082            err = "equal";
083        } else if (precision.eq(dist, Math.PI)) {
084            err = "antipodal";
085        }
086
087        if (err != null) {
088            throw new IllegalArgumentException("Cannot create great circle from points " + a + " and " + b +
089                    ": points are " + err);
090        }
091
092        final Vector3D.Unit u = a.getVector().normalize();
093        final Vector3D.Unit pole = u.cross(b.getVector()).normalize();
094        final Vector3D.Unit v = pole.cross(u).normalize();
095
096        return new GreatCircle(pole, u, v, precision);
097    }
098
099    /** 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}