Jump to content

Problems drawing images with PHP GD


sagnik

Recommended Posts

Hello again, I've got a problem with PHP GD. The problem is I'm trying to draw charts (i.e.Bar) and I'm trying to draw the Bell Curve of a data or an array with "Normal Distribution", but the problem is the chart is not fitting inside the allocated image size (1920 x 1080) so the chart is exceeding the boundaries of the image. So I've just tried a library "JpGraph" it works very well but doesn't fit my requirements, so I saw that the library is using some kind of canvas to draw the chart and then merging the image with the original one and I'm trying to do the same but I couldn't understand how. Please help me out, it's very urgent, I've to finish the project within a week.

 

public function histogram(array $samples, int $bins, string $heading = null, string $xAxisHeading = null, string $yAxisHeading = null) {
		if(is_array($samples)) {
			$data = $_bins = $this->calculateBins($samples, $bins);
			/*
			 * Chart settings and create image
			 */

			// Margin between label and axis
			$labelMargin = 8;

			$count = count($samples);

			// Max value on y-axis
			$yMaxValue = max(array_column($data, 'count'));
			if($yMaxValue % 2 == 1)
				$yMaxValue++;
			// Distance between grid lines on y-axis
			$yLabelSpan = ceil($yMaxValue / $bins);
			$t1 = ($yLabelSpan * $bins);
			if($t1 > $yMaxValue)
				$yMaxValue = $t1;

			// Bar and line width
			$lineWidth = 1;
			$barWidth = 20;

			// Image dimensions
			$imageWidthActual = round(count($data) * $barWidth * ($labelMargin / 2));
			$imageHeightActual = round((count($data) / 2) * $count / 2);
			$imageWidth = min(1920, $imageWidthActual);
			$imageHeight = min(1024, $imageHeightActual);

			// Grid dimensions and placement within image
			$gridTop = 40;
			$gridBottom = round(($imageHeight - $gridTop));
			$gridLeft = 50;
			$gridRight = round(($imageWidth - $gridLeft));
			$gridHeight = $gridBottom - $gridTop;
			$gridWidth = $gridRight - $gridLeft;

			// Bar space
			$barSpacing = $gridWidth / count($data);

			// Font settings
			$font = PHPML_ROOT.'assets/fonts/OpenSans-Regular.ttf';
			$headingFont = PHPML_ROOT.'assets/fonts/OpenSans-Bold.ttf';
			$fontSize = 10;
			$headingFontSize = 12;

			// Init image
			$chart = imagecreatetruecolor($imageWidth, $imageHeight);

			// Setup colors
			$backgroundColor = imagecolorallocate($chart, 255, 255, 255);
			$headingColor = imagecolorallocate($chart, 0, 0, 0);
			$axisColor = imagecolorallocate($chart, 85, 85, 85);
			$gridColor = imagecolorallocate($chart, 212, 212, 212);
			//$barColor = imagecolorallocate($chart, 47, 133, 217);
			$labelColor = $axisColor;

			imagefill($chart, 0, 0, $backgroundColor);
			imagesetthickness($chart, $lineWidth);

			/*
			 * Print grid lines bottom up and Y-Labels
             */
			for($i = 0; $i <= $yMaxValue; $i += $yLabelSpan) {
				$y = $gridBottom - $i * $gridHeight / $yMaxValue;

				// draw the line
				imageline($chart, $gridLeft, $y, $gridRight, $y, $gridColor);

				// draw right aligned label
				$labelBox = imagettfbbox($fontSize, 0, $font, strval($i));
				$labelWidth = $labelBox[4] - $labelBox[0];

				$labelX = $gridLeft - $labelWidth - $labelMargin;
				$labelY = $y + $fontSize / 2;

				imagettftext($chart, $fontSize, 0, $labelX, $labelY, $labelColor, $font, strval($i));
			}

			/*
			 * Draw x- and y-axis
             */
			imageline($chart, $gridLeft, $gridTop, $gridLeft, $gridBottom, $axisColor); //Left
			//imageline($chart, $gridRight, $gridTop, $gridRight, $gridBottom, $axisColor); //Right
			//imageline($chart, $gridLeft, $gridTop, $gridRight, $gridTop, $axisColor); //Top
			imageline($chart, $gridLeft, $gridBottom, $gridRight, $gridBottom, $axisColor); //Bottom

			$headingBox = $this->getTextBoundingBox($chart, $headingFontSize, $headingFont, strval($heading), 0);
			$xAxisHeadingBox = $this->getTextBoundingBox($chart, $fontSize, $font, strval($xAxisHeading), 0);
			$yAxisHeadingBox = $this->getTextBoundingBox($chart, $fontSize, $font, strval($yAxisHeading), 90);

			if(!empty($heading))
				imagettftext($chart, $headingFontSize, 0, $headingBox['x']['center'], $headingBox['y']['top'], $headingColor, $headingFont, $heading);
			if(!empty($xAxisHeading))
				imagettftext($chart, $fontSize, 0, $xAxisHeadingBox['x']['center'], $xAxisHeadingBox['y']['bottom'], $headingColor, $font, $xAxisHeading);
			if(!empty($yAxisHeading))
				imagettftext($chart, $fontSize, 90, $yAxisHeadingBox['x']['left'], $yAxisHeadingBox['y']['middle'], $headingColor, $font, $yAxisHeading);

			/*
			 * Draw the bars with X-labels
             */
			$itemX = $gridLeft + $barSpacing / 2;
			$canvasWidth = ($imageWidth * 2);
			$canvasHeight = ($imageHeight * 2);
			$canvas = imagecreatetruecolor($canvasWidth, $canvasHeight);
			$canvasBackgroundColor = imagecolorallocate($canvas, 255, 255, 255);
			$labelColor = imagecolorallocate($canvas, 85, 85, 85);
			$barColor = imagecolorallocate($canvas, 47, 133, 217);
			imagefill($canvas, 0, 0, $canvasBackgroundColor);
			foreach($data as $key => $bin) {
				// Draw the label
				$labelBox = imagettfbbox($fontSize, 0, $font, $key);
				$labelWidth = $labelBox[4] - $labelBox[0];
				$labelX = $itemX - $labelWidth / 2;
				$labelY = $gridBottom + $labelMargin + $fontSize;
				imagettftext($canvas, $fontSize, 0, $labelX, $labelY, $labelColor, $font, $key);
				foreach($bin as $k=>$value) {
					// Draw the bar
					$x1 = $itemX - $barWidth / 2;
					$y1 = $gridBottom - $value / $yMaxValue * $gridHeight;
					$x2 = $itemX + $barWidth / 2;
					$y2 = $gridBottom - 1;
					imagefilledrectangle($canvas, $x1, $y1, $x2, $y2, $barColor);
				}
				$itemX += $barSpacing;
			}
			$resized = $this->resize($canvas, $imageWidth, $imageHeight);
			imagealphablending($canvas, false);
			imagesavealpha($canvas, true);
			//imagecopyresampled($chart, $canvas, 0, 0, 0, 0, $canvasWidth, $canvasHeight, $imageWidth, $imageHeight);
			imagecopymerge($chart, $resized, 0, 0, 0, 0, $canvasWidth, $canvasHeight, 100);
			//imagecopyresized($chart, $canvas, 10, 10, 0, 0, $canvasWidth, $canvasHeight, $imageWidth, $imageHeight);
			/*
			 *  Output image to browser
             */
			//header('Content-Type: image/png');
			ob_start();
			imagepng($chart);
			$outBlob = ob_get_contents();
			ob_end_clean();
			imagedestroy($canvas);
			imagedestroy($chart);
			$outImg = "data: image/png;base64,".base64_encode($outBlob);
			/*
			 * Output image to file
             */
			//imagepng($chart, 'chart.png');
			return $outImg;
		}
		return false;
	}
Link to comment
Share on other sites

I know it's not what you're asking, but I've always used google charts to do what you're trying to do https://developers.google.com/chart

 

They have the advantage over static images of being able to hover your mouse over it and see extra data like exact values at different times or whatever your data is. Google charts is how I did the memory/cpu bar graph in cpanel, and HelioHost has some admin charts for monitoring the servers too that have been working great for years. They're really quick and easy to set up. I just use php and loop through the data from the database and output the javascript onto the page. I can provide some example code if that's something you're interested in.

  • Like 1
Link to comment
Share on other sites

I understand but I want it to be solely based on PHP. It would be better if I could do it without any dependencies. And I'm trying to avoid any type of client-side scripts for now. But I'll do it for sure in the future.

The project I'm currently working on is "PHPML" which is a PHP implementation of some Python Machine Learning libraries (like NumPy, scipy, matplotlib, NLP, etc.) as the project is currently under development I don't want to mess with client-side. I want the users to just import one PHP Script on a page and access all the libraries.

But in the future if it becomes a need to include some javascripts for any reason I lok into it but for now I want to stick with PHP GD (if possible).

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...