TinyRobotics
Loading...
Searching...
No Matches
GPSCoordinate.h
1#pragma once
2
3#include <cmath>
4#include <string>
5#include <vector>
6
7namespace tinyrobotics {
8
9/**
10 * @class GPSCoordinate
11 * @ingroup coordinates
12 * @brief Represents a geodetic GPS coordinate with latitude, longitude, and
13 * optional altitude.
14 *
15 * The GPSCoordinate class encapsulates a single point on Earth using WGS84
16 * latitude and longitude (in degrees), and optional altitude (in meters). It
17 * provides methods for:
18 * - Calculating geodesic distance and bearing to another coordinate (using
19 * the haversine formula)
20 * - Computing elevation angle and altitude difference
21 * - Navigating to a new coordinate given a distance and bearing (great-circle
22 * navigation)
23 * - Comparing coordinates with a specified tolerance (for proximity checks)
24 * - Serializing/deserializing to and from string representations for storage
25 * or transmission
26 *
27 * Fields:
28 * - latitude: WGS84 latitude in degrees (-90 to 90)
29 * - longitude: WGS84 longitude in degrees (-180 to 180)
30 * - altitude: Altitude above ellipsoid in meters (optional)
31 *
32 * Usage:
33 * - Use isValid() to check if the coordinate is usable for navigation.
34 * - Use distance(), bearing(), and elevation() to compute spatial
35 * relationships.
36 * - Use navigate() to project a new coordinate from the current one.
37 * - Use equals() to compare two coordinates within a tolerance.
38 * - Use toString() and fromString() for serialization.
39 *
40 * Example:
41 * GPSCoordinate a(48.8584, 2.2945, 35); // Eiffel Tower
42 * GPSCoordinate b(51.5007, -0.1246, 15); // London
43 * float dist = a.distance(b); // meters
44 * float brng = a.bearing(b); // degrees
45 * GPSCoordinate c = a.navigate(1000, 90); // 1km east
46 *
47 * This class is suitable for robotics, mapping, navigation, and geospatial
48 * applications on embedded or desktop systems.
49 * @ingroup coordinates
50 */
51
52class GPSCoordinate {
53 public:
54 float latitude = 360; // Degrees (-90 to 90)
55 float longitude = 360; // Degrees (-180 to 180)
56 float altitude = 0; // Meters above ellipsoid (optional)
57
58 GPSCoordinate() = default;
59
60 GPSCoordinate(float lat, float lon, float alt = 0)
61 : latitude(lat), longitude(lon), altitude(alt) {}
62
63 /// Check if the values are valid
64 bool isValid() const {
65 return latitude >= -90 && latitude <= 90 && longitude >= -180 &&
66 longitude <= 180;
67 }
68
69 /// Check if the values are valid
70 operator bool() const { return isValid(); }
71
72 /// Calculate distance to other GPS coordinate
73 float distance(const GPSCoordinate& other,
74 DistanceUnit unit = DistanceUnit::M) const {
75 float distM = distanceM(other);
76 Distance dist(distM, DistanceUnit::M);
77 return dist.getValue(unit);
78 }
79
80 /// Calculate the bearing (heading) in degrees from this coordinate to another
81 /// GPS coordinate.
82 float bearing(const GPSCoordinate& other,
83 AngleUnit unit = AngleUnit::DEG) const {
84 float bearingDeg = bearingDegree(other);
85 Angle angle(bearingDeg, AngleUnit::DEG);
86 return angle.getValue(unit);
87 }
88
89 /// Calculate the elevation angle in degrees from this coordinate to another
90 /// GPS coordinate.
91 float elevation(const GPSCoordinate& other,
92 AngleUnit unit = AngleUnit::DEG) const {
93 float angleDeg = elevationDeg(other);
94 Angle angle(angleDeg, AngleUnit::DEG);
95 return angle.getValue(unit);
96 }
97
98 /// Calculate the altitude difference in meters between this coordinate and
99 /// another
100 float altitudeDifference(const GPSCoordinate& other) const {
101 return other.altitude - altitude;
102 }
103
104 /// Calculate a new GPS coordinate given a distance (in meters) and bearing
105 GPSCoordinate navigate(Distance distance, Angle bearing,
106 Distance altDiff) const {
107 float bearingDeg = bearing.getValue(AngleUnit::DEG);
108 float distanceM = distance.getValue(DistanceUnit::M);
109 float altDiffM = altDiff.getValue(DistanceUnit::M);
110 return navigate(distanceM, bearingDeg, altDiffM);
111 }
112
113 /// Calculate a new GPS coordinate given a distance (in meters) and bearing
114 GPSCoordinate navigate(float distance_m, float bearing_deg,
115 float alt_diff_m = 0) const {
116 float R = 6371000.0f; // Earth radius in meters
117 float bearing_rad = bearing_deg * static_cast<float>(M_PI) / 180.0f;
118 float lat1 = latitude * static_cast<float>(M_PI) / 180.0f;
119 float lon1 = longitude * static_cast<float>(M_PI) / 180.0f;
120
121 float lat2 = std::asin(std::sin(lat1) * std::cos(distance_m / R) +
122 std::cos(lat1) * std::sin(distance_m / R) *
123 std::cos(bearing_rad));
124 float lon2 =
125 lon1 +
126 std::atan2(
127 std::sin(bearing_rad) * std::sin(distance_m / R) * std::cos(lat1),
128 std::cos(distance_m / R) - std::sin(lat1) * std::sin(lat2));
129
130 return GPSCoordinate(lat2 * 180.0f / static_cast<float>(M_PI),
131 lon2 * 180.0f / static_cast<float>(M_PI),
132 altitude + alt_diff_m);
133 }
134
135 /// Compare two GPS coordinates for proximity within a specified distance
136 /// limit
137 bool equals(const GPSCoordinate& other, float limit) const {
138 return distance(other) < limit;
139 }
140
141 /// Compare two GPS coordinates for proximity within specified distance and
142 /// altitude limits
143 bool equalsWithAltitude(const GPSCoordinate& other, float limit,
144 float altLimit) const {
145 return distance(other) < limit &&
146 std::fabs(altitudeDifference(other)) < altLimit;
147 }
148
149 /// Serialize GPS coordinate to string representation
150 std::string toString() const {
151 char buf[100];
152 snprintf(buf, sizeof(buf), "%s: %.8f, %.8f, %.2fm", getTypeName(), latitude,
153 longitude, altitude);
154 return std::string(buf);
155 }
156
157 /// load GPS coordinate from string representation (must match toString()
158 /// format)
159 bool fromString(const std::string& str) {
160 float lat, lon, alt;
161 char typeName[80]{};
162 // The format string must match toString()
163 int n =
164 sscanf(str.c_str(), "%79[^:]: %f, %f, %fm", typeName, &lat, &lon, &alt);
165 if (n == 4) {
166 if (strcmp(typeName, getTypeName()) != 0) return false;
167 latitude = lat;
168 longitude = lon;
169 altitude = alt;
170 return true;
171 }
172 return false;
173 }
174
175 const char* getTypeName() const { return "GPSCoordinate"; }
176
177 protected:
178 /// Calculate the bearing (heading) in degrees from this coordinate to another
179 /// GPS coordinate.
180 float bearingDegree(const GPSCoordinate& other) const {
181 float lat1 = latitude * static_cast<float>(M_PI) / 180.0f;
182 float lat2 = other.latitude * static_cast<float>(M_PI) / 180.0f;
183 float dLon =
184 (other.longitude - longitude) * static_cast<float>(M_PI) / 180.0f;
185
186 float y = std::sin(dLon) * std::cos(lat2);
187 float x = std::cos(lat1) * std::sin(lat2) -
188 std::sin(lat1) * std::cos(lat2) * std::cos(dLon);
189 float brng = std::atan2(y, x);
190 return normalizeAngleDeg(brng * 180.0f /
191 static_cast<float>(M_PI)); // Degrees
192 }
193
194 float elevationDeg(const GPSCoordinate& other) const {
195 float dz = other.altitude - altitude;
196 float dxy = distanceM(other);
197 return std::atan2(dz, dxy) * 180.0f / static_cast<float>(M_PI); // Degrees
198 }
199
200 /// Distance in meters between this coordinate and another GPS coordinate
201 float distanceM(const GPSCoordinate& other) const {
202 float R = 6371000.0f; // Earth radius in meters
203 float lat1 = latitude * static_cast<float>(M_PI) / 180.0f;
204 float lat2 = other.latitude * static_cast<float>(M_PI) / 180.0f;
205 float dLat =
206 (other.latitude - latitude) * static_cast<float>(M_PI) / 180.0f;
207 float dLon =
208 (other.longitude - longitude) * static_cast<float>(M_PI) / 180.0f;
209
210 float a = std::sin(dLat / 2.0f) * std::sin(dLat / 2.0f) +
211 std::cos(lat1) * std::cos(lat2) * std::sin(dLon / 2.0f) *
212 std::sin(dLon / 2.0f);
213 float c = 2.0f * std::atan2(std::sqrt(a), std::sqrt(1.0f - a));
214 return R * c; // Distance in meters
215 }
216};
217
218} // namespace tinyrobotics
Represents a geodetic GPS coordinate with latitude, longitude, and optional altitude.
Definition: GPSCoordinate.h:52
std::string toString() const
Serialize GPS coordinate to string representation.
Definition: GPSCoordinate.h:150
float bearingDegree(const GPSCoordinate &other) const
Definition: GPSCoordinate.h:180
float distance(const GPSCoordinate &other, DistanceUnit unit=DistanceUnit::M) const
Calculate distance to other GPS coordinate.
Definition: GPSCoordinate.h:73
bool isValid() const
Check if the values are valid.
Definition: GPSCoordinate.h:64
bool equalsWithAltitude(const GPSCoordinate &other, float limit, float altLimit) const
Definition: GPSCoordinate.h:143
operator bool() const
Check if the values are valid.
Definition: GPSCoordinate.h:70
float elevation(const GPSCoordinate &other, AngleUnit unit=AngleUnit::DEG) const
Definition: GPSCoordinate.h:91
bool fromString(const std::string &str)
Definition: GPSCoordinate.h:159
GPSCoordinate navigate(float distance_m, float bearing_deg, float alt_diff_m=0) const
Calculate a new GPS coordinate given a distance (in meters) and bearing.
Definition: GPSCoordinate.h:114
float altitudeDifference(const GPSCoordinate &other) const
Definition: GPSCoordinate.h:100
GPSCoordinate navigate(Distance distance, Angle bearing, Distance altDiff) const
Calculate a new GPS coordinate given a distance (in meters) and bearing.
Definition: GPSCoordinate.h:105
float distanceM(const GPSCoordinate &other) const
Distance in meters between this coordinate and another GPS coordinate.
Definition: GPSCoordinate.h:201
bool equals(const GPSCoordinate &other, float limit) const
Definition: GPSCoordinate.h:137
float bearing(const GPSCoordinate &other, AngleUnit unit=AngleUnit::DEG) const
Definition: GPSCoordinate.h:82