Post

Further Adventures in SVGs

Creating a Trend SVG Visual in Power BI

In a previous post I created a SVG Barbell visual. While this was a good start I wanted to expand upon this concept by adding an extra dimension of time to see trends. The results are below.

Trend SVG =
VAR _SvgWidth =                 100
VAR _SvgHeight =                20

// values
VAR _ActualValue =              [Max Value]
VAR _ActualColour=              [Colour Hex]
VAR _ActualValueFormatted =     IF( MAX( Metrics[format] ) = "Percent", FORMAT( _ActualValue, "0.0%"), FORMAT( _ActualValue, "0.0") )
VAR _RedValue =                 MAX( Metrics[red#] )
VAR _GreenValue =               MAX( Metrics[green#] )

// y axis
VAR _Actual =
    ADDCOLUMNS(
        CALCULATETABLE( VALUES( Periods[Reporting Period] ), ALLSELECTED( Periods ) )
        ,"@Actual", CALCULATE( [Max Value] )
    )

VAR _MinActual =                MINX( _Actual, [@Actual] )
VAR _MaxActual =                MAXX( _Actual, [@Actual] )
VAR _SmallValue =               MIN( _RedValue, _GreenValue)
VAR _LargeValue =               MAX( _RedValue, _GreenValue) 
VAR _SmallestValue =            MIN( _SmallValue, _MinActual )
VAR _LargestValue =             MAX( _LargeValue, _MaxActual )

VAR _YOffset =                  5
VAR _YInputStart =              _SmallestValue                     // The lowest number of the range input
VAR _YInputEnd =                _LargestValue                      // The largest number of the range input
VAR _YOutputStart =             _SvgHeight -  _YOffset             // The lowest number of the range output
VAR _YOutputEnd =               0 + _YOffset                        // The largest number of the range output

// x axis
VAR _DateMin =                  CALCULATE( MINX( SUMMARIZE( 'Fact', Periods[End Date] ), Periods[End Date] ), ALLSELECTED( Periods ), ALLSELECTED( Metrics ) )
VAR _DateMax =                  CALCULATE( MAXX( SUMMARIZE( 'Fact', Periods[End Date] ), Periods[End Date] ), ALLSELECTED( Periods ), ALLSELECTED( Metrics ) )
VAR _MaxDateWithActual =        CALCULATE( MAXX( SUMMARIZE( 'Fact', Periods[End Date] ), Periods[End Date] ) )

VAR _XOffset =                   5
VAR _XInputStart =               _DateMin                           // The lowest number of the range input
VAR _XInputEnd =                 _DateMax                           // The largest number of the range input
VAR _XOutputStart =              0 + _XOffset                       // The lowest number of the range output
VAR _XOutputEnd =                _SvgWidth - _XOffset - 20          // The largest number of the range output

// Colours
VAR _Opacity =                  "73" // 45%
VAR _RedHex =                   "#A9000A"
VAR _AmberHex =                 "#E49F16"
VAR _GreenHex =                 "#00847E"
VAR _GreyHex =                  "#A3A3A3"
VAR _BlackHex =                 "#000000"
VAR _SmallHex =                 IF( _GreenValue = _SmallValue, _GreenHex, _RedHex )
VAR _LargeHex =                 IF( _GreenValue = _LargeValue, _GreenHex, _RedHex )
VAR _CallOutHex =               _BlackHex

// Vectors
VAR _TopPosition =             _YOutputStart + ((_YOutputEnd - _YOutputStart) / (_YInputEnd - _YInputStart)) * (_LargeValue  - _YInputStart)
VAR _BottomPosition =          _YOutputStart + ((_YOutputEnd - _YOutputStart) / (_YInputEnd - _YInputStart)) * (_SmallValue  - _YInputStart)
VAR _TopLine =                  "<line x1=""" & _XOutputStart & """" & UNICHAR(10) & "y1=""" & _TopPosition & """ x2=""" & _XOutputEnd & """ y2=""" & _TopPosition & """ stroke=""" & _LargeHex & _Opacity & """/>"
VAR _BottomLine =               "<line x1=""" & _XOutputStart & """" & UNICHAR(10) & "y1=""" & _BottomPosition & """ x2=""" & _XOutputEnd & """ y2=""" & _BottomPosition & """ stroke=""" & _SmallHex & _Opacity & """/>"

// Circles
var _SmallCircleSize =          1.
var _LargeCircleSize =          3
var _Circles =
                                ADDCOLUMNS(
                                    CALCULATETABLE( SUMMARIZE( 'Fact', Periods[End Date] ) ,REMOVEFILTERS( Periods ), DATESBETWEEN( Dates[Date], _DateMin, _MaxDateWithActual ) )
                                    ,"@circles"
                                        ,var xVal = Periods[End Date]
                                        var yVal =  CALCULATE( [Max Value], REMOVEFILTERS( Periods ), TREATAS( { xVal }, Periods[End Date] ))
                                        var hex =   CALCULATE( [Colour Hex], REMOVEFILTERS( Periods ), TREATAS( { xVal }, Periods[End Date] ))
                                        var x =     _XOutputStart + ((_XOutputEnd - _XOutputStart) / (_XInputEnd - _XInputStart)) * (xVal - _XInputStart)
                                        var y =     _YOutputStart + ((_YOutputEnd - _YOutputStart) / (_YInputEnd - _YInputStart)) * (yVal  - _YInputStart)
                                        var size =  IF( Periods[End Date] = _MaxDateWithActual, _LargeCircleSize, _SmallCircleSize )
                                        return
                                        IF( not ISBLANK( xVal ), "<circle cx=""" & x &""" cy=""" & y & """ r=""" & size & """ fill=""" & IF( Periods[End Date] = _MaxDateWithActual, hex, hex & _Opacity ) & """ stroke= '" & IF( Periods[End Date] = _MaxDateWithActual, _BlackHex, _BlackHex & _Opacity )  & "'/>", "")
                                        & IF( Periods[End Date] = _MaxDateWithActual  , "<text x='" & _SvgWidth - 20 & "' y='"& y + _LargeCircleSize & "' fill='" & _CallOutHex  & "' font-size='8' font-family='Segoe UI, sans-serif' >"& _ActualValueFormatted &"</text>", "")
                                )               

VAR _Svg =
    "data:image/svg+xml;utf8, <svg width=""" & _SvgWidth & """ height=""" & _SvgHeight &""" xmlns=""http://www.w3.org/2000/svg"">" &
    _TopLine &
    _BottomLine &
    CONCATENATEX( _Circles, [@circles] ) &
    "</svg>"

RETURN
    IF( not ISBLANK( _ActualValue ) &&  not ISBLANK( MAX( Metrics[RAG] )), _Svg )

Resulting in the following:

SVG Dumbbell

I have seen some interesting examples of SVGs that utilize css and classes. SVG’s still have plenty of depth that I have yet to dive into.

This post is licensed under CC BY 4.0 by the author.