Skip to content

Commit 7604956

Browse files
OzGavjoostlek
andauthored
Improve bounding box calculations (#35)
Co-authored-by: Joostlek <[email protected]>
1 parent 07889d5 commit 7604956

File tree

2 files changed

+52
-121
lines changed

2 files changed

+52
-121
lines changed

src/python_opensky/opensky.py

Lines changed: 41 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -222,127 +222,55 @@ def _convert_state(state: list[Any]) -> dict[str, Any]:
222222
return dict(zip(keys, state, strict=True))
223223

224224
@staticmethod
225-
# pylint: disable=too-many-locals
226-
def calculate_point(
225+
def get_bounding_box(
227226
latitude: float,
228227
longitude: float,
229-
distance: float,
230-
degrees: float,
231-
) -> tuple[float, float]:
232-
"""Calculate a point from an origin point, direction in degrees and distance."""
233-
# ruff: noqa: N806
234-
# pylint: disable=invalid-name
235-
pi_d4 = math.atan(1.0)
236-
two_pi = pi_d4 * 8.0
237-
latitude = latitude * pi_d4 / 45.0
238-
longitude = longitude * pi_d4 / 45.0
239-
degrees = degrees * pi_d4 / 45.0
240-
if degrees < 0.0:
241-
degrees = degrees + two_pi
242-
if degrees > two_pi:
243-
degrees = degrees - two_pi
244-
axis_a = 6378137
245-
flattening = 1 / 298.257223563
246-
axis_b = axis_a * (1.0 - flattening)
247-
tan_u1 = (1 - flattening) * math.tan(latitude)
248-
u1 = math.atan(tan_u1)
249-
sigma1 = math.atan2(tan_u1, math.cos(degrees))
250-
sinalpha = math.cos(u1) * math.sin(degrees)
251-
cosalpha_sq = 1.0 - sinalpha * sinalpha
252-
u2 = cosalpha_sq * (axis_a * axis_a - axis_b * axis_b) / (axis_b * axis_b)
253-
A = 1.0 + (u2 / 16384) * (4096 + u2 * (-768 + u2 * (320 - 175 * u2)))
254-
B = (u2 / 1024) * (256 + u2 * (-128 + u2 * (74 - 47 * u2)))
255-
# Starting with the approx
256-
sigma = distance / (axis_b * A)
257-
last_sigma = 2.0 * sigma + 2.0 # something impossible
258-
259-
# Iterate the following 3 eqs until no sig change in sigma
260-
# two_sigma_m , delta_sigma
261-
while abs((last_sigma - sigma) / sigma) > 1.0e-9:
262-
two_sigma_m = 2 * sigma1 + sigma
263-
delta_sigma = (
264-
B
265-
* math.sin(sigma)
266-
* (
267-
math.cos(two_sigma_m)
268-
+ (B / 4)
269-
* (
270-
math.cos(sigma)
271-
* (
272-
-1
273-
+ 2 * math.pow(math.cos(two_sigma_m), 2)
274-
- (B / 6)
275-
* math.cos(two_sigma_m)
276-
* (-3 + 4 * math.pow(math.sin(sigma), 2))
277-
* (-3 + 4 * math.pow(math.cos(two_sigma_m), 2))
278-
)
279-
)
280-
)
281-
)
282-
last_sigma = sigma
283-
sigma = (distance / (axis_b * A)) + delta_sigma
284-
phi2 = math.atan2(
285-
(
286-
math.sin(u1) * math.cos(sigma)
287-
+ math.cos(u1) * math.sin(sigma) * math.cos(degrees)
288-
),
289-
(
290-
(1 - flattening)
291-
* math.sqrt(
292-
math.pow(sinalpha, 2)
293-
+ pow(
294-
math.sin(u1) * math.sin(sigma)
295-
- math.cos(u1) * math.cos(sigma) * math.cos(degrees),
296-
2,
297-
),
298-
)
299-
),
228+
radius: float,
229+
) -> BoundingBox:
230+
"""Get bounding box from radius and a point."""
231+
half_side_in_km = abs(radius) / 1000
232+
233+
lat = math.radians(latitude)
234+
lon = math.radians(longitude)
235+
236+
approx_earth_radius = 6371
237+
hypotenuse_distance = math.sqrt(2 * (math.pow(half_side_in_km, 2)))
238+
239+
lat_min = math.asin(
240+
math.sin(lat) * math.cos(hypotenuse_distance / approx_earth_radius)
241+
+ math.cos(lat)
242+
* math.sin(hypotenuse_distance / approx_earth_radius)
243+
* math.cos(225 * (math.pi / 180)),
300244
)
301-
lembda = math.atan2(
302-
(math.sin(sigma) * math.sin(degrees)),
303-
(
304-
math.cos(u1) * math.cos(sigma)
305-
- math.sin(u1) * math.sin(sigma) * math.cos(degrees)
306-
),
245+
lon_min = lon + math.atan2(
246+
math.sin(225 * (math.pi / 180))
247+
* math.sin(hypotenuse_distance / approx_earth_radius)
248+
* math.cos(lat),
249+
math.cos(hypotenuse_distance / approx_earth_radius)
250+
- math.sin(lat) * math.sin(lat_min),
307251
)
308-
C = (flattening / 16) * cosalpha_sq * (4 + flattening * (4 - 3 * cosalpha_sq))
309-
omega = lembda - (1 - C) * flattening * sinalpha * (
310-
sigma
311-
+ C
312-
* math.sin(sigma)
313-
* (
314-
math.cos(two_sigma_m)
315-
+ C * math.cos(sigma) * (-1 + 2 * math.pow(math.cos(two_sigma_m), 2))
316-
)
252+
253+
lat_max = math.asin(
254+
math.sin(lat) * math.cos(hypotenuse_distance / approx_earth_radius)
255+
+ math.cos(lat)
256+
* math.sin(hypotenuse_distance / approx_earth_radius)
257+
* math.cos(45 * (math.pi / 180)),
317258
)
318-
lembda2 = longitude + omega
319-
math.atan2(
320-
sinalpha,
321-
(
322-
-math.sin(u1) * math.sin(sigma)
323-
+ math.cos(u1) * math.cos(sigma) * math.cos(degrees)
324-
),
259+
lon_max = lon + math.atan2(
260+
math.sin(45 * (math.pi / 180))
261+
* math.sin(hypotenuse_distance / approx_earth_radius)
262+
* math.cos(lat),
263+
math.cos(hypotenuse_distance / approx_earth_radius)
264+
- math.sin(lat) * math.sin(lat_max),
325265
)
326-
phi2 = phi2 * 45.0 / pi_d4
327-
lembda2 = lembda2 * 45.0 / pi_d4
328-
return phi2, lembda2
329266

330-
@staticmethod
331-
def get_bounding_box(
332-
latitude: float,
333-
longitude: float,
334-
radius: float,
335-
) -> BoundingBox:
336-
"""Get bounding box from radius and a point."""
337-
north = OpenSky.calculate_point(latitude, longitude, radius, 0)
338-
east = OpenSky.calculate_point(latitude, longitude, radius, 90)
339-
south = OpenSky.calculate_point(latitude, longitude, radius, 180)
340-
west = OpenSky.calculate_point(latitude, longitude, radius, 270)
267+
rad2deg = math.degrees
268+
341269
return BoundingBox(
342-
min_latitude=min(north[0], south[0]),
343-
max_latitude=max(north[0], south[0]),
344-
min_longitude=min(east[1], west[1]),
345-
max_longitude=max(east[1], west[1]),
270+
min_latitude=rad2deg(lat_min),
271+
max_latitude=rad2deg(lat_max),
272+
min_longitude=rad2deg(lon_min),
273+
max_longitude=rad2deg(lon_max),
346274
)
347275

348276
async def close(self) -> None:

tests/test_radius.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@
2424
max_longitude=1.0,
2525
),
2626
),
27+
(
28+
45.0,
29+
0.0,
30+
111120,
31+
BoundingBox(
32+
min_latitude=44.0,
33+
max_latitude=46.0,
34+
min_longitude=-1.4,
35+
max_longitude=1.438,
36+
),
37+
),
2738
(
2839
360.0,
2940
0.0,
@@ -77,11 +88,3 @@ async def test_calculating_bounding_box(
7788
bounding_box.max_longitude,
7889
PRECISION,
7990
)
80-
81-
82-
async def test_calculating_direction() -> None:
83-
"""Test calculating direction."""
84-
second_point = OpenSky.calculate_point(0.0, 0.0, 25000.0, -180)
85-
assert second_point == (-0.22609235747829648, 2.7503115231199028e-17)
86-
second_point = OpenSky.calculate_point(0.0, 0.0, 25000.0, 361)
87-
assert second_point == (0.22605792234324162, 0.003919461063277522)

0 commit comments

Comments
 (0)