GreatArc.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 java.util.Collections;
  19. import java.util.List;

  20. import org.apache.commons.geometry.core.Transform;
  21. import org.apache.commons.geometry.core.partitioning.Hyperplane;
  22. import org.apache.commons.geometry.core.partitioning.HyperplaneConvexSubset;
  23. import org.apache.commons.geometry.core.partitioning.Split;
  24. import org.apache.commons.geometry.core.partitioning.SplitLocation;
  25. import org.apache.commons.geometry.spherical.oned.AngularInterval;
  26. import org.apache.commons.geometry.spherical.oned.CutAngle;
  27. import org.apache.commons.geometry.spherical.oned.CutAngles;
  28. import org.apache.commons.geometry.spherical.oned.Transform1S;

  29. /** Class representing a single, <em>convex</em> angular interval in a {@link GreatCircle}. Convex
  30.  * angular intervals are those where the shortest path between all pairs of points in the
  31.  * interval are completely contained in the interval. In the case of paths that tie for the
  32.  * shortest length, it is sufficient that one of the paths is completely contained in the
  33.  * interval. In spherical 2D space, convex arcs either fill the entire great circle or have
  34.  * an angular size of less than or equal to {@code pi} radians.
  35.  *
  36.  * <p>Instances of this class are guaranteed to be immutable.</p>
  37.  * @see GreatCircles
  38.  */
  39. public final class GreatArc extends GreatCircleSubset implements HyperplaneConvexSubset<Point2S> {
  40.     /** The interval representing the region of the great circle contained in the arc.
  41.      */
  42.     private final AngularInterval.Convex interval;

  43.     /** Create a new instance from a great circle and the interval embedded in it.
  44.      * @param circle defining great circle instance
  45.      * @param interval convex angular interval embedded in the great circle
  46.      */
  47.     GreatArc(final GreatCircle circle, final AngularInterval.Convex interval) {
  48.         super(circle);

  49.         this.interval = interval;
  50.     }

  51.     /** Return the start point of the arc, or null if the arc represents the full space.
  52.      * @return the start point of the arc, or null if the arc represents the full space.
  53.      */
  54.     public Point2S getStartPoint() {
  55.         if (!interval.isFull()) {
  56.             return getCircle().toSpace(interval.getMinBoundary().getPoint());
  57.         }

  58.         return null;
  59.     }

  60.     /** Return the end point of the arc, or null if the arc represents the full space.
  61.      * @return the end point of the arc, or null if the arc represents the full space.
  62.      */
  63.     public Point2S getEndPoint() {
  64.         if (!interval.isFull()) {
  65.             return getCircle().toSpace(interval.getMaxBoundary().getPoint());
  66.         }

  67.         return null;
  68.     }

  69.     /** Return the midpoint of the arc, or null if the arc represents the full space.
  70.      * @return the midpoint of the arc, or null if the arc represents the full space.
  71.      */
  72.     public Point2S getMidPoint() {
  73.         if (!interval.isFull()) {
  74.             return getCircle().toSpace(interval.getMidPoint());
  75.         }

  76.         return null;
  77.     }

  78.     /** Get the angular interval for the arc.
  79.      * @return the angular interval for the arc
  80.      * @see #getSubspaceRegion()
  81.      */
  82.     public AngularInterval.Convex getInterval() {
  83.         return interval;
  84.     }

  85.     /** {@inheritDoc} */
  86.     @Override
  87.     public AngularInterval.Convex getSubspaceRegion() {
  88.         return getInterval();
  89.     }

  90.     /** {@inheritDoc} */
  91.     @Override
  92.     public List<GreatArc> toConvex() {
  93.         return Collections.singletonList(this);
  94.     }

  95.     /** {@inheritDoc} */
  96.     @Override
  97.     public Split<GreatArc> split(final Hyperplane<Point2S> splitter) {
  98.         final GreatCircle splitterCircle = (GreatCircle) splitter;
  99.         final GreatCircle thisCircle = getCircle();

  100.         final Point2S intersection = splitterCircle.intersection(thisCircle);

  101.         GreatArc minus = null;
  102.         GreatArc plus = null;

  103.         if (intersection != null) {
  104.             // use a negative-facing cut angle to account for the fact that the great circle
  105.             // poles point to the minus side of the circle
  106.             final CutAngle subSplitter = CutAngles.createNegativeFacing(
  107.                     thisCircle.toSubspace(intersection), splitterCircle.getPrecision());

  108.             final Split<AngularInterval.Convex> subSplit = interval.splitDiameter(subSplitter);
  109.             final SplitLocation subLoc = subSplit.getLocation();

  110.             if (subLoc == SplitLocation.MINUS) {
  111.                 minus = this;
  112.             } else if (subLoc == SplitLocation.PLUS) {
  113.                 plus = this;
  114.             } else if (subLoc == SplitLocation.BOTH) {
  115.                 minus = GreatCircles.arcFromInterval(thisCircle, subSplit.getMinus());
  116.                 plus = GreatCircles.arcFromInterval(thisCircle, subSplit.getPlus());
  117.             }
  118.         }

  119.         return new Split<>(minus, plus);
  120.     }

  121.     /** {@inheritDoc} */
  122.     @Override
  123.     public GreatArc transform(final Transform<Point2S> transform) {
  124.         return new GreatArc(getCircle().transform(transform), interval);
  125.     }

  126.     /** {@inheritDoc} */
  127.     @Override
  128.     public GreatArc reverse() {
  129.         return new GreatArc(
  130.                 getCircle().reverse(),
  131.                 interval.transform(Transform1S.createNegation()));
  132.     }

  133.     /** Return a string representation of this great arc.
  134.      *
  135.      * <p>In order to keep the string representation short but useful, the exact format of the return
  136.      * value depends on the properties of the arc. See below for examples.
  137.      *
  138.      * <ul>
  139.      *      <li>Full arc
  140.      *          <ul>
  141.      *              <li>{@code GreatArc[full= true, circle= GreatCircle[pole= (0.0, 0.0, 1.0), x= (1.0, 0.0, 0.0), y= (0.0, 1.0, 0.0)]}</li>
  142.      *          </ul>
  143.      *      </li>
  144.      *      <li>Non-full arc
  145.      *          <ul>
  146.      *              <li>{@code GreatArc[start= (1.0, 1.5707963267948966), end= (2.0, 1.5707963267948966)}</li>
  147.      *          </ul>
  148.      *      </li>
  149.      * </ul>
  150.      */
  151.     @Override
  152.     public String toString() {
  153.         final StringBuilder sb = new StringBuilder();
  154.         sb.append(this.getClass().getSimpleName()).append('[');

  155.         if (isFull()) {
  156.             sb.append("full= true, circle= ")
  157.                 .append(getCircle());
  158.         } else {
  159.             sb.append("start= ")
  160.                 .append(getStartPoint())
  161.                 .append(", end= ")
  162.                 .append(getEndPoint());
  163.         }

  164.         return sb.toString();
  165.     }
  166. }