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
19 import java.io.Serializable;
20 import java.text.NumberFormat;
21
22 import org.hipparchus.geometry.euclidean.threed.Vector3D;
23 import org.hipparchus.util.CompositeFormat;
24 import org.hipparchus.util.FastMath;
25 import org.hipparchus.util.MathUtils;
26 import org.hipparchus.util.SinCos;
27
28 /** Point location relative to a 2D body surface.
29 * <p>Instance of this class are guaranteed to be immutable.</p>
30 * @see BodyShape
31 * @see FieldGeodeticPoint
32 * @author Luc Maisonobe
33 */
34 public class GeodeticPoint implements Serializable {
35
36 /** North pole.
37 * @since 10.0
38 */
39 public static final GeodeticPoint NORTH_POLE = new GeodeticPoint(+0.5 * FastMath.PI, 0.0, 0.0);
40
41 /** South pole.
42 * @since 10.0
43 */
44 public static final GeodeticPoint SOUTH_POLE = new GeodeticPoint(-0.5 * FastMath.PI, 0.0, 0.0);
45
46 /** Serializable UID. */
47 private static final long serialVersionUID = 7862466825590075399L;
48
49 /** Latitude of the point (rad). */
50 private final double latitude;
51
52 /** Longitude of the point (rad). */
53 private final double longitude;
54
55 /** Altitude of the point (m). */
56 private final double altitude;
57
58 /** Zenith direction. */
59 private transient Vector3D zenith;
60
61 /** Nadir direction. */
62 private transient Vector3D nadir;
63
64 /** North direction. */
65 private transient Vector3D north;
66
67 /** South direction. */
68 private transient Vector3D south;
69
70 /** East direction. */
71 private transient Vector3D east;
72
73 /** West direction. */
74 private transient Vector3D west;
75
76 /**
77 * Build a new instance. The angular coordinates will be normalized so that
78 * the latitude is between ±π/2 and the longitude is between ±π.
79 *
80 * @param latitude latitude of the point (rad)
81 * @param longitude longitude of the point (rad)
82 * @param altitude altitude of the point (m)
83 * @see SexagesimalAngle
84 */
85 public GeodeticPoint(final double latitude, final double longitude,
86 final double altitude) {
87 double lat = MathUtils.normalizeAngle(latitude, FastMath.PI / 2);
88 double lon = MathUtils.normalizeAngle(longitude, 0);
89 if (lat > FastMath.PI / 2.0) {
90 // latitude is beyond the pole -> add 180 to longitude
91 lat = FastMath.PI - lat;
92 lon = MathUtils.normalizeAngle(longitude + FastMath.PI, 0);
93 }
94 this.latitude = lat;
95 this.longitude = lon;
96 this.altitude = altitude;
97 }
98
99 /** Get the latitude.
100 * @return latitude, an angular value in the range [-π/2, π/2]
101 */
102 public double getLatitude() {
103 return latitude;
104 }
105
106 /** Get the longitude.
107 * @return longitude, an angular value in the range [-π, π]
108 */
109 public double getLongitude() {
110 return longitude;
111 }
112
113 /** Get the altitude.
114 * @return altitude
115 */
116 public double getAltitude() {
117 return altitude;
118 }
119
120 /** Get the direction above the point, expressed in parent shape frame.
121 * <p>The zenith direction is defined as the normal to local horizontal plane.</p>
122 * @return unit vector in the zenith direction
123 * @see #getNadir()
124 */
125 public Vector3D getZenith() {
126 if (zenith == null) {
127 final SinCos scLat = FastMath.sinCos(latitude);
128 final SinCos scLon = FastMath.sinCos(longitude);
129 zenith = new Vector3D(scLon.cos() * scLat.cos(), scLon.sin() * scLat.cos(), scLat.sin());
130 }
131 return zenith;
132 }
133
134 /** Get the direction below the point, expressed in parent shape frame.
135 * <p>The nadir direction is the opposite of zenith direction.</p>
136 * @return unit vector in the nadir direction
137 * @see #getZenith()
138 */
139 public Vector3D getNadir() {
140 if (nadir == null) {
141 nadir = getZenith().negate();
142 }
143 return nadir;
144 }
145
146 /** Get the direction to the north of point, expressed in parent shape frame.
147 * <p>The north direction is defined in the horizontal plane
148 * (normal to zenith direction) and following the local meridian.</p>
149 * @return unit vector in the north direction
150 * @see #getSouth()
151 */
152 public Vector3D getNorth() {
153 if (north == null) {
154 final SinCos scLat = FastMath.sinCos(latitude);
155 final SinCos scLon = FastMath.sinCos(longitude);
156 north = new Vector3D(-scLon.cos() * scLat.sin(), -scLon.sin() * scLat.sin(), scLat.cos());
157 }
158 return north;
159 }
160
161 /** Get the direction to the south of point, expressed in parent shape frame.
162 * <p>The south direction is the opposite of north direction.</p>
163 * @return unit vector in the south direction
164 * @see #getNorth()
165 */
166 public Vector3D getSouth() {
167 if (south == null) {
168 south = getNorth().negate();
169 }
170 return south;
171 }
172
173 /** Get the direction to the east of point, expressed in parent shape frame.
174 * <p>The east direction is defined in the horizontal plane
175 * in order to complete direct triangle (east, north, zenith).</p>
176 * @return unit vector in the east direction
177 * @see #getWest()
178 */
179 public Vector3D getEast() {
180 if (east == null) {
181 final SinCos scLon = FastMath.sinCos(longitude);
182 east = new Vector3D(-scLon.sin(), scLon.cos(), 0);
183 }
184 return east;
185 }
186
187 /** Get the direction to the west of point, expressed in parent shape frame.
188 * <p>The west direction is the opposite of east direction.</p>
189 * @return unit vector in the west direction
190 * @see #getEast()
191 */
192 public Vector3D getWest() {
193 if (west == null) {
194 west = getEast().negate();
195 }
196 return west;
197 }
198
199 @Override
200 public boolean equals(final Object object) {
201 if (object instanceof GeodeticPoint) {
202 final GeodeticPoint other = (GeodeticPoint) object;
203 return this.getLatitude() == other.getLatitude() &&
204 this.getLongitude() == other.getLongitude() &&
205 this.getAltitude() == other.getAltitude();
206 }
207 return false;
208 }
209
210 @Override
211 public int hashCode() {
212 return Double.valueOf(this.getLatitude()).hashCode() ^
213 Double.valueOf(this.getLongitude()).hashCode() ^
214 Double.valueOf(this.getAltitude()).hashCode();
215 }
216
217 @Override
218 public String toString() {
219 final NumberFormat format = CompositeFormat.getDefaultNumberFormat();
220 return "{lat: " +
221 format.format(FastMath.toDegrees(this.getLatitude())) +
222 " deg, lon: " +
223 format.format(FastMath.toDegrees(this.getLongitude())) +
224 " deg, alt: " +
225 format.format(this.getAltitude()) +
226 "}";
227 }
228 }