A graph with force layout (using d3.js) and labels on edges, autorotation of those labels and arrowheads
forked from jhb's block: Graph with labeled edges
| license: mit |
A graph with force layout (using d3.js) and labels on edges, autorotation of those labels and arrowheads
forked from jhb's block: Graph with labeled edges
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Force Layout with labels on edges</title> | |
| <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
| <style type="text/css"> | |
| </style> | |
| </head> | |
| <body> | |
| <script type="text/javascript"> | |
| var w = 1000; | |
| var h = 600; | |
| var linkDistance=200; | |
| var colors = d3.scale.category10(); | |
| var dataset = { | |
| nodes: [ | |
| {name: "Adam"}, | |
| {name: "Bob"}, | |
| {name: "Carrie"}, | |
| {name: "Donovan"}, | |
| {name: "Edward"}, | |
| {name: "Felicity"}, | |
| {name: "George"}, | |
| {name: "Hannah"}, | |
| {name: "Iris"}, | |
| {name: "Jerry"} | |
| ], | |
| edges: [ | |
| {source: 0, target: 1}, | |
| {source: 0, target: 2}, | |
| {source: 0, target: 3}, | |
| {source: 0, target: 4}, | |
| {source: 1, target: 5}, | |
| {source: 2, target: 5}, | |
| {source: 2, target: 5}, | |
| {source: 3, target: 4}, | |
| {source: 5, target: 8}, | |
| {source: 5, target: 9}, | |
| {source: 6, target: 7}, | |
| {source: 7, target: 8}, | |
| {source: 8, target: 9} | |
| ] | |
| }; | |
| var svg = d3.select("body").append("svg").attr({"width":w,"height":h}); | |
| var force = d3.layout.force() | |
| .nodes(dataset.nodes) | |
| .links(dataset.edges) | |
| .size([w,h]) | |
| .linkDistance([linkDistance]) | |
| .charge([-500]) | |
| .theta(0.1) | |
| .gravity(0.05) | |
| .start(); | |
| var edges = svg.selectAll("line") | |
| .data(dataset.edges) | |
| .enter() | |
| .append("line") | |
| .attr("id",function(d,i) {return 'edge'+i}) | |
| .attr('marker-end','url(#arrowhead)') | |
| .style("stroke","#ccc") | |
| .style("pointer-events", "none"); | |
| var nodes = svg.selectAll("circle") | |
| .data(dataset.nodes) | |
| .enter() | |
| .append("circle") | |
| .attr({"r":15}) | |
| .style("fill",function(d,i){return colors(i);}) | |
| .call(force.drag) | |
| var nodelabels = svg.selectAll(".nodelabel") | |
| .data(dataset.nodes) | |
| .enter() | |
| .append("text") | |
| .attr({"x":function(d){return d.x;}, | |
| "y":function(d){return d.y;}, | |
| "class":"nodelabel", | |
| "stroke":"black"}) | |
| .text(function(d){return d.name;}); | |
| var edgepaths = svg.selectAll(".edgepath") | |
| .data(dataset.edges) | |
| .enter() | |
| .append('path') | |
| .attr({'d': function(d) {return 'M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y}, | |
| 'class':'edgepath', | |
| 'fill-opacity':0, | |
| 'stroke-opacity':0, | |
| 'fill':'blue', | |
| 'stroke':'red', | |
| 'id':function(d,i) {return 'edgepath'+i}}) | |
| .style("pointer-events", "none"); | |
| var edgelabels = svg.selectAll(".edgelabel") | |
| .data(dataset.edges) | |
| .enter() | |
| .append('text') | |
| .style("pointer-events", "none") | |
| .attr({'class':'edgelabel', | |
| 'id':function(d,i){return 'edgelabel'+i}, | |
| 'dx':80, | |
| 'dy':0, | |
| 'font-size':10, | |
| 'fill':'#aaa'}); | |
| edgelabels.append('textPath') | |
| .attr('xlink:href',function(d,i) {return '#edgepath'+i}) | |
| .style("pointer-events", "none") | |
| .text(function(d,i){return 'label '+i}); | |
| svg.append('defs').append('marker') | |
| .attr({'id':'arrowhead', | |
| 'viewBox':'-0 -5 10 10', | |
| 'refX':25, | |
| 'refY':0, | |
| //'markerUnits':'strokeWidth', | |
| 'orient':'auto', | |
| 'markerWidth':10, | |
| 'markerHeight':10, | |
| 'xoverflow':'visible'}) | |
| .append('svg:path') | |
| .attr('d', 'M 0,-5 L 10 ,0 L 0,5') | |
| .attr('fill', '#ccc') | |
| .attr('stroke','#ccc'); | |
| force.on("tick", function(){ | |
| edges.attr({"x1": function(d){return d.source.x;}, | |
| "y1": function(d){return d.source.y;}, | |
| "x2": function(d){return d.target.x;}, | |
| "y2": function(d){return d.target.y;} | |
| }); | |
| nodes.attr({"cx":function(d){return d.x;}, | |
| "cy":function(d){return d.y;} | |
| }); | |
| nodelabels.attr("x", function(d) { return d.x; }) | |
| .attr("y", function(d) { return d.y; }); | |
| edgepaths.attr('d', function(d) { var path='M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y; | |
| //console.log(d) | |
| return path}); | |
| edgelabels.attr('transform',function(d,i){ | |
| if (d.target.x<d.source.x){ | |
| bbox = this.getBBox(); | |
| rx = bbox.x+bbox.width/2; | |
| ry = bbox.y+bbox.height/2; | |
| return 'rotate(180 '+rx+' '+ry+')'; | |
| } | |
| else { | |
| return 'rotate(0)'; | |
| } | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> |