Chapter 15: SVG and Pie Charts

   



Drawing Circular Pie Charts

Consider the circular pie chart rendered in Figure 15.1.

click to expand
Figure 15.1: A circular pie chart.

The SVG document in Listing 15.1 demonstrates how to draw a circular pie chart.

Listing 15.1 circularPieChart1.svg

start example
<?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"  "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <svg width="100%" height="100%" onload="init(evt)"      xmlns="http://www.w3.org/2000/svg">  <script type="text/ecmascript">    <![CDATA[     var width          = 800.;     var height         = 500.;     var basePointX     = 300.;     var basePointY     = 150.;     var currentX       = 0.0;     var currentY       = 0.0;     var offsetX1       = 0.0;     var offsetY1       = 0.0;     var offsetX2       = 0.0;     var offsetY2       = 0.0;     var radius         = 100.;     var angleSum1      = 0.;     var angleSum2      = 0.;     var vertexCount    = 8.;     var xPts           = Array(vertexCount);     var yPts           = Array(vertexCount);     var angles         = Array(vertexCount);     var pointPath      = "";     var circleColors   = ['red','green',                           'blue','yellow'];     var colorCount     = 4.;     var pieNode        = null;     var svgDocument    = null;     var target         = null;     var gcNode         = null;     function init(event)     {        target = event.getTarget();        svgDocument = target.getOwnerDocument();        gcNode = svgDocument.getElementById("gc");        initializeChart();        drawChart();     }     function initializeChart()     {        angles[0] = 30.;        angles[1] = 60.;        angles[2] = 45.;        angles[3] = 45.;        angles[4] = 20.;        angles[5] = 30.;        angles[6] = 50.;        angles[7] = 80.;     } // initializeChart     function drawChart()     {        for(var v=0; v<vertexCount; v++)        {           angleSum2 = angleSum1 + angles[v];           offsetX1 = radius*Math.cos(                               angleSum1*Math.PI/180);           offsetY1 = radius*Math.sin(                               angleSum1*Math.PI/180);           offsetX2 = radius*Math.cos(                               angleSum2*Math.PI/180);           offsetY2 = radius*Math.sin(                               angleSum2*Math.PI/180);           currentX = basePointX+offsetX2;           currentY = basePointY-offsetY2;           // the vertical offset must be subtracted,           // so we need to "flip" the sign of offsetY1           offsetY1 *= -1;           pointPath = "M"+basePointX+","+basePointY;           pointPath += " l"+offsetX1+","+offsetY1;           pointPath += " A"+radius+","+radius+" 0 0 0 ";           pointPath += currentX+","+currentY;           pointPath += " L"+basePointX+","+                             basePointY+"z";           fillColor  = "fill:"+                         circleColors[v%colorCount];           pieNode = svgDocument.createElement("path");           pieNode.setAttribute("d",    pointPath);           pieNode.setAttribute("style",fillColor);           gcNode.appendChild(pieNode);           angleSum1 += angles[v];        }     } // drawPattern  ]]> </script>  <!-- ============================ -->  <g  transform="translate(10,10)">    <rect x="0" y="0"          width="800" height="500"          fill="none" stroke="none"/>  </g> </svg>
end example

Remarks

The SVG code in Listing 15.1 starts with a block of global ECMAScript variables, followed by the ECMAScript function init() that initializes the variables svgDocument, target, and gcNode. The init() function invokes the ECMAScript function initializeChart(), which assigns a value (measured in degrees) to each sector of the pie chart as listed below:

function initializeChart() {    angles[0] = 30.;    angles[1] = 60.;    angles[2] = 45.;    angles[3] = 45.;    angles[4] = 20.;    angles[5] = 30.;    angles[6] = 50.;    angles[7] = 80.; } // initializeChart

The last line of the init() function invokes drawChart(), which performs all the necessary calculations for determining the coordinates of the circular pie sectors.

The first portion of drawChart() updates the current cumulative angle and then computes the x-coordinates and the y-coordinates of the two 'end points' (of the current circular arc) that lie on the circumference of the outer circle:

angleSum2 = angleSum1 + angles[v]; offsetX1 = radius*Math.cos(angleSum1*Math.PI/180); offsetY1 = radius*Math.sin(angleSum1*Math.PI/180); offsetX2 = radius*Math.cos(angleSum2*Math.PI/180); offsetY2 = radius*Math.sin(angleSum2*Math.PI/180);

The argument to the functions Math.cos() and Math.sin() is interpreted as a radian value; therefore, you must multiply the variables angleSum1 and angleSum2 (which represent degree values) by the constant Math.PI/180.

Next, drawChart() constructs the string pointPath that will be used in an SVG path element:

currentX = basePointX+offsetX2; currentY = basePointY-offsetY2; // the vertical offset must be subtracted, // so we need to "flip" the sign of offsetY1
  • offsetY1 *= -1;

     pointPath = "M"+basePointX+","+basePointY; pointPath += " l"+offsetX1+","+offsetY1; pointPath += " A"+radius+","+radius+" 0 0 0 "; pointPath += currentX+","+currentY; pointPath += " L"+basePointX+","+                   basePointY+"z";

The following line of code emphasizes the fact that the vertical offset must be subtracted or its sign must be inverted:

offsetY1 *= -1;

An alternative would be to embed the negative sign as follows:

pointPath += " l"+offsetX1+","+(-offsetY1); 

Both techniques have advantage and disadvantages; select one that suits your needs and then use it consistently in your code.

The last portion of drawChart() dynamically creates an SVG path element and assigns values to the attributes path and style:

fillColor = "fill:"+circleColors[v%colorCount]; pieNode = svgDocument.createElement("path"); pieNode.setAttribute("d",    pointPath); pieNode.setAttribute("style",fillColor); gcNode.appendChild(pieNode); angleSum1 += angles[v];

Notice how the current 'color index' is determined in the following line of code:

fillColor = "fill:"+circleColors[v%colorCount];

The term v%colorCount ensures that the index into the color array circleColors will not exceed the number of elements in the array. You will see this technique (as well as some other variants) used in many examples in this book.

SVG makes it very easy to create a 'separation' effect between adjacent pie slices; simply add the following code to drawChart():

var style       = ""; var strokeWidth = 4.; var strokeColor = 'white'; stroke   = "stroke:"+strokeColor+";"; stroke  += "stroke-width:"+strokeWidth+";"; fillColor = "fill:"+circleColors[v%colorCount]; style = stroke+fillColor; pieNode.setAttribute("style",style);

If you want to create a 'dotted' effect, you can add this line of code:

style += "stroke-dasharray:2 2 2 2";

Instead of hard-coding the value of the attribute stroke-dasharray, you can use a more flexible approach (which is used extensively in this book):

var dashStyle = "2 2 2 2"; .... style += "stroke-dasharray:"+dashStyle;

If you use the preceding technique, make sure that you include semi-colons (';') in the proper locations, or you will see the following type of message in the status bar of your browser:

bad CSS property or descriptor declaration

If you are dynamically concatenating multiple strings together in order to define the value of the style attribute, you can display the value of this attribute in an ECMAScript alert() function as follows:

if( count == 0 ) {    alert("style "+style); }

The if statement is useful when you need to display the value of the style attribute inside a for loop that has a large number of iterations.

Another variation of the code involves creating a shadow-like three-dimensional effect; this change requires adding the following code to the function drawChart():

function drawChart() {    circleNode = svgDocument.createElement("circle");    circleNode.setAttribute("cx",   basePointX+10);    circleNode.setAttribute("cy",   basePointY+10);    circleNode.setAttribute("r",    radius);    circleNode.setAttribute("style","fill:black;");    gcNode.appendChild(circleNode);    // the remaining code is the same.... }

The complete code for the preceding modifications is available on the CD-ROM in the SVG files and .



   



Fundamentals of SVG Programming. Concepts to Source Code
Fundamentals of SVG Programming: Concepts to Source Code (Graphics Series)
ISBN: 1584502983
EAN: 2147483647
Year: 2003
Pages: 362

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net