Grafika publishing webtip.cz grafika.cz mujiPod.cz mujmac.cz fotografovani.cz printing.cz builder.cz galerie
webtip.cz
adresář  | práce  | diskuse  | redakce  | inzerce
 
 


  Grafy v PHP ... část 1.
o autorovi 
poslat mailem 
tisknout článek 
aktuální rubrika 
PHP je jedním z nejrozšířenějších internetových programovacích jazyků, ale jeho přednosti vynikají i v jiných oblastech.

Čevelíček Marek - 10.02.2003 - clanek - Rubrika: PHP
Seriál: 
Následující díl: Grafy v PHP ... část 2.

PHP je jedním z nejrozšířenějších internetových programovacích jazyků, ale jeho přednosti vynikají i v jiných oblastech. Vezměme si například provádění různých početních operací, či dynamických aplikací pro soukromou potřebu. Zajisté je zde jedna nevýhoda. Skripty PHP nelze provozovat jako jiné windowsovské (a další) programy, nýbrž je nutné je spouštět přes webový server a lze s nimi pracovat jen v prohlížeči internetových stránek. Ani toho ale nedokáže zakrýt nesporné přednosti PHP.

V tomto seriálu vám chci představit PHP jako nástroj pro vykreslování grafů. Se skriptem budete mít možnost pracovat jak na internetu (např. přehledné zobrazování statistik), tak i na vašem PC, kde si můžete pro svou vlastní potřebu generovat ze zadaných hodnot přehledné grafy. Grafy nebudou generované pomocí GD knihovny, výsledek tedy nebude obrázek, ale konstrukce v HTML. K obrázkovým grafům přejdu možná jindy.

Většinou začínám tím, že předvedu hotovou aplikaci a pak ji popisuji. Dnes to udělám jinak. Tento skript je totiž mnohem složitější a k jeho pochopení bude nutné soustředit pozornost hlavně na uspořádání celé aplikace. Pro začátek vám ukážu finální výsledek našeho snažení:

Hlavní roli v mém skriptu hraje uspořádání tabulky, do níž se bude graf vypisovat. Na následujícím obrázku jsem vám celou tabulku nechal zobrazit. Věřím, že když se pouštíte do PHP, už nejspíš znáte něco z jazyka HTML. Podle obrázku tedy jistě poznáte sami, že k takovéto struktuře bude zapotřebí použít tagy COLSPAN a ROWSPAN a to v nemalé míře. Z hlavy byste tabulku sestavovali velmi dlouho. Já jsem k tomu použil Namo Web editor, kde se tabulky dají tvořit pohodlnějším způsobem. Pokud strukturu rozdělíme na části podle řádků, dostaneme 4 hlavní: řádek s nadpisem, stupnice osy Y + vlastní pole grafů, stupnice osy X + osa X a posledním je řádek s legendou. Největší kus práce bude na části s osou Y a grafem. Jak je vyznačeno zeleně, osa Y je tvořená ze dvou buňek - v jedné je číslo a ve druhé obrázek s odrážkou. Červeně označená část jedné hodnoty z grafu musí tyto jednotlivé buňky osy Y spojit v celek, aby bylo možné zvětšovat obrázek do libovolné výšky. Modrou barvou je zvýrazněna stupnice osy X - zde to není obrázek jako u osy Y, nýbrž jen obarvený spodní okraj tabulky (obrázek je pouze ten výčnělek). Tímto způsobem zabráníme nepříjemným bílým mezerám nezi odrážkami osy X, když budou popisky příliš dlouhé. Růžovou barvou není označen rámeček spojených buňek, ale pozadí grafu. Pozadí je tam z důvodu lepší přehlednosti grafu.

Nyní, když si vytvoříte takovou tabulku, je nutné zjistit, které části HTML kódu patří té dané buňce. V takto složité tabulce je orientace hodně složitá, a proto vám nezbývá než řešit to motodou pokusu a omylu. Do proměnných si pak můžete uložit některé statické údaje, jako datum, nadpis grafu, osu Y, osu X a tyto potom nechat v určených buňkách vypsat, ať nemusíte ty samé buňky zdlouhavě hledat při každém novém generování grafu.

Přichází část, kde vás potrápím oním HTML+PHP kódem. Kdybych to vysvětloval po částech mohlo by se stát, že v tom budete mít zmatek. Takhle vám tu označím nejdůležitější části a vysvětlím, proč je to řešené tak a ne onak.:

<?
//*****ADDING HEADER*****//
header("Pragma: no-cache");

//*****EDIT THESE VARIABLES*****//
$cisla = array(54, 108, 12, 215, 160, 223, 140, 102, 98, 111, 60, 66, 142, 78);
$ox = array("Karlovarský", "Plzeňský", "Praha", "Jihočeský", "Vysočina", "Jihomoravský", "Zlínský", "Moravskoslezský", "Olomoucký", "Pardubický", "Královéhradecký", "Liberecký", "Středočeský", "Ústecký");

//*****DO NOT EDIT THIS*****//
function liq_error($msg){
echo "<html><head><title>Liquid chart spec.</title></head><body><p style=\"font-family: arial; font-size: 12px; color: red;\"><b>ERROR: </b>".$msg."</p></body></html>";
exit;
}

//*****EDIT THESE VARIABLES*****//
$ytit = "[tun]";
$xtit = "[Kraj]";
$hd = "Sklizeň pšenice v r. 1997";
$bar = "bar4.gif";
$bar_avg = "bar3.gif";
$bar_mark = "bar5.gif";
$bar_zn = "bar6.gif";
$top_file = "top3.gif";
$mark = 100;
$oy_round = 1;
$pointize = 1;
$lab_bg_num = "#FFFF66";
$lab_bg_ox = "#FFFFCC";
$bg_tbl = "#eae3d1";

//*****INCLUDE MAIN DRAWING FILE*****//
$pocet = Count($cisla);
$pocet_ox = Count($ox);
if ($pocet > $pocet_ox):
liq_error("\$cisla has more values than \$ox");
elseif ($pocet < $pocet_ox):
liq_error("\$cisla has less values than \$ox");
endif;

if ($top_file != ""): $top_info = GetImageSize($top_file);
$width = $top_info[0];
$height = $top_info[1];
$top = "<img src=\"".$top_file."\" border=\"0\" width=\"".$width."\" height=\"".$height."\" alt=\"\"><br>";
else:
$top = "";
$height = 0;
endif;

for ($i=0; $i<$pocet; $i++):
$avg += $cisla[$i]; endfor;
$average = Round($avg/$pocet); $pocet = array_push($cisla, $average);

$maximum = 0;
for ($i=0; $i<$pocet; $i++):
if ($maximum <= $cisla[$i]):
$maximum = $cisla[$i];
endif;
endfor;

for ($i=0; $i<$pocet; $i++):
$pomer = (161)/$maximum;
$a[$i] = Round($pomer*$cisla[$i]);
endfor;
?>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1250">
<title>Liquid chart spec.</title>
<!--this style is needed to perfect display-->
<style type="text/css">
p, span, div {font-family: arial; font-size: 10px; color: black; padding: 2px; }
.img {font-family: arial; font-size: 10px; color: black; padding: 0px; }
.hd {font-family: arial; font-size: 14px; font-weight: bold; color: black; padding: 2px;}
.o {color: red;}
td {background-repeat: repeat-x; background-position: 0px 17px;;}
</style>
<!--this script shows text labels-->
<script>
function showup(text, col){
mouse.innerText = text;
mouse.style.display = "inline";
mouse.style.background = col;
}
function showdown(){
mouse.innerText = "";
mouse.style.display = "none";
}
function move(){
mouse.style.left = event.clientX+13;
mouse.style.top = event.clientY+8;
}
</script>
</head>

<body bgcolor="white" text="black" link="blue" vlink="purple" alink="red" onmousemove="move()">
<div id="mouse" style="display: none; position: absolute; padding: 0px 5px 0px 5px; border: 1px solid black;"></div>

<div align="left">
<table border="0" cellpadding="0" cellspacing="0" bgcolor="<?echo $bg_tbl?>">
<tr>
<td colspan="2" valign="bottom" align="right"><p style="font-weight: 600;"><? echo $ytit?></p></td>
<td align="center" colspan="<?echo $pocet?>">
<table border="0" cellspacing="0" cellpadding="0" width="100%" height="100%" align="center" valign="top">
<tr>
<td align="center"><p style="font-size: 9px;"><?echo Date("d.m.Y")." - ".Date("H:i:s");?></p></td>
</tr>
<tr>
<td align="center" width="55%"><p class="hd">- <? echo $hd ?> -</p></td>
</tr>
</table></td>
</tr>
<tr>
<td align="right"><p class="o"></p></td><td align="right"><p class="img"><img src="nic.gif" border="0" width="4" height="17" alt=""></p></td>
<!--bars-->
<?
//*****čísla nad bary*****//
if (strlen($maximum) > 4):
$del = 1;
for ($i=0; $i<strlen($maximum)-3; $i++):
$del *= 10;
endfor;
$sub1 = "<span class=\"lab\" onmouseover=\"showup('";
$sub2 = "', '".$lab_bg_num."')\" onmouseout=\"showdown()\" style=\"color: #006600; cursor: default; font-weight: 600;\">";
$sub3 = "</span>";
else:
$del = 1;
$sub1 = "<span style=\"cursor: default;\" title=\"";
$sub2 = "\">";
$sub3 = "</span>";
endif;

for ($i=0; $i<$pocet-1; $i++):
//*****separovat čísla v popisku*****//
if ($pointize == 1):
$no_final = $sub1.number_format($cisla[$i], 0, "", ".").$sub2.Round($cisla[$i]/$del).$sub3;
else:
$no_final = $sub1.$cisla[$i].$sub2.Round($cisla[$i]/$del).$sub3;
endif;
//*****mared bars*****//
if (($mark != false) && ($cisla[$i] < $mark)):
if ($zn == $i+1):
echo "<td align=\"center\" valign=\"bottom\" rowspan=\"12\" style=\"background-image: url('lin.gif');\"><p class=\"img\" align=\"center\">".$no_final."<br>".$top."<img src=\"".$bar_zn."\" border=\"0\" width=\"9\" height=\"".$a[$i]."\" alt=\"\"></p></td>";
else:
echo "<td align=\"center\" valign=\"bottom\" rowspan=\"12\" style=\"background-image: url('lin.gif');\"><p class=\"img\" align=\"center\">".$no_final."<br><a href=\"index.php?zn=".($i+1)."\">".$top."<img src=\"".$bar_mark."\" border=\"0\" width=\"9\" height=\"".$a[$i]."\" alt=\"\"></a></p></td>";
endif;
//*****unmarked bars*****//
else:
if ($zn == $i+1):
echo "<td align=\"center\" valign=\"bottom\" rowspan=\"12\" style=\"background-image: url('lin.gif');\"><p class=\"img\" align=\"center\">".$no_final."<br>".$top."<img src=\"".$bar_zn."\" border=\"0\" width=\"9\" height=\"".$a[$i]."\" alt=\"\"></p></td>";
else:
echo "<td align=\"center\" valign=\"bottom\" rowspan=\"12\" style=\"background-image: url('lin.gif');\"><p class=\"img\" align=\"center\">".$no_final."<br><a href=\"index.php?zn=".($i+1)."\">".$top."<img src=\"".$bar."\" border=\"0\" width=\"9\" height=\"".$a[$i]."\" alt=\"\"></a></p></td>";
endif;
endif;
endfor;
//*****separovat čísla v AVG*****//
if ($pointize == 1):
$no_final = $sub1.number_format($cisla[$i], 0, "", ".").$sub2.Round($cisla[$i]/$del).$sub3;
else:
$no_final = $sub1.$cisla[$i].$sub2.Round($cisla[$i]/$del).$sub3;
endif;
//*****average*****//
echo "<td align=\"center\" valign=\"bottom\" rowspan=\"12\" style=\"background-image: url('lin.gif');\"><p class=\"img\" align=\"center\">".$no_final."<br>".$top."<img src=\"".$bar_avg."\" border=\"0\" width=\"9\" height=\"".$a[$i]."\" alt=\"\"></p></td>";
?>

<?
//*****oY nums*****//
for ($i=10; $i>0; $i--):
//*****zaokrouhlit podle $del*****//
if ($oy_round == 1):
$oy_num = Round(Round($maximum-($maximum-$maximum/100*$i*10.5))/$del)*$del;
else:
$oy_num = Round($maximum-($maximum-$maximum/100*$i*10.5));
endif;
//*****separovat*****//
if ($pointize == 1):
$oy_num_final = number_format($oy_num, 0, "", ".");
else:
$oy_num_final = $oy_num;
endif;
echo "<tr><td align=\"right\" style=\"cursor: default;\"><p class=\"o\">".$oy_num_final."</p></td><td align=\"right\"><p class=\"img\"><img src=\"oy_odr.gif\" border=\"0\" width=\"4\" height=\"17\" alt=\"\"></p></td></tr>"; endfor;
?>

<tr><td align="right"><p class="o"></p></td><td align="right" style="border-right: 1px solid red;"><p class="img"><img src="nic.gif" border="0" width="4" height="8" alt=""></p></td></tr>
<tr><td align="right" valign="top" rowspan="3" colspan="2"><p class="o">0</p></td>
<?
//*****oX odrážky*****//
for ($i=0; $i<$pocet-1; $i++):
echo "<td align=\"center\" valign=\"top\" style=\"border-top: 1px solid red;\"><p class=\"img\"><img src=\"ox_odr.gif\" border=\"0\" width=\"27\" height=\"3\" alt=\"\"></p></td>";
endfor;
echo "<td align=\"center\" valign=\"top\" style=\"border-top: 1px solid black;\"><p style=\"font-size: 1px;\"> </p></td>";
?>

</tr>
<tr>
<?
//*****popisky oX*****//
for ($i=0; $i<$pocet-1; $i++):
if (strlen($ox[$i]) > 3):
$mouseover = $ox[$i];
$ox[$i] = substr($ox[$i], 0, 3);
else:
$mouseover = $ox[$i];
endif;
echo "<td align=\"center\" valign=\"top\" style=\"cursor: default;\"><span class=\"lab\" onmouseover=\"showup('".$mouseover."', '".$lab_bg_ox."')\" onmouseout=\"showdown()\"><div class=\"o\">".$ox[$i]."</div></span></td>";
endfor;
echo "<td align=\"center\" valign=\"top\"><p>AVG</p></td>";
?>

</tr>
<tr><td align="center" colspan="<?echo $pocet?>"><p style="font-weight: 600;"><? echo $xtit ?></p></td></tr>
<tr><td align="left" colspan="<?echo $pocet+2?>"><p><img src="<?echo $bar_avg?>" border="0" width="9" height="9" alt=""> - průměr<?if ($mark != false):?>       <img src="<?echo $bar_mark?>" border="0" width="9" height="9" alt=""> < <?echo $mark;?>       <img src="<?echo $bar?>" border="0" width="9" height="9" alt=""> > <?echo $mark;?> <?endif;?><?if ($zn):?>       <img src="<?echo $bar_zn?>" border="0" width="9" height="9" alt=""> - marked <?endif;?></p></td></tr>
</table>
</div>
</body>
</html>

Budu postupovat od začátku a řeknu něco k těm nejdůležitějším částem. Do proměnných $cisla a $ox si uložíte hodnoty k vypsání a stupnici osy X. Podle vlastních potřeb můžete editovat označené proměnné.

Fialová část má za úkol jediné - porovnat, zda počet prvků v poli $ox a $cisla se shoduje. Pokud ne, vypíše se chybová hláška.

Oranžová část: na vrchol každého grafu je možno umístit obrázek jako dekoraci. Tento obrázek je uložen v proměnné $top_file. Tato část zjistí, zda je v proměnné nějaký záznam, a pokud ano, uloží do proměnné $top HTML kód k tomuto obrázku. Není-li $top_file definována, do proměnné $top se nic neuloží. V každém případě však musí být proměnná $top definována, protože se vypisuje vždy (ušetříme tím pár řádek php kódu uvnitř tabulky).

Šedá část: spočítá průměrnou hodnotu v grafu a tuto uloží na konec pole $cisla.

Béžová část: zjistí nejvyšší hodnotu v grafu. Toho využijeme při určování poměru zmenšení grafů oproti nejvyššímu.

Zelená část: počítá onen poměr zmenšení. Maximální hodnota bude mít vždy výšku 161 a ostatní hodnoty se podle ní zmenší. Proč 161? Vyzkoušel jsem si, že je to optimální výška, aby nám graf zůstal celistvý. Při vyšší výšce by se tabulka rozházela a graf by potom nabral velmi zajímavých tvarů :). Zmenšenou výšku každého grafu si uložíme do pole $a.

...

Vínově zbarvená část: Pokud by hodnoty garů byly příliš vysoké (v řádu tísíců až miliard), tabulka by se opět rozsunula a nevypadalo by to přehledně. Čísla nad 1000 tedy ořízneme a vypíšeme jen první 3 číslice. Na druhou stranu lidé, kteří si budou graf prohlížet musí vidět tato čísla. Pro tento případ jsem si vytvořil malý JavaScript, který při přesunu myší nad zkráceným číslem ukáže jeho celou hodnotu. Tato část určí, která čísla potřebují zkrátit a podle toho stanoví proměnné $sub1, $sub2 a $sub3, které potom využijeme ke složení odkazu na JavaScriptovou funkci.

Hnědá část: zde se vypisují všechny grafy mino průměru. Pro velké hodnoty v grafech je zde opět funkce, která zlepšuje přehlednost. A to tím způsobem, že oddělí velká čísla tečkou mezi tisíci. Aktivaci této funkce provedete v proměnné $pointize. Zajímavou funkci najdeme, pokud určíme proměnnou $mark. Skript je dělán tak, že pro všechny grafy, které mají menší hodnotu, než číslo uvedené v $mark se použije jiná barva. Můžete si ji určit v $bar_mark. Tohle ještě není všechno. Každý graf je vlastně odkaz. A po kliknutí na něj se označí opět úplně rozdílnou barvou. Tohle může pomoci při prezentaci grafu jako zvýraznění některé hodnoty. Růžová část php kódu tedy dělí skript ještě na zvýrazněnou a nezvýrazněnou část. Při označování se využívá prostého předání parametru v URL. Parametrem je zde číslo grafu, který má být zvýrazněn.

S průměrnou hodnotou nemůžeme pracovat jako s celým grafem, protože zde je to už jen statistika a ne údaj. Fialová část skriptu tedy vytiskne graf průměru.

Modrá část: Vypisuje hodnoty na ose Y. Nová vlastnst je ta, že po určení $oy_round mohou být čísla zaokrouhlena na čitelnější podobu. Funkce oddělení tisíců tečkou se shoduje s předchozí.

Oranžová část vytiskne jen odrážky osy X.

Hnědá část: Pokud jsou popisky osy X příliš hlouhé, využijeme zde opět předchozí JavaScriptové funkce a po přejetí myší přes popisek se zobrazí celý název.

Toť nejdůležitější části skriptu. Doufám, že se mi podařilo tento článek uspořádat do pochopitelné podoby. Jste-li v PHP začátečníkem, možná je toho na vás příliš, ale věřím, že zkušenějším programátorům by většina věcí měla být jasná. Přeji vám hodně úspěchů s výslednou aplikací, kterou ZDE přikládám ke stažení.

Příště tento skript ještě trochu rozšířím (ano...je to možné :))) ) o jedno významnou drobnost :o). Těším se s vámi u dalšího dílu...

Čevelíček Marek


Hodnocení článku: -1- -2- -3- -4- -5-  Aktuální hodnocení: 2.76 (2784)

Relevantní články
Žádné články


Příspěvky do diskuse o aktuálním článku
Dalibor12.02.11:34Grafy v PHP-MySQL
Marek Čevelíček12.02.21:41RE: Grafy v PHP-MySQL
Dalibor13.02.9:19RE: RE: Grafy v PHP-MySQL
Marek15.02.20:53RE: RE: RE: Grafy v PHP-MySQL
Dalibor16.02.9:17RE: RE: RE: RE: Grafy v PHP-MySQL
Marek16.02.10:37RE: RE: RE: RE: RE: Grafy v PHP-MySQL
Dalibor16.02.16:28Grafy v PHP-MySQL
  

 

  O vydavateli | Kontakt | Ceník reklamy | Ochrana osobních dat
©2002 Grafika Publishing. Všechna práva vyhrazena!