LoxodromeArc.java

  1. /* Copyright 2022-2025 Joseph Reed
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * Joseph Reed 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.orekit.bodies;

  18. import org.hipparchus.util.FastMath;
  19. import org.hipparchus.util.MathUtils;

  20. /** Loxodrome defined by a start and ending point.
  21.  *
  22.  * @author Joe Reed
  23.  * @since 11.3
  24.  */
  25. public class LoxodromeArc extends Loxodrome {

  26.     /** Threshold for considering latitudes equal, in radians. */
  27.     private static final double LATITUDE_THRESHOLD = 1e-6;

  28.     /** Maximum number of iterations used when computing distance between points. */
  29.     private static final int MAX_ITER = 50;

  30.     /** Ending point of the arc. */
  31.     private final GeodeticPoint endPoint;

  32.     /** Delta longitude, cached, radians. */
  33.     private final double deltaLon;

  34.     /** Cached arc distance, meters. */
  35.     private double distance = -1;

  36.     /** Class constructor where the arc's altitude is the average of the initial and final points.
  37.      * @param point the starting point
  38.      * @param endPoint the ending point
  39.      * @param body the body on which the loxodrome is defined
  40.      */
  41.     public LoxodromeArc(final GeodeticPoint point, final GeodeticPoint endPoint, final OneAxisEllipsoid body) {
  42.         this(point, endPoint, body, (point.getAltitude() + endPoint.getAltitude()) / 2.);
  43.     }

  44.     /** Class constructor.
  45.      * @param point the starting point
  46.      * @param endPoint the ending point
  47.      * @param body the body on which the loxodrome is defined
  48.      * @param altitude the altitude above the reference body (meters)
  49.      */
  50.     public LoxodromeArc(final GeodeticPoint point, final GeodeticPoint endPoint, final OneAxisEllipsoid body,
  51.                         final double altitude) {
  52.         super(point, body.azimuthBetweenPoints(point, endPoint), body, altitude);
  53.         this.endPoint = endPoint;
  54.         this.deltaLon = MathUtils.normalizeAngle(endPoint.getLongitude(), point.getLongitude()) -
  55.                         point.getLongitude();
  56.     }

  57.     /** Get the final point of the arc.
  58.      *
  59.      * @return the ending point of the arc
  60.      */
  61.     public GeodeticPoint getFinalPoint() {
  62.         return this.endPoint;
  63.     }

  64.     /** Compute the distance of the arc along the surface of the ellipsoid.
  65.      *
  66.      * @return the distance (meters)
  67.      */
  68.     public double getDistance() {
  69.         if (distance >= 0) {
  70.             return distance;
  71.         }

  72.         // compute the e sin(lat)^2
  73.         final double ptLat  = getPoint().getLatitude();
  74.         final double sinLat = FastMath.sin(ptLat);
  75.         final double eccSinLatSq = getBody().getEccentricitySquared() * sinLat * sinLat;

  76.         // compute intermediate values
  77.         final double t1 = 1. - getBody().getEccentricitySquared();
  78.         final double t2 = 1. - eccSinLatSq;
  79.         final double t3 = FastMath.sqrt(t2);

  80.         final double semiMajorAxis = getBody().getEquatorialRadius() + getAltitude();

  81.         final double meridianCurve = (semiMajorAxis * t1) / (t2 * t3);

  82.         if (FastMath.abs(endPoint.getLatitude() - ptLat) < LATITUDE_THRESHOLD) {
  83.             distance = (semiMajorAxis / t3) * FastMath.abs(FastMath.cos(ptLat) * deltaLon);
  84.         }
  85.         else {
  86.             final double eccSq34 = 0.75 * getBody().getEccentricitySquared();
  87.             final double halfEccSq34 = eccSq34 / 2.;
  88.             final double t6 = 1. / (1. - eccSq34);
  89.             final double t7 = t1 * semiMajorAxis / meridianCurve;

  90.             final double t8 = ptLat + t6 *
  91.                 (t7 * (endPoint.getLatitude() - ptLat) + halfEccSq34 * FastMath.sin(ptLat * 2.));
  92.             final double t9 = halfEccSq34 * t6;

  93.             double guess = 0;
  94.             double lat = endPoint.getLatitude();
  95.             for (int i = 0; i < MAX_ITER; i++) {
  96.                 guess = lat;
  97.                 lat = t8 - t9 * FastMath.sin(2. * guess);

  98.                 if (FastMath.abs(lat - guess) < LATITUDE_THRESHOLD) {
  99.                     break;
  100.                 }
  101.             }

  102.             final double azimuth = FastMath.atan2(deltaLon,
  103.                 getBody().geodeticToIsometricLatitude(lat) - getBody().geodeticToIsometricLatitude(ptLat));
  104.             distance = meridianCurve * FastMath.abs((lat - ptLat) / FastMath.cos(azimuth));
  105.         }

  106.         return distance;
  107.     }

  108.     /** Calculate a point at a specific percentage along the arc.
  109.      *
  110.      * @param fraction the fraction along the arc to compute the point
  111.      * @return the point along the arc
  112.      */
  113.     public GeodeticPoint calculatePointAlongArc(final double fraction) {
  114.         if (fraction == 0.) {
  115.             return getPoint();
  116.         }
  117.         else if (fraction == 1.) {
  118.             return getFinalPoint();
  119.         }
  120.         else {
  121.             final double d = getDistance() * fraction;
  122.             return this.pointAtDistance(d);
  123.         }
  124.     }
  125. }