Initial Vibe coded project
This commit is contained in:
commit
0891cf12a5
21 files changed
+2830
No files matched your search
@@ -0,0 +1,85 @@
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
from itertools import groupby, pairwise
|
||||
|
||||
from flight_map.curves import Cubic, interpolate
|
||||
from flight_map.geo import Projection
|
||||
from flight_map.storage import Observation
|
||||
from flight_map.terrain import Elevation
|
||||
|
||||
|
||||
@dataclass
|
||||
class Track:
|
||||
observations: list[Observation]
|
||||
curves: list[Cubic]
|
||||
average_height: float | None
|
||||
start: float
|
||||
end: float
|
||||
|
||||
|
||||
def split_tracks(observations: list[Observation], max_gap: float) -> list[list[Observation]]:
|
||||
if not math.isfinite(max_gap) or max_gap <= 0:
|
||||
raise ValueError("Maximum track gap must be positive.")
|
||||
result = []
|
||||
for _, aircraft in groupby(
|
||||
sorted(observations, key=lambda o: (o.icao24, o.timestamp)), key=lambda o: o.icao24
|
||||
):
|
||||
current: list[Observation] = []
|
||||
for item in aircraft:
|
||||
if item.on_ground:
|
||||
if len(current) >= 2:
|
||||
result.append(current)
|
||||
current = []
|
||||
continue
|
||||
if current and (
|
||||
item.timestamp - current[-1].timestamp > max_gap
|
||||
or item.timestamp <= current[-1].timestamp
|
||||
or item.callsign != current[-1].callsign
|
||||
):
|
||||
if len(current) >= 2:
|
||||
result.append(current)
|
||||
current = []
|
||||
current.append(item)
|
||||
if len(current) >= 2:
|
||||
result.append(current)
|
||||
return result
|
||||
|
||||
|
||||
def make_tracks(
|
||||
observations: list[Observation],
|
||||
projection: Projection,
|
||||
elevation: Elevation,
|
||||
ground: float | None,
|
||||
max_gap: float,
|
||||
) -> list[Track]:
|
||||
result = []
|
||||
for items in split_tracks(observations, max_gap):
|
||||
points = [projection.point(o.longitude, o.latitude) for o in items]
|
||||
curves = interpolate(points)
|
||||
if all(curve.start == curve.end for curve in curves):
|
||||
continue
|
||||
total_time = 0.0
|
||||
altitude_integral = 0.0
|
||||
missing_height = ground is None
|
||||
start, end = math.inf, -math.inf
|
||||
for index, (first, last) in enumerate(pairwise(items)):
|
||||
duration = last.timestamp - first.timestamp
|
||||
h0, h1 = elevation.aircraft_altitude(first), elevation.aircraft_altitude(last)
|
||||
for f0, f1 in curves[index].inside_intervals(projection.rectangle.bounds):
|
||||
inside_duration = duration * (f1 - f0)
|
||||
total_time += inside_duration
|
||||
start = min(start, first.timestamp + f0 * duration)
|
||||
end = max(end, first.timestamp + f1 * duration)
|
||||
if h0 is None or h1 is None:
|
||||
missing_height = True
|
||||
else:
|
||||
entry = h0 + (h1 - h0) * f0
|
||||
exit_height = h0 + (h1 - h0) * f1
|
||||
altitude_integral += (entry + exit_height) / 2 * inside_duration
|
||||
if total_time == 0:
|
||||
continue
|
||||
average = None
|
||||
if not missing_height and ground is not None:
|
||||
average = altitude_integral / total_time - ground
|
||||
result.append(Track(items, curves, average, start, end))
|
||||
return sorted(result, key=lambda track: (track.start, track.observations[0].icao24))
|
||||
Reference in new issue
Block a user