Блочное время
Готовые к запуску примеры оценки блочного времени (от гейта до гейта) с помощью ML-модели SkyLink. Подробности о модели, метриках точности и поддерживаемых типах ВС см. на странице ML-оценка времени полёта.
| # | Вопрос | Используемые данные |
|---|---|---|
| 1 | Оценить блочное время KJFK → KLAX на B738 | GET /ml/flight-time |
| 2 | Сравнить время полёта по 4 маршрутам из EGLL | GET /ml/flight-time |
Требования
x-api-key в каждом запросе (прямая подписка). Подробнее →Установка
Приведённые ниже скрипты используют requests для вызовов API и rich для вывода в терминал. Установите один раз:
pip install requests rich1. Оценка блочного времени: KJFK → KLAX на B738
Вопрос: каково расчётное блочное время (от гейта до гейта) для 737-800 на маршруте KJFK–KLAX?
Модель возвращает расчётное время и диапазон min/max, полученный на основе исторических данных (GradientBoosting, R²≈0.98). Ветер не моделируется — фактическое время будет отличаться в зависимости от маршрутизации и условий.
import os
import requests
from rich.console import Console
from rich.panel import Panel
console = Console()
HEADERS = {
"X-RapidAPI-Key": os.getenv("RAPIDAPI_KEY", "YOUR_RAPIDAPI_KEY"),
"X-RapidAPI-Host": "skylink-api.p.rapidapi.com",
}
BASE = "https://skylink-api.p.rapidapi.com"
def fetch_flight_time(
origin: str, destination: str, aircraft: str | None = None
) -> dict | None:
params: dict[str, str] = {"from": origin, "to": destination}
if aircraft:
params["aircraft"] = aircraft
r = requests.get(
f"{BASE}/ml/flight-time",
headers=HEADERS,
params=params,
timeout=(10, 15),
)
if r.status_code == 404:
return None
r.raise_for_status()
return r.json()
def fmt_mins(minutes: int | None) -> str:
if minutes is None:
return "—"
return f"{minutes // 60}h {minutes % 60:02d}m"
def main() -> None:
data = fetch_flight_time("KJFK", "KLAX", aircraft="B738")
if not data:
console.print("[red]No estimate returned.[/red]")
return
origin = data.get("origin", "KJFK")
dest = data.get("destination", "KLAX")
aircraft = data.get("aircraft_type", "B738")
est_display = data.get("estimated_hours_display") or fmt_mins(
data.get("estimated_minutes")
)
min_m = data.get("min_minutes")
max_m = data.get("max_minutes")
distance = data.get("distance_nm")
range_str = f"{fmt_mins(min_m)} – {fmt_mins(max_m)}"
body = (
f"Route: [bold]{origin} → {dest}[/bold]\n"
f"Aircraft: {aircraft}\n"
f"Estimated: [bold green]{est_display}[/bold green]\n"
f"Range: {range_str}\n"
f"Distance: {distance:,.1f} nm" if distance else ""
)
console.print(Panel(body, title="Block Time Estimate", style="blue"))
console.print(
"[dim]Estimate is statistical average for this route/type — "
"actual time varies with winds and routing.[/dim]"
)
if __name__ == "__main__":
main()Результат: панель с маршрутом, типом ВС, расчётным блочным временем (в формате для отображения), диапазоном min–max и расстоянием по ортодромии в морских милях.
2. Сравнение маршрутов из EGLL
Вопрос: как соотносится время полёта из лондонского Хитроу по четырём дальнемагистральным направлениям?
Запросите все четыре маршрута параллельно и отсортируйте по расчётному времени по возрастанию. Полезно для специалистов по расписаниям, выявляющих короткие окна для стыковок.
import os
import concurrent.futures
import requests
from rich.console import Console
from rich.table import Table
console = Console()
HEADERS = {
"X-RapidAPI-Key": os.getenv("RAPIDAPI_KEY", "YOUR_RAPIDAPI_KEY"),
"X-RapidAPI-Host": "skylink-api.p.rapidapi.com",
}
BASE = "https://skylink-api.p.rapidapi.com"
ROUTES = [
("EGLL", "KJFK"),
("EGLL", "OMDB"),
("EGLL", "YSSY"),
("EGLL", "RJTT"),
]
def fetch_flight_time(origin: str, destination: str) -> tuple[str, str, dict | None]:
try:
r = requests.get(
f"{BASE}/ml/flight-time",
headers=HEADERS,
params={"from": origin, "to": destination},
timeout=(10, 15),
)
if r.status_code == 404:
return origin, destination, None
r.raise_for_status()
return origin, destination, r.json()
except Exception as exc: # noqa: BLE001
console.print(f"[red]{origin}→{destination}: {exc}[/red]")
return origin, destination, None
def fmt_mins(minutes: int | None) -> str:
if minutes is None:
return "—"
return f"{minutes // 60}h {minutes % 60:02d}m"
def main() -> None:
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool:
results = list(pool.map(lambda r: fetch_flight_time(*r), ROUTES))
valid = [
(orig, dest, d)
for orig, dest, d in results
if d and d.get("estimated_minutes") is not None
]
valid.sort(key=lambda x: x[2].get("estimated_minutes", 0))
table = Table(
title="EGLL Long-haul Route Comparison (model default aircraft)",
show_lines=True,
)
table.add_column("Route", style="cyan", no_wrap=True)
table.add_column("Distance (nm)", justify="right")
table.add_column("Est. Time", justify="center", style="bold")
table.add_column("Min", justify="center")
table.add_column("Max", justify="center")
for orig, dest, d in valid:
distance = d.get("distance_nm")
dist_str = f"{distance:,.0f}" if distance else "—"
table.add_row(
f"{orig} → {dest}",
dist_str,
fmt_mins(d.get("estimated_minutes")),
fmt_mins(d.get("min_minutes")),
fmt_mins(d.get("max_minutes")),
)
console.print(table)
console.print(
"[dim]Statistical estimates — wind not modelled. "
"Routes sorted by estimated time ascending.[/dim]"
)
if __name__ == "__main__":
main()Результат: таблица, отсортированная от самого короткого маршрута к самому длинному, с маршрутом, расстоянием в морских милях, расчётным временем и историческим диапазоном min/max для каждой пары городов.