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