Eigene Wetterseite: Yahoo Icons einbinden (statisch oder Ajax) und lokal cachen

Die Yahoo Wettericons sind eindeutig schöner als die Google Icons, es gibt sie in zwei Auflösungen und sie unterscheiden zwischen Tag und Nacht (dass in Googles Reich die Sonne nie untergeht, möchte man wohl auch über die Icons verdeutlichen :rolleyes:).

Das zur Wetterlage am Wohnort passende Icon kann man mit dem Skript unten nicht nur in die eigene Seite einbinden sondern auch gleich lokal cachen. Der lokale Cache erspart zukünftiges, unnötiges Laden vom Yahoo Server und ermöglicht es, die lokalen (dauerhaften) Kopien an eigene Bedürfnisse (Größe, Farbgebung etc.) anzupassen. Durch Löschen lokaler Kopien kann jederzeit erreicht werden, dass ‚Frischfleisch‘ vom Yahoo Server nachgeladen wird.

Das Skript kann über zwei Methoden eingebunden werden:

[ol]
[li]statisch bei Aufruf der Seite via PHP (include()) oder[/li][li]dynamisch via Javascript (XMLHttpRequest / Ajax)[/li][/ol]
Die zweite Methode bietet sich an, wenn man das Icon in festgelegten Zeitabständen via window.setInterval() dynamisch aktualisieren möchte. Also zeitnah auf dem Bildschirm immer das passende Icon sehen möchte, ohne die Seite nachladen zu müssen.

Die Funtionsweise des Skriptes ist einfach: es ruft den aktuellen Yahoo RSS-Wetterfeed für einen Ort ab und extrahiert aus den gelieferten Informationen die URL des im Feed eingebetteten Icons.

Dann prüft es auf Basis des Dateinamens, ob schon eine lokale Kopie existiert. Ist das der Fall, wird der Pfad der lokalen Kopie zurückgegeben (es wird also aufgrund der Yahoo Angaben die aktuell passende lokale Kopie - auch angepasste - ausgewählt). Wenn nicht, wird versucht, eine lokale Kopie anzulegen. Ist das erfolgreich, wird der Pfad zur (gerade angelegten) lokalen Kopie zurückgegeben.

Scheitert das Anlegen einer lokalen Kopie, wird die URL der Originaldatei zurückgegeben. Das Skript ist also robust genug, um Fehler beim Cachen für den Betrachter der Seite transparent zu ‚überspielen‘.

Das statische Einbinden ist mit zwei Zeilen erledigt. Um zum Beispiel das hochauflösende Yahoo Icon in die eigene Seite einzubauen genügt:


<?php include ("dasSkript.php"); ?>
<img
    id="yahooIcon"
    src="<?php echo $imageURLs["hiRes"]; ?>"
    title="<?php echo $imageTitle; ?>"
    class="<?php echo $imageClassName . " hiRes"; ?>" />

Das dynamische Einbinden via Ajax gestaltet sich komplexer ist aber prinzipiell auch nur ein Vierzeiler (hier nur symbolhaft, wäre aber voll funtionstüchtig):


function loadYahooIcon()
{
    /* 1 */ ajaxRequestSenden('dasSkript.php', 'resolution=high', onLoadYahooIcon);
}

function onLoadYahooIcon(_responseText)
{
    /* 2 */ eval(_responseText);
    /* 3 */ document.getElementById('yahooIcon').src = imageData.url;
    ...
}

/* 4 */ window.setInterval(loadYahooIcon, 5 * 60 * 1000) // Alle 5 Minuten neu laden

Wer das möchte, sollte sich ein wenig mit Ajax auskennen. Fragen danach und auch Fragen, wie das mit Javascript und dem Programmieren überhaupt funktionert, werden ohne Antwort bleiben.

Und hier das Skript (1:1 Kopie aus laufendem System, also Pfade und IDs anpassen):


<?php
	/*
	 * Wird das Skript via Ajax aufgerufen, gibt es folgenden Javascript
	 * Code als Text zurück:
	 *
	 *		error = <null>|<'<Fehlerbeschreibung>'>;
	 *		imageData =
	 *		{
	 *			className: '<CSS Klassenname>',
	 *			title: '<Text für das title-Tag>',
	 *			url: <null>|<'<URL des Bildes>'>
	 *		};
	 *
	 */
	
	// Die Yahoo Orts-ID (steht bei Aufruf des Yahoo
	// Wetterberichtes am Ende der URL);
	$yahooLocId = 675871; // <= Mölln // $yahooLocId = 641055; // <= Breitenfelde
	// Breiten- und Längengrad des Ortes. Wird zur Berechnung
	// des Sonnenaufganges und Unterganges herangezogen, um die
	// Tag- oder Nachtversion des Icons auszuwählen. Kann mit
	// Google Maps leicht ermittelt werden
	$locationLat = 53.611921;
	$locationLng = 10.68083;
	// Die URL des Yahoo Wetterfeeds
	$yahooRSSURL = "http://weather.yahooapis.com/forecastrss?w=$yahooLocId";
	// Array zur Aufnahme der beiden URLs der hoch- beziehungsweise
	// niedrigaufgelösten Bildversion
	$imageURLs = array("loRes" => null, "hiRes" => null);
	// Der CSS-Klassenname des Bildes
	$imageClassName = "yahoo";
	// Der Text für das title-Tag des Bildes
	$imageTitle = "Copyright © Yahoo! Inc., Weather Channel Enterprises, Inc.";
	// Das Server Dokumentenverzeichnis
	// # für den Gebrauch mit WebFront:
	// # IPS_GetKernelDir() . "webfront\\"
	$documentRoot = $_SERVER["DOCUMENT_ROOT"];
	// Der lokale Speicherort für (Yahoo)Bilder
	// # für den Gebrauch mit WebFront:
	// # "user\\irgend\\ein\\Pfad\\"
	$imagePath = "/forecasts/images/yahoo/";
	// Bestimmt, welche URL bei einem Aufruf via Ajax zurückgegeben
	// werden soll. Wird auch zum CSS-Klassennamen hinzugefügt, um
	// per Stylesheet auf verschiedene Auflösungen reagieren zu können.
	$ajaxResolution = "loRes";

	// Ajax Antwort vorbereiten	
	if (isset($_POST["resolution"]) && $_POST["resolution"] == "high")
	{
		$ajaxResolution = "hiRes";
	}
	$ajaxImageData
		= "className: '$imageClassName $ajaxResolution', "
		. "title: '$imageTitle', "
		. "url: ";
	
	// Zeigt an, ob das Skript per include() eingebunden
	// beziehungsweise direkt aufgerufen wurde (via Ajax)
	$isInclude = sizeof(debug_backtrace()) > 0;

	// Yahoo RSS-Feed laden
	if ($xml = simplexml_load_file($yahooRSSURL))
	{
		// <description> Tag(s) des Feed auswählen
		if ($descrTags = @$xml->xpath("/rss/channel/item/description"))
		{
			// <img> Tag finden und URL herausfiltern
			if (preg_match("/img\s+src=\"([^\"]+)\"/i", $descrTags[0], $matches))
			{
				// Im RSS Feed liefert Yahoo niedrig aufgelöste Bilder
				$imageURLs["loRes"] = $matches[1];
				// Die hochaufgelösten Bilder liegen aber nur unter anderem Namen
				// (00d.png oder 00n.png statt 00.gif) in einem anderen Verzeichnis.
				// Dazu muss noch ermittelt werden, ob Tag oder Nacht ist, um die
				// passende Erweiterung zum Dateinamen hinzuzufügen.
				$timestamp = mktime();
				$sunrise = date_sunrise($timestamp, SUNFUNCS_RET_TIMESTAMP, $locationLat, $locationLng);
				$sunset = date_sunset($timestamp, SUNFUNCS_RET_TIMESTAMP, $locationLat, $locationLng);
				$dayNightChar = mktime() >= $sunrise && mktime() <= $sunset ? "d" : "n";
				$imageURLs["hiRes"]
					= str_replace(
						array("/we/52/", ".gif"),
						array("/nws/weather/gr/", "$dayNightChar.png"),
						$matches[1]);
				
				// 2 Durchläufe: loRes und hiRes
				foreach ($imageURLs as $res => $URL)
				{
					// Dateinamen extrahieren
					$imageName = basename($URL);
					// Wenn wir schon eine lokale Kopie haben ...
					if (file_exists($documentRoot . $imagePath . $imageName))
					{
						// ... nur Yahoo URL durch lokalen Pfad ersetzen
						$imageURLs[$res] = $imagePath . $imageName;
					}
					// Andernfalls Bild vom Yahoo Server laden und lokale Kopie speichern
					else
					{
						// Wenn das Laden und Speichern klappt ...
						if($imageData = @file_get_contents($URL))
						{							
							if (@file_put_contents(
								$documentRoot . $imagePath . $imageName,
								$imageData))
							{
								// ... Yahoo URL durch lokalen Pfad ersetzen
								$imageURLs[$res] = $imagePath . $imageName;
							}
						}
					}
				}
				// An dieser Stelle stehen jetzt in $imageURLs[$res] entweder
				// die lokalen Pfade oder (w.g. Fehler) noch die Yahoo URLs.
				
				// Wert von imageData.icon auf URL setzen (! 'URL ist String' !)
				$ajaxImageData .= " '" . $imageURLs[$ajaxResolution] . "'";
			}
			// Kein <img> Tag im <description> Tag gefunden
			// oder dessen src-Attribut ist leer
			else
			{
				// Wert von imageData.icon auf null setzen
				$ajaxImageData .= " null";
			}
		}
		// XPath fehlgeschlagen (interner Fehler oder Pfad nicht gefunden)
		else
		{
			// Wert von imageData.icon auf null setzen
			$ajaxImageData .= " null";
		}
		// Skript wurde eingebunden
		if ($isInclude)
		{
			// Ob ein Fehler aufgetreten ist, kann durch Abfrage von
			// $imageURLs[Auflösung] === null festgestellt werden.
			return;
		}
		// Ajax Aufruf
		else
		{
			exit("error = null; imageData = { $ajaxImageData };");
		}
	}
	// Laden des RSS-Feeds fehlgeschlagen
	else
	{
		if ($isInclude)
		{
			// Dass ein Fehler aufgetreten ist, kann durch Abfrage von
			// $imageURLs[Auflösung] === null festgestellt werden.
			return;
		}
		// Ajax Aufruf
		else
		{
			exit("error = 'Laden von [$yahooRSSURL] fehlgeschlagen'; imageData = { $ajaxImageData }'");
		}
	}
?>

Wie das live aussieht und dass die Vorgehensweise auch für dynamische (Ajax) FTP-Downloads (z.B. DWD-Server) taugt, kann man durch klicken auf die URL in der Signatur feststellen.