forecast

Wettervorhersage Applikation (Teil 2) - Wettervorhersage MET Norway implementieren

Wettervorhersage MET Norway implementieren

Quelle: MET Norway Weather API v.3 - Locationforecast (compact)

a) Die Daten asynchron in der Funktion loadWeather laden

const response = await fetch(url);
const jsondata = await response.json();
console.log(jsondata);

Wir sehen ein GeoJSON Objekt vom Typ Point mit Koordinaten und Seehöhe in geometry.coordinates, Metadaten der Attribute in properties.meta sowie den eigentlichen Vorhersagedaten im Array properties.timeseries. Jeder Eintrag dieses Array besitzt einen Zeitstempel time, die vorhergesagten Werte in data.instant.details sowie Wetteraussichten mit voraussichtlichen Niederschlagsmengen für die nächsten 1, 6 und 12 Stunden in data.next_n_hours

🔗 COMMIT (dieser COMMIT enthält leider auch den, zuvor vergessenen Schritt der Plugin Konfiguration von Leaflet velocity)

b) Einen Marker mit Popup in einem neuen Overlay vorbereiten

Bevor wir die Wettervorhersage für den Referenzpunkt visualisieren, hängen wir außerhalb der Funktion loadWeather das Overlay für die Wettervorhersage an die Layer control

layerControl.addOverlay(overlays.weather, "Wettervorhersage met.no");

Gleich danach defineren wir einen neuen L.circleMarker, geben ihm die vorläufigen Koordinaten [0,0], fügen ein Popup hinzu und hängen das Ganze an das soeben erstellte Overlay. Damit wir später auf den Marker und das Popup zugreifen können, merken wir uns den Marker in einer Variablen marker

let marker = L.circleMarker([
    47.267222, 11.392778
]).bindPopup("Wettervorhersage").addTo(overlays.weather);

🔗 COMMIT

Leider landeten Marker und Overlay im vorhergehenden Schritt innerhalb der Funktion, weshalb eine Verschiebung des Codes vor die Funktion nötig war

🔗 COMMIT

Marker und Overlay sind damit verfügbar und können innerhalb der Funktion loadWeather befüllt werden

c) Marker positionieren

Die Positionierung des Markers erledigt .setLatLng() für uns. Die Kordinaten entnehmen wir der Geometrie des GeoJSON-Objekts

// Marker positionieren
marker.setLatLng([
    jsondata.geometry.coordinates[1],
    jsondata.geometry.coordinates[0]
]);

🔗 COMMIT

d) Aktuelle Wetterwere in das Popup schreiben

Die aktuellen Wetterwerte finden wir im ersten Eintrag des jsondata.properties.timeseries Array und dort in jsondata.properties.timeseries[0].data.instant.details - ein shortcut zu diesen tief verschachtelten Details bietet sich an:

let details = jsondata.properties.timeseries[0].data.instant.details;

Danach definieren wir in einer Variablen popup mit Template-Syntax die aktuellen Wetterwerte als ungeordnete Liste. Die Windgschwindigkeit rechnen wir in km/h um.

let popup = `
    <ul>
        <li>Luftdruck: ${details.air_pressure_at_sea_level} (hPa)</li>
        <li>Luftemperatur: ${details.air_temperature} (°C)</li>
        <li>Bewölkung: ${details.cloud_area_fraction} (%)</li>
        <li>Niederschlag: ${details.precipitation_amount} (mm)</li>
        <li>Relative Luftfeuchtigkeit: ${details.relative_humidity} (%)</li>
        <li>Windrichtung: ${details.wind_from_direction} (°)</li>
        <li>Windgeschwindigkeit: ${details.wind_speed * 3.6} (km/h)</li>
    </ul>
`;

Den Popupinhalt des Markers können wir schließlich über die Methode .setPopupContent() setzen und mit .openPopup() das Popup anzeigen.

marker.setPopupContent(popup).openPopup();

🔗 COMMIT

e) Überschrift mit Datum der Wetterwerte beim Popup ergänzen

Ähnlich wie beim Vorhersagezeitpunkt der ECMWF Windvorhersage, können wir auch den Zeitpunkt der Wettervorhersage bestimmen und formatieren. Wir finden das Datum für die Details des Popups im ersten Eintrag des timeseries Arrays im Attribut time, definieren ein echtes Datum damit und formatieren es wieder über unsere formatDate Funktion.

let forecastDate = new Date(jsondata.properties.timeseries[0].time);
let forecastLabel = formatDate(forecastDate);

Danach können wir beim Popup eine Überschrift mit Datum ergänzen

let popup = `
    <strong>Wettervorhersage für ${forecastLabel}</strong>
    <!-- Liste -->
`;

🔗 COMMIT

f) Wettervorhersage für jeden Ort der Welt implementieren

Alles was wir für unser Vorhersagepopup benötigen, bekommen wir vom Locationforecast (compact) indem wir die Koordinaten LAT/LNG übergeben. Leaflet kann uns onclick für jeden Punkt der Karte diese Werte liefern und deshalb ist es einfach, für jeden Punkt der Erde ein Popup mit der Vorhersage zu generieren. Wir müssen nur auf das Klicken auf die Karte reagieren und unsere Funktion loadWeather mit der passenden URL füttern. In Leafletsprache sieht das (ganz am Ende des Skripts) so aus:

map.on("click", function(evt) {
    console.log(evt);
});

Im Klickevent finden wir in evt.latlng.lat und evt.latlng.lng die Koordinaten des geklickten Punkts aus denen wir die URL für https://api.met.no/ generieren können. Der Aufruf der Funktion loadWeather mit dieser URL platziert den Marker neu und holt sich die Vorhersage für diesen Punkt

// auf Klick auf die Karte reagieren
map.on("click", function(evt) {
    let url = `https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=${evt.latlng.lat}&lon=${evt.latlng.lng}`;
    loadWeather(url);
});

🔗 COMMIT

g) Wettersymbole für die nächsten 24 Stunden in 3 Stunden Abständen hinzufügen

Nachdem der timeseries Array aus 89 Einträge in Stunden- und 6 Stundenabständen besteht, können wir die Witterungsprognose für die nächsten 24 Stunden in 3 Stundenschritten implementieren. Wir verwenden dazu jeweils das Wettersymbol in data.next_1_hours.summary.symbol_code. Für alle dort möglichen Werte stellt uns der Locationforecast unter dem Link WeatherIcon 2.0 service passende Icons zur Verfügung, die wir uns als erstes in einem Unterverzeichnis icons/ speichern. Wir verwenden die SVG-Version da sie skalierbar ist und weniger Speicherplatz benötigt

🔗 COMMIT

Am Beispiel des ersten Eintrags im timeseriesArray finden wir den Dateinamen des gewünschten Symbols für die aktuelle Wetterlage im längsten verschachtelten Objekt, das wir bisher kennengelernt haben ;-) Wir speichern den Wert von jsondata.properties.timeseries[0].data.next_1_hours.summary.symbol_code in einer Variablen symbol und fügen ein <img> Element mit diesem Icon über += zum Popup hinzu. Ein style-Attribut verkleinert die Breite des Icons auf 32px Breite. Den Code schreiben wir direkt vor marker.setPopupContent()

    // Wettericon
    let symbol = jsondata.properties.timeseries[0].data.next_1_hours.summary.symbol_code;
    popup += `<img src="icons/${symbol}.svg" alt="${symbol}" style="width:32px">`;

🔗 COMMIT

Um alle Icons der nächsten 24 Stunden in 3 Stunden Schritten anzuzeigen, können wir in einer klassischen for-Schleife mit einer Schleifenvariable i den timeseries Array abarbeiten. Nach jedem Schleifendurchlauf erhöhen wir die Zählervariable um 3 (Stunden) und beenden die Schleife beim Wert 24. Wir verpacken also den Code für unser Icon der aktuellen Wetterlage in diese for-Schleife und ändern timeseries[0] auf timeseries[i]

// Wettericons
for (let i=0; i <= 24; i+=3) {
    let symbol = jsondata.properties.timeseries[i].data.next_1_hours.summary.symbol_code;
    popup += `<img src="icons/${symbol}.svg" alt="${symbol}" style="width:32px">`;
}

🔗 COMMIT

Zur besseren Lesbarkeit ergänzen wir bei den Symbolen das jeweilige Datum als Tooltip über ein title-Attribut. Das Datum finden wir in jsondata.properties.timeseries[i].time - wir formatieren es analog zum Vorhersagezeitpunkt mit der formatdate Funktion und setzten es als title-Attribut beim Symbolbild ein

let forecastDate = new Date(jsondata.properties.timeseries[i].time);
let forecastLabel = formatDate(forecastDate);
popup += `<img src="icons/${symbol}.svg" title="${forecastLabel}" alt="${symbol}" style="width:32px">`;

🔗 COMMIT

Kosmetik: die Windgeschwindigkeit kann noch ein .toFixed(1) vertragen

<li>Windgeschwindigkeit: ${(details.wind_speed * 3.6).toFixed(1)} (km/h)</li>

🔗 COMMIT