KanoopCommonQt 2.1.1
Kanoop foundational Qt utility library
Loading...
Searching...
No Matches
geocoordinate.h
1/**
2 * @brief A WGS-84 geographic coordinate (latitude, longitude, altitude).
3 */
4#ifndef GEOCOORDINATE_H
5#define GEOCOORDINATE_H
6
7#include "Kanoop/geo/geotypes.h"
8#include <QString>
9#include <QtMath>
10
11#include "Kanoop/kanoopcommon.h"
12
13/**
14 * @brief Represents a geographic position as WGS-84 latitude, longitude, and altitude.
15 *
16 * Provides distance, azimuth, and point-at-distance-and-azimuth calculations
17 * using the Haversine formula, as well as cardinal direction comparisons.
18 */
19class KANOOP_EXPORT GeoCoordinate
20{
21public:
22 /** @brief Default constructor — creates an invalid coordinate at (0, 0, 0). */
24 _latitude(0),
25 _longitude(0),
26 _altitude(0),
27 _precision(6),
28 _cardinalLatitude(Geo::InvalidCardinalDirection),
29 _cardinalLongitude(Geo::InvalidCardinalDirection),
30 _valid(false) {}
31
32 /**
33 * @brief Construct a coordinate from decimal latitude, longitude, and optional altitude.
34 * @param latitude Latitude in decimal degrees (negative = south)
35 * @param longitude Longitude in decimal degrees (negative = west)
36 * @param altitude Altitude in metres above the WGS-84 ellipsoid (default 0)
37 */
38 GeoCoordinate(double latitude, double longitude, double altitude = 0) :
39 _latitude(latitude),
40 _longitude(longitude),
41 _altitude(altitude),
42 _precision(6),
43 _cardinalLatitude(Geo::InvalidCardinalDirection),
44 _cardinalLongitude(Geo::InvalidCardinalDirection),
45 _valid(false)
46 {
47 GeoCoordinate::validate();
48 }
49
50 /**
51 * @brief Construct a coordinate by parsing a string.
52 * @param value Coordinate string (see fromString())
53 */
54 GeoCoordinate(const QString& value);
55
56 /**
57 * @brief Parse a GeoCoordinate from a string representation.
58 * @param value Coordinate string in decimal or DMS format
59 * @return Parsed GeoCoordinate, or an invalid one on failure
60 */
61 static GeoCoordinate fromString(const QString& value)
62 {
63 return GeoCoordinate(value);
64 }
65
66 /**
67 * @brief Construct a coordinate from degrees-minutes-seconds components.
68 * @param latitudeCardinalDirection North or South
69 * @param latitudeDegrees Whole degrees of latitude
70 * @param latitudeMinutes Minutes of latitude
71 * @param latitudeSeconds Seconds of latitude
72 * @param longitudeCardinalDirection East or West
73 * @param longitudeDegrees Whole degrees of longitude
74 * @param longitudeMinutes Minutes of longitude
75 * @param longitudeSeconds Seconds of longitude
76 * @return Constructed GeoCoordinate
77 */
78 static GeoCoordinate fromDMS(Geo::CardinalDirection latitudeCardinalDirection, double latitudeDegrees, double latitudeMinutes, double latitudeSeconds,
79 Geo::CardinalDirection longitudeCardinalDirection, double longitudeDegrees, double longitudeMinutes, double longitudeSeconds);
80
81 /**
82 * @brief Output format for toString().
83 */
85 {
86 Degrees, ///< Decimal degrees (e.g. "37.7749, -122.4194")
87 Parsable, ///< Format suitable for round-trip parsing by fromString()
88 };
89
90 /** @brief Equality comparison at the configured precision.
91 * @param other Coordinate to compare against
92 * @return true if coordinates are equal within the configured precision */
93 bool operator==(const GeoCoordinate& other) const;
94 /** @brief Inequality comparison.
95 * @param other Coordinate to compare against
96 * @return true if coordinates are not equal */
97 bool operator!=(const GeoCoordinate& other) const { return !(*this == other); }
98
99 /**
100 * @brief Return the latitude in decimal degrees.
101 * @return Latitude (negative = south)
102 */
103 double latitude() const { return _latitude; }
104
105 /**
106 * @brief Set the latitude in decimal degrees.
107 * @param value New latitude value
108 */
109 void setLatitude(double value) { _latitude = value; validate(); }
110
111 /**
112 * @brief Return the longitude in decimal degrees.
113 * @return Longitude (negative = west)
114 */
115 double longitude() const { return _longitude; }
116
117 /**
118 * @brief Set the longitude in decimal degrees.
119 * @param value New longitude value
120 */
121 void setLongitude(double value) { _longitude = value; validate(); }
122
123 /**
124 * @brief Return the altitude in metres.
125 * @return Altitude above the WGS-84 ellipsoid
126 */
127 double altitude() const { return _altitude; }
128
129 /**
130 * @brief Set the altitude in metres.
131 * @param value New altitude value
132 */
133 void setAltitude(double value) { _altitude = value; validate(); }
134
135 /**
136 * @brief Return the decimal precision used for coordinate comparisons.
137 * @return Number of decimal places
138 */
139 int precision() const { return _precision; }
140
141 /**
142 * @brief Set the decimal precision used for coordinate comparisons.
143 * @param value Number of decimal places
144 */
145 void setPrecision(int value) { _precision = value; }
146
147 /**
148 * @brief Compute the great-circle distance to another coordinate in metres.
149 * @param other Destination coordinate
150 * @return Distance in metres
151 */
152 double distanceTo(const GeoCoordinate& other) const { return getDistance(*this, other); }
153
154 /**
155 * @brief Compute the azimuth (bearing) to another coordinate in degrees.
156 * @param other Destination coordinate
157 * @return Azimuth in degrees clockwise from North
158 */
159 double azimuthTo(const GeoCoordinate& other) const { return getAzimuth(*this, other); }
160
161 /**
162 * @brief Return the coordinate reached by travelling a given distance and azimuth from this point.
163 * @param distance Distance in metres
164 * @param azimuth Bearing in degrees clockwise from North
165 * @return Resulting GeoCoordinate
166 */
167 GeoCoordinate atDistanceAndAzimuth(double distance, double azimuth) { return getPointAtDistanceAndAzimuth(*this, distance, azimuth); }
168
169 /**
170 * @brief Test whether this coordinate is north of or at the same latitude as another.
171 * @param other Coordinate to compare against
172 * @return true if this latitude ≥ other's latitude (at configured precision)
173 */
174 bool isNorthOfOrEqualTo(const GeoCoordinate& other) const
175 {
176 return isNorthOf(other) || (equalAtPrecision(_latitude, other._latitude, _precision) && _cardinalLatitude == other._cardinalLatitude);
177 }
178
179 /**
180 * @brief Test whether this coordinate is strictly north of another.
181 * @param other Coordinate to compare against
182 * @return true if this latitude > other's latitude
183 */
184 bool isNorthOf(const GeoCoordinate& other) const;
185
186 /**
187 * @brief Test whether this coordinate is south of or at the same latitude as another.
188 * @param other Coordinate to compare against
189 * @return true if this latitude ≤ other's latitude (at configured precision)
190 */
191 bool isSouthOfOrEqualTo(const GeoCoordinate& other) const
192 {
193 return isSouthOf(other) || (equalAtPrecision(_latitude, other._cardinalLatitude, _precision) && _cardinalLatitude == other._cardinalLatitude);
194 }
195
196 /**
197 * @brief Test whether this coordinate is strictly south of another.
198 * @param other Coordinate to compare against
199 * @return true if this latitude < other's latitude
200 */
201 bool isSouthOf(const GeoCoordinate& other) const{ return !isNorthOf(other); }
202
203 /**
204 * @brief Test whether this coordinate is west of or at the same longitude as another.
205 * @param other Coordinate to compare against
206 * @return true if this longitude ≤ other's longitude (at configured precision)
207 */
208 bool isWestOfOrEqualTo(const GeoCoordinate& other) const
209 {
210 return isWestOf(other) || equalAtPrecision(_longitude, other._longitude, _precision);
211 }
212
213 /**
214 * @brief Test whether this coordinate is strictly west of another.
215 * @param other Coordinate to compare against
216 * @return true if this longitude < other's longitude
217 */
218 bool isWestOf(const GeoCoordinate& other) const;
219
220 /**
221 * @brief Test whether this coordinate is east of or at the same longitude as another.
222 * @param other Coordinate to compare against
223 * @return true if this longitude ≥ other's longitude (at configured precision)
224 */
225 bool isEastOfOrEqualTo(const GeoCoordinate& other) const
226 {
227 return isEastOf(other) || equalAtPrecision(_longitude, other._longitude, _precision);
228 }
229
230 /**
231 * @brief Test whether this coordinate is strictly east of another.
232 * @param other Coordinate to compare against
233 * @return true if this longitude > other's longitude
234 */
235 bool isEastOf(const GeoCoordinate& other) const { return !isWestOf(other); }
236
237 /**
238 * @brief Test whether this coordinate equals the default-constructed (empty) value.
239 * @return true if this coordinate is at (0, 0, 0) with no cardinal directions set
240 */
241 bool isEmpty() const { return *this == GeoCoordinate(); }
242
243 /**
244 * @brief Test whether this coordinate represents a valid position.
245 * @return true if the coordinate is not empty
246 */
247 bool isValid() const { return !isEmpty(); }
248
249 /**
250 * @brief Format this coordinate as a string.
251 * @param format Output format (Degrees or Parsable)
252 * @param precision Decimal places for coordinate values (0 = use instance precision)
253 * @return Formatted coordinate string
254 */
255 QString toString(CoordinateFormat format = Degrees, int precision = 0) const;
256
257 /**
258 * @brief Attempt to parse a coordinate string, reporting success.
259 * @param stringValue String to parse
260 * @param point Output receiving the parsed coordinate
261 * @return true if parsing succeeded
262 */
263 static bool tryParse(const QString& stringValue, GeoCoordinate &point);
264
265 /**
266 * @brief Return a default-constructed (empty) coordinate.
267 * @return Empty GeoCoordinate
268 */
269 static GeoCoordinate empty() { return GeoCoordinate(); }
270
271 /**
272 * @brief Compute the great-circle distance between two coordinates in metres.
273 * @param pt1 First coordinate
274 * @param pt2 Second coordinate
275 * @return Distance in metres
276 */
277 static double getDistance(const GeoCoordinate& pt1, const GeoCoordinate& pt2);
278
279 /**
280 * @brief Compute the azimuth from one coordinate to another in degrees.
281 * @param pt1 Origin coordinate
282 * @param pt2 Destination coordinate
283 * @return Azimuth in degrees clockwise from North
284 */
285 static double getAzimuth(const GeoCoordinate& pt1, const GeoCoordinate& pt2);
286
287 /**
288 * @brief Compute the coordinate reached by travelling from an origin at a given distance and azimuth.
289 * @param origin Starting coordinate
290 * @param distance Distance in metres
291 * @param azimuth Bearing in degrees clockwise from North
292 * @return Resulting GeoCoordinate
293 */
294 static GeoCoordinate getPointAtDistanceAndAzimuth(const GeoCoordinate& origin, double distance, double azimuth);
295
296private:
297 /** @brief Validate and set the _valid flag based on the current coordinate values. */
298 void validate();
299
300 /** @brief Test two doubles for equality at a given number of decimal places. */
301 static bool equalAtPrecision(double v1, double v2, int precision);
302
303 double _latitude;
304 double _longitude;
305 double _altitude;
306
307 int _precision;
308
309 Geo::CardinalDirection _cardinalLatitude;
310 Geo::CardinalDirection _cardinalLongitude;
311
312 bool _valid;
313
314 static QString TOSTRING_DELIM;
315};
316
317#endif // GEOCOORDINATE_H
A WGS-84 geographic coordinate (latitude, longitude, altitude).
bool isEmpty() const
Test whether this coordinate equals the default-constructed (empty) value.
GeoCoordinate atDistanceAndAzimuth(double distance, double azimuth)
Return the coordinate reached by travelling a given distance and azimuth from this point.
int precision() const
Return the decimal precision used for coordinate comparisons.
void setAltitude(double value)
Set the altitude in metres.
static GeoCoordinate fromDMS(Geo::CardinalDirection latitudeCardinalDirection, double latitudeDegrees, double latitudeMinutes, double latitudeSeconds, Geo::CardinalDirection longitudeCardinalDirection, double longitudeDegrees, double longitudeMinutes, double longitudeSeconds)
Construct a coordinate from degrees-minutes-seconds components.
bool isWestOfOrEqualTo(const GeoCoordinate &other) const
Test whether this coordinate is west of or at the same longitude as another.
bool isValid() const
Test whether this coordinate represents a valid position.
GeoCoordinate()
Default constructor — creates an invalid coordinate at (0, 0, 0).
CoordinateFormat
Output format for toString().
@ Parsable
Format suitable for round-trip parsing by fromString()
@ Degrees
Decimal degrees (e.g. "37.7749, -122.4194")
bool operator!=(const GeoCoordinate &other) const
Inequality comparison.
bool isSouthOf(const GeoCoordinate &other) const
Test whether this coordinate is strictly south of another.
bool isWestOf(const GeoCoordinate &other) const
Test whether this coordinate is strictly west of another.
double altitude() const
Return the altitude in metres.
bool isNorthOfOrEqualTo(const GeoCoordinate &other) const
Test whether this coordinate is north of or at the same latitude as another.
void setLatitude(double value)
Set the latitude in decimal degrees.
bool isEastOf(const GeoCoordinate &other) const
Test whether this coordinate is strictly east of another.
GeoCoordinate(double latitude, double longitude, double altitude=0)
Construct a coordinate from decimal latitude, longitude, and optional altitude.
void setLongitude(double value)
Set the longitude in decimal degrees.
bool isEastOfOrEqualTo(const GeoCoordinate &other) const
Test whether this coordinate is east of or at the same longitude as another.
bool isSouthOfOrEqualTo(const GeoCoordinate &other) const
Test whether this coordinate is south of or at the same latitude as another.
bool operator==(const GeoCoordinate &other) const
Equality comparison at the configured precision.
double latitude() const
Return the latitude in decimal degrees.
static double getDistance(const GeoCoordinate &pt1, const GeoCoordinate &pt2)
Compute the great-circle distance between two coordinates in metres.
double distanceTo(const GeoCoordinate &other) const
Compute the great-circle distance to another coordinate in metres.
static bool tryParse(const QString &stringValue, GeoCoordinate &point)
Attempt to parse a coordinate string, reporting success.
static GeoCoordinate getPointAtDistanceAndAzimuth(const GeoCoordinate &origin, double distance, double azimuth)
Compute the coordinate reached by travelling from an origin at a given distance and azimuth.
void setPrecision(int value)
Set the decimal precision used for coordinate comparisons.
double azimuthTo(const GeoCoordinate &other) const
Compute the azimuth (bearing) to another coordinate in degrees.
static double getAzimuth(const GeoCoordinate &pt1, const GeoCoordinate &pt2)
Compute the azimuth from one coordinate to another in degrees.
QString toString(CoordinateFormat format=Degrees, int precision=0) const
Format this coordinate as a string.
static GeoCoordinate fromString(const QString &value)
Parse a GeoCoordinate from a string representation.
static GeoCoordinate empty()
Return a default-constructed (empty) coordinate.
double longitude() const
Return the longitude in decimal degrees.
GeoCoordinate(const QString &value)
Construct a coordinate by parsing a string.
bool isNorthOf(const GeoCoordinate &other) const
Test whether this coordinate is strictly north of another.
Geographic type enumerations and string-conversion helpers for Qt geometry types.
Definition geotypes.h:14
CardinalDirection
Cardinal compass directions used by geographic coordinates.
Definition geotypes.h:20