var width = document.getElementById("force-directed-graph").offsetWidth; var height = 600; var margin = {top: 0, right: 0, bottom: 0, left: 0}; var svg = d3.select("#force-directed-graph").append("svg") .attr("width", width) .attr("height", height) .attr("border", 2) .attr("margin", 2) .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // svg border svg.append('rect') .attr('height', height) .attr('width', width) .attr('fill', '#353535') .attr('stroke', 'white') .attr('stroke-width', 1); var ratingColorScale = d3.scaleLinear() .domain([-10,10]) .range([0,1]); var forceScale = d3.scaleLinear() .domain([0, 6005]) .range([0,1]); var group = svg.append('g'); var graph = group.append('g'); // this is an invisible rect that allows for dragging anywhere on the svg svg.append('rect') .attr('class', 'zoom-bg') .attr('height', height) .attr('width', width); // zoom/drag functionality var zoom = d3.zoom() .scaleExtent([1,100]) .on("zoom", zoomed); svg.call(zoom); // force simulation var simulation = d3.forceSimulation() .force('charge', d3.forceManyBody().strength(-10)) .force('center', d3.forceCenter(width / 2, height / 2)); // load in the csv data d3.json('soc-sign-bitcoinotc-1000.json', function(error, data) { if (error) throw error; else console.log(data); var max = d3.max(data.links, function(d) { return d.target; }); var min = d3.min(data.links, function(d) { return d.source; }); // edges var links = graph.append('g') .selectAll('line') .data(data.links) .enter() .append('line') .attr('class', 'link') .attr('id', function(d, i) { return 'l-' + i; }) .attr('stroke', function(d) { return d3.interpolateRdYlGn(ratingColorScale(d.rating)); }) .attr('stroke-width', function(d) { return 1; }) .on('mouseover', function(d, i) { d3.select(this).attr('stroke-width', 3.5); document.getElementById('p-' + i).setAttribute('r', 10); }) .on('mouseout', function(d, i) { d3.select(this).attr('stroke-width', 1); document.getElementById('p-' + i).setAttribute('r', 1.5); }); // nodes var nodes = graph.append('g') .attr('class', 'node') .selectAll('circle') .data(data.nodes) .enter() .append('circle') .attr('r', 3) .attr('fill', '#86dbfe') .attr('stroke', 'white') .attr('stroke-width', 0) .call(d3.drag() .on('start', dragstarted) .on('drag', dragged) .on('end', dragended)); // handle force location data simulation.nodes(data.nodes) .on('tick', tick); simulation .force('link', d3.forceLink().id(function(d) { return d.id; })); simulation .force('link') .links(data.links) function tick() { nodes .attr('cx', function(d) { return d.x; }) .attr('cy', function(d) { return d.y; }); links .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; }); } }); // drag events based on https://bl.ocks.org/mbostock/4062045 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; } function zoomed() { graph.attr("transform", d3.event.transform); }