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 }