Thursday, June 16, 2016

Making pie charts

This isn't related to "open source software and usability" but I thought this was neat so I wanted to share it here.

On my Coaching Buttons blog, I'm planning a new article about how we divide our time between Lead, Manage and Do tasks. For example, "Lead" is about setting a direction, getting people on board with a vision, and generally providing leadership. You can lead at all levels in an organization. "Manage" is the day-to-day of keeping things running, coordinating people and processes, and allocating resources and budgets. And "Do" are the hands-on tasks like coding, analyzing data, or responding to issues. Email and writing reports can also be "Do" tasks.

The goal of the "Lead-Manage-Do" project is to focus your time appropriately. Think of your available time as a "pie," and how you divide your time as "slices" of the pie. That's your time for the week. You can't make the pie any bigger, unless you want to work through the weekend. How do you spend this available time? I'm looking to build on my Lead-Manage-Do since I've moved to my new role.

My new post will require pie charts to illustrate how I spend my time. I could generate a few quick pie charts in a spreadsheet and post them to my blog. That's what I did in my previous blog posts on this topic. But lately, I have tried to use more SVG graphics on my personal websites, due to the reduced size and greater flexibility. So I wondered if I could create a pie chart using SVG.

When I searched for examples in creating pie charts in SVG, many sources pointed me to Designing Flexible, Maintainable Pie Charts With CSS And SVG by Lea Verou. It's an interesting article. But I found the article's method limiting, using one slice in a larger pie. My "Lead-Manage-Do" charts need a balance of three pie slices. There's a section at the end using a conical gradient method that might do the job, but I knew there had to be a cleaner way to do it.

And there is: SVG arcs.

Mozilla Developer Network has a great article on SVG Paths. Skip to the end where they talk about arcs. The general syntax for an arc is:
A rx ry x_axis_rotation large_arc_flag sweep_flag x y
Arcs are tricky because the arc can take different paths (clockwise or counterclockwise) and they can go "in" or "out." And if your arc is based on an oval, you can rotate the oval. Hence the additional arguments. The MDN article explains the large arc flag and the sweep flag:
The first argument is the large-arc-flag. It simply determines if the arc should be greater than or less than 180 degrees; in the end, this flag determines which direction the arc will travel around a given circle. The second argument is the sweep-flag. It determines if the arc should begin moving at negative angles or positive ones, which essentially picks which of the two circles you will travel around.
You can use the SVG arc to create a neat pie wedge.

If you haven't used SVG before, think of SVG as a form of Turtle graphics (I suppose this analogy only helps if you have used Turtle graphics). You define a shape by moving to (M) a starting x,y position, then you can draw lines (L) or arcs (A) or other elements as needed. The "Close Path" command (Z) automatically closes the shape by connecting a "line" from the ending position back to the starting position.

And with that, we can define a pie wedge! Let's define a box 400×400. A circle in this box would be centered at 200,200. Since it's a circle, I don't need an x axis rotation, so I'll set that to zero. By default, the SVG will have 0,0 in the upper-left corner.

To draw a pie wedge, move (M) to a starting point on the circle's edge, then draw an arc defined by that circle. To close the pie wedge, create a line back to the circle's center at 200,200. Since I'm using absolute coordinates, use uppercase M, A, and L. Here's the code:
<path d="M 400 200 A 200 200 0 0 0 200 0 L 200 200 Z" style="fill: green;">
</path>
A simple example:

That defines a green wedge from 400,200 to 200,0 (0 degrees to 90 degrees) and a line back to the center of the circle at 200,200. The Z command automatically closes the shape by projecting a line from the end (200,200) to the start (400,200). The above drawing also provides a pink circle with red center dot and a red outline so you can see the circle that defines the arc. The arc has x radius 200 and y radius 200.

To help me create multiple pie wedges that can start and stop at any angle, I needed a little extra help. To run the math, I created a simple spreadsheet where I define the starting θ and ending θ, both in degrees, and the spreadsheet converts that to radians and calculates appropriate x,y values based on the size of the pie chart I want to make. For a circle this size, two decimal places is more than enough precision. (For a larger SVG image, you might use only integer coordinates. For a smaller SVG image, you might need another decimal place.)

You can easily work the math out on your own. An x,y coordinate on the edge of a circle is defined by rcosθ and rsinθ. With that, it's trivial to work out the start and stop coordinates for the arc. Just remember 0,0 is at the top left and positive values are right and down. Here's some pseudo-code for the x,y of any point on a circle of radius r, given an angle θ:
x = xcenter + r cosθ
y = ycenter - r sinθ

Also remember that the large arc flag "determines if the arc should be greater than or less than 180 degrees" (from the MDN article). So the limitation here is that my wedges need to be less than 180 degrees if I use a zero large arc flag.

And you can take a shortcut. If you first define a circle behind the wedges, you really only need to create n–1 wedges. This is especially helpful if you know one of the wedges will be larger than 180 degrees.

Putting it all together, you have a 2D pie chart in SVG:

The SVG code first draws a background circle (orange), then defines a red wedge from 30 to 110 degrees, and a blue wedge from 110 to 280 degrees. Visually, this leaves an orange wedge from 280 to 30 degrees.
<svg height="400" width="400">
<circle cx="200" cy="200" fill="orange" r="200"></circle>
<path d="M 373.21 100.02 A 200 200 0 0 0 131.66 12.04 L 200 200 Z" style="fill: firebrick;"></path>
<path d="M 131.66 12.04 A 200 200 0 0 0 234.55 396.99 L 200 200 Z" style="fill: royalblue;"></path>
</svg>
You can also apply other styles to create outlines, etc. on the pie chart. But I'll leave that to you.

No comments:

Post a Comment