Array to Graph

Immerwieder passiert es. Man hat Daten die man gerne in einem Diagramm darstellen möchte. Da dies mitunter recht schwer sein kann, habe ich mir meine eigene PHP-Funktion geschrieben, die es mir ermöglicht ein Array direkt mit einem Diagramm zu visualisieren.

Um eine solche Funktion zu Programmieren benötigen wir mehrere Teile.

0. Der Funktionskopf sieht so aus

function arraytograph($data,$w=300,$h=100,$show_keys = true,$label = '')

1. Prüfen ob die übergebenen Werte gefüllt sind. Des Weiteren prüfen wir, welche Achsenbeschriftung angezeigt werden soll. Auch definieren wir hier den Standard Abstand nach oben und unten.

	if (!isset($data) || empty($data))	{
		return FALSE;
	}
	if($label == 'value'){
		$show_precent = false;
		$show_number = true;
	}
	elseif($label == 'percent'){
		$show_precent = true;
		$show_number = false;
	}
	else{
		$show_precent = true;
		$show_number = false;
	}
	$pic_border_top = 5;
	$pic_border_bottom = 5;

2. Die Schlüssel des Arrays holen, um die Achsenbeschriftung zu zeichnen

	// getting the keys from array
	$keys = array_keys($data);

3. Um den Platz für die Achsenbeschriftung zu machen, wird der Längste Schlüssel gesucht. Um genug Platz zu haben, wird die pro Zeichen 6 Pixel gerechnet. Damit die Schrift nicht am Boden “aufsetzt” werden nochmals 12 Pixel hinzugerechnet.

	// the keys should be displayed, so add a stripe for displaying the keys
	// find the longest key. the length of string *6pixel per letter are added to the border bottom
	if($show_keys){
		$max = 0;
		for($i=0;$i

4. Damit die Möglichkeit besteht, nicht nur die relativen Prozentralen anzeigen zu lassen, suchen wir erneut den längsten String in dem Array. Diesmal jedoch auf die Werte bezogen. Pro Zeichen benutzen wir diesmal 8 Pixel. Wenn Prozentzahlen angezeigt werden sollen, so wird der obere freie Platz um 32 Pixel vergrößert.

	// the number should be displayed at the top of the diagramm
	if($show_number){
		$max_num = 0;
		for($i=0;$i$pic_border_top){
			$pic_border_top += $max_num*8;
		}
	}
	if($show_precent){
		$pic_border_top += 32;
	}

5. Um die relative Höhe auszurechnen suchen wir den größten Wert in dem Datenarray und setzten alle anderen Werte in Relation zu diesem Wert.

	//Convert the values to percent values
	$hundredp = max($data);
	$newarray = array();
	foreach ($data as $el) {
		$newarray[] = intval($el / $hundredp * 100);
	}

6. Um nun ein Diagramm zu erstellen, nehmen wir die Parameter $w und $h und rufen mit diesen die Funktion imagecreatetruecolor auf. Um später verschiedene Farben benutzen zu können definieren wir uns hier auch gleich ein paar Farben

	// let's start with the image-creation
	$im = imagecreatetruecolor($w, $h);
	//define colors.
	$col_background = imagecolorallocate($im, 0, 0, 0);
	$col_grid = imagecolorallocate($im, 0x53, 0x44, 0x01);
	$col_bars = imagecolorallocate($im, 0xb7, 0x6b, 0x1a);
	$col_text = imagecolorallocate($im, 200, 200, 200);

7. Um ein schönes Diagramm erstellen zu können muss es natürlich auch ein Raster geben. Hierzu werden zuerst die horizontalen Linien gezeichnet. Diese werden über die gesamte Breite des Bildes gezeichnet. Um natürlich die horizontalen Linien nur in dem Diagrammbereich zu zeichen, werden hier die Bereiche für die Schlüsselanzeige (unten) und die Werteanzeige(oben) subtrahiert/addiert.
Bei den vertikalen Linien geben wir soviele Linien aus wie es auch Balken geben wird. Auch hier wird nur im Diagrammbereich gezeichnet und der Bereich für die Beschriftungen weggelassen. Mit der Variable $space definieren wir auch gleich die Breite der einzelnen Balken. Da wir natürlich nur begrenzt Platz für die Werte haben, setzen wir diese in eine Relation.

	// drawing the grid
	//horizontal lines
		for ($i=1; $i<=10; $i++)	{
			imageline($im,
				0,
				$i*(($h-$pic_border_bottom-$pic_border_top)/10)+$pic_border_top,
				$w,
				$i*(($h-$pic_border_bottom-$pic_border_top)/10)+$pic_border_top,
				$col_grid);
		}
	//vertical
		$space = intval($w / count($newarray));
		for ($i=1; $i<=count($newarray); $i++)		{
			imageline($im,
				$i*$space,
				$pic_border_top,
				$i*$space,
				$h-$pic_border_bottom,
				$col_grid);
		}

8. Das Zeichnen der Balken
Nun gehen wir das neue Array von vorne beginnend durch und zeichnen jeden Balken.
Da wir wieder nur in dem Diagrammbereich zeichnen wollen, ziehen wir wieder die die $pic_border_top und $pic_border_bottom von den jeweiligen Werten ab. Hier gibt es auch die Möglichkeit die Werte relativ zueinander, also in Prozent, oder in absoluten Zahlen darzustellen. Mit der Variablen $show_percent werden relative Werte ausgegeben mit der Variablen $show_number werden absolute Zahlen angezeigt. Auch gibt es die Möglichkeit die Schlüssel mit anzeigen zu lassen, oder eben nicht

	// start drawing the datas
	for ($i=0; $i

9. Da bisher keine einzige Ausgabe stattfand, können wir nun den Header senden und spezifizieren das es sich um ein png bild handelt. Danach geben wir aus dem erstellten Bild ein PNG aus und zerstören dies auch sogleich. So wird es asugegeben, jedoch nicht in einer datei gespeichert.

	//tell the browser that this will be a png image.
	header("Content-type: image/png");
 
	//finally generate the image;
	imagepng($im);
	imagedestroy($im);

So nun haben wir eine fertige Funktion die uns ein Bild ausgibt, sobald wir diese aufrufen.
Da die Funktion mit dem Header arbeitet, können wir diese nicht einfach in den Quellcode einbinden, sondern müssen den Weg über eine neue Datei gehen.
Also erstellen wir uns eine neue Datei die wir als Bild von einer anderen Datei aufrufen können. Sollten keine Daten zum Anzeigen übergeben werden, bricht das Script ab. Ansonsten holt es sich das Datenarray aus der übergebenen URL. Mit serialize lassen sich Arrays in einen String codieren. mit der Funktion unserialize genau das Gegenteil, es wird also aus einem codierten String wieder ein Array geformt. Um die Verarbeitung zu erleichtern, wird nur ein Array übergeben das aus 3 Teilen besteht,

  • den anzuzeigenden Daten
  • der Höhe
  • der Breite
if(!isset($_GET['data'])){
	die();
}
else{
	$data = unserialize(stripslashes($_GET['data']));
	$cont = $data['data'];
 
	if(isset($data['h']))
		$h = $data['h'];
	if(isset($data['b']))
		$b = $data['b'];
	if(isset($data['keys']))
		$keys = $data['keys'];
	if(isset($data['label']))
		$keys = $data['label'];
}

Sobald wir dies haben, können wir das ganze Zu einer kompletten Datei zusammensetzen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
<?php
function arraytograph($data,$w=300,$h=100,$show_keys = true,$label = ''){
	// Check if the array isn't empty
	if (!isset($data) || empty($data))	{
		return FALSE;
	}
	if($label == 'value'){	
		$show_precent = false;
		$show_number = true;
	}
	elseif($label == 'percent'){
		$show_precent = true;
		$show_number = false;
	}
	else{
		$show_precent = false;
		$show_number = false;
	}	
	$pic_border_top = 5;
	$pic_border_bottom = 5;
 
	// getting the keys from array
	$keys = array_keys($data);
 
 
	// the keys should be displayed, so add a stripe for displaying the keys
	// find the longest key. the length of string *6pixel per letter are added to the border bottom
	if($show_keys){
		$max = 0;
		for($i=0;$i<count($keys);$i++){
			if($max<strlen($keys[$i]))
				$max = strlen($keys[$i]);
		}
		$pic_border_bottom = $max *6+12;
	}
 
	// the number should be displayed at the top of the diagramm
	if($show_number){
		$max_num = 0;
		for($i=0;$i<count($keys);$i++){
			if($max_num<strlen($data[$keys[$i]]))
				$max_num = strlen($data[$keys[$i]]);
		}
		if(($max_num*7)+3>$pic_border_top){
			$pic_border_top += $max_num*8;
		}
	}
	if($show_precent){
		$pic_border_top += 32;
	}
 
	//Convert the values to percent values
	$hundredp = max($data);
	$newarray = array();
	foreach ($data as $el) {
		$newarray[] = intval($el / $hundredp * 100);
	}
 
	// let's start with the image-creation
	$im = imagecreatetruecolor($w, $h);
	//define colors.
	$col_background = imagecolorallocate($im, 0, 0, 0);
	$col_grid = imagecolorallocate($im, 0x53, 0x44, 0x01);
	$col_bars = imagecolorallocate($im, 0xb7, 0x6b, 0x1a);
	$col_text = imagecolorallocate($im, 200, 200, 200);
 
	//fill with background color
		imagefill($im, 0, 0, $col_background);
 
	// drawing the grid
	//horizontal lines
		for ($i=1; $i<=10; $i++)	{
			imageline($im,
				0,
				$i*(($h-$pic_border_bottom-$pic_border_top)/10)+$pic_border_top,
				$w,
				$i*(($h-$pic_border_bottom-$pic_border_top)/10)+$pic_border_top,
				$col_grid);
		}
	//vertical
		$space = intval($w / count($newarray));
		for ($i=1; $i<=count($newarray); $i++)		{
			imageline($im,
				$i*$space,
				$pic_border_top,
				$i*$space,
				$h-$pic_border_bottom,
				$col_grid);
		}
 
		// start drawing the datas
	for ($i=0; $i<count($newarray);$i++){
		$x1 = intval($space/2) -1 + $space*$i;
		$y_bottom = $h-$pic_border_bottom;
		$y_top = ($h-$pic_border_bottom)-(($newarray[$i])*((($h-$pic_border_bottom)-$pic_border_top)/100));
 
		imagefilledrectangle($im, $x1, $y_bottom, $x1+3, $y_top, $col_bars);
 
		if($show_number && !$show_precent){
			$number = $data[$keys[$i]];
			imagestringup($im, 2, $x1-5, $y_top-10,$number,$col_text);
		}
 
		if($show_precent && !$show_number)
			imagestringup($im, 2, $x1-5, $y_top-10, $newarray[$i].'%',$col_text);
 
		if($show_keys)
			imagestringup($im, 2, $x1-5, $h-10-(($max-strlen($keys[$i]))*6), $keys[$i],$col_text);
	}
	//tell the browser that this will be a png image.
	header("Content-type: image/png");
 
	//finally generate the image;
	imagepng($im);
	imagedestroy($im);
}
 
if(!isset($_GET['data'])){
	die();
}
else{
	$data = unserialize(stripslashes($_GET['data']));
	$cont = $data['data'];
 
	if(isset($data['h']))
		$h = $data['h'];
	if(isset($data['b']))
		$b = $data['b'];
	if(isset($data['keys']))
		$keys = $data['keys'];
	if(isset($data['label']))
		$label = $data['label'];
}
arraytograph($cont,$b,$h,$keys,$label);
?>

Nun sollte es möglich sein in einer neuen Datei, in der man das Diagramm anzeigen möchte unsere Diagrammdatei als Bildquelle einzubinden. Als zusätzliche Parameter an das Bild übergeben wir die oben genannten 3 Parameter (Daten, Höhe, Breite)

<?php
$datas['data'] = array("Stein"=>64,"Lehm"=>128,"Holz"=>256,"Nahrung"=>512,"Menschen"=>1024);
$datas['h'] = 200;
$datas['b'] = 150;
$datas['keys'] = true;
$datas['label'] = 'value';
echo "<img src='arraytograph.php?data='.urlencode(serialize ($datas)).'" alt="" /> ';
?>


Nun sehen wir in unserer Datei ein schönes Diagramm :-)

Leicht lassen sich auch andere Möglichkeiten generieren

<?php
$datas['data'] = array("Stein"=>64,"Lehm"=>128,"Holz"=>256,"Nahrung"=>512,"Menschen"=>1024);
$datas['h'] = 200;
$datas['b'] = 150;
$datas['keys'] = true;
$datas['label'] = 'percent';
echo "<img src='arraytograph.php?data='.urlencode(serialize ($datas)).'" alt="" /> ';
?>

oder auch

<?php
$datas['data'] = array("Stein"=>64,"Lehm"=>128,"Holz"=>256,"Nahrung"=>512,"Menschen"=>1024);
$datas['h'] = 150;
$datas['b'] = 150;
$datas['keys'] = true;
$datas['label'] = 'value';
echo "<img src='arraytograph.php?data=".urlencode(serialize ($datas))."' > ";
?>

Viel Spass damit :-)


Erweiterung von Gaming with PHP

Verwandte Artikel

Autor: Simon
Datum: Saturday, 5. July 2008 11:17
Trackback: Trackback-URL Themengebiet: PHP

Feed zum Beitrag: RSS 2.0 Diesen Artikel kommentieren

Kommentar abgeben