So I have been doing a lot of experimenting with D3 but have never attempted to turn it into an AngularJS directive, and I am not even sure of the proper way to change it over. I have an example that I believe I turned into an Angular D3 chart however I believe there are more efficient ways in loading the data , maybe in a $scope.data... Please use my jsfiddle to reference or correct.
https://jsfiddle.net/bcnmLrns/1/
var app = angular.module("chartApp", []); {
var data = {
"nodes": [{
"name": "hblodget",
"group": 1,
"size": 1,
"image": null
}, {
"name": "DowntownDonna69",
"group": 1,
"size": 20,
"image": "http://pbs.twimg.com/profile_images/636139174672732160/L5cd008s_normal.jpg"
}, {
"name": "PupsherLive",
"group": 1,
"size": 19,
"image": "http://pbs.twimg.com/profile_images/378800000210840839/93a8ba3852a8e20364957eb8b907b6b3_normal.jpeg"
}],
"links": [{
"source": 1,
"target": 0,
"value": 1
}, {
"source": 2,
"target": 0,
"value": 1
}]
};
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json(data, function(error, graph) {
if (error) throw error;
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) {
return Math.sqrt(d.value);
});
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function(d) {
return color(d.group);
})
.call(force.drag);
node.append("title")
.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("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
});
});
}
You can use other directives and services people have made to make D3 easier to use in Angular (see comments on question), but I still like the easy setup of modularizing graphs and such inside a simple directive:
myApp.directive('graph', function() {
var graphLink = function(scope, element) {
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select(element[0]).append("svg") // attach d3 to directive element
.attr("width", width)
.attr("height", height);
d3.json(data, function(error, graph) {
if (error) throw error;
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link") // there might be a more angular way to do this...
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) {
return Math.sqrt(d.value);
});
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function(d) {
return color(d.group);
})
.call(force.drag);
node.append("title")
.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("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
});
};
return {
link: graphLink, // pass in your link function here
scope: {
data: '=' // pass in your data as an attribute
// this makes this reusable, and you can redraw if the data changes
}
};
});
Related
I am developing a directed graph with nodes in static position using d3.js library.
Currently I am having 3 nodes, and I want to connect the two computers with the printer. The edges are set to different positions as seen in this image:
How can i make the lines connect the nodes based on the nodes position?
Here is the code I have so far.
var width = 640,
height = 400;
var nodes = [
{ x: 184.53020651496104, y: 0, id: 0, url:
"http://icons.iconarchive.com/icons/tpdkdesign.net/refresh-
cl/32/Hardware-My-Computer-3-icon.png"},
{ x: 100, y: 150, id: 1, url:
"http://icons.iconarchive.com/icons/tpdkdesign.net/refresh-
cl/32/Hardware-My-Computer-3-icon.png" },
{ x: width/3, y: height/2, id: 2, url:
"http://icons.iconarchive.com/icons/tpdkdesign.net/refresh-
cl/32/Hardware-Printer-Blue-icon.png" },
];
var links = [
{ source: 1, target: 2 },
{ source: 0, target: 2 }
];
var graph = d3.select('#graph');
var svg = graph.append('svg')
.attr('width', width)
.attr('height', height);
var force = d3.layout.force()
.size([width, height])
.nodes(nodes)
.links(links);
force.linkDistance(width/2);
var link = svg.selectAll('.link')
.data(links)
.enter().append('line')
.attr('class', 'link');
var node = svg.selectAll('.node')
.data(nodes)
.enter().append("image")
.attr("xlink:href", d=> d.url)
.attr("x", d=> d.x)
.attr("y", d=> d.y)
.attr("width", 30)
.attr("height", 30)
.attr('class', 'node');
force.on('end', function() {
node.attr('r', width/25)
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; });
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; });
});
force.start();
Thank you in advance.
This code works perfectly with a JSON file where the source and index are in the form of indices. However, when I switch to a format with the source and target as strings, it throws up TypeError: e[u.source.index] is undefined. How do I overcome this?
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var width = 960,
height = 500,
active = d3.select(null);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 8])
.on("zoom", zoomed);
var force = d3.layout.force()
.size([width, height])
.charge(-400)
.linkDistance(40)
.on("tick", tick);
var drag = force.drag()
.on("dragstart", dragstart);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
// .on("click", reset);
var g = svg.append("g");
var link = g.selectAll(".link"),
node = g.selectAll(".node");
svg
.call(zoom) // delete this line to disable free zooming
.call(zoom.event);
d3.json("data/miserables.json", function(error, graph) {
if (error) throw error;
force
.nodes(graph.nodes)
.links(graph.links)
.start();
link = link.data(graph.links)
.enter().append("line")
.attr("class", "links")
.style("stroke", "#999");
node = node.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 12)
.on("click", clicked)
//.call(drag);
});
function tick() {
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 clicked(d){
if (active.node() === this) return reset();
active.classed("active", false);
active = d3.select(this).classed("active", true);
var bbox = active.node().getBBox(),
bounds = [[bbox.x, bbox.y],[bbox.x + bbox.width, bbox.y + bbox.height]];
var dx = bounds[1][0] - bounds[0][0],
dy = bounds[1][1] - bounds[0][1],
x = (bounds[0][0] + bounds[1][0]) / 2,
y = (bounds[0][1] + bounds[1][1]) / 2,
scale = Math.max(1, Math.min(8, 0.9 / Math.max(dx / width, dy / height))),
translate = [width / 2 - scale * x, height / 2 - scale * y];
svg.transition()
.duration(750)
.call(zoom.translate(translate).scale(scale).event);
}
function reset() {
active.classed("active", false);
active = d3.select(null);
svg.transition()
.duration(750)
.call(zoom.translate([0, 0]).scale(1).event);
}
function dragstart(d) {
d3.select(this).classed("fixed", d.fixed = true);
}
function zoomed() {
console.log(d3.event)
g.style("stroke-width", 1.5 / d3.event.scale + "px");
g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
</script>
You're using D3 v3.x. While linking by name is an easy task in D3 v4.x, it seems that it's not possible in D3 v3.x. See this issue in D3 v3.x, and this explanation in the API:
Note: the values of the source and target attributes may be initially specified as indexes into the nodes array; these will be replaced by references after the call to start.
Thus, the snippet below won't work (the code is not mine, I just found it online and changed the links array from indices to names):
var nodes = [{
name: "node1"
}, {
name: "node2"
}, {
name: "node3"
}, {
name: "node4"
}, {
name: "node5"
}, {
name: "node6"
}, {
name: "node7"
}];
var edges = [{
source: "node1",
target: "node3"
}, {
source: "node1",
target: "node2"
}, {
source: "node1",
target: "node4"
}, {
source: "node2",
target: "node3"
}, {
source: "node2",
target: "node5"
}, {
source: "node2",
target: "node6"
}, {
source: "node3",
target: "node"
}];
var width = 400;
var height = 400;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.nodes(nodes)
.links(edges)
.size([width, height])
.linkDistance(150)
.charge(-400);
force.start();
var svg_edges = svg.selectAll("line")
.data(edges)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var color = d3.scale.category20();
var svg_nodes = svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("r", 20)
.style("fill", function(d, i) {
return color(i);
})
.call(force.drag);
var svg_texts = svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.style("fill", "black")
.attr("dx", 20)
.attr("dy", 8)
.text(function(d) {
return d.name;
});
force.on("tick", function() {
svg_edges.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;
});
svg_nodes.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
svg_texts.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
});
});
<script src="https://d3js.org/d3.v4.min.js"></script>
If you click "run snippet", you'll only see an error:
Uncaught TypeError: Cannot read property 'force' of undefined
Solution: Keep your links array with indices. However, if you already have/receive an array with names, you can change it to indices:
var nodeByName = d3.map(nodes, function(d) {
return d.name;
});
edges.forEach(function(d) {
d.source = nodeByName.get(d.source);
d.target = nodeByName.get(d.target);
});
Here is the same code of the first snippet with the above-mentioned changes. Now it works:
var nodes = [{
name: "node1"
}, {
name: "node2"
}, {
name: "node3"
}, {
name: "node4"
}, {
name: "node5"
}, {
name: "node6"
}, {
name: "node7"
}];
var edges = [{
source: "node1",
target: "node3"
}, {
source: "node1",
target: "node2"
}, {
source: "node1",
target: "node4"
}, {
source: "node2",
target: "node3"
}, {
source: "node2",
target: "node5"
}, {
source: "node2",
target: "node6"
}, {
source: "node2",
target: "node7"
}];
var nodeByName = d3.map(nodes, function(d) {
return d.name;
});
edges.forEach(function(d) {
d.source = nodeByName.get(d.source);
d.target = nodeByName.get(d.target);
});
var width = 400;
var height = 400;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.nodes(nodes)
.links(edges)
.size([width, height])
.linkDistance(150)
.charge(-400);
force.start();
var svg_edges = svg.selectAll("line")
.data(edges)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var color = d3.scale.category20();
var svg_nodes = svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("r", 20)
.style("fill", function(d, i) {
return color(i);
})
.call(force.drag);
var svg_texts = svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.style("fill", "black")
.attr("dx", 20)
.attr("dy", 8)
.text(function(d) {
return d.name;
});
force.on("tick", function() {
svg_edges.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;
});
svg_nodes.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
svg_texts.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
});
});
<script src="https://d3js.org/d3.v3.min.js"></script>
I am a beginner at d3. One of the diagrams I find particularly helpful and fun is the d3 force diagram. I've been playing around with it, but for some reason I've had a lot of trouble with this particular data set. Specifically, I've been trying forever to get the labels to show up on my d3 force diagram, but it just won't work. Here is my code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
.node text {
pointer-events: none;
font: 10px sans-serif;
}
</style>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
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));
d3.json("links.json", function(error, graph) {
if (error) throw error;
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) { return 1});
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", function(d){
return 3*d.size;
})
.attr("fill", function(d) { return color(d.id); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("svg:title")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.id });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.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;
}
</script>
thanks so much for your help
edit: here is my links.json
{
"nodes": [
{"id": "AK","size": "1.414213562"},
{"id": "AL","size": "1.414213562"},
{"id": "AR","size": "1.414213562"},
{"id": "AZ","size": "2.828427125"}],
"links": [
{"source": "ABC","target": "CO","value": "A"},
{"source": "ABC","target": "CO","value": "A"},
{"source": "ABC","target": "CO","value": "A"},
{"source": "ABC","target": "FL","value": "A"}
}
Added Edit below :
var svg = d3.select("svg"),
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));
graph = {
"nodes": [
{"id": "AK","size": "1.414213562"},
{"id": "AL","size": "1.414213562"},
{"id": "AR","size": "1.414213562"},
{"id": "AZ","size": "2.828427125"}],
"links": [
{"source": "AK","target": "AL","value": "A"},
{"source": "AL","target": "AZ","value": "A"},
{"source": "AL","target": "AR","value": "A"},
{"source": "AZ","target": "AR","value": "A"}]
};
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) { return 1});
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", function(d){
return 5;
})
.attr("fill", function(d) { return color(d.id); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("svg:title")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.id });
var labels = svg.append("g")
.attr("class", "label")
.selectAll("text")
.data(graph.nodes)
.enter().append("text")
.attr("dx", 6)
.attr("dy", ".35em")
.style("font-size",10)
.text(function(d) { return d.id });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.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; });
labels
.attr("x", function(d) { return d.x; })
.attr("y", 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;
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
.node text {
pointer-events: none;
font: 10px sans-serif;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="600"></svg>
Original answer :
Your json is not "proper". First of all there is no source named ABC or targets named CO or FL.
Source and target values should be an id from the nodes. You cannot link something that is not defined.
Also you are not using the size and value attribute.
var svg = d3.select("svg"),
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));
graph = {
"nodes": [
{"id": "AK","size": "1.414213562"},
{"id": "AL","size": "1.414213562"},
{"id": "AR","size": "1.414213562"},
{"id": "AZ","size": "2.828427125"}],
"links": [
{"source": "AK","target": "AL","value": "A"},
{"source": "AL","target": "AZ","value": "A"},
{"source": "AL","target": "AR","value": "A"},
{"source": "AZ","target": "AR","value": "A"}]
};
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) { return 1});
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", function(d){
return 5;
})
.attr("fill", function(d) { return color(d.id); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("svg:title")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.id });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.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;
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
.node text {
pointer-events: none;
font: 10px sans-serif;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="600"></svg>
I have been trying to add text and arrow to a d3js force layout diagram but was not being able to get the desired results.
My script looks like the following:
var width = 600;
height = 400;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(100)
.size([width, height]);
var svg = d3.select("#t").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("topology.json", function(error, graph) {
if (error) throw error;
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return 2*(d.value); });//Math.sqrt
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 10)
.style("fill", function(d) { return color(d.group); })
.call(force.drag);
node.append("title")
.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("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
});
I tried to add the following snippet to add text
node.append("text")
.attr("x", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
The sample json data is
{
"nodes": [{
"name": "0",
"group": 2
}, {
"name": "1",
"group": 2
}, {
"name": "2",
"group": 2
}, {
"name": "3",
"group": 1
}],
"links": [{
"source": 0,
"target": 1,
"value": 1
}, {
"source": 1,
"target": 3,
"value": 2
}, {
"source": 2,
"target": 3,
"value": 1
}]
}
Any help with adding text to the circles and arrows would be great. Sorry if I sounded a noob.
Thanks :) :)
Mobashyr,I have used the below sample for fulfill my requirement :
// Restart the force layout.
var force.nodes(nodes)
.links(links)
.charge(-1000)
.linkDistance(120)
.alpha(-15)
.start();
var link = vis.selectAll(".link")
.data(links);
link.enter().insert("svg:line", ".node")
.attr("class", "link")
.style("stroke", "#ccc")
.attr("cursor", "pointer")
.style("stroke-width", "0")
.style("stroke-width", function(d) { return d.weight;})
.on("mouseover", function() { d3.select(this).style("stroke", "#555555").attr("stroke-opacity", "1.0").attr("stroke-width","10");})
.on("mouseout", function() { d3.select(this).style("stroke", "#ccc").attr("stroke-opacity", "1.0").attr("stroke-width","4") });
link.exit().remove();
var node = vis.selectAll("g.node")
.data(nodes)
var groups = node.enter().append("g")
.attr("class", "node")
.attr("id", function (d) {
return d.entityType;
})
.on('click', click)
groups.append("circle")
.attr("cursor", "pointer")
.style("fill", function(d) { return color(d.entityType); })
.style("fill", "#fff")
.style("stroke-width", "0")
.style("stroke", "#ddd")
.attr("r", 20);
groups.append("text")
.attr("dy", 18)
.style("font-size", "2.5px")
.style("text-anchor", "middle")
.style('fill','#000')
.attr("refX", 15)
.attr("refY", -1.5)
.text(function (d) {
return d.text;
});
node.exit().remove();
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 + ")";
});
I am trying to build a force directed graph in D3. Here is the code for it -
index.html
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
stroke: #FFFF;
stroke-width: 1.5px;
}
.link {
stroke: #111;
}
</style>
<body>
<h1>Hello there</h1>
<script src="d3.v3.min.js"></script>
<script>
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("copy.json", function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { console.log(d.value); return d.value; });
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 10)
.style("fill", function(d) { console.log(d.name); return color(d.group); })
.call(force.drag);
node.append("title")
.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("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
});
</script>
</body>
</html>
This code works with copy.json file. I just copy pasted the json from here into that file. I have tried to create my own json by parsing through a txt file. The same code is giving me the error when I use my json file. Here is my json file (part of it) -
{
"nodes":[
"group": 5,
"name": "Nancie"
},
{
"group": 5,
"name": "Jonell"
}
],
"links": [
{
"source": 1,
"target": 29,
"value": 2
},
{
"source": 1,
"target": 43,
"value": 3
}
]
}
I have tested this json on jsonlint. Does the newline make a difference? I did read a couple of answer that are there on SO about this but none of them match. My source and target nodes are within range, I have no null values and I have taken care of the case when creating my JSON.
Well as it turns out I made a stupid mistake while creating my json file. In the links part of the json the numbering for source starts from 0. Hence for the last element I was exceeding the value. And hence I was getting that error.