• 0

SVG line graph


Question

I was looking for a simple way of making a line graph with svg.

Since I did not really want to include yet another library for something that seemed quite simple I decided to write something myself.

After some fiddling around I came up with the following php file which returns a svg graph based on the url parameter values you pass to it:

<?php
header('Content-type: image/svg+xml');
echo '<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">';
if(isset($_GET[0])) {
	$points = $_GET;
} else {
	echo "<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"></svg>";
	die();
}
$height = max($points);
$step = 100/(count($points)-1);
$path = $line = "m-10,".($height+10)." l0,-".($points[0]+10)." l10,0 ";
for($p=1;$p<count($points);$p++) {
	$path .= "l$step,".($points[$p-1]-$points[$p])." ";
}
$path .= "l10,".($points[count($points)-1]+10)." z";
?>
<svg
	width="100%"
	height="100%"
	id="graph"
	version="1.1"
	xmlns="http://www.w3.org/2000/svg"
	viewBox="0 0 100 <?php echo $height;?>"
	preserveAspectRatio="none">
	<linearGradient
		id="linearGradient"
		x1="0"
		y1="0"
		x2="0"
		y2="100%">
		<stop
			style="stop-color:#5677fc;stop-opacity:.3;"
			offset="0" />
		<stop
			style="stop-color:#5677fc;stop-opacity:0;"
			offset="1" />
	</linearGradient>
	<path
		id="line"
		stroke-width="2px"
		vector-effect="non-scaling-stroke"
		style="fill:url(#linearGradient);stroke:#5677fc;"
		d="<?php echo $path;?>"
	/>
</svg>

As example I use it like this:

<?php
$points = array(100,28,89,33,54,12,90,56,84,37,19,129);
?>
<img class="graph" src="/img/graph.php?<?php echo http_build_query($points);?>" />

What do you guys think? Is this a bad or a good idea? Anything to improve?

I'm thinking about making it possible to add multiple lines.

 

Oh and here's a image of the graph :

AU1q7.jpg

  • Like 2
Link to comment
Share on other sites

14 answers to this question

Recommended Posts

  • 0

here's updated code for multiple graphs:

 

usage:

<?php
$a = array(45,28,89,33,54,12,90,56,84,37,19,129);
$b = array(66,100,65,0,30,57,3,53,71,20,23,47);
//count($a) and count($b) must be equal
$colors = array("5677fc","fbc157");
$points = array("points"=>array($a,$b),"colors"=>$colors);
?>
<img class="graph" src="graph.php?<?php echo http_build_query($points);?>" />

graph.php:

<?php
header('Content-type: image/svg+xml');
echo '<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">';
if(isset($_GET["points"])) {
	$values = $_GET["points"];
	$colors = $_GET["colors"];
} else {
	echo "<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"></svg>";
	die();
}
$paths = array();
$mergevalues = array();
foreach($values as $points) {
	$mergevalues = array_merge($mergevalues,$points);
}
$height = max($mergevalues);
foreach($values as $i => $points) {
	$step = 100/(count($points)-1);
	$paths[$i] = $line = "m-10,".($height+10)." l0,-".($points[0]+10)." l10,0 ";
	for($p=1;$p<count($points);$p++) {
		$paths[$i] .= "l$step,".($points[$p-1]-$points[$p])." ";
	}
	$paths[$i] .= "l10,".($points[count($points)-1]+10)." z";
}
?>
<svg
	width="100%"
	height="100%"
	id="graph"
	version="1.1"
	xmlns="http://www.w3.org/2000/svg"
	viewBox="0 0 100 <?php echo $height;?>"
	preserveAspectRatio="none">
	<?php
	foreach($colors as $color) {
		echo '
		<linearGradient
			id="gradient'.$color.'"
			x1="0"
			y1="0"
			x2="0"
			y2="100%">
			<stop
				style="stop-color:#'.$color.';stop-opacity:.3;"
				offset="0" />
			<stop
				style="stop-color:#'.$color.';stop-opacity:0;"
				offset="1" />
		</linearGradient>
		';
	}
	foreach($paths as $i => $path) {
		echo '
		<path
			id="line"
			stroke-width="2px"
			vector-effect="non-scaling-stroke"
			style="fill:url(#gradient'.$colors[$i].');stroke:#'.$colors[$i].';"
			d="'.$path.'"
		/>
		';
	}
	?>
</svg>

Feel free to use the code however and for whatever you want.

bFGow.jpg

Link to comment
Share on other sites

  • 0

Here's another update with a bit cleaner code.

<?php
header('Content-type: image/svg+xml');
echo '<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">';
if(isset($_GET["points"])) {
	$values = $_GET["points"];
	$colors = $_GET["colors"];
} else {
	echo "<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"></svg>";
	die();
}
$paths = array();
$mergevalues = array();
foreach($values as $points) {
	$mergevalues = array_merge($mergevalues,$points);
}
$height = max($mergevalues);
foreach($values as $i => $points) {
	$step = 100/(count($points)-1);
	$paths[$i] = $line = "m-10,".($height+10)." l0,-".($points[0]+10)." l10,0 ";
	foreach($points as $p => $point) {
		$paths[$i] .= "L".($p*$step).",".($height-$point)." ";
	}
	$paths[$i] .= "l10,".($points[count($points)-1]+10)." z";
}
?>
<svg
	width="100%"
	height="100%"
	id="graph"
	version="1.1"
	xmlns="http://www.w3.org/2000/svg"
	viewBox="0 0 100 <?php echo $height;?>"
	preserveAspectRatio="none">
	<?php
	foreach($colors as $color) {
		echo '
		<linearGradient
			id="gradient'.$color.'"
			x1="0"
			y1="0"
			x2="0"
			y2="100%">
			<stop
				style="stop-color:#'.$color.';stop-opacity:.3;"
				offset="0" />
			<stop
				style="stop-color:#'.$color.';stop-opacity:0;"
				offset="1" />
		</linearGradient>
		';
	}
	foreach($paths as $i => $path) {
		echo '
		<path
			id="line"
			stroke-width="2px"
			vector-effect="non-scaling-stroke"
			style="fill:url(#gradient'.$colors[$i].');stroke:#'.$colors[$i].';"
			d="'.$path.'"
		/>
		';
	}
	?>
</svg>
Link to comment
Share on other sites

  • 0

Thanks for that code! However I am slightly confused..When I plug in your code, I get a broken image..Never worked with svg files before..Is this only part of the code? I pretty much just copied your code, I didn't change anything. I assume that I need an image..Since no where in that code do I see anything about listing the months. Where would I put that?

Thanks for any help!

Link to comment
Share on other sites

  • 0

Thanks for that code! However I am slightly confused..When I plug in your code, I get a broken image..Never worked with svg files before..Is this only part of the code? I pretty much just copied your code, I didn't change anything. I assume that I need an image..Since no where in that code do I see anything about listing the months. Where would I put that?

Thanks for any help!

Just save the code as graph.php then refer to it with a img tag like this:

<?php
$a = array(45,28,89,33,54,12,90,56,84,37,19,129); //points
$colors = array("5677fc");
$points = array("points"=>array($a),"colors"=>$colors);
?>
<img class="graph" src="graph.php?<?php echo http_build_query($points);?>" />

The months are not included in the code and are just html elements.

I'll rewrite the graph with jslogo and include the months ^^

So you are using php to generate svg code?

Never thought of that.

Might give it a try.

Yeah I'm also currently working on using js to generate svg code.

https://www.neowin.net/forum/topic/1239360-svg-js-logo/

Link to comment
Share on other sites

  • 0
var graph = new jslogo("#jslogo");
var points = [100,28,89,33,54,12,90,56,84,37,19,129];
var max = Math.max.apply(Math, points);
graph.pen("rgba(20,80,200,.7)");
graph.gradient("graph_bg","rgba(20,80,200,.1)","rgba(20,80,200,.4)",0);
graph.fill("url(#graph_bg)");
graph.step(-10,0);
graph.stepA(-10,(graph.height/max)*points[0]);
for(x=0;x<points.length;x++) {
	graph.stepA(graph.width/(points.length-1)*x,(graph.height/max)*points[x]);
}
graph.stepA(graph.width,0);

Here's the same code for jslogo, it's simpler in my opinion :P

I'll add the months and also change the border thickness.

Link to comment
Share on other sites

  • 0

Here's some jslogo code that also includes the axis:

//Resize svg drawing
//This code is not necessary and only used for changing some html on the jslogo site
$("#jslogo")[0].setAttribute("viewBox","0 0 800 400");
$("#jslogo").css("width","800px");
$("#jslogo").css("height","400px");

//Disable grid bg
//This code is not necessary and only used for changing some html on the jslogo site
grid()

//Properties and values
var graph = new jslogo("#jslogo");
var points = [100,28,89,33,54,12,90,56,84,37,19,129];
var x_axis = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; //points.length must be equal with x_axis.length
var padding = 50;
var y_step_height = 40;

//Draw graph
var max = Math.max.apply(Math, points);
graph.pen("none");
graph.step(padding,padding);
graph.pen("rgba(20,80,200,.7)");
graph.gradient("graph_bg","rgba(20,80,200,0)","rgba(20,80,200,.2)",0);
graph.fill("url(#graph_bg)");
graph.stepA(padding,((graph.height-2*padding)/max)*points[0]+padding);
for(x=0;x<points.length;x++) {
	graph.stepA((graph.width-2*padding)/(points.length-1)*x+padding,((graph.height-2*padding)/max)*points[x]+padding);
}
graph.stepA(graph.width-padding,padding);
graph.pen("none");
graph.fill("none");

//Draw graph border
graph.stepA(padding,padding);
graph.pen("#ddd");
graph.step(0,graph.height-2*padding);
graph.step(graph.width-2*padding,0);
graph.step(0,-graph.height+2*padding);
graph.step(-graph.width+2*padding,0);
graph.pen("none");


//Draw y-axis
graph.stepA(padding-10,padding);
var y_steps = Math.floor((graph.height-2*padding)/y_step_height);
for(x=0;x<y_steps;x++) {
	graph.text(Math.round(max/y_steps*x),"#777","end");
	graph.forward(y_step_height);
}

//Draw x-axis
graph.stepA(padding,padding-20);
graph.right(90);
for(x=0;x<x_axis.length;x++) {
	graph.text(x_axis[x],"#777","middle");
	graph.forward((graph.width-2*padding)/(x_axis.length-1));
}

//Change font-size
//This code is not necessary and only used for changing some css on the jslogo site
$("#jslogo").css("font-size","12px");

You can try the code on http://seapip.com/jslogo/

 

Preview:

2QwI8.jpg

 

I haven't added support yet for using background images in jslogo and using a different border width then 1px.

 

Implementing jslogo is quite simple, download jslogo.js(requires jquery) and put on your site and then just start drawing with the logo syntax as shown above in the example.

Edited by Seahorsepip
Link to comment
Share on other sites

  • 0

Could we please have an update for some axis and there labels on the php generate svg graph?

What would you like to see changed/added? And do you prefer the php method or the js method?

  • Like 1
Link to comment
Share on other sites

  • 0

This is an amazing piece of work that you have created here and will be really beneficial to my work.

What would you like to see changed/added? And do you prefer the php method or the js method?

If it is at all possible, for you to add axis to the chart with the axis labels; that would be awesome. Although I am not sure I am setting it up right because your example image shows axis and labels. Here is my example (live version found here http://pupil.space/test/linegraph.php):                                                                Screenshot_2015-09-27_at_14.28.59.thumb.

I would prefer you to use the PHP method; because, it reduces the amount of data which my client has to load; increasing the speed of the webpage.

Thanks again, for your contribution.

Link to comment
Share on other sites

  • 0

This is an amazing piece of work that you have created here and will be really beneficial to my work.

If it is at all possible, for you to add axis to the chart with the axis labels; that would be awesome. Although I am not sure I am setting it up right because your example image shows axis and labels. Here is my example (live version found here http://pupil.space/test/linegraph.php):                                                                Screenshot_2015-09-27_at_14.28.59.thumb.

I would prefer you to use the PHP method; because, it reduces the amount of data which my client has to load; increasing the speed of the webpage.

Thanks again, for your contribution.

The axis labels were not part of the code but added manually with html elements, this is far from efficient :/ 

I can try and see if I can add the text in the php code. But keep in mind that the js method is more bandwidth efficient since you're only loading points on every page refresh, the js files(jslogo.js and graph.js) are only loaded once and then cached ^^

  • Like 1
Link to comment
Share on other sites

  • 0

The axis labels were not part of the code but added manually with html elements, this is far from efficient :/ 

I can try and see if I can add the text in the php code. But keep in mind that the js method is more bandwidth efficient since you're only loading points on every page refresh, the js files(jslogo.js and graph.js) are only loaded once and then cached ^^

It would be greatly appreciated if you could do this.

Link to comment
Share on other sites

  • 0

It would be greatly appreciated if you could do this.

Just went and gave it a try but there is a problem, When the labels are part of the svg the text is also transformed when the image is stretched, which means the text will be stretched with the graph :/

So only thing that would work properly would be putting html tags with the labels which are positioned with css around the img that loads the svg.

Link to comment
Share on other sites

  • 0

 

It would be greatly appreciated if you could do this.

Wrote a axis labels implementation in php with some css, the labels are generated on the page with the image html element itself.
It was a bit tricky to have the labels positioned correctly and move correctly when the graph is resize but somehow I managed to do it.

Preview:

3i4PPPo.png

Code:

<style type="text/css">
html,body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,abbr,address,cite,code,del,dfn,em,img,ins,kbd,q,samp,small,strong,sub,sup,var,b,i,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,figcaption,figure,footer,header,hgroup,menu,nav,section,summary,time,mark,audio,video {
	margin: 0;
	padding: 0;
	border: 0;
	outline: 0;
	font-size: 100%;
	vertical-align: baseline;
	background: transparent;
}
article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section {
	display: block;
}
</style>
<?php
//Above css is just used to reset the css of the page,
//not needed when using this code on your own site since I assume it has a css reset already


$y_steps = 20;//The amount of steps to show in the y-axis
$padding = 48;//Padding around the graph in px for the axis labels
$distance = 10;//Distance between axis labels and the graph in px

$a = array(45,28,89,33,54,12,90,56,84,37,19,129);
$b = array(66,100,65,0,30,57,3,53,71,20,23,47);
$x_axis = array("a","b","c","d","e","f","g","h","i","j","k","l");
$colors = array("5677fc","fbc157");
$points = array("points"=>array($a,$b), "x_axis"=>$x_axis, "colors"=>$colors);
?>
<style type="text/css">
.graph_wrapper {
	position: relative;
	height: 100%;
	width: 100%;
	overflow: hidden;
}
.graph_wrapper .graph_img {
	border: 1px solid #eee;
	position: absolute;
	bottom: <?php echo $padding;?>px;
	left: <?php echo $padding;?>px;
	right: <?php echo $padding;?>px;
	top: <?php echo $padding;?>px;
}
.graph_wrapper .graph {
	margin: 0;
	width: 100%;
	height: 100%;
}
.graph_wrapper .y_axis {
	position: absolute;
	bottom: <?php echo $padding;?>px;
	top: <?php echo $padding;?>px;
	left: 0;
	width: <?php echo $padding;?>px;
}
.graph_wrapper .y_axis .label_wrapper {
	height: <?php echo 100/($y_steps);?>%;
	position: relative;
}
.graph_wrapper .y_axis .label_wrapper .label {
	text-align: right;
	line-height: 100%;
	height: 100%;
	position: absolute;
	right: <?php echo $distance;?>px;
	top: -50%;
	display: inline-block;
	vertical-align: middle;
}
.graph_wrapper .y_axis .label_wrapper .label:before {
	content: '';
	display: inline-block;
	height: 100%;
	vertical-align: middle;
}
.graph_wrapper .x_axis {
	position: absolute;
	bottom: 0;
	left: <?php echo $padding;?>px;
	right: <?php echo $padding;?>px;
	height: <?php echo $padding;?>px;
}
.graph_wrapper .x_axis .label_wrapper {
	width: <?php echo 100/(count($x_axis)-1);?>%;
	float: left;
	height: <?php echo $padding;?>px;
}
.graph_wrapper .x_axis .label_wrapper .label {
	margin-left: -50%;
	margin-right: 50%;
	text-align: center;
	margin-top: <?php echo $distance;?>px;
}
.graph_wrapper .x_axis div:first-child {
}
.graph_wrapper .x_axis .label_wrapper:last-child {
	position: absolute;
	bottom: 0;
	right: <?php echo -100/(count($x_axis)-1);?>%;
}
</style>
<div class="graph_wrapper">
	<div class="graph_img">
		<img class="graph" src="line_graph.php?<?php echo http_build_query($points);?>" />
	</div>
	<div class="y_axis">
		<?php
		$mergevalues = array();
		foreach($points["points"] as $points) {
			$mergevalues = array_merge($mergevalues,$points);
		}
		$max = max($mergevalues);
		for($y=$y_steps;$y>=0;$y--){
			echo "<div class=\"label_wrapper\"><div class=\"label\">".round($max/$y_steps*$y)."</div></div>";
		}
		?>
	</div>
	<div class="x_axis">
		<?php
		foreach($x_axis as $x=>$label) {
			echo "<div class=\"label_wrapper\"><div class=\"label\">$label</div></div>";
		}
		?>
	</div>
</div>

 

Link to comment
Share on other sites

This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.