Forum Discussion
Gantt Chart with SVG objects in a measure and matrix visual
Hello, dear PowerBi experts!
I saw a post in one of my social networks showing a nice collapsible Gantt Chart that can be done using SVG objects in a measure and the matrix visual (at least this was the claim from the owner of the post). I was surprised by the final result which includes the visualization of a baseline and current bars, milestones (diamond shape), a graphic visualization of the progress (blue color in the bar) and summary activity (or parent activity) that can be collapsed/expanded. Can somebody provide me with a summary of the steps to get such result so I can investigate and recreate someting similar? Could you help me indicating a guide to understand SVG and how to use it in this case?
I have to say that I don't know of any free version "ready to use" app for Gantt Charts in the PowerBi store that includes such capabilities, so I am amazed with the result of below example and what you can do using the existing build-in tools in Power Bi.
Thanks in advance for your comments!
Patricio.
Hi Patricio479 ,
I got inspired by exactly the same post and spent a bit of time in trying to rebuild something similiar. I tried various available Gantt chart visualization available for Power BI before, but wasn't happy at all, so I decided to built something basing on a standard matrix visual using SVG objects since it has been done by several people before.
Some informations:
- I'm using data from the Project web app ( Microsoft Project ) via an ODataFeed connector
- The data contains information in relation to parent - child hierarchies of the tasks, so I implemented the parent child solution as per the SQL BI pattern available: https://www.daxpatterns.com/parent-child-hierarchies/
- This will provide the possibility to expand and collapse on tasks ( summary tasks etc. )
- The bar visualization has been created via SVG objects. The visualization part is nothing else than a column of the matrix visual in which via a measure SVG objects are created. There are several tutorials on youtube how to do this, also ChatGPT can be very helpful in this matter.
- Project summary tasks, summary tasks, tasks, milestones etc. appear differently in order to be able to distinguish between them
- The maximum width of SVG objects in the matrix is 512px. This is a limitation which has to be considered, the bars of the individual lines are scaled to the 512px maximum width. --> 512px is the longest bar in the current selection, any other bar is scaled by the measure accodingly.
- I also created a time bar which shows Years and months and placed it in the column header of the column which visulalizes the SVG's in the matrix. This is nothing else than the standard Power BI table in which via a dedicated measure the time bar is created also via a SVG. If positioned correctly in the column header, it appears as it would be an integral part of the Gantt chart. Infact it is a separate visualization.
The result is the following:
The code for the bars in the above example is the following. SVG codes tend to get quite long, but in the end it is not very complex but more repetitive once the concept has been understood.
GanttSVG = VAR _MaxDuration = DATEDIFF ( CALCULATE ( MIN ( Tasks[TaskStartDate] ), ALLSELECTED ( TasksHeader ) ), CALCULATE ( MAX ( Tasks[TaskFinishDate] ), ALLSELECTED ( TasksHeader ) ), DAY ) VAR _DurationP = DATEDIFF ( MIN ( Tasks[TaskStartDate] ), MAX ( Tasks[TaskFinishDate] ), DAY ) VAR _BarStart = DATEDIFF ( CALCULATE ( MIN ( Tasks[TaskStartDate] ), ALLSELECTED ( TasksHeader ) ), MIN ( Tasks[TaskStartDate] ), DAY ) * DIVIDE ( 512, _MaxDuration ) VAR _BarStartMS = SWITCH ( TRUE (), _BarStart < 4, 4, _BarStart > 508, 508, _BarStart ) VAR _TextStartMS = IF ( _BarStartMS < 450, _BarStartMS + 8, _BarStartMS - 38 ) VAR _BarWidth = _DurationP * DIVIDE ( 512, _MaxDuration ) VAR _StartDate = FORMAT ( MIN ( Tasks[TaskStartDate] ), "DD.MM.YY" ) VAR _TodayLine = DATEDIFF ( CALCULATE ( MIN ( Tasks[TaskStartDate] ), ALLSELECTED ( TasksHeader ) ), TODAY (), DAY ) * DIVIDE ( 512, _MaxDuration ) VAR _IsPST = SELECTEDVALUE ( TasksHeader[TaskIsProjectSummary] ) VAR _IsST = SELECTEDVALUE ( TasksHeader[TaskIsSummary] ) VAR _IsMS = SELECTEDVALUE ( TasksHeader[TaskIsMilestone] ) VAR _ColourTL = "rgb(255,190,0)" VAR _WidtTL = "0.5" VAR _PST = "data:image/svg+xml;utf8,<svg width='512' height='16' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> <rect x='" & _BarStart & "' y='4' width='" & _BarWidth & "' height='8' fill='rgb(90, 90, 90)' stroke='rgb(255, 100, 30)' stroke-width='0.1' /> <polygon points='" & _BarStart & ",4 " & _BarStart + 4 & ",4 " & _BarStart + 4 & ",12 " & _BarStart & ",16 ' fill='rgb(255, 100, 30)' /> <polygon points='" & _BarStart + _BarWidth & ",4 " & _BarStart - 4 + _BarWidth & ",4 " & _BarStart - 4 + _BarWidth & ",12 " & _BarStart + _BarWidth & ",16 ' fill='rgb(255, 100, 30)' /> <line x1='" & _TodayLine & "' y1='0' x2='" & _TodayLine & "' y2='16' style='stroke:" & _ColourTL & ";stroke-width:" & _WidtTL & "' /> </svg>" VAR _ST = "data:image/svg+xml;utf8,<svg width='512' height='16' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> <rect x='" & _BarStart & "' y='5' width='" & _BarWidth & "' height='6' fill='rgb(125, 125, 125)' stroke='rgb(190, 190, 190)' stroke-width='0.1' /> <polygon points='" & _BarStart & ",5 " & _BarStart + 4 & ",5 " & _BarStart + 4 & ",11 " & _BarStart & ",15 ' fill='rgb(190, 190, 190)' /> <polygon points='" & _BarStart + _BarWidth & ",5 " & _BarStart - 4 + _BarWidth & ",5 " & _BarStart - 4 + _BarWidth & ",11 " & _BarStart + _BarWidth & ",15 ' fill='rgb(190, 190, 190)' /> <line x1='" & _TodayLine & "' y1='0' x2='" & _TodayLine & "' y2='16' style='stroke:" & _ColourTL & ";stroke-width:" & _WidtTL & "' /> </svg>" VAR _T = "data:image/svg+xml;utf8,<svg width='512' height='16' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> <rect x='" & _BarStart & "' y='4' width='" & _BarWidth & "' height='8' fill='rgb(190, 190, 190)' rx = '2.5'/> <line x1='" & _TodayLine & "' y1='0' x2='" & _TodayLine & "' y2='16' style='stroke:" & _ColourTL & ";stroke-width:" & _WidtTL & "' /> </svg>" VAR _MS = "data:image/svg+xml;utf8,<svg width='512' height='16' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> <polygon points='" & _BarStartMS - 4 & ",8 " & _BarStartMS & ",4 " & _BarStartMS + 4 & ",8, " & _BarStartMS & ",12' fill='rgb(90, 90, 90)'/> <text x='" & _TextStartMS & "' y='11' font-family='Arial' font-size='8' fill='rgb(190, 190, 190)'>" & _StartDate & "</text> <line x1='" & _TodayLine & "' y1='0' x2='" & _TodayLine & "' y2='16' style='stroke:" & _ColourTL & ";stroke-width:" & _WidtTL & "' /> </svg>" VAR _SVG = SWITCH ( TRUE (), MAX ( TasksHeader[TaskIsProjectSummary] ) = 1, _PST, MAX ( TasksHeader[TaskIsSummary] ) = 1, _ST, MAX ( TasksHeader[TaskIsMilestone] ) = 1, _MS, _T ) VAR _CheckBrowseDepth = [TaskBrowseDepth] <= [TaskRowDepth] VAR _Result = IF ( _CheckBrowseDepth && NOT ( ISBLANK ( _DurationP ) ), _SVG ) RETURN _ResultThe time bar is created via the following code:
GanttTimeLine = VAR _MinDate = CALCULATE ( MIN ( Tasks[TaskStartDate] ), ALLSELECTED ( TasksHeader ) ) VAR _MaxDate = CALCULATE ( MAX ( Tasks[TaskFinishDate] ), ALLSELECTED ( TasksHeader ) ) VAR _MaxDuration = DATEDIFF ( _MinDate, _MaxDate, DAY ) VAR _Scaling = DIVIDE ( 512, _MaxDuration ) VAR _TodayLine = DATEDIFF ( _MinDate, TODAY (), DAY ) * _Scaling VAR _FontSM = "6.5" VAR _ColourTimeL = "rgb(235,235,235)" VAR _ColourTL = "rgb(255,190,0)" VAR _WidtTL = "0.5" VAR _YLines = CONCATENATEX ( ADDCOLUMNS ( ADDCOLUMNS ( FILTER ( ADDCOLUMNS ( GENERATESERIES ( _MinDate, _MaxDate, 1 ), "MinDate", _MinDate, "EndofMonth", IF ( [Value] = DATE ( YEAR ( [Value] ), 12, 31 ), [Value] ) ), NOT ( ISBLANK ( [EndofMonth] ) ) ), "PosMLine", DATEDIFF ( [MinDate], [EndofMonth], DAY ) * _Scaling ), "SVGLine", VAR _PosMLine = [PosMLine] RETURN "<line x1='" & _PosMLine & "' y1='0' x2='" & _PosMLine & "' y2='33' style='stroke:" & _ColourTimeL & "; stroke-width:" & _WidtTL & "' />" ), [SVGLine] ) VAR _YText = CONCATENATEX ( ADDCOLUMNS ( ADDCOLUMNS ( FILTER ( ADDCOLUMNS ( GENERATESERIES ( _MinDate, _MaxDate, 1 ), "MinDate", _MinDate, "EndofMonth", IF ( [Value] = DATE ( YEAR ( [Value] ), 12, 31 ), [Value] ) ), NOT ( ISBLANK ( [EndofMonth] ) ) ), "PosMLine", DATEDIFF ( [MinDate], [EndofMonth], DAY ) * _Scaling, "YearText", YEAR ( [EndofMonth] ) + 1 ), "SVGText", VAR _PosMLine = [PosMLine] VAR _Year = [YearText] RETURN "<text x='" & _PosMLine + 5 & "' y='13' font-family='Arial' font-size='10' fill='" & _ColourTimeL & "'>" & _Year & "</text>" ), [SVGText] ) VAR _MLines = CONCATENATEX ( ADDCOLUMNS ( ADDCOLUMNS ( FILTER ( ADDCOLUMNS ( GENERATESERIES ( _MinDate, _MaxDate, 1 ), "MinDate", _MinDate, "EndofMonth", IF ( [Value] = EOMONTH ( [Value], 0 ), [Value] ) ), NOT ( ISBLANK ( [EndofMonth] ) ) ), "PosMLine", DATEDIFF ( [MinDate], [EndofMonth], DAY ) * _Scaling ), "SVGLine", VAR _PosMLine = [PosMLine] RETURN "<line x1='" & _PosMLine & "' y1='18' x2='" & _PosMLine & "' y2='28' style='stroke:" & _ColourTimeL & "; stroke-width:" & _WidtTL & "' />" ), [SVGLine] ) VAR _MText = CONCATENATEX ( ADDCOLUMNS ( ADDCOLUMNS ( ADDCOLUMNS ( FILTER ( ADDCOLUMNS ( GENERATESERIES ( _MinDate, _MaxDate, 1 ), "MinDate", _MinDate, "EndofMonth", IF ( [Value] = EOMONTH ( [Value], 0 ), [Value] ) ), NOT ( ISBLANK ( [EndofMonth] ) ) ), "PosMLine", DATEDIFF ( [MinDate], [EndofMonth], DAY ) * _Scaling, "PosMLineNext", DATEDIFF ( [MinDate], EOMONTH ( [EndofMonth], + 1 ), DAY ) * _Scaling, "MonthsText", LEFT ( FORMAT ( EOMONTH ( [EndofMonth], + 1 ), "mmm" ), 1 ) ), "Gap", [PosMLineNext] - [PosMLine] ), "SVGText", VAR _PosMText = [PosMLine] + DIVIDE ( [Gap], 2 ) VAR _MText = [MonthsText] RETURN "<text x='" & _PosMText & "' y='26' text-anchor='middle' font-family='Arial' font-size='" & _FontSM & "' fill='" & _ColourTimeL & "'>" & _MText & "</text>" ), [SVGText] ) VAR _TL1 = "data:image/svg+xml;utf8,<svg width='512' height='40' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> " & _YLines & " " & _YText & " " & _MLines & " " & _MText & " <line x1='0' y1='18' x2='512' y2='18' style='stroke:" & _ColourTimeL & ";stroke-width:" & _WidtTL & "' /> <line x1='" & _TodayLine & "' y1='0' x2='" & _TodayLine & "' y2='39' style='stroke:" & _ColourTL & ";stroke-width:" & _WidtTL & "' /> <rect x='0' y='16' width='2' height='5' fill='rgb(144,142,142)' /> </svg>" RETURN _TL1My data model is the following:
With the same concept, i have also created a baseline - actual comparison:
In the end one can build a very nice solution with no need of custom visuals.
I hope this provides some useful informations for someone who wants to rebuild it.
Best regards
15 Replies
- ITManuel
Responsive Resident
Hi Patricio479 ,
I got inspired by exactly the same post and spent a bit of time in trying to rebuild something similiar. I tried various available Gantt chart visualization available for Power BI before, but wasn't happy at all, so I decided to built something basing on a standard matrix visual using SVG objects since it has been done by several people before.
Some informations:
- I'm using data from the Project web app ( Microsoft Project ) via an ODataFeed connector
- The data contains information in relation to parent - child hierarchies of the tasks, so I implemented the parent child solution as per the SQL BI pattern available: https://www.daxpatterns.com/parent-child-hierarchies/
- This will provide the possibility to expand and collapse on tasks ( summary tasks etc. )
- The bar visualization has been created via SVG objects. The visualization part is nothing else than a column of the matrix visual in which via a measure SVG objects are created. There are several tutorials on youtube how to do this, also ChatGPT can be very helpful in this matter.
- Project summary tasks, summary tasks, tasks, milestones etc. appear differently in order to be able to distinguish between them
- The maximum width of SVG objects in the matrix is 512px. This is a limitation which has to be considered, the bars of the individual lines are scaled to the 512px maximum width. --> 512px is the longest bar in the current selection, any other bar is scaled by the measure accodingly.
- I also created a time bar which shows Years and months and placed it in the column header of the column which visulalizes the SVG's in the matrix. This is nothing else than the standard Power BI table in which via a dedicated measure the time bar is created also via a SVG. If positioned correctly in the column header, it appears as it would be an integral part of the Gantt chart. Infact it is a separate visualization.
The result is the following:
The code for the bars in the above example is the following. SVG codes tend to get quite long, but in the end it is not very complex but more repetitive once the concept has been understood.
GanttSVG = VAR _MaxDuration = DATEDIFF ( CALCULATE ( MIN ( Tasks[TaskStartDate] ), ALLSELECTED ( TasksHeader ) ), CALCULATE ( MAX ( Tasks[TaskFinishDate] ), ALLSELECTED ( TasksHeader ) ), DAY ) VAR _DurationP = DATEDIFF ( MIN ( Tasks[TaskStartDate] ), MAX ( Tasks[TaskFinishDate] ), DAY ) VAR _BarStart = DATEDIFF ( CALCULATE ( MIN ( Tasks[TaskStartDate] ), ALLSELECTED ( TasksHeader ) ), MIN ( Tasks[TaskStartDate] ), DAY ) * DIVIDE ( 512, _MaxDuration ) VAR _BarStartMS = SWITCH ( TRUE (), _BarStart < 4, 4, _BarStart > 508, 508, _BarStart ) VAR _TextStartMS = IF ( _BarStartMS < 450, _BarStartMS + 8, _BarStartMS - 38 ) VAR _BarWidth = _DurationP * DIVIDE ( 512, _MaxDuration ) VAR _StartDate = FORMAT ( MIN ( Tasks[TaskStartDate] ), "DD.MM.YY" ) VAR _TodayLine = DATEDIFF ( CALCULATE ( MIN ( Tasks[TaskStartDate] ), ALLSELECTED ( TasksHeader ) ), TODAY (), DAY ) * DIVIDE ( 512, _MaxDuration ) VAR _IsPST = SELECTEDVALUE ( TasksHeader[TaskIsProjectSummary] ) VAR _IsST = SELECTEDVALUE ( TasksHeader[TaskIsSummary] ) VAR _IsMS = SELECTEDVALUE ( TasksHeader[TaskIsMilestone] ) VAR _ColourTL = "rgb(255,190,0)" VAR _WidtTL = "0.5" VAR _PST = "data:image/svg+xml;utf8,<svg width='512' height='16' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> <rect x='" & _BarStart & "' y='4' width='" & _BarWidth & "' height='8' fill='rgb(90, 90, 90)' stroke='rgb(255, 100, 30)' stroke-width='0.1' /> <polygon points='" & _BarStart & ",4 " & _BarStart + 4 & ",4 " & _BarStart + 4 & ",12 " & _BarStart & ",16 ' fill='rgb(255, 100, 30)' /> <polygon points='" & _BarStart + _BarWidth & ",4 " & _BarStart - 4 + _BarWidth & ",4 " & _BarStart - 4 + _BarWidth & ",12 " & _BarStart + _BarWidth & ",16 ' fill='rgb(255, 100, 30)' /> <line x1='" & _TodayLine & "' y1='0' x2='" & _TodayLine & "' y2='16' style='stroke:" & _ColourTL & ";stroke-width:" & _WidtTL & "' /> </svg>" VAR _ST = "data:image/svg+xml;utf8,<svg width='512' height='16' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> <rect x='" & _BarStart & "' y='5' width='" & _BarWidth & "' height='6' fill='rgb(125, 125, 125)' stroke='rgb(190, 190, 190)' stroke-width='0.1' /> <polygon points='" & _BarStart & ",5 " & _BarStart + 4 & ",5 " & _BarStart + 4 & ",11 " & _BarStart & ",15 ' fill='rgb(190, 190, 190)' /> <polygon points='" & _BarStart + _BarWidth & ",5 " & _BarStart - 4 + _BarWidth & ",5 " & _BarStart - 4 + _BarWidth & ",11 " & _BarStart + _BarWidth & ",15 ' fill='rgb(190, 190, 190)' /> <line x1='" & _TodayLine & "' y1='0' x2='" & _TodayLine & "' y2='16' style='stroke:" & _ColourTL & ";stroke-width:" & _WidtTL & "' /> </svg>" VAR _T = "data:image/svg+xml;utf8,<svg width='512' height='16' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> <rect x='" & _BarStart & "' y='4' width='" & _BarWidth & "' height='8' fill='rgb(190, 190, 190)' rx = '2.5'/> <line x1='" & _TodayLine & "' y1='0' x2='" & _TodayLine & "' y2='16' style='stroke:" & _ColourTL & ";stroke-width:" & _WidtTL & "' /> </svg>" VAR _MS = "data:image/svg+xml;utf8,<svg width='512' height='16' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> <polygon points='" & _BarStartMS - 4 & ",8 " & _BarStartMS & ",4 " & _BarStartMS + 4 & ",8, " & _BarStartMS & ",12' fill='rgb(90, 90, 90)'/> <text x='" & _TextStartMS & "' y='11' font-family='Arial' font-size='8' fill='rgb(190, 190, 190)'>" & _StartDate & "</text> <line x1='" & _TodayLine & "' y1='0' x2='" & _TodayLine & "' y2='16' style='stroke:" & _ColourTL & ";stroke-width:" & _WidtTL & "' /> </svg>" VAR _SVG = SWITCH ( TRUE (), MAX ( TasksHeader[TaskIsProjectSummary] ) = 1, _PST, MAX ( TasksHeader[TaskIsSummary] ) = 1, _ST, MAX ( TasksHeader[TaskIsMilestone] ) = 1, _MS, _T ) VAR _CheckBrowseDepth = [TaskBrowseDepth] <= [TaskRowDepth] VAR _Result = IF ( _CheckBrowseDepth && NOT ( ISBLANK ( _DurationP ) ), _SVG ) RETURN _ResultThe time bar is created via the following code:
GanttTimeLine = VAR _MinDate = CALCULATE ( MIN ( Tasks[TaskStartDate] ), ALLSELECTED ( TasksHeader ) ) VAR _MaxDate = CALCULATE ( MAX ( Tasks[TaskFinishDate] ), ALLSELECTED ( TasksHeader ) ) VAR _MaxDuration = DATEDIFF ( _MinDate, _MaxDate, DAY ) VAR _Scaling = DIVIDE ( 512, _MaxDuration ) VAR _TodayLine = DATEDIFF ( _MinDate, TODAY (), DAY ) * _Scaling VAR _FontSM = "6.5" VAR _ColourTimeL = "rgb(235,235,235)" VAR _ColourTL = "rgb(255,190,0)" VAR _WidtTL = "0.5" VAR _YLines = CONCATENATEX ( ADDCOLUMNS ( ADDCOLUMNS ( FILTER ( ADDCOLUMNS ( GENERATESERIES ( _MinDate, _MaxDate, 1 ), "MinDate", _MinDate, "EndofMonth", IF ( [Value] = DATE ( YEAR ( [Value] ), 12, 31 ), [Value] ) ), NOT ( ISBLANK ( [EndofMonth] ) ) ), "PosMLine", DATEDIFF ( [MinDate], [EndofMonth], DAY ) * _Scaling ), "SVGLine", VAR _PosMLine = [PosMLine] RETURN "<line x1='" & _PosMLine & "' y1='0' x2='" & _PosMLine & "' y2='33' style='stroke:" & _ColourTimeL & "; stroke-width:" & _WidtTL & "' />" ), [SVGLine] ) VAR _YText = CONCATENATEX ( ADDCOLUMNS ( ADDCOLUMNS ( FILTER ( ADDCOLUMNS ( GENERATESERIES ( _MinDate, _MaxDate, 1 ), "MinDate", _MinDate, "EndofMonth", IF ( [Value] = DATE ( YEAR ( [Value] ), 12, 31 ), [Value] ) ), NOT ( ISBLANK ( [EndofMonth] ) ) ), "PosMLine", DATEDIFF ( [MinDate], [EndofMonth], DAY ) * _Scaling, "YearText", YEAR ( [EndofMonth] ) + 1 ), "SVGText", VAR _PosMLine = [PosMLine] VAR _Year = [YearText] RETURN "<text x='" & _PosMLine + 5 & "' y='13' font-family='Arial' font-size='10' fill='" & _ColourTimeL & "'>" & _Year & "</text>" ), [SVGText] ) VAR _MLines = CONCATENATEX ( ADDCOLUMNS ( ADDCOLUMNS ( FILTER ( ADDCOLUMNS ( GENERATESERIES ( _MinDate, _MaxDate, 1 ), "MinDate", _MinDate, "EndofMonth", IF ( [Value] = EOMONTH ( [Value], 0 ), [Value] ) ), NOT ( ISBLANK ( [EndofMonth] ) ) ), "PosMLine", DATEDIFF ( [MinDate], [EndofMonth], DAY ) * _Scaling ), "SVGLine", VAR _PosMLine = [PosMLine] RETURN "<line x1='" & _PosMLine & "' y1='18' x2='" & _PosMLine & "' y2='28' style='stroke:" & _ColourTimeL & "; stroke-width:" & _WidtTL & "' />" ), [SVGLine] ) VAR _MText = CONCATENATEX ( ADDCOLUMNS ( ADDCOLUMNS ( ADDCOLUMNS ( FILTER ( ADDCOLUMNS ( GENERATESERIES ( _MinDate, _MaxDate, 1 ), "MinDate", _MinDate, "EndofMonth", IF ( [Value] = EOMONTH ( [Value], 0 ), [Value] ) ), NOT ( ISBLANK ( [EndofMonth] ) ) ), "PosMLine", DATEDIFF ( [MinDate], [EndofMonth], DAY ) * _Scaling, "PosMLineNext", DATEDIFF ( [MinDate], EOMONTH ( [EndofMonth], + 1 ), DAY ) * _Scaling, "MonthsText", LEFT ( FORMAT ( EOMONTH ( [EndofMonth], + 1 ), "mmm" ), 1 ) ), "Gap", [PosMLineNext] - [PosMLine] ), "SVGText", VAR _PosMText = [PosMLine] + DIVIDE ( [Gap], 2 ) VAR _MText = [MonthsText] RETURN "<text x='" & _PosMText & "' y='26' text-anchor='middle' font-family='Arial' font-size='" & _FontSM & "' fill='" & _ColourTimeL & "'>" & _MText & "</text>" ), [SVGText] ) VAR _TL1 = "data:image/svg+xml;utf8,<svg width='512' height='40' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> " & _YLines & " " & _YText & " " & _MLines & " " & _MText & " <line x1='0' y1='18' x2='512' y2='18' style='stroke:" & _ColourTimeL & ";stroke-width:" & _WidtTL & "' /> <line x1='" & _TodayLine & "' y1='0' x2='" & _TodayLine & "' y2='39' style='stroke:" & _ColourTL & ";stroke-width:" & _WidtTL & "' /> <rect x='0' y='16' width='2' height='5' fill='rgb(144,142,142)' /> </svg>" RETURN _TL1My data model is the following:
With the same concept, i have also created a baseline - actual comparison:
In the end one can build a very nice solution with no need of custom visuals.
I hope this provides some useful informations for someone who wants to rebuild it.
Best regards
- Patricio479Frequent Visitor
Hello, dear ITManuel
First of all, I just have one word for this: wow!
Secondly, I would like to thank you and show my biggest appreciation as this might have taken lots of time to put together: since the start of the project to this final very-detailed post.
I am still giving my first steps into understanding how the SVG and coding/dax works, so I might reach out to you in the future in case I have questions.
Thanks again for your effort and time to teach us how to do a very professional Gantt chart using the tools within PowerBI... I am pretty sure that this will help not only me but a considerable amount of people willing to learn a more advanced PowerBI.
Have a nice rest of the day, dear ITManuel .
Kind regards,
Patricio.
- ITManuel
Responsive Resident
Hi Patricio479 ,
thank you very much, you are welcome. I have got to solutions for the issues I had with Power BI so many times via this community, so its time to give something back. 😊
Feel free to reach out for any further issues.
Best regards
- pnm_100Frequent Visitor
Hey, love this! Is there any way to get a copy of the .pbix file? I am trying to make one similar but can't seem to get it to work. It is also hard to see the table structure in the picture. Thanks