mysterious_guy mysterious_guy - 1 month ago 12
Javascript Question

d3..js : adding texts to nodes and links using incoming JSON data of varying size

My JSON data is as follows:

var IDData = JSON.stringify([
["node/105173", "node/38180995", "Agent", "Customer", "1379644.0", 1, 264, "1374903"],
["node/1061", "node/21373542", "Agent", "Customer", "530848.0", 1, 3000, "529502"],
["node/10750", "node/59648369", "Agent", "Customer", "1454228.0", 1, 120, "1454118"],
["node/10750", "node/78569210", "Agent", "Customer", "1425251.0", 1, 234, "1421416"],
["node/10750", "node/96726118", "Agent", "Customer", "1376239.0", 1, 434, "1376152"],
["node/10946829", "node/11190", "Customer", "Agent", "1409620.0", 20, 3380, "1406665"]...])


The length of the array of array is nt fixed but format stays the same.

Below is the link to the working JSfiddle:

https://jsfiddle.net/user/vishy1988/fiddles/

The above code runs fine on my system. but for some reason I cannot get it to run on JSfiddle. the code generates the below graph.

node graph

This is how I iterate through the above array and make the data for the graph:

$(document).ready(function() {
console.log(IDData);
var galData = JSON.parse(IDData);
var startnodes = [];
var endnodes = [];
var startnodetype = [];
var endnodetype = [];
var PayTime = [];
var TXN_COUNT = [];
var Total_Amt = [];
var SendTime = [];
galData.map(function(e, i) {
startnodes.push(e[0]);
endnodes.push(e[1]);
startnodetype.push(e[2]);
endnodetype.push(e[3]);
PayTime.push(e[4]);
TXN_COUNT.push(e[5]);
Total_Amt.push(e[6]);
SendTime.push(e[7]);
});
var final_data = createNodes(startnodes, endnodes, startnodetype, endnodetype, PayTime, TXN_COUNT, Total_Amt, SendTime);
makeGraph("#Network_graph", final_data);

});


The createnodes function which creates the data necessary to render a graph is as follows:

function createNodes(startnodes, endnodes, startnodetype, endnodetype, PayTime, TXN_COUNT, Total_Amt, SendTime) {
var node_set = d3.set();
var links = [];
var nodetype = d3.set();
startnodes.forEach(function(src, i) {
var tgt = endnodes[i];
node_set.add(src);
node_set.add(tgt);
links.push({
source: src,
target: tgt,
value: 1
});
});

var d3GraphData = {
nodes: node_set.values().map(function(d) {
return {
id: d,
group: 1
}
}),
links: links
}


return d3GraphData;

};


Also need to calculate below elements and add them to

d3graphdata


Below should be the Node text on hovering over the nodes:

startnodetype, //node text on hover
endndodetype, // node text on hover


below is the block of link text on hovering over the links:

PayTime, //
TXN_COUNT, block of text when hovering over links
Total_Amt,
SendTime //


Below is the code I am using for makegraph and inspired by this force directed graph from the d3 library:

http://bl.ocks.org/mbostock/4062045

function makeGraph(selector, d3GraphData) {
var svg = d3.select(selector),
width = +svg.attr("width"),
height = +svg.attr("height");

var color = d3.scaleOrdinal(d3.schemeCategory20);

var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));


var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(d3GraphData.links)
.enter().append("line")
.attr("stroke-width", function(d) {
return Math.sqrt(d.value);
});

var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(d3GraphData.nodes)
.enter().append("circle")
.attr("r", 5)
.attr("fill", function(d) {
return color(d.group);
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));

node.append("title")
.text(function(d) {
return d.id;
});

simulation
.nodes(d3GraphData.nodes)
.on("tick", ticked);

simulation.force("link")
.links(d3GraphData.links);

function ticked() {
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("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}

function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}

function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}

function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}

}


Below is the HTML:

<!doctype html>
<head>
<meta charset="UTF-8">

<title> Main Page </title>
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>

<script src="http://d3js.org/d3.v4.min.js"></script>
<script src="{{url_for('static',filename='graphlogic.js')}}"></script>

</head>


<form action = "{{url_for('enter_ID')}}" method = "POST">
<p>
Galactic_ID: <input type = "text" name = "Galactic_ID" />
<button type="submit" value="Submit">Submit</button>
Phone_ID: <input type = "text" name = "Phone_ID" />
<button type="submit" value="Submit">Submit</button>
ID_Card: <input type = "text" name = "ident_ID" />
<button type="submit" value="Submit">Submit</button>
</form>
</p>
<body>
<style>
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
.tooltip {
position: absolute;
background-color: white;
max-width: 200px;
height: auto;
padding: 1px;
border-style: solid;
border-radius: 4px;
border-width: 1px;
box-shadow: 3px 3px 10px rgba(0, 0, 0, .5);
pointer-events: none;
}
</style>
<svg id="Network_graph" width="960" height="600"></svg>


<script type="text/javascript">
var IDData = JSON.stringify({{data|safe}});
</script>

<div id = "graph"></div>




</body>
</html>


In the above makegraph code , I need to add "hover" function for nodes and links which will display the necessary texts while hovering over nodes and links.

Brand new in Javascript and d3.js.

Answer

You have to add the data (node type) to the node object in order to be used later.

Here is the example for your CreateNodes function.The object has a propertiw type. Note I am using a ES6 set instead of d3.set:

function createNodes(startnodes, endnodes, startnodetype, endnodetype, PayTime, TXN_COUNT, Total_Amt, SendTime) {
  var node_set = new Set();
  var links = [];
  var nodetype = d3.set();
  startnodes.forEach(function(src, i) {
    var tgt = endnodes[i];
    node_set.add({id:src, type:startnodetype[i]});
    node_set.add({id:tgt, type:endnodetype[i]});
    links.push({
      source: src,
      target: tgt,
      paytime:PayTime[i],
      value: 1 
    });
  });

  startnodetype.forEach(function(src, i) {
    var tgt_type = endnodetype[i];
    nodetype.add(src);
    nodetype.add(tgt_type);
  });

  var d3GraphData = {
    nodes: [...node_set.values()].map(function(d) {
      return {
        id: d.id,
        type: d.type,
        group: 1
      }
    }),
    links: links,
    nodetype: nodetype.values().map(function(d) {
      return {
        id: d.id,
        group: 1
      }
    })
  }
  return d3GraphData;

};

Then you can use the data to fill the tool tip. I decided to use a div for the tool tip. You have more control of the tool tip appearance that way. I create the tooltip div on the fly:

//$(document).ready(function() {

  var tooltip = d3.select("body")
                            .append("div")
                            .attr("class","tooltip")
                            .style("opacity", 0);

  console.log(IDData);
  var galData = JSON.parse(IDData);

Then when you add the node. You can add events for mouseover and update the tooltip div:

    var node = svg.append("g")
        .attr("class", "nodes")
        .selectAll("circle")
        .data(d3GraphData.nodes)
        .enter().append("circle")
        .attr("r", 5)
        .attr("fill", function(d) {
          return color(d.group);
        })
        .on('mouseover', function(d) {
                        tooltip.transition()
                        .duration(300)
                        .style("opacity", .8);
                        tooltip.html(d.id +"<p/>type:" + d.type)
                        .style("left", (d3.event.pageX) + "px")                 
                        .style("top", (d3.event.pageY + 10) + "px");
                })
    .on(...    

Working JSFiffdle.

You can use the same method for the links tool tips.

Comments