PHP Magazin

Das Magazin für PHP Entwicklung

Graphen mit den eZ Components

Visualisiert

Graphen mit den eZ Components

Kore Nordmann

Graphische Darstellung von Daten ist und bleibt wohl immer relevant. Seien es Statistiken über die Seitenzugriffe auf die eigene Webapplikation, oder speziellere Auswertungen für den Benutzer der Applikation. Graphiken ziehen das Auge des Betrachters auf sich, Tendenzen lassen sich in Diagrammen schneller ablesen als in Zahlenkolonnen, Tortendiagramme können Mengenverhältnisse einfacher begreifbar machen.

In dynamischen Applikationen müssen diese Diagramme automatisch generiert werden und es ist nicht rentabel diese von einem Graphiker aufarbeiten zu lassen. Das hat zur Konsequenz, dass oft unansehnliche Graphiken den Gesamteindruck stören, oder sich gar nicht an das Corporate Design anpassen lassen.

Die Graph-Komponente aus den eZ Components stellt nun seit einiger Zeit Möglichkeiten zur Verfügung, graphisch hochwertige Resultate zu erzeugen und die Ausgabe weitgehend an den Wunsch des Kunden anzupassen. Durch konsequent objektorientiertes Design lässt sich ezcGraph dazu an jeder Stelle erweitern, wenn Features noch nicht den eigenen Wünschen entsprechen sollten.

eZ Components

Die Graph-Komponente, auf die dieser Artikel eingehen wird, ist Bestandteil der Komponentenbibliothek eZ Components, die Entwicklern von Applikationen gut getestet und dokumentierte Komponenten zur Verfügung stellen will, damit diese häufig wiederkehrende Probleme nicht erneut lösen müssen. Die Komponentenbibliothek steht unter der New-BSD-Lizenz und ist damit auch problemlos in kommerziellen Produkten einsetzbar.

Installieren lassen die Komponente über einen eigenen pear-Channel, durch das Herunterladen eines Archives, oder direkt aus dem SVN-Sourceode-Repository. Die Installation ist auf der Webseite detailliert geschildert, nur die Kommandos für den pear-Installer seien hier kurz gezeigt, auf den meisten aktuellen Systemen sollten sie ausreichen:

pear channel-discover components.ez.no
pear install ezc/Graph

Wenn das pear-Verzeichnis nun korrekt im include_path angegeben ist, lassen sich die Komponenten einfach verwenden, wenn das das folgende Code-Snippet eingebunden wird:

require 'ezc/Base/base.php';
function __autoload( $class )
{
	ezcBase::autoload( $class );
}

Dies lässt sich entsprechend auch in den eigenen autoload-Mechanismus integrieren, oder mit spl_autoload() verwenden. Weitere Details finden sich in der erwähnten Installationsbeschreibung.

Liniendiagramme

Ein einfaches Liniendiagramm lässt sich mit nur drei Zeilen Code erzeugen, um ein wenig mehr Informationen unterzubringen fügen wir noch einen zweiten Datensatz und einen Titel ein, beides optional:

$graph = new ezcGraphLineChart();
$graph->title = 'Code Coverage';
$graph->data['Executable'] = new ezcGraphArrayDataSet( array( 
	127, 253, 512, 560, 893
) );
$graph->data['Covered'] = new ezcGraphArrayDataSet( array(
	15, 86, 412, 530, 893
) );
$graph->render( 300, 150, '01_line.svg' );

Der gewählte Konstruktor gibt den Typen des darzustellenden Graphen an. In diesem ersten einfachen Beispiel erstellen wir ein Liniendiagramm mit imaginären Coverage-Informationen. In der zweiten Zeile wird eines der Graphen-Elemente konfiguriert, einfach indem der Titel gesetzt wird. Die Elemente des Graphen, wie Legende, Titel, Hintergrund und andere haben noch mehr Konfigurationsoptionen, wie Hintergrundfarbe und Rahmen, wie später noch zu sehen sein wird.

Die Graph-Komponente kann mit verschiedenen Typen von Datensätzen umgehen, der einfachste ist das hier gezeigte Array-Dataset. Diesem kann einfach ein normales PHP-Array übergeben werden, wobei sowohl String-Indizes als auch numerische Indizes, wie hier implizit verwendet, von der Standard-Achse adäquat dargestellt werden. Der verwendete Schlüssel des data-Arrays des Graph-Objektes definiert gleichzeitig auch den Namen des Datensatzes, und ermöglicht die Identifikation, um diesen noch weiter konfigurieren zu können.

Zum Schluss wird der Graph gerendert, hierfür wird lediglich die Größe der angestrebten Ausgabe und die Zieldatei angegeben. Alle notwendigen Formatierungen und Berechnungen der Achsen werden automatisch im Hintergrund erledigt. Die Standard-Ausgabe ist SVG, weil die dafür notwendige DOM-Erweiterung in nahezu jeder PHP-Installation vorhanden ist.



Abb. 1: Einfaches Liniendiagramm
Ausgabeformate

Die Graph-Komponente unterstützt verschiedene Backends um unterschiedliche Ausgabeformate zu erzeugen. In der Graph-Komponente als Treiber bezeichnet, haben sie strukturell bedingte Vor- und Nachteile. Ein kurzer Überblick:

  • Der SVG-Treiber:
    SVG ist ein auf XML-basierendes Vektorgrafikformat, das in allen modernen Browsern darstellbar ist. Bislang braucht man für den IE 6 und 7 ein Plug-in, in Version 8 soll auch dieser es endlich nativ darstellen können. Der SVG-Treiber hat einen einzigen Nachteil, es ist bislang nicht immer möglich die Abmessung von Texten exakt zu berechnen.
  • GD-basierter Treiber:
    GD hat viele Probleme mit der Qualität bei der Generierung von Grafiken. Kein Anti-Aliasing bei den meisten Formen, teilweise interessante Effekte bei der Verwendung von Transparenz. Trotzdem ist es immer noch die am weitesten verbreitete Erweiterung und durch Supersampling weiß die Graph-Komponente einige der Fehler auszumerzen. Dazu kommt, dass sich mit GD keine Farbverläufe in akzeptabler Zeit rendern lassen, was die Graph-Komponente einiger graphischer Effekte beraubt.
  • Flash-Treiber:
    Mit der Ming-Erweiterung, die sich immer noch im Alpha-Stadium befindet, können die Graphen auch als Flash ausgegeben werden. Neben seiner proprietären Natur kann Flash alles darstellen, es werden lediglich spezielle Schriftartendateien gebraucht.
  • PNGs mit Cairo:
    Cairo ist die aktuellste Bibliothek zur Generierung von 2D-Graphiken und wird beispielsweise von GTK (Gnome) und Firefox zum Rendern verwendet. Mit der Erweiterung pecl/cairo_wrapper ist Cairo dank Hartmut Holzgraefe auch aus PHP zu verwenden und besticht durch die beste Darstellungsqualität bei hoher Rendergeschwindigkeit.

Der Geschwindigkeitsvergleich in Abbildung 2 gibt einen groben Überblick über die verfügbaren Backends, wobei bislang kein iMagick für die Graph-Komponente existiert. Das Diagramm ist natürlich auch mit der Graph-Komponente erstellt worden.



Abb. 2: Geschwindigkeitsvergleich Grafikbackends
Erweitertes Beispiel

Trotz der schlechteren graphischen Qualität, aufgrund der häufigen Verwendung, soll das letzte Beispiel erweitert werden, um einige der Anpassungsmöglichkeiten der Graph-Komponente zu zeigen:

$graph = new ezcGraphLineChart();
$graph->options->fillLines = 230;
$graph->options->highlightSize = 8;

$graph->options->font = '/path/to/font.ttf';

$graph->palette = new ezcGraphPaletteBlack();

$graph->title = 'Code Coverage';
$graph->title->margin = 2;
$graph->title->background = '#555753';

$graph->data['Executable'] = new ezcGraphArrayDataSet( array( 
	127, 253, 512, 560, 893
) );
$graph->data['Covered'] = new ezcGraphArrayDataSet( array( 
	15, 86, 412, 530, 893
) );

$graph->data['Covered']->symbol = ezcGraph::CIRCLE;
$graph->data['Executable']->highlight[4] = true;

$graph->driver = new ezcGraphGdDriver();

$graph->render( 500, 250, '02_line.png' );

In den ersten Zeilen nach dem Konstruktor werden übliche Eigenschaften konfiguriert, die abhängig vom Typen des Graphen sind. In diesem Fall wird die maximale Schriftgröße für Highlights, dazu gleich mehr, gesetzt und die Transparenz der Füllfläche unterhalb der Linien gesetzt.

Im GD-Treiber muss ein Pfad zu der zu verwendeten Schriftart-Datei gesetzt werden. Dies sollte üblicherweise zu relativ zu Beginn getan werden, denn wenn die Schrift-Konfiguration eines Elements (wie dem Titel, oder einer Achse) geändert wird, hat eine Änderung der globalen Schrift-Konfiguration keine Auswirkungen mehr auf diese und die Schriftart müsste in beiden gesetzt werden. So lassen sich allerdings auch für unterschiedliche Elemente unterschiedliche Schriftarten verwenden.

Die Graph-Komponente unterstützt Paletten, die ein generelles Farbschema für den Graphen definieren. Dies können natürlich auch eigene Klassen sein, die entsprechend verwendet werden. Abseits der Angabe einer Farbpalette können alle Farben auch manuell konfiguriert werden.
In den darauf folgenden Zeilen wird zum ersten Mal eines der Graphelemente, in diesem Fall der Titel, speziell konfiguriert. Die Box, die den Titel umgibt bekommt einen größeren Aussenabstand (margin) und eine gesonderte Hintergrundfarbe, die die Voreinstellung in der Palette überschreibt.

Danach werden, wie im letzten Beispiel, die Datensätze hinzugefügt, doch sollen auch die noch anders konfiguriert werden. Die Datensätze haben in ihrem Kontext weitere Konfigurationsoptionen, wie die zu sehenden Eigenschaften symbol und highlight. Das Symbol auch als solches nicht nur in der Legende auf, sondern auch, je nach Konfiguration und Palette, an den Datenpunkten im Graphen.

Die Option highlight wird je nach Diagrammtyp unterschiedlich interpretiert. In Balken- und Liniendiagrammen bedeutet es, dass der Wert an der entsprechenden Stelle im Graphen annotiert wird. Über die Option hihglightString kann auch ein anderer Wert zur Anzeige angegeben werden, und die Schrift kann optional wie andere Elemente auch formatiert werden. In Tortendiagrammen bedeutet es dagegen, dass das entsprechende Tortenstück ein wenig aus der Mitte herausgerückt wird.

Zum Schluss setzen wir noch, wie angekündigt, einen anderen zu verwendenden Treiber. Alle Graphen funktionieren mit allen Ausgabetreibern, lediglich die verwendete Schrift muss dabei extra berücksichtigt werden. Beim Rendern wählen wir als Dateiendung nun png. Es wird auch das Rendern von JPEGs unterstützt, doch eignet sich dieses Bildformat nicht zur Darstellung von Graphiken, wie den hier gezeigten.

Verschiedene Renderer

Das letzte Beispiel zeigte die Verwendung eines anderen Treibers, der die Ausgabe nur in ein anderes Format übersetzt. Eine Ebene davor finden sich in der Graph-Komponente die Renderer, die die Graphenprimitive, wie ein Balken, oder ein Tortenstück in Bildprimitive (Rechteck, Polygon, Kreisausschnit, ...) übersetzen. Diese Abstraktion macht es nicht nur möglich, wie gezeigt, die Ausgabetreiber zu ersetzen, sondern auch ganz andere Arten der Darstellung der Graphen, unabhängig von den Daten, zu implementieren.

Bislang existieren der gezeigte Standard-Renderer, der zweidimensonale Bilder berechnet, sowie ein 3D-Renderer, der optisch aufwändigere Graphen generiert. Die Renderer selbst lassen sich durch vielfältige Optionen noch weiter in ihrer Darstellung anpassen, wie das Beispiel eines einfachen Tortendiagrammes zeigt:

$graph = new ezcGraphPieChart();

$graph->title = 'Builds';
$graph->legend->position = ezcGraph::BOTTOM;

$graph->data['Builds'] = new ezcGraphArrayDataSet( array( 
	'Successfull Builds' => 42,
	'Failed Builds' => 5,
) );

$graph->data['Builds']->highlight['Failed Builds'] = true;
$graph->data['Builds']->color['Successfull Builds'] = '#4e9a0680';
$graph->data['Builds']->color['Failed Builds'] = '#f5900080';

$graph->renderer = new ezcGraphRenderer3d();

$graph->renderer->options->pieChartGleam = .3;
$graph->renderer->options->pieChartGleamColor = '#FFFFFF';
$graph->renderer->options->dataBorder = .2;

$graph->renderer->options->pieChartShadowSize = 5;
$graph->renderer->options->pieChartShadowColor = '#000000';

$graph->renderer->options->pieChartSymbolColor = '#55575388';
$graph->renderer->options->pieChartRotation = .7;

$graph->driver = new ezcGraphCairoDriver();

$graph->render( 500, 250, '03_pie.png' );

Dass ein Tortendiagramm erzeugt werden soll, spezifiziert erneut direkt der Konstruktor des Graphens. In der dritten Zeile des Code-Beispieles ist eine weitere übliche Konfigurationsmöglichkeit für die Elemente im Graphen gezeigt; die Legende wird nicht, wie es der Standard ist, links platziert, sondern unterhalb des Diagramms.

Ein Tortendiagramm kann nur einen Datensatz aufnehmen, dessen Werte die jeweiligen Tortenstücke definieren. Normalerweise wird die Summe automatisch errechnet, jedoch lässt sich hier auch eine eigene Summe vorgeben, so dass die angegebenen Stücke die Torte nicht vervollständige, oder es lassen sich Tortenstücke unterhalb einer definierten Schwelle zusammenfassen.

Um Erfolg und Misserfolg der Builds eindeutig zu kennzeichnen, färben wir die entsprechenden Datensätze über die Eigenschaft color ein. Farben lassen sich als Array oder, wie hier gezeigt, in der üblichen Hexadezimal-Schreibweise angeben. Wenn ein vierter Wert (hier 80) angegeben wird, ist das die Transparenz der Farbe - alle Treiber können mit RGBA (red, green, blue, alpha) umgehen.

Wie versprochen verwendet das Beispiel den 3D-Renderer, wobei wir noch einige zusätzliche spezielle Konfigurationsoptionen setzen, um Schatten unterhalb der Tortenstücke hinzuzufügen und einen Glanzeffekt auf den Tortenstücken zu rendern. Dazu verwenden wir den neuen Cairo-Treiber, der uns ein sehr ansehnliches PNG liefert.



Abb. 4: Dreidimensionales Tortendiagramm
Achsen, Balken und Datensätze

In den bisherigen Beispielen ging es maßgeblich um eine Modifikation der Ausgabe der Graphen. Ein zweiter wesentlicher Punkt ist jedoch die korrekte Verarbeitung von Eingaben, um beispielsweise die Achsenskalierung optimal wählen zu können. Um im Kontext der Beispiele zu bleiben zeigen wir einen Graphen, der die Test-Ausführungszeit im Verlaufe der Builds darstellt:

$graph = new ezcGraphBarChart();
$graph->options->fillLines = 210;

$graph->title = 'Test Execution Time';

$graph->data['Measured'] = new ezcGraphArrayDataSet( array( 
	17, 19, 62, 417, 83, 76
) );
$graph->data['Average'] = new ezcGraphDataSetAveragePolynom(
	$graph->data['Measured'], 3
);

$graph->data['Average']->displayType = ezcGraph::LINE;

$graph->xAxis = new ezcGraphChartElementNumericAxis();
$graph->xAxis->axisLabelRenderer = new ezcGraphAxisBoxedLabelRenderer();
$graph->xAxis->minorStep = 1;

$graph->yAxis = new ezcGraphChartElementDateAxis();
$graph->yAxis->axisLabelRenderer = new ezcGraphAxisCenteredLabelRenderer();
$graph->yAxis->dateFormat = 'i\\m';

$graph->yAxis->font->maxFontSize = 10;

$graph->driver = new ezcGraphCairoDriver();

$graph->render( 500, 250, '04_bar.png' );

Dieses Beispiel zeigt einige der weiteren möglichen Anpassungen des Graphen. Wir beginnen mit dem Konstruktor eines Balkendiagrammes und setzen die "fillLines" Option und den Titel, wie bekannt. Danach fügen wir einen Datensatz mit den gemessenen Testlaufzeiten in Sekunden ein.

Wie angesprochen existieren noch andere Typen von Datensätzen, wie das hier verwendete ezcGraphDataSetAveragePolynom, welches dazu dient über die Methode der kleinsten Quadrate ein Polynom zu errechnen, dass den gegebenen Datensatz annähert. Hier erzeugen wir ein Polynom des Grades 3, das versuchen soll die Entwicklung der Testlaufzeiten aufzuzeigen.

Obwohl der Graph-Typ als Balkendiagramm durch den Konstruktor gesetzt ist, können einzelne Datensätze als Linien deklariert werden, umgekehrt können genauso einzelne Datensätze als Balken in einem Liniendiagramm deklariert werden.

Entsprechend der Daten sollen andere Achsentypen verwendet werden. Im Normalfall wird für die X-Achse die LabeledAxis verwendet, die alle Schlüssel als Strings behandelt und in der benutzergegebenen Reihenfolge listet. Für die Y-Achse wird die NumericAxis verwendet, die auf die Darstellung von Zahlen spezialisiert ist. In diesem Fall verwenden wir die Achse für numerische Werte auf der X-Achse und die DateAxis auf der Y-Achse. Die Datumsachse ist zur Darstellung von Datumsspannen gedacht, und kann auch mit unregelmäßigen Intervallen wie Monaten (die ja nicht alle gleich lang sind) umgehen, und die Werte an den Achsen werden mittels der PHP-Funktion date(), oder über ein eigenes Callback formatiert.

Nicht nur die Achsen können konfiguriert werden um besser mit den gegebenen Werten umgehen zu können, sondern kann auch der Renderer bestimmt werden, mit dem die Label (Beschriftungen) auf der Achse angeordnet werden. Für die X-Achse in Balkendiagrammen ist der BoxedLabelRenderer gedacht, der die Beschriftung zwischen den Ticks platziert. Für die Y-Achse verwenden wir hier auch einen anderen Renderer, der Unterschied sollte sich durch Namen und Vergleich mit den vorigen Bildern erschließen.



Abb. 4: Kombiniertes Balken-/Liniendiagramm

Wie schon kurz im Bezug auf den GD-Treiber erwähnt lassen sich die verwendete Schrift-Konfiguration für jedes Graph-Element einzeln anpassen. Auch die Achsen sind solche Graph-Elemente, mit einer Besonderheit: Die Schriftgröße in der X- und Y-Achse werden einander angeglichen. Zum Ende des obigen Beispiels wird eine maximale Schriftgröße für die Achsenbeschriftung gesetzt.

Normalerweise wählt die Graph-Komponente in jeder Gruppe von Texten automatisch die maximale Schriftgröße um sich Skalierungen der Ausgabe anzupassen. Wenn die minimale Schriftgröße nicht reicht, um Text im zugewiesenen Rahmen unterzubringen, mag dieser gekürzt werden. Damit der Benutzer der Komponente dieses Verhalten beeinflussen kann, lassen sich sowohl minimale als auch maximale Schriftgröße pro Element oder für den ganzen Graphen konfigurieren.

Datensätze

Bislang zu sehen war hauptsächlich das Array-Dataset. Eigene Datensätze lassen sich jederzeit durch Ableiten von der Klasse ezcGraphDataSet selbst definieren, mitgeliefert werden noch die Folgenden:

  • ezcGraphNumericDataSet
    
Dem Konstruktor dieses Datesatz kann ein Callback für eine PHP-Funktion übergeben werden, die Aufgrund einer Eingabe eine Ausgabe errechnet. In einem anzugebenden Wertebereich lassen sich damit zum Beispiel mathematische Funktionen darstellen.
  • ezcGraphDataSetAveragePolynom
    
Der bereits erwähnte Datensatz kann aus existierenden Datensätzen ein Polynom ermitteln, dass über einen üblichen Algorithmus versucht eine Tendenz in der Entwicklung aufzuzeigen.
  • ezcGraphDatabaseDataSet
    
Diesem Datensatz kann ein PDOStatement übergeben werden, um Daten aus der Datenbank direkt in einem Graphen anzeigen zu lassen.
Fazit

Weitere Achsentypen, Diagrammtypen und einfache eigene Erweiterungen, wie das Erstellen einer eigenen Palette, werden im nächsten Teil dieses Artikels in der Ausgabe 4.08 des PHP Magazins gezeigt. Dazu kommen Möglichkeiten, direkt interaktive, das heisst klickbare, Diagramme zu schaffen.

Mit den Treibern, den Renderern, und der erweiterbaren Struktur lässt sich die Graph-Komponente aus den eZ Components einfach um zusätzliche Features erweitern, sie bietet aber auch jetzt schon alle üblichen Graphentypen. Die hohe graphische Qualität der Ausgabe macht sie optimal für den Einsatz auf Webseiten mit einem gehobenen Anspruch an den visuellen Eindruck.

Kore Nordmann ist Autor der Graph-Komponente und arbeitet für eZ Systems am eZ Components-Projekt mit. Er studiert Informatik an der Universität Dortmund, ist Freelancer, Autor und regelmäßiger Sprecher auf verschiedenen Konferenzen; entwickelt, leitet oder beteiligt sich an zahlreichen Open-Source-Projekten, wie WCV, Torii, Image_3D, eZ Components, PHPUnit, busimess und Weiteren.

 

Kommentare

Ihr Kommentar zum Thema

Als Gast kommentieren:

Gastkommentare werden nach redaktioneller Prüfung freigegeben (bitte Policy beachten).