Grundlagen CGI-Programmierung mit Perl


von Prof. Jürgen Plate

6 Grafiken erzeugen mit dem GD-Modul

Vorbemerkung: Damit diese Programme funktionieren, muß das GD-Modul von Perl installiert sein. Weiterhin braucht man die GD-Bibliothek von Thomas Boutell (http://www.boutell.com/gd/), die ihrerseits die Bibliotheken Libpng und Zlib benötigt. Sie müssen also zunächst die drei Bibliotheken installieren (mit apt-get, yum etc.) und danach das GD-Modul von Perl.

Es gibt zahlreiche Anwendungen, bei denen Bilder zur Laufzeit des Programms manipuliert oder erzeugt werden sollen, z.B.:

Dabei generiert Perl das Bild und speichert es entweder ab oder schickt es an ein Programm, welches in der Lage ist, diese Bilder darzustellen. Bei CGI-Skripten erfolgt die Ausgabe auf STDOUT. Wahlweise können gif-und png-Bilder sowie Bilder im eigenen gd-Format erzeugt werden. Ein einfaches Bild wird von folgenden Skript erzeugt:
use GD;
use strict;

my $bild = new GD::Image(100,100); 
my $gray = $bild->colorAllocate(200,200,200);
my $blau = $bild->colorAllocate(0,0,255); 
my $rot = $bild->colorAllocate(255,0,0); 
$bild->rectangle(20,20,80,80,$rot);
$bild->rectangle(30,30,70,70,$blau);

binmode STDOUT;    # nur bei Windoof noetig
print "Content-type: image/gif\n\n";
print $bild->gif;

Im Programm wird zuerst das GD-Modul geladen. Dann wird ein (noch virtuelles Bild der Größe 100 x 100 Pixel erzeugt). Die erste der drei erzeugten Palettenfarben bildet die Hintergrundfarbe. Danach wird ein rotes und ein blaues Rechteck gemalt.

Nachdem mit "use GD" das GD-Modul eingebunden wurde, bildet das Programm eine Instanz der Klasse GD::Image. Diesem Objekt ordnen wir dann drei Farben zu. Die Methode collorAllocate hat drei Parameter, und zwar der jeweilige 8-Bit-Wert für rot, grün und blau. Jede Farbe wird als eine Mischung aus diesen drei Grundfarben definiert. Die erste Farbe, die zugewiesen wird, ist immer die Hintergrundfarbe des Bildes. Die Werte für die anderen Farben werden in Variablen abgespeichert und können dann zugewiesen werden. Weitere Methoden in diesem Zusammenhang sind:

Bei der Farbzuordnung kann man eine Farbe, meist die Hintergrundfarbe auf "transparent" setzen:

$bild->transparent($gray);
Soll das Bild sich Schritt für Schritt aufbauen, setzt man zusätzlich das Bild auf "interlaced":
$bild->interlaced('true');
Die im Beispiel verwendete Methode rectangle hat insgesamt fünf Parameter. Die ersten zwei Parameter definieren die linke obere Ecke, die folgenden zwei die rechte untere Ecke und der letzte Parameter die Farbe.

Wie Linien gezeichnet werden, zeigt das folgende Beispiel. Die Methode line hat fünf Parameter: die Koordinaten von Anfangs- und Endpunkt sowie wieder die Farbe.

use GD;
use strict;
my $bild = new GD::Image(100,100); 
my $gray = $bild->colorAllocate(200,200,200);
my $blau = $bild->colorAllocate(0,0,255); 
my $rot = $bild->colorAllocate(255,0,0); 
my $gruen = $bild->colorAllocate(0,255,0);
$bild->rectangle(20,20,80,80,$rot);
$bild->line(20,20,80,80,$gruen); 
$bild->line(80,20,20,80,$gruen); 
$bild->line(50,20,50,80,$gruen); 
$bild->line(20,50,80,50,$gruen); 

binmode STDOUT;    # nur bei Windoof noetig
print "Content-type: image/gif\n\n";
print $bild->gif;

Die Linienbreite von Linie, Rechteck und Kreisbogen kann mittels setThickness($thickness) verändert werden.

Eine strichlierte Linke bekommt man mit dashedLine anstelle von line. Weitere Linienmuster lassen sich mit setStyle erzeugen. Diese Methode hat ein Array von Farbwerten als Parameter. Das so erzeugte Linienmuster kann bei Linie, Rechteck und Kreisbogen anstelle einer Farbe angegeben werden. Dazu ein Beispiel:

use GD;
use strict;
my $bild = new GD::Image(100,100); 
my $gray = $bild->colorAllocate(200,200,200);
my $blue = $bild->colorAllocate(0,0,255); 
my $red = $bild->colorAllocate(255,0,0); 
my $green = $bild->colorAllocate(0,255,0);
my $yellow = $bild->colorAllocate(255,255,0);

my @linestyle = ($blue,$blue,     # 2 Pixel blau
                 $red,$red,       # 2 Pixel rot
                 $yellow,$yellow, # usw.
                 gdTransparent,gdTransparent);

$bild->rectangle(20,20,80,80,$red);
$bild->setStyle(@linestyle);
$bild->rectangle(25,25,75,75,gdStyled);
$bild->rectangle(24,24,74,74,gdStyled);

binmode STDOUT;    # nur bei Windoof noetig
print "Content-type: image/gif\n\n";
print $bild->gif;

Das Rechteck kann auch ausgefüllt werden, wenn man statt der Methode rectangle nun filledRectangle verwendet. Die Parameter sind bei beiden Methoden gleich.

use GD;
use strict;
my $bild = new GD::Image(100,100); 
my $gray = $bild->colorAllocate(200,200,200);
my $blau = $bild->colorAllocate(0,0,255); 
my $rot = $bild->colorAllocate(255,0,0); 
my $gruen = $bild->colorAllocate(0,255,0);
$bild->filledRectangle(20,20,50,50,$rot);
$bild->filledRectangle(50,50,80,80,$rot);
$bild->filledRectangle(20,50,50,80,$blau);
$bild->filledRectangle(50,20,80,50,$blau);

binmode STDOUT;    # nur bei Windoof noetig
print "Content-type: image/gif\n\n";
print $bild->gif;

Meist enthält ein Bild auch noch Text, der in der Regel ebenfalls dynamisch erzeugt werden soll. Wie Text in ein Bild eingefügt wird, zeigt folgendes Skript:

use GD;
use strict;

my $bild = new GD::Image(100,100); 
my $gray = $bild->colorAllocate(200,200,200);
my $blau = $bild->colorAllocate(0,0,255); 
my $rot = $bild->colorAllocate(255,0,0); 
$bild->rectangle(20,30,80,80,$rot);
$bild->string(gdLargeFont,2,5,"Hello World",$blau);

binmode STDOUT;    # nur bei Windoof noetig
print "Content-type: image/gif\n\n";
print $bild->gif;

Die Methode string hat vier Parameter, Font, Startposition (X,Y), den auszugebenden Text und die Textfarbe. Eine Feinsteuerung der Schrift ist nicht möglich, man hat lediglich die Auswahl zwischen gdTinyFont (5x8), gdSmallFont (6x12), gdMediumBoldFont (7x13) gdLargeFont (8x16) und gdGiantFont (9x15).

use GD;
use strict;

my $bild = new GD::Image(100,100); 
my $gray = $bild->colorAllocate(200,200,200);
my $blau = $bild->colorAllocate(0,0,255); 
my $rot = $bild->colorAllocate(255,0,0); 
$bild->string(gdTinyFont,10,10,"Hello World",$blau);
$bild->string(gdSmallFont,10,25,"Hello World",$blau);
$bild->string(gdMediumBoldFont,10,40,"Hello World",$blau);
$bild->string(gdLargeFont,10,70,"Hello World",$blau);

binmode STDOUT;    # nur bei Windoof noetig
print "Content-type: image/gif\n\n";
print $bild->gif;

Mittels stringUp kann auch ein vertikaler Text (Laufrichtung von unten nach oben) gezeichnet werden. Leider sind die Stringmethoden recht simpel, es gibt also keinen mehrzeiligen Text usw. Man kann sich aber die Zeichengröße ausgeben lassen und dann für jede Zeile entsprechend positionieren:

($breite,$hoehe) = (gdSmallFont->width,gdSmallFont->height);
Es gibt jedoch nicht nur Linien und Rechtecke, sondern es lassen sich auch Kreise und Ellipsen zeichnen. Dazu dient die Methode arc, die im folgenden Beispiel gleich dreimal aufgerufen wird, um einen gut sichtbaren Kreis zu zeichnen. Die Bedeutung der sieben Parameter ist Mitte-X, Mitte-Y, Breite-X, Breite-Y, Startwinkel, Winkel-Bogen und wieder die Farbe.
use GD;
use strict;
my $bild = new GD::Image(100,100); 
my $gray = $bild->colorAllocate(200,200,200);
my $blau = $bild->colorAllocate(0,0,255); 
my $rot = $bild->colorAllocate(255,0,0); 
my $gruen = $bild->colorAllocate(0,255,0);
$bild->filledRectangle(20,20,50,50,$rot);
$bild->filledRectangle(50,50,80,80,$rot);
$bild->filledRectangle(20,50,50,80,$blau);
$bild->filledRectangle(50,20,80,50,$blau);
$bild->arc(50,50,50,50,0,360,$gruen);
$bild->arc(50,50,51,51,0,360,$gruen);
$bild->arc(50,50,52,52,0,360,$gruen);

binmode STDOUT;    # nur bei Windoof noetig
print "Content-type: image/gif\n\n";
print $bild->gif;

Mitte-X und Mitte-Y bezeichen den Mittelpunkt des Bogens, Breite-X und Breite-Y jeweils die Ausdehnung in der X- und Y-Achse. Der Startwinkel legt fest, wo der Bogen beginnen soll (Koordinatensystem wie in der Mathematik üblich; 0 Grad = waagrecht rechts). Danach folgt die Angabe, wieviele Grad der Bogen durchlaufen soll (für einen Quadranten z.B. 90). Dazu noch ein Beispiel.

use GD;
use strict;

my $bild = new GD::Image(100,100); 
my $gray = $bild->colorAllocate(200,200,200);
my $blau = $bild->colorAllocate(0,0,255); 
my $rot = $bild->colorAllocate(255,0,0); 
my $gruen = $bild->colorAllocate(0,255,0);
my $gelb = $bild->colorAllocate(255,255,0);
$bild->filledRectangle(20,20,50,50,$rot);
$bild->filledRectangle(50,50,80,80,$rot);
$bild->filledRectangle(20,50,50,80,$blau);
$bild->filledRectangle(50,20,80,50,$blau);
# gruene Ellipse unten
$bild->arc(50,50,50,30,0,180,$gruen);
#gelber Kreis oben
$bild->arc(50,50,50,50,181,360,$gelb);

binmode STDOUT;    # nur bei Windoof noetig
print "Content-type: image/gif\n\n";
print $bild->gif;

Schliesslich kann man mit setPixel(x,y,farbe) auch einzelne Pixel setzen.

Für das Zeichen von Polygonzügen, beispielsweise für die Darstellung vom Messwerten, muss zuerst ein neues Objekt definiert werden:

$poly = new GD::Polygon;
Mittels der Methode addPt kann ein Stützpunkt zum Polygon hinzigefügt werden (z.B. $poly ->addPt(10,5);). Ruft man $bild->polygon($poly,$rot); auf, werden alle Punkte miteinander verbunden. Es entsteht ein Polygon. Wer will, kann dieses Polygon auch ausfüllen (filledPolygon). Der Polygonzug wird immer geschlossen.
use GD;
use strict;

my $bild = new GD::Image(100,100); 
my $gray = $bild->colorAllocate(200,200,200);
my $blau = $bild->colorAllocate(0,0,255); 
my $rot = $bild->colorAllocate(255,0,0); 
my $gruen = $bild->colorAllocate(0,255,0);
my $gelb = $bild->colorAllocate(255,255,0);

my $poly = new GD::Polygon;
$poly ->addPt(80,80);
$poly ->addPt(20,70);
$poly ->addPt(30,50);
$poly ->addPt(40,15);
$poly ->addPt(50,60);
$poly ->addPt(15,20);
$bild->polygon($poly,$rot);

binmode STDOUT;    # nur bei Windoof noetig
print "Content-type: image/gif\n\n";
print $bild->gif;
Links der einfache Polygonzug, rechts gefüllt:

Die einzelnen Stützpunkte werden beginnend von 0 an durchnumeriert. Man kann sich auch ein Rechteck geben lassen, welches das Polygon exakt umschliesst:

my @boundingbox = $poly->bounds;
$bild->rectangle(@boundingbox, $blau);
Weitere Methoden sind:

Beim Anlegen eines neuen Bildes ist man nicht auf ein leeres Bild beschränkt, es lassen sich vielmehr auch vorhandene Bilder (GD-, Gif, Jpeg-, Xbm-Format) laden. newFromGif(FH): Erzeugt ein Image aus der GIF-Datei, die durch das Dateihandle FH angegeben wurde. Das Dateihandle muß vorher mit einer gültigen GIF-Datei oder Pipe geöffnet worden sein. Bei Erfolg gibt dieser Aufruf ein initialisiertes Image zurück. Schlägt der Aufruf fehl, wird undef zurückgegeben. Z.B.:

open (GIF,"bildchen.gif") || die;
$bild = newFromGif GD::Image(\*GIF) || die;
close GIF;
Die Methoden newFromXbm(FILEHANDLE), newFromGd(FILEHANDLE) und newFromGd(FILEHANDLE) funktionieren auf genau die gleiche Weise. GD ist Tom Boutells plattenbasiertes Speicherformat. Es ist für die seltenen Fälle gedacht, in denen Sie Images schnell von/auf Platte lesen/schreiben müssen. Es ist nicht zur regulären Nutzung gedacht, weil es im Gegensatz zu GIF oder JPEG keinerlei Komprimierung durchführt, d.h., die Dateien können sehr groß werden.

Man kann Linien und Formen (Shapes) mit einem sogenannten "Brush-Pattern" zeichnen. Brushes ("Pinsel") sind einfach Bilder, die auf die übliche Art und Weise erzeugt und bearbeitet werden. Wenn man mit ihnen zeichnet, wird ihr Inhalt für Farbe und Form der Linien verwendet. Um eine gemusterte Linie zu zeichnen, erzeugen oder laden Sie zuerst einen Brush und weisen ihn dann mit setBrush() einem Image zu. Das Zeichnen mit diesem Muster wird dann über die spezielle Farbe gdBrushed festgelegt. Es ist meist günstig, den Hintergrund des Brush transparent zu halten, damit die nicht-farbigen Teile andere Teile Ihres Bildes nicht abdecken.

use GD;
use strict;
my $bild = new GD::Image(200,200); 
my $gray = $bild->colorAllocate(200,200,200);
my $blue = $bild->colorAllocate(0,0,255); 
my $red = $bild->colorAllocate(255,0,0); 
my $green = $bild->colorAllocate(0,255,0);
my $yellow = $bild->colorAllocate(255,255,0);


# Brush mit diagonalem Muster
my $brush = new GD::Image(5,5);
my $white = $brush->colorAllocate(255,255,255);
my $black = $brush->colorAllocate(0,0,0);
$brush->transparent($white);
$brush->line(0,4,4,0,$black);
 
$bild->setBrush($brush);
$bild->arc(100,100,90,90,0,360,gdBrushed);

binmode STDOUT;    # nur bei Windoof noetig
print "Content-type: image/gif\n\n";
print $bild->gif;

Es lassen sich auch gefüllte Umrisse und Flood-Fills mit Hilfe eines Musters zeichnen. Das Muster ist, wie zuvor, einfach ein weiteres Bild. Das Muster wird wiederholt ausgegeben ("gekachelt"), um den benötigten Platz zu füllen. setTile wird aufgerufen, um das jeweilige Füllmuster zu definieren und das Muster wird über die Farbe gdTiled angesprochen.

use GD;
use strict;
my $bild = new GD::Image(200,200); 
my $gray = $bild->colorAllocate(200,200,200);
my $blue = $bild->colorAllocate(0,0,255); 
my $red = $bild->colorAllocate(255,0,0); 
my $green = $bild->colorAllocate(0,255,0);
my $yellow = $bild->colorAllocate(255,255,0);


# Brush mit diagonalem Muster
my $brush = new GD::Image(5,5);
my $w = $brush->colorAllocate(255,255,255);
my $b = $brush->colorAllocate(0,0,255); 
my $r = $brush->colorAllocate(255,0,0); 
$brush->transparent($w);
$brush->line(0,4,4,0,$b);
$brush->line(0,0,4,4,$r);
 
$bild->setTile($brush);
$bild->filledRectangle(20,20,180,180,gdTiled);

binmode STDOUT; 
print "Content-type: image/gif\n\n";
print $bild->gif;

Das Zeichen von Linien oder Füllen von Rechtecken macht ja noch keine sinnvolle Grafik. Versuchen wir mal den Verlauf einer Kurve zu zeichen, z.B. den Umsatz einer Firma über die Zeit. Es bietet sich an, so etwas mit der Polygonfunktion zu machen:

use GD;
use strict;
my $bild = new GD::Image(300,200); 
my $gray = $bild->colorAllocate(200,200,200);
my $blue = $bild->colorAllocate(0,0,255); 
my $red = $bild->colorAllocate(255,0,0); 
my $black = $bild->colorAllocate(0,0,0);

my $poly = new GD::Polygon;
$poly ->addPt(21,170);
$poly ->addPt(105,120);
$poly->addPt(155,80);
$poly->addPt(205,130);
$poly->addPt(255,110);
$poly->addPt(290,50);
$bild->polygon($poly,$red);

$bild->string(gdLargeFont,120,180,"Zeit",$blue);
$bild->stringUp(gdLargeFont,2,120,"Umsatz",$blue);
$bild->line(20,170,290,170,$black); 
$bild->line(20,170,20,20,$black); 

binmode STDOUT;
print $bild->gif;

Das funktioniert so natürlich nicht! Polygone sind immer geschlossen, folglich wird das Polygon automatisch geschlossen, und das sieht nicht besonders toll aus. Der Trick ist, das Polygon um zwei Linien zu ergänzen. Die erste führt nach unten bis zu der Stelle, an der die horizontale Achse gezeichnet wird, die zweite zurück nach links. Diese beiden Polygonlinien ziehen wir nochmals mit der Hintergrundfarbe nach.

use GD;
use strict;
my $bild = new GD::Image(300,200); 
my $gray = $bild->colorAllocate(200,200,200);
my $blue = $bild->colorAllocate(0,0,255); 
my $red = $bild->colorAllocate(255,0,0); 
my $black = $bild->colorAllocate(0,0,0);

my $poly = new GD::Polygon;
$poly ->addPt(21,170);
$poly ->addPt(105,120);
$poly->addPt(155,80);
$poly->addPt(205,130);
$poly->addPt(255,110);
$poly->addPt(290,50);
# die beiden zusaetlichen Punkte
$poly->addPt(290,170);
$poly->addPt(21,170);
$bild->polygon($poly,$red);
# abdecken
$bild->line(290,50,290,170,$gray); 
$bild->line(290,170,21,170,$gray); 

# und die Legende
$bild->string(gdLargeFont,120,180,"Zeit",$blue);
$bild->stringUp(gdLargeFont,2,120,"Umsatz",$blue);

$bild->line(20,170,290,170,$black); 
$bild->line(20,170,20,20,$black); 

binmode STDOUT;
print $bild->gif;
Das linke Bild zeigt das Ergebnis des Programmlaufs, das recht eine Alternative mit gefülltem Polygon.

Man kann mit dem GD-Mudul auch einen Teil eines Bildes ausschneiden und in ein anderes Bild hineinkopieren. Dazu dient die Methode copy:

copy(SrcImage, ZielX, ZielY, QuelleX, QuelleY, QBreite, QHoehe)
Die Funktion copy hat also sieben Parameter.
use GD;
use strict;
my $bild = new GD::Image(100,100); 
my $gray = $bild->colorAllocate(200,200,200);
my $blue = $bild->colorAllocate(0,0,255); 
my $red = $bild->colorAllocate(255,0,0); 
my $black = $bild->colorAllocate(0,0,0);

# Quellbild (blauer Hintergrund, lauter rote Punkte
my $quelle = new GD::Image(100,100);
my $b = $quelle->colorAllocate(0,0,255); 
my $r = $quelle->colorAllocate(255,0,0); 
for (my $i=0;$i<=100;$i=$i+2)
  {
  for (my $j=0;$j<=100;$j=$j+2)
    { $quelle->setPixel($i,$j,$r); }
  }

# Qell-Ausschnitt (40x30 Pixel) an zwei verschiedene
# Stellen kopieren
$bild->copy($quelle,0,0,0,0,40,30);
$bild->copy($quelle,50,50,0,0,40,30);

binmode STDOUT;
print $bild->gif;

Im folgenden Beispiel wird ein Ausschnitt aus dem Quellbild nicht einfach in das Zielbild eingefügt, sondern bei dieser Gelegenheit auch noch in seiner Größe verändert. Die Methode copyResized besitzt daher noch zwei zusätzliche Parameter für Breite und Hoehe beim Zielbild:

copy(SrcImage, ZielX, ZielY, QuelleX, QuelleY, ZBreite, ZHoehe, QBreite, QHoehe)
Im folgenden Beispiel verdoppeln wir einmal die Größe des Quellbildes und einmal halbieren wir sie:
use GD;
use strict;
my $bild = new GD::Image(100,100); 
my $gray = $bild->colorAllocate(200,200,200);
my $blue = $bild->colorAllocate(0,0,255); 
my $red = $bild->colorAllocate(255,0,0); 
my $black = $bild->colorAllocate(0,0,0);

# Quellbild (blauer Hintergrund, lauter rote Punkte
my $quelle = new GD::Image(100,100);
my $b = $quelle->colorAllocate(0,0,255); 
my $r = $quelle->colorAllocate(255,0,0); 
for (my $i=0;$i<=100;$i=$i+2)
  {
  for (my $j=0;$j<=100;$j=$j+2)
    { $quelle->setPixel($i,$j,$r); }
  }

# Qell-Ausschnitt (40x30 Pixel) an zwei verschiedene
# Stellen kopieren
$bild->copyResized($quelle,0,0,0,0,80,60,40,30); # doppelt gross
$bild->copyResized($quelle,50,50,0,0,20,15,40,30); # halbiert

binmode STDOUT;
print $bild->gif;

Wie man sieht, sind bei der Halbierung die roten Punkte verschwunden, weil jedes zweite Pixel ausgelassen wurde.

Das GD-Modul stellt die Grafikpromitive zur Verfügung, auf die sich zahlreiche andere Perl-Module stützen, die dann das Zeichnen von zwei- oder dreidimensionalen Charts, Barcodes, usw. ermöglichen. Dazu nur ein Beispiel:

Das folgende Programm ermöglicht es, schnell mal nebenbei Datenreihen zu visualisieren. So eröffnet Perl die Möglichkeit, ein Diagramm on-the-fly per CGI-Skript produzieren zu lassen. Basierend auf dem GD-Grafikpaket hat David Bonner ein Chart-Modul erstellt, das uns fast alle Arbeit abnimmt. Tatsächlich gibt es aber kein Chart-Modul. Die unterschiedlichen Chart-Typen (Points, Lines, Bars, LinesPoint, StackedBars, Pie etc.) sind selbst Klassen und erben eine Reihe von Methoden der Klasse Chart::Base. Ersetzen Sie einfach das Wort "Typ" durch den gewünschten Chart-Typ und fertig. Beispielsweise würde der Befehl use Chart::Lines das Lines-Modul aufrufen. Da die Methoden sich gleichen, kann man durch Wechsel des Moduls auch problemlos andere Grafiken erzeugen.

Mit der Kommandozeile chart.pl < data wird das Programm ausgeführt. Die Datei data hat folgenden Aufbau, wobei einzelne Items innerhalb einer Zeile durch Strichpunkte getrennt werden (Leerzeichen rechts und links vom Strichpunkt stören nicht):

1. Zeile: Dateiname (ohne Endung) der Ausgabedatei für die Grafik
2. Zeile: Überschrift der Chart, danach der Typ der Grafik ("L" für Linien, "B" für Balken oder "p" für Punkte), gegebenenfalls gefolgt von einem "g", falls Gitterlinien gewünscht werden. Werden diese Angaben weggelassen, erzeugt das Programm eine Liniengrafik ohne Gitterlinien.
3. Zeile: Überschrift der Y-Achse, bei Bedarf gefolgt von Minimum und Maximum der Y-Achsenskala (z. B. Bruttoumsatz;0;150)
4. Zeile: Überschrift der X-Achse
5. Zeile: X-Achsen-Tics, d. h. die Werteskala der X-Achse. Hier müssen genauso viele Werte angegeben werden, wie Zahlenwerte in den folgenden Zeilen (z. B. 2000; 2001; 2002; 2003; 2004)
6. und folgende Zeilen: Datenwerte für eine Kurve. der erste Wert dient der Legende, danach folgen die einzelnen Werte.

Eine Eingabedatei für das Chart-Programm könnte beispielsweise folgendermaßen aussehen:

Umsatz
Umsatzentwicklung;p;n
Bruttoumsatz;0;150
Jahr
2000;2001;2002;2003;2004
eins;10;20;30;40
zwei;20;30;40;50
drei;30;60;90;120
vier;15;30;45;60 
Solche Dateien lassen sich schnell von Hand erzeugen, aber auch recht leicht per Programm generieren, denn recht oft haben Programme, die mit Datenmanipulation zu tun haben, eine Export-Möglichkeit im CSV-Format.
#!/usr/bin/perl -w
use strict;
use Chart::Lines;
use Chart::Points;
use Chart::Bars;

my (@achse,@legende,@felder);
my ($datei,$diagr,$titel,$typ,
    $xachse,$yachse,$grid,$maxy,$miny);
 
 # 1. Zeile: Name der Ausgabedatei
 chomp($datei = <>);
 
 # 2. Zeile: Titel;Typ [L,P,B];Gridlines
 chomp($titel = <>);
 ($titel,$typ,$grid) = split(/\s*;\s*/,$titel);
 $typ = uc($typ);
 $typ = 'L' unless(defined($typ));
 if ($typ eq 'P')
   { $diagr = Chart::Points -> new(600,350); }
 elsif ($typ eq 'B')
   { $diagr = Chart::Bars -> new(600,350); }
 else # 'L'
   { $diagr = Chart::Lines -> new(600,350); }
 
 # 3. Zeile: Y-Achsen-Legende, ggf. min. und max. Y-Wert
 chomp($yachse = <>);
 ($yachse,$miny,$maxy) = split(/\s*;\s*/,$yachse);
 
 # 4. Zeile: X-Achsen-Legende
 chomp($xachse = <>);
 
 # 5. Zeile: n X-Achsenticks
 chomp($_ = <>);
 @achse = split(/\s*;\s*/);
 shift(@achse); # 1. Feld = Dummy
 $diagr->add_dataset(@achse);
 
 # ab 6. Zeile: Datensaetze, jeweils n pro Zeile, erster
 # Datenwert = Bezeichnung
 while (<>) 
   {
   chomp;
   @felder = split(/\s*;\s*/);
   push(@legende, shift(@felder)); # 1. Wert
   $diagr->add_dataset(@felder);
   }
 
 $diagr->set('title' => $titel);
 $diagr->set('x_label' => $xachse);
 $diagr->set('y_label' => $yachse);
 $diagr->set('transparent' => 'true');
 $diagr->set('legend_labels' => \@legende);
 $diagr->set('y_ticks' => '10');
 $diagr->set('grid_lines' => 'true') if (uc($grid) eq 'Y');
 $diagr->set('min_val' => $miny) if (defined($miny));
 $diagr->set('max_val' => $maxy) if (defined($maxy));
 $diagr->png($datei . '.png');
Die beiden folgenden Grafiken zeigen Beispielausgaben:

Zum vorhergehenden Abschnitt Zum Inhaltsverzeichnis Zum nächsten Abschnitt


Copyright © Hochschule München, FK 04, Prof. Jürgen Plate
Letzte Aktualisierung: