# Расстояние

Интеграция расчёта расстояния и пеленга — расчёты по ортодромии между аэропортами или координатами.



Готовые к запуску примеры расчёта [расстояния по ортодромии](https://en.wikipedia.org/wiki/Great-circle_distance) через эндпоинт [Distance](/docs/v31/distance). Рассмотрены измерение одного маршрута и сравнение нескольких маршрутов для планирования маршрутной сети.

| # | Вопрос                                                                                  | Используемые данные |
| - | --------------------------------------------------------------------------------------- | ------------------- |
| 1 | Расстояние по ортодромии между двумя аэропортами для планирования маршрута              | Distance            |
| 2 | Сравнение расстояний нескольких маршрутов: ранжирование маршрутов из EGLL по расстоянию | Distance            |

<DocRequirements>
  <DocRequirementAuth />
</DocRequirements>

### Установка [#install]

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

```bash
pip install requests rich
```

***

### 1. Расстояние маршрута между двумя аэропортами [#1-route-distance-between-two-airports]

**Вопрос:** Каково [расстояние по ортодромии](https://en.wikipedia.org/wiki/Great-circle_distance) между KJFK и KLAX и каков начальный пеленг?

```python
import os

import requests
from rich.console import Console
from rich.panel import Panel
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"


def fetch_distance(from_icao: str, to_icao: str) -> dict | None:
    r = requests.get(
        f"{BASE}/distance",
        headers=HEADERS,
        params={"from_icao": from_icao, "to_icao": to_icao},
        timeout=(10, 15),
    )
    if r.status_code == 404:
        return None
    r.raise_for_status()
    return r.json()


def main() -> None:
    data = fetch_distance("KJFK", "KLAX")
    if not data:
        console.print("[yellow]No distance data returned.[/]")
        return

    table = Table(title="Route Distance")
    table.add_column("Field", style="cyan", min_width=22)
    table.add_column("Value")

    table.add_row("Origin", data.get("from_airport_name", data.get("from_icao", "—")))
    table.add_row("Destination", data.get("to_airport_name", data.get("to_icao", "—")))
    table.add_row("Distance (nm)", f"{data.get('distance_nm', '—')} nm")
    table.add_row("Distance (km)", f"{data.get('distance_km', '—')} km")
    table.add_row("Distance (mi)", f"{data.get('distance_mi', '—')} mi")
    table.add_row("Initial bearing", f"{data.get('bearing_deg', '—')}° true")

    console.print(table)
    console.print(Panel(
        "Distance shown is great-circle (shortest path on a sphere). "
        "Actual filed routes are typically longer due to airways, wind routing, and restricted airspace.",
        title="Note",
        style="dim",
    ))


if __name__ == "__main__":
    main()
```

**Результат:** Таблица с названиями аэропортов вылета и назначения, расстоянием в морских милях, километрах и сухопутных милях, а также начальным истинным пеленгом. Примечание поясняет, что [расстояние по ортодромии](https://en.wikipedia.org/wiki/Great-circle_distance) отличается от фактических маршрутов в поданных планах полёта.

***

### 2. Ранжирование нескольких маршрутов из EGLL по расстоянию [#2-multi-route-distance-ranking-from-egll]

**Вопрос:** Какие из четырёх направлений из лондонского Хитроу самые короткие, а какие самые длинные?

Скрипт параллельно запрашивает все маршруты, а затем сортирует их по возрастанию расстояния в морских милях.

```python
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"

ORIGIN = "EGLL"
DESTINATIONS: list[tuple[str, str]] = [
    ("KJFK", "New York"),
    ("OMDB", "Dubai"),
    ("YSSY", "Sydney"),
    ("RJTT", "Tokyo"),
]


def fetch_distance(from_icao: str, to_icao: str) -> dict | None:
    r = requests.get(
        f"{BASE}/distance",
        headers=HEADERS,
        params={"from_icao": from_icao, "to_icao": to_icao},
        timeout=(10, 15),
    )
    if r.status_code == 404:
        return None
    r.raise_for_status()
    return r.json()


def main() -> None:
    results: list[tuple[str, str, dict]] = []

    with concurrent.futures.ThreadPoolExecutor(max_workers=4) as ex:
        futures = {
            ex.submit(fetch_distance, ORIGIN, icao): (icao, city)
            for icao, city in DESTINATIONS
        }
        for fut, (icao, city) in futures.items():
            data = fut.result()
            if data:
                results.append((icao, city, data))

    results.sort(key=lambda x: x[2].get("distance_nm", float("inf")))

    table = Table(title=f"Routes from {ORIGIN} — sorted by distance")
    table.add_column("Destination", style="cyan")
    table.add_column("City")
    table.add_column("Distance (nm)", justify="right")
    table.add_column("Distance (km)", justify="right")
    table.add_column("Bearing", justify="right")

    for icao, city, data in results:
        table.add_row(
            icao,
            city,
            str(data.get("distance_nm", "—")),
            str(data.get("distance_km", "—")),
            f"{data.get('bearing_deg', '—')}°",
        )

    console.print(table)


if __name__ == "__main__":
    main()
```

**Результат:** Таблица четырёх маршрутов из EGLL, отсортированных по возрастанию расстояния в морских милях, с кодом ICAO аэропорта назначения, названием города, расстоянием в nm и km и начальным пеленгом.
