|
17 | 17 | */
|
18 | 18 |
|
19 | 19 | #include "AP_Math.h"
|
| 20 | +#include "float.h" |
20 | 21 |
|
21 | 22 | #pragma GCC optimize("O2")
|
22 | 23 |
|
@@ -124,7 +125,6 @@ template bool Polygon_complete<int32_t>(const Vector2l *V, unsigned n);
|
124 | 125 | template bool Polygon_outside<float>(const Vector2f &P, const Vector2f *V, unsigned n);
|
125 | 126 | template bool Polygon_complete<float>(const Vector2f *V, unsigned n);
|
126 | 127 |
|
127 |
| - |
128 | 128 | /*
|
129 | 129 | determine if the polygon of N verticies defined by points V is
|
130 | 130 | intersected by a line from point p1 to point p2
|
@@ -196,21 +196,96 @@ float Polygon_closest_distance_line(const Vector2f *V, unsigned N, const Vector2
|
196 | 196 | return sqrtf(closest_sq);
|
197 | 197 | }
|
198 | 198 |
|
| 199 | +const ftype EARTH_RADIUS_METERS = 6378137.0; // Earth's radius in meters (WGS84) |
| 200 | + |
| 201 | +// Converts 1e7 degrees to radians |
| 202 | +static inline ftype to_radians(ftype degrees_1e7) |
| 203 | +{ |
| 204 | + return radians(degrees_1e7 / 1.0e7); |
| 205 | +} |
| 206 | + |
| 207 | +// Converts (latitude, longitude) in radians to 3D Cartesian coordinates on a unit sphere |
| 208 | +static void latlng_to_cartesian(ftype latRad, ftype lonRad, Vector3F& v) |
| 209 | +{ |
| 210 | + v.x = cosF(latRad) * cosF(lonRad); |
| 211 | + v.y = cosF(latRad) * sinF(lonRad); |
| 212 | + v.z = sinF(latRad); |
| 213 | +} |
| 214 | + |
| 215 | +// Haversine distance between two points given in 1e7 degrees |
| 216 | +static ftype haversine(long lat1_1e7, long lon1_1e7, long lat2_1e7, long lon2_1e7) |
| 217 | +{ |
| 218 | + ftype lat1 = to_radians(lat1_1e7); |
| 219 | + ftype lat2 = to_radians(lat2_1e7); |
| 220 | + |
| 221 | + ftype dLat = to_radians(lat2_1e7 - lat1_1e7); |
| 222 | + ftype dLon = to_radians(lon2_1e7 - lon1_1e7); |
| 223 | + |
| 224 | + // (d/2r)^2 |
| 225 | + ftype a = dLat * dLat / 4 + cosF(lat2) * cosF(lat1) * dLon * dLon / 4; |
| 226 | + ftype d = sqrtF(a) * 2 * EARTH_RADIUS_METERS; |
| 227 | + |
| 228 | + return d; |
| 229 | +} |
| 230 | + |
| 231 | +// Compute closest distance from a point to a line segment on a sphere |
| 232 | +static ftype closest_distance_to_segment(const Vector2l& point, const Vector2l& line_start, const Vector2l& line_end) |
| 233 | +{ |
| 234 | + // Convert lat/lon to 3D Cartesian coordinates on a unit sphere |
| 235 | + Vector3F v1, v2, vp; |
| 236 | + latlng_to_cartesian(to_radians(line_start.x), to_radians(line_start.y), v1); |
| 237 | + latlng_to_cartesian(to_radians(line_end.x), to_radians(line_end.y), v2); |
| 238 | + latlng_to_cartesian(to_radians(point.x), to_radians(point.y), vp); |
| 239 | + |
| 240 | + // Compute vector from lineStart to lineEnd (v) and from lineStart to point (w) |
| 241 | + Vector3F v = v2 - v1; |
| 242 | + Vector3F w = vp - v1; |
| 243 | + |
| 244 | + // Project w onto v |
| 245 | + ftype dot_vw = v * w; |
| 246 | + ftype dot_vv = v * v; |
| 247 | + ftype t = dot_vw / dot_vv; |
| 248 | + |
| 249 | + Vector3F closest; |
| 250 | + if (t < 0) { |
| 251 | + // Closest to line start |
| 252 | + closest = v1; |
| 253 | + } else if (t > 1) { |
| 254 | + // Closest to line end |
| 255 | + closest = v2; |
| 256 | + } else { |
| 257 | + // Closest point is on the line |
| 258 | + closest = v1 + v * t; |
| 259 | + } |
| 260 | + |
| 261 | + // Convert closest point back to lat/lon (reverse cartesian) |
| 262 | + ftype norm = closest.length(); |
| 263 | + ftype closestLat = asinF(closest.z / norm); // Latitude from z-coordinate |
| 264 | + ftype closestLon = atan2F(closest.y, closest.x); // Longitude from x and y |
| 265 | + |
| 266 | + // Convert back to 1e7 degrees |
| 267 | + long closestLat_1e7 = static_cast<long>(degrees(closestLat) * 1e7); |
| 268 | + long closestLon_1e7 = static_cast<long>(degrees(closestLon) * 1e7); |
| 269 | + |
| 270 | + // Calculate the Haversine distance from the point to the closest point on the line |
| 271 | + return haversine(point.x, point.y, closestLat_1e7, closestLon_1e7); |
| 272 | +} |
| 273 | + |
199 | 274 | /*
|
200 |
| - return the closest distance that point p comes to an edge of closed |
201 |
| - polygon V, defined by N points |
| 275 | + return the closest distance in meters that point p in lat/lng 1e7 comes to an edge of closed |
| 276 | + polygon V, defined by N points of lat/lng 1e7 |
202 | 277 | */
|
203 |
| -float Polygon_closest_distance_point(const Vector2f *V, unsigned N, const Vector2f &p) |
| 278 | +ftype Polygon_closest_distance_point(const Vector2l *V, unsigned N, const Vector2l &p) |
204 | 279 | {
|
205 |
| - float closest_sq = FLT_MAX; |
206 |
| - for (uint8_t i=0; i<N-1; i++) { |
207 |
| - const Vector2f &v1 = V[i]; |
208 |
| - const Vector2f &v2 = V[i+1]; |
| 280 | + ftype closest = std::numeric_limits<ftype>::max(); |
| 281 | + for (uint8_t i=0; i<N; i++) { |
| 282 | + const Vector2l &v1 = V[i]; |
| 283 | + const Vector2l &v2 = V[(i+1) % N]; |
209 | 284 |
|
210 |
| - float dist_sq = Vector2f::closest_distance_between_line_and_point_squared(v1, v2, p); |
211 |
| - if (dist_sq < closest_sq) { |
212 |
| - closest_sq = dist_sq; |
| 285 | + ftype distance = closest_distance_to_segment(p, v1, v2); |
| 286 | + if (distance < closest) { |
| 287 | + closest = distance; |
213 | 288 | }
|
214 | 289 | }
|
215 |
| - return sqrtf(closest_sq); |
| 290 | + return closest; |
216 | 291 | }
|
0 commit comments