Forum Discussion

alpayne's avatar
alpayne
Frequent Visitor
7 years ago

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,

 

namegroupsourcetarget
A101
B204
C305
D412
E516
F623
G772
H834
I938
J1049

 

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-evelk's avatar
    v-evelk
    Microsoft Employee

    Hi,

     

    Have you already checked how ForceGraph is implemented?

    Please note that source code for this visual is public, feelfreetofork and modify it for your needs.

     

    Evgenii Elkin,
    Software Engineer
    Microsoft Power BI Custom Visuals
    [email protected]

     

    • alpayne's avatar
      alpayne
      Frequent 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. 

      • alpayne's avatar
        alpayne
        Frequent Visitor

        Sorry I should have also said. I'm only using Power BI Desktop, hence adapting the code will not be possible. 

  • Anonymous's avatar
    Anonymous
    Not 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!

  • Anonymous's avatar
    Anonymous
    Not 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;
    }