Save up to 17% with yearly billing

See pricing

Блочное время

Готовые к запуску примеры оценки блочного времени (от гейта до гейта) с помощью ML-модели SkyLink. Подробности о модели, метриках точности и поддерживаемых типах ВС см. на странице ML-оценка времени полёта.

#ВопросИспользуемые данные
1Оценить блочное время KJFK → KLAX на B738GET /ml/flight-time
2Сравнить время полёта по 4 маршрутам из EGLLGET /ml/flight-time

Требования

Авторизация
x-api-key в каждом запросе (прямая подписка). Подробнее →

Установка

Приведённые ниже скрипты используют requests для вызовов API и rich для вывода в терминал. Установите один раз:

pip install requests rich

1. Оценка блочного времени: 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 для каждой пары городов.