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