Bonjour à tous
Je viens de développer un camembert 2D dans le but de m'entrainer, mais aussi de ressembler au mieux à celui généré par Flex 4 (j'ai un projet sous Flex 4 et j'ai un module d'impression PDF qui doit rendre le même résultat que Flex 4, d'où la nécessité de coder le mien, les autres ayant à chaque fois au moins un détail gênant qui diffère).
J'aimerais vos avis et suggestions sur ce qui a été fait. :)
Il s'agit d'une version 1, et plusieurs améliorations sont à venir:
- explodeSlice(): pour détacher un morceau du reste du camembert (mise en retrait)
- drawVLegend(): contrairement à drawHLegend() qui dessine la légende horizontalement à la base, celui-ci les dessinera à gauche ou à droite et verticalement
- set3D(): pour une version 3D du camembert
- et d'autres encore
Le point où j'aimerais aussi avoir part de vos idées, c'est celui pour rendre le graphique plus joli (éviter de voir les pixels).
J'ai essayé d'appliquer l'antialiasing, mais soit je ne m'en sers pas correctement, soit il est inefficace ici...
Ci-dessous:
- AKPie.php : la classe du graphique
- test.php : un exemple d'utilisation
- et il faudra inclure "font.ttf" qui n'est autre que la police courrier par défaut dans C:\Windows\Font\ (pour windows)
Voilà :)
Et merci à ceux qui auront le courage de lire, voire de tester ;)
Source / Exemple :
// --- test.php -----------------------------------------------
include 'AKPie.php';
// --- Call example
// test.php?seriesX=a:11:{s:10:%22Logistique%22;s:2:%2216%22;s:18:%22Cabinet%20de%20conseil%22;s:1:%227%22;s:14:%22Pharmaceutique%22;s:1:%227%22;s:10:%22Automobile%22;s:1:%227%22;s:28:%22SSII%20et%20Conseil%20Informatique%22;s:1:%227%22;s:8:%22T%E9l%E9coms%22;s:1:%227%22;s:55:%22Administration%20d%27Etat,%20Poste,%20Collectivit%E9%20Territoriale%22;s:1:%224%22;s:8:%22B%E2timent%22;s:1:%223%22;s:6:%22M%E9dias%22;s:1:%223%22;s:9:%22Industrie%22;s:1:%223%22;s:5:%22Autre%22;s:1:%227%22;}
// test.php?seriesX=a:3:{i:0;i:2009;i:1;i:2010;i:2;i:2011;}&seriesY=a:3:{i:0;i:118;i:1;i:218;i:2;i:780;}
// --- Data retrieving
$seriesX = (!empty($_GET['seriesX']))
? unserialize(stripslashes($_GET['seriesX']))
: array();
$seriesY = (!empty($_GET['seriesY']))
? unserialize(stripslashes($_GET['seriesY']))
: array();
// --- Class instanciation
$pie = new AKPie($seriesX, $seriesY);
$pie->draw();
// --- AKPie.php ---------------------------------------------------------------------------------------
<?php
/* ------------------------------------------------------------ *
- ___ ___ *
- / // / *
- / // / __ __ *
- / // \ / /__ __ __ __/ /_ _ _ _ __ ____ *
- / /| |\ \ __ __ / //_/ _//_ |/_ __/_/ \/ _ |/ _/ *
- / /_| | \ \ . \ . \ // / /_/ . | / / / / / / / | /\_ \ *
- /________| \__\ _/ _///_/\__/\__|_\\_//_/\__/_/ /_/____/ *
- / / / / *
- /_/ /_/ *
- *
- ------------------------------------------------------------ *
- Class: AKPie *
- *
- @author: Asph@tor (under Advanced Partner's project) *
- @date: 2011-05-17 *
- @version: v1.0.0 *
- @compatible: PHP >= 4.2 *
- @dependancies: GD2 *
- font.ttf *
- @license: free of use, of modifications and of *
- commercialization. Just past this header *
- at the begining of your code *
- @contact: akpplications@gmail.com *
- ------------------------------------------------------------ */
class AKPie {
protected $img; // The chart instance (true colors)
protected $antialias; // Antialiasing
protected $backgroundColor; // Background color behind the chart
protected $borderColor; // Chart border's color
protected $pieBorderColor; // Pie border's color
protected $pieDelta; // Distance between pie and legend's labels
protected $piePadding; // Chart's padding
protected $pieX; // Chart's origin x value
protected $pieY; // Chart's origin y value
protected $pieSize; // Chart's size
protected $width; // Display area width
protected $height; // Display area height
protected $title; // Chart's title
protected $titleColor; // Chart title's color
protected $titleFont; // Chart title's font to use
protected $titlePadding; // Chart title's distance from the nearest side
protected $titleSize; // Chart title's size
protected $legendLabelColor; // Legend item label's color
protected $legendLabelGap; // Gap between legend's labels
protected $legendLabelSize; // Legend item label's size
protected $legendPadding; // Legend box's distance from the nearest side
protected $legendPosition; // Legend position (left, bottom, right, top)
protected $legendWidth; // Legend width
protected $guidLabelSize; // Legend guid label's size
protected $guidLinesColor; // Legend guid line's color
protected $guidLabelsColor; // Legend guid label's color
protected $seriesX; // Data keys' label
protected $seriesY; // Data values' label
protected $seriesZ; // Data values in percents
protected $precision; // Percents precision
protected $showBorder; // Show chart's borders
protected $showLegend; // Show legend
protected $showLegendBorder; // Show legend's border
protected $showPieBorder; // Show pie's and slices' border
protected $showShadow; // Show pie's shadow
protected $showGuidLines; // Show lines between slices and slices' label
protected $showGuidLabels; // Show slices' labels
protected $titleY; // First free y after title's drawing
protected $legendY; // Last free y after legend's drawing
protected $slicesColors; // Chart's color range
protected $shadowColor; // Pie shadow's color
protected $shadowLagX; // Pie shadow's x distance from pie
protected $shadowLagY; // Pie shadow's y distance from pie
// ---------------------------------------------------------
// --- CONSTRUCTOR ---
// ---------------------------------------------------------
function __construct($seriesX=array(), $seriesY=array()) {
if (count($seriesY) == 0) {
$this->seriesX = array_keys($seriesX);
$this->seriesY = array_values($seriesX);
} else if (count($seriesX) == 0 || count($seriesY) != count($seriesX)) {
echo "ERROR: incorrect parameters - size of each parameters should be the same!";
} else {
$this->seriesX = array_values($seriesX);
$this->seriesY = array_values($seriesY);
}
$this->width = 800;
$this->height = 600;
$this->precision = 2;
$this->antialias = true;
$this->title = 'My Pie Chart';
$this->titleColor = '333333';
$this->titleFont = 'font.ttf';
$this->titlePadding = 40;
$this->titleSize = 16;
$this->titleY = 0;
$this->backgroundColor = '#FFFFFF';
$this->borderColor = '0x444444';
$this->legendY = $this->height;
$this->legendLabelColor = 'ff0';
$this->legendLabelSize = 8;
$this->legendLabelGap = $this->legendLabelSize*3/4;
$this->legendPadding = 20;
$this->legendPosition = 'bottom';
$this->legendWidth = 250;
$this->guidLabelSize = 8;
$this->guidLinesColor = '0x444444';
$this->guidLabelsColor = '0x444444';
$this->pieBorderColor = 'ccc';
$this->pieDelta = 20;
$this->piePadding = 0;
$this->showBorder = true;
$this->showLegend = true;
$this->showLegendBorder = true;
$this->showPieBorder = true;
$this->showShadow = true;
$this->showGuidLines = true;
$this->showGuidLabels = true;
$this->shadowColor = '444';
$this->shadowLagX = 3;
$this->shadowLagY = 3;
$this->slicesColors = array(
'0xE48701', '0x1B85D9', '0xA5BC4E', '0xFFFC93', '0x7ECFE1',
'0xFF84BC', '0xC5F57E', '0x7EA6CE', '0xFF7E9F', '0x7EC29B',
'0x7E83BB', '0xFFB17E', '0x7EC9BD', '0xC57EBF', '0xFFBD8A');
}
// ---------------------------------------------------------
// --- SETTERS ---
// ---------------------------------------------------------
function setAntialias($antialias) { $this->antialias = $antialias; }
function setBackgroundColor($color) { $this->backgroundColor = $color; }
function setBorderColor($color) { $this->borderColor = $color; }
function setPieBorderColor($color) { $this->pieBorderColor = $color; }
function setPieDelta($delta) { $this->pieDelta = $delta; }
function setPiePadding($padding) { $this->piePadding = $padding; }
function setWidth($width) { $this->width = $width; }
function setHeight($height) { $this->height = $height; }
function setTitle($title) { $this->title = $title; }
function setTitleFont($font) { $this->titleFont = $font; }
function setTitleColor($color) { $this->titleColor = $color; }
function setTitleSize($size) { $this->titleSize = $size; }
function setTitlePadding($padding) { $this->titlePadding = $padding; }
function setLabelFunction($function) { $this->labelFunction = $function; }
function setLegendWidth($width) { $this->legendWidth = $width; }
function setLegendLabelColor($color) { $this->legendLabelColor = $color; }
function setLegendLabelGap($gap) { $this->legendLabelGap = $gap; }
function setLegendLabelSize($size) { $this->legendLabelSize = $size;
$this->legendLabelGap = $size*3/4; }
function setLegendPadding($padding) { $this->legendPadding = $padding; }
function setLegendPosition($position) { $this->legendPosition = $position; }
function setGuidLabelSize($size) { $this->guidLabelSize = $size; }
function setGuidLabelsColor($color) { $this->guidLabelsColor = $color; }
function setGuidLinesColor($color) { $this->guidLinesColor = $color; }
function setPrecision($precision) { $this->precision = $precision; }
function showBorder() { $this->showBorder = true; }
function showLegend() { $this->showLegend = true; }
function showLegendBorder() { $this->showLegendBorder = true; }
function showPieBorder() { $this->showPieBorder = true; }
function showShadow() { $this->showShadow = true; }
function showGuidLines() { $this->showGuidLines = true; }
function showGuidLabels() { $this->showGuidLabels = true; }
function setSlicesColors($colors) { $this->slicesColors = $colors; }
function setShadowColor($color) { $this->shadowColor = $color; }
function setShadowLagX($lag) { $this->shadowLagX = $lag; }
function setShadowLagY($lag) { $this->shadowLagY = $lag; }
// ---------------------------------------------------------
// --- PUBLIC METHODS ---
// ---------------------------------------------------------
function draw() {
$this->img = imagecreatetruecolor($this->width, $this->height);
imageantialias($this->img, $this->antialias);
$this->drawBackground();
$this->drawBorder();
$this->drawTitle();
$this->drawHLegend();
$this->drawPie();
$this->drawGuidLines();
header('Content-Type: image/png');
imagepng($this->img);
imagedestroy($this->img);
}
// ---------------------------------------------------------
// --- PRIVATE METHODS ---
// ---------------------------------------------------------
protected function calculatePercents($data) {
$percents = array();
$len = count($data);
$total = 0;
foreach ($data as $v)
$total+=$v;
foreach ($data as $v)
$percents[] = round($v * 100 / $total, $this->precision);
return $percents;
}
protected function formatGuidLabel($label, $width) {
$str = explode(' ', $label);
$len = count($str);
$line = 1;
$txt = $str[0];
$label = '';
$maxWidth = 0;
for ($i=1; $i<$len; $i++) {
if (strlen($txt.' '.$str[$i])*$this->guidLabelSize*3/4 < $width)
$txt .= ' '.$str[$i];
else {
$w = strlen($txt)*$this->guidLabelSize*3/4;
if ($maxWidth < $w)
$maxWidth = $w;
$label .= $txt."\n";
$txt = $str[$i];
$line++;
}
}
$label .= $txt;
return array($label, $line, $maxWidth);
}
protected function drawBackground() {
list($r, $g, $b) = $this->html2rgb($this->backgroundColor);
$bgcolor = imagecolorallocate($this->img, $r, $g, $b);
imagefilledrectangle($this->img, 0, 0, $this->width-1, $this->height-1, $bgcolor);
}
protected function drawBorder() {
if ($this->showBorder) {
list($r, $g, $b) = $this->html2rgb($this->borderColor);
$bgcolor = imagecolorallocate($this->img, $r, $g, $b);
imagerectangle($this->img, 0, 0, $this->width-1, $this->height-1, $bgcolor);
}
}
protected function drawTitle() {
// POSSIBLE OPTIMIZATION: word wrapping
$x = ($this->width - (strlen($this->title) * $this->titleSize*5/6)) / 2;
$y = $this->titlePadding;
list($r, $g, $b) = $this->html2rgb($this->titleColor);
$titleColor = imagecolorallocate($this->img, $r, $g, $b);
imagettftext($this->img, $this->titleSize, 0, $x, $y, $titleColor, $this->titleFont, $this->title);
$this->titleY = $y + $this->titlePadding;
}
protected function drawHLegend() {
if ($this->showLegend) {
// box sizes and paddings
$cellSize = $this->legendLabelSize * 0.75;
$textSize = $this->legendLabelSize;
$cellPadding = ($textSize - $cellSize) / 2;
// lengths and item/line numbers
$len = count($this->seriesX);
$max = 0;
$nb = 0;
$flag = $this->width - 2*$this->legendPadding;
// colors
$slices = count($this->slicesColors);
list($r, $g, $b) = $this->html2rgb($this->borderColor);
$borderColor = imagecolorallocate($this->img, $r, $g, $b);
// longest label width
for ($i=0; $i<$len; $i++)
if (strlen($this->seriesX[$i]) > strlen($this->seriesX[$max]))
$max = $i;
$wordWidth = strlen($this->seriesX[$max]) * $textSize;
// number of lines and items per line
$cellWidth = 3*$cellSize + $wordWidth;
$cellHeight = $textSize + $cellSize;
while ($flag > 0 && $nb < $len) {
$flag -= $cellWidth;
$nb++;
}
if ($cellWidth * $nb > $this->width - $this->legendPadding*2)
$nb--;
$line = ceil($len / $nb);
// positions
switch ($this->legendPosition) {
case "top":
$x = ($this->width - $nb*$cellWidth) / 2;
$y = $this->titleY;
$this->legendY = $this->height;
break;
case "bottom":
default:
$x = ($this->width - $nb*$cellWidth) / 2 ;
$y = $this->height - ($textSize+$this->legendLabelGap)*$line - $this->legendPadding;
$this->legendY = $y - $this->legendPadding;
break;
}
// draw items
for ($i=0; $i<$line; $i++) {
for ($j=0; $j<$nb && $j<$len-$i*$nb; $j++) {
// draw color cell
list($r, $g, $b) = $this->html2rgb($this->slicesColors[($i*$nb+$j)%$slices]);
$legendColor = imagecolorallocate($this->img, $r, $g, $b);
imagefilledrectangle(
$this->img,
$x+1+$j*$cellWidth,
$y+$cellPadding+1+$i*$cellHeight,
$x+$cellSize-2+$j*$cellWidth,
$y+$cellPadding+$cellSize-2+$i*$cellHeight,
$legendColor);
imagerectangle(
$this->img,
$x+$j*$cellWidth,
$y+$cellPadding+$i*$cellHeight,
$x+$cellSize-1+$j*$cellWidth,
$y+$cellPadding+$cellSize-1+$i*$cellHeight,
$borderColor);
// draw label
imagettftext(
$this->img,
$textSize,
0,
$x+2*$cellSize+$j*$cellWidth,
$y+$textSize-1+$i*$cellHeight,
$titleColor,
$this->titleFont,
$this->seriesX[$i*$nb+$j]);
}
}
// draw border
if ($this->showLegendBorder)
imagerectangle(
$this->img,
$x-$this->legendPadding/2,
$y-$this->legendPadding/2,
$x+$nb*$cellWidth,
$y+$line*$cellHeight,
$borderColor);
if ($this->legendPosition == 'top')
$this->titleY = $y+$line*$cellHeight+$this->legendPadding;
}
}
protected function drawPie() {
// position
$x = $this->width / 2;
$y = ($this->titleY + $this->legendY) / 2;
$pieSize = $this->height - ($this->titleY + $this->height - $this->legendY + 2*$this->piePadding);
$pieSize *= 3/5;
$this->pieX = $x;
$this->pieY = $y;
$this->pieSize = $pieSize;
// shadow
if ($this->showShadow) {
list($r, $g, $b) = $this->html2rgb($this->shadowColor);
$color = imagecolorallocate($this->img, $r, $g, $b);
imagefilledarc($this->img, $x+$this->shadowLagX, $y+$this->shadowLagY, $pieSize, $pieSize, 0, 360, $color, IMG_ARC_PIE);
}
// calculate percents
$this->seriesZ = $this->calculatePercents($this->seriesY);
// slices drawing
$len = count($this->seriesX);
$slen = count($this->slicesColors);
list($r, $g, $b) = $this->html2rgb($this->pieBorderColor);
$borderColor = imagecolorallocate($this->img, $r, $g, $b);
$deg = 0;
for ($i=0; $i<$len; $i++) {
list($r, $g, $b) = $this->html2rgb($this->slicesColors[$i%$slen]);
$color = imagecolorallocate($this->img, $r, $g, $b);
$end = ($i==$len-1) ? 360 : $deg + $this->seriesZ[$i] * 360 / 100;
imagefilledarc($this->img, $x, $y, $pieSize, $pieSize, $deg, $end, $color, IMG_ARC_PIE);
if ($this->showPieBorder)
imagefilledarc($this->img, $x, $y, $pieSize, $pieSize, $deg, $end, $borderColor, IMG_ARC_EDGED | IMG_ARC_NOFILL);
$deg = $end;
}
// IMG_ARC_PIE | IMG_ARC_EDGED | IMG_ARC_NOFILL | IMG_ARC_CHORD
}
protected function drawGuidLines() {
if ($this->showGuidLabels) {
// data retrieving
$x = $this->pieX;
$y = $this->pieY;
$d = $this->pieSize/2;
$xl = $x - ($d + $this->pieDelta);
$xr = $x + ($d + $this->pieDelta);
list($r, $g, $b) = $this->html2rgb($this->guidLinesColor);
$color = imagecolorallocate($this->img, $r, $g, $b);
// drawing
$deg = 0; $v = $this->seriesZ[0];
foreach ($this->seriesZ as $k => $v) {
$tmp = ($v*2*pi()/100);
// lines
$xx = ($x + ($d + $this->pieDelta)*cos($deg + $tmp/2)) > $x ? $xr : $xl;
if ($this->showGuidLines) {
imageline(
$this->img,
$x + ($d - $this->pieDelta)*cos($deg + $tmp/2),
$y + ($d - $this->pieDelta)*sin($deg + $tmp/2),
$x + ($d + $this->pieDelta)*cos($deg + $tmp/2),
$y + ($d + $this->pieDelta)*sin($deg + $tmp/2),
$color);
imageline(
$this->img,
$x + ($d + $this->pieDelta)*cos($deg + $tmp/2),
$y + ($d + $this->pieDelta)*sin($deg + $tmp/2),
$xx,
$y + ($d + $this->pieDelta)*sin($deg + $tmp/2),
$color);
}
// labels
if ($xx == $xr) {
list($txt, $nb, $w) = $this->formatGuidLabel($this->seriesX[$k], $this->width - $xr - $this->pieDelta);
$xx = $xr + $this->pieDelta;
} else {
$width = strlen($this->seriesX[$k])*$this->guidLabelSize*3/4;
list($txt, $nb, $w) = $this->formatGuidLabel($this->seriesX[$k], $xl - $this->pieDelta);
$xx = $xl - $this->pieDelta - ($nb > 1 ? $w : $width);
}
imagettftext(
$this->img,
$this->guidLabelSize,
0,
$xx,
$y + ($d+$this->pieDelta)*sin($deg+$tmp/2) + $this->guidLabelSize/2 - ($nb-1)*$this->guidLabelSize/2,
$this->guidLabelsColor,
$this->titleFont,
$txt);
$deg += $tmp;
}
}
}
// ---------------------------------------------------------
// --- STATIC METHODS ---
// ---------------------------------------------------------
static function html2rgb($color) {
if ($color[0] == '#')
$color = substr($color, 1);
if ($color[0] == '0' && $color[1] == 'x')
$color = substr($color, 2);
if (strlen($color) == 6)
return array(
hexdec($color[0].$color[1]),
hexdec($color[2].$color[3]),
hexdec($color[4].$color[5]));
elseif (strlen($color) == 3)
return array(
hexdec($color[0].$color[0]),
hexdec($color[1].$color[1]),
hexdec($color[2].$color[2]));
else return false;
}
static function rgb2html($r, $g=-1, $b=-1) {
if (is_array($r) && sizeof($r) == 3)
list($r, $g, $b) = $r;
$r = intval($r);
$g = intval($g);
$b = intval($b);
$r = dechex($r < 0 ? 0 : ($r > 255 ? 255 : $r));
$g = dechex($g < 0 ? 0 : ($g > 255 ? 255 : $g));
$b = dechex($b < 0 ? 0 : ($b > 255 ? 255 : $b));
$color = (strlen($r) < 2 ? '0' : '').$r;
$color .= (strlen($g) < 2 ? '0' : '').$g;
$color .= (strlen($b) < 2 ? '0' : '').$b;
return '#'.$color;
}
}
?>
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.