View Javadoc
1   /*
2    * Copyright (C) 2021-2023 Dipl.-Inform. Kai Hofmann. All rights reserved!
3    */
4   package de.powerstat.validation.values;
5   
6   
7   import java.util.Objects;
8   
9   import de.powerstat.validation.interfaces.IValueObject;
10  
11  
12  /**
13   * World geodetic system 1984 position.
14   *
15   * Possibly DSGVO relevant.
16   *
17   * TODO precision
18   * TODO output formats
19   * TODO Get address for position if possible
20   */
21  public final class WGS84Position implements Comparable<WGS84Position>, IValueObject
22   {
23    /* *
24     * Cache for singletons.
25     */
26    // private static final Map<NTuple3<Double, Double, Double>, WGS84Position> CACHE = new WeakHashMap<>();
27  
28    /**
29     * Epsilon for double compare.
30     */
31    private static final double EPSILON = 0.000001D;
32  
33    /**
34     * Position separator.
35     */
36    private static final String SEPARATOR = " ";
37  
38    /**
39     * Positions latitude specifies the north–south position of a point on the Earth's surface.
40     *
41     * Ranges from 0° at the Equator to 90° (North or South) at the poles.
42     */
43    private final double latitude;
44  
45    /**
46     * Positions longitude specifies the east–west position of a point on the Earth's surface.
47     *
48     * The prime meridian, which passes near the Royal Observatory, Greenwich, England, is defined as 0° longitude by convention. Positive longitudes are east of the prime meridian, and negative ones are west.
49     */
50    private final double longitude;
51  
52    /**
53     * Positions altitude - height above sea level.
54     */
55    private final double altitude;
56  
57  
58    /**
59     * Constructor.
60     *
61     * @param latitude Positions latitude specifies the north–south position of a point on the Earth's surface. Ranges from 0° at the Equator to 90° (North or South) at the poles.
62     * @param longitude Positions longitude specifies the east–west position of a point on the Earth's surface. The prime meridian, which passes near the Royal Observatory, Greenwich, England, is defined as 0° longitude by convention. Positive longitudes are east of the prime meridian, and negative ones are west.
63     * @param altitude Positions altitude - height above sea level.
64     */
65    private WGS84Position(final double latitude, final double longitude, final double altitude)
66     {
67      super();
68      if ((latitude < -90.0) || (latitude > 90.0))
69       {
70        throw new IndexOutOfBoundsException("Latitude out of range"); //$NON-NLS-1$
71       }
72      if ((longitude < -180.0) || (longitude > 180.0))
73       {
74        throw new IndexOutOfBoundsException("Longitude out of range"); //$NON-NLS-1$
75       }
76      this.latitude = latitude;
77      this.longitude = longitude;
78      this.altitude = altitude;
79     }
80  
81  
82    /**
83     * WGS84Position factory.
84     *
85     * @param latitude Positions latitude specifies the north–south position of a point on the Earth's surface. Ranges from 0° at the Equator to 90° (North or South) at the poles.
86     * @param longitude Positions longitude specifies the east–west position of a point on the Earth's surface. The prime meridian, which passes near the Royal Observatory, Greenwich, England, is defined as 0° longitude by convention. Positive longitudes are east of the prime meridian, and negative ones are west.
87     * @param altitude Positions altitude - height above sea level.
88     * @return WGS84Position object
89     */
90    public static WGS84Position of(final double latitude, final double longitude, final double altitude)
91     {
92      /*
93      final NTuple3<Double, Double, Double> tuple = NTuple3.of(latitude, longitude, altitude);
94      synchronized (WGS84Position.class)
95       {
96        WGS84Position obj = WGS84Position.CACHE.get(tuple);
97        if (obj != null)
98         {
99          return obj;
100        }
101       obj = new WGS84Position(latitude, longitude, altitude);
102       WGS84Position.CACHE.put(tuple, obj);
103       return obj;
104      }
105     */
106     return new WGS84Position(latitude, longitude, altitude);
107    }
108 
109 
110   /**
111    * WGS84Position factory.
112    *
113    * @param value latitude longitude altitude separated by one space
114    * @return WGS84Position object
115    */
116   public static WGS84Position of(final String value)
117    {
118     final String[] values = value.split(SEPARATOR);
119     if (values.length != 3)
120      {
121       throw new IllegalArgumentException("value not of expected format");
122      }
123     final double latitude = Double.parseDouble(values[0]);
124     final double longitude = Double.parseDouble(values[1]);
125     final double altitude = Double.parseDouble(values[2]);
126     return of(latitude, longitude, altitude);
127    }
128 
129 
130   /**
131    * Get latitude.
132    *
133    * @return Latitude
134    */
135   public double getLatitude()
136    {
137     return this.latitude;
138    }
139 
140 
141   /**
142    * Get longitude.
143    *
144    * @return Longitude
145    */
146   public double getLongitude()
147    {
148     return this.longitude;
149    }
150 
151 
152   /**
153    * Get altitude.
154    *
155    * @return Altitude
156    */
157   public double getAltitude()
158    {
159     return this.altitude;
160    }
161 
162 
163   /**
164    * Returns the value of this WGS84Position as a String.
165    *
166    * @return The value represented by this object after conversion to type String.
167    */
168   @Override
169   public String stringValue()
170    {
171     return this.latitude + SEPARATOR + this.longitude + SEPARATOR + this.altitude;
172    }
173 
174 
175   /**
176    * Calculate hash code.
177    *
178    * @return Hash
179    * @see java.lang.Object#hashCode()
180    */
181   @Override
182   public int hashCode()
183    {
184     int result = Double.hashCode(this.latitude);
185     result = (31 * result) + Double.hashCode(this.longitude);
186     return (31 * result) + Double.hashCode(this.altitude);
187    }
188 
189 
190   /**
191    * Is equal with another object.
192    *
193    * @param obj Object
194    * @return true when equal, false otherwise
195    * @see java.lang.Object#equals(java.lang.Object)
196    */
197   @Override
198   public boolean equals(final Object obj)
199    {
200     if (this == obj)
201      {
202       return true;
203      }
204     if (!(obj instanceof WGS84Position))
205      {
206       return false;
207      }
208     final WGS84Position other = (WGS84Position)obj;
209     return (Math.abs(this.latitude - other.latitude) < WGS84Position.EPSILON) && (Math.abs(this.longitude - other.longitude) < WGS84Position.EPSILON) && (Math.abs(this.altitude - other.altitude) < WGS84Position.EPSILON);
210    }
211 
212 
213   /**
214    * Returns the string representation of this WGS84Position.
215    *
216    * The exact details of this representation are unspecified and subject to change, but the following may be regarded as typical:
217    *
218    * "WGS84Position[latitude=0.0, longitude=0.0, altitude=0.0]"
219    *
220    * @return String representation of this WGS84Position
221    * @see java.lang.Object#toString()
222    */
223   @Override
224   public String toString()
225    {
226     final var builder = new StringBuilder(47);
227     builder.append("WGS84Position[latitude=").append(this.latitude).append(", longitude=").append(this.longitude).append(", altitude=").append(this.altitude).append(']'); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
228     return builder.toString();
229    }
230 
231 
232   /**
233    * Compare with another object.
234    *
235    * @param obj Object to compare with
236    * @return 0: equal; 1: greater; -1: smaller
237    * @see java.lang.Comparable#compareTo(java.lang.Object)
238    */
239   @Override
240   public int compareTo(final WGS84Position obj)
241    {
242     Objects.requireNonNull(obj, "obj"); //$NON-NLS-1$
243     int result = Double.compare(this.latitude, obj.latitude);
244     if (result == 0)
245      {
246       result = Double.compare(this.longitude, obj.longitude);
247       if (result == 0)
248        {
249         result = Double.compare(this.altitude, obj.altitude);
250        }
251      }
252     return result;
253    }
254 
255  }