Forum Discussion
Network (force-directed) visual using power BI d3 custom visual
Hi All,
I'm having trouble getting a force-directed visual to display in Power BI, I think its related to how the data is referenced. I can get it working outside of Power Bi but just can't get it working in Power BI. Here it what it looks like (just using dummy data, which has the form of four colums name, group, source and target. I loaded the data into Power BI from a csv file).
Simple force-directed chart
The dummy data I used was,
| name | group | source | target |
| A | 1 | 0 | 1 |
| B | 2 | 0 | 4 |
| C | 3 | 0 | 5 |
| D | 4 | 1 | 2 |
| E | 5 | 1 | 6 |
| F | 6 | 2 | 3 |
| G | 7 | 7 | 2 |
| H | 8 | 3 | 4 |
| I | 9 | 3 | 8 |
| J | 10 | 4 | 9 |
and here is the code that I wrote outside of Power BI
var margin = {top: 10, right: 30, bottom: 30, left: 40},
width = 400 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var force = d3.layout.force()
.gravity(0.1)
.distance(100)
.charge(-700)
.size([width,height]);
var color = function(group) {
if (group==1) {return "#aaa"}
else if (group==2) {return "#fbc280"}
else {return "#405275"}};
var links = [];
var nodes = [];
// d3.json("Network.json", function(error, data) {
// if (error) throw error;
d3.csv("Network.csv")
.row(function(d) {return {name:d.name, group:Number(d.group),source:Number(d.source), target:Number(d.target)};})
.get(function(error,data) {
for(var i = 0;i < 10; i++){
links.push({"source":data[i].source,
"target":data[i].target
})
nodes.push({"name":data[i].name,
"group":data[i].group
})
};
var dat = {"nodes":nodes, "links":links}
console.log(dat);
console.log(dat.nodes);
console.log(dat.links);
force
.nodes(dat.nodes)
.links(dat.links)
.start();
var link = svg.selectAll(".link")
.data(dat.links)
.enter()
.append("line")
.attr("class","link");
var node = svg.selectAll(".node")
.data(dat.nodes)
.enter()
.append("g")
.attr("class", "node")
.call(force.drag);
node
.append('circle')
.attr('r', 13)
.attr('fill', function (d) {
return color(d.group);});
node
.append("text")
.attr("dx", -18)
.attr("dy", 8)
.style("font-family", "overwatch")
.style("font-size", "18px")
.text(function (d) {
return d.name});
force.on("tick", function () {
link
.attr("x1", function (d) {
return d.source.x;})
.attr("y1", function (d) {
return d.source.y;})
.attr("x2", function (d) {
return d.target.x;})
.attr("y2", function (d) {
return d.target.y;});
node
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";});
});
// //console.log(links);
// //console.log(nodes);
});And here is the code I'm trying to get running in Power BI (adapted from the above code).
var margin = {top: 10, right: 30, bottom: 30, left: 40},
width = pbi.width - margin.left - margin.right,
height = pbi.height - margin.top - margin.bottom;
// append the svg object to chart
var svg = d3.select("#chart")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var force = d3.layout.force()
.gravity(0.1)
.distance(100)
.charge(-700)
.size([width,height]);
var color = function(group) {
if (group==1) {return "#aaa"}
else if (group==2) {return "#fbc280"}
else {return "#405275"}};
var links = [];
var nodes = [];
pbi.dsv(function(data) {
for(var i = 0;i < 10; i++){
links.push({"source":data[i].source,
"target":data[i].target
})
nodes.push({"name":data[i].name,
"group":data[i].group
})
};
var dat = {"nodes":nodes, "links":links}
force
.nodes(dat.nodes)
.links(dat.links)
.start();
var link = svg.selectAll(".link")
.data(dat.links)
.enter()
.append("line")
.attr("class","link");
var node = svg.selectAll(".node")
.data(dat.nodes)
.enter()
.append("g")
.attr("class", "node")
.call(force.drag);
node
.append('circle')
.attr('r', 13)
.attr('fill', function (d) {
return color(d.group);});
node
.append("text")
.attr("dx", -18)
.attr("dy", 8)
.style("font-family", "allan")
.style("font-size", "18px")
.text(function (d) {
return d.name});
force.on("tick", function () {
link
.attr("x1", function (d) {
return d.source.x;})
.attr("y1", function (d) {
return d.source.y;})
.attr("x2", function (d) {
return d.target.x;})
.attr("y2", function (d) {
return d.target.y;});
node
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";});
});
});If anyone can provide some help than I would be very greatful.
10 Replies
- v-evelkMicrosoft Employee
Hi,
Have you already checked how ForceGraph is implemented?
Please note that source code for this visual is public, feel free to fork and modify it for your needs.
Evgenii Elkin,
Software Engineer
Microsoft Power BI Custom Visuals
[email protected]- alpayneFrequent Visitor
Thank you for you reply. I had already looked at the Force Graph Power BI Visual, but it didn't suit my needs. I feel changing the source code of this visual would be whole heap more complicated than just trying to get my simple script to run in the Power Bi D3 visual. My thinking was, if I can implement the very simply example I outlined then once it was working I could adpated it to display a much more complicated scenario. The issue I'm having I think is to do with how I get Power BI to recognise the input data.
- alpayneFrequent Visitor
Sorry I should have also said. I'm only using Power BI Desktop, hence adapting the code will not be possible.
- AnonymousNot applicable
Hi,
Were you able to get this resolved? I'm having the exact same issue where the D3 force chart won't render despite it working fine outside of the tool.
Thanks!
- AnonymousNot applicable
I contacted the developer and was able to get the answer on how to do this. More info was needed in the declaration pbi.dsv line.
var margin = {top: 10, right: 30, bottom: 30, left: 40}, width = pbi.width - margin.left - margin.right, height = pbi.height - margin.top - margin.bottom; // append the svg object to chart var svg = d3.select("#chart") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var force = d3.layout.force() .gravity(0.1) .distance(100) .charge(-700) .size([width,height]); var color = function(group) { if (group==1) {return "#aaa"} else if (group==2) {return "#fbc280"} else {return "#405275"}}; var links = []; var nodes = []; pbi.dsv(function(d) {return {name:d.name, group:Number(d.group),source:Number(d.source), target:Number(d.target)};}, function(data) { for(var i = 0;i < 10; i++){ links.push({"source":data[i].source, "target":data[i].target }) nodes.push({"name":data[i].name, "group":data[i].group }) }; var dat = {"nodes":nodes, "links":links} force .nodes(dat.nodes) .links(dat.links) .start(); var link = svg.selectAll(".link") .data(dat.links) .enter() .append("line") .attr("class","link"); var node = svg.selectAll(".node") .data(dat.nodes) .enter() .append("g") .attr("class", "node") .call(force.drag); node .append('circle') .attr('r', 13) .attr('fill', function (d) { return color(d.group);}); node .append("text") .attr("dx", -18) .attr("dy", 8) .style("font-family", "allan") .style("font-size", "18px") .text(function (d) { return d.name}); force.on("tick", function () { link .attr("x1", function (d) { return d.source.x;}) .attr("y1", function (d) { return d.source.y;}) .attr("x2", function (d) { return d.target.x;}) .attr("y2", function (d) { return d.target.y;}); node .attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")";}); }); });To get the links to show you will also need to add some CSS to that tab:
.link { stroke-width: 1px; stroke: #ccc; }