GreatCircles.java

  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. import org.apache.commons.geometry.euclidean.threed.Vector3D;
  19. import org.apache.commons.geometry.spherical.oned.AngularInterval;
  20. import org.apache.commons.geometry.spherical.oned.Point1S;
  21. import org.apache.commons.numbers.core.Precision;

  22. /** Class containing factory methods for constructing {@link GreatCircle} and {@link GreatCircleSubset} instances.
  23.  */
  24. public final class GreatCircles {

  25.     /** Utility class; no instantiation. */
  26.     private GreatCircles() {
  27.     }

  28.     /** Create a great circle instance from its pole vector. An arbitrary u-axis is chosen.
  29.      * @param pole pole vector for the great circle
  30.      * @param precision precision context used to compare floating point values
  31.      * @return a great circle defined by the given pole vector
  32.      */
  33.     public static GreatCircle fromPole(final Vector3D pole, final Precision.DoubleEquivalence precision) {
  34.         final Vector3D.Unit u = pole.orthogonal();
  35.         final Vector3D.Unit v = pole.cross(u).normalize();
  36.         return new GreatCircle(pole.normalize(), u, v, precision);
  37.     }

  38.     /** Create a great circle instance from its pole vector and a vector representing the u-axis
  39.      * in the equator plane. The u-axis vector defines the {@code 0pi} location for the embedded
  40.      * subspace.
  41.      * @param pole pole vector for the great circle
  42.      * @param u u-axis direction for the equator plane
  43.      * @param precision precision context used to compare floating point values
  44.      * @return a great circle defined by the given pole vector and u-axis direction
  45.      */
  46.     public static GreatCircle fromPoleAndU(final Vector3D pole, final Vector3D u,
  47.             final Precision.DoubleEquivalence precision) {

  48.         final Vector3D.Unit unitPole = pole.normalize();
  49.         final Vector3D.Unit unitX = pole.orthogonal(u);
  50.         final Vector3D.Unit unitY = pole.cross(u).normalize();

  51.         return new GreatCircle(unitPole, unitX, unitY, precision);
  52.     }

  53.     /** Create a great circle instance from two points on the circle. The u-axis of the
  54.      * instance points to the location of the first point. The orientation of the circle
  55.      * is along the shortest path between the two points.
  56.      * @param a first point on the great circle
  57.      * @param b second point on the great circle
  58.      * @param precision precision context used to compare floating point values
  59.      * @return great circle instance containing the given points
  60.      * @throws IllegalArgumentException if either of the given points is NaN or infinite, or if the given points are
  61.      *      equal or antipodal as evaluated by the given precision context
  62.      */
  63.     public static GreatCircle fromPoints(final Point2S a, final Point2S b,
  64.             final Precision.DoubleEquivalence precision) {

  65.         if (!a.isFinite() || !b.isFinite()) {
  66.             throw new IllegalArgumentException("Invalid points for great circle: " + a + ", " + b);
  67.         }

  68.         String err = null;

  69.         final double dist = a.distance(b);
  70.         if (precision.eqZero(dist)) {
  71.             err = "equal";
  72.         } else if (precision.eq(dist, Math.PI)) {
  73.             err = "antipodal";
  74.         }

  75.         if (err != null) {
  76.             throw new IllegalArgumentException("Cannot create great circle from points " + a + " and " + b +
  77.                     ": points are " + err);
  78.         }

  79.         final Vector3D.Unit u = a.getVector().normalize();
  80.         final Vector3D.Unit pole = u.cross(b.getVector()).normalize();
  81.         final Vector3D.Unit v = pole.cross(u).normalize();

  82.         return new GreatCircle(pole, u, v, precision);
  83.     }

  84.     /** Construct an arc along the shortest path between the given points. The underlying
  85.      * great circle is oriented in the direction from {@code start} to {@code end}.
  86.      * @param start start point for the interval
  87.      * @param end end point point for the interval
  88.      * @param precision precision context used to compare floating point numbers
  89.      * @return an arc representing the shortest path between the given points
  90.      * @throws IllegalArgumentException if either of the given points is NaN or infinite, or if the given
  91.      *      points are equal or antipodal as evaluated by the given precision context
  92.      * @see GreatCircles#fromPoints(Point2S, Point2S, org.apache.commons.numbers.core.Precision.DoubleEquivalence)
  93.      */
  94.     public static GreatArc arcFromPoints(final Point2S start, final Point2S end,
  95.             final Precision.DoubleEquivalence precision) {
  96.         final GreatCircle circle = GreatCircles.fromPoints(start, end, precision);

  97.         final Point1S subspaceStart = circle.toSubspace(start);
  98.         final Point1S subspaceEnd = circle.toSubspace(end);
  99.         final AngularInterval.Convex interval = AngularInterval.Convex.of(subspaceStart, subspaceEnd, precision);

  100.         return arcFromInterval(circle, interval);
  101.     }

  102.     /** Construct an arc from a great circle and an angular interval.
  103.      * @param circle circle defining the arc
  104.      * @param interval interval representing the portion of the circle contained
  105.      *      in the arc
  106.      * @return an arc created from the given great circle and interval
  107.      */
  108.     public static GreatArc arcFromInterval(final GreatCircle circle, final AngularInterval.Convex interval) {
  109.         return new GreatArc(circle, interval);
  110.     }

  111.     /** Validate that the actual great circle is equivalent to the expected great circle,
  112.      * throwing an exception if not.
  113.      * @param expected the expected great circle
  114.      * @param actual the actual great circle
  115.      * @throws IllegalArgumentException if the actual great circle is not equivalent to the
  116.      *      expected great circle
  117.      */
  118.     static void validateGreatCirclesEquivalent(final GreatCircle expected, final GreatCircle actual) {
  119.         if (!expected.eq(actual, expected.getPrecision())) {
  120.             throw new IllegalArgumentException("Arguments do not represent the same great circle. Expected " +
  121.                     expected + " but was " + actual + ".");
  122.         }
  123.     }
  124. }