Visio is a great tool for representing complex systems and data and the data graphics functionality in 2007 makes the communication of data even simpler than before. The charting stencil however, while providing an array of charting shapes, doesn’t make it easy to tie actual data to respective chart and so I thought I’d have a look, over the next few posts, at some custom chart shapes that deal more directly with the underlying data. First off is Edward Tufte’s Sparkline chart…
For all the Edward Tufte fans out there (and I count myself as one) I’ll point out straight away that this implementation has some flaws and chief amongst them is the limited dataset that the shape can handle. As Tufte points out, a Sparkline is a “data-intense” chart and my example holds just 12 data points. This is really a demonstration of what you can do and you’re of course welcome to expand this up to whatever the limits the Polyline function, used in the shape, can take. So that caveat aside, let’s get on with the shape itself and how to go about building it.
I won’t force you into the details of the ShapeSheet if you don’t want to – so if you just want the shape, you can download and stop reading here!
Shape functionality and structure
The shape allows you to enter an array of (up to 12) values and this creates the line graph. The minimum and maximum are 0 and 100 respectively (although this can be changed within the shape) and a range band can be displayed, with its upper and lower limits set via Shape Data. A red point highlights a specific index (again set via Shape Data) and this value is shown as red text at the right hand side of the shape. Various visibility cells are available through the shape’s context menu as shown below:
In terms of structure, the shape is a group shape that contains a single child shape, with the group shape holding the line chart and range band geometries plus overall shape logic. The child shape is responsible for displaying the highlight point and point value text. An exploded view of this can be see here:
The group shape contains two geometry sections, the first to hold the range band (which has a NoLine cell set to True) and the second to hold the Polyline-based line chart (which has a NoFill cell set to True). The result of these NoLine and NoFill cells means that the users can set the Fill colour to change the range band and Line colour for the line chart itself.
The size of the ‘point’ sub-shape (highlighted as a blue rectangle above) is matched to that of its parent, which makes moving the red point (ellipsis) around within the shape a little simpler while retaining a fixed position for the value text.
So let’s have a look at two of the main areas of how the shape works underneath:
Polyline - The key part is a dynamically built Polyline ShapeSheet function. This function takes a string of x,y coordinates to create the chart line itself and I’m using a series of scratch cells to build up those x,y pairs and concatenate them into a single string. A user cell then pushes the full function into the appropriate geometry cell every time there’s a change in the Shape Data cell “Data Series” (NB - I’ve divided this into a number of lines but it’s a single line in the ShapeSheet):
Scaling – The shape is designed to scale all of its constituent parts in a uniform manner and uses scaling ratios for line weight, font size and the size of the ellipsis. I’m not going to go into lots of details on this as Chris Roth of VisGuy.com has some great posts on the subject:
A final point on font scaling is that I assumed a standard font of 10pt but I still wanted the user to be able to change the font size while retaining future scaling. Here’s the original formula:
Char.Size = 10 pt*ThePage!PageScale/ThePage!DrawingScale*Height/8 mm
This would change the font’s size in relation to the shape’s height (which could also be width, but since the shape has a locked aspect ratio it doesn’t really matter). Now, if the user changes the font size via the UI our scaling formula would be blown away and replaced by the actual value. You could, of course, surround the formula in a GUARD() function, but then you’d lose the ability to allow the user to change the size.
The answer is to surround the original 10pt value in a SETATREFEXPR() function. This function effectively receives the incoming value and retains the rest of the original formula. You can see the modified formula here:
The SETATREFEXPR() function comes from a set of SETATREF related functions that deal with redirecting formula values. The Visio Insights blog has a couple of posts on the subject, (plus Graham Wideman’s books is still the best description of how they work):