diff --git a/GnuPlot.php b/GnuPlot.php index aeff0da..f68d128 100644 --- a/GnuPlot.php +++ b/GnuPlot.php @@ -6,6 +6,9 @@ class GnuPlot { // Values as an array protected $values = array(); + + // Values as an array + protected $pieValues = array(); // Time format if X data is time protected $timeFormat = null; @@ -47,6 +50,33 @@ class GnuPlot protected $process; protected $stdin; protected $stdout; + + // Pie chart scale + protected $pieScale = 0.25; + + // Pie chart position on paper + protected $piePosition = 0.5; + + // Pie chart a + protected $pieS = 0.05; + + // Pie Chart colors + protected $colors = array('#585C00', '#750400', '#A2A811', '#2474B5', '#F5140C'); + + // Pie Chart label row height + protected $labelRowHeight = 40; + + // Pie Chart label font size + protected $labelFontSize = 16; + + // Pie Chart font location + protected $font = ""; //img/fonts/TahomaRegular.ttf + + // Pie Chart font color + protected $fontColor = "#333333"; + + // Pie Chart legend color + protected $legendColor = "#ffffff"; public function __construct() { @@ -57,6 +87,8 @@ public function __construct() public function __destruct() { $this->sendCommand('quit'); + fclose($this->stdin); + fclose($this->stdout); proc_close($this->process); } @@ -192,14 +224,23 @@ protected function sendInit() /** * Runs the plot to the given pipe */ - public function plot($replot = false) + public function plot($pieChart = false, $replot = false) { if ($replot) { $this->sendCommand('replot'); } else { - $this->sendCommand('plot '.$this->getUsings()); + if(!$pieChart){ + $this->sendCommand('plot '.$this->getUsings()); + } } $this->plotted = true; + + if($pieChart){ + $this->sendPieData(); + $this->sendCommand('plot '.$this->getUsings($pieChart)); + return ; + } + $this->sendData(); } @@ -238,6 +279,11 @@ public function get() return $result; } + +// private function readStream(){ +// +// } + /** * Display the plot */ @@ -312,8 +358,12 @@ public function enableHistogram() /** * Gets the "using" line */ - protected function getUsings() + protected function getUsings($pieChart = false) { + if($pieChart){ + return "2"; + } + $usings = array(); for ($i=0; $ivalues); $i++) { @@ -371,4 +421,217 @@ protected function openPipe() $this->stdin = $pipes[0]; $this->stdout = $pipes[1]; } -} + + /** + * Sets the pie chart label font size + */ + public function setPieLabelFontSize($labelFontSize){ + $this->labelFontSize = $labelFontSize; + + return $this; + } + + /** + * Sets label of the pie chart data. If index is null it sets last + * inserted data title else it sets title for specified $index data; + */ + public function setPieLabel($title, $index = null){ + if(!$index){ + $this->titles[] = $title; + return $this; + } + + $this->titles[$index] = $title; + return $this; + } + + /** + * Sets pie chart scale + */ + public function setPieScale($pieScale){ + $this->pieScale = $pieScale; + + return $this; + } + + /** + * Sets pie chart scale + */ + public function setFontColor($fontColor){ + $this->fontColor = $fontColor; + + return $this; + } + + /** + * Sets pie chart scale + */ + public function setLegendColor($legendColor){ + $this->legendColor = $legendColor; + + return $this; + } + + /** + * Push a new data for pie chart. $value is a float number betwean 0 and 1. + */ + public function pushPie($value, $index = null){ + if(!$index){ + $this->pieValues[] = $value; + return $this; + } + + $this->pieValues[$index] = $value; + return $this; + } + + + /** + * Sends all the command to the given pipe to give it the + * current data + */ + protected function sendPieData() + { + $lVal = 0; + $cVal = 0; + foreach ($this->values as $index => $value) + { + $cVal = $lVal + $value*360; + if($index == (sizeof($this->values) - 1 )) + $cVal = 360; + + $this->sendCommand('set obj ' . ($index+1) . ' circle arc [' . $lVal . ':' . $cVal . '] fc rgb "' . $this->colors[$index] . '" '); + $this->sendCommand('set obj ' . ($index+1) . ' circle at screen ' . $this->piePosition . ',' . $this->piePosition . ' size screen ' . $this->pieScale . ' front'); + + $lVal = $cVal; + } + $this->sendCommand('e'); + } + + /** + * Prepares pie chart data to send + */ + protected function preparePieData(){ + $sum = 0; + $n = sizeof($this->pieValues); + + for($i = 0; $i < $n; $i++){ + $sum += $this->pieValues[$i]; + } + + foreach ($this->pieValues as $key => $value){ + $this->values[$key] = ($value/$sum); + } + } + + protected function sendPieInit(){ + if(!sizeof($this->values)) + return false; + + if ($this->title) { + $this->sendCommand('set title "'.$this->title.'"'); + } + + $this->sendCommand('reset'); + $this->sendCommand('unset border'); + $this->sendCommand('unset tics'); + $this->sendCommand('unset key'); + $this->sendCommand('set angles degree'); + $this->sendCommand('set yrange [0:1]'); + $this->sendCommand('set style fill solid 1.0 border -2'); + } + + /** + * Write the current plot to a file + */ + public function getPieChart() + { + $this->preparePieData(); + $this->sendPieInit(); + $this->sendCommand('set terminal png size '.$this->width.','.$this->height); + fflush($this->stdout); + $this->plot(true); + + // Reading data, timeout=100ms + $result = ''; + $timeout = 100; + do { + stream_set_blocking($this->stdout, false); + $data = fread($this->stdout, 128); + $result .= $data; + usleep(5000); + $timeout -= 5; + } while ($timeout>0 || $data); + + $imgChart = imagecreatefromstring($result); + $imgLabel = $this->getLabelImg(); + + $hheight = imagesy($imgLabel); + + imagecopy($imgLabel, $imgChart, 0, 0, 0, 0, $this->width, $hheight); + + $this->drawLabels($imgLabel); + + return imagepng($imgLabel); + } + + protected function getLabelImg(){ + $height = $this->height + sizeof($this->titles) * $this->labelRowHeight; + + $image = imagecreatetruecolor($this->width, $height); + $white = imagecolorallocatealpha($image, 255, 255, 255, 127); + imagefill($image, 0, 0, $white); + return $image; + } + + protected function drawLabels($image){ + $rgb = $this->hexToRGB($this->fontColor); + $fontColor = imagecolorallocate($image, $rgb['r'], $rgb['g'], $rgb['b']); + + $rgb = $this->hexToRGB($this->legendColor); + $legendColor = imagecolorallocate($image, $rgb['r'], $rgb['g'], $rgb['b']); + + $x1 = 20; + $x2 = 40; + $labelX = 10; + + imagefilledrectangle($image, $labelX, $this->height-$labelX, $this->width - $labelX, imagesy($image)-3, $legendColor); + + foreach ($this->titles as $key => $value) { + $rgb = $this->hexToRGB($this->colors[$key]); + $color = imagecolorallocate($image, $rgb['r'], $rgb['g'], $rgb['b']); + + $y = $this->height + $key * $this->labelRowHeight; + $y2 = $y + 30; + + $value .= ' ' . round($this->values[$key]*100, 2) . '%'; + + if($this->font){ + imagettftext($image, $this->labelFontSize, 0, $x2+8, $y2-7, $fontColor, $this->font, $value); + } else { + imagestring($image, $this->labelFontSize, $x2+8, $y+8, $value, $fontColor); + } + + imagefilledrectangle($image, $x1, $y, $x2, $y2, $color); + } + } + + protected function hexToRGB ($hexColor){ + if( preg_match( '/^#?([a-h0-9]{2})([a-h0-9]{2})([a-h0-9]{2})$/i', $hexColor, $matches ) ) + { + return array( + 'r' => hexdec( $matches[ 1 ] ), + 'g' => hexdec( $matches[ 2 ] ), + 'b' => hexdec( $matches[ 3 ] ) + ); + } + else + { + return array( + 'r' => 0, + 'g' => 0, + 'b' => 0 + ); + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index 136bc01..f952064 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,24 @@ run `realTime.php` for example: $plot->refresh(); ``` +PIE CHART USAGE +=== +```php +setHeight(400); + $plot->setWidth(400); + + $plot->pushPie(50)->setPieLabel('1950'); + $plot->pushPie(100)->setPieLabel('2000'); + $plot->pushPie(150)->setPieLabel('2050'); + + header('Content-type: image/png'); + echo $plot->getPieChart(); +``` + + API === @@ -105,6 +123,8 @@ API * `setHeight($height)`, sets the width of the graph * `addLabel($x, $y, $text)`, add some label at a point + + License =======