This repository was archived by the owner on Jul 14, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGeometry.py
More file actions
204 lines (159 loc) · 6.77 KB
/
Copy pathGeometry.py
File metadata and controls
204 lines (159 loc) · 6.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
"""
@file Geometry.py
@author 30hours
"""
import math
class Geometry:
"""
@class Geometry
@brief A class to store geometric functions.
@details Assumes WGS-84 ellipsoid for all functions.
"""
def __init__(self):
"""
@brief Constructor for the Geometry class.
"""
def lla2ecef(lat, lon, alt):
"""
@brief Converts geodetic coordinates (latitude, longitude, altitude) to ECEF coordinates.
@param lat (float): Geodetic latitude in degrees.
@param lon (float): Geodetic longitude in degrees.
@param alt (float): Altitude above the ellipsoid in meters.
@return ecef_x (float): ECEF x-coordinate in meters.
@return ecef_y (float): ECEF y-coordinate in meters.
@return ecef_z (float): ECEF z-coordinate in meters.
"""
# WGS84 constants
a = 6378137.0 # semi-major axis in meters
f = 1 / 298.257223563 # flattening
e = 0.081819190842622
lat_rad = math.radians(lat)
lon_rad = math.radians(lon)
cos_lat = math.cos(lat_rad)
sin_lat = math.sin(lat_rad)
N = a / math.sqrt(1 - f * (2 - f) * sin_lat**2)
# calculate ECEF coordinates
ecef_x = (N + alt) * cos_lat * math.cos(lon_rad)
ecef_y = (N + alt) * cos_lat * math.sin(lon_rad)
ecef_z = ((1-(e**2)) * N + alt) * sin_lat
return ecef_x, ecef_y, ecef_z
def ecef2lla(x, y, z):
"""
@brief Converts ECEF coordinates to geodetic coordinates (latitude, longitude, altitude).
@param x (float): ECEF x-coordinate in meters.
@param y (float): ECEF y-coordinate in meters.
@param z (float): ECEF z-coordinate in meters.
@return lat (float): Geodetic latitude in degrees.
@return lon (float): Geodetic longitude in degrees.
@return alt (float): Altitude above the ellipsoid in meters.
"""
# WGS84 ellipsoid constants:
a = 6378137
e = 8.1819190842622e-2
b = math.sqrt(a**2 * (1 - e**2))
ep = math.sqrt((a**2 - b**2) / b**2)
p = math.sqrt(x**2 + y**2)
th = math.atan2(a * z, b * p)
lon = math.atan2(y, x)
lat = math.atan2((z + ep**2 * b * math.sin(th)**3), (p - e**2 * a * math.cos(th)**3))
N = a / math.sqrt(1 - e**2 * math.sin(lat)**2)
alt = p / math.cos(lat) - N
# return lon in range [0, 2*pi)
lon = lon % (2 * math.pi)
# correct for numerical instability in altitude near exact poles:
k = abs(x) < 1e-10 and abs(y) < 1e-10
alt = abs(z) - b if k else alt
lat = math.degrees(lat)
lon = math.degrees(lon)
return lat, lon, alt
def enu2ecef(e1, n1, u1, lat, lon, alt):
"""
@brief Converts East-North-Up (ENU) coordinates to ECEF coordinates.
@param e1 (float): Target east ENU coordinate in meters.
@param n1 (float): Target north ENU coordinate in meters.
@param u1 (float): Target up ENU coordinate in meters.
@param lat (float): Observer geodetic latitude in degrees.
@param lon (float): Observer geodetic longitude in degrees.
@param alt (float): Observer geodetic altitude in meters.
@return x (float): Target x ECEF coordinate in meters.
@return y (float): Target y ECEF coordinate in meters.
@return z (float): Target z ECEF coordinate in meters.
"""
x0, y0, z0 = Geometry.lla2ecef(lat, lon, alt)
dx, dy, dz = Geometry.enu2uvw(e1, n1, u1, lat, lon)
return x0 + dx, y0 + dy, z0 + dz
def enu2uvw(east, north, up, lat, lon):
"""
@brief Converts East-North-Up (ENU) coordinates to UVW coordinates.
@param east (float): Target east ENU coordinate in meters.
@param north (float): Target north ENU coordinate in meters.
@param up (float): Target up ENU coordinate in meters.
@param lat (float): Observer geodetic latitude in degrees.
@param lon (float): Observer geodetic longitude in degrees.
@return u (float): Target u coordinate in meters.
@return v (float): Target v coordinate in meters.
@return w (float): Target w coordinate in meters.
"""
lat = math.radians(lat)
lon = math.radians(lon)
t = math.cos(lat) * up - math.sin(lat) * north
w = math.sin(lat) * up + math.cos(lat) * north
u = math.cos(lon) * t - math.sin(lon) * east
v = math.sin(lon) * t + math.cos(lon) * east
return u, v, w
def ecef2enu(x, y, z, lat, lon, alt):
"""
@brief Converts ECEF coordinates to East-North-Up (ENU) coordinates.
@param x (float): Target x ECEF coordinate in meters.
@param y (float): Target y ECEF coordinate in meters.
@param z (float): Target z ECEF coordinate in meters.
@param lat (float): Observer geodetic latitude in degrees.
@param lon (float): Observer geodetic longitude in degrees.
@param alt (float): Observer geodetic altitude in meters.
@return east (float): Target east ENU coordinate in meters.
@return north (float): Target north ENU coordinate in meters.
@return up (float): Target up ENU coordinate in meters.
"""
x0, y0, z0 = Geometry.lla2ecef(lat, lon, alt)
return Geometry.uvw2enu(x - x0, y - y0, z - z0, lat, lon)
def uvw2enu(u, v, w, lat, lon):
"""
@brief Converts UVW coordinates to East-North-Up (ENU) coordinates.
@param u (float): Shifted ECEF coordinate in the u-direction (m).
@param v (float): Shifted ECEF coordinate in the v-direction (m).
@param w (float): Shifted ECEF coordinate in the w-direction (m).
@param lat (float): Observer geodetic latitude in degrees.
@param lon (float): Observer geodetic longitude in degrees.
@return e (float): Target east ENU coordinate in meters.
@return n (float): Target north ENU coordinate in meters.
@return u (float): Target up ENU coordinate in meters.
"""
lat = math.radians(lat)
lon = math.radians(lon)
cos_lat = math.cos(lat)
sin_lat = math.sin(lat)
cos_lon = math.cos(lon)
sin_lon = math.sin(lon)
t = cos_lon * u + sin_lon * v
e = -sin_lon * u + cos_lon * v
u = cos_lat * t + sin_lat * w
n = -sin_lat * t + cos_lat * w
return e, n, u
def distance_ecef(point1, point2):
"""
@brief Computes the Euclidean distance between two points in ECEF coordinates.
@param point1 (tuple): Coordinates of the first point (x, y, z) in meters.
@param point2 (tuple): Coordinates of the second point (x, y, z) in meters.
@return distance (float): Euclidean distance between the two points in meters.
"""
return math.sqrt(
(point2[0]-point1[0])**2 +
(point2[1]-point1[1])**2 +
(point2[2]-point1[2])**2)
def average_points(points):
"""
@brief Computes the average point from a list of points.
@param points (list): List of points, where each point is a tuple of coordinates (x, y, z) in meters.
@return average_point (list): Coordinates of the average point (x_avg, y_avg, z_avg) in meters.
"""
return [sum(coord) / len(coord) for coord in zip(*points)]