На маршруте
Готовые к запуску примеры работы с погодой на маршруте в пределах географического ограничивающего прямоугольника (bounding box): Ветер на высотах, PIREP и AIRMET/SIGMET. Сценарии с METAR/TAF для аэропортов см. в разделе В аэропорту.
| # | Вопрос | Используемые данные |
|---|---|---|
| 7 | Ветер на крейсерском эшелоне FL180 в районе вылета из KJFK (региональный bbox в США)? | Ветер на высотах |
| 8 | Есть ли срочные донесения пилотов (UUA) вдоль маршрута за последние 3 часа? | PIREP |
| 9 | Действующие полигоны AIRMET, пересекающие bbox маршрута? | AIRMET/SIGMET |
Требования
x-api-key в каждом запросе (прямая подписка). Подробнее →Установка
Приведённые ниже скрипты используют requests для вызовов API и rich для вывода в терминал. Установите их один раз:
pip install requests rich7. Ветер на крейсерском эшелоне FL180 (ветер на высотах)
Вопрос: какой ближайший к FL180 уровень ветра на высотах в районе вылета из KJFK?
Ветер на высотах доступен только для США. Для длинных маршрутов запрашивайте региональные bbox вдоль участка над США (прямоугольник от побережья до побережья обрабатывается медленно и часто приводит к 503). Ниже — компактный прямоугольник вокруг Нью-Йорка.
import os
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"
# Regional box around KJFK - reliable for FB Winds (US only)
NYC_BBOX = "38,-76,42,-71"
TARGET_ALT_FT = 18_000
def fetch_winds_aloft(bbox: str, forecast: int = 12) -> dict | None:
r = requests.get(
f"{BASE}/weather/winds-aloft",
headers=HEADERS,
params={"bbox": bbox, "forecast": forecast, "level": "low"},
timeout=(10, 20),
)
if r.status_code in (404, 503):
return None
r.raise_for_status()
return r.json()
def print_cruise_wind(bbox: str, label: str, target_ft: int = TARGET_ALT_FT) -> None:
data = fetch_winds_aloft(bbox)
if not data:
console.print("[yellow]Winds aloft unavailable or no stations - retry or widen bbox.[/]")
return
if not data.get("stations"):
console.print("[yellow]No FB winds stations in this bbox.[/]")
return
station = data["stations"][0]
levels = station.get("winds") or []
if not levels:
console.print(f"[yellow]Station {station['station']} returned no wind levels.[/]")
return
best = min(levels, key=lambda w: abs(w["altitude_ft"] - target_ft))
table = Table(title=f"Cruise wind near FL{target_ft // 100} - {label}")
table.add_column("Field", style="cyan")
table.add_column("Value", justify="right")
table.add_row("Bbox", bbox)
table.add_row("Station", station["station"])
table.add_row("Level used", f"{best['altitude_ft']} ft MSL")
table.add_row("Direction", f"{best.get('wind_direction') or '-'}° true")
table.add_row("Speed", f"{best.get('wind_speed_kt') or '-'} kt")
temp = best.get("temperature_c")
table.add_row("Temperature", f"{temp} °C" if temp is not None else "not reported")
table.add_row("Valid time", data.get("valid_time") or "-")
console.print(table)
if __name__ == "__main__":
print_cruise_wind(NYC_BBOX, "KJFK departure area")Результат: таблица с ближайшим к целевой высоте уровнем ветра для первой станции в bbox.
8. Срочные PIREP вдоль маршрута
Вопрос: поступали ли за последние 3 часа в пределах bbox маршрута UUA (срочные) донесения пилотов?
PIREP поступают редко — пустой список здесь обычен. Если донесения есть, выводите UUA выше обычных донесений UA.
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"
KJFK = (40.6413, -73.7781)
KLAX = (33.9425, -118.4081)
def route_bbox(dep, arr, pad_deg: float = 2.0) -> str:
dep_lat, dep_lon = dep
arr_lat, arr_lon = arr
return (
f"{min(dep_lat, arr_lat) - pad_deg},{min(dep_lon, arr_lon) - pad_deg},"
f"{max(dep_lat, arr_lat) + pad_deg},{max(dep_lon, arr_lon) + pad_deg}"
)
def fetch_pireps(bbox: str, hours: int = 3) -> dict:
r = requests.get(
f"{BASE}/weather/pireps",
headers=HEADERS,
params={"bbox": bbox, "hours": hours},
timeout=(10, 20),
)
r.raise_for_status()
return r.json()
def print_urgent_pireps(dep, arr) -> None:
bbox = route_bbox(dep, arr)
data = fetch_pireps(bbox, hours=3)
reports = data.get("reports") or []
urgent = [r for r in reports if r.get("report_type") == "UUA"]
routine = [r for r in reports if r.get("report_type") != "UUA"]
if not reports:
console.print(Panel(
f"No PIREPs in bbox {bbox} (last 3 h).\n"
"This is normal on quiet routes - not an error.",
title="PIREPs - none filed",
style="blue",
))
return
lines = []
for r in urgent + routine[:5]:
tag = "[red]UUA[/]" if r.get("report_type") == "UUA" else "UA"
turb = r.get("turbulence") or "-"
icing = r.get("icing") or "-"
lines.append(f"{tag} {r.get('location') or '?'} @ {r.get('altitude') or '?'} - turb: {turb}, ice: {icing}")
console.print(Panel(
"\n".join(lines),
title=f"PIREPs in route bbox ({data['total']} total, {len(urgent)} urgent)",
))
if __name__ == "__main__":
print_urgent_pireps(KJFK, KLAX)Результат: панель, где сначала перечислены срочные PIREP, а затем до пяти обычных донесений; либо понятное сообщение «донесений нет».
9. Действующие AIRMET на маршруте
Вопрос: пересекают ли сейчас bbox маршрута какие-либо предупреждения AIRMET?
Используйте type=airmet для бюллетеней об IFR, турбулентности и обледенении в формате, привычном для службы диспетчерского обеспечения полётов. Если observation.coords присутствует, отрисуйте его на карте.
import os
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"
KJFK = (40.6413, -73.7781)
KLAX = (33.9425, -118.4081)
def route_bbox(dep, arr, pad_deg: float = 2.0) -> str:
dep_lat, dep_lon = dep
arr_lat, arr_lon = arr
return (
f"{min(dep_lat, arr_lat) - pad_deg},{min(dep_lon, arr_lon) - pad_deg},"
f"{max(dep_lat, arr_lat) + pad_deg},{max(dep_lon, arr_lon) + pad_deg}"
)
def fetch_airsigmets(bbox: str, advisory_type: str = "airmet") -> dict:
r = requests.get(
f"{BASE}/weather/airsigmet",
headers=HEADERS,
params={"bbox": bbox, "type": advisory_type},
timeout=(10, 25),
)
r.raise_for_status()
return r.json()
def print_route_airsigmets(dep, arr) -> None:
bbox = route_bbox(dep, arr)
data = fetch_airsigmets(bbox)
reports = data.get("reports") or []
if not reports:
console.print(f"[green]No active AIRMETs intersect bbox {bbox} - fair weather or outside advisory area.[/]")
return
table = Table(title=f"Active AIRMETs - {len(reports)} in bbox")
table.add_column("Type")
table.add_column("Valid")
table.add_column("Hazard")
table.add_column("Polygon points")
for r in reports:
obs = r.get("observation") or {}
hazard = obs.get("type") or r.get("report_type") or "-"
floor = obs.get("floor") or "-"
ceiling = obs.get("ceiling") or "-"
coords = obs.get("coords") or []
valid = f"{r.get('start_time') or '?'} → {r.get('end_time') or '?'}"
table.add_row(
r.get("report_type") or "AIRMET",
valid,
f"{hazard} ({floor}–{ceiling})",
str(len(coords)),
)
console.print(table)
console.print("[dim]Render observation.coords as map polygons; hide expired advisories past end_time.[/dim]")
if __name__ == "__main__":
print_route_airsigmets(KJFK, KLAX)Результат: таблица действующих AIRMET с окном действия, типом явления и числом вершин полигона.
См. также: METAR · TAF · Ветер на высотах · PIREP · AIRMET/SIGMET · Обработка ошибок · Кэширование и мониторинг