Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Find everything you need to get certified on Fabric—skills challenges, live sessions, exam prep, role guidance, and more. Get started

Reply
Anonymous
Not applicable

Using D3.JS visual in PowerBI and I can't get it to work.

Hi all, 
I am new to the D3.js visual in PowerBI and can't seem to get it to work. I have followed the tutorial the best I know how without any luck. Here is the tutorial I am referring to: https://azurebi-docs.jppp.org/powerbi-visuals/d3js.html?tabs=docs%2Cdocs-open

 

I have an animated multiple line chart: the lines are the different ids in my dataset, the animation is based off of a timestamp column in which the datapoints (and the connecting lines) display based on each datapoint's timestamp. There is a replay button to replay the animation. 
My data has the following columns: id, left, top, timestamp. One id may have multiple rows. y-axis is left, x-axis is top. 
I also have a background image for the plotArea.

This is what i have so far in the D3.JS editor:

 

 

 

 

var margin = {top: 20, right: 30, bottom: 50, left: 40},
    width = pbi.width - margin.left - margin.right,
    height = pbi.height - margin.top - margin.bottom;

var x = d3.scaleLinear()      
    .domain([0, 1800])
    .range([margin.left, width - margin.right]).clamp(true);

var y = d3.scaleLinear()     
    .domain([0, 1200])      
    .range([margin.top, height - margin.bottom]).clamp(true);

var svg = d3.select("#chart")
      	.attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom);

var plotAreaWidth = pbi.width - margin.left - margin.right;
var plotAreaHeight = pbi.height - margin.top - margin.bottom;

var plotArea = svg.append("g")
               .attr("class", "plot-area")
               .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

plotArea.append("image")
        .attr("xlink:href", "backdrop.png") 
        .attr("width", plotAreaWidth)
        .attr("height", plotAreaHeight);

svg.append("g")
   .attr("transform", "translate(0," + (height - margin.bottom) + ")")
   .call(d3.axisBottom(x).ticks(4).tickSizeOuter(0))
   .call(function(g) { g.select(".domain").remove(); })
   .call(function(g) { g.selectAll(".tick line").clone()
   						.attr("y2", -height)
    					.attr("stroke-opacity", 0.1); })
   .append("text")
   .attr("x", width - 4)
   .attr("y", -4)
   .attr("font-weight", "bold")
   .attr("text-anchor", "end")
   .attr("fill", "currentColor")
   .text("Top");

svg.append("g")
   .attr("transform", "translate(" + margin.left + ",0)")
   .call(d3.axisLeft(y).ticks(6).tickSizeOuter(0))
   .call(function(g) { g.select(".domain").remove(); })
   .call(function(g) { g.selectAll(".tick line").clone()
                    	.attr("x2", width)
                    	.attr("stroke-opacity", 0.1); })
   .append("text")
   .attr("x", 4)
   .attr("y", margin.top - 10)
   .attr("font-weight", "bold")
   .attr("text-anchor", "start")
   .attr("fill", "currentColor")
   .text("Left");

var loadChart = function(){
pbi.dsv(function(data){
        data.sort(function(a, b) { return new Date(a.timestamp) - new Date(b.timestamp); });

	var ids = Array.from(new Set(data.map(function(d) { return d.id; })));
  	
  	var color = d3.scaleOrdinal()
                .domain(ids)
                .range(d3.schemeCategory10);

    ids.forEach(function(id) {
       var idData = data.filter(function(d) { return d.id === id; });
       var lineData = idData; // Include all data points for the line

       plotArea.append("path")
               .datum(lineData)
               .attr("fill", "none")
               .attr("stroke", color(id))
               .attr("stroke-width", 2.5)
               .attr("stroke-linejoin", "round")
               .attr("stroke-linecap", "round")
               .attr("d", d3.line()
                        .x(function(d) { return x(d.top); })
                        .y(function(d) { return y(d.left); }));
    });
  
    data.forEach(function(d, i) {
         	setTimeout(function() {
             	plotArea.append("circle")
                        .attr("cx", x(d.top))
                        .attr("cy", y(d.left))
                        .attr("r", 4)
                        .attr("fill", "steelblue")
                        .on("mouseover", function() {
                            d3.select(this.nextSibling).style("visibility", "visible");
                        })
                        .on("mouseout", function() {
                            d3.select(this.nextSibling).style("visibility", "hidden");
                        });

                plotArea.append("text")
                        .attr("x", x(d.top))
                        .attr("y", y(d.left) - 10)
                        .attr("text-anchor", "middle")
                        .text(d.timestamp.toLocaleString())
                        .attr("fill", "black")
                        .style("visibility", "hidden");

                    ids.forEach(function(id) {
                        var idData = data.filter(function(item) { return item.id === id && item.timestamp <= d.timestamp; });
                        var lineData = idData; // Include all data points for the line

                        plotArea.select('path[stroke="' + color(id) + '"]')
                            .datum(lineData)
                            .attr("d", d3.line()
                                .x(function(d) { return x(d.top); })
                                .y(function(d) { return y(d.left); }));
                    });
                }, i * 100);
            });
  
});
}

loadChart();
var replayButton = d3.select("body").append("button").attr("id", "replay-button").text("Replay");
        replayButton.on("click", function() {
            d3.select("#chart").selectAll("*").remove(); // Clear the chart
            drawConnectedScatterplot(data);
        });

 

 

 

 


I have tried different variations and have tried simplifying it by focusing on simply getting a line chart to display but i can't get it to work, nothing gets generated at all - all i see is a blank placeholder.
 
This is the html version that works as expected:

 

 

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Connected Scatterplot</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
    <div id="chart"></div>

    <script>
        function drawConnectedScatterplot(data) {
            // Sort the data based on the timestamp
            data.sort(function(a, b) { return a.timestamp - b.timestamp; });

            var width = 600;
            var height = 400;
            var margin = { top: 20, right: 30, bottom: 50, left: 40 };

            var x = d3.scaleLinear()      
                .domain([0, 1800])
                .range([margin.left, width - margin.right]).clamp(true);

            var y = d3.scaleLinear()     
                .domain([0, 1200])      
                .range([margin.top, height - margin.bottom]).clamp(true);

            var svg = d3.select("#chart").append("svg")
                .attr("width", width)
                .attr("height", height);

            var plotAreaWidth = width - margin.left - margin.right;
            var plotAreaHeight = height - margin.top - margin.bottom;

            var plotArea = svg.append("g")
                .attr("class", "plot-area")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

            // Append background image to the plot area
            plotArea.append("image")
                .attr("xlink:href", "backdrop.png")
                .attr("width", plotAreaWidth)
                .attr("height", plotAreaHeight);

                svg.append("g")
                .attr("transform", "translate(0," + (height - margin.bottom) + ")")
                .call(d3.axisBottom(x).ticks(4).tickSizeOuter(0))
                .call(function(g) { g.select(".domain").remove(); })
                .call(function(g) { g.selectAll(".tick line").clone()
                    .attr("y2", -height)
                    .attr("stroke-opacity", 0.1); })
                .append("text")
                .attr("x", width - 4)
                .attr("y", -4)
                .attr("font-weight", "bold")
                .attr("text-anchor", "end")
                .attr("fill", "currentColor")
                .text("Top");

            svg.append("g")
                .attr("transform", "translate(" + margin.left + ",0)")
                .call(d3.axisLeft(y).ticks(6).tickSizeOuter(0))
                .call(function(g) { g.select(".domain").remove(); })
                .call(function(g) { g.selectAll(".tick line").clone()
                    .attr("x2", width)
                    .attr("stroke-opacity", 0.1); })
                .append("text")
                .attr("x", 4)
                .attr("y", margin.top - 10)
                .attr("font-weight", "bold")
                .attr("text-anchor", "start")
                .attr("fill", "currentColor")
                .text("Left");

            var ids = Array.from(new Set(data.map(function(d) { return d.id; })));

            var color = d3.scaleOrdinal()
                .domain(ids)
                .range(d3.schemeCategory10);

            ids.forEach(function(id) {
                var idData = data.filter(function(d) { return d.id === id; });
                var lineData = idData; // Include all data points for the line

                plotArea.append("path")
                    .datum(lineData)
                    .attr("fill", "none")
                    .attr("stroke", color(id))
                    .attr("stroke-width", 2.5)
                    .attr("stroke-linejoin", "round")
                    .attr("stroke-linecap", "round")
                    .attr("d", d3.line()
                        .x(function(d) { return x(d.top); })
                        .y(function(d) { return y(d.left); }));
            });

            

            data.forEach(function(d, i) {
                setTimeout(function() {
                    plotArea.append("circle")
                        .attr("cx", x(d.top))
                        .attr("cy", y(d.left))
                        .attr("r", 4)
                        .attr("fill", "steelblue")
                        .on("mouseover", function() {
                            d3.select(this.nextSibling).style("visibility", "visible");
                        })
                        .on("mouseout", function() {
                            d3.select(this.nextSibling).style("visibility", "hidden");
                        });

                    plotArea.append("text")
                        .attr("x", x(d.top))
                        .attr("y", y(d.left) - 10)
                        .attr("text-anchor", "middle")
                        .text(d.timestamp.toLocaleString())
                        .attr("fill", "black")
                        .style("visibility", "hidden");

                    ids.forEach(function(id) {
                        var idData = data.filter(function(item) { return item.id === id && item.timestamp <= d.timestamp; });
                        var lineData = idData; // Include all data points for the line

                        plotArea.select('path[stroke="' + color(id) + '"]')
                            .datum(lineData)
                            .attr("d", d3.line()
                                .x(function(d) { return x(d.top); })
                                .y(function(d) { return y(d.left); }));
                    });
                }, i * 100);
            });
        }

        var data = [
            { id: "ID1", left: 744, top: 987, timestamp: new Date("2024-01-01T05:00:00") },
            { id: "ID1", left: 828, top: 938, timestamp: new Date("2024-01-01T10:00:00") },
            { id: "ID1", left: 886, top: 883, timestamp: new Date("2024-01-01T11:00:00") },
            { id: "ID1", left: 940, top: 734, timestamp: new Date("2024-01-01T12:00:00") },
            { id: "ID1", left: 997, top: 638, timestamp: new Date("2024-01-01T13:00:00") },
            { id: "ID1", left: 1056, top: 577, timestamp: new Date("2024-01-01T14:00:00") },
            { id: "ID1", left: 1101, top: 516, timestamp: new Date("2024-01-01T15:00:00") },
            { id: "ID1", left: 1146, top: 473, timestamp: new Date("2024-01-01T16:00:00") },
            { id: "ID1", left: 1179, top: 411, timestamp: new Date("2024-01-01T17:00:00") },
            { id: "ID1", left: 1212, top: 372, timestamp: new Date("2024-01-01T18:00:00") },
            { id: "ID2", left: 1417, top: 312, timestamp: new Date("2024-01-01T06:00:00") },
            { id: "ID2", left: 1436, top: 330, timestamp: new Date("2024-01-01T11:30:00") },
            { id: "ID2", left: 1448, top: 355, timestamp: new Date("2024-01-01T12:45:00") },
            { id: "ID2", left: 1445, top: 386, timestamp: new Date("2024-01-01T13:30:00") },
            { id: "ID2", left: 1444, top: 420, timestamp: new Date("2024-01-01T14:15:00") },
            { id: "ID2", left: 1430, top: 467, timestamp: new Date("2024-01-01T15:00:00") },
            { id: "ID2", left: 1414, top: 512, timestamp: new Date("2024-01-01T15:45:00") },
            { id: "ID2", left: 1392, top: 573, timestamp: new Date("2024-01-01T16:30:00") },
            { id: "ID2", left: 1363, top: 628, timestamp: new Date("2024-01-01T17:15:00") },
            { id: "ID2", left: 1318, top: 699, timestamp: new Date("2024-01-01T18:00:00") }
        ];

        drawConnectedScatterplot(data);

        var replayButton = d3.select("body").append("button").attr("id", "replay-button").text("Replay");
        replayButton.on("click", function() {
            d3.select("#chart").selectAll("*").remove(); // Clear the chart
            drawConnectedScatterplot(data);
        });
    </script>
</body>
</html>

 

 

 

Since i am in powerbi desktop im finding it excessively difficult to find the issue/s since i can't debug as i would in the browser.


Any guidance on how to get it to work is much appeciated!

1 ACCEPTED SOLUTION
v-shex-msft
Community Support
Community Support

HI @Anonymous,

I'd like to suggest you to check the d3.js visual official support page to know the usage and the limitations at the first:

Power BI D3.js Visual - Azure BI (jppp.org)

Regards,

Xiaoxin Sheng

Community Support Team _ Xiaoxin
If this post helps, please consider accept as solution to help other members find it more quickly.

View solution in original post

1 REPLY 1
v-shex-msft
Community Support
Community Support

HI @Anonymous,

I'd like to suggest you to check the d3.js visual official support page to know the usage and the limitations at the first:

Power BI D3.js Visual - Azure BI (jppp.org)

Regards,

Xiaoxin Sheng

Community Support Team _ Xiaoxin
If this post helps, please consider accept as solution to help other members find it more quickly.

Helpful resources

Announcements
Sept PBI Carousel

Power BI Monthly Update - September 2024

Check out the September 2024 Power BI update to learn about new features.

September Hackathon Carousel

Microsoft Fabric & AI Learning Hackathon

Learn from experts, get hands-on experience, and win awesome prizes.

Sept NL Carousel

Fabric Community Update - September 2024

Find out what's new and trending in the Fabric Community.

Top Solution Authors