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

The ultimate Microsoft Fabric, Power BI, Azure AI & SQL learning event! Join us in Las Vegas from March 26-28, 2024. Use code MSCUST for a $100 discount. Register Now

Reply
Sir_91
New Member

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 @Sir_91,

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 @Sir_91,

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
Fabric Community Conference

Microsoft Fabric Community Conference

Join us at our first-ever Microsoft Fabric Community Conference, March 26-28, 2024 in Las Vegas with 100+ sessions by community experts and Microsoft engineering.

February 2024 Update Carousel

Power BI Monthly Update - February 2024

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

Fabric Career Hub

Microsoft Fabric Career Hub

Explore career paths and learn resources in Fabric.

Fabric Partner Community

Microsoft Fabric Partner Community

Engage with the Fabric engineering team, hear of product updates, business opportunities, and resources in the Fabric Partner Community.