SVG in Power BI – Part 5 – Creating a dial
Series
This post is the fifth in my series of exploring using SVG within Power BI to create visuals. Here are links to the complete series.
- Introduction to SVG
- KPI Shapes in Power BI
- Filling up with colour using SVG in Power BI
- Using Text in SVG
- Using SVG Rotate to create a dial in Power BI
- SVG Icons in Conditional Formatting
- Using a Theme to add SVG Icons
- Feb 2023 Update – 5 SVG Stars
In this post I will introduce the possibilities that SVG rotate gives. This post will walk through creating a dial.
This post will be done in 2 stages, first a simple dial and then add colours.
Simple Dial
We will start with a simple dial, made up of an arch with a line to indicate a percentage.
The arch is made using a path which combines 2 arcs to make an arch. The arch is then nested in a group <g> element that applies a fill.
Simple Dial =
// svg essentials
var svg_start = "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 50'>"
var svg_end = "</svg>"
// arch shape
var svg_arch = "<path d='
M 0 50 A 50 50 0 0 1 100 50
L 80 50 A 30 30 0 0 0 20 50
' /> "
// light blue arch
var svg_blue_arch = "<g fill='#42cbf4'>" & svg_arch & "</g>"
return
svg_start & svg_blue_arch & svg_end
We now need to add a line that will move based on a measure called Score. So I start by calculating the rotation angle and drawing a line that matches the bottom left of the arch and then rotate it centered on the center point of the arch.
The rotation will be 0 to 180 degrees and the measure Score is a percentage so the calculation is [Score] * 180.
The line is drawn from 0,50 to 20,50 and then a transform is applied of a rotate which has 3 parameters, angle to rotate, x and y for the centre of the rotation. So the bottom part of my measure becomes
// light blue arch
var svg_blue_arch = "<g fill='#42cbf4'>" & svg_arch & "</g>"
// draw line and rotate
var svg_rotate = [Score] * 180
var svg_line = "<line x1='0' y1='50' x2='20' y2='50' stroke-width='1' stroke='black'
transform='rotate(" & svg_rotate & " 50 50)'/>"
return
svg_start & svg_blue_arch & svg_line & svg_end
Show the Value
To make it very obvious what value is shown I’m going to add the value of the Score measure. The centre of the arch is 50,50 so I’ll put the middle of the text at 50,40. I used the FORMAT function to apply a percentage, “0%”, format to the score. (See the previous post in this series to look at SVG text)
The bottom part of the measure now becomes
var svg_line = "<line x1='0' y1='50' x2='20' y2='50' stroke-width='1' stroke='black'
transform='rotate(" & svg_rotate & " 50 50)'/>"
// Show Score value
var svg_score = "<text x='50' y='45' text-anchor='middle'>"&FORMAT([Score],"0%")&"</text>"
return
svg_start & svg_blue_arch & svg_line & svg_score & svg_end
Adding Colours
For this I researched being able to draw parts of an arch. In order to do that I would need to calculate the end points of each arch, which was possible but involved way more mathematics than I was willing to re-learn. My days of using Cos, Sin and Tan have long gone.
So I the trick I used was to draw the arch upside down out of sight and rotate the arch around into view. Different coloured arches can rotated into view to show different colours.
To draw the arch up the other way I just changed 2 values in svg_arch variable. When drawing an arc in SVG the 5th number after the A specifies which way round the to reach the final point. So the svg_arch variable becomes.
// arch shape
var svg_arch = "<path d='
M 0 50 A 50 50 0 0 0 100 50
L 80 50 A 30 30 0 0 1 20 50
' /> "
Then we add a coloured arch rotated for each part of the gauge. In my example I am going to show Green upto 100%, Amber starts at 75% and Red at 50%. So the complete measure code now is
Coloured Dial =
// svg essentials
var svg_start = "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 50'>"
var svg_end = "</svg>"
// arch shape
var svg_arch = "<path d='
M 0 50 A 50 50 0 0 0 100 50
L 80 50 A 30 30 0 0 1 20 50
' /> "
// coloured arches
var svg_green_arch = "<g fill='#22FF22' transform='rotate(180 50 50)'>" & svg_arch & "</g>"
var svg_amber_arch = "<g fill='#FFBF00' transform='rotate(135 50 50)'>" & svg_arch & "</g>"
var svg_red_arch = "<g fill='#ff2222' transform='rotate(90 50 50)'>" & svg_arch & "</g>"
// draw line and rotate
var svg_rotate = [Score] * 180
var svg_line = "<line x1='0' y1='50' x2='20' y2='50' stroke-width='1' stroke='black'
transform='rotate(" & svg_rotate & " 50 50)'/>"
// Show Score value
var svg_score = "<text x='50' y='45' text-anchor='middle'>"&FORMAT([Score],"0%")&"</text>"
return
svg_start & svg_green_arch & svg_amber_arch & svg_red_arch & svg_line & svg_score & svg_end
Conclusion
This post ended up being longer than I expected so my the series will expand to make use of a table to store the colour values in another post and drawing a clock into another post.