Make Line Chart from User-Inputted Data
In this tutorial, we will make a Line chart from User-Inputted data. For line drawing in this line chart creation program, we chose to use Make Anti-alias (Almost) Lines, but could have used Bresenham's Line Algorithm instead which is here (Make Anti-alias (Almost) Lines Using Bresenham's Line Algorithm) on our site.
JavaScript Charts, Graphs, Graphics, Circles, Ellipses, Arcs, Lines, and Polygons
Grab and Drop, Not Drag and Drop
Add Ids and onClicks to Divs
Add Ids and onClicks and Grab and Drop to Divs
Make Anti-alias (Almost) Lines
Make Anti-alias (Almost) Lines Using Bresenham's Line Algorithm
Good JavaScript Circle Algorithm
Good JavaScript Ellipse Algorithm
Good JavaScript Arc Algorithm
Make JavaScript Irregular Polygon
JavaScript Area of Irregular Polygon Algorithm
Make Line Chart from User-Inputted Data
Make Line Chart from CSV Data
Make Line Chart from MySQL Table Data
Make Bar Chart from User-Inputted Data
Make Bar Chart from CSV Data
Make Bar Chart from MySQL Table Data
Make Pie Chart from User-Inputted Data
Make Pie Chart from CSV Data
Make Pie Chart from MySQL Table Data
Data for line charts and bar graphs and pie charts can come from any number of sources. We considered putting together a user input form and the numeric data could be charted and the non-numeric data turned into labels, and we did that in a line chart script below—the script on this page. But we decided to also interface with the two most important database sources needing charting: CSV files and MySQL tables. CSV is a delimited data format that has fields/columns separated by the comma character and records/rows terminated by newlines, and MySQL is the most popular open-source database ever. The first, CSV, can be pulled out of any competant database app, for example: spreadsheets such as Microsoft Excel or databases such as Microsoft Access database or Apache OpenOffice, which is a spreadsheet, database, word processor, plus other stuff (and it's free). MySQL is a server-side relational database, and the tables that we store in this database can be easily created, modified, or just read with server-side computer languages—such as PHP. We'll read a MySQL table here: Make Line Chart from MySQL table data, and a CSV file here: Make Line Chart from CSV Data, but on this page we'll stick to User-Inputted data.
In the script, the user will simply type a title and comma-separated data and labels into a form and press the submit button. Excel can make lots better graphs than what we'll do here (but at some point one has to enter data even in Excel). However, if you like programming as much as we do, you'd like to check out how it's done. Besides, what if you've created a PHP poll but do not feel like dealing with the script for making a chart, but you do want a bar chart or line chart or pie chart?
When entering comma-separated labels, remember to type in comma-separated words, like the names of months for example (labels need to be 12 characters or less, so keep this in mind, or you can change the number in the trimit() function). When entering comma-separated data amounts, keep in mind that these must be just plain numbers without decimal points. If your data is .05, .77, 1.07, and .98, simply type 5,77,107,98 with no spaces, dollar signs, quotes, or other things. If you need it to be about money, have this in your chart name, such as Monthly Data in Dollars Collected From Selling Pottery. If you'd like to add a $ filter or decimal point filter so that you can deal with exported Excel data with $ in front of it, that's simple programming we'll leave up to you to add to the PHP program below.
Check out the PHP program below called Make Line Chart from User-Inputted Data, which you may copy from this page and name as a PHP file called getuserdatatolinechart.php.
In the script, after a bit of CSS styling, we code the bDiv DIV styles. bDiv we use for both the "labels" labels and the "data" labels. We'll be adding these labels to the document with DOM methods like createElement() and appendChild(), using innerHTML for inserting the actual text into the DIV. We'll use calculated height and marginLeft properties on the DIVs later. Note that the overflow property is hidden and the background-color is transparent so in case you decide to have longer labels, the text won't slobber all over nearby text.
Now we have the function validate(), for form validation. We include PHP form validation later. We use the standard regular expression codes, and also utilize the JavaScript search method. So, sendname is the form name and title is the input tag name, and the next two scripts filter data and labels, similarly. Note the focus() method is used if the user uses forbidden characters. What this does is keep the user in the input box until he does it right, by focusing on that box, which means keeping the cursor there. Our JavaScript filter is friendlier than our PHP input filter, since the former lets you edit input box contents on the fly, while the latter reloads the page so you start over—your penalty for having JavaScript turned off! The return true code means input is okay, and return false means it was not. The form onsubmit event runs the validate() function, and return false means the form will not get submitted.
Next comes the bye() and hello() functions. We didn't end up needing the hello() function, but the bye() function we
did. Once the line chart is made, we run bye() to display the question Do another? by setting the display property for this question to block. It's a link that prompts you for an answer. At the same time,
bye() sets the display property for the user input form to none. We are preventing these forms from being displayed on top of each other, chaotically.
Next, there's the data input form whose action is the PHP file getuserdatatolinechart.php, which is the name you need to give the PHP file on this page. In other words, once you type in your data, the web page calls itself, reloading but remembering the POSTed title, data, and labels you typed into this user input form.
Next comes the div with the message Do another? Note that its display property, gotten from class='reload', starts out as none—it will stay that way until form submission. This message is link text and clicking the link reloads the page (getuserdatatolinechart.php) and displays the input form again.
Next comes the PHP script. We POST in the title and filter it with regular expressions, using the preg_match() function, which was likely invented to filter input. We do the same with the POSTed data and labels fields, which have comma-separated data, you recall. But we cannot leave them in this form, so we use the explode() function, which returns an array of strings, each of which is a substring of the to-be-processed POSTed string. The array is formed by splitting the POSTed string on boundaries formed by the string delimiter—our commas. These PHP arrays will get converted to JavaScript arrays with JavaScript Object Notation (JSON), a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse. How else will you convert our data from PHP to JavaScript? We employ the max() function to get the biggest value in the data array. Finally we use the strlen() function to check if the POSTed array strings were empty or nearly empty. If so, we show a message and reload the page.
Now come JavaScript Object Notation (JSON) for PHP to JavaScript conversions. We use the json_encode() function to convert the PHP $data and $labels arrays and $flag flag and $biggest value and the chart title into JavaScript arrays and variables, respectively, for convenient use.
In the line drawing script, the function goodline() first gets its variables declared. Next the hypotenuse length is put into the lengthofline variable, using good old Pythagorus. (The formula gets the square root of the sum of the squares of two adjacent sides.) Next, we define a for loop where we go from one end (of the line we're drawing) to the other, a pixel at a time, plotting divs on each line pixel. So, technically, given two points we find the length of the line between two given points and then a variation on slope-intercept form and point-slope form equations is used to plot each point of the line. The line is not quite a line with anti-aliased smoothness, but this technique using a 4x4 grey-background-colored div is the next best thing. The addtopage variable has all those divs added to it and now we add one more thing: a vertical line that will be right above one of the "labels" labels and going to the top of the chart, intersecting a
point where two lines meet and the data label is later inserted. Finally, we use the innerHTML() function to put the two lines on the page. Where does the xx[q] number for the left style position of the vertical line come from? You'll see below.
Now come JavaScript Object Notation (JSON) for PHP to JavaScript conversions. We use the json_encode() function to convert the labels array and the data array and the biggest data number and the title name into JavaScript arrays and variables, respectively, for convenient use. Then we declare xx and yy as arrays. Then we get a factor (r) to divide each data amount by, after which we subtract all this from 497 (bottom y edge of graph) to get a y value to end a line at. Next we get ll, the actual number of characters in the highest data amount. E.g., the amount 555 would make ll=3. Next we get 0.01 as the value of factor (still using 555 as an example), using the Math.pow() method. Next we get 500 (still using 555 as an example) as the value of num which is the highest value where there will be a horizontal line. There will be 5 horizontal lines added. They will be at y values corresponding to the data values 100, 200, 300, 400, and 500.
Next we get fac=100 by use of Math.pow(10,ll-1), so we have the lowest value where there will be a horizontal line. Now we use the variable firstint to hold num/fac which is 5—the number of horizontal graph grid lines. And next we use variable dd to hold num/b which is 0.9009. Then we get 63 when we use the variable o to hold Math.round((350*dd)/firstint). So this 63 is the distance apart of each horizontal line. The 350 is the height of the graph minus 30—which we did because we don't want a data label in the graph or a line end to hit the top of the line graph. Only the vertical grid lines are allowed to touch the top.
Next we use the Document Object Model (DOM) in a for loop to add horizontal lines to the page, even though the line chart's data lines in goodline() use innerHTML only. We use the createElement() Method to bring a div into existence and the appendChild() Method to get it integrated into the document. The innerHTML property is used to put the line divs into the div elements.
Next we test the a and the m arrays—if both have lengths greater than 0, we go on. Otherwise we jump to the end of the script and do nothing. This ensures the inputted data really contained data to graph. Now comes a for loop in which we put values in the xx and yy arrays. But since i starts at 0, the first data line start (also the vertical line x coordinate) is at xx[i]=59. The next xx[i] value is at 137, which is 78+59, and so on, with each new line starting 78 horizontal pixels right of the last. The factor r is used to divide each data amount (a[i]) by, after which we subtract all this from 497 (bottom y edge of graph) to get a y value to end a line at. We use if(i>0) before running the line routine because we are drawing lines from A to B, and when i is 0, we know only the A but not the B so where would we draw such a line TO? And so the "else" is that we do nothing but draw the vertical line at x=59 right now. But if i > 0 we run the line routine, packing it with 4 parameter values: goodline(xx[i-1], xx[i], yy[i-1], yy[i]);. This is all the data needed for A and B, above, which we're just using here to say one line's start point to the next line's start point.
We've already talked about the line routine, goodline(), which draws a 4-pixel-thick line representing data fluctuations and then draws a vertical line as a graph grid line. The for loop that runs this function several times continues until the last line is drawn. However, there are 3 more things to do: the "labels" labels, the data labels, and the box around the entire line chart.
Starting with the "labels" labels, we use the Document Object Model (DOM) in a for loop to add "labels" labels to the page. We use createElement() and appendChild() DOM functions. Note the calculated marginLeft property and the innerHTML() method to get the label into the new div.
We do the same thing with the data labels: we use the Document Object Model (DOM) in a for loop to add data labels to the page. We use createElement() and appendChild() DOM functions. Note the calculated marginLeft property and top property and the innerHTML() method to get the label into the new div.
Finally, we insert the line chart title under the chart and we draw a box around the whole line chart using a width forced by the width and min-width properties. And we end with the bye() function to undisplay the input form and display the Do another? question link instead.
<html>
<head>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<TITLE>Make Line Charts from User-Inputted Data</TITLE>
<meta name="description" content="Make Line Charts from User-Inputted Data">
<meta name="keywords" content="Make Line Charts from User-Inputted Data,View User-Inputted Data as Line Charts,line chart User-Inputted Data,php,javascript, dhtml, DHTML">
<STYLE TYPE="text/css">
BODY {margin-left:0; margin-right:0; margin-top:0;text-align:left;}
p, li, td {font:13px Verdana; color:black;text-align:left}
h1 {font:bold 28px Verdana; color:black;text-align:center}
h2 {font:bold 24px Verdana;text-align:center}
h3 {font:bold 15px Verdana;}
input {font:13px Verdana;text-align:left}
#myid {position:absolute;left:10px;top:117px;height:380px;border: solid 1px #000;}
#myform {position:absolute;left:50px;top:20px}
#label {position:absolute;left:400px;top:550px;}
.l {font:bold 13px Verdana;text-align:center}
.reload {position:absolute;left:400px;top:50px;text-align:center;display:none}
.bDiv {
width: 80px;
border: none;
background-color: transparent;
font-size: 11px;
font-weight: bold;
font-family: verdana;
color: #000;
padding: 5px;
overflow:hidden
}
</STYLE>
<script language="javascript">
function validate(){
if(document.sendname.title.value.length>0){
var ck_title = /^[a-zA-Z0-9\s\_\,]{2,40}$/;
if (document.sendname.title.value.search(ck_title)==-1)
{alert("Please only enter 2 to 40 letters, numbers, spaces and underlines for Line Chart Title.");document.sendname.title.focus();return false}}
if(document.sendname.data.value.length>0){
var ck_data = /^[0-9\,]{3,200}$/;
if (document.sendname.data.value.search(ck_data)==-1)
{alert("Please only enter 3 to 200 integer numbers separated by commas for Line Chart Data.");document.sendname.data.focus();return false}}
if(document.sendname.labels.value.length>0){
var ck_labels = /^[a-zA-Z0-9\s\_\,]{3,200}$/;
if (document.sendname.labels.value.search(ck_labels)==-1)
{alert("Please only enter 3 to 200 letters, numbers, spaces and underlines separated by commas for Line Chart Labels.");document.sendname.labels.focus();return false}}
return true;}
function bye(){var thediv=document.getElementById('myform');thediv.style.display='none';var b=document.getElementById('b');b.style.display='block';}
function hello(){var thediv=document.getElementById('myform');thediv.style.display='block';var b=document.getElementById('b');b.style.display='none';}
</script>
</head>
<body>
<div id='myform'>
<center><h1>Make Line Charts from User-Inputted Data</h1></center>
<form action='getuserdatatolinechart.php' method='post' name='sendname' onsubmit='return validate()'>
Line Chart Title: <input type='text' name='title' id='whattable' size='35' maxlength='40' value=''><BR>
Line Chart Data (comma-separated integers only): <input type='text' name='data' id='whatdata' size='35' maxlength='200' value=''><BR>
Line Chart Labels (comma-separated letters, numbers, spaces, underscores only): <input type='text' name='labels' id='whatlabels' size='35' maxlength='200' value=''><BR>
<input type='submit' class='l' value='Get line chart' name='flag'></form></div>
<div id='b' class='reload'><a HREF="getuserdatatolinechart.php">Do another?</a></div>
<?php
$flag=$_POST['flag'];
if (isset($flag)){
$t = $_POST['title'];
if(preg_match('/[^a-zA-Z0-9\\s\\_\\,]/', $t)){echo '<script language="javascript">alert("Enter title using only letters and numbers and spaces and underscores.");window.location="getuserdatatolinechart.php";</script>;';}else
{$d = $_POST['data'];}
if(preg_match('/[^0-9\\,]/', $d)){echo '<script language="javascript">alert("Enter data using only comma-separated integer numbers.");window.location="getuserdatatolinechart.php";</script>;';}else
{$data = explode(',',$d);$biggest=max($data);}
$l = $_POST['labels'];
if(preg_match('/[^a-zA-Z0-9\\s\\_\\,]/', $l)){echo '<script language="javascript">alert("Enter labels using only comma-separated letters and numbers and spaces and underscores.");window.location="getuserdatatolinechart.php";</script>;';}else
{$labels = explode(',',$l);}
if(strlen($d) <3 || strlen($l) <3){
echo '<script language="javascript">alert("Enter both data and labels, using only integer numbers for data and letters and numbers and spaces and underscores for labels.");window.location="getuserdatatolinechart.php";</script>;';}}
?>
<script language="javascript">
var xa, xb, ya, yb, x, y; var addtopage = "";
var m = <?php echo json_encode($labels); ?>;
var a = <?php echo json_encode($data); ?>;
var b = <?php echo json_encode($biggest); ?>;
var t = <?php echo json_encode($t); ?>;
var f = <?php echo json_encode($flag); ?>;
function goodline(xa, xb, ya, yb) {
var lengthofline = Math.sqrt((xa-xb)*(xa-xb)+(ya-yb)*(ya-yb));
for(var i=0; i<lengthofline; i++){
x=Math.round(xa+(xb-xa)*i/lengthofline);
y=Math.round(ya+(yb-ya)*i/lengthofline);
addtopage += "<div style='position:absolute;left:"+x+"px;top:"+y+"px;background-color:#a4a4a4;width:4px;height:4px;font-size:1px'></div>";}
addtopage += "<div style='position:absolute;left:"+xx[q]+"px;top:117px;'><IMG SRC='vert.gif' WIDTH=2 HEIGHT=380 BORDER=0></div>";
document.body.innerHTML += addtopage;}
var r=b/350;var xx = new Array();var yy = new Array();
var ll=(b.toString()).length;
var factor=Math.pow(10,-1*(ll-1));
var num=(Math.floor(b*factor)/factor);
var fac=Math.pow(10,ll-1);
var firstint=num/fac;
var dd=num/b;
var o=Math.round((350*dd)/firstint); //497y start then go up (y less) for each tic
for (var i=firstint;i>0;i--){
var divTag = document.createElement("div");
divTag.id="a" + i;
divTag.style.marginLeft = 10+"px";
divTag.style.position = "absolute";
divTag.style.top = (497-o*(firstint-(firstint-i)))+"px";
divTag.style.height = 2+"px";
divTag.innerHTML = "<IMG SRC='hor.gif' WIDTH='"+(a.length*80)+"' HEIGHT='2' BORDER='0'>";
document.body.appendChild(divTag);
}
if (a.length > 0 && m.length > 0) {
for (var i=0;i<a.length;i++){q=i;
xx[i]=i*78+59;
yy[i]=497-(a[i]/r);
if(i>0){goodline(xx[i-1], xx[i], yy[i-1], yy[i]);}else{addtopage += "<div style='position:absolute;left:"+xx[q]+"px;top:117px;'><IMG SRC='vert.gif' WIDTH=2 HEIGHT=380 BORDER=0></div>";}
}
for (var i=0;i<a.length;i++){
var divTag = document.createElement("div");
divTag.id="b" + i;
divTag.setAttribute("align", "center");
divTag.style.marginLeft = (i*78+20)+"px";
divTag.style.position = "absolute";
divTag.style.top = 500+"px";
divTag.style.height = 20+"px";
divTag.className = "bDiv";
divTag.innerHTML = m[i];
document.body.appendChild(divTag);
}
for (var i=0;i<a.length;i++){
var divTag = document.createElement("div");
divTag.id="c" + i;
divTag.setAttribute("align", "center");
divTag.style.marginLeft = (i*78+20)+"px";
divTag.style.position = "absolute";
divTag.style.top = (470-(a[i]/r))+"px";
divTag.style.height = 20+"px";
divTag.className = "bDiv";
divTag.innerHTML = a[i];
document.body.appendChild(divTag);
}
document.write("<div id='label'><h1>"+t+"</h1></div><div id='myid' style='min-width:"+(a.length*80)+"px; width:"+(a.length*80)+"px'> </div>");
}
bye();
</script>
</body>
</html>