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