1 |
<?
|
2 |
/* ---------------------------------------------------------------------------------------- */
|
3 |
/* Draw_Secteurs.php : Représentation de statistiques sous forme de secteurs */
|
4 |
/* ---------------------------------------------------------------------------------------- */
|
5 |
/* $Tab_Stats : tableau de statistiques à illustrer sous forme de secteurs */
|
6 |
/* $ImgLarg : largeur de l'image résultante */
|
7 |
/* $ImgHaut : hauteur de l'image résultante */
|
8 |
/* $ImgMarge : marge entre le bord de l'image, le graphe et la legende */
|
9 |
/* $AffLegende : affichage ou non de la légende (valeur true ou false) */
|
10 |
/* $filename : nom du fichier dans lequel sera enregistrée l'image */
|
11 |
/* */
|
12 |
/* La fonction retourne la valeur true en cas de création de l'image avec succès */
|
13 |
/* et la valeur false en cas d'échec */
|
14 |
/* ---------------------------------------------------------------------------------------- */
|
|
Contrôle de la valeur des paramètres de la fonction Draw_Secteurs |
- les résultats doivent être présentés sous forme de tableau,
- les dimensions de l'image et de la marge doivent être
strictement positives.
|
15 |
function Draw_Secteurs($Tab_Stats,$ImgLarg,$ImgHaut,$ImgMarge,$Titre,$AffLegende,$filename){
|
16 |
if (!is_array($Tab_Stats) || ($ImgLarg<=0) || ($ImgHaut<=0) || ($ImgMarge<=0)) return false;
|
|
Initialisation des paramètres internes, de présentation du graphique |
- La constante $LegendeInterLigne représente l'espacement entre deux lignes dans la légende.
- Les constantes $Font et $FontTitre représentent deux des cinq polices standards de PHP. La
police $Font est utilisée pour la légende et les valeurs du graphique.
- Si le graphe comporte un titre, la hauteur qu'il occupe est égale à la hauteur de la
poilice, à laquelle s'ajoute, la hauteur de la marge.
- La prédéfinition de couleurs permet de choisir les nuances et leur ordre d'apparition.
Parmi les couleurs prédéfinies, la juxtaposition de couleurs peu contrastées peut ainsi
être évitée. Le nombre de couleurs ainsi que leurs valeurs sont personnalisables.
|
17 |
$LegendeInterLigne=5;
|
18 |
$Font=2;$FontTitre=5;
|
19 |
if ($Titre!="") $TitreHaut=imagefontheight($FontTitre)+$ImgMarge;
|
20 |
$ListeCouleurs=Array("#ff0000","#ffff00","#00ff00","#0000ff","#ff7fff","#7f007f","#ff7f00",
|
21 |
"#007f00","#00ffff","#7f7fff","#ff007f","#7f7f7f","#7f7f00","#ffff7f","#7fff7f","#00007f");
|
|
Décomposition des résultats à représenter |
- La table associative comportant les valeurs associées à chaque rubrique est décomposée en
une table de rubriques et une table de valeurs.
- Les résultats doivent porter sur au moins une rubrique et la somme des valeurs doit être
différente de zéro.
|
22 |
$Tab_Rub=array_keys($Tab_Stats); //extrait les rubriques statistiques
|
23 |
$Tab_Val=array_values($Tab_Stats); //extrait les valeurs statistiques
|
24 |
$NbRub=sizeof($Tab_Rub); //détermine le nombre de rubriques (pour la légende)
|
25 |
if ($NbRub==0) return false;
|
26 |
$Total=array_sum($Tab_Val); //Somme les valeurs statistiques
|
27 |
if ($Total==0) return false;
|
|
Création du fond d'image |
- Création de l'image, aux dimensions indiquées.
- Allocation de la couleur blanche et de la couleur noire.
- Remplissage du fond de l'image en blanc et utiliser cette couleur pour la
transparence.
|
28 |
$Image=imagecreate($ImgLarg,$ImgHaut); //création de l'image aux dimensions indiquées
|
29 |
$Blanc=imagecolorallocate($Image,255,255,255);
|
30 |
$Noir=imagecolorallocate($Image,0,0,0);
|
31 |
imagefill($Image,0,0,$Blanc); //remplit le fond de l'image en blanc
|
32 |
imagecolortransparent($Image,$Blanc); //considère le blanc comme transparent
|
|
Optimisation de la répartition de l'espace entre le camembert et la légende
|
- Si le camembert ne doit être accompagné d'aucune légende, son diamètre s'inscrit dans la
hauteur de l'image (ou la largeur si celle-ci est inférieure à la hauteur), en conservant
une marge de chaque côté.
- Si le camembert est accompagné d'une légende,
- La largeur maximale de cette dernière, est égale au nombre de caractères de la
rubrique la plus longue, multipliée par la largeur de la police, ajoutée à la largeur
du carré coloré, en conservant une marge, entre le carré coloré et l'intitulé de la
rubrique, ainsi qu'aux extrémités, à l'intérieur du cadre de la légende.
- Le camembert s'inscrit dans les 2/3 gauche du graphique. Son diamètre est déterminé en
fonction des dimensions de l'image :
- Si la hauteur de l'image, déduction faite de la hauteur éventuellement consacrée
au titre du graphique, est inférieure aux 2/3 de la largeur de l'image, alors le
diamètre du camembert est égal à la hauteur de l'image, déduction faite de la
hauteur éventuellement consacrée au titre du graphique, en conservant une marge,
au dessus et en dessous du camembert.
La légende se contente alors de la place qu'il lui reste, c'est à dire : la
largeur de l'image à laquelle il convient de retirer le diamètre du camembert,
en conservant une marge entre le camembert et la légende, une marge à gauche et
une à droite du graphique.
- Sinon, si l'espace réservé au camembert est plus haut que large, le diamètre de
ce dernier s'inscrit dans les 2/3 gauche de l'image, avec tentative d'envahir
l'espace réservé à la légende, si la largeur de cette dernière, n'occupe pas
le 1/3 droit de l'image.
Pour cela, il convient de commencer par limiter la largeur de la légende à 1/3
de la largeur de l'image, déduction faite de la marge de droite.
Le diamètre du camembert correspond ensuite, à la largeur de l'image, à laquelle
il convient de retirer la largeur de la légende, en conservant une marge entre le
camembert et la légende, une à gauche et une à droite du graphique.
|
33 |
/* ---------------------------------------------------------------------------------------- */
|
34 |
/* Détermination de la rubrique la plus longue et de la largeur de la légende */
|
35 |
/* La légende occupe au maximum, le tiers droit ou plus si la hautuer de l'image est petite */
|
36 |
/* ---------------------------------------------------------------------------------------- */
|
37 |
if (!$AffLegende) $Diametre=min($ImgHaut,$ImgLarg)-2*$ImgMarge;
|
38 |
else {
|
39 |
$RubMaxCar=0;
|
40 |
foreach ($Tab_Rub as $Rub) {
|
41 |
if (strlen($Rub)>$RubMaxCar) $RubMaxCar=strlen($Rub);
|
42 |
}
|
43 |
$LegendeLarg=$RubMaxCar*imagefontwidth($Font)+3*$ImgMarge+imagefontheight($Font);
|
44 |
if ($ImgHaut-$TitreHaut<$ImgLarg*2/3) {
|
45 |
$Diametre=$ImgHaut-$TitreHaut-2*$ImgMarge;
|
46 |
if ($LegendeLarg>$ImgLarg-$Diametre-3*$ImgMarge) $LegendeLarg=$ImgLarg-$Diametre-3*$ImgMarge;
|
47 |
} else {
|
48 |
if ($LegendeLarg>$ImgLarg/3-$ImgMarge) $LegendeLarg=$ImgLarg/3-$ImgMarge;
|
49 |
$Diametre=$ImgLarg-$LegendeLarg-3*$ImgMarge;
|
50 |
}
|
51 |
}
|
|
Détermination des coordonnées du centre du camembert |
L'abscisse du centre du camembert se situe à 1/2 diamètre, de la
marge gauche de l'image. Son ordonnée, à partir du haut de l'image, tient compte de la hauteur
d'un éventuel titre du graphique, à laquelle il convient d'ajouter une marge intermédiaire et
1/2 diamètre du camembert.
|
52 |
/* ---------------------------------------------------------------------------------------- */
|
53 |
/* le graphique s'inscrit dans la hauteur ou les deux tiers de la largeur de l'image, */
|
54 |
/* alligné plutôt en haut et à gauche de l'image */
|
55 |
/* ---------------------------------------------------------------------------------------- */
|
56 |
$Centre_x=round($Diametre/2)+$ImgMarge;
|
57 |
$Centre_y=round($Diametre/2)+$TitreHaut+$ImgMarge;
|
|
Encadrement de la légende
|
Si le camembert est accompagné d'une légende, la hauteur de cette
dernière est égale à la hauteur de la police, à laquelle s'ajoute la hauteur de l'interligne, le
tout multiplié par le nombre de rubriques. Ne pas oublier une interligne supplémentaire
(problème traditionnel des intervalles). En cas de présence d'un nombre de rubriques trop important,
la hauteur de la légende est limitée à la hauteur de l'image, déduction faite d'un éventuel
titre du graphique, et en conservant une marge au dessus et une en dessous de la légende.
Connaissant la hauteur et la largeur de la légende, il est maintenant possible de tracer son encadrement : |
- Le coin supérieur gauche a pour abscisse la largeur de l'image, de laquelle il convient
de déduire la largeur de la légende et la marge de droite, et pour ordonnée, la hauteur
d'un éventuel titre de graphique et la marge intermédiaire, entre le titre et la légende.
- Le coin inférieur droit a pour abscisse la largeur de l'image, de laquelle il convient de
déduire simplement la marge de droite, et pour ordonnée, celle du coin supérieur droit du cadre,
à laquelle vient s'ajouter la hauteur de la légende.
|
La variable $Legende_y sert à pointer la hauteur, dans l'image, où
inscrire la rubrique et le carré coloré associé à la légende. La première ligne sera inscrite
à une distance du haut de l'image, correspondant à la hauteur d'un éventuel titre du graphique,
à laquelle s'ajoute la marge au dessus de la légende et la première interligne. A chaque rubrique,
la variable $Legende_y s'incrémente avec la hauteur de la police et la hauteur de l'interligne.
Le nombre de caractères maximum autorisé pour les rubriques, dépend de la largeur définitive de
la légende. Il correspond à la largeur consacrée à la légende, déduction faite de la largeur
d'un carré coloré (égale à la hauteur de la police), de la marge intermédiaire, entre le carré
et la rubrique, ainsi qu'une marge à gauche et une à droite, à l'intérieur de la légende, le tout,
divisé par la largeur de la police.
|
58 |
/* ---------------------------------------------------------------------------------------- */
|
59 |
/* la légende occupe le tiers de droite ou plus si la hauteur de l'image est petite, */
|
60 |
/* allignée plutôt en haut et à droite de l'image */
|
61 |
/* ---------------------------------------------------------------------------------------- */
|
62 |
if ($AffLegende) {
|
63 |
$LegendeHaut=min($LegendeInterLigne+($LegendeInterLigne+imagefontheight($Font))*$NbRub,
|
64 |
$ImgHaut-$TitreHaut-2*$ImgMarge);
|
65 |
imagerectangle($Image,$ImgLarg-$LegendeLarg-$ImgMarge,$TitreHaut+$ImgMarge,$ImgLarg-$ImgMarge,
|
66 |
$TitreHaut+$ImgMarge+$LegendeHaut,$Noir);
|
67 |
$Legende_y=$TitreHaut+$ImgMarge+$LegendeInterLigne;
|
68 |
$LegendeMaxCar=round(($LegendeLarg-imagefontheight($Font)-3*$ImgMarge)/imagefontwidth($Font));
|
69 |
}
|
|
Représentation graphique des résultats
|
La fonction aléatoire srand() est initialisée au cas où le nombre
de rubriques dépasserait le nombre de couleurs prédéfinies.
Les secteurs sont juxtaposés, dans le sens des aiguilles d'une montre (sens trigonométrique inverse).
Le premier secteur commence à partir du rayon vertical supérieur, correspondant à un angle de 270°,
le rayon horizontal de droite correspondant à l'angle d'origine 0°.
Pour chaque rubrique :
|
70 |
/* ---------------------------------------------------------------------------------------- */
|
71 |
/* constitution du graphe et de la légende (attribution des couleurs, rubriques et */
|
72 |
/* étiquettes */
|
73 |
/* ---------------------------------------------------------------------------------------- */
|
74 |
srand((double)microtime()*1000000); //initialisation de la fonction aléatoire
|
75 |
$AngleDeb = 270; //angle de départ
|
76 |
for($i=0;$i<$NbRub;$i++) {
|
|
| Détermination de la couleur
|
| La couleur attribuée à la rubrique, est la suivante, dans la liste des
couleurs préétablies. S'il n'existe plus de couleur disponible dans la liste, la couleur de la
rubrique est définie aléatoirement.
|
77 |
if ($i<sizeof($ListeCouleurs)) {
|
78 |
$temp=hexdec($ListeCouleurs[$i]);
|
79 |
$Red=floor($temp/65536);$temp=$temp-$Red*65536;
|
80 |
$Green=floor($temp/256);
|
81 |
$Blue=$temp-$Green*256;
|
82 |
} else {
|
83 |
$Red=rand(1,254);$Green=rand(1,254);$Blue=rand(1,254);
|
84 |
}
|
85 |
$Color=imagecolorallocate($Image,$Red,$Green,$Blue);
|
|
| Dessin du secteur
|
| L'angle de début d'un secteur est égal à l'angle de fin du secteur
précédent. L'angle de fin d'un secteur est déterminé à partir de son angle de début, en y ajoutant
un angle proportionnel à la valeur associée à la rubrique.
La dernière rubrique se termine sur le rayon du départ (270° + 360°, correspondant à un tour
complet).
|
86 |
if ($i==$NbRub-1) $AngleFin=630; //angle de fin
|
87 |
else $AngleFin=round($AngleDeb+$Tab_Val[$i]/$Total*360);
|
88 |
imagefilledarc($Image,$Centre_x,$Centre_y,$Diametre,$Diametre,$AngleDeb,$AngleFin,$Color,
|
89 |
IMG_ARC_EDGED);
|
90 |
$AngleDeb=$AngleFin; //angle de début de secteur = angle de fin de secteur précédent
|
|
| Inscription d'une ligne de la légende
|
| Si le camembert doit être accompagné d'une légende, il convient d'inscrire
la rubrique et un carré de la couleur associée. La ligne ne peut s'inscrire que dans le cadre de
la légende.
Le carré coloré est localisé à l'aide de son coin supérieur gauche et du coin inférieur droit. |
|
- Le coin supérieur gauche a pour abscisse, la largeur de l'image, déduction faite de la
largeur de la légende, c'est à dire, en respectant une marge, à partir du cadre gauche de
la légende.
Son ordonnée se situe sur la ligne courante.
- Chaque carré coloré a pour côté, la hauteur de la police. L'abscisse et l'ordonnée du coin
inférieur droit se déduisent donc, de celles du coin supérieur gauche en ajoutant à chacune, la
hauteur de la police.
|
| L'intitulé de la rubrique se situe sur la même ligne que le carré
coloré, en respectant une marge intermédiaire. Il peut éventuellement être tronqué, pour éviter
de déborder à droite de l'encadrement de la légende.
La ligne suivante devient la ligne courante, pour préparer l'inscription, dans l'encadrement de
la légende, de la rubrique suivante.
|
91 |
if ($AffLegende) {
|
92 |
if ($Legende_y<($LegendeHaut+$TitreHaut)) { //ne pas dépasser le cadre bas de la légende
|
93 |
$LegendeCoul_x1=$ImgLarg-$LegendeLarg; //abscisse du coin supérieur gauche de la légende
|
94 |
$LegendeCoul_y1=$Legende_y; //ordonnée du coin supérieur gauche de la légende
|
95 |
imagefilledrectangle($Image,$LegendeCoul_x1,$LegendeCoul_y1,$LegendeCoul_x1+
|
96 |
imagefontheight($Font),$LegendeCoul_y1+imagefontheight($Font),$Color);
|
97 |
imagestring($Image,$Font,$LegendeCoul_x1+imagefontheight($Font)+$ImgMarge,$Legende_y,
|
98 |
substr($Tab_Rub[$i],0,$LegendeMaxCar),$Noir);
|
99 |
}
|
100 |
$Legende_y=$Legende_y+$LegendeInterLigne+imagefontheight($Font);
|
101 |
}
|
102 |
}
|
|
Inscription des valeurs sur le camembert
|
Si le camembert est accompagné d'une légende, il convient de le
compléter, avec les valeurs associées à chaque rubrique.
Pour chaque rubrique, une médiane imaginaire, part du centre du camembert et coupe le secteur en
deux.
La valeur associée à la rubrique, s'inscrit horizontalement, à partir de cette médiane, à une
distace égale à 3/4 de rayon, du centre du camembert.
Si l'écartement du secteur est inférieur à 10° et que ce dernier se situe dans la partie droite
du camembert, la valeur s'inscrit à partir du début de secteur plutôt qu'à partir de la médiane.
Ceci afin de limiter le décentrage de la valeur, par rapport au secteur.
|
103 |
if ($AffLegende) {
|
104 |
$AngleDeb=270; //angle de départ
|
105 |
for($i=0;$i<$NbRub;$i++) {
|
106 |
$AngleFin=round($AngleDeb+$Tab_Val[$i]/$Total*360); //angle de fin
|
107 |
$Milieu=round(($AngleFin+$AngleDeb)/2);
|
108 |
if ($Milieu<450 and $AngleFin-$AngleDeb<10) {
|
109 |
$Milieu_x=round(cos(deg2rad($AngleDeb))*($Diametre*3/8)+$Centre_x);
|
110 |
$Milieu_y=round(sin(deg2rad($AngleDeb))*($Diametre*3/8)+$Centre_y);
|
111 |
} else {
|
112 |
$Milieu_x=round(cos(deg2rad($Milieu))*($Diametre*3/8)+$Centre_x);
|
113 |
$Milieu_y=round(sin(deg2rad($Milieu))*($Diametre*3/8)+$Centre_y);
|
114 |
}
|
115 |
imagestring($Image,2,$Milieu_x,$Milieu_y,number_format($Tab_Val[$i],'0','.',' '),$Noir);
|
116 |
$AngleDeb=$AngleFin;
|
117 |
}
|
118 |
}
|
|
Finition du graphique
|
Le contour du camembert est repassé en noir.
Le titre du graphique est centré horizontalement, en respectant la marge du haut.
L'image est enregistrée au format png, dans le fichier dont le nom a été spécifié, puis détruite
de l'espace mémoire.
En de succès, la fonction retourne la valeur true.
|
119 |
imagearc($Image,$Centre_x,$Centre_y,$Diametre,$Diametre,0,360,$Noir);
|
120 |
imagestring($Image,$FontTitre,($ImgLarg-strlen($Titre)*
|
121 |
imagefontwidth($FontTitre))/2,$ImgMarge,$Titre,$Noir);
|
122 |
imagepng($Image, $filename);
|
123 |
imagedestroy($Image);
|
124 |
if (!file_exists($filename)) return false;
|
125 |
return true;
|
126 |
}
|
|
Utilisation de la fonction
|
La fonction Draw_Secteurs() est terminée. La suite du script est
chargée de l'appeler et d'afficher le graphique résultant.
Pour celà, une table, pouvant être issue d'une requête SQL ("select Libelle, Valeur from Statistiques"),
est initialisée manuellement.
La somme des valeurs ne sert qu'à la présentation du tableau, affiché à gauche du graphique.
|
127 |
$TabAffich = array (
|
128 |
array ( "Libelle"=>"Alimentation","Valeur"=>7000 ),
|
129 |
array ( "Libelle"=>"Energie","Valeur"=>1000 ),
|
130 |
array ( "Libelle"=>"Téléphone","Valeur"=>500 ),
|
131 |
array ( "Libelle"=>"Eau","Valeur"=>400 ),
|
132 |
array ( "Libelle"=>"Habillement","Valeur"=>1000 ),
|
133 |
array ( "Libelle"=>"Santé","Valeur"=>4000 ),
|
134 |
array ( "Libelle"=>"Loisirs","Valeur"=>3500 ),
|
135 |
array ( "Libelle"=>"Voitures","Valeur"=>1700 ),
|
136 |
array ( "Libelle"=>"Assurance","Valeur"=>1000 ),
|
137 |
array ( "Libelle"=>"Equipement de la Maison","Valeur"=>600 ),
|
138 |
array ( "Libelle"=>"Remboursement d'emprunts","Valeur"=>10000 ),
|
139 |
array ( "Libelle"=>"Impôts","Valeur"=>2000 ) );
|
140 |
foreach ($TabAffich as $rec=>$record) {
|
141 |
$Total+=$record[Valeur];
|
142 |
} ?>
|
|
Mise en page
|
Les styles ci-dessous ne sont pas tous utilisés dans ce script.
Ils sont bien sûr personnalisables et n'influent que sur la présentation des résultats
|
143 |
<HTML>
|
144 |
<HEAD>
|
145 |
<title>Représentation graphique sous forme de camembert</title>
|
146 |
<style>
|
147 |
BODY {background-color:#FFFFD2; color:#000000;font-family:Arial, Helvetica, sans-serif;font-size:12}
|
148 |
A:link {color:#993300}
|
149 |
A:visited {color:#cc6633}
|
150 |
A:hover,A:active {background:#008080;color:#FFFFFF}
|
151 |
.tdleft,.tdright,.tdcenter,.thcenter,.thleft,.thright {padding-left: 5pt;padding-right:5pt;
|
152 |
padding-top:0;padding-bottom:0;vertical-align:middle}
|
153 |
.tdleft,.thleft {text-align:left;font-size:12}
|
154 |
.tdright,.thright {text-align:right;font-size:12}
|
155 |
.tdcenter,.thcenter {text-align:center;font-size:12}
|
156 |
.thcenter,.thleft,.thright {color:white;background-color:#008080;font-weight:bold}
|
157 |
</style>
|
158 |
</HEAD>
|
|
Présentation du tableau de résultats
|
Les résultats sont présentés sous forme de tableau, dans la partie
gauche.
La table associative des données à passer à la fonction Draw_Secteurs(), en vue de leur
représentation graphique, est constituée à partir de la table initialisée manuellement ou à
l'aide d'une requête SQL.
|
159 |
<BODY>
|
160 |
<table width=778 align="center"><tr><td>
|
161 |
<table border=0 cellspacing=1 cellpadding=0 width="100%"><tr><td>
|
162 |
<table>
|
163 |
<tr>
|
164 |
<th class=thcenter>Dépenses</th>
|
165 |
<th class=thcenter><? echo number_format($Total,'0','.',' ')," €"; ?></th>
|
166 |
</tr><? $bgcolor="";
|
167 |
foreach ($TabAffich as $rec=>$record) {
|
168 |
$bgcolor == "#99CCFF" ? $bgcolor="#CCFFFF" : $bgcolor="#99CCFF";
|
169 |
$TabStats[$record[Libelle]]=$record[Valeur]; ?>
|
170 |
<tr bgcolor="<? echo $bgcolor; ?>"><?
|
171 |
foreach($record as $fld=>$field) {
|
172 |
switch ($fld) {
|
173 |
case "Libelle":echo "<td class=tdleft>",$field,"</td>";break;
|
174 |
default:echo "<td class=tdright nowrap>",number_format($field,'0','.',' ')." €</td>";
|
175 |
}
|
176 |
} ?>
|
177 |
</tr><?
|
178 |
} ?>
|
179 |
</table></td>
|
|
Présentation du graphique
|
Le principe est de changer de nom de fichier, à chaque création de
graphique. Sinon, le navigateur récupère toujours celui en mémoire cache et ne prend pas la
peine de recherche la dernière version, sur disque, même si son contenu est différent.
Pour éviter, à la longue, de polluer le disque avec des graphiques obsolètes, il convient
d'effacer tous les fichiers précédemment générés, avant toute création d'un nouveau graphique.
|
180 |
<td class=tdright>
|
181 |
<table><?
|
182 |
$Path="Graphique/"; //remplacer Graphique/ par tout autre chemin de votre choix
|
183 |
$Folder=dir($Path);
|
184 |
while ($Fichier=$Folder->read()) {
|
185 |
if (substr($Fichier,0,9)=="Camembert") unlink($Path.$Fichier);
|
186 |
}
|
187 |
$Folder->close();
|
188 |
$fileCamembert=$Path."Camembert_".microtime().".png";
|
189 |
Draw_Secteurs($TabStats,480,300,10,"Analyse des dépenses",true,$fileCamembert); ?>
|
190 |
<tr><td class=tdright><img src="<? echo $fileCamembert; ?>" alt="Camembert"></td></tr>
|
191 |
</table></td></tr>
|
192 |
</table></td></tr>
|
193 |
</table>
|
194 |
</BODY>
|
195 |
</HTML> |