GeodeticPoint.java

  1. /* Copyright 2002-2025 CS GROUP
  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.  * CS 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 java.io.Serializable;
  19. import java.text.NumberFormat;

  20. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  21. import org.hipparchus.util.CompositeFormat;
  22. import org.hipparchus.util.FastMath;
  23. import org.hipparchus.util.MathUtils;
  24. import org.hipparchus.util.SinCos;

  25. /** Point location relative to a 2D body surface.
  26.  * <p>Instance of this class are guaranteed to be immutable.</p>
  27.  * @see BodyShape
  28.  * @see FieldGeodeticPoint
  29.  * @author Luc Maisonobe
  30.  */
  31. public class GeodeticPoint implements Serializable {

  32.     /** North pole.
  33.      * @since 10.0
  34.      */
  35.     public static final GeodeticPoint NORTH_POLE = new GeodeticPoint(+0.5 * FastMath.PI, 0.0, 0.0);

  36.     /** South pole.
  37.      * @since 10.0
  38.      */
  39.     public static final GeodeticPoint SOUTH_POLE = new GeodeticPoint(-0.5 * FastMath.PI, 0.0, 0.0);

  40.     /** Serializable UID. */
  41.     private static final long serialVersionUID = 7862466825590075399L;

  42.     /** Latitude of the point (rad). */
  43.     private final double latitude;

  44.     /** Longitude of the point (rad). */
  45.     private final double longitude;

  46.     /** Altitude of the point (m). */
  47.     private final double altitude;

  48.     /** Zenith direction. */
  49.     private transient Vector3D zenith;

  50.     /** Nadir direction. */
  51.     private transient Vector3D nadir;

  52.     /** North direction. */
  53.     private transient Vector3D north;

  54.     /** South direction. */
  55.     private transient Vector3D south;

  56.     /** East direction. */
  57.     private transient Vector3D east;

  58.     /** West direction. */
  59.     private transient Vector3D west;

  60.     /**
  61.      * Build a new instance. The angular coordinates will be normalized so that
  62.      * the latitude is between ±π/2 and the longitude is between ±π.
  63.      *
  64.      * @param latitude latitude of the point (rad)
  65.      * @param longitude longitude of the point (rad)
  66.      * @param altitude altitude of the point (m)
  67.      * @see SexagesimalAngle
  68.      */
  69.     public GeodeticPoint(final double latitude, final double longitude,
  70.                          final double altitude) {
  71.         double lat = MathUtils.normalizeAngle(latitude, FastMath.PI / 2);
  72.         double lon = MathUtils.normalizeAngle(longitude, 0);
  73.         if (lat > FastMath.PI / 2.0) {
  74.             // latitude is beyond the pole -> add 180 to longitude
  75.             lat = FastMath.PI - lat;
  76.             lon = MathUtils.normalizeAngle(longitude + FastMath.PI, 0);
  77.         }
  78.         this.latitude  = lat;
  79.         this.longitude = lon;
  80.         this.altitude  = altitude;
  81.     }

  82.     /** Get the latitude.
  83.      * @return latitude, an angular value in the range [-π/2, π/2]
  84.      */
  85.     public double getLatitude() {
  86.         return latitude;
  87.     }

  88.     /** Get the longitude.
  89.      * @return longitude, an angular value in the range [-π, π]
  90.      */
  91.     public double getLongitude() {
  92.         return longitude;
  93.     }

  94.     /** Get the altitude.
  95.      * @return altitude
  96.      */
  97.     public double getAltitude() {
  98.         return altitude;
  99.     }

  100.     /** Get the direction above the point, expressed in parent shape frame.
  101.      * <p>The zenith direction is defined as the normal to local horizontal plane.</p>
  102.      * @return unit vector in the zenith direction
  103.      * @see #getNadir()
  104.      */
  105.     public Vector3D getZenith() {
  106.         if (zenith == null) {
  107.             final SinCos scLat = FastMath.sinCos(latitude);
  108.             final SinCos scLon = FastMath.sinCos(longitude);
  109.             zenith = new Vector3D(scLon.cos() * scLat.cos(), scLon.sin() * scLat.cos(), scLat.sin());
  110.         }
  111.         return zenith;
  112.     }

  113.     /** Get the direction below the point, expressed in parent shape frame.
  114.      * <p>The nadir direction is the opposite of zenith direction.</p>
  115.      * @return unit vector in the nadir direction
  116.      * @see #getZenith()
  117.      */
  118.     public Vector3D getNadir() {
  119.         if (nadir == null) {
  120.             nadir = getZenith().negate();
  121.         }
  122.         return nadir;
  123.     }

  124.     /** Get the direction to the north of point, expressed in parent shape frame.
  125.      * <p>The north direction is defined in the horizontal plane
  126.      * (normal to zenith direction) and following the local meridian.</p>
  127.      * @return unit vector in the north direction
  128.      * @see #getSouth()
  129.      */
  130.     public Vector3D getNorth() {
  131.         if (north == null) {
  132.             final SinCos scLat = FastMath.sinCos(latitude);
  133.             final SinCos scLon = FastMath.sinCos(longitude);
  134.             north = new Vector3D(-scLon.cos() * scLat.sin(), -scLon.sin() * scLat.sin(), scLat.cos());
  135.         }
  136.         return north;
  137.     }

  138.     /** Get the direction to the south of point, expressed in parent shape frame.
  139.      * <p>The south direction is the opposite of north direction.</p>
  140.      * @return unit vector in the south direction
  141.      * @see #getNorth()
  142.      */
  143.     public Vector3D getSouth() {
  144.         if (south == null) {
  145.             south = getNorth().negate();
  146.         }
  147.         return south;
  148.     }

  149.     /** Get the direction to the east of point, expressed in parent shape frame.
  150.      * <p>The east direction is defined in the horizontal plane
  151.      * in order to complete direct triangle (east, north, zenith).</p>
  152.      * @return unit vector in the east direction
  153.      * @see #getWest()
  154.      */
  155.     public Vector3D getEast() {
  156.         if (east == null) {
  157.             final SinCos scLon = FastMath.sinCos(longitude);
  158.             east = new Vector3D(-scLon.sin(), scLon.cos(), 0);
  159.         }
  160.         return east;
  161.     }

  162.     /** Get the direction to the west of point, expressed in parent shape frame.
  163.      * <p>The west direction is the opposite of east direction.</p>
  164.      * @return unit vector in the west direction
  165.      * @see #getEast()
  166.      */
  167.     public Vector3D getWest() {
  168.         if (west == null) {
  169.             west = getEast().negate();
  170.         }
  171.         return west;
  172.     }

  173.     @Override
  174.     public boolean equals(final Object object) {
  175.         if (object instanceof GeodeticPoint) {
  176.             final GeodeticPoint other = (GeodeticPoint) object;
  177.             return this.getLatitude() == other.getLatitude() &&
  178.                    this.getLongitude() == other.getLongitude() &&
  179.                    this.getAltitude() == other.getAltitude();
  180.         }
  181.         return false;
  182.     }

  183.     @Override
  184.     public int hashCode() {
  185.         return Double.valueOf(this.getLatitude()).hashCode() ^
  186.                Double.valueOf(this.getLongitude()).hashCode() ^
  187.                Double.valueOf(this.getAltitude()).hashCode();
  188.     }

  189.     @Override
  190.     public String toString() {
  191.         final NumberFormat format = CompositeFormat.getDefaultNumberFormat();
  192.         return "{lat: " +
  193.                format.format(FastMath.toDegrees(this.getLatitude())) +
  194.                " deg, lon: " +
  195.                format.format(FastMath.toDegrees(this.getLongitude())) +
  196.                " deg, alt: " +
  197.                format.format(this.getAltitude()) +
  198.                "}";
  199.     }
  200. }