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

Get inspired! Check out the entries from the Power BI DataViz World Championships preliminary rounds and give kudos to your favorites. View the vizzies.

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
Las Vegas 2025

Join us at the Microsoft Fabric Community Conference

March 31 - April 2, 2025, in Las Vegas, Nevada. Use code FABINSIDER for a $400 discount!

FebPBI_Carousel

Power BI Monthly Update - February 2025

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

March2025 Carousel

Fabric Community Update - March 2025

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

Top Solution Authors
Top Kudoed Authors