aws-tirol

Wetterstationen Tirol HOWTO (Teil 2) - Temperatur Layer mit L.divIcon() und COLORS Objekt

1. die Funktion drawTemperature() vorbereiten

Wir erstellen zuerst die Funktion drawTemperature als Kopie von drawStations. Einzige Änderung (neben dem Namen der Funktion) ist die letzte Zeile, bei der wir die Marker an overlays.temperature hängen.

// Temperaturdaten
let drawTemperature = function(geojson) {
    L.geoJSON(geojson, {
        pointToLayer: function(geoJsonPoint, latlng) {
            let popup = `
                ${geoJsonPoint.properties.name} (${geoJsonPoint.geometry.coordinates[2]}m)
            `;
            return L.marker(latlng, {
                icon: L.icon({
                    iconUrl: "icons/wifi.png",
                    iconAnchor: [16, 37],
                    popupAnchor: [0, -37]
                })
            }).bindPopup(popup);
        }
    }).addTo(overlays.temperature);
}

Damit der Layer gezeigt wird, müssen wir drawTemperature(geojson) in unserer async function loadData(url) noch aufrufen

async function loadData(url) {
    let response = await fetch(url);
    let geojson = await response.json();

    drawStations(geojson);
    drawTemperature(geojson);
}

🔗 COMMIT

Damit wir beim Entwickeln nur unseren Temperatur Layer sehen, zeigen wir ihn statt dem Stationslayer beim Laden an. Den Kommentar der dort steht änderen wird gleich mit.

// diesen Layer beim Laden anzeigen
overlays.temperature.addTo(map);

🔗 COMMIT

2. L.divIcon() für Marker mit Text verwenden

Den Code der Option icon ersetzen wir mit einem L.divIcon(), dessen Optionen-Objekt aus zwei Key/Value Paaren besteht:

icon: L.divIcon({
    className: "aws-div-icon",
    html: `<span>${geoJsonPoint.properties.LT}</span>`
})

🔗 COMMIT

3. Den Text Marker in main.css stylen

Über die Klasse .aws-div-icon können wir die Texte des <span> Elements im Stylesheet main.css stylen:

aws-div-icon span {
    font-size: 1.25em;
    font-weight: bold;
    padding: 0.3em;
    border: 1px solid silver;
    text-shadow:
        -1px -1px 0 white,
        -1px 1px 0 white,
        1px -1px 0 white,
        1px 1px 0 white
} 

🔗 COMMIT

Die einzelnen CSS Regeln sind selbsterklärend, lediglich die text-shadow Anweisung ist für uns in dieser Form neu. Sie sorgt dafür, dass auf allen Seiten des Textes ein weißer Schatten entsteht, der den Text auch auf farbigem Hintergrund (wie wir ihn später implementieren werden) gut lesbar macht

Zwei Verbesserungen müssen wir in main.js noch implementieren. Vorallem die Stationen mit undefined bereiten uns Probleme, wir müssen sie daher beim Zeichnen ignorieren. Leaflet stellt uns dafür die GeoJSON Option filter zur Verfügung

GeoJSON Objekte beim Zeichnen filtern

Genau wie pointToLayer ist filter eine Funktion, die für jeden Marker ausgeführt wird. Liefert sie true zurück, wird der Marker gezeichnet. In der Funktion können wir auf die Attribute unseres Markers zugreifen und so entscheiden, ob wir true zurückgeben möchten. Wir schreiben die Option oberhalb von pointToLayer und liefern true zurück, wenn die Temperatur zwischen -50° und +50° liegt:

filter: function(geoJsonPoint) {
    if (geoJsonPoint.properties.LT > -50 && geoJsonPoint.properties.LT < 50) {
        return true;
    }
}

Neu bei dieser if-Abfrage ist die Verknüpfung zweier Bedingungen mit dem Logical AND (&&) Operator

🔗 COMMIT

Damit verschwinden Stationen ohne Temperatur und wir können die zweite Verbesserung angehen - die Zahl der Nachkommastellen mit der Javascript Methode .toFixed auf eine Nachkommastelle festlegen:

html: `<span>${geoJsonPoint.properties.LT.toFixed(1)}</span>`

🔗 COMMIT

Dieser Schritt hätte ohne das Ausschließen von Stationen ohne Temperatur unser Skript gestoppt, denn der Versuch den Wert undefined auf eine Nachkommastelle zu formatieren wäre natürlich gescheitert.

4. Temperaturwerte in Farben umsetzen

a) Farb-Objekt COLORS mit Schwellen und Farben

Zur Umsetzung der Temperaturwerte in Farben der Rechtecke, definieren wir zunächst in einer neuen Javascript-Datei colors.js ein COLORS-Objekt mit Farbwerten und dazugehörigen min/max Schwellen. Visual Studio Code hilft uns beim Erstellen im <head> Bereich von index.html mit dem Baustein script:src und STRG-Klick auf den generierten Link. Den Kommentar dort erweiteren wir entsprechend.

<!-- eigene Stile, Farben und Hauptskript -->
<link rel="stylesheet" href="main.css">
<script src="colors.js"></script>
<script defer src="main.js"></script>

Die Farben mit Schwellen “leihen” wir uns von der Temperaturkarte von Lawinen.report. Mit Rechtsklick / Untersuchen bei den farbigen Legendenkästchen können wir die CSS Farbwerte der einzelnen Kästchen ablesen. Wir notieren sie als color mit min/max Schwellen in unserem COLORS-Objekt das a) eine Konstante ist, b) aus Objekten je nach Layern und c) darin aus einem Array von Objekten für die Definition besteht - so sieht es aus:

const COLORS = {
    temperature: [{
        min: -50,
        max: -25,
        color: "#9f80ff"
    }, {
        min: -25,
        max: -20,
        color: "#784cff"
    }, {
        // und so weiter
    }

    ]
};

🔗 COMMIT

Das vorläufige Resultat ist in colors.js sichtbar. Später werden wir auch noch Farbpaletten für die anderen Layer hinzufügen …

b) Funktion getColor(value, ramp) definieren

Nachdem wir den Task eine Farbe für einen Wert nach Schwellen ermitteln noch öfters brauchen werden, definieren wir eine Funktion getColor, die das für uns übernehmen wird. Sie erwartet beim Aufruf den Wert und die Farbpalette mit Schwellen, die in der Funktion dann als value und ramp verfügbar sind. In einer for of Schleife werden alle Einträge in ramp mit dem value verglichen und sobald der Wert in die Schwellen passt, die entsprechende Farbe zurückgegeben. Wir schreiben die Funktion noch vor der Funktion für das Zeichnen der Stationen und testen sie mit einem Aufruf, dem wir den Wert -40 und die Farbpalette COLORS.temperature übergeben.

// Farben nach Schwellen ermitteln
let getColor = function(value, ramp) {
    //console.log(value,ramp);
    for (let rule of ramp) {
        //console.log(rule)
        if (value >= rule.min && value < rule.max) {
            return rule.color;
        }
    }
};
console.log(getColor(-40, COLORS.temperature));

🔗 COMMIT

Wie erwartet, bekommen wir den Farbwert #9f80ff angezeigt. Er steht für Temperaturen zwischen -50° und -25°.

c) Die ermittelte Farbe als style-Attribut beim <span> Element setzen

Jetzt müssen wir nur noch in pointToLayer, direkt nach let popup, die richtige Farbe für das L.divIcon ermitteln und in let color speichern.

let color = getColor(
    geoJsonPoint.properties.LT,
    COLORS.temperature
);

🔗 COMMIT

Danach können wir die Farbe als style-Attribut beim <span> Element über die CSS Regel background-color:${color} einsetzen.

icon: L.divIcon({
    className: "aws-div-icon",
    html: `<span style="background-color:${color}">${geoJsonPoint.properties.LT.toFixed(1)}</span>`
})

🔗 COMMIT

d) Leider ist die Position des Icons noch nicht ganz richtig :-(

… und wird es auch nicht hunderprozentig werden, denn die Positionierung von DIV-Icons ist nicht gerade einfach. Fügen wir vor dem return des DIV-Icons mit L.marker(latlng).addTo(map) einen zweiten Marker an der selben Position ein, erkennen wir, dass die Koordinate “ungefähr” links, oben beim Text liegt. Über einen Workaround im CSS von main.css verschieben wir deshalb die Icons nach links oben:

.aws-div-icon span {
    /* bestehende CSS Regeln */
    display: inline-block;
    transform: translate(-30%, -50%);
}

🔗 COMMIT

Das Verschieben der Marker bewirkt die CSS-Eigenschaft transform, deren translate Anweisung zwei Offsets in x und y Richtung erwartet. Warum wir beim Verschieben -30% für die x-Richtung verwenden müssen bleibt allerdings ein Rätsel. Immerhin liegt der Textmarker jetzt mit seinem Zentrum an der annähernd richtigen Position.

Hinweis: mit transform und der rotate Anweisung könn(t)en wir die Marker auch drehen …