# Auf der Strecke

Beispiele für die Integration von Streckenwetter — Höhenwinde, PIREPs und AIRMET/SIGMET innerhalb eines Begrenzungsrahmens (Bounding Box).



Lauffähige Beispiele für Wetter &#x2A;*auf der Strecke (En-Route)** innerhalb eines geografischen &#x2A;*Begrenzungsrahmens (Bounding Box)**: [Höhenwinde](/docs/v31/winds-aloft), [PIREPs](/docs/v31/pireps) und [AIRMET/SIGMET](/docs/v31/airsigmet). Für Abläufe mit [METAR](https://en.wikipedia.org/wiki/METAR)/[TAF](https://en.wikipedia.org/wiki/Terminal_aerodrome_forecast) am Flughafen siehe [Am Flughafen](/docs/v31/weather-integration-airport).

| # | Frage                                                                                    | Verwendete Daten |
| - | ---------------------------------------------------------------------------------------- | ---------------- |
| 7 | Reiseflugwind auf FL180 nahe dem Abflug in **KJFK** (regionale US-Bbox)?                 | Höhenwinde       |
| 8 | Gibt es **dringende Pilotenmeldungen** (UUA) entlang der Route in den letzten 3 Stunden? | PIREPs           |
| 9 | Aktive **AIRMET**-Polygone, die die Bbox der Route schneiden?                            | AIRMET/SIGMET    |

<DocRequirements>
  <DocRequirementAuth />
</DocRequirements>

### Installation [#install]

Die folgenden Skripte verwenden `requests` für API-Aufrufe und `rich` für die Terminalausgabe. Einmalig installieren:

```bash
pip install requests rich
```

***

### 7. Reiseflugwind auf FL180 (Höhenwinde) [#7-cruise-wind-at-fl180-winds-aloft]

**Frage:** Welche Höhenwind-Stufe liegt nahe dem Abflug in **KJFK** am nächsten an FL180?

Höhenwinde sind **nur für die USA** verfügbar. Fragen Sie bei langen Routen **regionale Bboxes** entlang des US-Abschnitts ab (eine Box von Küste zu Küste ist langsam und führt oft zu `503`). Im Folgenden: eine enge Box rund um New York.

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

**Ausgabe:** Tabelle mit der Windstufe, die Ihrer Zielhöhe am nächsten liegt, von der ersten Station in der Bbox.

***

### 8. Dringende PIREPs entlang der Route [#8-urgent-pireps-along-the-route]

**Frage:** Wurden in den letzten 3 Stunden in der Bbox der Route **UUA**-Pilotenmeldungen (dringend) abgegeben?

[PIREPs](https://www.aviationweather.gov/help/data/#pirep) sind selten — eine leere Liste ist üblich. Wenn Meldungen vorhanden sind, zeigen Sie `UUA` vor routinemäßigen `UA`-Meldungen an.

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

**Ausgabe:** Panel, das zuerst dringende [PIREPs](https://www.aviationweather.gov/help/data/#pirep) und danach bis zu fünf Routinemeldungen auflistet — oder eine eindeutige Meldung „keine abgegeben“.

***

### 9. Aktive AIRMETs auf der Route [#9-active-airmets-on-the-route]

**Frage:*&#x2A; Schneiden derzeit &#x2A;*[AIRMET](https://www.aviationweather.gov/help/data/#airmet)**-Warnungen die Bbox der Route?

Verwenden Sie `type=airmet` für Bulletins zu IFR, Turbulenz und Vereisung, wie sie in der Flugdienstberatung (Dispatch) genutzt werden. Zeichnen Sie `observation.coords` auf einer Karte ein, sofern vorhanden.

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

**Ausgabe:** Tabelle der aktiven [AIRMETs](https://www.aviationweather.gov/help/data/#airmet) mit Gültigkeitszeitraum, Gefahrentyp und Anzahl der Polygoneckpunkte.

***

Siehe auch: [METAR](/docs/v31/weather-metar) · [TAF](/docs/v31/weather-taf) · [Höhenwinde](/docs/v31/winds-aloft) · [PIREPs](/docs/v31/pireps) · [AIRMET/SIGMET](/docs/v31/airsigmet) · [Fehlerbehandlung](/docs/v31/weather-integration-errors) · [Caching & Monitoring](/docs/v31/weather-integration-production)
