Helligkeit von Farben, Kontrast, Farbunterschiede per PHP berechnen

PHP und Farbe – Helligkeit, Kontrast und Farbunterschied berechnen

Beim Sinieren über SEO und On-Page Optimierung kreisten meine Gedanken kürzlich um Folgendes:

Um eine Webseite „oben“ in den Google Suchergebnissen zu positionieren braucht es Content. Eine Überschrift „Keyword1 Keyword2“ hilft bereits weiter.

Erst recht, wenn diese über einem themenrelevanten Text mit einer bestimmten Keyword Dichte steht. Nun könnte es erstrebenswert sein, eine Seite unter einer Keyword Kombination finden zu lassen, die man seinen Besuchern nicht als prominente Überschrift oder Textblock präsentieren möchte. CSS bietet natürlich die Möglichkeit < h1 > Tags oder andere Elemente weniger dominant zu formatieren, doch darum soll es hier nicht gehen.

In den 90er Jahren soll es tatsächlich funktioniert haben, Überschriften oder gar Texte mit weisser Schrift auf weissem Hintergrund einzubinden und die Suchmaschinen so zu täuschen und sich eine bessere Position in den Trefferlisten zu erschleichen.

SCHON SEIT LÄNGERER ZEIT FUNKTIONIERT DIES NICHT MEHR! (vgl. hierzu http://www.seo-united.de/onpage-optimierung/content-texte.html und https://support.google.com/webmasters/answer/66353?hl=en) doch auch darum soll es hier nicht gehen.

Mich beschäftigt viel eher die Frage: „Wie schafft es Google oder generell eine Maschine dies zu erkennen (und zu bestrafen)?“. Denkt man darüber nach, kommt man zur Frage: Wie kann man z.B. einem PHP Skript beibringen den Helligkeitsunterschied zwischen 2 Farben zu bestimmen? Der Unterschied zwischen Hellgelb (#FFFFE0) und weissem Hintergrund müsste ja deutlich kleiner sein, als der Unterschied zwischen Dunkelbraun (#654321) und dem selben Hintergrund. Aber wie kann man dies berechnen?

Fangen wir mit den Grundlagen der Farbentheorie an: Im HTML Code einer Internetseite werden Farben als sog. HEX Code definiert (z.B #FFFFFF für weiss). http://de.wikipedia.org/wiki/Hexadezimale_Farbdefinition

Dieser Code entspricht einfach der hexadezimalen Schreibweise des entsprechenden RGB Codes, der jede Farbe in einen Rot, Grün und Blau Anteil zwischen 0 und 255 aufspaltet. Weiss hat die Anteile R=255, G=255 und B=255. Schwarz hat die Anteile R=0, G=0, B=0.

Nun gibt es das interessante Konzept des RGB Farbwürfels. Die x, y und z Achse dieses Würfels repräsentieren die Farben Rot, Grün und Blau und gehen jeweils von 0 bis 255. Jede Farbe kann nun durch 3 Koordinaten beschrieben werden und entspricht einem Punkt innerhalb dieses Würfels.

rgb_farbraum

Soweit so gut. Noch interessanter wird es, wenn man sich mit der Helligkeit dieser Farben beschäftigt. Je näher der beschriebene „Farbpunkt“ an der weissen Ecke dieses Würfels zu liegen kommt, desto heller ist die entsprechende Farbe. Das bedeutet aber auch, dass es einen Grünton gibt, dessen Helligkeit exakt der eines (z. B.) Rottones in der selben Entfernung der weissen Ecke entspricht. Eine bestimmte Helligkeit kann man sich also als Ansammlung/Fläche von Punkten innerhalb des Farbwürfels vorstellen, die denselben Abstand zur „weissen Ecke“ haben. Diese Fläche hat die Form eines Kugelsegments.

Diesen Abstand könnte man nun berechnen und daraus auf die Helligkeit einer beliebigen Farbe schliessen:

248_155_253

RGB-Farbe 248,155,253 (http://www.colorhexa.com/f89bfd)

255-248 = 7
255-155 = 100
255-253 = 2

Summe Δ 109

204_204_204

RGB-Farbe 204,204,204 (http://www.colorhexa.com/cccccc)

255-204 = 51
255-204 = 51
255-204 = 51

Summe Δ 153

Je kleiner diese Zahl, desto näher ist die betrachtete Farbe an der weissen Ecke, sprich umso heller. Und je gösser diese Zahl desta dunkler die entsprechende Farbe:

farben

Nun ist es natürlich ein Leichtes diese Zahlen per PHP miteinander zu vergleichen um herauszufinden, wie gross der Helligkeitsunterschied zwischen den beiden (z.B. Hintergrundfarbe und Textfarbe) ist. In unserem Fall 153-109=44. Nun könnten man einen Schwellenwert definieren (z.B. 50), ab dem eine Seite wegen schlechter Lesbarkeit oder vermeintlicher Betrugsversuche abgestraft wird.

//PHP Hex in RGB umrechnen
$hex_code_text="#000000";
//Nachdem der Hexcode mittels substr in Zweierpäckchen zerteilt wurde, wandelt die Funktion hexdec die Hexadezimalzahlen in Dezimalwerte um
$red = hexdec(substr($hex_code_text, 1, 2));
$green = hexdec(substr($hex_code_text, 3, 2));
$blue = hexdec(substr($hex_code_text, 5, 2));
 
echo "RGB-Code: (".$red."/".$green."/".$blue.")<br>";
 
//Differenz der einzelnen Werte von 255 ermitteln
$dif_white_red=255-$red;
$dif_white_green=255-$green;
$dif_white_blue=255-$blue;
 
$dif_white_sum=$dif_white_red+$dif_white_green+$dif_white_blue;
 
echo "Differenz zu weiss: ".$dif_white_sum."<br>";
 
//Differenz zu beliebiger Hintergrundfarbe
$hex_code_bg="#CCCCCC";
$bg_red = hexdec(substr($hex_code_bg, 1, 2));
$bg_green = hexdec(substr($hex_code_bg, 3, 2));
$bg_blue = hexdec(substr($hex_code_bg, 5, 2));
 
//durch abs wird sichergestellt, dass die Werte nicht negativ werden.
$dif_bg_red=abs($bg_red-$red);
$dif_bg_green=abs($bg_red-$green);
$dif_bg_blue=abs($bg_red-$blue);
 
$dif_bg_sum=$dif_bg_red+$dif_bg_green+$dif_bg_blue;
 
echo "Differenz zur Hintergrundfarbe: ".$dif_bg_sum;

P.S.: Natürlich gibt es andere Farbräume um Farben zu beschreiben, die ggf. zu einem anderen Ergebniss führen würden. Auch sind Farbeindrücke immer subjektiv. Was für den Einen noch gut lesbar erscheint, lässt den Anderen bereits die Augen zusammenkneifen. Es ist ausserdem bekannt, dass Farben umso heller erscheinen, je gesättigter sie sind (Helmholtz Kohlrausch Effekt: http://en.wikipedia.org/wiki/Helmholtz%E2%80%93Kohlrausch_effect). Als erste Näherung und um das Konzept zu verstehen, soll die hier vorgestellte Lösung jedoch genügen.

Das Sommerloch in der Medienlandschaft und wie Sie es für sich nutzen können

Arbeiten Sie in einem Büro? Dann ist Ihnen sicherlich aufgefallen, wie viele Ihrer Arbeitskollegen derzeit im Urlaub sind. Sommerzeit ist Urlaubszeit. Für die Daheimgebliebenen oft eine recht ereignisarme Zeit.

Betreibt man professionalle Pressearbeit, kann man versuchen, dieses so genannte Sommerloch für sich auszunutzen. Da auch in den Monaten Juli und August Tageszeitungen erscheinen und Radio- und Fernsehsendungen produziert werden, ist es in dieser Zeit leichter mit einem Thema in die Medien zu gelangen, dass sonst eher unberücksichtigt bliebe. Das einfache Prinzip von Angebot und Nachfrage spielt einem in der „Saure-Gurken-Zeit“ in die Karten.

Um es mit einer Meldung in die Presse zu schaffen gilt es jedoch einge grundlegende Regeln zu beachten:

1.) Auch im Sommerloch gilt: Professionelle Mitteilungen verbreiten

Lassen Sie Ihre Meldungen von Profis verfassen, Redaktionsbüros erwarten einen ganz bestimmten Aufbau einer Pressemitteilung. Erfüllt eine Meldung diese Erwartungen nicht, wird sie aussortiert.

2.) Verbreitung über seriöse Kanäle

Verzichten Sie auf eine manuelle Verbreitung an das Ihrer Meinung nach zuständige Resort des Zielmediums per eMail. Selbst wenn es Ihre Meldung durch etwaige Filter zum zustängigen Mitarbeiter schafft, wird er Ihre Nachricht höchstwahrscheinlich gelangweilt wegklicken. Er erhält täglich sicherlich dutzende oder gar hunderte derartige Meldungen. Erhält er dieselbe Meldung jedoch über eine seriöse Presseagentur, mit der er bereits in der Vergangenheit gute Erfahrungen gemacht hat (z.B. über die Kanäle der dpa), wird er der Mitteilung sehr viel mehr Aufmerksamkeit schenken.

3.) Verbreitungszeitpunkt geschickt wählen

Verschicken Sie Ihre Meldung keinesfalls an einem Freitag kurz vor Feierabend. Auch Redakteure machen (gerade zu Zeiten des Sommerloch) gegebenenfalls Freitags früher Schluss und Meldungen aus der Vorwoche fallen in der Folgewoche gerne unter den Redaktionstisch. Selbst wenn der Redakteur Ihre Nachricht kurz vor dem Wochenende noch lesen sollte und von Ihrem Produkt angetan ist, besteht die Möglichkeit, dass er Montags zahlreiche andere Dinge im Kopf hat und Ihre Meldung schlicht „vergessen“ wird.

Verschicken Sie Ihre Meldung auch nicht Mo-Do vor 9 Uhr oder nach 17 Uhr. Vielleicht ist Ihre „Zielperson“ ein Langschläfer und beginnt seinen Arbeitstag erst um 09:30 Uhr. Oder er ist Frühaufsteher und bereits ab 15:30 Uhr nicht mehr im Büro.

Der meiner Meinung nach ideale Verbreitungszeitpunkt für eine Pressemitteilung ist an einem Dienstag oder Donnerstag gegen 10:30 Uhr.

Probieren Sie es aus, Sie werden erstaunt sein, welchen Erfolg Sie haben, wenn Sie sich an die oben genannten einfachen Regeln halten. Sollten Sie bei einem oder allen der oben genannten Punkte Hilfe benötigen, stehen wir Ihnen mit unserer Marke Sommerloch PR gerne zur Verfügung…

Eine Einschränkung zum oben genannten gibt es: Sollte die deutsche Nationalmannschaft Fussball-Weltmeister werden, über der Ukraine ein Passagierflugzeug abgeschossen werden oder andere dramatische Ereignisse von bundes- oder weltweitem Interesse stattfinden, wird es schwierig für die eigene Pressemitteilung mit den Meldungen über diese Ereignisse zu konkurrieren. In einem solchen Fall empfiehlt es sich, sich in Geduld zu üben bis das Interesse der Medienlandschaft an diesen Ereignissen abgeflaut ist und es danach zu versuchen…

PHP Gezeiten berechnen | Gezeiten PHP Skript | Ebbe und Flut berechnen | Teil 5

Qualitätskontrolle und weitere Verbesserungen

Im fünften und letzten Teil unseres kleinen Tutorials “PHP Gezeiten berechnen” geht es um den Vergleich der von unserem Ebbe und Flut PHP Script gelieferten Daten mit der Realität.

Dieser fällt zunächst ernüchternd aus:

Ebbe_und_Flut_PHP_Gezeiten_qc1

Problem 1: Unser Skript kann Ebbe und Flut berechnen (Zeitpunkte und Wasserstände). Aber: während zumindest die Zeitpunkte für die Hochwasserevents relativ gut mit der Realität übereinstimmen, trifft unsere Annahme, die Niedrigwasserzeitpunkte würden exakt in der Mitte zwischen zwei Hochwassern liegen offensichtlich nicht zu. Lösung: Statt vom Benutzer lediglich einen Hochwasserevent abzufragen und davon ausgehend künftige Hoch- und Niedrigwasser-Zeiten zu berechnen, fügen wir mit $LW_event_user_timestamp eine weitere Benutzereingabe ein, nämlich ein reales, beobachtetes Niedrigwasser, das uns als Berechnungsgrundlage für künftige Niedrigwasserzeitpunkte dient. Auch hier sollte ein Zeitabstand von 44700 s gelten.

Damit stimmen nun beide Zeitpunkte gut überein:

Ebbe_und_Flut_PHP_Gezeiten_qc2

Problem 2: Unsere zwischen 1.44 und 0.56 schwankende Extremfaktorfunktion fällt im Vergleich zur Realität viel zu schnell ab. Lösung: Das Hochwasser schwankt in Dover zwischen 7.27 m (100%, entspricht 1.44) und 5.54 m (76.2%). Unser Skript geht derzeit jedoch von einer Schwankung zwischen 100% und 56% aus, was zum beobachteten zu schnellen Abfall führt. Verwenden wir für die Schwankungen von Hochwasser und Niedrigwasser nicht unsere Extremfaktorfunktion sondern Funktion Nr. 2 aus Teil 2 sollte dieses Problem behoben sein.

Problem 3: Dem aufmerksamen Beobachter fällt noch eine weitere Ungenauigkeit auf: In der Realität (zumindest an unserem Beispielort Dover) beobachtet man den höchsten Wasserstand (7.27 m) eben nicht wie erwartet bei Voll- oder Neumond sondern erst 2.558 Tage später. Das niedrigste Niedrigwasser fällt jedoch ziemlich exakt mit den Mondextremereignissen zusammen. Lösung: Ich habe für Ebbe und Flut eine “offset” Variable eingefügt, die dies berücksichtigt.

Schlussendlich sieht unser fertiges PHP Ebbe und Flut Skript also so aus:

//Benutzerangaben z. B. via Formular
$MaxHW=7.27;
$MinHW=5.54;
$MaxLW=1.96;
$MinLW=0.2;
$HW_offset=2.558;
$LW_offset=0;
//$HW_event_user=01.01.2014 10:42
$HW_event_user_timestamp=gmmktime(23,07,00,01,01,14);
//$LW_event_user=02.01.2014 06:31
$LW_event_user_timestamp=gmmktime(06,31,00,01,02,14);
//andere Konstanten
$pi=3.1415927;
$two_pi=2*$pi;
//Aus der Monddatenbank entsprechend des eingegebenen Hochwasserdatums die Periodizität berechnen im Bsp 14.73472 Tage = 1273080 s
$extreme_period_sec=1273080;
$extreme_period_days=$extreme_period_sec/(60*60*24);
//letztes Extremereignis aus der Datenbank auslesen
$last_extreme_event_timestamp=gmmktime(11,14,00,01,01,14);
$delta_seconds=$HW_event_user_timestamp-$last_extreme_event_timestamp;
$delta_days=$delta_seconds/(60*60*24);
$HW_event[]=$HW_event_user_timestamp;
$LW_event[]=$LW_event_user_timestamp;
$HW_delta_days[]=0;
 
$i=0;
//HW_events und Wasserhöhen berechnen
while($i<7)
{
$HW_event[]=$HW_event[$i]+44700;
$HW_delta_days[]=$HW_delta_value=($HW_event[$i]-$last_extreme_event_timestamp-$HW_offset)/(60*60*24);
$HW_value[]=($MaxHW-$MinHW)/2*COS($two_pi/$extreme_period_days*$HW_delta_days[$i])+($MinHW)+($MaxHW-$MinHW)/2;
$i++;
}
 
//LW_events und Wasserhöhen berechnen
$i=0;
while($i<7)
{
$LW_event[]=$LW_event[$i]+44700;
$LW_delta_days[]=$LW_delta_value=($LW_event[$i]-$last_extreme_event_timestamp-$LW_offset)/(60*60*24);
$LW_value[]=($MaxLW-$MinLW)/2*COS($two_pi/$extreme_period_days*$LW_delta_days[$i]+$extreme_period_days)+($MinLW)+($MaxLW-$MinLW)/2;
$i++;
}
 
echo "<table border=\"1\"><tr><td>HW</td><td>Wasserstand</td><td>NW</td><td>Wasserstand</td></tr>";
$i=0;
while($i<7)
{
echo "<tr><td>".$HW_event_formated = gmdate("d.m.Y H:i",$HW_event[$i])."</td><td>".number_format($HW_value[$i], 2)."</td><td>".$LW_event_formated = gmdate("d.m.Y H:i",$LW_event[$i])."</td><td>".number_format($LW_value[$i], 2)."</td></tr>";
$i++;
}
echo "</table>";
HW Wasserstand NW Wasserstand
01.01.2014 23:07 7.27 02.01.2014 06:31 0.37
02.01.2014 11:32 7.25 02.01.2014 18:56 0.27
02.01.2014 23:57 7.19 03.01.2014 07:21 0.22
03.01.2014 12:22 7.09 03.01.2014 19:46 0.20
04.01.2014 00:47 6.96 04.01.2014 08:11 0.23
04.01.2014 13:12 6.80 04.01.2014 20:36 0.30
05.01.2014 01:37 6.62 05.01.2014 09:01 0.40

Und liefert mit weniger als 50 Zeilen Code folgende Daten, die im grafischen Vergleich doch relativ gut mit der Realität übereinstimmen:

Ebbe_und_Flut_PHP_Gezeiten_qc3

Das in diesem Tutorial entwickelte Script oder Teile davon können gerne unter Angabe der Quelle verwendet werden. Bitte jedoch immer mit dem Hinweis, dass die berechneten Daten keinesfalls zur Navigation verwendete werden sollten.

Viel Spaß und hoffentlich: Immer eine handbreit Wasser unterm Kiel…

PHP Gezeiten berechnen | PHP Ebbe und Flut berechnen | Teil 4

Weiter geht´s mit der Herausforderung mit “PHP Ebbe und Flut berechnen

Angenommen an unserem Beispielort Dover liegt das höchste mögliche Hochwasser (extreme Wetterverhältnisse, Tornados, Sturmfluten, Tsunamis etc. lassen sich nicht vorhersagen und bleiben ausgeklammert) bei 7.27 m und das niedrigste möglich Niedrigwasser bei 0.20 m. Der maximale Tidenhub (der ja bei Springzeit auftreten sollte) liegt dann bei 7.07 m.

Wir erinnern uns an unsere Extremfaktor-Funktion aus Teil 2:

y=0.44*cos(2pi/14.8961805555x)+1 

X war hierbei der Abstand in Tagen vom beobachteten Extremereignis.

Einige Zeilen PHP Code später, liefert uns unser Gezeiten-Skript eine Tabelle der nächsten 7 Ebbe und Flut (oder Hoch- und Niedrigwasser-) Ereignisse und der zugehörenden Wasserstände:

//Benutzerangaben z. B. via Formular
$max_HW=7.27;
$min_LW=0.2;
//$HW_event_user=01.01.2014 10:42
$HW_event_user_timestamp=gmmktime(23,07,00,01,01,14);
//Aus der Monddatenbank entsprechend des eingegebenen Hochwasserdatums die Periodizität berechnen im Bsp 14.73472 Tage = 1273080 s
$extreme_period_sec=1273080;
$extreme_period_days=$extreme_period_sec/(60*60*24);
//letztes Extremereignis aus der Datenbank auslesen
$last_extreme_event_timestamp=gmmktime(11,14,00,01,01,14);
$pi=3.1415927;
$two_pi=2*$pi;
$delta_seconds=$HW_event_user_timestamp-$last_extreme_event_timestamp;
$delta_days=$delta_seconds/(60*60*24);
$HW_event[]=$HW_event_user_timestamp;
$LW_event[]=$HW_event_user_timestamp+22350;
$HW_delta_days[]=0;
$i=0;
//HW_events, Extremheitsfaktor und Wasserhöhen berechnen
while($i&lt;7)
{
$HW_event[]=$HW_event[$i]+44700;
$HW_delta_days[]=$HW_delta_value=($HW_event[$i]-$last_extreme_event_timestamp)/(60*60*24);
$Extreme_factor_HW[]=$Extreme_value_HW=0.44*COS($two_pi/$extreme_period_days*$HW_delta_value)+1;
$HW_value[]=$max_HW*$Extreme_value_HW/1.44;
$i++;
}
 
$i=0;
while($i&lt;7)
{
$LW_event[]=$LW_event[$i]+44700;
$LW_delta_days[]=$LW_delta_value=($LW_event[$i]-$last_extreme_event_timestamp)/(60*60*24);
$Extreme_factor_LW[]=$Extreme_value_LW=0.44*COS($two_pi/$extreme_period_days*$LW_delta_value)+1;
$LW_value[]=$min_LW*1.44/$Extreme_value_LW;
$i++;
}
 
$i=0; while($i&lt;7) { echo "
"; $i++; } echo "<table border="\&quot;1\&quot;">
<tbody>
<tr>
<td>HW</td>
<td>Wasserstand</td>
<td>NW</td>
<td>Wasserstand</td>
</tr>
<tr>
<td>".$HW_event_formated = gmdate("d.m.Y H:i",$HW_event[$i])."</td>
<td>".number_format($HW_value[$i], 2)."</td>
<td>".$LW_event_formated = gmdate("d.m.Y H:i",$LW_event[$i])."</td>
<td>".number_format($LW_value[$i], 2)."</td>
</tr>
</tbody>
</table>
HW Wasserstand NW Wasserstand
01.01.2014 23:07 7.22 02.01.2014 05:19 0.20
02.01.2014 11:32 7.07 02.01.2014 17:44 0.21
02.01.2014 23:57 6.81 03.01.2014 06:09 0.22
03.01.2014 12:22 6.48 03.01.2014 18:34 0.23
04.01.2014 00:47 6.07 04.01.2014 06:59 0.25
04.01.2014 13:12 5.61 04.01.2014 19:24 0.27
05.01.2014 01:37 5.13 05.01.2014 07:49 0.30

So weit so gut, wir haben ein PHP-Script geschrieben, dass uns Ebbe und Flut Zeiten und zugehörige Wasserstände vorhersagt. Im nächsten Schritt testen wir durch Vergleich mit “echten” Daten, was das Skript bereits taugt und wie wir es weiter verbessern können…

PHP Gezeiten berechnen | Ebbe und Flut per PHP Script | Teil 3

Nachdem wir uns bisher notwendigerweise mit Mondphasen und trigonometrischen Funktionen als Grundlagen beschäftigt haben, heute die Zeiten von Hoch- und Niedrigwasser: Wann kommt die Flut?

Geplant ist z.B. per Formular folgende Parameter an das PHP Skript zu übergeben:

  • Extrem HW
  • Extrem NW
  • Datum (unsere Datenbank ist auf 2014 beschränkt!) und Uhrzeit des letzten beobachteten Hochwassers

Das Skript soll anhand des Datums berechnen, welches Mondextremereignis am nächsten vor diesem Datum liegt (Vollmond oder Neumond) und wie lange der aktuelle Mond-Monat (von diesem Extremereignis zum nächsten) dauert. Von dieser Springzeit aus, berechnen wir einen Vorfaktor der angibt wie gross der Einfluss der Sonne auf die Gezeiten aktuell ist.

Bsp.: Aufgrund der Tatsache, dass es an unserem bisher verwendeten Beispielort (Hamburg) einen Wechsel zwischen Sommer und Winterzeit gibt und Hamburg sich nicht in der GMT Zeitzone befindet, habe ich für die folgenden Berechnungen Dover als Beispielort gewählt, da die dann verfügbaren Vergleichsdaten direkt in GMT vorliegen und keine Umrechnung vorgenommen werden muss.

Die Uhrzeit der vorhergesagten Hochwassers/Niedrigwasser Ereignisse berechnen wir näherungsweise mit unserer Funktion Nr 3:

Angenommen des vom Benutzer eingegebene Hochwasser war 01.01.2014 10:42:00. In Form eines Unix-GMT timestamps also

$timestamp=gmmktime(10, 42, 00, 1, 1, 2014)

Von einem Hochwasser zum nächsten dauert es 0.51736 Tage oder 0.51735d*24h*60min*60s= 44699,904 Sekunden (wir rechnen mit 44700).

Das vom Benutzerzeitpunkt aus gesehene Hochwasser findet also zu folgender Zeit statt:

echo $timestamp=gmmktime(10, 42, 00, 1, 1, 2014)." + 44700 statt:";
$next_HW=$timestamp+44700;
echo $next_HW."
 Also am: ";
echo gmdate("d. M. Y - H:i:s", $next_HW);

1388572920 + 44700 statt:1388617620
Also am: 01. Jan. 2014 – 23:07:00

Wollen wir nun die nächsten 7 Hochwasserereignisse ausgeben geht das mit folgender Schleife:

$timestamp=gmmktime(10, 42, 00, 1, 1, 2014);
$i=1;
$next_HW=$timestamp;
while($i&lt;=7) 
{
$next_HW=$next_HW+44700;
echo $i." | ".$next_HW." | ";
echo gmdate("d. M. Y - H:i:s", $next_HW);
echo "
";
$i++;
}

Was uns die folgende Liste liefert:

1 | 1388617620 | 01. Jan. 2014 – 23:07:00
2 | 1388662320 | 02. Jan. 2014 – 11:32:00
3 | 1388707020 | 02. Jan. 2014 – 23:57:00
4 | 1388751720 | 03. Jan. 2014 – 12:22:00
5 | 1388796420 | 04. Jan. 2014 – 00:47:00
6 | 1388841120 | 04. Jan. 2014 – 13:12:00
7 | 1388885820 | 05. Jan. 2014 – 01:37:00

Die Uhrzeiten der Hochwasserereignisse hätten wir, es folgt analog dazu die Zeit des Niedrigwassers. Näherungsweise gehen wir davon aus, dass zwischen HW und NW immer 44700/2 also 22350 Sekunden liegen.

$timestamp=gmmktime(04, 56, 00, 1, 16, 2014);
$i=1;
$next_LW=$timestamp;
while($i&lt;=7) 
{
$next_LW=$next_LW+22350;
echo $i." | ".$next_LW." | ";
echo gmdate("d. M. Y - H:i:s", $next_LW);
echo "
";
$i++;
}

1 | 1388595270 | 01. Jan. 2014 – 16:54:30
2 | 1388617620 | 01. Jan. 2014 – 23:07:00
3 | 1388639970 | 02. Jan. 2014 – 05:19:30
4 | 1388662320 | 02. Jan. 2014 – 11:32:00
5 | 1388684670 | 02. Jan. 2014 – 17:44:30
6 | 1388707020 | 02. Jan. 2014 – 23:57:00
7 | 1388729370 | 03. Jan. 2014 – 06:09:30

Die Zeitpunkte hätten wir damit. Es fehlt die Berechnung des erwarteten Wasserstands aus den Extremwerten mittels Vorfaktor-Funktion.

Im nächsten Teil kommt es hoffentlich endlich zum “PHP Ebbe und Flut berechnen”.

PHP Gezeiten berechnen | Ebbe und Flut | Teil 2

In Teil 1 haben wir mittels gmmktime() timestamps sämtlicher Mondphasen für 2014 erzeugt und in einer Datenbank abgelegt. Wir nähern uns langsam dem PHP Gezeiten Rechner, auch wenn man dieses Kapitel alternativ mit “Wiederholung trigonometrischer Funktionen” hätte überschreiben können…

Wie man aus unserer Monddatenbank leicht errechnen kann, lagen zwischen dem 1. und 2. Vollmond 2014 genau (!) 2574060 Sekunden (oder 29.7923611111 Tage) also rund 6.28 Stunden mehr als der Mittelwert unserer ersten Näherung.

Dies kann man als trigonometrische Funktion darstellen. Ich habe mich für den Cosinus entschieden.

Man erinnert sich an die Schulzeit y=cos(x). Mit einigen Modifikationen was Amplitude und Frequenz angeht landen wir bei:

1) y=0.5*cos(2pi/29.7923611111x)+0.5

cos_mondphasen

x ist hierbei die Anzahl der Tage seit dem letzen Vollmond, wenn x=14 ist, ist gerade Neumond, bei x=7 befinden wir uns im „letzten Viertel“ etc.

Wir brauchen noch weitere periodische Funktionen, mit deren Hilfe unser PHP-Script dann später die Berechnung von Ebbe und Flut vornehmen kann. Beim BSH erfährt man, dass zwischen 2 Hochwassern im Durchschnitt (Achtung Näherung!) 12h und 25 min liegen. Das entspricht 0.51736 Tagen. Wir brauchen also eine Funktion die mit einer Periodizität von eben diesen 0.51736 Tagen oszilliert (von einem (Funktions-)Wellenberg zum nächsten soll es also nicht 2pi sondern 0.51736 dauern).

y=cos(2pi/0.51736x)

Diese Funktion hat die gewünschte Periodizität, die Amplitude beträgt jedoch 1. Angenommen das letzte Hochwasser lag bei 5 Metern und das letzte Niedrigwasser bei 1 Meter, dann bräuchten wir eine Funktion, die um den Mittelwert 3 Meter oszilliert und im oberen Extremfall den Wert 5, im unteren Extremfall den Wert 1 zurück liefert:

y=2*cos(2pi/0.51736x)+1+2

Diese Funktion schwankt mit der gewünschten Periodizität und Amplitude:

cos_tidenhub

Verallgemeinert kann man schreiben:

2) y=(HW-NW)/2*cos(2pi/0.51736x)+1+(HW-NW)/2

Hier ist x Anzahl Tage seit dem letzten Hochwasser, wenn seither 0.51736 Tage vergangen sind, gibt es wieder einen neuen Wellenberg (nächstes Hochwasser). Wenn seither nur 0.25868 Tage vergangen sind haben wir gerade Niedrigwasser…

Die letzte periodische Funktion die wir brauchen, berücksichtigt den Einfluss der Sonne. Stehen Sonne, Erde und Mond in einer Linie (also immer bei Vollmond und Neumond) sind die Gezeiten besonders stark ausgeprägt (der Seemann spricht von Springzeit). Stehen Sonne und Mond von der Erde aus betrachtet im rechten Winkel zueinander, sind die Gezeiten relativ schwach (der Wassersportler spricht von Nippzeit), da der Einfluss der Sonne dem (stärkeren) Einfluss des Mondes entgegen wirkt.

Angenommen wir werten den Einfluss des Mondes auf die Gezeiten mit 100 Punkten. Der Einfluss der Sonne beträgt (Achtung Näherung!) 44% des Einflusses des Mondes, wird also mit 44 Punkten bewertet. Ziehen Sonne und Mond nun also am gleichen Strang (verstärken sich also) erhalten wir einen Gesamteffekt von 144 Punkten. Stehen Sonne und Mond im rechten Winkel zueinander muss die Wirkung der Sonne von der Wirkung des Mondes abgezogen werden. Der Gesamteffekt beträgt dann 100-44=56 Punkte. Wir brauchen also eine Funktion. die zwischen 1.44 und 0.56 oszilliert:

y=(1.44-0.56)/2*cos(2pi/x)+0.56+(1.44-0.56)/2

Wir brauchen eine Periodizität von 29.7923611111/2 also 14.8961805555 Tagen

y=(1.44-0.56)/2*cos(2pi/14.8961805555x)+0.56+(1.44-0.56)/2

Da sich die Werte 1.44 und 0.56 als genäherte Naturkonstanten nicht ändern, können wir vereinfachen:

3) y=0.44*cos(2pi/14.8961805555x)+1 

cos_zusammenspiel_sonne_und_mond

Hier ist x der Tag nach dem letzten extremen Mondereignis, also 3 wenn gerade 3 Tage nach Neumond ist oder 6 wenn 6 Tage nach Vollmond ist.

Wir haben also nun 3 trigonometrische Funktionen die die Gezeitenphänomene näherungsweise beschreiben.

Im nächsten Kapitel basteln wir aus diesen Funktionen ein PHP Skript, dass uns die Rechenarbeit abnimmt. Mein Plan ist dem Skript als Benutzereingabe folgende Daten zu liefern (z.B. über ein Formular):

  • Datum, Uhrzeit des letzten Hoch- und Niedrigwassers
  • Extrem Hoch und Niedrigwasserdaten

Anhand dieser Angaben soll das Skript zunächst (durch Vergleich mit unserer „Vollmond-Datenbank“) ermitteln, in welcher Mondphase diese Daten zustande kamen und anschliessend Wasserstände und Uhrzeiten der nächsten Ebbe und Flut berechnen.

PHP Gezeiten berechnen | Ebbe und Flut | Teil 1

Die Gezeiten hängen vom Stand des Mondes und der Sonne ab.

Da Ebbe und Flut darüber hinaus auch von örtlichen/geographischen Gegebenheiten abhängen, ist mein Ansatz wie folgt: Wie bereits beim Mond erhält man recht verlässliche Angaben (die die örtlichen Gegebenheiten bereits berücksichtigen), wenn man seine Berechnungen auf empirisch beobachtete Ereignisse “aufpfropft”. Will heissen: Wenn man aus Gezeitentabellen weiss, dass z.B. am 01. September 2014 an einem bestimmten Ort um 19:43 Hochwasser ist/war, müsste es möglich sein, den Zeitpunkt des nächsten Hochwassers mittels PHP-Skript zu berechnen (Wir basteln also einen PHP Gezeiten Rechner).

Da es gar nicht so trivial ist, den nächsten Vollmond zu berechnen (bzw. da triviale Lösungen dieses Problems eine Ungenauigkeit von mehreren Stunden aufweisen, was bei Gezeitentabellen natürlich inakzeptabel wäre) bin ich froh, die Berechnung der Mondphasen Profis überlassen zu können. Die NASA veröffentlicht dankenswerterweise Mondphasen Daten für das komplette 21. Jahrhundert (wer am verwendeten Algorithmus interessiert ist wird im Buch Astronomical Algorithms auf knapp 60 (!) Seiten fündig). Auch wenn ein Streben nach Autarkie und die Demonstration des technisch Machbaren sicher erstrebenswert wäre. Ein solches PHP Script würde den Rahmen eines “kostenlosen” Codeschnipsels sprengen (falls es einen Sponsor gibt der an einer Umsetzung Interesse hat freue ich mich über eine eMail ;-))

Wir werden für die Gezeitenberechnung, die auch so noch ausreichend komplex wird, auf die Daten der NASA zurückgreifen. Und zwar auf den Abschnitt für 2014:

 Year      New Moon       First Quarter       Full Moon       Last Quarter     

 2014   Jan  1  11:14     Jan  8  03:39     Jan 16  04:52     Jan 24  05:19            
        Jan 30  21:39     Feb  6  19:22     Feb 14  23:53     Feb 22  17:15    
        Mar  1  08:00     Mar  8  13:27     Mar 16  17:09     Mar 24  01:46    
        Mar 30  18:45     Apr  7  08:31     Apr 15  07:42 t   Apr 22  07:52    
        Apr 29  06:14 A   May  7  03:15     May 14  19:16     May 21  12:59    
        May 28  18:40     Jun  5  20:39     Jun 13  04:11     Jun 19  18:39    
        Jun 27  08:09     Jul  5  11:59     Jul 12  11:25     Jul 19  02:08    
        Jul 26  22:42     Aug  4  00:50     Aug 10  18:09     Aug 17  12:26    
        Aug 25  14:13     Sep  2  11:11     Sep  9  01:38     Sep 16  02:05    
        Sep 24  06:14     Oct  1  19:33     Oct  8  10:51 t   Oct 15  19:12    
        Oct 23  21:57 P   Oct 31  02:48     Nov  6  22:23     Nov 14  15:16    
        Nov 22  12:32     Nov 29  10:06     Dec  6  12:27     Dec 14  12:51    
        Dec 22  01:36     Dec 28  18:31

Mit Hilfe dieser Tabelle können wir also die Zeitspanne zwischen zwei Vollmonden durch Differenzbildung exakter bestimmen.

Zunächst werden wir diese Zeitangaben in einen UNIX timestamp transformieren und in einer MySQL Datenbank ablegen.

Um Problemen mit Zeitzonen ebenso aus dem Weg zu gehen, wie der Frage ob gerade Sommerzeit oder Winterzeit ist, erfolgen alle Berechnungen in UT (auch GMT). Die Daten in obiger Tabelle beziehen sich ebenfalls auf UT.

Auch PHP weiss um die Zeitzonen Problematik und enthält mit gmmktime() dankenswerterweise eine eigene Funktion um Zeitangaben die in Universal Time gemacht wurden in entsprechende UT-Timestamps umzuwandeln:

echo $timestamp=gmmktime(11, 14, 00, 1, 1, 2014)."
";
//Zur Kontrolle, dass kein Fehler passiert ist lassen wir den timestamp wieder "zurückkonvertieren" und Datum/Uhrzeit ausgeben.
echo gmdate("d.M.Y - H:i:s", $timestamp);

Dieses Script liefert folgende Ausgabe:

1388574840
01.Jan.2014 – 11:14:00

Die MySQL Datenbank, die unsere Monddaten enthält sieht dann etwa so aus:

-- 
-- Tabellenstruktur für Tabelle `moondata`
-- 
 
CREATE TABLE `moondata` (
  `ID` tinyint(4) NOT NULL AUTO_INCREMENT,
  `TYPE` varchar(15) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `TIME` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=MyISAM AUTO_INCREMENT=51 DEFAULT CHARSET=latin1 COLLATE=latin1_german2_ci AUTO_INCREMENT=51 ;
 
-- 
-- Daten für Tabelle `moondata`
-- 
 
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (1, 'new', '1389698040');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (2, 'new', '1391117940');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (3, 'new', '1393632000');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (4, 'new', '1396205100');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (5, 'new', '1398752040');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (6, 'new', '1401302400');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (7, 'new', '1403827200');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (8, 'new', '1406414520');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (9, 'new', '1408975980');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (10, 'new', '1411539240');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (11, 'new', '1414101420');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (12, 'new', '1416660720');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (13, 'new', '1419212160');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (14, 'first', '1389152340');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (15, 'first', '1391714520');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (16, 'first', '1394285220');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (17, 'first', '1396859460');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (18, 'first', '1399432500');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (19, 'first', '1402000740');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (20, 'first', '1404561540');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (21, 'first', '1407113400');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (22, 'first', '1409656260');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (23, 'first', '1412191980');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (24, 'first', '1414723680');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (25, 'first', '1417255560');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (26, 'first', '1419791460');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (27, 'full', '1389847920');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (28, 'full', '1392421980');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (29, 'full', '1394989200');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (30, 'full', '1397547720');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (31, 'full', '1400181360');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (32, 'full', '1402632660');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (33, 'full', '1405164300');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (34, 'full', '1407693600');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (35, 'full', '1385775480');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (36, 'full', '1412074260');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (37, 'full', '1415312580');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (38, 'full', '1417868820');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (39, 'last', '1390540740');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (40, 'last', '1393089300');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (41, 'last', '1395625560');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (42, 'last', '1398153120');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (43, 'last', '1400677140');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (44, 'last', '1403203140');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (45, 'last', '1405735200');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (46, 'last', '1408278360');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (47, 'last', '1410833100');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (48, 'last', '1413400920');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (49, 'last', '1415978160');
INSERT INTO `moondata` (`ID`, `TYPE`, `TIME`) VALUES (50, 'last', '1418561460');

Genug für heute, im zweiten Teil folgt die Berechnung der Gezeiten…

PHP Vollmond berechnen, Mondphasen Rechner

Durch mein Interesse für den Segelsport stolperte ich kürzlich über folgende Frage:

Gibt es eine Möglichkeit zukünftige Gezeiten (Tidenhub, Zeiten von Hochwasser und Niedrigwasser, Ebbe und Flut) für einen bestimmten Ort relativ einfach mittels PHP zu berechnen?

Das Bundesamt für Seeschifffahrt und Hydrographie (BSH) und einige andere kommerzielle Anbieter liefern solche Daten in Form von Gezeitentabellen. Das BSH schreibt auf seiner Internetseite, was für den Laien (in vereinfachter Form) zum Allgemeinwissen gehört, nämlich, dass die Gezeiten von den Himmelsgestirnen, im Wesentlichen von Sonne und Mond verursacht werden.

Das wiederum war Hinweis und Ausgangspunkt für das erste Script, dass ich in der neuen Rubrik „Codeschnipsel“ vorstellen möchte, nämlich ein Skript, dass die Mondphasen (genauer den Zeitpunkt des/der nächsten Vollmond/e) berechnet. Gerne kann es angepasst und auf der eigenen Internetseite verwendet werden. Support oder eine irgendwie geartete Garantie für Richtigkeit und Zuverlässigkeit gibt es (wie für alle kostenlose Scripte von zoerb.net) nicht. Wird das Script eingesetzt, freuen wir uns über einen Link zu unserer Internetseite. Los geht‘s:

PHP Vollmond berechnen | PHP Mondphasen Rechner

Vollmond ist einmal im Monat, dann müsste man den nächsten Vollmond doch leicht berechnen können, wenn man zu einem Referenzvollmond (empirisch beobachteter, bekannter Vollmond) 30 Tage addiert, oder?

Bei wikipedia lernt man, dass zwischen 2 Voll- oder Neumondern, der so genannte synodische Monat und daher nicht 30 sondern exakt 29.530589 Tage liegen. Exakt ist durchgestrichen, da es sich beim angegebenen Wert um einen Mittelwert handelt, der von diversen weiteren Faktoren abhängt. Für unser PHP-Script müssen wir uns aber wohl mit dieser Näherung zufrieden geben, jetzt aber zu PHP:

//Aktueller timestamp um zu erkennen ob eine Zeitdifferenz (z.B. MESZ, andere Zeitzone etc) berücksichtigt werden muss
//Wenn sich Serverzeit von aktueller Benutzerzeit unterscheidet muss dies bei der Eingabe des Referenzmondes berücksichtigt werden
$current_timestamp=time();
$current_date=date("d.m.Y - H:i",$current_timestamp);
echo "Aktuelles Serverdatum und Uhrzeit: ".$current_date;
echo "
";
//Erzeugt einen timestamp für einen Referenz Vollmond
//z. B. 14.05.14 20:14
$reference_moon = mktime(20,14,00,5,14,2014);
//$reference_moon_format = date("d.m.Y - H:i",$reference_moon);
//echo $reference_moon_format;
//Zwischen 2 Vollmonden liegen 29.530589 Tage oder X tausend Sekunden
$moon_days=29.530589;
$moon_seconds=$moon_days*24*60*60;
$next_moon=$reference_moon+$moon_seconds;
$next_moon=round($next_moon, 0);
echo "
";
echo "N&amp;auml;chster Vollmond: ";
echo date("d.m.Y - H:i",$next_moon);
echo "
";
echo "&amp;uuml;bern&amp;auml;chster Vollmond: ";
$next_moon=$next_moon+$moon_seconds;
$next_moon=round($next_moon, 0);
echo date("d.m.Y - H:i",$next_moon);

Das Script liefert als Ausgabe folgendes:


Aktuelles Serverdatum und Uhrzeit: 31.07.2014 – 19:34
Nächster Vollmond: 13.06.2014 – 08:58
übernächster Vollmond: 12.07.2014 – 21:42


Schlussbemerkung: Um auf die Gezeitenberechnung zurück zu kommen. Das BSH schreibt auch, dass ortabhängige Faktoren (Gestalt und Tiefe der Ozeane) ganz wesentlich zu den beobachteten Gezeiten beitragen. Die Gezeiten der Nordsee, sind demnach fast ausschließlich durch das Mitschwingen mit den angrenzenden Ozeanen und nur zu einem sehr geringen Teil durch die unmittelbare Einwirkung der gezeitenerzeugenden Gestirne verursacht. Die Gezeiten an irgendeiner bestimmten Stelle brauchen daher keineswegs dem Verlauf der örtlichen Schwerkraftstörungen zu ähneln. Da aber diese Störungen überall nur von den scheinbaren Stellungen der gezeitenerzeugenden Gestirne zur Erde abhängen, können die Gezeiten und Gezeitenströme an jedem Ort unmittelbar zu den scheinbaren Bewegungen des Mondes und der Sonne in Beziehung gesetzt werden. Die besondere Form dieser Beziehungen für einen bestimmten Ort wird bisher am genauesten und bequemsten aus örtlichen Beobachtungen der Gezeiten und Gezeitenströme, also aus der Erfahrung. Aber das ist als Aufhänger für das nächste Script gedacht…