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