Sunday, July 20, 2008

Cool javascript pie chart generator with SVG

You might found this very interesting and useful to be able to generate a SVG piechart. I have done all the heavy calculation to make this pieces of code, so you just need to provide only few value, then will be able get the piechart display on the screen. For the other kinds of statistical graph I will post it once I have free time to complete it. Now let enjoy my SVG Piechat in javascript.
var drawPieChart = function(/*Value of slices*/val, /*Optional object*/ o){

/**
* Store a polyline object
* (object)
*/
var pieChart;

/**
* Store a string of points required by polyline object
* (string)
*/
var pointStr;

/**
* Contains the namespace for SVG-related utilities
* (Object)
*/
var SVG = {
ns: "http://www.w3.org/2000/svg",
xlinks: "http://www.w3.org/1999/xlink"
};

/**
* Create default object if not being passed
* (object)
*/
var o = o ? o : {};

/**
* Create a SVG dom object
* (object)
*/
var svg = document.createElementNS(SVG.ns, "svg:svg");

//Set default value if (options {o}) not being passed
var xOrigin = o.xOrigin ? o.xOrigin : 0;
var yOrigin = o.yOrigin ? o.yOrigin : 0;
var xCoordinateSize = o.xCoordinateSize;
var yCoordinateSize = o.yCoordinateSize;
var strokeColor = o.strokeColor ? o.strokeColor : "black";
var strokeOpacity = o.strokeOpacity ? o.fillOpacity : 0.7;
var strokeSize = o.strokeSize ? o.strokeSize : 2;
var strokeLinecap = o.strokeLinecap ? o.strokeLinecap : "round";
var strokeJoincap = o.strokeJoincap ? o.strokeJoincap : "round";
var radius = o.radius ? o.radius : 150;

// Set the size the canvas in pixels
svg.setAttribute( "width", xCoordinateSize + "px" );
svg.setAttribute( "height", yCoordinateSize + "px" );
svg.style.position = "absolute";

// Set the coordinates used by drawings in the canvas
svg.setAttribute( "viewBox", xOrigin + " " + yOrigin + " " + xCoordinateSize + " " + yCoordinateSize );

// Define the XLink namespace that SVG uses
svg.setAttributeNS( "http://www.w3.org/2000/xmlns/", "xmlns:xlink", SVG.xlinks );

var onePixel = xCoordinateSize / yCoordinateSize;

//The total value of slices
var total = 0;

//Figure out the value in degree of each slice value
for(var i = 0; i < val.length; i++){
total += val[i].value;
}

for(var i = 0; i < val.length; i++){
val[i].percentage = ( val[i].value * 100 ) / total;
val[i].value = ( val[i].value * 360 ) / total;

}

/** Create Shadow **/
var pieShadow = document.createElementNS( SVG.ns, "circle" );
pieShadow.setAttribute("cx","655");
pieShadow.setAttribute("cy","205");
pieShadow.setAttribute("r",radius);
pieShadow.setAttribute("fill","black");
pieShadow.setAttribute("fill-opacity","0.4");
pieShadow.setAttribute("filter","url(#dropshadow)");
svg.appendChild(pieShadow);

/** Creating pie chart **/

//Creating the first slice
var pieChart = document.createElementNS( SVG.ns, "path" );
var startX, startY, endX, endY;

//The point the the pieChart will draw from the center point
startX = Math.cos(0) * radius;
startY = Math.sin(0) * radius;

//The end point the pieChart will end its drawing before close
endX = startX - Math.cos( (val[0].value) * 0.017453293) * radius;
endY = (Math.sin( (val[0].value) * 0.017453293) * radius) - startY;

//Build a string of value to be used by path
var pieValue;
pieValue = (val[0].value > 180)? "M650,200" + "l"+ startX + ","+ -startY + "a"+radius+","+radius+" 0 1,0 " + -endX + "," + -endY + "z" : "M650,200" + "l"+ startX + ","+ -startY + "a"+radius+","+radius+" 0 0,0 " + -endX + "," + -endY + "z";

//Draw piechart and set its attributes
pieChart.setAttribute( "d", pieValue);
pieChart.setAttribute( "stroke-width", strokeSize * onePixel);
pieChart.setAttribute( "stroke", strokeColor );
pieChart.setAttribute( "fill", val[0].color );
pieChart.setAttribute( "stroke-linecap", strokeLinecap );
pieChart.setAttribute( "stroke-linejoin", strokeJoincap );

//Append polyline dom object to view box
svg.appendChild(pieChart);

//The point the the pieText will be placed
var textX = Math.cos( (val[0].value/2 ) * 0.017453293 ) * radius;
var textY = Math.sin( (val[0].value/2 ) * 0.017453293 ) * radius;

//Create svg text dom
var pieText = document.createElementNS( SVG.ns, "text" );
pieText.setAttribute("x",650 + textX/2 );
pieText.setAttribute("y",200 - textY/2 );
pieText.setAttribute("font-family","verdana, arial, sans-serif");
pieText.setAttribute("font-size","14");
pieText.appendChild(document.createTextNode(val[0].percentage.toFixed(2) + "%"));
svg.appendChild(pieText);

//Accumlate the agles
var angles = val[0].value;

/** Creating the rest of piechat slice **/
for(var i = 1; i < val.length; i++){

//Create pieChart dom
var pieChart = document.createElementNS( SVG.ns, "path" );

//The point the the pieChart will draw from the center point
startX = Math.cos( angles * 0.017453293 ) * radius;
startY = Math.sin( angles * 0.017453293 ) * radius;

//The point the the pieText will be placed
textX = Math.cos( (val[i].value/2 + angles) * 0.017453293 ) * radius;
textY = Math.sin( (val[i].value/2 + angles) * 0.017453293 ) * radius;

//Accumulate the angles
angles += val[i].value;

//The end point the pieChart will end its drawing before close
endX = startX - Math.cos( angles * 0.017453293) * radius;
endY = (Math.sin( angles * 0.017453293) * radius) - startY;
pieValue = (val[i].value > 180)? "M650,200" + "l"+ startX + ","+ -startY + "a"+radius+","+radius+" 0 1,0 " + -endX + "," + -endY + "z" : "M650,200" + "l"+ startX + ","+ -startY + "a"+radius+","+radius+" 0 0,0 " + -endX + "," + -endY + "z";

//Draw piechart and set its attributes
pieChart.setAttribute( "d", pieValue);
pieChart.setAttribute( "stroke-width", strokeSize * onePixel);
pieChart.setAttribute( "stroke", strokeColor );
pieChart.setAttribute( "fill", val[i].color );
pieChart.setAttribute( "stroke-linecap", strokeLinecap );
pieChart.setAttribute( "stroke-linejoin", strokeJoincap );

//Append pieChart dom object to view box
svg.appendChild(pieChart);

/** Create piechart text **/

var pieText = document.createElementNS( SVG.ns, "text" );
pieText.setAttribute("x",650 + textX/2 );
pieText.setAttribute("y",200 - textY/2 );
pieText.setAttribute("font-family","verdana, arial, sans-serif");
pieText.setAttribute("font-size","14");
pieText.appendChild(document.createTextNode(val[i].percentage.toFixed(2) + "%"));

//Append pieText dom object to view box
svg.appendChild(pieText);
}

return svg;
}

function init( ) {

var sliceValue = [
{value: 3000,color:"red" },
{value: 1000,color:"pink" },
{value: 500,color:"blue" },
{value: 500,color:"white" },
{value: 500,color:"yellow" },
{value: 500,color:"black" },
{value: 500,color:"silver" }

] };

Monday, July 14, 2008

The memoization

The memoization is a very efficiency way when you want some heavy computation to be done only once at first time. Let check the ajax creating an XhrObject example createXhrObject: function() { // Factory method.
var methods = [
function() { return new XMLHttpRequest(); },
function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
function() { return new ActiveXObject('Microsoft.XMLHTTP'); }
];
for(var i = 0, len = methods.length; i <>
try {
methods[i]();
}
catch(e) {
continue;
}
// If we reach this point, method[i] worked.
this.createXhrObject = methods[i]; // Memoize the method.
return methods[i];
}
The object creation will be heavily checking the computability of each browser. But it will done only at the first time, the function createXhrObject will replace itself with the creation method once found the browser compatibility.