Difference between revisions of "Making a Pie Chart"

From Scribus Wiki
Jump to navigation Jump to search
m
m
Line 1: Line 1:
 
[[Category:HOWTO]]
 
[[Category:HOWTO]]
 +
[[Category:Scripts & Plugins]]
  
 
The difficulty with making a pie chart with Scribus is that, if there is a way to use the various drawing tools to do it, the programming is quite beyond anything one would want to tackle. What we need then, is a more elegant way. Fortunately, with SVGs, there is a way.
 
The difficulty with making a pie chart with Scribus is that, if there is a way to use the various drawing tools to do it, the programming is quite beyond anything one would want to tackle. What we need then, is a more elegant way. Fortunately, with SVGs, there is a way.

Revision as of 07:15, 1 June 2006


The difficulty with making a pie chart with Scribus is that, if there is a way to use the various drawing tools to do it, the programming is quite beyond anything one would want to tackle. What we need then, is a more elegant way. Fortunately, with SVGs, there is a way.

What you cannot do is draw a circle, then subdivide it, because what we really want is a series of segments like variably sized pieces of pie, each of which can have its own fill color. What I want to show in this page is the process of putting information together to make the python script you will find at the end.

SVG Specifications and Drawing Arcs

For the details and much more information about SVG specifications, check this link.

Abcpie.png Let's start with this idealized pie chart segment. It turns out that the way we are going to use SVG specs to tell a program how to draw this shape is to start with point 1, draw a line to point 2, then draw the arc from 2 to 3, then go back to 1.

Here is the path command that will accomplish this:

<path d="M 200,200 l 150,0 a150,150 0 0,0 -37,-97 z" 
fill="red" stroke="black" stroke-width="2" stroke-linejoin="round" />

The first line shows our directions. M is the command for our starting point, the center of our pie circle, and relates to a point relative to the screen origin of our SVG space -- the upper left hand corner. After this all subsequent commands will be relative ones (in this example). Next the l says to draw a line from the starting point 150 X-units, but Y stays the same. (For this first segment, we could have used the h command, which will draw a horizontal line, but we need to generalize our process, and most or all of the other segments will not have a horizontal line.)

Now we draw our arc, using the a command, which is followed by X and Y radii, each of which we have set to 150 -- if they were not equal, we would be drawing an elliptical arc. Next are three zeros, but only the middle might need to be changed for our pie chart. Since we are only specifying two points along a hypothetical circle, we need to specify whether we will take the short route or the long route around the circle. Our middle zero says to take the short route (our piece of pie is less than half the pie).

Now, the tricky part. The last two numbers (-37, -97) are the relative distances from point 2 to point 3, in Cartesian X,Y computer-display coordinates. Finally, the z at the end says to complete this shape to a closed form -- go back to point 1.

How do we figure out these relative measurements along our arc? Trigonometry.

Trigonometry Awakens

Let's think about what pie charts represent. Pie charts merely show us visually how some whole list of data values is subdivided into its components, with the size of each piece corresponding to the size of its share of the pie. So to know how big this piece should display, I need to divide it by the total size of the pie. Since the circle of our pie spans 360°, our formula looks like this:

 [(size of piece)/total] × 360 
123pie.png We're trying to figure out the X,Y coordinates of point 3 relative to the center -- remember those relative commands we used above in the SVG file? So we imagine a right triangle, with 1-3 being its hypoteneuse. So now, the coordinates of point 3 are x = base, y = height of this imaginary triangle. The distance 1-3 is our radius, and so if we also know the angle of our pie piece:
  x = cos(angle) × radius
and
  y = sin(angle) × radius
But what about the next segment? We relied on the starting line being along the X-axis for our calculations. For the subsequent segments, let's stay with that reference point. We don't necessarily need to know the angle occupied by each segment, what we need are the corner points for our SVG notation.

So let's just add the original data value for the first pie piece to the value of the second, and divide that by the total, for the fraction of our pie circle.

[(size of piece A + size of piece B)/total] × 360 
1234pie.png

Now if we use our cosine and sine formulas again with this obtuse angle, we will get the correct X,Y values for point 4.

Eventually, we work our way around the pie, so that we have the coordinates for all these points, and once we have that, we make the relative calculations you see in piechart.py.

piechart.py

Bear with me -- I will put more annotations to this script later.

#!/usr/bin/env python
# File piechart.py
# Created 2006-05-29
# Gregory Pittman
# Automatically creates a piechart SVG file
# from a list of data
##########################################
from __future__ import division

import math
import scribus
# We'll create a list L, append to it, then copy the list to a file at the end
L = ['<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n','<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"\n','"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n','<svg width="20cm" height="20cm" xmlns="http://www.w3.org/2000/svg" version="1.1">\n']
svgfile = scribus.valueDialog('SVG File','Enter name of file to save to\n".svg" will be appended')
svgfile = svgfile + '.svg'
sectors = []
while 1:
    newvalue = scribus.valueDialog('Data Entry','Enter Data, 0 to End')
    if (newvalue == ''):
        newvalue = '0'
    newnum = float(newvalue)
    if newnum == 0:break
    sectors.append(newnum)
total = 0
i = 0
seg = 0
radius = 150
startx = 200
starty = 200
lastx = radius
lasty = 0
colors = ['red','blue','yellow','magenta','orange','slateblue','slategrey','greenyellow','wheat']
bordercolor = 'black'
for n in sectors:
    total = total + n

for n in sectors:
    arc = "0"
    seg = n/total * 360 + seg
    if ((n/total * 360) > 180):
        arc = "1"
    radseg = math.radians(seg)
    nextx = int(math.cos(radseg) * radius)
    nexty = int(math.sin(radseg) * radius)

    L.append('<path d="M '+str(startx)+','+str(starty) + ' l '+str(lastx)+','+str(-(lasty))+' a150,150 0 ' + arc + ',0 '+str(nextx - lastx)+','+str(-(nexty - lasty))+ ' z" \n') 
    L.append('fill="'+colors[i]+'" stroke="' + bordercolor + '" stroke-width="2" stroke-linejoin="round" />\n')
    lastx = nextx
    lasty = nexty
    i += 1
L.append('</svg>')
output = open(svgfile,'w')
output.writelines(L)
output.close()
endmessage = svgfile + ' was created'
scribus.messageBox("Finished",endmessage,icon=0,button1=1)
scribus.importSVG(svgfile)

Incidentally, the drawings in this Wiki page were made using piechart.py, and afterprocessing with Scribus, then a screen grab.